1 module ddbus.util;
2 
3 import ddbus.thin;
4 import std.typecons;
5 import std.range;
6 import std.traits;
7 
8 struct DictionaryEntry(K, V) {
9   K key;
10   V value;
11 }
12 
13 auto byDictionaryEntries(K, V)(V[K] aa) {
14   import std.algorithm : map;
15 
16   return aa.byKeyValue.map!(pair => DictionaryEntry!(K, V)(pair.key, pair.value));
17 }
18 
19 template isVariant(T) {
20   static if(isBasicType!T || isInputRange!T) {
21     enum isVariant = false;
22   } else static if(__traits(compiles, TemplateOf!T) 
23                 && __traits(isSame, TemplateOf!T, Variant)) {
24     enum isVariant = true;
25   } else {
26     enum isVariant = false;
27   }
28 }
29 
30 template VariantType(T) {
31   alias VariantType = TemplateArgsOf!(T)[0];
32 }
33 
34 template allCanDBus(TS...) {
35   static if (TS.length == 0) {
36     enum allCanDBus = true; 
37   } else static if(!canDBus!(TS[0])) {
38     enum allCanDBus = false;
39   } else {
40     enum allCanDBus = allCanDBus!(TS[1..$]);
41   }
42 }
43 
44 template basicDBus(T) {
45   static if(is(T == byte) || is(T == short) || is (T == ushort) || is (T == int)
46             || is (T == uint) || is (T == long) || is (T == ulong)
47             || is (T == double) || is (T == string) || is(T == bool)) {
48     enum basicDBus = true;
49   } else {
50     enum basicDBus = false;
51   }
52 }
53 
54 template canDBus(T) {
55   static if(basicDBus!T || is(T == DBusAny)) {
56     enum canDBus = true;
57   } else static if(isVariant!T) {
58     enum canDBus = canDBus!(VariantType!T);
59   } else static if(isTuple!T) {
60     enum canDBus = allCanDBus!(T.Types);
61   } else static if(isInputRange!T) {
62     enum canDBus = canDBus!(ElementType!T);
63   } else static if(isAssociativeArray!T) {
64     enum canDBus = canDBus!(KeyType!T) && canDBus!(ValueType!T);
65   } else static if(is(T == DictionaryEntry!(K, V), K, V)) {
66     enum canDBus = canDBus!K && canDBus!V;
67   } else {
68     enum canDBus = false;
69   }
70 }
71 unittest {
72   import dunit.toolkit;
73   (canDBus!int).assertTrue();
74   (canDBus!(int[])).assertTrue();
75   (allCanDBus!(int,string,bool)).assertTrue();
76   (canDBus!(Tuple!(int[],bool,Variant!short))).assertTrue();
77   (canDBus!(Tuple!(int[],int[string]))).assertTrue();
78   (canDBus!(int[string])).assertTrue();
79 }
80 
81 string typeSig(T)() if(canDBus!T) {
82   static if(is(T == byte)) {
83     return "y";
84   } else static if(is(T == bool)) {
85     return "b";
86   } else static if(is(T == short)) {
87     return "n";
88   } else static if(is(T == ushort)) {
89     return "q";
90   } else static if(is(T == int)) {
91     return "i";
92   } else static if(is(T == uint)) {
93     return "u";
94   } else static if(is(T == long)) {
95     return "x";
96   } else static if(is(T == ulong)) {
97     return "t";
98   } else static if(is(T == double)) {
99     return "d";
100   } else static if(is(T == string)) {
101     return "s";
102   } else static if(isVariant!T) {
103     return "v";
104   } else static if(is(T == DBusAny)) {
105     static assert(false, "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired.");
106   } else static if(isTuple!T) {
107     string sig = "(";
108     foreach(i, S; T.Types) {
109       sig ~= typeSig!S();
110     } 
111     sig ~= ")";
112     return sig;
113   } else static if(is(T == DictionaryEntry!(K, V), K, V)) {
114     return '{' ~ typeSig!K ~ typeSig!V ~ '}';
115   } else static if(isInputRange!T) {
116     return "a" ~ typeSig!(ElementType!T)();
117   } else static if(isAssociativeArray!T) {
118     return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
119   }
120 }
121 
122 string[] typeSigReturn(T)() if(canDBus!T) {
123   static if(is(T == Tuple!TS, TS...))
124     return typeSigArr!TS;
125   else
126     return [typeSig!T];
127 }
128 
129 string typeSigAll(TS...)() if(allCanDBus!TS) {
130   string sig = "";
131   foreach(i,T; TS) {
132     sig ~= typeSig!T();
133   }
134   return sig;
135 }
136 
137 string[] typeSigArr(TS...)() if(allCanDBus!TS) {
138   string[] sig = [];
139   foreach(i,T; TS) {
140     sig ~= typeSig!T();
141   }
142   return sig;
143 }
144 
145 int typeCode(T)() if(canDBus!T) {
146   string sig = typeSig!T();
147   return sig[0];
148 }
149 
150 unittest {
151   import dunit.toolkit;
152   // basics
153   typeSig!int().assertEqual("i");
154   typeSig!bool().assertEqual("b");
155   typeSig!string().assertEqual("s");
156   typeSig!(Variant!int)().assertEqual("v");
157   // structs
158   typeSig!(Tuple!(int,string,string)).assertEqual("(iss)");
159   typeSig!(Tuple!(int,string,Variant!int,Tuple!(int,"k",double,"x"))).assertEqual("(isv(id))");
160   // arrays
161   typeSig!(int[]).assertEqual("ai");
162   typeSig!(Variant!int[]).assertEqual("av");
163   typeSig!(Tuple!(byte)[][]).assertEqual("aa(y)");
164   // dictionaries
165   typeSig!(int[string]).assertEqual("a{si}");
166   typeSig!(DictionaryEntry!(string, int)).assertEqual("{si}");
167   // multiple arguments
168   typeSigAll!(int,bool).assertEqual("ib");
169   // type codes
170   typeCode!int().assertEqual(cast(int)('i'));
171   typeCode!bool().assertEqual(cast(int)('b'));
172   // ctfe-capable
173   static string sig = typeSig!ulong();
174   sig.assertEqual("t");
175   static string sig2 = typeSig!(Tuple!(int,string,string));
176   sig2.assertEqual("(iss)"); 
177   static string sig3 = typeSigAll!(int,string,string);
178   sig3.assertEqual("iss"); 
179 }
180