1 /**
2  * This module provides conversion functionality of different elements
3  * to the D Programming Language which are campatible with version 1.
4  */
5 module dprotobuf.generator.d1lang;
6 
7 import dprotobuf.pbroot;
8 import dprotobuf.pbgeneral;
9 import dprotobuf.pbchild;
10 import dprotobuf.pbenum;
11 import dprotobuf.pbmessage;
12 import dprotobuf.wireformat;
13 import codebuilder.structure;
14 
15 version(D_Version2) {
16 	import std.algorithm;
17 	import std.range;
18 	import std.regex;
19 	mixin(`
20 	version(unittest) {
21 		import std.conv;
22 		string makeString(T)(T v) {
23 			return to!(string)(v);
24 		}
25 		PBMessage PBCompileTime(ParserData pbstring) {
26 			return PBMessage(pbstring);
27 		}
28 		PBEnum PBCTEnum(ParserData pbstring) {
29 			return PBEnum(pbstring);
30 		}
31     }`);
32 
33 }
34 
35 import std.conv;
36 import std..string : format;
37 
38 /*
39  * Appropriately wraps the type based on the option.
40  *
41  * Repeated types are arrays.
42  * All types are nullable
43  */
44 private string typeWrapper(PBChild child) {
45 	if(child.modifier == "repeated")
46 		return format("%s[]", toDType(child.type));
47 	else
48 		return format("%s", toDType(child.type));
49 }
50 
51 string langD1(PBRoot root) {
52 	auto code = CodeBuilder(0);
53     code.put("import dprotobuf.wireformat;\n");
54     code.put("import std.string;\n\n");
55 
56     code.put("version(D_Version2) {\n");
57     code.put("\timport std.conv;\n");
58     code.put("\tstring makeString(T)(T v) {\n");
59     code.put("\t\treturn to!(string)(v);\n");
60     code.put("\t}\n");
61     code.put("} else {\n");
62     code.put("\timport std.string;\n");
63     code.put("\tstring makeString(T)(T v) {\n");
64     code.put("\t\tstatic if(is(T == enum))\n");
65     code.put("\t\t\treturn toString(cast(int)v);\n");
66     code.put("\t\t\telse\n");
67     code.put("\t\t\treturn toString(v);\n");
68     code.put("\t}\n");
69     code.put("}\n");
70 
71     // do what we need for extensions defined here
72     code.put(root.extensions.genExtString(""));
73     // write out enums
74     foreach(pbenum; root.enum_defs) {
75         code.put(toD1(pbenum, 0));
76     }
77     // write out message definitions
78     foreach(pbmsg; root.message_defs) {
79         code.put(toD1(pbmsg, 0));
80     }
81     return code.finalize();
82 }
83 
84 /**
85  */
86 string toD1(PBChild child, int indentCount = 0) {
87 	auto code = CodeBuilder(indentCount);
88 	with(child) {
89 		code.put(toDType(type)~(modifier=="repeated"?"[]_":" _")~
90 			name~(valdefault.length?" = "~valdefault:"")~";\n");
91 
92 		foreach(c; comments)
93 			code.put((c.empty() ? "":"/") ~ c ~ "\n");
94 		if(comments.empty())
95 			code.put("///\n");
96 		auto fieldName = name;
97 		if(isReserved(fieldName))
98 			fieldName = name ~ "_";
99 		// get accessor
100 		code.put((is_dep?"deprecated ":"")~toDType(type)~(modifier==
101 		         "repeated"?"[]":" ")~fieldName~"() {\n", Indent.open);
102 		code.put("return _"~name~";\n");
103 		code.put("}\n", Indent.close);
104 
105 		if(!comments.empty())
106 			code.put("/// ditto\n");
107 		else
108 			code.put("///\n");
109 		// set accessor
110 		code.put((is_dep?"deprecated ":"")~"void "~fieldName~"("~toDType(type)~
111 		         (modifier=="repeated"?"[]":" ")~"input_var) {\n", Indent.open);
112 		code.put("_"~name~" = input_var;\n");
113 
114 		if (modifier != "repeated") code.put("_has_"~name~" = true;\n");
115 		code.put("}\n", Indent.close);
116 		if (modifier == "repeated") {
117 			code.put((is_dep?"deprecated ":"")~"bool has_"~name~" () {\n",
118 			         Indent.open);
119 			code.put("return _"~name~".length?1:0;\n");
120 			code.put("}\n", Indent.close);
121 			code.put((is_dep?"deprecated ":"")~"void clear_"~name~" () {\n",
122 			         Indent.open);
123 			code.put("_"~name~" = null;\n");
124 			code.put("}\n", Indent.close);
125 			// technically, they can just do class.item.length
126 			// there is no need for this
127 			code.put((is_dep?"deprecated ":"")~"size_t "~name~"_size () {\n",
128 			         Indent.open);
129 			code.put("return _"~name~".length;\n");
130 			code.put("}\n", Indent.close);
131 			// functions to do additions, both singular and array
132 			code.put((is_dep?"deprecated ":"")~"void add_"~name~" ("~
133 			         toDType(type)~" __addme) {\n", Indent.open);
134 			code.put("_"~name~" ~= __addme;\n");
135 			code.put("}\n", Indent.close);
136 			code.put((is_dep?"deprecated ":"")~"void add_"~name~" ("~
137 			         toDType(type)~"[]__addme) {\n", Indent.open);
138 			code.put("_"~name~" ~= __addme;\n");
139 			code.put("}\n", Indent.close);
140 		} else {
141 			code.put("bool _has_"~name~" = false;\n");
142 			code.put((is_dep?"deprecated ":"")~"bool has_"~name~" () {\n",
143 			         Indent.open);
144 			code.put("	return _has_"~name~";\n");
145 			code.put("}\n", Indent.close);
146 			code.put((is_dep?"deprecated ":"")~"void clear_"~name~" () {\n",
147 			         Indent.open);
148 			code.put("	_has_"~name~" = false;\n");
149 			code.put("}\n", Indent.close);
150 		}
151     }
152 	return code.finalize();
153 }
154 
155 version(D_Version2)
156 unittest {
157 	PBChild child;
158 
159 	// Conversion for optional
160 	auto str = ParserData("optional HeaderBBox bbox = 1;");
161 	child = PBChild(str);
162     assert(!child.toD1().find(r"bbox(HeaderBBox").empty);
163     assert(!child.toD1().find(r"bbox()").empty);
164     assert(!child.toD1().find(r"has_bbox").empty);
165     assert(!child.toD1().find(r"clear_bbox").empty);
166 
167 	// Conversion for repeated
168 	str = ParserData("repeated HeaderBBox bbox = 1;");
169 	child = PBChild(str);
170     assert(!child.toD1().find(r"bbox(HeaderBBox[]").empty);
171     assert(!child.toD1().find(r"bbox()").empty);
172     assert(!child.toD1().find(r"has_bbox").empty);
173     assert(!child.toD1().find(r"clear_bbox").empty);
174     assert(!child.toD1().find(r"add_bbox").empty);
175 
176 	// Conversion for required
177 	str = ParserData("required int32 value = 1;");
178 	child = PBChild(str);
179 
180 	// Conversion for default value
181 	str = ParserData("required int64 value = 1 [default=6]; ");
182 	child = PBChild(str);
183 	assert(child.toD1().startsWith(r"long _value = 6;"));
184 
185 	// Conversion for default, negative, deprecated value
186 	str = ParserData("optional int64 value = 1 [default=-32,deprecated=true];");
187 	child = PBChild(str);
188 	assert(child.toD1().startsWith(r"long _value = -32;"));
189 	assert(!child.toD1().find(r"deprecated long value()").empty);
190 
191 	// Conversion for commented, indented
192 	str = ParserData("optional HeaderBBox bbox = 1;");
193 	child = PBChild(str);
194 	child.comments ~= "// This is a comment";
195 	assert(!child.toD1().find(r"/// This is a comment").empty);
196 	assert(!child.toD1().find(r"/// ditto").empty);
197 }
198 
199 CodeBuilder genExtenCode(PBChild child, int indentCount) {
200     auto code = CodeBuilder(indentCount);
201     with(child) {
202         auto extName = "__exten_"~name;
203 
204         if(is_dep) code.put("deprecated ");
205         else code.put("");
206         code.rawPut(typeWrapper(child));
207         code.rawPut(extName);
208         if(!empty(valdefault)) // Apply default value
209             code.rawPut(" = " ~ valdefault);
210         code.rawPut(";\n");
211 
212         // get accessor
213         if(is_dep) code.put("deprecated ");
214         else code.put("");
215         code.rawPut(toDType(type));
216         code.rawPut("GetExtension(int T:");
217         code.rawPut(to!(string)(index)~")() {\n", Indent.open);
218         code.put("return "~extName~";\n");
219         code.put("}\n", Indent.close);
220         // set accessor
221         if(is_dep) code.put("deprecated ");
222         else code.put("");
223         code.rawPut("void SetExtension(int T:");
224         code.rawPut(to!(string)(index)~")("~toDType(type)~"input_var) {\n");
225         code.put(Indent.open);
226         code.put(extName~" = input_var;\n");
227         if (modifier != "repeated")
228             code.put("_has"~extName~" = true;\n");
229         code.put("}\n", Indent.close);
230 
231         if (modifier == "repeated") {
232             if(is_dep) code.put("deprecated ");
233             else code.put("");
234             code.rawPut("bool HasExtension(int T:");
235             code.rawPut(to!(string)(index)~")() {\n", Indent.open);
236             code.put("return !"~extName~".isNull;\n");
237             code.put("}\n", Indent.close);
238             if(is_dep) code.put("deprecated ");
239             else code.put("");
240             code.rawPut("void ClearExtension(int T:");
241             code.rawPut(to!(string)(index)~")() {\n", Indent.open);
242             code.put(extName~" = null;\n");
243             code.put("}\n", Indent.close);
244             // technically, they can just do class.item.length
245             // there is no need for this
246             if(is_dep) code.put("deprecated ");
247             else code.put("");
248             code.rawPut("int ExtensionSize(int T:");
249             code.rawPut(to!(string)(index)~")() {\n", Indent.open);
250             code.put("return "~extName~".length;\n");
251             code.put("}\n", Indent.close);
252             // functions to do additions, both singular and array
253             if(is_dep) code.put("deprecated ");
254             else code.put("");
255             code.rawPut("void AddExtension(int T:");
256             code.rawPut(to!(string)(index)~")("~toDType(type)~" __addme) {\n");
257             code.put(Indent.open);
258             code.put(extName~" ~= __addme;\n");
259             code.put("}\n", Indent.close);
260             if(is_dep) code.put("deprecated ");
261             else code.put("");
262             code.rawPut("void AddExtension(int T:");
263             code.rawPut(to!(string)(index)~")("~toDType(type)~"[]__addme) {\n");
264             code.put(Indent.open);
265             code.put(extName~" ~= __addme;\n");
266             code.put("}\n", Indent.close);
267         } else {
268             code.put("bool _has"~extName~" = false;\n");
269             if(is_dep) code.put("deprecated ");
270             else code.put("");
271             code.rawPut("bool HasExtension(int T:");
272             code.rawPut(to!(string)(index)~")() {\n", Indent.open);
273             code.put("return _has"~extName~";\n");
274             code.put("}\n", Indent.close);
275             if(is_dep) code.put("deprecated ");
276             else code.put("");
277             code.rawPut("void ClearExtension(int T:");
278             code.rawPut(to!(string)(index)~")() {\n", Indent.open);
279             code.put("_has"~extName~" = false;\n");
280             code.put("}\n", Indent.close);
281         }
282         return code;
283     }
284 }
285 
286 private string constructMismatchException(string type, int indentCount) {
287 	auto code = CodeBuilder(indentCount);
288 	code.put("throw new Exception(\"Invalid wiretype \" ~\n");
289 	code.put("   makeString(wireType) ~\n");
290 	code.put("   \" for variable type "~type~"\");\n\n");
291 	return code.finalize();
292 }
293 
294 private string constructUndecided(PBChild child, int indentCount, CodeBuilder tname, CodeBuilder append_open, CodeBuilder append_close) {
295 	auto code = CodeBuilder(indentCount);
296 	// this covers enums and classes,
297 	// since enums are declared as classes
298 	// also, make sure we don't think we're root
299 	with(child) {
300 		code.put("static if (is("~type~":Object)) {\n", Indent.open);
301 		auto closeIf = CodeBuilder(1);
302 		closeIf.put("} else\n", Indent.close | Indent.open);
303 		closeIf.put("static assert(0,\n");
304 		closeIf.put("  \"Can't identify type `" ~ type ~ "`\");\n");
305 		closeIf.put(Indent.close);
306 		code.push(closeIf);
307 
308 		code.put("if(wireType != WireType.lenDelimited)\n", Indent.open);
309 		code.rawPut(constructMismatchException(type, code.indentCount));
310 		code.put(Indent.close);
311 
312 		// no need to worry about packedness here, since it can't be
313 		code.put(append_open);
314 		code.rawPut(type~".Deserialize(input,false)");
315 		code.put(append_close);
316 
317 		code.put("} else static if (is("~type~" == enum)) {\n",
318 		         Indent.close | Indent.open);
319 
320 		// worry about packedness here
321 		code.put("if (wireType == WireType.varint) {\n", Indent.open);
322 		closeIf = CodeBuilder(1);
323 		closeIf.put("} else\n", Indent.close | Indent.open);
324 		closeIf.rawPut(constructMismatchException(type, code.indentCount));
325 		closeIf.put(Indent.close);
326 		code.push(closeIf);
327 
328 		code.put(append_open);
329 		code.rawPut("cast("~toDType(type)~")\n");
330 		code.put("   fromVarint!(int)(input)");
331 		code.put(append_close);
332 		if (modifier == "repeated") {
333 			code.put("} else if (wireType == WireType.lenDelimited) {\n",
334 			         Indent.close | Indent.open);
335 			code.put("// Accept packed enum even if not specified as packed\n");
336 			code.put(tname);
337 			code.rawPut("fromPacked!("~toDType(type));
338 			code.rawPut(",fromVarint!(int))(input);\n");
339 		}
340 	}
341 	return code.finalize();
342 }
343 
344 string genDes(PBChild child, int indentCount = 0, bool is_exten = false) {
345 	auto code = CodeBuilder(indentCount);
346 	with(child) {
347 		if(type == "group")
348 			throw new Exception("Group type not supported");
349 		string tname = name;
350 		if (is_exten) tname = "__exten"~tname;
351 		auto append_open = CodeBuilder(0);
352 		auto append_close = CodeBuilder(0);
353 		auto tnameCode = CodeBuilder(0);
354 		if(modifier == "repeated") {
355 			append_open.put("add_");
356 			append_open.rawPut(tname);
357 			append_open.rawPut("(");
358 			append_close.rawPut(");\n");
359 
360 			tnameCode.put(tname);
361 			if(isReserved(tname))
362 				tnameCode.rawPut("_");
363 			tnameCode.rawPut(" = ");
364 		} else {
365 			append_open.put(tname);
366 			if(isReserved(tname))
367 				 append_open.rawPut("_");
368 			append_open.rawPut(" = ");
369 			append_close.rawPut(";\n");
370 		}
371 		// check header ubyte with case since we're guaranteed to be in a switch
372 		code.put("case "~to!(string)(index)~":", Indent.open);
373 		code.push("break;\n");
374 
375 		code.rawPut("// Deserialize member ");
376 		code.rawPut(to!(string)(index));
377 		code.rawPut(" Field Name " ~ name ~ "\n");
378 
379 		// Class and Enum will have an undecided type
380 		if(wTFromType(type) == WireType.undecided) {
381 			code.rawPut(constructUndecided(child, indentCount, tnameCode, append_open, append_close));
382 			return code.finalize();
383 		}
384 
385 		// Handle Packed type
386 		if (packed) {
387 			assert(modifier == "repeated");
388 			assert(isPackable(type));
389 			// Allow reading data even when not packed
390 			code.put("if (wireType != WireType.lenDelimited)\n", Indent.open);
391 			code.push(Indent.close);
392 		}
393 
394 		// Verify wire type is expected type else
395 		// this is not condoned, wiretype is invalid, so explode!
396 		code.put("if (wireType != " ~
397 			to!(string)(cast(byte)wTFromType(type))~")\n", Indent.open);
398 		code.rawPut(constructMismatchException(type, code.indentCount));
399 		code.put(Indent.close);
400 
401 		if (packed)
402 			code.pop();
403 
404 		string pack;
405 		switch(type) {
406 		case "float","double","sfixed32","sfixed64","fixed32","fixed64":
407 			pack = "fromByteBlob!("~toDType(type)~")";
408 			break;
409 		case "bool","int32","int64","uint32","uint64":
410 			pack = "fromVarint!("~toDType(type)~")";
411 			break;
412 		case "sint32","sint64":
413 			pack = "fromSInt!("~toDType(type)~")";
414 			break;
415 		case "string","bytes":
416 			// no need to worry about packedness here, since it can't be
417 			code.put(append_open);
418 			code.rawPut("fromByteString!("~toDType(type)~")(input)");
419 			code.put(append_close);
420 			return code.finalize();
421 		default:
422 			assert(0, "class/enum/group handled by undecided type.");
423 		}
424 
425 		if(packed) {
426 			code.put("if (wireType == WireType.lenDelimited) {\n", Indent.open);
427 			code.put(append_open);
428 			code.rawPut("fromPacked!("~toDType(type)~","~pack~")(input)");
429 			code.put(append_close);
430 			code.put("//Accept data even when not packed\n");
431 			code.put("} else {\n", Indent.close | Indent.open);
432 			code.push("}\n");
433 		}
434 
435 		code.put(append_open);
436 		code.rawPut(pack~"(input)");
437 		code.put(append_close);
438 
439 		return code.finalize();
440 	}
441 }
442 
443 string genSer(PBChild child, int indentCount = 0, bool is_exten = false) {
444 	auto code = CodeBuilder(indentCount);
445 	with(child) {
446 		if(type == "group")
447 			throw new Exception("Group type not supported");
448 		code.put("// Serialize member ");
449 		code.rawPut(to!(string)(index));
450 		code.rawPut(" Field Name " ~ name ~ "\n");
451 
452 		string func;
453 		bool customType = false;
454 		switch(type) {
455 		case "float","double","sfixed32","sfixed64","fixed32","fixed64":
456 			func = "toByteBlob";
457 			break;
458 		case "bool","int32","int64","uint32","uint64":
459 			func = "toVarint";
460 			break;
461 		case "sint32","sint64":
462 			func = "toSInt";
463 			break;
464 		case "string","bytes":
465 			// the checks ensure that these can never be packed
466 			func = "toByteString";
467 			break;
468 		default:
469 			// this covers defined messages and enums
470 			func = "toVarint";
471 			customType = true;
472 			break;
473 		}
474 
475 		auto tname = name;
476 		if (is_exten) tname = "__exten" ~ name;
477 		auto funName = tname;
478 
479 		if(isReserved((is_exten?"__exten":"") ~ name))
480 			tname ~= "_";
481 
482 		if (modifier == "repeated" && !packed) {
483 			code.put("foreach(iter;");
484 			code.rawPut(tname);
485 			code.rawPut(") {\n", Indent.open);
486 			code.push("}\n");
487 			tname = "iter";
488 		}
489 
490 		// we have to have some specialized code to deal with enums vs
491 		// user-defined classes, since they are both detected the same
492 		if (customType) {
493 			code.put("static if (is("~type~" : Object)) {\n", Indent.open);
494 			auto closeIf = CodeBuilder(1);
495 			closeIf.put("} else\n", Indent.close | Indent.open);
496 			closeIf.put("static assert(0,\"Can't identify type `");
497 			closeIf.rawPut(type ~ "`\");\n");
498 			closeIf.put(Indent.close);
499 			code.push(closeIf);
500 
501 			// packed only works for primitive types, so take care of normal
502 			// repeated serialization here since we can't easily detect this
503 			// without decent type resolution in the .proto parser
504 			if (packed) {
505 				assert(modifier == "repeated");
506 				code.put("foreach(iter;");
507 				code.rawPut(tname);
508 				code.put(") {\n", Indent.open);
509 				code.push("}\n");
510 			}
511 			code.put("ret ~= ");
512 			if(packed)
513 				code.rawPut("iter");
514 			else
515 				code.rawPut(tname);
516 			code.rawPut(".Serialize("~to!(string)(index)~");\n");
517 			if (packed)
518 				code.pop();
519 
520 			// done taking care of unpackable classes
521 			code.put("} else static if (is("~type~" == enum)) {\n",
522 			         Indent.close | Indent.open);
523 		}
524 		// take care of packed circumstances
525 		if (packed) {
526 			assert(modifier == "repeated");
527 			auto packType = toDType(type);
528 			if(customType)
529 				packType = "int";
530 			code.put("if(has_");
531 			code.rawPut(funName);
532 			code.rawPut(")\n", Indent.open);
533 			code.put("ret ~= toPacked!("~packType~"[],"~func~")");
534 			code.put(Indent.close);
535 		} else if (modifier != "required" && modifier != "repeated") {
536 			code.put("if (has_");
537 			code.rawPut(funName);
538 			code.rawPut(") ");
539 		} else
540 			code.put(""); // Adds indenting
541 
542 		if(!packed)
543 			code.rawPut("ret ~= "~func);
544 		// finish off the parameters, because they're the same for packed or not
545 		if(customType) {
546 			if(packed)
547 				code.rawPut("(cast(int[])(");
548 			else
549 				code.rawPut("(cast(int)(");
550 			code.rawPush("),", Indent.none);
551 		} else {
552 			code.rawPut("(");
553 			code.rawPush(",", Indent.none);
554 		}
555 
556 		code.put(tname);
557 		code.pop();
558 		code.rawPut(to!(string)(index)~");\n");
559 	}
560 	return code.finalize();
561 }
562 
563 /**
564  */
565 string toD1(PBEnum child, int indentCount = 0) {
566 	auto code = CodeBuilder(indentCount);
567 	with(child) {
568 		// Apply comments to enum
569 		foreach(c; comments)
570 			code.put((c.empty() ? "":"/") ~ c ~ "\n");
571 
572 		code.put("enum "~name~" {\n", Indent.open);
573 		code.push("}\n");
574 		foreach (key, value; values) {
575 			// Apply comments to field
576 			if(key in valueComments)
577 				foreach(c; valueComments[key])
578 					code.put("/" ~ c ~ "\n");
579 
580 			code.put(value~" = "~to!(string)(key)~",\n");
581 		}
582 	}
583 	return code.finalize();
584 }
585 
586 version(D_Version2)
587 unittest {
588 	auto str = ParserData("enum potato {TOTALS = 1;JUNK= 5 ; ALL =3;}");
589 	auto enm = PBEnum(str);
590 	auto ans = regex(r"enum potato \{\n" ~
591 r"\t\w{3,6} = \d,\n" ~
592 r"\t\w{3,6} = \d,\n" ~
593 r"\t\w{3,6} = \d,\n\}");
594     assert(!enm.toD1.match(ans).empty, enm.toD1);
595     assert(!enm.toD1.find(r"TOTALS = 1").empty);
596     assert(!enm.toD1.find(r"ALL = 3").empty);
597     assert(!enm.toD1.find(r"JUNK = 5").empty);
598 
599 	// Conversion for commented, indented
600 	str = ParserData("enum potato {\n// The total\nTOTALS = 1;}");
601 	enm = PBEnum(str);
602 	enm.comments ~= "// My food";
603 	ans = regex(r"\t/// My food\n"
604 r"\tenum potato \{\n" ~
605 r"\t\t/// The total\n" ~
606 r"\t\tTOTALS = \d,\n" ~
607 r"\t\}");
608     assert(!enm.toD1(1).match(ans).empty);
609 }
610 
611 string genDes(PBMessage msg, int indentCount = 0) {
612 	auto indent = indented(indentCount);
613 	string ret = "";
614 	with(msg) {
615 		// add comments
616 		ret ~= indent~"// if we're root, we can assume we own the whole string\n";
617 		ret ~= indent~"// if not, the first thing we need to do is pull the length that belongs to us\n";
618 		ret ~= indent~"static "~name~" Deserialize(ref ubyte[] manip, bool isroot=true) {return new "~name~"(manip,isroot);}\n";
619 		ret ~= indent~"this() { }\n";
620 		ret ~= indent~"this(ref ubyte[] manip,bool isroot=true) {\n";
621 		indent = indented(++indentCount);
622 		ret ~= indent~"ubyte[] input = manip;\n";
623 
624 		ret ~= indent~"// cut apart the input string\n";
625 		ret ~= indent~"if (!isroot) {\n";
626 		indent = indented(++indentCount);
627 		ret ~= indent~"uint len = fromVarint!(uint)(manip);\n";
628 		ret ~= indent~"input = manip[0..len];\n";
629 		ret ~= indent~"manip = manip[len..$];\n";
630 		indent = indented(--indentCount);
631 		ret ~= indent~"}\n";
632 
633 		// deserialization code goes here
634 		ret ~= indent~"while(input.length) {\n";
635 		indent = indented(++indentCount);
636 		ret ~= indent~"int header = fromVarint!(int)(input);\n";
637 		ret ~= indent~"auto wireType = getWireType(header);\n";
638 		ret ~= indent~"switch(getFieldNumber(header)) {\n";
639 		//here goes the meat, handily, it is generated in the children
640 		foreach(pbchild;children) {
641 			ret ~= genDes(pbchild, indentCount);
642 		}
643 		foreach(pbchild;child_exten) {
644 			ret ~= genDes(pbchild, indentCount, true);
645 		}
646 		// take care of default case
647 		ret ~= indent~"default:\n";
648 		ret ~= indent~"	// rip off unknown fields\n";
649 		ret ~= indent~"if(input.length)\n";
650 		ret ~= indented(indentCount+1)~"ufields ~= toVarint(header)~\n";
651 		ret ~= indented(indentCount+1)~
652 			"   ripUField(input,getWireType(header));\n";
653 		ret ~= indent~"	break;\n";
654 		ret ~= indent~"}\n";
655 		indent = indented(--indentCount);
656 		ret ~= indent~"}\n";
657 
658 		// check for required fields
659 		foreach(pbchild;child_exten) if (pbchild.modifier == "required") {
660 			ret ~= indent~"if (_has__exten_"~pbchild.name~" == false) throw new Exception(\"Did not find a "~pbchild.name~" in the message parse.\");\n";
661 		}
662 		foreach(pbchild;children) if (pbchild.modifier == "required") {
663 			ret ~= indent~"if (!_has_"~pbchild.name~") throw new Exception(\"Did not find a "~pbchild.name~" in the message parse.\");\n";
664 		}
665 		indent = indented(--indentCount);
666 		ret ~= indent~"}\n";
667 		return ret;
668 	}
669 }
670 
671 string genSer(PBMessage msg, int indentCount = 0) {
672 	auto indent = indented(indentCount);
673 	string ret = "";
674 	with(msg) {
675 		// use -1 as a default value, since a nibble can not produce that number
676 		ret ~= indent~"ubyte[] Serialize(int field = -1) {\n";
677 		indent = indented(++indentCount);
678 		// codegen is fun!
679 		ret ~= indent~"ubyte[] ret;\n";
680 		// serialization code goes here
681 		foreach(pbchild;children) {
682 			ret ~= genSer(pbchild, indentCount);
683 		}
684 		foreach(pbchild;child_exten) {
685 			ret ~= genSer(pbchild, indentCount,true);
686 		}
687 		// tack on unknown bytes
688 		ret ~= indent~"ret ~= ufields;\n";
689 
690 		// include code to determine if we need to add a tag and a length
691 		ret ~= indent~"// take care of header and length generation if necessary\n";
692 		ret ~= indent~"if (field != -1) {\n";
693 		// take care of length calculation and integration of header and length
694 		ret ~= indented(indentCount+1)~"ret = genHeader(field,WireType.lenDelimited)~toVarint(ret.length,field)[1..$]~ret;\n";
695 		ret ~= indent~"}\n";
696 
697 		ret ~= indent~"return ret;\n";
698 		indent = indented(--indentCount);
699 		ret ~= indent~"}\n";
700 	}
701 	return ret;
702 }
703 string genMerge(PBMessage msg, int indentCount = 0, bool is_exten = false) {
704 	auto indent = indented(indentCount);
705 	string ret = "";
706 	with(msg) {
707 		ret ~= indent~"void MergeFrom("~name~" merger) {\n";
708 		indent = indented(++indentCount);
709 		// merge code
710 		foreach(pbchild;children) {
711 			string tname = pbchild.name;
712 			if (is_exten) tname = "__exten"~tname;
713 			auto nameForAdd = tname;
714 			if(isReserved(tname)) {
715 				tname = tname ~ "_";
716 			}
717 
718 			if (pbchild.modifier != "repeated") {
719 				ret ~= indent~"if (merger.has_"~nameForAdd~") "~
720 					tname~" = merger."~tname~";\n";
721 			} else {
722 				ret ~= indent~"if (merger.has_"~nameForAdd~") add_"~
723 					nameForAdd~"(merger."~tname~");\n";
724 			}
725 		}
726 		indent = indented(--indentCount);
727 		ret ~= indent~"}\n";
728 		return ret;
729 	}
730 }
731 
732 /**
733  */
734 string toD1(PBMessage msg, int indentCount = 0) {
735 	auto indent = indented(indentCount);
736 	string ret = "";
737 	with(msg) {
738 		foreach(c; comments)
739 			ret ~= indent ~ (c.empty() ? "":"/") ~ c ~ "\n";
740 		ret ~= indent~(indent.length?"static ":"")~"class "~name~" {\n";
741 		indent = indented(++indentCount);
742 		ret ~= indent~"// deal with unknown fields\n";
743 		ret ~= indent~"ubyte[] ufields;\n";
744 		// fill the class with goodies!
745 		// first, we'll do the enums!
746 		foreach(pbenum;enum_defs) {
747 			ret ~= toD1(pbenum, indentCount);
748 			ret ~= "\n\n";
749 		}
750 		// now, we'll do the nested messages
751 		foreach(pbmsg;message_defs) {
752 			ret ~= toD1(pbmsg, indentCount);
753 			ret ~= "\n\n";
754 		}
755 		// do the individual instantiations
756 		foreach(pbchild;children) {
757 			ret ~= toD1(pbchild, indentCount);
758 			ret ~= "\n";
759 		}
760 		// last, do the extension instantiations
761 		foreach(pbchild;child_exten) {
762 			ret ~= genExtenCode(pbchild, indentCount).finalize;
763 			ret ~= "\n";
764 		}
765 		ret ~= "\n";
766 		// here is where we add the code to serialize and deserialize
767 		ret ~= genSer(msg, indentCount);
768 		ret ~= "\n";
769 		ret ~= genDes(msg, indentCount);
770 		ret ~= "\n";
771 		// define merging function
772 		ret ~= genMerge(msg, indentCount);
773 		ret ~= "\n";
774 		// deal with what little we need to do for extensions
775 		ret ~= extensions.genExtString(indent~"static ");
776 
777 		// guaranteed to work, since we tack on a tab earlier
778 		indent = indented(--indentCount);
779 		ret ~= indent~"}\n";
780 	}
781 	return ret;
782 }
783 
784 bool isReserved(string field) {
785 	string[] words = [
786 "Error", "Exception", "Object", "Throwable", "__argTypes", "__ctfe",
787 	"__gshared", "__monitor", "__overloadset", "__simd", "__traits",
788 	"__vector", "__vptr", "_argptr", "_arguments", "_ctor", "_dtor",
789 	"abstract", "alias", "align", "assert", "auto", "body", "bool", "break",
790 	"byte", "cast", "catch", "cdouble", "cent", "cfloat", "char", "class",
791 	"const", "contained", "continue", "creal", "dchar", "debug", "delegate",
792 	"delete", "deprecated", "do", "double", "dstring", "else", "enum",
793 	"export", "extern", "false", "final", "finally", "float", "float", "for",
794 	"foreach", "foreach_reverse", "function", "goto", "idouble", "if",
795 	"ifloat", "immutable", "import", "in", "in", "inout", "int", "int",
796 	"interface", "invariant", "ireal", "is", "lazy", "lazy", "long", "long",
797 	"macro", "mixin", "module", "new", "nothrow", "null", "out", "out",
798 	"override", "package", "pragma", "private", "protected", "public", "pure",
799 	"real", "ref", "return", "scope", "shared", "short", "static", "string",
800 	"struct", "super", "switch", "synchronized", "template", "this", "throw",
801 	"true", "try", "typedef", "typeid", "typeof", "ubyte", "ucent", "uint",
802 	"uint", "ulong", "ulong", "union", "unittest", "ushort", "ushort",
803 	"version", "void", "volatile", "wchar", "while", "with", "wstring"];
804 
805 	foreach(string w; words)
806 		if(w == field)
807 			return true;
808 	return false;
809 }
810 
811 version(D_Version2)
812 unittest {
813 	// Conversion for optional
814 	mixin(`enum str = ParserData("message Test1 { required int32 a = 1; }");`);
815 	mixin(`enum msg = PBCompileTime(str);`);
816 	mixin(`import dprotobuf.wireformat;`);
817 	mixin(`import std.typecons;`);
818 	mixin("static " ~ msg.toD1);
819 	ubyte[] feed = [0x08,0x96,0x01]; // From example
820 	auto t1 = new Test1(feed);
821 	assert(t1.a == 150);
822 	assert(t1.Serialize() == feed);
823 }
824 
825 unittest {
826 	auto str = ParserData("optional OtherType type = 1;");
827 	auto ms = PBChild(str);
828     toD1(ms);
829 }
830 
831 version(D_Version2)
832 unittest {
833 	// Conversion for repated packed
834 	mixin(`enum str = ParserData("message Test4 {
835 	                              repeated int32 d = 4 [packed=true]; }");`);
836 	mixin(`enum msg = PBCompileTime(str);`);
837 	mixin(`import dprotobuf.wireformat;`);
838 	mixin(`import std.typecons;`);
839 	mixin("static " ~ msg.toD1);
840 	ubyte[] feed = [0x22, // Tag (field number 4, wire type 2)
841 		0x06, // payload size (6 bytes)
842 		0x03, // first element (varint 3)
843 		0x8E,0x02, // second element (varint 270)
844 		0x9E,0xA7,0x05 // third element (varint 86942)
845 			]; // From example
846 	auto t4 = new Test4(feed);
847 	assert(t4.d == [3,270,86942]);
848 	assert(t4.Serialize() == feed);
849 }
850 
851 version(D_Version2)
852 unittest {
853 	// Conversion for string
854 	mixin(`enum str = ParserData("message Test2 {
855 	                              required string b = 2; }");`);
856 	mixin(`enum msg = PBCompileTime(str);`);
857 	mixin(`import dprotobuf.wireformat;`);
858 	mixin(`import std.typecons;`);
859 	mixin("static " ~ msg.toD1);
860 	ubyte[] feed = [0x12,0x07, // (tag 2, type 2) (length 7)
861 		0x74,0x65,0x73,0x74,0x69,0x6e,0x67
862 			]; // From example
863 	auto t2 = new Test2(feed);
864 	assert(t2.b == "testing");
865 	assert(t2.Serialize() == feed);
866 }
867 
868 version(D_Version2)
869 unittest {
870 	// Tests parsing does not pass message
871 	mixin(`enum str = ParserData("message Test2 {
872 	                              repeated string b = 2;
873 	                              repeated string c = 3; }");`);
874 	mixin(`enum msg = PBCompileTime(str);`);
875 	mixin(`import dprotobuf.wireformat;`);
876 	mixin(`import std.typecons;`);
877 	mixin("static " ~ msg.toD1);
878 	ubyte[] feed = [0x09,(2<<3) | 2,0x07,
879 		0x74,0x65,0x73,0x74,0x69,0x6e,0x67,
880 		3<<3 | 0,0x08
881 			];
882 	auto feedans = feed;
883 	auto t2 = new Test2(feed, false);
884 	assert(t2.b == ["testing"]);
885 	assert(t2.Serialize() == feedans[1..$-2]);
886 }
887 
888 version(D_Version2)
889 unittest {
890 	// Packed enum data
891 	mixin(`enum um = ParserData("enum MyNum {
892 	                              YES = 1; NO = 2; }");`);
893 	mixin(`enum str = ParserData("message Test {
894 	                              repeated MyNum b = 2 [packed=true]; }");`);
895 	mixin(`enum msg = PBCompileTime(str);`);
896 	mixin(`enum yum = PBCTEnum(um);`);
897 	mixin(`import dprotobuf.wireformat;`);
898 	mixin(`import std.typecons;`);
899 	mixin(yum.toD1);
900 	mixin("static " ~ msg.toD1);
901 	ubyte[] feed = [(2<<3) | 2,0x02,
902 		0x01,0x02
903 			];
904 	auto t = new Test(feed);
905 	assert(t.b == [MyNum.YES, MyNum.NO]);
906 	assert(t.Serialize() == feed);
907 }
908 
909 version(D_Version2)
910 unittest {
911 	// Type Generation
912 	mixin(`enum one = ParserData("enum Settings {
913 	                              FOO = 1;
914 	                              BAR = 2;
915 	                          }");`);
916 	mixin(`enum two = ParserData("message Type {
917 	                              repeated int32 data = 1;
918 	                              repeated int32 extra = 2 [packed = true];
919 	                              optional int32 last = 3;
920 	                          }");`);
921 	mixin(`enum three = ParserData("message OtherType {
922 	                              optional Type struct = 1;
923 	                              repeated Settings enum = 2 [packed = true];
924 	                              repeated Settings num = 3;
925 	                              repeated string a = 4;
926 	                              required string b = 5;
927 	                              repeated Type t = 6;
928 	                          }");`);
929 	mixin(`enum ichi = PBCTEnum(one);`);
930 	mixin(`enum ni = PBCompileTime(two);`);
931 	mixin(`enum san = PBCompileTime(three);`);
932 	mixin(`import dprotobuf.wireformat;`);
933 	mixin(`import std.typecons;`);
934 	mixin(ichi.toD1);
935 	mixin("static " ~ ni.toD1);
936 	mixin("static " ~ san.toD1);
937 	ubyte[] feed = [((1 << 3) | 2), 8, // OtherType.Type
938 	((1 << 3) | 0), 1, ((1 << 3) | 0), 2, // Type.Data
939 	((2 << 3) | 2), 2, 3, 4, // Type.Extra
940 
941 	((2 << 3) | 2), 1, 1, // OtherType.enum
942 	((3 << 3) | 0), 2, // OtherType.num
943 	((4 << 3) | 2), 1, 'a', // OtherType.a
944 	((4 << 3) | 2), 1, 'b', // OtherType.a
945 	((5 << 3) | 2), 2, 'c', 'd', // OtherType.b
946 
947 	((6 << 3) | 2), 4, // OtherType.Type
948 	((1 << 3) | 0), 2, // Type.Data
949 	((1 << 3) | 0), 2, // Type.Data
950 	((6 << 3) | 2), 2, // OtherType.Type
951 	((1 << 3) | 0), 3, // Type.Data
952 	];
953 	auto ot = new OtherType(feed);
954 	assert(ot.struct_.data == [1, 2]);
955 	assert(ot.struct_.extra == [3, 4]);
956 	assert(ot.enum_ == [Settings.FOO]);
957 	assert(ot.num == [Settings.BAR]);
958 	assert(ot.a == ["a", "b"]);
959 	assert(ot.b == "cd");
960 	assert(ot.t[0].data == [2,2]);
961 	assert(ot.t[1].data == [3]);
962 	assert(ot.t.length == 2);
963 
964 	auto res = ot.Serialize();
965 	assert(res == feed);
966 }
967 
968 version(D_Version2)
969 unittest {
970 	// Conversion of reserved names
971 	mixin(`enum str = ParserData("message String {
972 	                              repeated string version = 1;
973 	                              optional string enum = 2; }");`);
974 	mixin(`enum msg = PBCompileTime(str);`);
975 	mixin(`import dprotobuf.wireformat;`);
976 	mixin(`import std.typecons;`);
977 	mixin("static " ~ msg.toD1);
978 	//auto t2 = new Test2(feed);
979 	//assert(t2.b == "testing");
980 	//assert(t2.Serialize() == feed);
981 }