1 module mir.toml.deserializer;
2 
3 // debug = Deserializer;
4 
5 static import toml;
6 
7 import std.datetime.date;
8 import std.datetime.systime;
9 import std.meta;
10 
11 import mir.timestamp : Timestamp;
12 
13 template parseToml(T)
14 {
15 	void parseToml(scope const(char)[] inputData, ref T serializer) @safe
16 	{
17 		auto document = toml.parseTOML(inputData);
18 		serializeValue(document.table, serializer);
19 	}
20 
21 @safe pure:
22 
23 	private void serializeValue(scope toml.TOMLValue value, ref T serializer)
24 	{
25 		final switch (value.type)
26 		{
27 		case toml.TOML_TYPE.STRING:
28 			serializeValue(value.str, serializer);
29 			break;
30 		case toml.TOML_TYPE.INTEGER:
31 			serializeValue(value.integer, serializer);
32 			break;
33 		case toml.TOML_TYPE.FLOAT:
34 			serializeValue(value.floating, serializer);
35 			break;
36 		case toml.TOML_TYPE.TRUE:
37 			serializeValue(true, serializer);
38 			break;
39 		case toml.TOML_TYPE.FALSE:
40 			serializeValue(false, serializer);
41 			break;
42 		case toml.TOML_TYPE.OFFSET_DATETIME:
43 			serializeValue(value.offsetDatetime, serializer);
44 			break;
45 		case toml.TOML_TYPE.LOCAL_DATETIME:
46 			serializeValue(value.localDatetime, serializer);
47 			break;
48 		case toml.TOML_TYPE.LOCAL_DATE:
49 			serializeValue(value.localDate, serializer);
50 			break;
51 		case toml.TOML_TYPE.LOCAL_TIME:
52 			serializeValue(value.localTime, serializer);
53 			break;
54 		case toml.TOML_TYPE.ARRAY:
55 			serializeValue(value.array, serializer);
56 			break;
57 		case toml.TOML_TYPE.TABLE:
58 			serializeValue(value.table, serializer);
59 			break;
60 		}
61 	}
62 
63 	static foreach (SimpleType; AliasSeq!(bool, const(char)[], long, double))
64 		private void serializeValue(scope SimpleType value, ref T serializer)
65 		{
66 			serializer.putValue(value);
67 		}
68 
69 	static foreach (TimeType; AliasSeq!(SysTime, DateTime, Date, TimeOfDay))
70 		private void serializeValue(scope TimeType value, ref T serializer)
71 		{
72 			serializer.putValue(Timestamp(value));
73 		}
74 
75 	private void serializeValue(scope toml.TOMLValue[] array, ref T serializer)
76 	{
77 		auto state = serializer.listBegin(array.length);
78 		foreach (value; array)
79 		{
80 			serializer.elemBegin();
81 			serializeValue(value, serializer);
82 		}
83 		serializer.listEnd(state);
84 	}
85 
86 	private void serializeValue(scope toml.TOMLValue[string] table, ref T serializer)
87 	{
88 		auto state = serializer.structBegin(table.length);
89 		foreach (key, value; table)
90 		{
91 			serializer.putKey(key);
92 			serializeValue(value, serializer);
93 		}
94 		serializer.structEnd(state);
95 	}
96 }
97 
98 debug (Deserializer)
99 {
100 	import mir.ser;
101 	import mir.bignum.decimal: Decimal;
102 	import mir.bignum.integer: BigInt;
103 	import mir.lob: Blob, Clob;
104 	import mir.timestamp: Timestamp;
105 	import mir.ion.type_code : IonTypeCode;
106 
107 	struct DebugSerializer(T)
108 	{
109 		import std.algorithm;
110 		import std.range;
111 		import std.stdio;
112 		import std.string;
113 
114 		T forwarder;
115 		private enum definedMethods = [
116 			"void putStringPart(scope const(char)[] value)",
117 			"void stringEnd(size_t state)",
118 			"size_t structBegin(size_t length = size_t.max)",
119 			"void structEnd(size_t state)",
120 			"size_t listBegin(size_t length = size_t.max)",
121 			"void listEnd(size_t state)",
122 			"size_t sexpBegin(size_t length = size_t.max)",
123 			"void sexpEnd(size_t state)",
124 			"void putSymbol(scope const char[] symbol)",
125 			"void putAnnotation(scope const(char)[] annotation)",
126 			"size_t annotationsEnd(size_t state)",
127 			"size_t annotationWrapperBegin()",
128 			"void annotationWrapperEnd(size_t annotationsState, size_t state)",
129 			"void nextTopLevelValue()",
130 			"void putKey(scope const char[] key)",
131 			"void putValue(long value)",
132 			"void putValue(ulong value)",
133 			"void putValue(float value)",
134 			"void putValue(double value)",
135 			"void putValue(real value)",
136 			"void putValue(scope ref const BigInt!128 value)",
137 			"void putValue(scope ref const Decimal!128 value)",
138 			"void putValue(typeof(null))",
139 			"void putNull(IonTypeCode code)",
140 			"void putValue(bool b)",
141 			"void putValue(scope const char[] value)",
142 			"void putValue(scope Clob value)",
143 			"void putValue(scope Blob value)",
144 			"void putValue(Timestamp value)",
145 			"void elemBegin()",
146 			"void sexpElemBegin()",
147 		];
148 
149 		private static int prefixLength()
150 		{
151 			return __FUNCTION__.length - "prefixLength".length;
152 		}
153 
154 		int fndepth;
155 
156 	@safe:
157 		static foreach (method; definedMethods)
158 			mixin(method ~ " pure {
159 				enum fnname = __FUNCTION__[prefixLength..$];
160 				static if (fnname.length > 3 && fnname[$ - 3 .. $] == `End`)
161 					fndepth--;
162 				scope (exit)
163 				{
164 					static if (fnname.length > 5 && fnname[$ - 5 .. $] == `Begin`
165 						&& fnname != `elemBegin`)
166 						fndepth++;
167 				}
168 				static if (is(typeof(return) == void))
169 				{
170 					debug writeln(`  `.repeat(fndepth).join, '\\x1B', `[1m`, fnname, '\\x1B', `[0m`, `(`, __traits(parameters), `)`);
171 					__traits(getMember, forwarder, fnname)(__traits(parameters));
172 				}
173 				else
174 				{
175 					debug write(`  `.repeat(fndepth).join, '\\x1B', `[1m`, fnname, '\\x1B', `[0m`, `(`, __traits(parameters), `)`);
176 					auto ret = __traits(getMember, forwarder, fnname)(__traits(parameters));
177 					debug writeln(` -> `, ret);
178 					return ret;
179 				}
180 			}");
181 	}
182 
183 	private auto makeDebugSerializer(T)(lazy T forwarder)
184 	{
185 		return DebugSerializer!T(forwarder);
186 	}
187 }
188 
189 @trusted
190 immutable(ubyte)[] tomlToIon(scope const(char)[] inputData)
191 {
192 	import std.algorithm : move;
193 	import mir.appender : scopedBuffer;
194 	import mir.ion.symbol_table: IonSymbolTable;
195     import mir.ion.internal.data_holder: ionPrefix;
196 	import mir.ser.ion : ionSerializer;
197 	import mir.serde : SerdeTarget;
198 	enum nMax = 4096;
199 
200 	auto buf = scopedBuffer!ubyte;
201 	
202 	IonSymbolTable!false table = void;
203 	table.initialize;
204 
205 	debug (Deserializer)
206 	{
207 		auto debugSerializer = makeDebugSerializer(ionSerializer!(nMax * 8, null, false));
208 		debugSerializer.forwarder.initialize(table);
209 		parseToml!(typeof(debugSerializer))(inputData, debugSerializer);
210 		ref auto serializer() { return debugSerializer.forwarder; }
211 	}
212 	else
213 	{
214 		auto serializer = ionSerializer!(nMax * 8, null, false);
215 		serializer.initialize(table);
216 		parseToml!(typeof(serializer))(inputData, serializer);
217 	}
218 
219 	serializer.finalize;
220 
221 	buf.put(ionPrefix);
222 	if (table.initialized)
223 	{
224 		table.finalize;
225 		buf.put(table.data);
226 	}
227 	buf.put(serializer.data);
228 
229 	return buf.data.idup;
230 }
231 
232 template deserializeToml(T)
233 {
234 	void deserializeToml(scope ref T value, scope const(char)[] data)
235 	{
236 		import mir.deser.ion : deserializeIon;
237 
238 		return deserializeIon!T(value, tomlToIon(data));
239 	}
240 
241 	T deserializeToml(scope const(char)[] data)
242 	{
243 		T value;
244 		deserializeToml(value, data);
245 		return value;
246 	}
247 }