1 ///
2 /// Defines an architecture for event receiving and subscribing.
3 /// Events may be structs or components.
4 /// Event receivers must implement the Receiver(E) interface.
5 ///
6 /// Copyright: Copyright (c) 2014 James Zhu.
7 ///
8 /// License: MIT License (Expat). See accompanying file LICENSE.
9 ///
10 /// Authors: James Zhu <github.com/jzhu98>
11 ///
12 
13 module star.event;
14 
15 import std.traits;
16 
17 debug import std.stdio;
18 
19 private interface BaseReceiver
20 {
21 }
22 
23 template typename(C)
24 {
25     alias typename = mangledName!C;
26 }
27 
28 /// Recieves events of type E.
29 interface Receiver(E) : BaseReceiver
30 {
31     /// Event callback for an event E.
32     void receive(E event) pure nothrow;
33 }
34 
35 /// Manages event subscription and emission.
36 class EventManager
37 {
38 public:
39     /// Subscribe reciever to a certain event E.
40     void subscribe(E)(Receiver!E receiver) pure nothrow @safe
41     {
42         auto name = typename!E;
43         if (name in _receivers)
44         {
45             _receivers[name] ~= receiver;
46         }
47         else
48         {
49             _receivers[name] = [receiver];
50         }
51     }
52 
53     /// Notify all receivers of an event E. Calls their receive callback.
54     void emit(E)(E event) pure nothrow @trusted
55     {
56         if (typename!E in _receivers)
57         {
58             foreach(r; _receivers[typename!E])
59             {
60                 auto receiver = cast(Receiver!E) r;
61                 receiver.receive(event);
62             }
63         }
64     }
65 private:
66     BaseReceiver[][string] _receivers;
67 }
68 
69 unittest
70 {
71     struct Explosion { }
72 
73     class Block : Receiver!Explosion
74     {
75         bool destroyed = false;
76         void receive(Explosion event)
77         {
78             destroyed = true;
79         }
80     }
81 
82     auto manager = new EventManager;
83     auto block = new Block;
84     assert(block.destroyed == false);
85 
86     manager.subscribe!Explosion(block);
87     assert(manager._receivers.length == 1);
88     assert(typename!Explosion in manager._receivers);
89     assert(manager._receivers[typename!Explosion].length == 1);
90 
91     bool hasReceiver(E)(EventManager manager, Receiver!E receiver)
92     {
93         bool result = false;
94         foreach(r; manager._receivers[typename!E])
95         {
96             if (r == receiver)
97             {
98                 result = true;
99             }
100         }
101         return result;
102     }
103 
104     assert(hasReceiver!Explosion(manager, block));
105 
106     manager.emit(Explosion());
107     assert(block.destroyed == true);
108 }