1 module ddbus.conv;
2 
3 import ddbus.c_lib;
4 import ddbus.util;
5 import ddbus.thin;
6 import std.string;
7 import std.typecons;
8 import std.range;
9 import std.traits;
10 
11 void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
12   foreach(index, arg; args) {
13     alias TS[index] T;
14     static if(is(T == string)) {
15       immutable(char)* cStr = arg.toStringz();
16       dbus_message_iter_append_basic(iter,typeCode!T,&cStr);
17     } else static if(is(T==bool)) {
18       dbus_bool_t longerBool = arg; // dbus bools are ints
19       dbus_message_iter_append_basic(iter,typeCode!T,&longerBool);
20     } else static if(isTuple!T) {
21       DBusMessageIter sub;
22       dbus_message_iter_open_container(iter, 'r', null, &sub);
23       buildIter(&sub, arg.expand);
24       dbus_message_iter_close_container(iter, &sub);
25     } else static if(isInputRange!T) {
26       DBusMessageIter sub;
27       const(char)* subSig = (typeSig!(ElementType!T)()).toStringz();
28       dbus_message_iter_open_container(iter, 'a', subSig, &sub);
29       foreach(x; arg) {
30         buildIter(&sub, x);
31       }
32       dbus_message_iter_close_container(iter, &sub);
33     } else static if(isAssociativeArray!T) {
34       buildIter(iter, arg.byDictionaryEntries);
35     } else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) {
36       static if(is(T == Variant!DBusAny)) {
37         auto val = arg.data;
38         val.explicitVariant = true;
39       } else {
40         auto val = arg;
41       }
42       DBusMessageIter subStore;
43       DBusMessageIter* sub = &subStore;
44       char[] sig = [cast(char) val.type];
45       if(val.type == 'a')
46         sig ~= val.signature;
47       else if(val.type == 'r')
48         sig = val.signature;
49       sig ~= '\0';
50       if (!val.explicitVariant)
51         sub = iter;
52       else
53         dbus_message_iter_open_container(iter, 'v', sig.ptr, sub);
54       if(val.type == 's') {
55         buildIter(sub, val.str);
56       } else if(val.type == 'b') {
57         buildIter(sub,val.boolean);
58       } else if(dbus_type_is_basic(val.type)) {
59         dbus_message_iter_append_basic(sub,val.type,&val.int64);
60       } else if(val.type == 'a') {
61         DBusMessageIter arr;
62         dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr);
63         if (val.signature == ['y'])
64           foreach (item; val.binaryData)
65             dbus_message_iter_append_basic(&arr, 'y', &item);
66         else
67           foreach(item; val.array)
68             buildIter(&arr, item);
69         dbus_message_iter_close_container(sub, &arr);
70       } else if(val.type == 'r') {
71         DBusMessageIter arr;
72         dbus_message_iter_open_container(sub, 'r', null, &arr);
73         foreach(item; val.tuple)
74           buildIter(&arr, item);
75         dbus_message_iter_close_container(sub, &arr);
76       } else if(val.type == 'e') {
77         DBusMessageIter entry;
78         dbus_message_iter_open_container(sub, 'e', null, &entry);
79         buildIter(&entry, val.entry.key);
80         buildIter(&entry, val.entry.value);
81         dbus_message_iter_close_container(sub, &entry);
82       }
83       if(val.explicitVariant)
84         dbus_message_iter_close_container(iter, sub);
85     } else static if(isVariant!T) {
86       DBusMessageIter sub;
87       const(char)* subSig = typeSig!(VariantType!T).toStringz();
88       dbus_message_iter_open_container(iter, 'v', subSig, &sub);
89       buildIter(&sub, arg.data);
90       dbus_message_iter_close_container(iter, &sub);
91     } else static if(is(T == DictionaryEntry!(K, V), K, V)) {
92       DBusMessageIter sub;
93       dbus_message_iter_open_container(iter, 'e', null, &sub);
94       buildIter(&sub, arg.key);
95       buildIter(&sub, arg.value);
96       dbus_message_iter_close_container(iter, &sub);
97     } else static if(basicDBus!T) {
98       dbus_message_iter_append_basic(iter,typeCode!T,&arg);
99     }
100   }
101 }
102 
103 T readIter(T)(DBusMessageIter *iter) if (canDBus!T) {
104   T ret;
105   static if(!isVariant!T || is(T == Variant!DBusAny)) {
106     if(dbus_message_iter_get_arg_type(iter) == 'v') {
107       DBusMessageIter sub;
108       dbus_message_iter_recurse(iter, &sub);
109       static if(is(T == Variant!DBusAny)) {
110         ret = variant(readIter!DBusAny(&sub));
111       } else {
112         ret = readIter!T(&sub);
113         static if(is(T == DBusAny))
114           ret.explicitVariant = true;
115       }
116       dbus_message_iter_next(iter);
117       return ret;
118     }
119   }
120   static if(isTuple!T) {
121     assert(dbus_message_iter_get_arg_type(iter) == 'r');
122   } else static if(is(T == DictionaryEntry!(K1, V1), K1, V1)) {
123     assert(dbus_message_iter_get_arg_type(iter) == 'e');
124   } else static if(!is(T == DBusAny) && !is(T == Variant!DBusAny)) {
125     assert(dbus_message_iter_get_arg_type(iter) == typeCode!T());
126   }
127   static if(is(T==string)) {
128     const(char)* cStr;
129     dbus_message_iter_get_basic(iter, &cStr);
130     ret = cStr.fromStringz().idup; // copy string
131   } else static if(is(T==bool)) {
132     dbus_bool_t longerBool;
133     dbus_message_iter_get_basic(iter, &longerBool);
134     ret = cast(bool)longerBool;
135   } else static if(isTuple!T) {
136     DBusMessageIter sub;
137     dbus_message_iter_recurse(iter, &sub);
138     readIterTuple!T(&sub, ret);
139   } else static if(is(T == DictionaryEntry!(K, V), K, V)) {
140     DBusMessageIter sub;
141     dbus_message_iter_recurse(iter, &sub);
142     ret.key = readIter!K(&sub);
143     ret.value = readIter!V(&sub);
144   } else static if(is(T t : U[], U)) {
145     assert(dbus_message_iter_get_element_type(iter) == typeCode!U);
146     DBusMessageIter sub;
147     dbus_message_iter_recurse(iter, &sub);
148     while(dbus_message_iter_get_arg_type(&sub) != 0) {
149       ret ~= readIter!U(&sub);
150     }
151   } else static if(isVariant!T) {
152     DBusMessageIter sub;
153     dbus_message_iter_recurse(iter, &sub);
154     ret.data = readIter!(VariantType!T)(&sub);
155   } else static if(isAssociativeArray!T) {
156     DBusMessageIter sub;
157     dbus_message_iter_recurse(iter, &sub);
158     while(dbus_message_iter_get_arg_type(&sub) != 0) {
159       auto entry = readIter!(DictionaryEntry!(KeyType!T, ValueType!T))(&sub);
160       ret[entry.key] = entry.value;
161     }
162   } else static if(is(T == DBusAny)) {
163     ret.type = dbus_message_iter_get_arg_type(iter);
164     ret.explicitVariant = false;
165     if(ret.type == 's') {
166       ret.str = readIter!string(iter);
167       return ret;
168     } else if(ret.type == 'b') {
169       ret.boolean = readIter!bool(iter);
170       return ret;
171     } else if(dbus_type_is_basic(ret.type)) {
172       dbus_message_iter_get_basic(iter, &ret.int64);
173     } else if(ret.type == 'a') {
174       DBusMessageIter sub;
175       dbus_message_iter_recurse(iter, &sub);
176       auto sig = dbus_message_iter_get_signature(&sub);
177       ret.signature = sig.fromStringz.dup;
178       dbus_free(sig);
179       if (ret.signature == ['y'])
180         while(dbus_message_iter_get_arg_type(&sub) != 0) {
181           ubyte b;
182           assert(dbus_message_iter_get_arg_type(&sub) == 'y');
183           dbus_message_iter_get_basic(&sub, &b);
184           dbus_message_iter_next(&sub);
185           ret.binaryData ~= b;
186         }
187       else
188         while(dbus_message_iter_get_arg_type(&sub) != 0) {
189           ret.array ~= readIter!DBusAny(&sub);
190         }
191     } else if(ret.type == 'r') {
192       auto sig = dbus_message_iter_get_signature(iter);
193       ret.signature = sig.fromStringz.dup;
194       dbus_free(sig);
195       DBusMessageIter sub;
196       dbus_message_iter_recurse(iter, &sub);
197       while(dbus_message_iter_get_arg_type(&sub) != 0) {
198         ret.tuple ~= readIter!DBusAny(&sub);
199       }
200     } else if(ret.type == 'e') {
201       DBusMessageIter sub;
202       dbus_message_iter_recurse(iter, &sub);
203       ret.entry = new DictionaryEntry!(DBusAny, DBusAny);
204       ret.entry.key = readIter!DBusAny(&sub);
205       ret.entry.value = readIter!DBusAny(&sub);
206     }
207   } else static if(basicDBus!T) {
208     dbus_message_iter_get_basic(iter, &ret);
209   }
210   dbus_message_iter_next(iter);
211   return ret;
212 }
213 
214 void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup && allCanDBus!(Tup.Types)) {
215   foreach(index, T; Tup.Types) {
216     tuple[index] = readIter!T(iter);
217   }
218 }
219 
220 unittest {
221   import dunit.toolkit;
222   import ddbus.thin;
223   Variant!T var(T)(T data){ return Variant!T(data); }
224   Message msg = Message("org.example.wow","/wut","org.test.iface","meth");
225   bool[] emptyB;
226   string[string] map;
227   map["hello"] = "world";
228   DBusAny anyVar = DBusAny(cast(ulong) 1561);
229   anyVar.type.assertEqual('t');
230   anyVar.uint64.assertEqual(1561);
231   anyVar.explicitVariant.assertEqual(false);
232   auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32, [1, 2], tuple(variant(4), 5), map));
233   Variant!DBusAny complexVar = variant(DBusAny([
234     "hello world": variant(DBusAny(1337)),
235     "array value": variant(DBusAny([42, 64])),
236     "tuple value": variant(tupleMember),
237     "optimized binary data": variant(DBusAny(cast(ubyte[]) [1, 2, 3, 4, 5, 6]))
238   ]));
239   complexVar.data.type.assertEqual('a');
240   complexVar.data.signature.assertEqual("{sv}".dup);
241   tupleMember.signature.assertEqual("(vviai(vi)a{ss})");
242   auto args = tuple(5,true,"wow",var(5.9),[6,5],tuple(6.2,4,[["lol"]],emptyB,var([4,2])),map,anyVar,complexVar);
243   msg.build(args.expand);
244   msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv");
245   msg.readTuple!(typeof(args))().assertEqual(args);
246   DBusMessageIter iter;
247   dbus_message_iter_init(msg.msg, &iter);
248   readIter!int(&iter).assertEqual(5);
249   readIter!bool(&iter).assertEqual(true);
250   readIter!string(&iter).assertEqual("wow");
251   readIter!double(&iter).assertEqual(5.9);
252   readIter!(int[])(&iter).assertEqual([6,5]);
253   readIter!(Tuple!(double,int,string[][],bool[],Variant!(int[])))(&iter).assertEqual(tuple(6.2,4,[["lol"]],emptyB,var([4,2])));
254   readIter!(string[string])(&iter).assertEqual(["hello": "world"]);
255   readIter!DBusAny(&iter).assertEqual(anyVar);
256   readIter!(Variant!DBusAny)(&iter).assertEqual(complexVar);
257 }