1 module spasm.hmr;
2 
3 version(hmr):
4 
5 import spasm.rt.array;
6 import spasm.node;
7 import std.traits;
8 
9 string dumpState(T)(ref T app) {
10   void recurse(T, Sink)(ref T app, ref Sink sink) {
11     enum len = T.tupleof.length;
12     bool first = true;
13     static foreach(idx, t; T.tupleof) {
14       static if (isBoolean!(typeof(t))) {
15         if (!first)
16           sink.write(",");
17         sink.write(__traits(identifier, t), ":b:");
18         if (app.tupleof[idx] == true)
19           sink.write("t");
20         else
21           sink.write("f");
22         first = false;
23       } else static if (is(typeof(t) : NamedNode!tag, string tag)) {
24       } else static if (isSomeString!(typeof(t))) {
25         if (!first)
26           sink.write(",");
27         sink.write(__traits(identifier, t), ":s:", app.tupleof[idx].length, ":", app.tupleof[idx]);
28         first = false;
29       } else static if (is(typeof(t) == enum)) {
30       } else static if (isAggregateType!(typeof(t))) {
31         if (!first)
32           sink.write(",");
33         sink.write(__traits(identifier, t), `:a:{`);
34         recurse(app.tupleof[idx], sink);
35         sink.write('}');
36         first = false;
37       } else static if (isIntegral!(typeof(t))) {
38         if (!first)
39           sink.write(",");
40         sink.write(__traits(identifier, t), `:i:`);
41         sink.write(app.tupleof[idx]);
42         first = false;
43       }
44     }
45   }
46   StringAppender!() sink;
47   sink.write('{');
48   recurse(app, sink);
49   sink.write('}');
50   auto end = sink.length;
51   return cast(string)sink[0..end];
52 }
53 
54 ptrdiff_t countUntil(Range, Needle)(Range range, Needle needle) {
55   foreach(idx, r; range)
56     if (r == needle)
57       return idx;
58   return -1;
59 }
60 
61 bool readBoolean(ref string state) {
62   auto c = state[0];
63   state = state[1..$];
64   return c == 't';
65 }
66 
67 unittest {
68   import unit_threaded;
69   string str = "t,";
70   str.readBoolean.should == true;
71   str.should == ",";
72 }
73 
74 int readInt(ref string state) {
75   size_t p = 0;
76   int c = 0;
77   while(true) {
78     if (state[p] < '0' || state[p] > '9') {
79       state = state[p..$];
80       return c;
81     }
82     c = c * 10 + (state[p] - '0');
83     p++;
84   }
85 }
86 
87 unittest {
88   import unit_threaded;
89   string str = "1234,";
90   readInt(str).should == 1234;
91   str.should == ",";
92 }
93 
94 string readString(ref string state) {
95   int size = readInt(state);
96   string s = text(state[1..size+1]);
97   state = state[size+1..$];
98   return s;
99 }
100 
101 void skipField(ref string state) {
102   if (state[0] == 'b')
103     readBoolean(state);
104   else if (state[0] == 's')
105     readString(state);
106   else if (state[0] == 'i')
107     readInt(state);
108   else if (state[0] == 'a')
109     skipObject(state);
110   else
111     state = "}";
112 }
113 
114 void skipObject(ref string state) {
115   while(1) {
116     auto d = countUntil(state, ':');
117     if (d == -1) {
118       state = "}";
119       return;
120     }
121     state = state[d+1..$];
122     skipField(state);
123     if (state[0] == ',') {
124       state = state[1..$];
125     } else {
126       if (state[0] == '}')
127         state = state[1..$];
128       else
129         state = "}";
130       return;
131     }
132   }
133 }
134 
135 private void update(string field, T)(ref T t) {
136   import spasm.dom;
137   alias member = __traits(getMember, t, field);
138   spasm.dom.update!(member)(t);
139 }
140 
141 void loadState(T)(ref T app, string state) {
142   void readObject(T)(ref T app, ref string state) {
143     while(1) {
144       auto d = countUntil(state, ':');
145       if (d == -1) {
146         state = "}";
147         return;
148       }
149       auto field = state[0..d];
150       state = state[d+1..$];
151       static foreach(idx, t; T.tupleof) {{
152           if (field == __traits(identifier, t)) {
153             static if (isBoolean!(typeof(t))) {
154               if (state[0] != 'b')
155                 skipField(state);
156               else {
157                 state = state[2..$];
158                 app.tupleof[idx] = readBoolean(state);
159                 update!(__traits(identifier, t),T)(app);
160               }
161             } else static if (is(typeof(t) : NamedNode!tag, string tag)) {
162             } else static if (isSomeString!(typeof(t))) {
163               if (state[0] != 's')
164                 skipField(state);
165               else {
166                 state = state[2..$];
167                 app.tupleof[idx] = readString(state);
168                 update!(__traits(identifier, t),T)(app);
169               }
170             } else static if (is(typeof(t) == enum)) {
171             } else static if (isAggregateType!(typeof(t))) {
172               if (state[0] != 'a')
173                 skipField(state);
174               else {
175                 state = state[3..$];
176                 readObject(app.tupleof[idx],state);
177               }
178             } else static if (isIntegral!(typeof(t))) {
179               if (state[0] != 'i')
180                 skipField(state);
181               else {
182                 state = state[2..$];
183                 app.tupleof[idx] = cast(typeof(t))readInt(state);
184                 update!(__traits(identifier, t),T)(app);
185               }
186             }
187           }
188         }}
189       if (state[0] == ',') {
190         state = state[1..$];
191       } else {
192         if (state[0] == '}')
193           state = state[1..$];
194         else
195           state = "}";
196         return;
197       }
198     }
199   }
200   state = state[1 .. $];
201   readObject(app, state);
202 }
203 
204 unittest {
205   import unit_threaded;
206   struct Bar {
207     string str = "My Nested String";
208     int number = 5678;
209     bool boolean = false;
210   }
211   struct Foo {
212     string str = "My String";
213     int number = 1234;
214     bool boolean = true;
215     Bar bar;
216   }
217   struct Bar2 {
218     string str;
219     int number;
220     bool boolean;
221   }
222   struct Foo2 {
223     string str;
224     int number;
225     bool boolean;
226     Bar bar;
227   }
228   Foo foo;
229   string state = foo.dumpState();
230   Foo2 foo2;
231   foo2.loadState(state);
232   foo2.str.should == "My String";
233   foo2.number.should == 1234;
234   foo2.boolean.should == true;
235   foo2.bar.str.should == "My Nested String";
236   foo2.bar.number.should == 5678;
237   foo2.bar.boolean.should == false;
238 }