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