1 module observable;
2 
3 import mutex : DummyMutex;
4 
5 private struct Observer(T) {
6 	void delegate(ref const(T)) @safe onMsg;
7 	void delegate() @safe onClose;
8 }
9 
10 struct Observable(T, Mutex = DummyMutex) {
11 	import std.traits : isImplicitlyConvertible;
12 	Observer!(T)[void*] observer;
13 	Mutex mutex;
14 	alias Type = T;
15 
16 	T value;
17 
18 	~this() @safe {
19 		import std.stdio;
20 		foreach(ref it; this.observer) {
21 			if(it.onClose) {
22 				it.onClose();
23 			}
24 		}
25 	}
26 
27 	T opCast(T)() {
28 		return this.value;
29 	}
30 
31 	typeof(this) opAssign(T newValue) {
32 		this.value = newValue;
33 		return this;
34 	}
35 
36 	@property size_t length() const @safe pure nothrow @nogc {
37 		return this.observer.length;
38 	}
39 
40 	void subscribe(void delegate(ref const(T)) @safe onMsg) @safe pure {
41 		this.subscribe(onMsg, null);
42 	}
43 
44 	void subscribe(void delegate(ref const(T)) @safe onMsg, 
45 			void delegate() @safe onClose) @safe pure
46 	{
47 		Observer!T ob;
48 		ob.onMsg = onMsg;
49 		ob.onClose = onClose;
50 
51 		void* fptr = () @trusted { return cast(void*)(onMsg.funcptr); }();
52 		assert(fptr);
53 		this.observer[fptr] = ob;
54 	}
55 
56 	void unSubscribe(void delegate(ref const(T)) @safe onMsg) @safe pure {
57 		void* fptr = () @trusted { return cast(void*)(onMsg.funcptr); }();
58 		assert(fptr);
59 
60 		if(fptr in this.observer) {
61 			this.observer.remove(fptr);
62 		}
63 	}
64 
65 	void push(S)(auto ref const(S) value) @safe
66 			if(isImplicitlyConvertible!(S,T))
67 	{
68 		this.value = value;
69 		this.publish();
70 	}
71 
72 	void publish() @safe {
73 		foreach(ref it; this.observer) {
74 			it.onMsg(this.value);
75 		}
76 	}
77 }
78 
79 unittest {
80 	int globalInt = 0;
81 
82 	void fun(ref const(int) f) @safe {
83 		globalInt = f;
84 	}
85 
86 	Observable!int intOb;
87 	assert(intOb.length == 0);
88 	intOb.subscribe(&fun);
89 	assert(intOb.length == 1);
90 	intOb.push(10);
91 	assert(globalInt == 10);
92 	intOb.push(globalInt);
93 	assert(globalInt == 10);
94 	intOb.unSubscribe(&fun);
95 	assert(intOb.length == 0);
96 }
97 
98 unittest {
99 	import std.conv : to;
100 	bool b;
101 
102 	void fun(ref const(int) f) @safe {
103 		int a = 10;
104 	}
105 
106 	void fun1(ref const(int) f) @safe {
107 		int c = 10;
108 	}
109 
110 	void fun2() @safe {
111 		b = true;
112 	}
113 
114 	{
115 		Observable!int ob;
116 		ob.subscribe(&fun, &fun2);
117 		ob.subscribe(&fun1);
118 		assert(ob.length == 2, to!string(ob.length));
119 		ob.push(10);
120 	}
121 
122 	assert(b);
123 }
124 
125 unittest {
126 	struct Foo {
127 		int a;
128 		int b;
129 	}
130 
131 	void fun(ref const(Foo) f) @safe {
132 		assert(f.b == 10);
133 	}
134 
135 	Observable!(Foo) ob;
136 	ob.subscribe(&fun);
137 	ob.push(Foo(9,10));
138 }