1 module minibus;
2 
3 import std.stdio;
4 import std.variant;
5 import std.array;
6 import std.algorithm;
7 static import std.uuid;
8 
9 alias Callback = void delegate(Variant);
10 
11 class Minibus {
12   class Subscription {
13     string key;
14     Callback callback;
15     string id;
16 
17     this(string key, Callback callback) {
18       this.key = key;
19       this.callback = callback;
20       this.id = std.uuid.randomUUID().toString();
21     }
22   }
23 
24 	Subscription[] subscriptions;
25 
26 	string subscribe(string key, Callback callback) {
27 		auto appender = appender(this.subscriptions);
28 		auto sub = new Subscription(key, callback);
29 		appender.put(sub);
30 		this.subscriptions = appender[];
31 		return sub.id;
32 	}
33 
34 	void emit(string key, Variant *arg) {
35 		auto matching = this.subscriptions.filter!(sub => sub.key == key);
36 		foreach(Subscription sub; matching.array) {
37 			sub.callback(*arg);
38 		}
39 	}
40 
41   void emit(string key) {
42     auto dummy_arg = new Variant();
43     this.emit(key, dummy_arg);
44   }
45 
46 	void unsubscribe(string id) {
47 		this.subscriptions = this.subscriptions.filter!(sub => sub.id != id).array;
48 	}
49 }
50 
51 // event without parameter
52 unittest {
53   struct Counter {
54     public int value;
55 
56     void increment() {
57       this.value++;
58     }
59   }
60 
61   auto bus = new Minibus();
62   auto counter = Counter(0);
63   
64   bus.subscribe("increment", (x) => counter.increment());
65   assert(counter.value == 0);
66   
67   bus.emit("increment");
68   assert(counter.value == 1);
69   
70   bus.emit("increment");
71   assert(counter.value == 2);
72 
73   bus.emit("decrement");
74   assert(counter.value == 2);
75 }
76 
77 // event with a parameter
78 unittest {
79   struct Counter {
80     public int value;
81 
82     void increment(int by) {
83       this.value += by;
84     }
85 
86     void decrement(int by) {
87       this.value -= by;
88     }
89   }
90 
91   auto bus = new Minibus();
92   auto counter = Counter(0);
93   auto counter2 = Counter(1);
94   
95   bus.subscribe("increment", (x) => counter.increment(x.get!(int)));
96   bus.subscribe("increment", (x) => counter2.increment(x.get!(int)));
97   bus.subscribe("decrement", (x) => counter.decrement(x.get!(int)));
98   assert(counter.value == 0);
99   
100   bus.emit("increment", new Variant(5));
101   assert(counter.value == 5);
102   assert(counter2.value == 6);
103   
104   bus.emit("increment", new Variant(3));
105   assert(counter.value == 8);
106   assert(counter2.value == 9);
107 
108   bus.emit("decrement", new Variant(6));
109   assert(counter.value == 2);
110   assert(counter2.value == 9);
111 }
112 
113 // unsubscribe
114 unittest {
115   struct Counter {
116     public int value;
117 
118     void increment() {
119       this.value++;
120     }
121   }
122 
123   auto bus = new Minibus();
124   auto counter = Counter(0);
125   
126   auto sub_id = bus.subscribe("increment", (x) => counter.increment());
127   assert(counter.value == 0);
128   
129   bus.emit("increment");
130   assert(counter.value == 1);
131 
132   bus.unsubscribe(sub_id);
133   
134   bus.emit("increment");
135   assert(counter.value == 1);
136 }
137 
138 // unsubscribe non-existing subscription
139 unittest {
140   import std.exception : assertNotThrown;
141   
142   auto bus = new Minibus();
143   assertNotThrown(bus.unsubscribe("abc"));
144 }