1 /*******************************************************************************
2  * Main library import for dproto
3  *
4  * Provides accessors for D string and D structs from proto files/data
5  *
6  * Authors: Matthew Soucy, msoucy@csh.rit.edu
7  * Date: Oct 5, 2013
8  * Version: 0.0.2
9  */
10 module dproto.dproto;
11 
12 import std.exception : enforce;
13 
14 /*******************************************************************************
15  * Create D structures from proto file
16  *
17  * Creates all required structs given a valid proto file
18  *
19  * Assumes that the file can be found in the string imports
20  */
21 template ProtocolBuffer(string s)
22 {
23 	import dproto.buffers;
24 	import dproto.exception;
25 	import dproto.serialize;
26 	import dproto.parse;
27 	mixin(ParseProtoSchema(s,import(s)).toD());
28 }
29 
30 /*******************************************************************************
31  * Create D structure strings from proto data
32  *
33  * Creates all required structs given a valid proto definition as a string
34  */
35 template ProtocolBufferFromString(string s)
36 {
37 	import dproto.buffers;
38 	import dproto.exception;
39 	import dproto.serialize;
40 	import dproto.parse;
41 	mixin(ParseProtoSchema("<none>",s).toD());
42 }
43 
44 unittest
45 {
46     assert(__traits(compiles,ProtocolBufferFromString!"message Test
47         {
48             optional string verySimple = 1;
49         }"));
50 }
51 
52 unittest
53 {
54     assert(__traits(compiles, ProtocolBufferFromString!"
55         message Test
56         {
57             optional string verySimple = 1;
58             enum TestEnum
59             {
60                 ONE = 1;
61                 UNO = 1;
62                 TOW = 2;
63             }
64         }"));
65 }
66 
67 unittest
68 {
69     mixin ProtocolBufferFromString!"
70         message Test
71         {
72             required int32 id = 1;
73             optional string verySimple = 2;
74             enum TestEnum
75             {
76                 ONE = 1;
77                 UNO = 1;
78                 TOW = 2;
79             }
80             optional TestEnum testValue = 3;
81         }";
82 }
83 
84 unittest
85 {
86     assert(__traits(compiles, ProtocolBufferFromString!"message Test
87         {
88             optional string verySimple = 1;
89             enum TestEnum
90             {
91                 ONE = 1;
92                 UNO = 1;
93                 TOW = 2;
94             }
95 
96             optional string testValue = 2;
97         }"));
98 }
99 
100 unittest
101 {
102     assert(__traits(compiles, ProtocolBufferFromString!"
103     message Test
104     {
105         optional string verySimple = 1;
106         message NestedTest
107         {
108             optional string verySimple = 1;
109         }
110 
111         optional NestedTest value = 2;
112     }"));
113 }
114 
115 unittest
116 {
117     assert(__traits(compiles, ProtocolBufferFromString!"
118     message Test
119     {
120         optional string verySimple = 1;
121         message NestedTest
122         {
123             optional string verySimple2 = 1;
124         }
125 
126         optional NestedTest value = 2;
127     }"));
128 }
129 
130 unittest
131 {
132     assert(__traits(compiles, ProtocolBufferFromString!"
133     message Test
134     {
135         optional string verySimple = 1;
136         message NestedTest
137         {
138             optional string verySimple = 1;
139         }
140 
141         repeated NestedTest value = 2;
142     }"));
143 }
144 
145 unittest
146 {
147     assert(__traits(compiles, ProtocolBufferFromString!"
148     message Test
149     {
150         required int32 id = 3;
151         optional string verySimple = 1;
152         message NestedTest
153         {
154             required string verySimple = 1;
155         }
156 
157         required NestedTest value = 2;
158     }"));
159 }
160 
161 unittest
162 {
163     assert(__traits(compiles, ProtocolBufferFromString!"
164     message Test
165     {
166         required int32 id = 3;
167         optional string verySimple = 1;
168         message NestedTest
169         {
170             required string verySimple = 1;
171         }
172 
173         repeated NestedTest value = 2;
174     }"));
175 }
176 
177 unittest
178 {
179     assert(__traits(compiles, ProtocolBufferFromString!"
180     message Test
181     {
182         required int32 id = 3;
183         optional string verySimple = 1;
184         message NestedTest
185         {
186             required string verySimple = 1;
187         }
188 
189         optional NestedTest value = 2;
190     }"));
191 }
192 
193 unittest
194 {
195     assert(__traits(compiles, ProtocolBufferFromString!"
196     message Test
197     {
198         required int32 id = 3;
199         optional string verySimple = 1;
200         message NestedTest
201         {
202             required string verySimple = 1;
203         }
204 
205         repeated NestedTest value = 2;
206     }"));
207 }
208 
209 unittest
210 {
211     mixin ProtocolBufferFromString!"
212 enum PhoneType {
213   MOBILE = 0;
214   HOME = 0;
215   WORK = 2;
216 }
217 
218 message Person {
219   required string name = 1;
220   required int32 id = 2;
221   optional string email = 3;
222 
223   message PhoneNumber {
224     required string number = 1;
225     optional PhoneType type = 2 [default = HOME];
226   }
227 
228   repeated PhoneNumber phone = 4;
229 }
230     ";
231 
232     Person t;
233     assert(t.name == "");
234     assert(t.id == 0);
235     assert(t.phone.length == 0);
236 
237     t.name = "Max Musterman";
238     assert(t.name == "Max Musterman");
239 
240     t.id = 3;
241     assert(t.id == 3);
242 
243     t.email = "Max.Musterman@example.com";
244     assert(t.email == "Max.Musterman@example.com");
245     assert(t.email.exists());
246 
247     Person.PhoneNumber pn1;
248     pn1.number = "0123456789";
249     assert(pn1.number == "0123456789");
250     assert(pn1.type == PhoneType.HOME);
251     assert(pn1.type == PhoneType.MOBILE);
252 
253     pn1.type = PhoneType.WORK;
254     assert(pn1.type == PhoneType.WORK);
255     assert(pn1.type == 2);
256     assert(pn1.type.exists());
257 
258     t.phone ~= pn1;
259     assert(t.phone[0] == pn1);
260     assert(t.phone.length == 1);
261 
262     pn1.type.clean();
263     assert(pn1.type == PhoneType.HOME);
264 
265     t.phone.clean();
266     assert(t.phone.length == 0);
267 
268     t.email.clean();
269     assert(t.email == "");
270 }
271 
272 //This unittest fails with DMD I don't know how to fix it. In the main function of a Project it works.
273 //Bjarne Leif Bruhn 2013-11-24
274 version(GNU)
275 {
276     unittest
277     {
278         mixin ProtocolBufferFromString!"
279             message Person {
280               required string name = 1;
281               required int32 id = 2;
282               optional string email = 3;
283 
284               enum PhoneType {
285                 MOBILE = 0;
286                 HOME = 0;
287                 WORK = 2;
288               }
289 
290               message PhoneNumber {
291                 required string number = 1;
292                 optional PhoneType type = 2 [default = HOME];
293               }
294 
295               repeated PhoneNumber phone = 4;
296             }
297 
298             message AddressBook {
299               repeated Person person = 1;
300             }
301         ";
302 
303          Person t;
304         assert(t.name == "");
305         assert(t.id == 0);
306         assert(t.test.length == 0);
307 
308         t.name = "Max Musterman";
309         assert(t.name == "Max Musterman");
310 
311         t.id = 3;
312         assert(t.id == 3);
313 
314         t.email = "Max.Musterman@example.com";
315         assert(t.email == "Max.Musterman@example.com");
316         assert(t.email.exists());
317 
318         Person.PhoneNumber pn1;
319         pn1.number = "0123456789";
320         assert(pn1.number == "0123456789");
321 
322         t.test ~= pn1;
323         assert(t.test[0] == pn1);
324         assert(t.test.length == 1);
325 
326         t.test.clean();
327         assert(t.test.length == 0);
328 
329         t.email.clean();
330         assert(t.email == "");
331 
332         AddressBook addressbook;
333         assert(addressbook.person.length == 0);
334         addressbook.person ~= t;
335         addressbook.person ~= t;
336         assert(addressbook.person[0] == t);
337         assert(addressbook.person[0] == addressbook.person[1]);
338         assert(addressbook.person.length == 2);
339    }
340 
341     unittest
342     {
343          mixin ProtocolBufferFromString!"
344     enum PhoneType {
345       MOBILE = 0;
346       HOME = 0;
347       WORK = 2;
348     }
349 
350     message Person {
351       required string name = 1;
352       required int32 id = 2;
353       optional string email = 3;
354 
355       message PhoneNumber {
356         required string number = 1;
357         optional PhoneType type = 2 [default = HOME];
358       }
359 
360       repeated PhoneNumber phone = 4;
361     }
362 
363     message AddressBook {
364       repeated Person person = 1;
365     }
366         ";
367 
368         Person t;
369         t.name = "Max Musterman";
370         t.id = 3;
371         t.email = "test@example.com";
372 
373         Person.PhoneNumber pn1;
374         pn1.number = "0123456789";
375         pn1.type = PhoneType.WORK;
376 
377         Person.PhoneNumber pn2;
378         pn2.number = "0123456789";
379 
380         t.phone = [pn1, pn2];
381         AddressBook addressbook;
382         addressbook.person ~= t;
383         addressbook.person ~= t;
384 
385         ubyte[] serializedObject = addressbook.serialize();
386 
387         AddressBook addressbook2 = AddressBook(serializedObject);
388         assert(addressbook2.person.length == 2);
389         foreach (t2; addressbook2.person[0])
390         {
391             assert(t2.name == "Max Musterman");
392             assert(t2.id == 3);
393             assert(t2.email == "test@example.com");
394             assert(t2.email.exists());
395             assert(t2.phone[0].number == "0123456789");
396             assert(t2.phone[0].type == PhoneType.WORK);
397             assert(t2.phone[1].number == "0123456789");
398             assert(t2.phone[1].type == PhoneType.HOME);
399             assert(t2.phone[1].type == PhoneType.MOBILE);
400             assert(t2.phone.length == 2);
401         }
402         //the gdc-4.8 evaluates false here. Maybe an compiler bug.
403         version(DigitalMars)
404         {
405             assert(addressbook2.person[0] == addressbook.person[1]);
406         }
407    }
408 
409     unittest
410     {
411         mixin ProtocolBufferFromString!"
412     message Person {
413       required string name = 1;
414       required int32 id = 2;
415       optional string email = 3;
416 
417       enum PhoneType {
418         MOBILE = 0;
419         HOME = 0;
420         WORK = 2;
421       }
422 
423       message PhoneNumber {
424         required string number = 1;
425         optional PhoneType type = 2 [default = HOME];
426       }
427 
428       repeated PhoneNumber phone = 4;
429     }
430 
431     message AddressBook {
432       repeated Person person = 1;
433     }
434         ";
435 
436         Person t;
437         assert(t.name == "");
438         assert(t.id == 0);
439         assert(t.phone.length == 0);
440 
441         t.name = "Max Musterman";
442         assert(t.name == "Max Musterman");
443 
444         t.id = 3;
445         assert(t.id == 3);
446 
447         t.email = "Max.Musterman@example.com";
448         assert(t.email == "Max.Musterman@example.com");
449         assert(t.email.exists());
450 
451         Person.PhoneNumber pn1;
452         pn1.number = "0123456789";
453         assert(pn1.number == "0123456789");
454         assert(pn1.type == Person.PhoneType.HOME);
455         assert(pn1.type == Person.PhoneType.MOBILE);
456 
457         pn1.type = Person.PhoneType.WORK;
458         assert(pn1.type == Person.PhoneType.WORK);
459         assert(pn1.type == 2);
460         assert(pn1.type.exists());
461 
462         t.phone ~= pn1;
463         assert(t.phone[0] == pn1);
464         assert(t.phone.length == 1);
465 
466         pn1.type.clean();
467         assert(pn1.type == Person.PhoneType.HOME);
468 
469         t.phone.clean();
470         assert(t.phone.length == 0);
471 
472         t.email.clean();
473         assert(t.email == "");
474 
475         AddressBook addressbook;
476         assert(addressbook.person.length == 0);
477         addressbook.person ~= t;
478         addressbook.person ~= t;
479         assert(addressbook.person[0] == t);
480         assert(addressbook.person[0] == addressbook.person[1]);
481         assert(addressbook.person.length == 2);
482     }
483 }