1 module ddbus.util; 2 3 import ddbus.thin; 4 import std.meta : AliasSeq, staticIndexOf; 5 import std.range; 6 import std.traits; 7 import std.typecons : BitFlags, isTuple, Tuple; 8 import std.variant : VariantN; 9 10 struct DictionaryEntry(K, V) { 11 K key; 12 V value; 13 } 14 15 auto byDictionaryEntries(K, V)(V[K] aa) { 16 import std.algorithm : map; 17 18 return aa.byKeyValue.map!(pair => DictionaryEntry!(K, V)(pair.key, pair.value)); 19 } 20 21 /+ 22 Predicate template indicating whether T is an instance of ddbus.thin.Variant. 23 24 Deprecated: 25 This template used to be undocumented and user code should not depend on it. 26 Its meaning became unclear when support for Phobos-style variants was added. 27 It seemed best to remove it at that point. 28 +/ 29 deprecated("Use std.traits.isInstanceOf instead.") template isVariant(T) { 30 static if (isBasicType!T || isInputRange!T) { 31 enum isVariant = false; 32 } else static if (__traits(compiles, TemplateOf!T) && __traits(isSame, TemplateOf!T, Variant)) { 33 enum isVariant = true; 34 } else { 35 enum isVariant = false; 36 } 37 } 38 39 template VariantType(T) { 40 alias VariantType = TemplateArgsOf!(T)[0]; 41 } 42 43 template allCanDBus(TS...) { 44 static if (TS.length == 0) { 45 enum allCanDBus = true; 46 } else static if (!canDBus!(TS[0])) { 47 enum allCanDBus = false; 48 } else { 49 enum allCanDBus = allCanDBus!(TS[1 .. $]); 50 } 51 } 52 53 /++ 54 AliasSeq of all basic types in terms of the DBus typesystem 55 +/ 56 package // Don't add to the API yet, 'cause I intend to move it later 57 alias BasicTypes = AliasSeq!(bool, ubyte, short, ushort, int, uint, long, ulong, 58 double, string, ObjectPath, InterfaceName, BusName, FileDescriptor); 59 60 template basicDBus(U) { 61 alias T = Unqual!U; 62 static if (staticIndexOf!(T, BasicTypes) >= 0) { 63 enum basicDBus = true; 64 } else static if (is(T B == enum)) { 65 enum basicDBus = basicDBus!B; 66 } else static if (isInstanceOf!(BitFlags, T)) { 67 alias TemplateArgsOf!T[0] E; 68 enum basicDBus = basicDBus!E; 69 } else { 70 enum basicDBus = false; 71 } 72 } 73 74 template canDBus(U) { 75 alias T = Unqual!U; 76 static if (basicDBus!T || is(T == DBusAny)) { 77 enum canDBus = true; 78 } else static if (isInstanceOf!(Variant, T)) { 79 enum canDBus = canDBus!(VariantType!T); 80 } else static if (isInstanceOf!(VariantN, T)) { 81 // Phobos-style variants are supported if limited to DBus compatible types. 82 enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes); 83 } else static if (isTuple!T) { 84 enum canDBus = allCanDBus!(T.Types); 85 } else static if (isInputRange!T) { 86 static if (is(ElementType!T == DictionaryEntry!(K, V), K, V)) { 87 enum canDBus = basicDBus!K && canDBus!V; 88 } else { 89 enum canDBus = canDBus!(ElementType!T); 90 } 91 } else static if (isAssociativeArray!T) { 92 enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T); 93 } else static if (is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) { 94 enum canDBus = allCanDBus!(AllowedFieldTypes!T); 95 } else { 96 enum canDBus = false; 97 } 98 } 99 100 unittest { 101 import dunit.toolkit; 102 103 (canDBus!int).assertTrue(); 104 (canDBus!(int[])).assertTrue(); 105 (allCanDBus!(int, string, bool)).assertTrue(); 106 (canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue(); 107 (canDBus!(Tuple!(int[], int[string]))).assertTrue(); 108 (canDBus!(int[string])).assertTrue(); 109 (canDBus!FileDescriptor).assertTrue(); 110 } 111 112 string typeSig(U)() 113 if (canDBus!U) { 114 alias T = Unqual!U; 115 static if (is(T == ubyte)) { 116 return "y"; 117 } else static if (is(T == bool)) { 118 return "b"; 119 } else static if (is(T == short)) { 120 return "n"; 121 } else static if (is(T == ushort)) { 122 return "q"; 123 } else static if (is(T == FileDescriptor)) { 124 return "h"; 125 } else static if (is(T == int)) { 126 return "i"; 127 } else static if (is(T == uint)) { 128 return "u"; 129 } else static if (is(T == long)) { 130 return "x"; 131 } else static if (is(T == ulong)) { 132 return "t"; 133 } else static if (is(T == double)) { 134 return "d"; 135 } else static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) { 136 return "s"; 137 } else static if (is(T == ObjectPath)) { 138 return "o"; 139 } else static if (isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) { 140 return "v"; 141 } else static if (is(T B == enum)) { 142 return typeSig!B; 143 } else static if (isInstanceOf!(BitFlags, T)) { 144 alias TemplateArgsOf!T[0] E; 145 return typeSig!E; 146 } else static if (is(T == DBusAny)) { 147 static assert(false, 148 "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired."); 149 } else static if (isTuple!T) { 150 string sig = "("; 151 foreach (i, S; T.Types) { 152 sig ~= typeSig!S(); 153 } 154 sig ~= ")"; 155 return sig; 156 } else static if (isInputRange!T) { 157 return "a" ~ typeSig!(ElementType!T)(); 158 } else static if (isAssociativeArray!T) { 159 return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}"; 160 } else static if (is(T == struct)) { 161 string sig = "("; 162 foreach (i, S; AllowedFieldTypes!T) { 163 sig ~= typeSig!S(); 164 } 165 sig ~= ")"; 166 return sig; 167 } 168 } 169 170 string typeSig(T)() 171 if (isInstanceOf!(DictionaryEntry, T)) { 172 alias typeof(T.key) K; 173 alias typeof(T.value) V; 174 return "{" ~ typeSig!K ~ typeSig!V ~ '}'; 175 } 176 177 string[] typeSigReturn(T)() 178 if (canDBus!T) { 179 static if (is(T == Tuple!TS, TS...)) 180 return typeSigArr!TS; 181 else 182 return [typeSig!T]; 183 } 184 185 string typeSigAll(TS...)() 186 if (allCanDBus!TS) { 187 string sig = ""; 188 foreach (i, T; TS) { 189 sig ~= typeSig!T(); 190 } 191 return sig; 192 } 193 194 string[] typeSigArr(TS...)() 195 if (allCanDBus!TS) { 196 string[] sig = []; 197 foreach (i, T; TS) { 198 sig ~= typeSig!T(); 199 } 200 return sig; 201 } 202 203 int typeCode(T)() 204 if (canDBus!T) { 205 int code = typeSig!T()[0]; 206 return (code != '(') ? code : 'r'; 207 } 208 209 int typeCode(T)() 210 if (isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) { 211 return 'e'; 212 } 213 214 /** 215 Params: 216 type = the type code of a type (first character in a type string) 217 Returns: true if the given type is an integer. 218 */ 219 bool dbusIsIntegral(int type) @property { 220 return type == 'y' || type == 'n' || type == 'q' || type == 'i' || type == 'u' || type == 'x' || type == 't'; 221 } 222 223 /** 224 Params: 225 type = the type code of a type (first character in a type string) 226 Returns: true if the given type is a floating point value. 227 */ 228 bool dbusIsFloating(int type) @property { 229 return type == 'd'; 230 } 231 232 /** 233 Params: 234 type = the type code of a type (first character in a type string) 235 Returns: true if the given type is an integer or a floating point value. 236 */ 237 bool dbusIsNumeric(int type) @property { 238 return dbusIsIntegral(type) || dbusIsFloating(type); 239 } 240 241 unittest { 242 import dunit.toolkit; 243 244 static assert(canDBus!ObjectPath); 245 static assert(canDBus!InterfaceName); 246 static assert(canDBus!BusName); 247 248 // basics 249 typeSig!int().assertEqual("i"); 250 typeSig!bool().assertEqual("b"); 251 typeSig!string().assertEqual("s"); 252 typeSig!InterfaceName().assertEqual("s"); 253 typeSig!(immutable(InterfaceName))().assertEqual("s"); 254 typeSig!ObjectPath().assertEqual("o"); 255 typeSig!(immutable(ObjectPath))().assertEqual("o"); 256 typeSig!(Variant!int)().assertEqual("v"); 257 typeSig!FileDescriptor().assertEqual("h"); 258 // enums 259 enum E : ubyte { 260 a, 261 b, 262 c 263 } 264 265 typeSig!E().assertEqual(typeSig!ubyte()); 266 enum U : string { 267 One = "One", 268 Two = "Two" 269 } 270 271 typeSig!U().assertEqual(typeSig!string()); 272 // bit flags 273 enum F : uint { 274 a = 1, 275 b = 2, 276 c = 4 277 } 278 279 typeSig!(BitFlags!F)().assertEqual(typeSig!uint()); 280 // tuples (represented as structs in DBus) 281 typeSig!(Tuple!(int, string, string)).assertEqual("(iss)"); 282 typeSig!(Tuple!(int, string, Variant!int, Tuple!(int, "k", double, "x"))).assertEqual( 283 "(isv(id))"); 284 // structs 285 struct S1 { 286 int a; 287 double b; 288 string s; 289 } 290 291 typeSig!S1.assertEqual("(ids)"); 292 struct S2 { 293 Variant!int c; 294 string d; 295 S1 e; 296 uint f; 297 FileDescriptor g; 298 } 299 300 typeSig!S2.assertEqual("(vs(ids)uh)"); 301 // arrays 302 typeSig!(int[]).assertEqual("ai"); 303 typeSig!(Variant!int[]).assertEqual("av"); 304 typeSig!(Tuple!(ubyte)[][]).assertEqual("aa(y)"); 305 // dictionaries 306 typeSig!(int[string]).assertEqual("a{si}"); 307 typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}"); 308 // multiple arguments 309 typeSigAll!(int, bool).assertEqual("ib"); 310 // Phobos-style variants 311 canDBus!(std.variant.Variant).assertFalse(); 312 typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v"); 313 // type codes 314 typeCode!int().assertEqual(cast(int)('i')); 315 typeCode!bool().assertEqual(cast(int)('b')); 316 typeCode!(Tuple!(int, string))().assertEqual(cast(int)('r')); 317 // ctfe-capable 318 static string sig = typeSig!ulong(); 319 sig.assertEqual("t"); 320 static string sig2 = typeSig!(Tuple!(int, string, string)); 321 sig2.assertEqual("(iss)"); 322 static string sig3 = typeSigAll!(int, string, InterfaceName, BusName); 323 sig3.assertEqual("isss"); 324 } 325 326 private template AllowedFieldTypes(S) 327 if (is(S == struct)) { 328 import ddbus.attributes : isAllowedField; 329 import std.meta : Filter, staticMap; 330 331 static alias TypeOf(alias sym) = typeof(sym); 332 333 alias AllowedFieldTypes = staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof)); 334 }