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 }