util.events: event handlers can now return a result, which also interrupts further...
[prosody.git] / util / events.lua
1 \r
2 local ipairs = ipairs;\r
3 local pairs = pairs;\r
4 local t_insert = table.insert;\r
5 local select = select;\r
6 \r
7 module "events"\r
8 \r
9 function new()\r
10         local dispatchers = {};\r
11         local handlers = {};\r
12         local event_map = {};\r
13         local function _rebuild_index() -- TODO optimize index rebuilding\r
14                 for event, _handlers in pairs(event_map) do\r
15                         local index = handlers[event];\r
16                         if index then\r
17                                 for i=#index,1,-1 do index[i] = nil; end\r
18                         else index = {}; handlers[event] = index; end\r
19                         for handler in pairs(_handlers) do\r
20                                 t_insert(index, handler);\r
21                         end\r
22                 end\r
23         end;\r
24         local function add_handler(event, handler)\r
25                 local map = event_map[event];\r
26                 if map then\r
27                         map[handler] = true;\r
28                 else\r
29                         map = {[handler] = true};\r
30                         event_map[event] = map;\r
31                 end\r
32                 _rebuild_index();\r
33         end;\r
34         local function remove_handler(event, handler)\r
35                 local map = event_map[event];\r
36                 if map then\r
37                         map[handler] = nil;\r
38                         _rebuild_index();\r
39                 end\r
40         end;\r
41         local function add_plugin(plugin)\r
42                 for event, handler in pairs(plugin) do\r
43                         add_handler(event, handler);\r
44                 end\r
45         end;\r
46         local function remove_plugin(plugin)\r
47                 for event, handler in pairs(plugin) do\r
48                         remove_handler(event, handler);\r
49                 end\r
50         end;\r
51         local function _create_dispatcher(event) -- FIXME duplicate code in fire_event\r
52                 local h = handlers[event];\r
53                 if not h then h = {}; handlers[event] = h; end\r
54                 local dispatcher = function(data)\r
55                         for _, handler in ipairs(h) do\r
56                                 local ret = handler(data);\r
57                                 if ret ~= nil then return ret; end\r
58                         end\r
59                 end;\r
60                 dispatchers[event] = dispatcher;\r
61                 return dispatcher;\r
62         end;\r
63         local function get_dispatcher(event)\r
64                 return dispatchers[event] or _create_dispatcher(event);\r
65         end;\r
66         local function fire_event(event, data) -- FIXME duplicates dispatcher code\r
67                 local h = handlers[event];\r
68                 if h then\r
69                         for _, handler in ipairs(h) do\r
70                                 local ret = handler(data);\r
71                                 if ret ~= nil then return ret; end\r
72                         end\r
73                 end\r
74         end;\r
75         local function get_named_arg_dispatcher(event, ...)\r
76                 local dispatcher = get_dispatcher(event);\r
77                 local keys = {...};\r
78                 local data = {};\r
79                 return function(...)\r
80                         for i, key in ipairs(keys) do data[key] = select(i, ...); end\r
81                         dispatcher(data);\r
82                 end;\r
83         end;\r
84         return {\r
85                 add_handler = add_handler;\r
86                 remove_handler = remove_handler;\r
87                 add_plugin = add_plugin;\r
88                 remove_plugin = remove_plugin;\r
89                 get_dispatcher = get_dispatcher;\r
90                 fire_event = fire_event;\r
91                 get_named_arg_dispatcher = get_named_arg_dispatcher;\r
92                 _dispatchers = dispatchers;\r
93                 _handlers = handlers;\r
94                 _event_map = event_map;\r
95         };\r
96 end\r
97 \r
98 return _M;\r