1 module reduxed;
2 
3 import mutex : DummyMutex;
4 
5 enum Reduxed;
6 
7 private string buildExecuteCallString(Args...)() {
8 	import std.traits : isInstanceOf, moduleName;
9 	import std.format : format;
10 	import observable;
11 	enum isStruct = is(Args[0].Type == struct);
12 	enum isClass = is(Args[0].Type == class);
13 	static if(isStruct || isClass) {
14 		string ret = format("import %s;\n", moduleName!(Args[0].Type));
15 	} else {
16 		string ret;
17 	}
18 	ret ~= "args[0] = Fun(";
19 	size_t idx = 0;
20 	static foreach(arg; Args) {
21 		if(idx > 0) {
22 			ret ~= ", ";
23 		}
24 		static if(isInstanceOf!(Observable, arg)) {
25 			ret ~= format("cast(%s)args[%u]", arg.Type.stringof, idx);
26 		} else {
27 			ret ~= format("args[%u]", idx);
28 		}
29 		++idx;
30 	}
31 	ret ~= ");\nargs[0].publish();";
32 	return ret;
33 }
34 
35 private string buildMutexArray(Args...)() {
36 	import std.traits : isInstanceOf;
37 	import std.format : format;
38 	import observable;
39 	string ret = "import std.algorithm.sorting : sort;\n";
40 	ret ~= format("Mutex*[%d] mutexes;\n", Args.length);
41 	ret ~= "size_t mutexIdx;\n";
42 	size_t idx = 0;
43 	static foreach(arg; Args) {{
44 		static if(isInstanceOf!(Observable, arg)) {
45 			ret ~= format("mutexes[mutexIdx++] = &(args[%u].mutex);\n", idx);
46 		}
47 		++idx;
48 	}}
49 	ret ~= "sort(mutexes[0 .. mutexIdx]);\n";
50 	return ret;
51 }
52 
53 private string indentString(size_t indent) pure {
54 	string ret;
55 	foreach(it; 0 .. indent) {
56 		ret ~= "\t";
57 	}
58 	return ret;
59 }
60 
61 private string buildStructImpl(S, size_t indent)() {
62 	import std.traits : isBasicType, hasUDA, fullyQualifiedName;
63 	import std.format : format;
64 
65 	string ret;
66 	if(indent) {
67 		ret = indentString(indent) ~ format("struct __Reduxed%s {\n", S.stringof);
68 	}
69 
70 	static assert(is(S == struct));
71 	foreach(mem; __traits(allMembers, S)) {
72 		alias Type = typeof(__traits(getMember, S, mem));
73 		enum isStruct = is(Type == struct);
74 		enum isClass = is(Type == class);
75 		static if((isStruct || isClass) && hasUDA!(Type,Reduxed)) {
76 			ret ~= buildStructImpl!(Type, indent + 1)();
77 			ret ~= indentString(indent + 1) ~ format("__Reduxed%s %s;\n", 
78 						Type.stringof, mem
79 					);
80 		} else {
81 			import std.traits : moduleName;
82 			static if(!isBasicType!(Type)) {
83 				ret ~= indentString(indent + 1) 
84 						~ format("import %s;\n",
85 								moduleName!(Type)
86 							);
87 			}
88 			ret ~= indentString(indent + 1) ~ format("Observable!(%s) %s;\n", 
89 						Type.stringof, mem
90 					);
91 		}
92 	}
93 
94 	if(indent) {
95 		ret ~= indentString(indent) ~ "}\n";
96 	}
97 
98 	return ret;
99 }
100 
101 private string buildStruct(S)() {
102 	string ret = "struct Store {\n";
103 	ret ~= buildStructImpl!(S, 0);
104 
105 	ret ~= "}\n";
106 	ret ~= "Store store;\n";
107 	ret ~= "alias store this;\n";
108 	return ret;
109 }
110 
111 struct Store(T,Mutex = DummyMutex) {
112 	import observable;
113 
114 	pragma(msg, buildStruct!T());
115 	mixin(buildStruct!T());
116 
117 	void execute(alias Fun, Args...)(ref Args args) {
118 		pragma(msg, buildMutexArray!Args());
119 		pragma(msg, buildExecuteCallString!Args());
120 		mixin(buildMutexArray!Args());
121 		foreach(Mutex* mu; mutexes[0 .. mutexIdx]) {
122 			mu.lock();
123 		}
124 		pragma(msg, buildExecuteCallString!Args());
125 		mixin(buildExecuteCallString!Args());
126 		foreach(Mutex* mu; mutexes[0 .. mutexIdx]) {
127 			mu.unlock();
128 		}
129 	}
130 
131 }
132 
133 unittest {
134 	import observable;
135 	struct Bar {
136 		Observable!float a;
137 		Observable!float b;
138 	}
139 
140 	struct Foo {
141 		Observable!int a;
142 		Observable!int b;
143 
144 		Bar bar;
145 	}
146 
147 	struct Flux {
148 		Foo foo;
149 	}
150 
151 	void fun(ref const(float) f) @safe {
152 		int a = 10;
153 	}
154 
155 	Flux f;
156 
157 	f.foo.bar.a.subscribe(&fun);
158 }
159 
160 unittest {
161 	// Define your store as a struct
162 	struct Bar {
163 		int b;
164 		int a;
165 	}
166 
167 	// Create the store
168 	Store!Bar store;
169 	// set store init values
170 	store.a = 1;
171 	store.b = 2;
172 
173 	int a = 1337;
174 
175 	// We use this function to observe Bar.a
176 	void fun(ref const(int) f) @safe {
177 		a = f;
178 	}
179 
180 	// Here we subscribe to Bar.a
181 	// Whenever Bar.a changes "fun" gets called
182 	store.a.subscribe(&fun);
183 
184 	// This function is used to change the value
185 	static int increment(int a, int b) pure {
186 		return a + b; 
187 	}
188 
189 	// Here we call increment with the values of store.a and store.b
190 	// the effect is equal to
191 	// store.a = increment(store.a, store.b);
192 	store.execute!(increment)(store.a, store.b);
193 
194 	assert(store.a.value == 3);
195 	assert(a == 3);
196 }	
197