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, byte, short, ushort, int, uint, long, ulong, 58 double, string, ObjectPath); 59 60 template basicDBus(T) { 61 static if (staticIndexOf!(T, BasicTypes) >= 0) { 62 enum basicDBus = true; 63 } else static if (is(T B == enum)) { 64 enum basicDBus = basicDBus!B; 65 } else static if (isInstanceOf!(BitFlags, T)) { 66 alias TemplateArgsOf!T[0] E; 67 enum basicDBus = basicDBus!E; 68 } else { 69 enum basicDBus = false; 70 } 71 } 72 73 template canDBus(T) { 74 static if (basicDBus!T || is(T == DBusAny)) { 75 enum canDBus = true; 76 } else static if (isInstanceOf!(Variant, T)) { 77 enum canDBus = canDBus!(VariantType!T); 78 } else static if (isInstanceOf!(VariantN, T)) { 79 // Phobos-style variants are supported if limited to DBus compatible types. 80 enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes); 81 } else static if (isTuple!T) { 82 enum canDBus = allCanDBus!(T.Types); 83 } else static if (isInputRange!T) { 84 static if (is(ElementType!T == DictionaryEntry!(K, V), K, V)) { 85 enum canDBus = basicDBus!K && canDBus!V; 86 } else { 87 enum canDBus = canDBus!(ElementType!T); 88 } 89 } else static if (isAssociativeArray!T) { 90 enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T); 91 } else static if (is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) { 92 enum canDBus = allCanDBus!(AllowedFieldTypes!T); 93 } else { 94 enum canDBus = false; 95 } 96 } 97 98 unittest { 99 import dunit.toolkit; 100 101 (canDBus!int).assertTrue(); 102 (canDBus!(int[])).assertTrue(); 103 (allCanDBus!(int, string, bool)).assertTrue(); 104 (canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue(); 105 (canDBus!(Tuple!(int[], int[string]))).assertTrue(); 106 (canDBus!(int[string])).assertTrue(); 107 } 108 109 string typeSig(T)() 110 if (canDBus!T) { 111 static if (is(T == byte)) { 112 return "y"; 113 } else static if (is(T == bool)) { 114 return "b"; 115 } else static if (is(T == short)) { 116 return "n"; 117 } else static if (is(T == ushort)) { 118 return "q"; 119 } else static if (is(T == int)) { 120 return "i"; 121 } else static if (is(T == uint)) { 122 return "u"; 123 } else static if (is(T == long)) { 124 return "x"; 125 } else static if (is(T == ulong)) { 126 return "t"; 127 } else static if (is(T == double)) { 128 return "d"; 129 } else static if (is(T == string)) { 130 return "s"; 131 } else static if (is(T == ObjectPath)) { 132 return "o"; 133 } else static if (isInstanceOf!(Variant, T) || isInstanceOf!(VariantN, T)) { 134 return "v"; 135 } else static if (is(T B == enum)) { 136 return typeSig!B; 137 } else static if (isInstanceOf!(BitFlags, T)) { 138 alias TemplateArgsOf!T[0] E; 139 return typeSig!E; 140 } else static if (is(T == DBusAny)) { 141 static assert(false, 142 "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired."); 143 } else static if (isTuple!T) { 144 string sig = "("; 145 foreach (i, S; T.Types) { 146 sig ~= typeSig!S(); 147 } 148 sig ~= ")"; 149 return sig; 150 } else static if (isInputRange!T) { 151 return "a" ~ typeSig!(ElementType!T)(); 152 } else static if (isAssociativeArray!T) { 153 return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}"; 154 } else static if (is(T == struct)) { 155 string sig = "("; 156 foreach (i, S; AllowedFieldTypes!T) { 157 sig ~= typeSig!S(); 158 } 159 sig ~= ")"; 160 return sig; 161 } 162 } 163 164 string typeSig(T)() 165 if (isInstanceOf!(DictionaryEntry, T)) { 166 alias typeof(T.key) K; 167 alias typeof(T.value) V; 168 return "{" ~ typeSig!K ~ typeSig!V ~ '}'; 169 } 170 171 string[] typeSigReturn(T)() 172 if (canDBus!T) { 173 static if (is(T == Tuple!TS, TS...)) 174 return typeSigArr!TS; 175 else 176 return [typeSig!T]; 177 } 178 179 string typeSigAll(TS...)() 180 if (allCanDBus!TS) { 181 string sig = ""; 182 foreach (i, T; TS) { 183 sig ~= typeSig!T(); 184 } 185 return sig; 186 } 187 188 string[] typeSigArr(TS...)() 189 if (allCanDBus!TS) { 190 string[] sig = []; 191 foreach (i, T; TS) { 192 sig ~= typeSig!T(); 193 } 194 return sig; 195 } 196 197 int typeCode(T)() 198 if (canDBus!T) { 199 int code = typeSig!T()[0]; 200 return (code != '(') ? code : 'r'; 201 } 202 203 int typeCode(T)() 204 if (isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) { 205 return 'e'; 206 } 207 208 unittest { 209 import dunit.toolkit; 210 211 // basics 212 typeSig!int().assertEqual("i"); 213 typeSig!bool().assertEqual("b"); 214 typeSig!string().assertEqual("s"); 215 typeSig!(Variant!int)().assertEqual("v"); 216 // enums 217 enum E : byte { 218 a, 219 b, 220 c 221 } 222 223 typeSig!E().assertEqual(typeSig!byte()); 224 enum U : string { 225 One = "One", 226 Two = "Two" 227 } 228 229 typeSig!U().assertEqual(typeSig!string()); 230 // bit flags 231 enum F : uint { 232 a = 1, 233 b = 2, 234 c = 4 235 } 236 237 typeSig!(BitFlags!F)().assertEqual(typeSig!uint()); 238 // tuples (represented as structs in DBus) 239 typeSig!(Tuple!(int, string, string)).assertEqual("(iss)"); 240 typeSig!(Tuple!(int, string, Variant!int, Tuple!(int, "k", double, "x"))).assertEqual( 241 "(isv(id))"); 242 // structs 243 struct S1 { 244 int a; 245 double b; 246 string s; 247 } 248 249 typeSig!S1.assertEqual("(ids)"); 250 struct S2 { 251 Variant!int c; 252 string d; 253 S1 e; 254 uint f; 255 } 256 257 typeSig!S2.assertEqual("(vs(ids)u)"); 258 // arrays 259 typeSig!(int[]).assertEqual("ai"); 260 typeSig!(Variant!int[]).assertEqual("av"); 261 typeSig!(Tuple!(byte)[][]).assertEqual("aa(y)"); 262 // dictionaries 263 typeSig!(int[string]).assertEqual("a{si}"); 264 typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}"); 265 // multiple arguments 266 typeSigAll!(int, bool).assertEqual("ib"); 267 // Phobos-style variants 268 canDBus!(std.variant.Variant).assertFalse(); 269 typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v"); 270 // type codes 271 typeCode!int().assertEqual(cast(int)('i')); 272 typeCode!bool().assertEqual(cast(int)('b')); 273 typeCode!(Tuple!(int, string))().assertEqual(cast(int)('r')); 274 // ctfe-capable 275 static string sig = typeSig!ulong(); 276 sig.assertEqual("t"); 277 static string sig2 = typeSig!(Tuple!(int, string, string)); 278 sig2.assertEqual("(iss)"); 279 static string sig3 = typeSigAll!(int, string, string); 280 sig3.assertEqual("iss"); 281 } 282 283 private template AllowedFieldTypes(S) 284 if (is(S == struct)) { 285 import ddbus.attributes : isAllowedField; 286 import std.meta : Filter, staticMap; 287 288 static alias TypeOf(alias sym) = typeof(sym); 289 290 alias AllowedFieldTypes = staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof)); 291 }