1 module vsignal.sink;
2 
3 import vsignal.signal;
4 import vsignal.slot : Slot, slot_connect = connect;
5 
6 // https://issues.dlang.org/show_bug.cgi?id=5710
7 /**
8 Connect a listener to a Signal.
9 
10 A payload can be passed if the function to call is a data member function of the
11 same or if the function accepts as its first parameter a reference to its type.
12 
13 Params:
14 	pred = the listener to connect.
15 	sink = the Sink that holds the signal.
16 	instance = the payload to store.
17 
18 Returns: A Connection of the listener.
19 */
20 Connection connect(alias pred, F)(auto ref Sink!F sink)
21 {
22 	sink.disconnect!pred;
23 
24 	import core.lifetime : move;
25 
26 	with (sink)
27 	{
28 		Slot!F call;
29 		call.slot_connect!pred;
30 		signal.calls ~= call.move();
31 
32 		Slot!(void delegate(void* signal) @safe pure nothrow) conn;
33 		conn.slot_connect!(release!(pred, F))();
34 
35 		return Connection(conn.move(), signal);
36 	}
37 }
38 
39 ///
40 Connection connect(alias pred, T, F)(auto ref Sink!F sink, ref T instance)
41 {
42 	sink.disconnect!pred(instance);
43 
44 	import core.lifetime : move;
45 
46 	with (sink)
47 	{
48 		Slot!F call;
49 		call.slot_connect!pred(instance);
50 		signal.calls ~= call.move();
51 
52 		Slot!(void delegate(void* signal) @safe pure nothrow) conn;
53 		conn.slot_connect!(release!(pred, T, F))(instance);
54 
55 		return Connection(conn.move(), signal);
56 	}
57 }
58 
59 // https://issues.dlang.org/show_bug.cgi?id=5710
60 /++
61 Disconnects a listener from a Signal. The order is not preserved after the
62 removal.
63 
64 Examples:
65 ---
66 void method() {}
67 struct Foo { void method() {} }
68 Foo foo;
69 
70 /* ... */
71 
72 // lets assume a signal exists with listeners
73 sink.disconnect!(Foo.method)(foo); // disconnects Foo.method with payload foo
74 sink.disconnect!method;            // disconnects method
75 sink.disconnect(foo);              // disconnects all listeners with payload foo
76 sink.disconnect();                 // disconnects all listeners
77 ---
78 
79 Params:
80 	pred = the listener to disconnect.
81 	sink = the Sink that holds the signal.
82 	instance = the payload that composes the listener.
83 +/
84 void disconnect(alias pred, F)(auto ref Sink!F sink)
85 {
86 	import std.algorithm.mutation : remove, SwapStrategy;
87 	import std.algorithm.searching : countUntil;
88 
89 	Slot!F call;
90 	call.slot_connect!pred;
91 
92 	with (sink)
93 	{
94 		const index = signal.calls.countUntil(call);
95 
96 		if (index > -1)
97 			signal.calls = signal.calls.remove!(SwapStrategy.unstable)(index);
98 	}
99 }
100 
101 ///
102 void disconnect(alias pred, T, F)(auto ref Sink!F sink, ref T instance)
103 {
104 	import std.algorithm.mutation : remove, SwapStrategy;
105 	import std.algorithm.searching : countUntil;
106 
107 	Slot!F call;
108 	call.slot_connect!pred(instance);
109 
110 	with (sink)
111 	{
112 		const index = signal.calls.countUntil(call);
113 
114 		if (index > -1)
115 			signal.calls = signal.calls.remove!(SwapStrategy.unstable)(index);
116 	}
117 }
118 
119 ///
120 void disconnect(T, F)(auto ref Sink!F sink, ref T instance)
121 {
122 	import std.algorithm.mutation : remove, SwapStrategy;
123 
124 	with (sink)
125 	{
126 		signal.calls = signal.calls.remove!(call => call.payload is &instance , SwapStrategy.unstable);
127 	}
128 }
129 
130 ///
131 void disconnect(F)(auto ref Sink!F sink)
132 {
133 	sinksignal.calls = [];
134 }
135 
136 // https://issues.dlang.org/show_bug.cgi?id=5710
137 /**
138 Used as a bridge between Sink and Connection to correctly disconnect a listener
139 from a signal.
140 
141 Params:
142 	pred = the listener to disconnect,
143 	signal = the signal that contains the listener.
144 	instance = the payload that is attached to the listener.
145 */
146 private void release(alias pred, F)(void* signal)
147 {
148 	Sink!F(() @trusted { return cast(Signal!F*) signal; } ()).disconnect!pred();
149 }
150 
151 ///
152 private void release(alias pred, T, F)(ref T instance, void* signal)
153 {
154 	Sink!F(() @trusted { return cast(Signal!F*) signal; } ()).disconnect!pred(instance);
155 }
156 
157 struct Sink(F)
158 {
159 	this(scope Signal!F* sig)
160 	{
161 		signal = sig;
162 	}
163 
164 	size_t length() const @property
165 	{
166 		return signal.length;
167 	}
168 
169 	bool empty() const @property
170 	{
171 		return signal.empty;
172 	}
173 
174 private:
175 	Signal!F* signal;
176 }
177 
178 struct Connection
179 {
180 	bool opCast(T : bool)() const
181 	{
182 		return disconnect;
183 	}
184 
185 	void release()()
186 	{
187 		if (disconnect)
188 		{
189 			disconnect(signal);
190 			disconnect.reset();
191 		}
192 	}
193 
194 private:
195 	Slot!(void delegate(void*) @safe pure nothrow) disconnect;
196 	void* signal;
197 }