Mercurial > irccd
comparison libirccd-js/irccd/mod-plugin.cpp @ 292:671612cbc721
Irccd: split lib into libirccd-js, #564
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 05 Oct 2016 20:32:27 +0200 |
parents | |
children | 45065955ba2d |
comparison
equal
deleted
inserted
replaced
291:b490853404d9 | 292:671612cbc721 |
---|---|
1 /* | |
2 * js-plugin.cpp -- Irccd.Plugin API | |
3 * | |
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include "irccd.hpp" | |
20 #include "plugin-js.hpp" | |
21 #include "service-plugin.hpp" | |
22 #include "mod-irccd.hpp" | |
23 #include "mod-plugin.hpp" | |
24 | |
25 namespace irccd { | |
26 | |
27 namespace { | |
28 | |
29 const char PluginGlobal[] = "\xff""\xff""irccd-plugin-ptr"; | |
30 | |
31 /* | |
32 * wrap | |
33 * ------------------------------------------------------------------ | |
34 * | |
35 * Wrap function for these functions because they all takes the same arguments. | |
36 * | |
37 * - load, | |
38 * - reload, | |
39 * - unload. | |
40 */ | |
41 template <typename Func> | |
42 duk_idx_t wrap(duk_context *ctx, int nret, Func &&func) | |
43 { | |
44 std::string name = duk_require_string(ctx, 0); | |
45 | |
46 try { | |
47 func(dukx_get_irccd(ctx), name); | |
48 } catch (const std::out_of_range &ex) { | |
49 dukx_throw(ctx, ReferenceError(ex.what())); | |
50 } catch (const std::exception &ex) { | |
51 dukx_throw(ctx, Error(ex.what())); | |
52 } | |
53 | |
54 return nret; | |
55 } | |
56 | |
57 /* | |
58 * set | |
59 * ------------------------------------------------------------------ | |
60 * | |
61 * This setter is used to replace the Irccd.Plugin.(config|format) property when | |
62 * the plugin assign a new one. | |
63 * | |
64 * Because the plugin configuration always has higher priority, when a new | |
65 * object is assigned to 'config' or to the 'format' property, the plugin | |
66 * configuration is merged to the assigned one, adding or replacing any values. | |
67 * | |
68 * Example: | |
69 * | |
70 * Plugin 'xyz' does: | |
71 * | |
72 * Irccd.Plugin.config = { | |
73 * mode: "simple", | |
74 * level: "123" | |
75 * }; | |
76 * | |
77 * The user configuration is: | |
78 * | |
79 * [plugin.xyz] | |
80 * mode = "hard" | |
81 * path = "/var" | |
82 * | |
83 * The final user table looks like this: | |
84 * | |
85 * Irccd.Plugin.config = { | |
86 * mode: "hard", | |
87 * level: "123", | |
88 * path: "/var" | |
89 */ | |
90 duk_ret_t set(duk_context *ctx, const char *name) | |
91 { | |
92 if (!duk_is_object(ctx, 0)) | |
93 duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name); | |
94 | |
95 // Merge old table with new one. | |
96 duk_get_global_string(ctx, name); | |
97 duk_enum(ctx, -1, 0); | |
98 | |
99 while (duk_next(ctx, -1, true)) | |
100 duk_put_prop(ctx, 0); | |
101 | |
102 // Pop enum and old table. | |
103 duk_pop_2(ctx); | |
104 | |
105 // Replace the old table with the new assigned one. | |
106 duk_put_global_string(ctx, name); | |
107 | |
108 return 0; | |
109 } | |
110 | |
111 /* | |
112 * get | |
113 * ------------------------------------------------------------------ | |
114 * | |
115 * Get the Irccd.Plugin.(config|format) property. | |
116 */ | |
117 duk_ret_t get(duk_context *ctx, const char *name) | |
118 { | |
119 duk_get_global_string(ctx, name); | |
120 | |
121 return 1; | |
122 } | |
123 | |
124 /* | |
125 * setConfig | |
126 * ------------------------------------------------------------------ | |
127 * | |
128 * Wrap setter for Irccd.Plugin.config property. | |
129 */ | |
130 duk_ret_t setConfig(duk_context *ctx) | |
131 { | |
132 return set(ctx, JsPlugin::ConfigProperty); | |
133 } | |
134 | |
135 /* | |
136 * getConfig | |
137 * ------------------------------------------------------------------ | |
138 * | |
139 * Wrap getter for Irccd.Plugin.config property. | |
140 */ | |
141 duk_ret_t getConfig(duk_context *ctx) | |
142 { | |
143 return get(ctx, JsPlugin::ConfigProperty); | |
144 } | |
145 | |
146 /* | |
147 * setFormat | |
148 * ------------------------------------------------------------------ | |
149 * | |
150 * Wrap setter for Irccd.Plugin.format property. | |
151 */ | |
152 duk_ret_t setFormat(duk_context *ctx) | |
153 { | |
154 return set(ctx, JsPlugin::FormatProperty); | |
155 } | |
156 | |
157 /* | |
158 * getFormat | |
159 * ------------------------------------------------------------------ | |
160 * | |
161 * Wrap getter for Irccd.Plugin.format property. | |
162 */ | |
163 duk_ret_t getFormat(duk_context *ctx) | |
164 { | |
165 return get(ctx, JsPlugin::FormatProperty); | |
166 } | |
167 | |
168 /* | |
169 * Function: Irccd.Plugin.info([name]) | |
170 * ------------------------------------------------------------------ | |
171 * | |
172 * Get information about a plugin. | |
173 * | |
174 * The returned object as the following properties: | |
175 * | |
176 * - name: (string) the plugin identifier, | |
177 * - author: (string) the author, | |
178 * - license: (string) the license, | |
179 * - summary: (string) a short description, | |
180 * - version: (string) the version | |
181 * | |
182 * Arguments: | |
183 * - name, the plugin identifier, if not specified the current plugin is | |
184 * selected. | |
185 * Returns: | |
186 * The plugin information or undefined if the plugin was not found. | |
187 */ | |
188 duk_idx_t info(duk_context *ctx) | |
189 { | |
190 std::shared_ptr<Plugin> plugin; | |
191 | |
192 if (duk_get_top(ctx) >= 1) | |
193 plugin = dukx_get_irccd(ctx).plugins().get(duk_require_string(ctx, 0)); | |
194 else | |
195 plugin = dukx_get_plugin(ctx); | |
196 | |
197 if (!plugin) | |
198 return 0; | |
199 | |
200 duk_push_object(ctx); | |
201 dukx_push_std_string(ctx, plugin->name()); | |
202 duk_put_prop_string(ctx, -2, "name"); | |
203 dukx_push_std_string(ctx, plugin->author()); | |
204 duk_put_prop_string(ctx, -2, "author"); | |
205 dukx_push_std_string(ctx, plugin->license()); | |
206 duk_put_prop_string(ctx, -2, "license"); | |
207 dukx_push_std_string(ctx, plugin->summary()); | |
208 duk_put_prop_string(ctx, -2, "summary"); | |
209 dukx_push_std_string(ctx, plugin->version()); | |
210 duk_put_prop_string(ctx, -2, "version"); | |
211 | |
212 return 1; | |
213 } | |
214 | |
215 /* | |
216 * Function: Irccd.Plugin.list() | |
217 * ------------------------------------------------------------------ | |
218 * | |
219 * Get the list of plugins, the array returned contains all plugin names. | |
220 * | |
221 * Returns: | |
222 * The list of all plugin names. | |
223 */ | |
224 duk_idx_t list(duk_context *ctx) | |
225 { | |
226 dukx_push_array(ctx, dukx_get_irccd(ctx).plugins().list(), [] (auto ctx, auto plugin) { | |
227 dukx_push_std_string(ctx, plugin->name()); | |
228 }); | |
229 | |
230 return 1; | |
231 } | |
232 | |
233 /* | |
234 * Function: Irccd.Plugin.load(name) | |
235 * ------------------------------------------------------------------ | |
236 * | |
237 * Load a plugin by name. This function will search through the standard | |
238 * directories. | |
239 * | |
240 * Arguments: | |
241 * - name, the plugin identifier. | |
242 * Throws: | |
243 * - Error on errors, | |
244 * - ReferenceError if the plugin was not found. | |
245 */ | |
246 duk_idx_t load(duk_context *ctx) | |
247 { | |
248 return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { | |
249 irccd.plugins().load(name); | |
250 }); | |
251 } | |
252 | |
253 /* | |
254 * Function: Irccd.Plugin.reload(name) | |
255 * ------------------------------------------------------------------ | |
256 * | |
257 * Reload a plugin by name. | |
258 * | |
259 * Arguments: | |
260 * - name, the plugin identifier. | |
261 * Throws: | |
262 * - Error on errors, | |
263 * - ReferenceError if the plugin was not found. | |
264 */ | |
265 duk_idx_t reload(duk_context *ctx) | |
266 { | |
267 return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { | |
268 irccd.plugins().reload(name); | |
269 }); | |
270 } | |
271 | |
272 /* | |
273 * Function: Irccd.Plugin.unload(name) | |
274 * ------------------------------------------------------------------ | |
275 * | |
276 * Unload a plugin by name. | |
277 * | |
278 * Arguments: | |
279 * - name, the plugin identifier. | |
280 * Throws: | |
281 * - Error on errors, | |
282 * - ReferenceError if the plugin was not found. | |
283 */ | |
284 duk_idx_t unload(duk_context *ctx) | |
285 { | |
286 return wrap(ctx, 0, [&] (Irccd &irccd, const std::string &name) { | |
287 irccd.plugins().unload(name); | |
288 }); | |
289 } | |
290 | |
291 const duk_function_list_entry functions[] = { | |
292 { "info", info, DUK_VARARGS }, | |
293 { "list", list, 0 }, | |
294 { "load", load, 1 }, | |
295 { "reload", reload, 1 }, | |
296 { "unload", unload, 1 }, | |
297 { nullptr, nullptr, 0 } | |
298 }; | |
299 | |
300 } // !namespace | |
301 | |
302 PluginModule::PluginModule() noexcept | |
303 : Module("Irccd.Plugin") | |
304 { | |
305 } | |
306 | |
307 void PluginModule::load(Irccd &, const std::shared_ptr<JsPlugin> &plugin) | |
308 { | |
309 StackAssert sa(plugin->context()); | |
310 | |
311 duk_push_pointer(plugin->context(), new std::shared_ptr<JsPlugin>(plugin)); | |
312 duk_put_global_string(plugin->context(), PluginGlobal); | |
313 duk_get_global_string(plugin->context(), "Irccd"); | |
314 duk_push_object(plugin->context()); | |
315 duk_put_function_list(plugin->context(), -1, functions); | |
316 | |
317 // 'config' property. | |
318 duk_push_string(plugin->context(), "config"); | |
319 duk_push_c_function(plugin->context(), getConfig, 0); | |
320 duk_push_c_function(plugin->context(), setConfig, 1); | |
321 duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); | |
322 | |
323 // 'format' property. | |
324 duk_push_string(plugin->context(), "format"); | |
325 duk_push_c_function(plugin->context(), getFormat, 0); | |
326 duk_push_c_function(plugin->context(), setFormat, 1); | |
327 duk_def_prop(plugin->context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); | |
328 | |
329 duk_put_prop_string(plugin->context(), -2, "Plugin"); | |
330 duk_pop(plugin->context()); | |
331 } | |
332 | |
333 void PluginModule::unload(Irccd &, const std::shared_ptr<JsPlugin> &plugin) | |
334 { | |
335 StackAssert sa(plugin->context()); | |
336 | |
337 duk_push_global_object(plugin->context()); | |
338 duk_get_prop_string(plugin->context(), -1, PluginGlobal); | |
339 delete static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(plugin->context(), -1)); | |
340 duk_pop(plugin->context()); | |
341 duk_del_prop_string(plugin->context(), -1, PluginGlobal); | |
342 duk_pop(plugin->context()); | |
343 } | |
344 | |
345 std::shared_ptr<JsPlugin> dukx_get_plugin(duk_context *ctx) | |
346 { | |
347 StackAssert sa(ctx); | |
348 | |
349 duk_get_global_string(ctx, PluginGlobal); | |
350 auto plugin = static_cast<std::shared_ptr<JsPlugin> *>(duk_to_pointer(ctx, -1)); | |
351 duk_pop(ctx); | |
352 | |
353 return *plugin; | |
354 } | |
355 | |
356 } // !irccd |