1 module tinyevent;
2 
3 /// Defines a regular event
4 alias Event(Args...) = void delegate(Args)[];
5 /// Defines a cancelable event by returning false to cancel
6 alias Cancelable(Args...) = bool delegate(Args)[];
7 
8 /// Calls all functions in a regular event
9 void emit(T : void delegate(Args), Args...)(T[] events, Args args) {
10 	foreach(fn; events)
11 		fn(args);
12 }
13 
14 /// Calls all functions in a cancelable event
15 bool emit(T : bool delegate(Args), Args...)(T[] events, Args args) {
16 	foreach(fn; events)
17 		if(!fn(args))
18 			return false;
19 	return true;
20 }
21 
22 /// Returns true if the type or variable is an Event!(...)
23 enum bool isEvent(T) = isEvent!(T.init);
24 /// ditto
25 enum bool isEvent(alias v) = is(typeof(v.length)) && is(typeof(v) : void delegate(Args)[], Args...);
26 /// Returns true if the type or variable is a Cancelable!(...)
27 enum bool isCancelable(T) = isCancelable!(T.init);
28 /// ditto
29 enum bool isCancelable(alias v) = is(typeof(v.length)) && is(typeof(v) : bool delegate(Args)[], Args...);
30 /// Returns true if the type or variable is an Event or Cancelable
31 enum bool isEmittable(T) = isEmittable!(T.init);
32 /// ditto
33 enum bool isEmittable(alias v) = is(typeof(v.length)) && is(typeof(v) : Ret delegate(Args)[], Ret, Args...)
34 		&& (is(Ret == bool) || is(Ret == void));
35 
36 ///
37 unittest {
38 	Event!string onStringChange;
39 	static assert (isEvent!onStringChange);
40 	static assert (isEvent!(typeof(onStringChange)));
41 	static assert (!isCancelable!onStringChange);
42 	static assert (!isCancelable!(typeof(onStringChange)));
43 	static assert (isEmittable!onStringChange);
44 	static assert (isEmittable!(typeof(onStringChange)));
45 	onStringChange ~= (s) { assert(s == "Foo"); };
46 	onStringChange.emit("Foo");
47 }
48 
49 ///
50 unittest {
51 	Cancelable!int onIntChange;
52 	static assert (!isEvent!onIntChange);
53 	static assert (!isEvent!(typeof(onIntChange)));
54 	static assert (isCancelable!onIntChange);
55 	static assert (isCancelable!(typeof(onIntChange)));
56 	static assert (isEmittable!onIntChange);
57 	static assert (isEmittable!(typeof(onIntChange)));
58 	int changed = 0;
59 	onIntChange ~= (i) { if(i > 5) return false; changed++; return true; };
60 	onIntChange ~= (i) { if(i > 4) return false; changed++; return true; };
61 	onIntChange ~= (i) { if(i > 3) return false; changed++; return true; };
62 	assert(onIntChange.emit(2));
63 	assert(changed == 3);
64 	
65 	changed = 0;
66 	assert(!onIntChange.emit(4));
67 	assert(changed == 2);
68 }
69 
70 // safety tests
71 @system unittest {
72 	void delegate(string, int, bool) @system [] eventSystem;
73 	eventSystem.emit("", 1, true);
74 }
75 
76 @safe unittest {
77 	void delegate(string, int, bool) @trusted [] eventTrusted;
78 	eventTrusted.emit("", 1, true);
79 	void delegate(string, int, bool) @safe [] eventSafe;
80 	eventSafe.emit("", 1, true);
81 }
82 
83 @safe nothrow unittest {
84 	void delegate(string, int, bool) @safe nothrow [] eventSafe;
85 	eventSafe.emit("", 1, true);
86 }
87 
88 unittest {
89 	string s;
90 	static assert (!isEvent!s);
91 	int i;
92 	static assert (!isEvent!i);
93 	void fn(int i) {
94 	}
95 	static assert (!isEvent!fn);
96 	static assert (isEvent!([&fn]));
97 	void function()[] fns;
98 	static assert (!isEvent!fns);
99 	void delegate()[] dels;
100 	static assert (isEvent!dels);
101 	Event!int e;
102 	static assert (isEvent!e);
103 	Cancelable!int c;
104 	static assert (isCancelable!c);
105 	static assert (isEmittable!e && isEmittable!c);
106 	Event!() en;
107 	static assert (isEvent!en);
108 	Cancelable!() cn;
109 	static assert (isCancelable!cn);
110 	Event!(Event!int, int, string, Object) em;
111 	static assert (isEvent!em);
112 	Cancelable!(Object, string, void function(int, string)) cm;
113 	static assert (isCancelable!cm);
114 }