1 module deredux; 2 3 import std.array : appender; 4 import std.format : formattedWrite; 5 import std.traits; 6 7 private struct ImmuWrapper(T) { 8 union { 9 immutable(T) immu; 10 T nImmu; 11 } 12 13 this(T v) { 14 this.nImmu = v; 15 } 16 } 17 18 private union Parameters { 19 ubyte Ubyte; 20 ushort Ushort; 21 uint Uint; 22 ulong Ulong; 23 byte Byte; 24 short Short; 25 int Int; 26 long Long; 27 float Float; 28 double Double; 29 real Real; 30 string String; 31 wstring Wstring; 32 dstring Dstring; 33 bool Bool; 34 } 35 36 private struct StringParameter { 37 import fixedsizearray; 38 import taggedalgebraic; 39 40 string funcName; 41 int line; 42 FixedSizeArray!(TaggedAlgebraic!Parameters,16) parameters; 43 44 this(Args...)(string funcName, int line, Args args) { 45 this.funcName = funcName; 46 this.line = line; 47 foreach(it; args) { 48 this.parameters.insertBack(it); 49 } 50 } 51 } 52 53 /** State can be your single source of truth if you let it. 54 Call `exe` to run a function and use to `peek` to have a look at the last 55 version of `immutable(Type)`. You should not hold a copy of the data returned 56 by `peek`. 57 */ 58 struct State(Type,int Size = 16) { 59 import std.typecons : Rebindable; 60 import std.variant : Variant; 61 import core.sync.mutex; 62 import fixedsizearray; 63 import taggedalgebraic; 64 65 FixedSizeArray!(ImmuWrapper!Type,Size) state; 66 FixedSizeArray!(StringParameter,Size - 1) parameters; 67 68 this() @disable; 69 70 /** Construct the State object with an `initState`. 71 */ 72 this(Type initState) { 73 this.state.insertBack(ImmuWrapper!Type(initState)); 74 } 75 76 /** Execute the function `F` on the current value with parameters 77 `Args...`. 78 */ 79 void exe(F,int line = __LINE__ ,Args...)(F f, Args args) { 80 if(this.state.length + 1 == this.state.capacity()) { 81 this.state.removeFront(); 82 this.parameters.removeFront(); 83 } 84 this.state.insertBack(ImmuWrapper!Type(f(this.state.back.immu, args))); 85 this.parameters.insertBack(StringParameter( 86 fullyQualifiedName!(F), line, args 87 )); 88 } 89 90 /** Peek at the current element. 91 */ 92 ref immutable(Type) peek() { 93 return this.state.back.immu; 94 } 95 96 /** Call this to get an output of the last few states and the passed 97 parameter. 98 */ 99 string toString() const { 100 import std.array : appender; 101 auto app = appender!string(); 102 103 this.toString(app); 104 return app.data; 105 } 106 107 /// Ditto 108 void toString(D)(D app) const { 109 import std.stdio; 110 import std.format : formattedWrite; 111 112 for(int i = 0; i < this.parameters.length; ++i) { 113 formattedWrite(app, "%2d %s line %d: %s(", this.parameters.length - i, 114 this.state[i].immu, this.parameters[i].line, 115 this.parameters[i].funcName 116 ); 117 bool first = true; 118 foreach(it; this.parameters[i].parameters[]) { 119 if(first) { 120 formattedWrite(app, "%s", it); 121 } else { 122 formattedWrite(app, ",%s", it); 123 } 124 first = false; 125 } 126 formattedWrite(app, ")\n"); 127 } 128 formattedWrite(app, "%2d %s", 0, this.state.back.immu); 129 } 130 } 131 132 /// Ditto 133 unittest { 134 struct Foo { 135 int value; 136 } 137 138 struct FooRedux { 139 Foo fun(const(Foo) foo) { 140 return Foo(foo.value + 2); 141 } 142 } 143 144 Foo bar(const(Foo) foo, int i) { 145 return Foo(foo.value + i); 146 } 147 import std.stdio; 148 import exceptionhandling; 149 150 const begin = 1337; 151 152 auto fooState = State!(Foo)(Foo(begin)); 153 154 int cnt = 0; 155 for(int i = 1; i <= 126; ++i) { 156 fooState.exe(&bar, i); 157 cnt += i; 158 cast(void)assertEqual(fooState.peek().value, begin + cnt); 159 } 160 161 cast(void)assertEqual(fooState.peek().value, begin + cnt); 162 163 auto f = fooState.peek(); 164 165 FooRedux fr; 166 fooState.exe(&fr.fun); 167 writeln(fooState.toString()); 168 }