Mercurial > irccd
changeset 758:445c071e8efb
Irccd: Javascript API cleanup
- Place all related code to `js` namespace,
- Import duk.hpp and duk.cpp from libduk.
line wrap: on
line diff
--- a/irccdctl/cli.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/irccdctl/cli.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -34,6 +34,14 @@ namespace { +template <typename T> +auto bind() noexcept -> cli::constructor +{ + return [] () noexcept { + return std::make_unique<T>(); + }; +} + auto format(std::vector<std::string> args) -> std::string { auto result = option::read(args, { @@ -223,6 +231,38 @@ // {{{ cli +const std::vector<cli::constructor> cli::registry{ + bind<plugin_config_cli>(), + bind<plugin_info_cli>(), + bind<plugin_list_cli>(), + bind<plugin_load_cli>(), + bind<plugin_reload_cli>(), + bind<plugin_unload_cli>(), + bind<rule_add_cli>(), + bind<rule_edit_cli>(), + bind<rule_info_cli>(), + bind<rule_info_cli>(), + bind<rule_list_cli>(), + bind<rule_move_cli>(), + bind<rule_remove_cli>(), + bind<server_connect_cli>(), + bind<server_disconnect_cli>(), + bind<server_info_cli>(), + bind<server_invite_cli>(), + bind<server_join_cli>(), + bind<server_kick_cli>(), + bind<server_list_cli>(), + bind<server_me_cli>(), + bind<server_message_cli>(), + bind<server_mode_cli>(), + bind<server_nick_cli>(), + bind<server_notice_cli>(), + bind<server_part_cli>(), + bind<server_reconnect_cli>(), + bind<server_topic_cli>(), + bind<watch_cli>() +}; + void cli::recv_response(ctl::controller& ctl, nlohmann::json req, handler_t handler) { ctl.read([&ctl, req, handler, this] (auto code, auto message) {
--- a/irccdctl/cli.hpp Mon Aug 06 21:27:00 2018 +0200 +++ b/irccdctl/cli.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,6 +19,7 @@ #ifndef IRCCD_CTL_CLI_HPP #define IRCCD_CTL_CLI_HPP +#include <functional> #include <stdexcept> #include <string> #include <string_view> @@ -38,10 +39,20 @@ class cli { public: /** - * Convenient handler for request function. + * \brief Convenient handler for request function. */ using handler_t = std::function<void (nlohmann::json)>; + /** + * \brief Command constructor factory. + */ + using constructor = std::function<auto () -> std::unique_ptr<cli>>; + + /** + * \brief Registry of all commands. + */ + static const std::vector<constructor> registry; + private: void recv_response(ctl::controller&, nlohmann::json, handler_t);
--- a/irccdctl/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/irccdctl/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -466,38 +466,11 @@ -- argc; ++ argv; - const auto add = [] (auto c) { - commands.emplace(c->get_name(), std::move(c)); - }; + for (const auto& f : cli::registry) { + auto c = f(); - add(std::make_unique<plugin_config_cli>()); - add(std::make_unique<plugin_info_cli>()); - add(std::make_unique<plugin_list_cli>()); - add(std::make_unique<plugin_load_cli>()); - add(std::make_unique<plugin_reload_cli>()); - add(std::make_unique<plugin_unload_cli>()); - add(std::make_unique<server_connect_cli>()); - add(std::make_unique<server_disconnect_cli>()); - add(std::make_unique<server_info_cli>()); - add(std::make_unique<server_invite_cli>()); - add(std::make_unique<server_join_cli>()); - add(std::make_unique<server_kick_cli>()); - add(std::make_unique<server_list_cli>()); - add(std::make_unique<server_me_cli>()); - add(std::make_unique<server_message_cli>()); - add(std::make_unique<server_mode_cli>()); - add(std::make_unique<server_nick_cli>()); - add(std::make_unique<server_notice_cli>()); - add(std::make_unique<server_part_cli>()); - add(std::make_unique<server_reconnect_cli>()); - add(std::make_unique<server_topic_cli>()); - add(std::make_unique<rule_add_cli>()); - add(std::make_unique<rule_edit_cli>()); - add(std::make_unique<rule_list_cli>()); - add(std::make_unique<rule_info_cli>()); - add(std::make_unique<rule_move_cli>()); - add(std::make_unique<rule_remove_cli>()); - add(std::make_unique<watch_cli>()); + commands.emplace(c->get_name(), std::move(c)); + } } void do_connect()
--- a/libirccd-js/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ b/libirccd-js/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -22,40 +22,40 @@ set( HEADERS - ${libirccd-js_SOURCE_DIR}/irccd/js/directory_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/duktape.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/duktape_vector.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/elapsed_timer_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/file_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/irccd_jsapi.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/directory_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/duk.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/elapsed_timer_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/file_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/irccd_js_api.hpp ${libirccd-js_SOURCE_DIR}/irccd/js/js_plugin.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/logger_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/plugin_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/server_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/system_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/timer_jsapi.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/logger_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/plugin_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/server_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/system_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/timer_js_api.hpp ${libirccd-js_SOURCE_DIR}/irccd/js/unicode.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/unicode_jsapi.hpp - ${libirccd-js_SOURCE_DIR}/irccd/js/util_jsapi.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/unicode_js_api.hpp + ${libirccd-js_SOURCE_DIR}/irccd/js/util_js_api.hpp ) set( SOURCES - ${libirccd-js_SOURCE_DIR}/irccd/js/directory_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/elapsed_timer_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/file_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/irccd_jsapi.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/duk.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/directory_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/elapsed_timer_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/file_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/irccd_js_api.cpp ${libirccd-js_SOURCE_DIR}/irccd/js/js_plugin.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/logger_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/plugin_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/server_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/system_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/timer_jsapi.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/logger_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/plugin_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/server_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/system_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/timer_js_api.cpp ${libirccd-js_SOURCE_DIR}/irccd/js/unicode.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/unicode_jsapi.cpp - ${libirccd-js_SOURCE_DIR}/irccd/js/util_jsapi.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/unicode_js_api.cpp + ${libirccd-js_SOURCE_DIR}/irccd/js/util_js_api.cpp ) irccd_define_library(
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/directory_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,406 @@ +/* + * directory_js_api.cpp -- Irccd.Directory API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/predef/os.h> + +#include <irccd/sysconfig.hpp> + +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <fstream> +#include <regex> +#include <stdexcept> +#include <string> + +#include <irccd/fs_util.hpp> + +#include "directory_js_api.hpp" +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" + +namespace fs = boost::filesystem; + +namespace irccd::js { + +namespace { + +// {{{ wrap + +/* + * Wrap the function and raise appropriate error in case of need. + */ +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const boost::system::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ path + +/* + * Get the path associated with the directory object as this. + */ +auto path(duk_context* ctx) -> std::string +{ + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, "path"); + + if (duk_get_type(ctx, -1) != DUK_TYPE_STRING) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object"); + + const auto ret = duk::get<std::string>(ctx, -1); + + if (ret.empty()) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path"); + + duk_pop_n(ctx, 2); + + return ret; +} + +// }}} + +// {{{ find + +/* + * Generic find function for: + * + * - Irccd.Directory.find + * - Irccd.Directory.prototype.find + * + * The pattern_index is the argument where to test if the argument is a regex or + * a string. + */ +auto find(duk_context* ctx, std::string base, bool recursive, int pattern_index) -> duk_ret_t +{ + /* + * Helper for checking if it's a valid RegExp object. + */ + const auto is_regex = [&] { + duk_get_global_string(ctx, "RegExp"); + const auto result = duk_instanceof(ctx, pattern_index, -1); + duk_pop(ctx); + + return result; + }; + + /* + * Helper for getting regex source. + */ + const auto pattern = [&] { + duk_get_prop_string(ctx, pattern_index, "source"); + const auto pattern = duk_to_string(ctx, -1); + duk_pop(ctx); + + return pattern; + }; + + std::string path; + + if (duk_is_string(ctx, pattern_index)) + path = fs_util::find(base, duk::get<std::string>(ctx, pattern_index), recursive); + else if (is_regex()) + path = fs_util::find(base, pattern(), recursive); + else + throw duk::type_error("pattern must be a string or a regex expression"); + + if (path.empty()) + return 0; + + return duk::push(ctx, path); +} + +// }}} + +// {{{ remove + +/* + * Generic remove function for: + * + * - Irccd.Directory.remove + * - Irccd.Directory.prototype.remove + */ +auto remove(const std::string& path, bool recursive) -> duk_ret_t +{ + if (!boost::filesystem::is_directory(path)) + throw std::system_error(make_error_code(std::errc::invalid_argument)); + + if (!recursive) + boost::filesystem::remove(path); + else + boost::filesystem::remove_all(path); + + return 0; +} + +// }}} + +// {{{ Irccd.Directory.prototype.find + +/* + * Method: Irccd.Directory.prototype.find(pattern, recursive) + * -------------------------------------------------------- + * + * Synonym of Irccd.Directory.find(path, pattern, recursive) but the path is + * taken from the directory object. + * + * Arguments: + * - pattern, the regular expression or file name, + * - recursive, set to true to search recursively (default: false). + * Returns: + * The path to the file or undefined if not found. + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_prototype_find(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return find(ctx, path(ctx), duk::get<bool>(ctx, 1), 0); + }); +} + +// }}} + +// {{{ Irccd.Directory.prototype.remove + +/* + * Method: Irccd.Directory.prototype.remove(recursive) + * -------------------------------------------------------- + * + * Synonym of Directory.remove(recursive) but the path is taken from the + * directory object. + * + * Arguments: + * - recursive, recursively or not (default: false). + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_prototype_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return remove(path(ctx), duk::get<bool>(ctx, 0)); + }); +} + +// }}} + +// {{{ Irccd.Directory [constructor] + +/* + * Function: Irccd.Directory(path) [constructor] + * -------------------------------------------------------- + * + * Opens and read the directory at the specified path. + * + * Arguments: + * - path, the path to the directory, + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_constructor(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + if (!duk_is_constructor_call(ctx)) + return 0; + + const auto path = duk::require<std::string>(ctx, 0); + + if (!boost::filesystem::is_directory(path)) + throw std::system_error(make_error_code(std::errc::invalid_argument)); + + duk_push_this(ctx); + + // 'entries' property. + duk_push_string(ctx, "entries"); + duk_push_array(ctx); + + unsigned i = 0; + for (const auto& entry : boost::filesystem::directory_iterator(path)) { + duk_push_object(ctx); + duk::push(ctx, entry.path().filename().string()); + duk_put_prop_string(ctx, -2, "name"); + duk_push_int(ctx, entry.status().type()); + duk_put_prop_string(ctx, -2, "type"); + duk_put_prop_index(ctx, -2, i++); + } + + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + + // 'path' property. + duk::push(ctx, "path"); + duk::push(ctx, path); + duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Directory.find + +/* + * Function: Irccd.Directory.find(path, pattern, recursive) + * -------------------------------------------------------- + * + * Find an entry by a pattern or a regular expression. + * + * Arguments: + * - path, the base path, + * - pattern, the regular expression or file name, + * - recursive, set to true to search recursively (default: false). + * Returns: + * The path to the file or undefined on errors or not found. + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_find(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return find(ctx, duk::require<std::string>(ctx, 0), duk::get<bool>(ctx, 2), 1); + }); +} + +// }}} + +// {{{ Irccd.Directory.remove + +/* + * Function: Irccd.Directory.remove(path, recursive) + * -------------------------------------------------------- + * + * Remove the directory optionally recursively. + * + * Arguments: + * - path, the path to the directory, + * - recursive, recursively or not (default: false). + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return remove(duk::require<std::string>(ctx, 0), duk::get<bool>(ctx, 1)); + }); +} + +// }}} + +// {{{ Irccd.Directory.mkdir + +/* + * Function: Irccd.Directory.mkdir(path, mode = 0700) + * -------------------------------------------------------- + * + * Create a directory specified by path. It will create needed subdirectories + * just like you have invoked mkdir -p. + * + * Arguments: + * - path, the path to the directory, + * Throws: + * - Irccd.SystemError on errors + */ +auto Directory_mkdir(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + boost::filesystem::create_directories(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ definitions + +const duk_function_list_entry methods[] = { + { "find", Directory_prototype_find, DUK_VARARGS }, + { "remove", Directory_prototype_remove, 1 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "find", Directory_find, DUK_VARARGS }, + { "mkdir", Directory_mkdir, DUK_VARARGS }, + { "remove", Directory_remove, DUK_VARARGS }, + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "TypeFile", static_cast<int>(fs::regular_file) }, + { "TypeDir", static_cast<int>(fs::directory_file) }, + { "TypeLink", static_cast<int>(fs::symlink_file) }, + { "TypeBlock", static_cast<int>(fs::block_file) }, + { "TypeCharacter", static_cast<int>(fs::character_file) }, + { "TypeFifo", static_cast<int>(fs::fifo_file) }, + { "TypeSocket", static_cast<int>(fs::socket_file) }, + { "TypeUnknown", static_cast<int>(fs::type_unknown) }, + { nullptr, 0 } +}; + +// }}} + +} // !namespace + +// {{{ directory_js_api + +auto directory_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Directory"; +} + +void directory_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), Directory_constructor, 2); + duk_put_number_list(plugin->get_context(), -1, constants); + duk_put_function_list(plugin->get_context(), -1, functions); + +#if BOOST_OS_WINDOWS + duk_push_string(plugin->get_context(), "\\"); +#else + duk_push_string(plugin->get_context(), "/"); +#endif + + duk_put_prop_string(plugin->get_context(), -2, "separator"); + + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "Directory"); + duk_pop(plugin->get_context()); +} + +// }}} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/directory_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * directory_js_api.hpp -- Irccd.Directory API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_DIRECTORY_JS_API_HPP +#define IRCCD_JS_DIRECTORY_JS_API_HPP + +/** + * \file directory_js_api.hpp + * \brief Irccd.Directory Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.Directory Javascript API. + * \ingroup js_api + */ +class directory_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_DIRECTORY_JS_API_HPP
--- a/libirccd-js/irccd/js/directory_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,376 +0,0 @@ -/* - * directory_jsapi.cpp -- Irccd.Directory API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/predef/os.h> - -#include <irccd/sysconfig.hpp> - -#include <cerrno> -#include <cstdio> -#include <cstring> -#include <fstream> -#include <regex> -#include <stdexcept> -#include <string> - -#include <irccd/fs_util.hpp> - -#include "directory_jsapi.hpp" -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" - -namespace fs = boost::filesystem; - -namespace irccd { - -namespace { - -template <typename Handler> -duk_ret_t wrap(duk_context* ctx, Handler handler) -{ - try { - return handler(); - } catch (const boost::system::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -std::string path(duk_context *ctx) -{ - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "path"); - - if (duk_get_type(ctx, -1) != DUK_TYPE_STRING) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Directory object"); - - const auto ret = dukx_get<std::string>(ctx, -1); - - if (ret.empty()) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "directory object has empty path"); - - duk_pop_n(ctx, 2); - - return ret; -} - -/* - * Generic find function for: - * - * - Irccd.Directory.find - * - Irccd.Directory.prototype.find - * - * The pattern_index is the argument where to test if the argument is a regex or - * a string. - */ -duk_ret_t find(duk_context* ctx, std::string base, bool recursive, int pattern_index) -{ - /* - * Helper for checking if it's a valid RegExp object. - */ - const auto is_regex = [&] { - duk_get_global_string(ctx, "RegExp"); - const auto result = duk_instanceof(ctx, pattern_index, -1); - duk_pop(ctx); - - return result; - }; - - /** - * Helper for getting regex source. - */ - const auto pattern = [&] { - duk_get_prop_string(ctx, pattern_index, "source"); - const auto pattern = duk_to_string(ctx, -1); - duk_pop(ctx); - - return pattern; - }; - - std::string path; - - if (duk_is_string(ctx, pattern_index)) - path = fs_util::find(base, dukx_get<std::string>(ctx, pattern_index), recursive); - else if (is_regex()) - path = fs_util::find(base, pattern(), recursive); - else - throw dukx_type_error("pattern must be a string or a regex expression"); - - if (path.empty()) - return 0; - - return dukx_push(ctx, path); -} - -/* - * Generic remove function for: - * - * - Irccd.Directory.remove - * - Irccd.Directory.prototype.remove - */ -duk_ret_t remove(const std::string& path, bool recursive) -{ - if (!boost::filesystem::is_directory(path)) - throw std::system_error(make_error_code(std::errc::invalid_argument)); - - if (!recursive) - boost::filesystem::remove(path); - else - boost::filesystem::remove_all(path); - - return 0; -} - -// {{{ Irccd.Directory.prototype.find - -/* - * Method: Irccd.Directory.prototype.find(pattern, recursive) - * -------------------------------------------------------- - * - * Synonym of Irccd.Directory.find(path, pattern, recursive) but the path is - * taken from the directory object. - * - * Arguments: - * - pattern, the regular expression or file name, - * - recursive, set to true to search recursively (default: false). - * Returns: - * The path to the file or undefined if not found. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Directory_prototype_find(duk_context* ctx) -{ - return wrap(ctx, [&] { - return find(ctx, path(ctx), dukx_get<bool>(ctx, 1), 0); - }); -} - -// }}} - -// {{{ Irccd.Directory.prototype.remove - -/* - * Method: Irccd.Directory.prototype.remove(recursive) - * -------------------------------------------------------- - * - * Synonym of Directory.remove(recursive) but the path is taken from the - * directory object. - * - * Arguments: - * - recursive, recursively or not (default: false). - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Directory_prototype_remove(duk_context* ctx) -{ - return wrap(ctx, [&] { - return remove(path(ctx), dukx_get<bool>(ctx, 0)); - }); -} - -// }}} - -// {{{ Irccd.Directory [constructor] - -/* - * Function: Irccd.Directory(path) [constructor] - * -------------------------------------------------------- - * - * Opens and read the directory at the specified path. - * - * Arguments: - * - path, the path to the directory, - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Directory_constructor(duk_context* ctx) -{ - return wrap(ctx, [&] { - if (!duk_is_constructor_call(ctx)) - return 0; - - const auto path = dukx_require<std::string>(ctx, 0); - - if (!boost::filesystem::is_directory(path)) - throw std::system_error(make_error_code(std::errc::invalid_argument)); - - duk_push_this(ctx); - - // 'entries' property. - duk_push_string(ctx, "entries"); - duk_push_array(ctx); - - unsigned i = 0; - for (const auto& entry : boost::filesystem::directory_iterator(path)) { - duk_push_object(ctx); - dukx_push(ctx, entry.path().filename().string()); - duk_put_prop_string(ctx, -2, "name"); - duk_push_int(ctx, entry.status().type()); - duk_put_prop_string(ctx, -2, "type"); - duk_put_prop_index(ctx, -2, i++); - } - - duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - - // 'path' property. - dukx_push(ctx, "path"); - dukx_push(ctx, path); - duk_def_prop(ctx, -3, DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_VALUE); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Directory.find - -/* - * Function: Irccd.Directory.find(path, pattern, recursive) - * -------------------------------------------------------- - * - * Find an entry by a pattern or a regular expression. - * - * Arguments: - * - path, the base path, - * - pattern, the regular expression or file name, - * - recursive, set to true to search recursively (default: false). - * Returns: - * The path to the file or undefined on errors or not found. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Directory_find(duk_context* ctx) -{ - return wrap(ctx, [&] { - return find(ctx, dukx_require<std::string>(ctx, 0), dukx_get<bool>(ctx, 2), 1); - }); -} - -// }}} - -// {{{ Irccd.Directory.remove - -/* - * Function: Irccd.Directory.remove(path, recursive) - * -------------------------------------------------------- - * - * Remove the directory optionally recursively. - * - * Arguments: - * - path, the path to the directory, - * - recursive, recursively or not (default: false). - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Directory_remove(duk_context *ctx) -{ - return wrap(ctx, [&] { - return remove(dukx_require<std::string>(ctx, 0), dukx_get<bool>(ctx, 1)); - }); -} - -// }}} - -// {{{ Irccd.Directory.mkdir - -/* - * Function: Irccd.Directory.mkdir(path, mode = 0700) - * -------------------------------------------------------- - * - * Create a directory specified by path. It will create needed subdirectories - * just like you have invoked mkdir -p. - * - * Arguments: - * - path, the path to the directory, - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Directory_mkdir(duk_context *ctx) -{ - return wrap(ctx, [&] { - boost::filesystem::create_directories(dukx_require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -const duk_function_list_entry methods[] = { - { "find", Directory_prototype_find, DUK_VARARGS }, - { "remove", Directory_prototype_remove, 1 }, - { nullptr, nullptr, 0 } -}; - -const duk_function_list_entry functions[] = { - { "find", Directory_find, DUK_VARARGS }, - { "mkdir", Directory_mkdir, DUK_VARARGS }, - { "remove", Directory_remove, DUK_VARARGS }, - { nullptr, nullptr, 0 } -}; - -const duk_number_list_entry constants[] = { - { "TypeFile", static_cast<int>(fs::regular_file) }, - { "TypeDir", static_cast<int>(fs::directory_file) }, - { "TypeLink", static_cast<int>(fs::symlink_file) }, - { "TypeBlock", static_cast<int>(fs::block_file) }, - { "TypeCharacter", static_cast<int>(fs::character_file) }, - { "TypeFifo", static_cast<int>(fs::fifo_file) }, - { "TypeSocket", static_cast<int>(fs::socket_file) }, - { "TypeUnknown", static_cast<int>(fs::type_unknown) }, - { nullptr, 0 } -}; - -} // !namespace - -std::string directory_jsapi::get_name() const -{ - return "Irccd.Directory"; -} - -void directory_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), Directory_constructor, 2); - duk_put_number_list(plugin->get_context(), -1, constants); - duk_put_function_list(plugin->get_context(), -1, functions); - -#if BOOST_OS_WINDOWS - duk_push_string(plugin->get_context(), "\\"); -#else - duk_push_string(plugin->get_context(), "/"); -#endif - - duk_put_prop_string(plugin->get_context(), -2, "separator"); - - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "Directory"); - duk_pop(plugin->get_context()); -} - -} // !irccd
--- a/libirccd-js/irccd/js/directory_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * directory_jsapi.hpp -- Irccd.Directory API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_DIRECTORY_JSAPI_HPP -#define IRCCD_JS_DIRECTORY_JSAPI_HPP - -/** - * \file directory_jsapi.hpp - * \brief Irccd.Directory Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd.Directory Javascript API. - * \ingroup jsapi - */ -class directory_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_DIRECTORY_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/duk.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,396 @@ +/* + * duk.cpp -- miscellaneous Duktape extras + * + * Copyright (c) 2017-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <cstdio> + +#include "duk.hpp" + +namespace irccd::js::duk { + +// {{{ stack_guard + +stack_guard::stack_guard(duk_context* ctx, unsigned expected) noexcept +#if !defined(NDEBUG) + : context_(ctx) + , expected_(expected) + , at_start_(duk_get_top(ctx)) +#endif +{ +#if defined(NDEBUG) + (void)ctx; + (void)expected; +#endif +} + +stack_guard::~stack_guard() noexcept +{ +#if !defined(NDEBUG) + auto result = duk_get_top(context_) - at_start_; + + if (result != static_cast<int>(expected_)) { + std::fprintf(stderr, "Corrupt stack detection in stack_guard:\n"); + std::fprintf(stderr, " Size at start: %d\n", at_start_); + std::fprintf(stderr, " Size at end: %d\n", duk_get_top(context_)); + std::fprintf(stderr, " Expected (user): %u\n", expected_); + std::fprintf(stderr, " Expected (adjusted): %u\n", expected_ + at_start_); + std::fprintf(stderr, " Difference count: %+d\n", result - expected_); + std::abort(); + } +#endif +} + +// }}} + +// {{{ context + +context::context() noexcept + : handle_(duk_create_heap_default(), duk_destroy_heap) +{ +} + +context::operator duk_context*() noexcept +{ + return handle_.get(); +} + +context::operator duk_context*() const noexcept +{ + return handle_.get(); +} + +// }}} + +// {{{ stack_info + +stack_info::stack_info(std::string name, + std::string message, + std::string stack, + std::string file_name, + unsigned line_number) noexcept + : name_(std::move(name)) + , message_(std::move(message)) + , stack_(std::move(stack)) + , file_name_(std::move(file_name)) + , line_number_(line_number) +{ +} + +auto stack_info::get_name() const noexcept -> const std::string& +{ + return name_; +} + +auto stack_info::get_message() const noexcept -> const std::string& +{ + return message_; +} + +auto stack_info::get_stack() const noexcept -> const std::string& +{ + return stack_; +} + +auto stack_info::get_file_name() const noexcept -> const std::string& +{ + return file_name_; +} + +auto stack_info::get_line_number() const noexcept -> unsigned +{ + return line_number_; +} + +auto stack_info::what() const noexcept -> const char* +{ + return message_.c_str(); +} + +// }}} + +// {{{ error + +error::error(int type, std::string message) noexcept + : type_(type) + , message_(std::move(message)) +{ +} + +error::error(std::string message) noexcept + : message_(std::move(message)) +{ +} + +void error::create(duk_context* ctx) const +{ + duk_push_error_object(ctx, type_, "%s", message_.c_str()); +} + +// }}} + +// {{{ eval_error + +eval_error::eval_error(std::string message) noexcept + : error(DUK_ERR_EVAL_ERROR, std::move(message)) +{ +} + +// }}} + +// {{{ range_error + +range_error::range_error(std::string message) noexcept + : error(DUK_ERR_RANGE_ERROR, std::move(message)) +{ +} + +// }}} + +// {{{ reference_error + +reference_error::reference_error(std::string message) noexcept + : error(DUK_ERR_REFERENCE_ERROR, std::move(message)) +{ +} + +// }}} + +// {{{ syntax_error + +syntax_error::syntax_error(std::string message) noexcept + : error(DUK_ERR_SYNTAX_ERROR, std::move(message)) +{ +} + +// }}} + +// {{{ type_error + +type_error::type_error(std::string message) noexcept + : error(DUK_ERR_TYPE_ERROR, std::move(message)) +{ +} + +// }}} + +// {{{ uri_error + +uri_error::uri_error(std::string message) noexcept + : error(DUK_ERR_URI_ERROR, std::move(message)) +{ +} + +// }}} + +// {{{ get_stack + +auto get_stack(duk_context* ctx, int index, bool pop) -> stack_info +{ + index = duk_normalize_index(ctx, index); + + duk_get_prop_string(ctx, index, "name"); + auto name = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "message"); + auto message = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "fileName"); + auto file_name = duk_to_string(ctx, -1); + duk_get_prop_string(ctx, index, "lineNumber"); + auto line_number = duk_to_uint(ctx, -1); + duk_get_prop_string(ctx, index, "stack"); + auto stack = duk_to_string(ctx, -1); + duk_pop_n(ctx, 5); + + if (pop) + duk_remove(ctx, index); + + return { + std::move(name), + std::move(message), + std::move(stack), + std::move(file_name), + line_number + }; +} + +// }}} + +// {{{ type_traits<std::exception> + +void type_traits<std::exception>::raise(duk_context* ctx, const std::exception& ex) +{ + duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); +} + +// }}} + +// {{{ type_traits<error> + +void type_traits<error>::raise(duk_context* ctx, const error& ex) +{ + ex.create(ctx); + duk_throw(ctx); +} + +// }}} + +// {{{ type_traits<bool> + +void type_traits<bool>::push(duk_context* ctx, bool value) +{ + duk_push_boolean(ctx, value); +} + +auto type_traits<bool>::get(duk_context* ctx, duk_idx_t index) -> bool +{ + return duk_get_boolean(ctx, index); +} + +auto type_traits<bool>::require(duk_context* ctx, duk_idx_t index) -> bool +{ + return duk_require_boolean(ctx, index); +} + +// }}} + +// {{{ type_traits<duk_double_t> + +void type_traits<duk_double_t>::push(duk_context* ctx, duk_double_t value) +{ + duk_push_number(ctx, value); +} + +auto type_traits<duk_double_t>::get(duk_context* ctx, duk_idx_t index) -> duk_double_t +{ + return duk_get_number(ctx, index); +} + +auto type_traits<duk_double_t>::require(duk_context* ctx, duk_idx_t index) -> duk_double_t +{ + return duk_require_number(ctx, index); +} + +// }}} + +// {{{ type_traits<duk_int_t> + +void type_traits<duk_int_t>::push(duk_context* ctx, duk_int_t value) +{ + duk_push_int(ctx, value); +} + +auto type_traits<duk_int_t>::get(duk_context* ctx, duk_idx_t index) -> duk_int_t +{ + return duk_get_int(ctx, index); +} + +auto type_traits<duk_int_t>::require(duk_context* ctx, duk_idx_t index) -> duk_int_t +{ + return duk_require_int(ctx, index); +} + +// }}} + +// {{{ type_traits<duk_uint_t> + +void type_traits<duk_uint_t>::push(duk_context* ctx, duk_uint_t value) +{ + duk_push_uint(ctx, value); +} + +auto type_traits<duk_uint_t>::get(duk_context* ctx, duk_idx_t index) -> duk_uint_t +{ + return duk_get_uint(ctx, index); +} + +auto type_traits<duk_uint_t>::require(duk_context* ctx, duk_idx_t index) -> duk_uint_t +{ + return duk_require_uint(ctx, index); +} + +// }}} + +// {{{ type_traits<const char*> + +void type_traits<const char*>::push(duk_context* ctx, const char* value) +{ + duk_push_string(ctx, value); +} + +auto type_traits<const char*>::get(duk_context* ctx, duk_idx_t index) -> const char* +{ + return duk_get_string(ctx, index); +} + +auto type_traits<const char*>::require(duk_context* ctx, duk_idx_t index) -> const char* +{ + return duk_require_string(ctx, index); +} + +// }}} + +// {{{ type_traits<std::string> + +void type_traits<std::string>::push(duk_context* ctx, const std::string& value) +{ + duk_push_lstring(ctx, value.data(), value.size()); +} + +auto type_traits<std::string>::get(duk_context* ctx, duk_idx_t index) -> std::string +{ + duk_size_t length; + const char* str = duk_get_lstring(ctx, index, &length); + + return { str, length }; +} + +auto type_traits<std::string>::require(duk_context* ctx, duk_idx_t index) -> std::string +{ + duk_size_t length; + const char* str = duk_require_lstring(ctx, index, &length); + + return { str, length }; +} + +// }}} + +// {{{ type_traits<std::string_view> + +void type_traits<std::string_view>::push(duk_context* ctx, std::string_view value) +{ + duk_push_lstring(ctx, value.data(), value.size()); +} + +auto type_traits<std::string_view>::get(duk_context* ctx, duk_idx_t index) -> std::string_view +{ + duk_size_t length; + const char* str = duk_get_lstring(ctx, index, &length); + + return { str, length }; +} + +auto type_traits<std::string_view>::require(duk_context* ctx, duk_idx_t index) -> std::string_view +{ + duk_size_t length; + const char* str = duk_require_lstring(ctx, index, &length); + + return { str, length }; +} + +// }}} + +} // !irccd::js::duk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/duk.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,885 @@ +/* + * duk.hpp -- miscellaneous Duktape extras + * + * Copyright (c) 2017-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DUKTAPE_HPP +#define DUKTAPE_HPP + +/** + * \file duk.hpp + * \brief Miscellaneous Duktape extras + * \author David Demelier <markand@malikania.fr> + * \version 0.2.0 + */ + +#include <exception> +#include <memory> +#include <string> +#include <string_view> +#include <vector> + +#include "duktape.h" + +/** + * \brief Miscellaneous Duktape extras. + */ +namespace irccd::js::duk { + +// {{{ stack_guard + +/** + * \brief Stack sanity checker. + * + * Instanciate this class where you need to manipulate the Duktape stack outside + * a Duktape/C function, its destructor will examinate if the stack size matches + * the user expected size. + * + * When compiled with NDEBUG, this class does nothing. + * + * To use it, just declare an lvalue at the beginning of your function. + */ +class stack_guard { +#if !defined(NDEBUG) +private: + duk_context* context_; + unsigned expected_; + int at_start_; +#endif + +public: + /** + * Create the stack checker. + * + * No-op if NDEBUG is set. + * + * \param ctx the context + * \param expected the size expected relative to the already existing values + */ + stack_guard(duk_context* ctx, unsigned expected = 0) noexcept; + + /** + * Verify the expected size. + * + * No-op if NDEBUG is set. + */ + ~stack_guard() noexcept; +}; + +// }}} + +// {{{ context + +/** + * \brief RAII based Duktape handler. + * + * This class is implicitly convertible to duk_context for convenience. + */ +class context { +private: + std::unique_ptr<duk_context, void (*)(duk_context*)> handle_; + + context(const context&) = delete; + void operator=(const context&) = delete; + +public: + /** + * Create default context. + */ + context() noexcept; + + /** + * Default move constructor. + */ + context(context&&) noexcept = default; + + /** + * Convert the context to the native Duktape/C type. + * + * \return the duk_context + */ + operator duk_context*() noexcept; + + /** + * Convert the context to the native Duktape/C type. + * + * \return the duk_context + */ + operator duk_context*() const noexcept; + + /** + * Default move assignment operator. + * + * \return this + */ + auto operator=(context&&) noexcept -> context& = default; +}; + +// }}} + +// {{{ stack_info + +/** + * \brief Error description. + * + * This class fills the fields got in an Error object. + */ +class stack_info : public std::exception { +private: + std::string name_; + std::string message_; + std::string stack_; + std::string file_name_; + unsigned line_number_; + +public: + /** + * Construct the stack information. + * + * \param name the exception name (e.g. ReferenceError) + * \param message the error message + * \param stack the stack trace + * \param file_name the optional filename + * \param line_number the optional line number + */ + stack_info(std::string name, + std::string message, + std::string stack, + std::string file_name, + unsigned line_number = 0) noexcept; + + /** + * Get the exception name. + * + * \return the exception name (e.g. ReferenceError) + */ + auto get_name() const noexcept -> const std::string&; + + /** + * Get the error message. + * + * \return the message + */ + auto get_message() const noexcept -> const std::string&; + + /** + * Get the stack trace. + * + * \return the stack + */ + auto get_stack() const noexcept -> const std::string&; + + /** + * Get the optional file name. + * + * \return the file name + */ + auto get_file_name() const noexcept -> const std::string&; + + /** + * Get the line number. + * + * \return the line number + */ + auto get_line_number() const noexcept -> unsigned; + + /** + * Get the error message. This effectively returns message field. + * + * \return the message + */ + auto what() const noexcept -> const char* override; +}; + +// }}} + +// {{{ type_traits + +/** + * \brief Operations on different types. + * + * This class provides some functions for the given type, depending on the + * nature of the function. + * + * For example, push will call type_traits<T>::push static function + * if the type_traits is implemented for that given T type. + * + * This helps passing/getting function between Javascript and C++ code. + * + * Example: + * + * ```cpp + * push(ctx, 123); // Uses type_traits<int> + * push(ctx, true); // Uses type_traits<bool> + * ``` + * + * This class is specialized for the following types: + * + * - `bool`, + * - `duk_int_t`, + * - `duk_uint_t`, + * - `duk_double_t`, + * - `const char*`, + * - `std::string`, + * - `std::string_view`. + * + * Regarding exceptions, this class is specialized for the following types: + * + * - error, + * - std::exception, + * + * \see push + * \see get + * \see require + * \see raise + */ +template <typename T> +struct type_traits; + +// }}} + +// {{{ push + +/** + * Generic push function. + * + * This function calls type_traits<T>::push if specialized. + * + * \param ctx the Duktape context + * \param value the forwarded value + * \return 1 for convenience + */ +template <typename T> +auto push(duk_context* ctx, T&& value) -> int +{ + using Type = typename std::decay<T>::type; + + type_traits<Type>::push(ctx, std::forward<T>(value)); + + return 1; +} + +// }}} + +// {{{ get + +/** + * Generic get function. + * + * This functions calls type_traits<T>::get if specialized. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ +template <typename T> +auto get(duk_context* ctx, duk_idx_t index) +{ + using Type = typename std::decay<T>::type; + + return type_traits<Type>::get(ctx, index); +} + +// }}} + +// {{{ require + +/** + * Generic require function. + * + * This functions calls type_traits<T>::require if specialized. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ +template <typename T> +auto require(duk_context* ctx, duk_idx_t index) +{ + using Type = typename std::decay<T>::type; + + return type_traits<Type>::require(ctx, index); +} + +// }}} + +// {{{ error + +/** + * \brief Base ECMAScript error class. + * \warning Override the function create for your own exceptions + */ +class error { +private: + int type_{DUK_ERR_ERROR}; + std::string message_; + +protected: + /** + * Constructor with a type of error specified, specially designed for + * derived errors. + * + * \param type of error (e.g. DUK_ERR_ERROR) + * \param message the message + */ + error(int type, std::string message) noexcept; + +public: + /** + * Constructor with a message. + * + * \param message the message + */ + error(std::string message) noexcept; + + /** + * Virtual destructor defaulted. + */ + ~error() = default; + + /** + * Create the exception on the stack. + * + * \note the default implementation search for the global variables + * \param ctx the context + */ + void create(duk_context* ctx) const; +}; + +// }}} + +// {{{ eval_error + +/** + * \brief Error in eval() function. + */ +class eval_error : public error { +public: + /** + * Construct an EvalError. + * + * \param message the message + */ + eval_error(std::string message) noexcept; +}; + +// }}} + +// {{{ range_error + +/** + * \brief Value is out of range. + */ +class range_error : public error { +public: + /** + * Construct an RangeError. + * + * \param message the message + */ + range_error(std::string message) noexcept; +}; + +// }}} + +// {{{ reference_error + +/** + * \brief Trying to use a variable that does not exist. + */ +class reference_error : public error { +public: + /** + * Construct an ReferenceError. + * + * \param message the message + */ + reference_error(std::string message) noexcept; +}; + +// }}} + +// {{{ syntax_error + +/** + * \brief Syntax error in the script. + */ +class syntax_error : public error { +public: + /** + * Construct an SyntaxError. + * + * \param message the message + */ + syntax_error(std::string message) noexcept; +}; + +// }}} + +// {{{ type_error + +/** + * \brief Invalid type given. + */ +class type_error : public error { +public: + /** + * Construct an TypeError. + * + * \param message the message + */ + type_error(std::string message) noexcept; +}; + +// }}} + +// {{{ uri_error + +/** + * \brief URI manipulation failure. + */ +class uri_error : public error { +public: + /** + * Construct an URIError. + * + * \param message the message + */ + uri_error(std::string message) noexcept; +}; + +// }}} + +// {{{ raise + +/** + * Create an exception into the stack and throws it. + * + * This function needs the following requirements in type_traits + * + * ```cpp + * static void raise(duk_context*, Error); + * ``` + * + * Error can be any kind of value, it is forwarded. + * + * \param ctx the Duktape context + * \param error the error object + */ +template <typename Error> +void raise(duk_context* ctx, Error&& error) +{ + using type = std::decay_t<Error>; + + type_traits<type>::raise(ctx, std::forward<Error>(error)); +} + +// }}} + +// {{{ get_stack + +/** + * Get the error object when a JavaScript error has been thrown (e.g. eval + * failure). + * + * \param ctx the context + * \param index the index + * \param pop if true, also remove the exception from the stack + * \return the information + */ +auto get_stack(duk_context* ctx, int index, bool pop = true) -> stack_info; + +// }}} + +// {{{ type_traits<std::exception> + +/** + * \brief Specialization for std::exception. + */ +template <> +struct type_traits<std::exception> { + /** + * Raise a Error object. + * + * \param ctx the Duktape context + * \param ex the exception + */ + static void raise(duk_context* ctx, const std::exception& ex); +}; + +// }}} + +// {{{ type_traits<error> + +/** + * \brief Specialization for error. + */ +template <> +struct type_traits<error> { + /** + * Raise a error. + * + * \param ctx the Duktape context + * \param ex the exception + */ + static void raise(duk_context* ctx, const error& ex); +}; + +// }}} + +// {{{ type_traits<bool> + +/** + * \brief Specialization for bool. + */ +template <> +struct type_traits<bool> { + /** + * Push a boolean. + * + * Uses duk_push_boolean + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, bool value); + + /** + * Get a boolean. + * + * Uses duk_get_boolean. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> bool; + + /** + * Require a boolean. + * + * Uses duk_require_boolean. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> bool; +}; + +// }}} + +// {{{ type_traits<duk_double_t> + +/** + * \brief Specialization for duk_double_t. + */ +template <> +struct type_traits<duk_double_t> { + /** + * Push a double. + * + * Uses duk_push_number + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, duk_double_t value); + + /** + * Get a double. + * + * Uses duk_get_number. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> duk_double_t; + + /** + * Require a double. + * + * Uses duk_require_double. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> duk_double_t; +}; + +// }}} + +// {{{ type_traits<duk_int_t> + +/** + * \brief Specialization for duk_int_t. + */ +template <> +struct type_traits<duk_int_t> { + /** + * Push an int. + * + * Uses duk_push_int + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, duk_int_t value); + + /** + * Get an int. + * + * Uses duk_get_number. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> duk_int_t; + + /** + * Require an int. + * + * Uses duk_require_int. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> duk_int_t; +}; + +// }}} + +// {{{ type_traits<duk_uint_t> + +/** + * \brief Specialization for duk_uint_t. + */ +template <> +struct type_traits<duk_uint_t> { + /** + * Push an unsigned int. + * + * Uses duk_push_uint + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, duk_uint_t value); + + /** + * Get an unsigned int. + * + * Uses duk_get_uint. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> duk_uint_t; + + /** + * Require an unsigned int. + * + * Uses duk_require_uint. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> duk_uint_t; +}; + +// }}} + +// {{{ type_traits<const char*> + +/** + * \brief Specialization for C strings. + */ +template <> +struct type_traits<const char*> { + /** + * Push a C string. + * + * Uses duk_push_string + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, const char* value); + + /** + * Get a C string. + * + * Uses duk_get_string. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> const char*; + + /** + * Require a C string. + * + * Uses duk_require_string. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> const char*; +}; + +// }}} + +// {{{ type_traits<std::string> + +/** + * \brief Specialization for C++ std::strings. + */ +template <> +struct type_traits<std::string> { + /** + * Push a C++ std::string. + * + * Uses duk_push_lstring + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, const std::string& value); + + /** + * Get a C++ std::string. + * + * Uses duk_get_lstring. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> std::string; + + /** + * Require a C++ std::string. + * + * Uses duk_require_lstring. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> std::string; +}; + +// }}} + +// {{{ type_traits<std::string_view> + +/** + * \brief Specialization for C++ std::string_views. + */ +template <> +struct type_traits<std::string_view> : public std::true_type { + /** + * Push a C++ std::string_view. + * + * Uses duk_push_lstring + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, std::string_view value); + + /** + * Get a C++ std::string_view. + * + * Uses duk_get_lstring. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto get(duk_context* ctx, duk_idx_t index) -> std::string_view; + + /** + * Require a C++ std::string_view. + * + * Uses duk_require_lstring. + * + * \param ctx the Duktape context + * \param index the value index + * \return the converted value + */ + static auto require(duk_context* ctx, duk_idx_t index) -> std::string_view; +}; + +// }}} + +// {{{ type_traits<std::vector<T>> + +/** + * \brief Specialization for std::vector. + */ +template <typename T> +struct type_traits<std::vector<T>> { + /** + * Push a vector. + * + * Uses duk_push_lstring + * + * \param ctx the Duktape context + * \param value the value + */ + static void push(duk_context* ctx, const std::vector<T>& values) + { + using size_type = typename std::vector<T>::size_type; + + duk_push_array(ctx); + + for (size_type i = 0; i < values.size(); ++i) { + type_traits<T>::push(ctx, values[i]); + duk_put_prop_index(ctx, -2, i); + } + } + + static auto get(duk_context* ctx, duk_idx_t index) -> std::vector<T> + { + using size_type = typename std::vector<T>::size_type; + + std::vector<T> result; + size_type length = duk_get_length(ctx, index); + + for (size_type i = 0; i < length; ++i) { + duk_get_prop_index(ctx, index, i); + result.push_back(type_traits<T>::get(ctx, -1)); + duk_pop(ctx); + } + + return result; + } +}; + +// }}} + +} // !irccd::js::duk + +#endif // !DUKTAPE_HPP
--- a/libirccd-js/irccd/js/duktape.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1147 +0,0 @@ -/* - * duktape.hpp -- miscellaneous Duktape extras - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_DUKTAPE_HPP -#define IRCCD_JS_DUKTAPE_HPP - -/** - * \file duktape.hpp - * \brief Miscellaneous Duktape extras - * \author David Demelier <markand@malikania.fr> - * \version 0.2.0 - */ - -#include <cassert> -#include <cstdio> -#include <initializer_list> -#include <iterator> -#include <memory> -#include <string> -#include <string_view> -#include <type_traits> -#include <utility> - -#include "duktape.h" - -namespace irccd { - -/** - * \brief Stack sanity checker. - * - * Instanciate this class where you need to manipulate the Duktape stack outside - * a Duktape/C function, its destructor will examinate if the stack size matches - * the user expected size. - * - * When compiled with NDEBUG, this class does nothing. - * - * To use it, just declare an lvalue at the beginning of your function. - */ -class dukx_stack_assert { -#if !defined(NDEBUG) -private: - duk_context* context_; - unsigned expected_; - int at_start_; -#endif - -public: - /** - * Create the stack checker. - * - * No-op if NDEBUG is set. - * - * \param ctx the context - * \param expected the size expected relative to the already existing values - */ - inline dukx_stack_assert(duk_context* ctx, unsigned expected = 0) noexcept -#if !defined(NDEBUG) - : context_(ctx) - , expected_(expected) - , at_start_(duk_get_top(ctx)) -#endif - { -#if defined(NDEBUG) - (void)ctx; - (void)expected; -#endif - } - - /** - * Verify the expected size. - * - * No-op if NDEBUG is set. - */ - inline ~dukx_stack_assert() noexcept - { -#if !defined(NDEBUG) - auto result = duk_get_top(context_) - at_start_; - - if (result != static_cast<int>(expected_)) { - std::fprintf(stderr, "Corrupt stack detection in dukx_stack_assert:\n"); - std::fprintf(stderr, " Size at start: %d\n", at_start_); - std::fprintf(stderr, " Size at end: %d\n", duk_get_top(context_)); - std::fprintf(stderr, " Expected (user): %u\n", expected_); - std::fprintf(stderr, " Expected (adjusted): %u\n", expected_ + at_start_); - std::fprintf(stderr, " Difference count: %+d\n", result - expected_); - std::abort(); - } -#endif - } -}; - -/** - * \brief RAII based Duktape handler. - * - * This class is implicitly convertible to duk_context for convenience. - */ -class dukx_context { -private: - std::unique_ptr<duk_context, void (*)(duk_context*)> handle_; - - dukx_context(const dukx_context&) = delete; - dukx_context &operator=(const dukx_context&) = delete; - -public: - /** - * Create default context. - */ - inline dukx_context() noexcept - : handle_(duk_create_heap_default(), duk_destroy_heap) - { - } - - /** - * Default move constructor. - */ - dukx_context(dukx_context&&) noexcept = default; - - /** - * Convert the context to the native Duktape/C type. - * - * \return the duk_context - */ - inline operator duk_context*() noexcept - { - return handle_.get(); - } - - /** - * Convert the context to the native Duktape/C type. - * - * \return the duk_context - */ - inline operator duk_context*() const noexcept - { - return handle_.get(); - } - - /** - * Default move assignment operator. - * - * \return this - */ - dukx_context& operator=(dukx_context&&) noexcept = delete; -}; - -/** - * \brief Error description. - * - * This class fills the fields got in an Error object. - */ -class dukx_stack_info : public std::exception { -private: - std::string name_; - std::string message_; - std::string stack_; - std::string file_name_; - int line_number_; - -public: - /** - * Construct the stack information. - * - * \param name the exception name (e.g. ReferenceError) - * \param message the error message - * \param stack the stack trace - * \param file_name the optional filename - * \param line_number the optional line number - */ - inline dukx_stack_info(std::string name, - std::string message, - std::string stack, - std::string file_name, - int line_number = 0) noexcept - : name_(std::move(name)) - , message_(std::move(message)) - , stack_(std::move(stack)) - , file_name_(std::move(file_name)) - , line_number_(line_number) - { - } - - /** - * Get the exception name. - * - * \return the exception name (e.g. ReferenceError) - */ - inline const std::string& name() const noexcept - { - return name_; - } - - /** - * Get the error message. - * - * \return the message - */ - inline const std::string& message() const noexcept - { - return message_; - } - - /** - * Get the stack trace. - * - * \return the stack - */ - inline const std::string& stack() const noexcept - { - return stack_; - } - - /** - * Get the optional file name. - * - * \return the file name - */ - inline const std::string& file_name() const noexcept - { - return file_name_; - } - - /** - * Get the line number. - * - * \return the line number - */ - inline int line_number() const noexcept - { - return line_number_; - } - - /** - * Get the error message. This effectively returns message field. - * - * \return the message - */ - const char* what() const noexcept override - { - return message_.c_str(); - } -}; - -/** - * \brief Base ECMAScript error class. - * \warning Override the function create for your own exceptions - */ -class dukx_error { -private: - int type_{DUK_ERR_ERROR}; - std::string message_; - -protected: - /** - * Constructor with a type of error specified, specially designed for - * derived errors. - * - * \param type of error (e.g. DUK_ERR_ERROR) - * \param message the message - */ - inline dukx_error(int type, std::string message) noexcept - : type_(type) - , message_(std::move(message)) - { - } - -public: - /** - * Constructor with a message. - * - * \param message the message - */ - inline dukx_error(std::string message) noexcept - : message_(std::move(message)) - { - } - - /** - * Virtual destructor defaulted. - */ - virtual ~dukx_error() = default; - - /** - * Get internal Duktape error (e.g. DUK_ERR_TYPE_ERROR) - * - * \return the type - */ - inline int get_type() const noexcept - { - return type_; - } - - /** - * Get the error message. - * - * \return the message - */ - inline const std::string& get_message() const noexcept - { - return message_; - } -}; - -/** - * \brief Error in eval() function. - */ -class dukx_eval_error : public dukx_error { -public: - /** - * Construct an EvalError. - * - * \param message the message - */ - inline dukx_eval_error(std::string message) noexcept - : dukx_error(DUK_ERR_EVAL_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Value is out of range. - */ -class dukx_range_error : public dukx_error { -public: - /** - * Construct an RangeError. - * - * \param message the message - */ - inline dukx_range_error(std::string message) noexcept - : dukx_error(DUK_ERR_RANGE_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Trying to use a variable that does not exist. - */ -class dukx_reference_error : public dukx_error { -public: - /** - * Construct an ReferenceError. - * - * \param message the message - */ - inline dukx_reference_error(std::string message) noexcept - : dukx_error(DUK_ERR_REFERENCE_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Syntax error in the script. - */ -class dukx_syntax_error : public dukx_error { -public: - /** - * Construct an SyntaxError. - * - * \param message the message - */ - inline dukx_syntax_error(std::string message) noexcept - : dukx_error(DUK_ERR_SYNTAX_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Invalid type given. - */ -class dukx_type_error : public dukx_error { -public: - /** - * Construct an TypeError. - * - * \param message the message - */ - inline dukx_type_error(std::string message) noexcept - : dukx_error(DUK_ERR_TYPE_ERROR, std::move(message)) - { - } -}; - -/** - * \brief URI manipulation failure. - */ -class dukx_uri_error : public dukx_error { -public: - /** - * Construct an URIError. - * - * \param message the message - */ - inline dukx_uri_error(std::string message) noexcept - : dukx_error(DUK_ERR_URI_ERROR, std::move(message)) - { - } -}; - -/** - * \brief Operations on different types. - * - * This class provides some functions for the given type, depending on the - * nature of the function. - * - * For example, dukx_push will call dukx_type_traits<T>::push static function - * if the dukx_type_traits is implemented for that given T type. - * - * This helps passing/getting function between Javascript and C++ code. - * - * Example: - * - * ```cpp - * dukx_push(ctx, 123); // Uses dukx_type_traits<int> - * dukx_push(ctx, true); // Uses dukx_type_traits<bool> - * ``` - * - * This class is specialized for the following types: - * - * - `bool`, - * - `duk_int_t`, - * - `duk_uint_t`, - * - `duk_double_t`, - * - `const char*`, - * - `std::string` - * - * It is also specialized for all exceptions types: - * - * - `dukx_error`, - * - `dukx_eval_error`, - * - `dukx_range_error`, - * - `dukx_reference_error`, - * - `dukx_syntax_error`, - * - `dukx_type_error`, - * - `dukx_uri_error`. - * - * And more general std::exception: - * - * - `std::exception`. - */ -template <typename T> -class dukx_type_traits : public std::false_type { -}; - -/** - * \brief Specialization for bool. - */ -template <> -class dukx_type_traits<bool> : public std::true_type { -public: - /** - * Push a boolean. - * - * Uses duk_push_boolean - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, bool value) - { - duk_push_boolean(ctx, value); - } - - /** - * Get a boolean. - * - * Uses duk_get_boolean. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static bool get(duk_context* ctx, duk_idx_t index) - { - return duk_get_boolean(ctx, index); - } - - /** - * Require a boolean. - * - * Uses duk_require_boolean. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static bool require(duk_context* ctx, duk_idx_t index) - { - return duk_require_boolean(ctx, index); - } -}; - -/** - * \brief Specialization for duk_double_t. - */ -template <> -class dukx_type_traits<duk_double_t> : public std::true_type { -public: - /** - * Push a double. - * - * Uses duk_push_number - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, duk_double_t value) - { - duk_push_number(ctx, value); - } - - /** - * Get a double. - * - * Uses duk_get_number. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static duk_double_t get(duk_context* ctx, duk_idx_t index) - { - return duk_get_number(ctx, index); - } - - /** - * Require a double. - * - * Uses duk_require_double. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static duk_double_t require(duk_context* ctx, duk_idx_t index) - { - return duk_require_number(ctx, index); - } -}; - -/** - * \brief Specialization for duk_int_t. - */ -template <> -class dukx_type_traits<duk_int_t> : public std::true_type { -public: - /** - * Push an int. - * - * Uses duk_push_int - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, duk_int_t value) - { - duk_push_int(ctx, value); - } - - /** - * Get an int. - * - * Uses duk_get_number. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static duk_int_t get(duk_context* ctx, duk_idx_t index) - { - return duk_get_int(ctx, index); - } - - /** - * Require an int. - * - * Uses duk_require_int. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static duk_int_t require(duk_context* ctx, duk_idx_t index) - { - return duk_require_int(ctx, index); - } -}; - -/** - * \brief Specialization for duk_uint_t. - */ -template <> -class dukx_type_traits<duk_uint_t> : public std::true_type { -public: - /** - * Push an unsigned int. - * - * Uses duk_push_uint - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, duk_uint_t value) - { - duk_push_uint(ctx, value); - } - - /** - * Get an unsigned int. - * - * Uses duk_get_uint. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static duk_uint_t get(duk_context* ctx, duk_idx_t index) - { - return duk_get_uint(ctx, index); - } - - /** - * Require an unsigned int. - * - * Uses duk_require_uint. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static duk_uint_t require(duk_context* ctx, duk_idx_t index) - { - return duk_require_uint(ctx, index); - } -}; - -/** - * \brief Specialization for C strings. - */ -template <> -class dukx_type_traits<const char*> : public std::true_type { -public: - /** - * Push a C string. - * - * Uses duk_push_string - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, const char* value) - { - duk_push_string(ctx, value); - } - - /** - * Get a C string. - * - * Uses duk_get_string. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static const char* get(duk_context* ctx, duk_idx_t index) - { - return duk_get_string(ctx, index); - } - - /** - * Require a C string. - * - * Uses duk_require_string. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static const char* require(duk_context* ctx, duk_idx_t index) - { - return duk_require_string(ctx, index); - } -}; - -/** - * \brief Specialization for C++ std::strings. - */ -template <> -class dukx_type_traits<std::string> : public std::true_type { -public: - /** - * Push a C++ std::string. - * - * Uses duk_push_lstring - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, const std::string& value) - { - duk_push_lstring(ctx, value.data(), value.size()); - } - - /** - * Get a C++ std::string. - * - * Uses duk_get_lstring. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static std::string get(duk_context* ctx, duk_idx_t index) - { - duk_size_t length; - const char* str = duk_get_lstring(ctx, index, &length); - - return {str, length}; - } - - /** - * Require a C++ std::string. - * - * Uses duk_require_lstring. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static std::string require(duk_context* ctx, duk_idx_t index) - { - duk_size_t length; - const char* str = duk_require_lstring(ctx, index, &length); - - return {str, length}; - } -}; - -/** - * \brief Specialization for C++ std::string_view. - */ -template <> -class dukx_type_traits<std::string_view> : public std::true_type { -public: - /** - * Push a C++ std::string_view. - * - * Uses duk_push_lstring - * - * \param ctx the Duktape context - * \param value the value - */ - static void push(duk_context* ctx, std::string_view value) - { - duk_push_lstring(ctx, value.data(), value.size()); - } - - /** - * Get a C++ std::string_view. - * - * Uses duk_get_lstring. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static std::string_view get(duk_context* ctx, duk_idx_t index) - { - duk_size_t length; - const char* str = duk_get_lstring(ctx, index, &length); - - return {str, length}; - } - - /** - * Require a C++ std::string_view. - * - * Uses duk_require_lstring. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ - static std::string_view require(duk_context* ctx, duk_idx_t index) - { - duk_size_t length; - const char* str = duk_require_lstring(ctx, index, &length); - - return {str, length}; - } -}; - -/** - * \brief Specialization for dukx_error. - */ -template <> -class dukx_type_traits<dukx_error> : public std::true_type { -public: - /** - * Create the exception on the stack. - * - * \param ctx the context - * \param ex the error - */ - static void raise(duk_context* ctx, const dukx_error& ex) - { - duk_error(ctx, ex.get_type(), "%s", ex.get_message().c_str()); - } -}; - -/** - * \brief Specialization for dukx_eval_error. - */ -template <> -class dukx_type_traits<dukx_eval_error> : public dukx_type_traits<dukx_error> { -}; - -/** - * \brief Specialization for dukx_range_error. - */ -template <> -class dukx_type_traits<dukx_range_error> : public dukx_type_traits<dukx_error> { -}; - -/** - * \brief Specialization for dukx_reference_error. - */ -template <> -class dukx_type_traits<dukx_reference_error> : public dukx_type_traits<dukx_error> { -}; - -/** - * \brief Specialization for dukx_syntax_error. - */ -template <> -class dukx_type_traits<dukx_syntax_error> : public dukx_type_traits<dukx_error> { -}; - -/** - * \brief Specialization for dukx_type_error. - */ -template <> -class dukx_type_traits<dukx_type_error> : public dukx_type_traits<dukx_error> { -}; - -/** - * \brief Specialization for dukx_uri_error. - */ -template <> -class dukx_type_traits<dukx_uri_error> : public dukx_type_traits<dukx_error> { -}; - -/** - * \brief Specialization for std::exception. - */ -template <> -class dukx_type_traits<std::exception> : public std::true_type { -public: - /** - * Raise std::exception as general DUK_ERR_ERROR. - * - * \param ctx the context - * \param ex the exception - */ - static void raise(duk_context* ctx, const std::exception& ex) { - duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what()); - } -}; - -/** - * \brief Partial specialization for collections. - * - * Derive from this class to implement type traits for collections. - * - * \see duktape_vector.hpp - */ -template <typename Container> -class dukx_array_type_traits : public std::true_type { -public: - /** - * Push a Javascript array by copying values. - * - * Uses dukx_push for each T values in the collection. - * - * \param ctx the Duktape context - * \param c the container - */ - static void push(duk_context* ctx, const Container& c) - { - using Type = typename Container::value_type; - - duk_push_array(ctx); - - int i = 0; - - for (auto v : c) { - dukx_type_traits<Type>::push(ctx, v); - duk_put_prop_index(ctx, -2, i++); - } - } - - /** - * Get an array from the Javascript array. - * - * \param ctx the Duktape context - * \param index the value index - * \return the container - */ - static Container get(duk_context* ctx, duk_idx_t index) - { - using Type = typename Container::value_type; - using Size = typename Container::size_type; - - Container result; - Size length = duk_get_length(ctx, index); - - for (Size i = 0; i < length; ++i) { - duk_get_prop_index(ctx, index, i); - result.push_back(dukx_type_traits<Type>::get(ctx, -1)); - duk_pop(ctx); - } - - return result; - } - - /** - * Require an array from the Javascript array. - * - * \param ctx the Duktape context - * \param index the value index - * \return the container - */ - static Container require(duk_context* ctx, duk_idx_t index) - { - duk_check_type(ctx, index, DUK_TYPE_OBJECT); - - return get(ctx, index); - } -}; - -/** - * \brief Partial specialization for maps. - * - * Derive from this class to implement type traits for maps. - * - * \see duktape_vector.hpp - */ -template <typename Container> -class dukx_object_type_traits : public std::true_type { -public: - /** - * Push a container by copying values. - * - * Uses dukx_push for each key/value pair in the container. - * - * \param ctx the Duktape context - * \param c the container - */ - static void push(duk_context* ctx, const Container& c) - { - using Type = typename Container::mapped_type; - - duk_push_object(ctx); - - for (const auto& pair : c) { - dukx_type_traits<std::string>::push(ctx, pair.first); - dukx_type_traits<Type>::push(ctx, pair.second); - duk_put_prop(ctx, -3); - } - } - - /** - * Get a object from the Javascript array. - * - * \param ctx the Duktape context - * \param index the value index - * \return the container - */ - static Container get(duk_context* ctx, duk_idx_t index) - { - using Type = typename Container::mapped_type; - - Container result; - - duk_enum(ctx, index, 0); - - while (duk_next(ctx, -1, true)) { - result.emplace( - dukx_type_traits<std::string>::get(ctx, -2), - dukx_type_traits<Type>::get(ctx, -1) - ); - duk_pop_n(ctx, 2); - } - - duk_pop(ctx); - - return result; - } - - /** - * Require a object from the Javascript array. - * - * \param ctx the Duktape context - * \param index the value index - * \return the container - */ - static Container require(duk_context* ctx, duk_idx_t index) - { - duk_check_type(ctx, index, DUK_TYPE_OBJECT); - - return get(ctx, index); - } -}; - -/** - * Generic push function. - * - * This function calls dukx_type_traits<T>::push if specialized. - * - * \param ctx the Duktape context - * \param value the forwarded value - * \return 1 for convenience - */ -template <typename T> -int dukx_push(duk_context* ctx, T&& value) -{ - using Type = typename std::decay<T>::type; - - static_assert(dukx_type_traits<Type>::value, "type T not supported"); - - dukx_type_traits<Type>::push(ctx, std::forward<T>(value)); - - return 1; -} - -/** - * Generic get function. - * - * This functions calls dukx_type_traits<T>::get if specialized. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ -template <typename T> -T dukx_get(duk_context* ctx, duk_idx_t index) -{ - using Type = typename std::decay<T>::type; - - static_assert(dukx_type_traits<Type>::value, "type T not supported"); - - return dukx_type_traits<Type>::get(ctx, index); -} - -/** - * Generic require function. - * - * This functions calls dukx_type_traits<T>::require if specialized. - * - * \param ctx the Duktape context - * \param index the value index - * \return the converted value - */ -template <typename T> -T dukx_require(duk_context* ctx, duk_idx_t index) -{ - using Type = typename std::decay<T>::type; - - static_assert(dukx_type_traits<Type>::value, "type T not supported"); - - return dukx_type_traits<Type>::require(ctx, index); -} - -/** - * Create an exception into the stack and throws it. - * - * The dukx_type_traits<Error> must have the following function: - * - * ``` - * static void raise(const Error&); - * ``` - * - * \param ctx the Duktape context - * \param error the error object - */ -template <typename Error> -void dukx_throw(duk_context* ctx, const Error& error) -{ - static_assert(dukx_type_traits<Error>::value, "type T not supported"); - - dukx_type_traits<Error>::raise(ctx, error); -} - -/** - * Get the error object when a JavaScript error has been thrown (e.g. eval - * failure). - * - * \param ctx the context - * \param index the index - * \param pop if true, also remove the exception from the stack - * \return the information - */ -inline dukx_stack_info dukx_stack(duk_context* ctx, int index, bool pop = true) -{ - index = duk_normalize_index(ctx, index); - - duk_get_prop_string(ctx, index, "name"); - auto name = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "message"); - auto message = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "fileName"); - auto file_name = duk_to_string(ctx, -1); - duk_get_prop_string(ctx, index, "lineNumber"); - auto line_number = duk_to_int(ctx, -1); - duk_get_prop_string(ctx, index, "stack"); - auto stack = duk_to_string(ctx, -1); - duk_pop_n(ctx, 5); - - if (pop) - duk_remove(ctx, index); - - return { - std::move(name), - std::move(message), - std::move(stack), - std::move(file_name), - line_number - }; -} - -} // !irccd - -#endif // !IRCCD_JS_DUKTAPE_HPP
--- a/libirccd-js/irccd/js/duktape_vector.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * duktape_vector.hpp -- miscellaneous Duktape extras (std::vector support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_DUKTAPE_VECTOR_HPP -#define IRCCD_JS_DUKTAPE_VECTOR_HPP - -/** - * \file duktape_vector.hpp - * \brief Miscellaneous Duktape extras (std::vector support). - */ - -#include <vector> - -#include "duktape.hpp" - -namespace irccd { - -/** - * \brief Specialization for std::vector<T>. - */ -template <typename T> -class dukx_type_traits<std::vector<T>> : public dukx_array_type_traits<std::vector<T>> { -}; - -} // !irccd - -#endif // !IRCCD_JS_DUKTAPE_VECTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/elapsed_timer_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,180 @@ +/* + * elapsed_timer_js_api.cpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/timer/timer.hpp> + +#include "elapsed_timer_js_api.hpp" +#include "js_plugin.hpp" + +namespace irccd::js { + +namespace { + +const std::string_view signature("\xff""\xff""Irccd.ElapsedTimer"); + +// {{{ self + +auto self(duk_context* ctx) -> boost::timer::cpu_timer* +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + const auto ptr = static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object"); + + return ptr; +} + +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.pause + +/* + * Method: ElapsedTimer.prototype.pause + * ------------------------------------------------------------------ + * + * Pause the timer, without resetting the current elapsed time stored. + */ +auto ElapsedTimer_prototype_pause(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->stop(); + + return 0; +} + +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.restart + +/* + * Method: Irccd.ElapsedTimer.prototype.restart + * ------------------------------------------------------------------ + * + * Restart the timer without resetting the current elapsed time. + */ +auto ElapsedTimer_prototype_restart(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->resume(); + + return 0; +} + +// }}} + +// {{{ Irccd.ElapsedTimer.prototype.elapsed + +/* + * Method: ElapsedTimer.prototype.elapsed + * ------------------------------------------------------------------ + * + * Get the number of elapsed milliseconds. + * + * Returns: + * The time elapsed. + */ +auto ElapsedTimer_prototype_elapsed(duk_context* ctx) -> duk_ret_t +{ + duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL); + + return 1; +} + +// }}} + +// {{{ Irccd.ElapsedTimer [constructor] + +/* + * Function: Irccd.ElapsedTimer [constructor] + * ------------------------------------------------------------------ + * + * Construct a new ElapsedTimer object. + */ +auto ElapsedTimer_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_pointer(ctx, new boost::timer::cpu_timer); + duk_put_prop_string(ctx, -2, signature.data()); + duk_pop(ctx); + + return 0; +} + +// }}} + +// {{{ Irccd.ElapsedTimer [destructor] + +/* + * Function: Irccd.ElapsedTimer [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +auto ElapsedTimer_destructor(duk_context* ctx) -> duk_ret_t +{ + duk_get_prop_string(ctx, 0, signature.data()); + delete static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, signature.data()); + + return 0; +} + +// }}} + +// {{{ definitions + +const duk_function_list_entry methods[] = { + { "elapsed", ElapsedTimer_prototype_elapsed, 0 }, + { "pause", ElapsedTimer_prototype_pause, 0 }, + { "restart", ElapsedTimer_prototype_restart, 0 }, + { nullptr, nullptr, 0 } +}; + +// }}} + +} // !namespace + +// {{{ elapsed_timer_js_api + +auto elapsed_timer_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.ElapsedTimer"; +} + +void elapsed_timer_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), ElapsedTimer_constructor, 0); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_push_c_function(plugin->get_context(), ElapsedTimer_destructor, 1); + duk_set_finalizer(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "ElapsedTimer"); + duk_pop(plugin->get_context()); +} + +// }}} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/elapsed_timer_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * elapsed_timer_js_api.hpp -- Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_ELAPSED_TIMER_JS_API_HPP +#define IRCCD_JS_ELAPSED_TIMER_JS_API_HPP + +/** + * \file elapsed_timer_js_api.hpp + * \brief Irccd.ElapsedTimer Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.ElapsedTimer Javascript API. + * \ingroup Javascript js_api + */ +class elapsed_timer_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_ELAPSED_TIMER_JS_API_HPP
--- a/libirccd-js/irccd/js/elapsed_timer_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * elapsed_timer_jsapi.cpp -- Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/timer/timer.hpp> - -#include "elapsed_timer_jsapi.hpp" -#include "js_plugin.hpp" - -namespace irccd { - -namespace { - -const char* signature("\xff""\xff""irccd-elapsed-timer-ptr"); - -// {{{ self - -boost::timer::cpu_timer* self(duk_context* ctx) -{ - dukx_stack_assert sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature); - const auto ptr = static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object"); - - return ptr; -} - -// }}} - -// {{{ Irccd.ElapsedTimer.prototype.pause - -/* - * Method: ElapsedTimer.prototype.pause - * ------------------------------------------------------------------ - * - * Pause the timer, without resetting the current elapsed time stored. - */ -duk_ret_t ElapsedTimer_prototype_pause(duk_context* ctx) -{ - self(ctx)->stop(); - - return 0; -} - -// }}} - -// {{{ Irccd.ElapsedTimer.prototype.restart - -/* - * Method: Irccd.ElapsedTimer.prototype.restart - * ------------------------------------------------------------------ - * - * Restart the timer without resetting the current elapsed time. - */ -duk_ret_t ElapsedTimer_prototype_restart(duk_context* ctx) -{ - self(ctx)->resume(); - - return 0; -} - -// }}} - -// {{{ Irccd.ElapsedTimer.prototype.elapsed - -/* - * Method: ElapsedTimer.prototype.elapsed - * ------------------------------------------------------------------ - * - * Get the number of elapsed milliseconds. - * - * Returns: - * The time elapsed. - */ -duk_ret_t ElapsedTimer_prototype_elapsed(duk_context* ctx) -{ - duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL); - - return 1; -} - -// }}} - -// {{{ Irccd.ElapsedTimer [constructor] - -/* - * Function: Irccd.ElapsedTimer [constructor] - * ------------------------------------------------------------------ - * - * Construct a new ElapsedTimer object. - */ -duk_ret_t ElapsedTimer_constructor(duk_context* ctx) -{ - duk_push_this(ctx); - duk_push_pointer(ctx, new boost::timer::cpu_timer); - duk_put_prop_string(ctx, -2, signature); - duk_pop(ctx); - - return 0; -} - -// }}} - -// {{{ Irccd.ElapsedTimer [destructor] - -/* - * Function: Irccd.ElapsedTimer [destructor] - * ------------------------------------------------------------------ - * - * Delete the property. - */ -duk_ret_t ElapsedTimer_destructor(duk_context* ctx) -{ - duk_get_prop_string(ctx, 0, signature); - delete static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_del_prop_string(ctx, 0, signature); - - return 0; -} - -// }}} - -const duk_function_list_entry methods[] = { - { "elapsed", ElapsedTimer_prototype_elapsed, 0 }, - { "pause", ElapsedTimer_prototype_pause, 0 }, - { "restart", ElapsedTimer_prototype_restart, 0 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string elapsed_timer_jsapi::get_name() const -{ - return "Irccd.ElapsedTimer"; -} - -void elapsed_timer_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), ElapsedTimer_constructor, 0); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_push_c_function(plugin->get_context(), ElapsedTimer_destructor, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "ElapsedTimer"); - duk_pop(plugin->get_context()); -} - -} // !irccd
--- a/libirccd-js/irccd/js/elapsed_timer_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * elapsed_timer_jsapi.hpp -- Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_ELAPSED_TIMER_JSAPI_HPP -#define IRCCD_JS_ELAPSED_TIMER_JSAPI_HPP - -/** - * \file elapsed_timer_jsapi.hpp - * \brief Irccd.ElapsedTimer Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd.ElapsedTimer Javascript API. - * \ingroup Javascript jsapi - */ -class elapsed_timer_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_ELAPSED_TIMER_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/file_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,805 @@ +/* + * file_js_api.cpp -- Irccd.File API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <algorithm> +#include <array> +#include <cassert> +#include <iterator> +#include <vector> + +#include <boost/filesystem.hpp> + +#include <irccd/fs_util.hpp> + +#include "file_js_api.hpp" +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" + +namespace irccd::js { + +namespace { + +const std::string_view signature("\xff""\xff""Irccd.File"); +const std::string_view prototype("\xff""\xff""Irccd.File.prototype"); + +// {{{ clear_crlf + +auto clear_crlf(std::string input) noexcept -> std::string +{ + if (input.length() > 0 && input.back() == '\r') + input.pop_back(); + + return input; +} + +// }}} + +// {{{ from_errno + +auto from_errno() noexcept -> std::system_error +{ + return std::system_error(make_error_code(static_cast<std::errc>(errno))); +} + +// }}} + +// {{{ self + +auto self(duk_context* ctx) -> std::shared_ptr<file> +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + auto ptr = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + return *ptr; +} + +// }}} + +// {{{ wrap + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const boost::system::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.File.prototype.basename + +/* + * Method: Irccd.File.prototype.basename() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.basename(path)` but with the path from the file. + * + * Returns: + * The base name. + */ +auto File_prototype_basename(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::base_name(self(ctx)->get_path())); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.close + +/* + * Method: Irccd.File.prototype.close() + * -------------------------------------------------------- + * + * Force close of the file, automatically called when object is collected. + */ +auto File_prototype_close(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + self(ctx)->close(); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.dirname + +/* + * Method: Irccd.File.prototype.dirname() + * -------------------------------------------------------- + * + * Synonym of `Irccd.File.dirname(path)` but with the path from the file. + * + * Returns: + * The directory name. + */ +auto File_prototype_dirname(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::dir_name(self(ctx)->get_path())); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.lines + +/* + * Method: Irccd.File.prototype.lines() + * -------------------------------------------------------- + * + * Read all lines and return an array. + * + * Returns: + * An array with all lines. + * Throws + * - Any exception on error. + */ +auto File_prototype_lines(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + duk_push_array(ctx); + + std::FILE* fp = self(ctx)->get_handle(); + std::string buffer; + std::array<char, 128> data; + std::int32_t i = 0; + + while (std::fgets(&data[0], data.size(), fp) != nullptr) { + buffer += data.data(); + + const auto pos = buffer.find('\n'); + + if (pos != std::string::npos) { + duk::push(ctx, clear_crlf(buffer.substr(0, pos))); + duk_put_prop_index(ctx, -2, i++); + + buffer.erase(0, pos + 1); + } + } + + // Maybe an error in the stream. + if (std::ferror(fp)) + throw from_errno(); + + // Missing '\n' in end of file. + if (!buffer.empty()) { + duk::push(ctx, clear_crlf(buffer)); + duk_put_prop_index(ctx, -2, i++); + } + + return 1; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.read + +/* + * Method: Irccd.File.prototype.read(amount) + * -------------------------------------------------------- + * + * Read the specified amount of characters or the whole file. + * + * Arguments: + * - amount, the amount of characters or -1 to read all (Optional, default: -1). + * Returns: + * The string. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_read(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + const auto fp = self(ctx)->get_handle(); + const auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; + + if (amount == 0 || !fp) + return 0; + + std::string data; + std::size_t total = 0; + + if (amount < 0) { + std::array<char, 128> buffer; + std::size_t nread; + + while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), fp)) > 0) { + if (std::ferror(fp)) + throw from_errno(); + + std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); + total += nread; + } + } else { + data.resize(static_cast<std::size_t>(amount)); + total = std::fread(&data[0], sizeof (data[0]), static_cast<std::size_t>(amount), fp); + + if (std::ferror(fp)) + throw from_errno(); + + data.resize(total); + } + + return duk::push(ctx, data); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.readline + +/* + * Method: Irccd.File.prototype.readline() + * -------------------------------------------------------- + * + * Read the next line available. + * + * Returns: + * The next line or undefined if eof. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_readline(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + + if (fp == nullptr || std::feof(fp)) + return 0; + + std::string result; + + for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) + result += (char)ch; + if (std::ferror(fp)) + throw from_errno(); + + return duk::push(ctx, clear_crlf(result)); + }); +} + +// }}} + +// {{{ Irccd.File.prototype.remove + +/* + * Method: Irccd.File.prototype.remove() + * -------------------------------------------------------- + * + * Synonym of Irccd.File.prototype.remove(path) but with the path from the file. + * + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + boost::filesystem::remove(self(ctx)->get_path()); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.seek + +/* + * Method: Irccd.File.prototype.seek(type, amount) + * -------------------------------------------------------- + * + * Sets the position in the file. + * + * Arguments: + * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), + * - amount, the new offset. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_seek(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + auto type = duk_require_int(ctx, 0); + auto amount = duk_require_int(ctx, 1); + + if (fp != nullptr && std::fseek(fp, amount, type) != 0) + throw from_errno(); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.stat + +#if defined(HAVE_STAT) + +/* + * Method: Irccd.File.prototype.stat() [optional] + * -------------------------------------------------------- + * + * Synonym of File.stat(path) but with the path from the file. + * + * Returns: + * The stat information. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_stat(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto file = self(ctx); + struct stat st; + + if (file->get_handle() == nullptr && ::stat(file->get_path().c_str(), &st) < 0) + throw from_errno(); + + duk::push(ctx, st); + + return 1; + }); +} + +#endif // !HAVE_STAT + +// }}} + +// {{{ Irccd.File.prototype.tell + +/* + * Method: Irccd.File.prototype.tell() + * -------------------------------------------------------- + * + * Get the actual position in the file. + * + * Returns: + * The position. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_tell(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + long pos; + + if (fp == nullptr) + return 0; + + if ((pos = std::ftell(fp)) == -1L) + throw from_errno(); + + duk_push_int(ctx, pos); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.File.prototype.write + +/* + * Method: Irccd.File.prototype.write(data) + * -------------------------------------------------------- + * + * Write some characters to the file. + * + * Arguments: + * - data, the character to write. + * Returns: + * The number of bytes written. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_prototype_write(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = self(ctx)->get_handle(); + auto data = duk::require<std::string>(ctx, 0); + + if (fp == nullptr) + return 0; + + const auto nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); + + if (std::ferror(fp)) + throw from_errno(); + + duk_push_uint(ctx, nwritten); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.File [constructor] + +/* + * Function: Irccd.File(path, mode) [constructor] + * -------------------------------------------------------- + * + * Open a file specified by path with the specified mode. + * + * Arguments: + * - path, the path to the file, + * - mode, the mode string. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_constructor(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + if (!duk_is_constructor_call(ctx)) + return 0; + + const auto path = duk::require<std::string>(ctx, 0); + const auto mode = duk::require<std::string>(ctx, 1); + + duk_push_this(ctx); + duk_push_pointer(ctx, new std::shared_ptr<file>(new file(path, mode))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_pop(ctx); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File [destructor] + +/* + * Function: Irccd.File() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +auto File_destructor(duk_context* ctx) -> duk_ret_t +{ + duk_get_prop_string(ctx, 0, signature.data()); + delete static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, signature.data()); + + return 0; +} + +// }}} + +// {{{ Irccd.File.basename + +/* + * Function: Irccd.File.basename(path) + * -------------------------------------------------------- + * + * duk_ret_turn the file basename as specified in `basename(3)` C function. + * + * Arguments: + * - path, the path to the file. + * Returns: + * The base name. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_basename(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::base_name(duk_require_string(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.File.dirname + +/* + * Function: Irccd.File.dirname(path) + * -------------------------------------------------------- + * + * duk_ret_turn the file directory name as specified in `dirname(3)` C function. + * + * Arguments: + * - path, the path to the file. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_dirname(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, fs_util::dir_name(duk_require_string(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.File.exists + +/* + * Function: Irccd.File.exists(path) + * -------------------------------------------------------- + * + * Check if the file exists. + * + * Arguments: + * - path, the path to the file. + * Returns: + * True if exists. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_exists(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, boost::filesystem::exists(duk_require_string(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.File.remove + +/* + * Function Irccd.File.remove(path) + * -------------------------------------------------------- + * + * Remove the file at the specified path. + * + * Arguments: + * - path, the path to the file. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_remove(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + boost::filesystem::remove(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.File.stat + +#if defined(HAVE_STAT) + +/* + * Function Irccd.File.stat(path) [optional] + * -------------------------------------------------------- + * + * Get file information at the specified path. + * + * Arguments: + * - path, the path to the file. + * Returns: + * The stat information. + * Throws: + * - Irccd.SystemError on errors + */ +auto File_stat(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + struct stat st; + + if (::stat(duk_require_string(ctx, 0), &st) < 0) + throw from_errno(); + + return duk::push(ctx, st); + }); +} + +#endif // !HAVE_STAT + +// }}} + +// {{{ definitions + +const duk_function_list_entry methods[] = { + { "basename", File_prototype_basename, 0 }, + { "close", File_prototype_close, 0 }, + { "dirname", File_prototype_dirname, 0 }, + { "lines", File_prototype_lines, 0 }, + { "read", File_prototype_read, 1 }, + { "readline", File_prototype_readline, 0 }, + { "remove", File_prototype_remove, 0 }, + { "seek", File_prototype_seek, 2 }, +#if defined(HAVE_STAT) + { "stat", File_prototype_stat, 0 }, +#endif + { "tell", File_prototype_tell, 0 }, + { "write", File_prototype_write, 1 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "basename", File_basename, 1 }, + { "dirname", File_dirname, 1 }, + { "exists", File_exists, 1 }, + { "remove", File_remove, 1 }, +#if defined(HAVE_STAT) + { "stat", File_stat, 1 }, +#endif + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "SeekCur", SEEK_CUR }, + { "SeekEnd", SEEK_END }, + { "SeekSet", SEEK_SET }, + { nullptr, 0 } +}; + +// }}} + +} // !namespace + +// {{{ file_js_api + +auto file_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.File"; +} + +void file_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), File_constructor, 2); + duk_put_number_list(plugin->get_context(), -1, constants); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_push_c_function(plugin->get_context(), File_destructor, 1); + duk_set_finalizer(plugin->get_context(), -2); + duk_dup(plugin->get_context(), -1); + duk_put_global_string(plugin->get_context(), prototype.data()); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "File"); + duk_pop(plugin->get_context()); +} + +// }}} + +// {{{ duk::type_traits<std::shared_ptr<file>> + +using file_traits = duk::type_traits<std::shared_ptr<file>>; + +void file_traits::push(duk_context* ctx, std::shared_ptr<file> fp) +{ + assert(ctx); + assert(fp); + + duk::stack_guard sa(ctx, 1); + + duk_push_object(ctx); + duk_push_pointer(ctx, new std::shared_ptr<file>(std::move(fp))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_get_global_string(ctx, prototype.data()); + duk_set_prototype(ctx, -2); +} + +auto file_traits::require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<file> +{ + if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature.data())) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); + + duk_get_prop_string(ctx, index, signature.data()); + const auto fp = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *fp; +} + +// }}} + +// {{{ duk::type_traits<struct stat> + +#if defined(HAVE_STAT) + +void duk::type_traits<struct stat>::push(duk_context* ctx, const struct stat& st) +{ + duk::stack_guard sa(ctx, 1); + + duk_push_object(ctx); + +#if defined(HAVE_STAT_ST_ATIME) + duk_push_int(ctx, st.st_atime); + duk_put_prop_string(ctx, -2, "atime"); +#endif +#if defined(HAVE_STAT_ST_BLKSIZE) + duk_push_int(ctx, st.st_blksize); + duk_put_prop_string(ctx, -2, "blksize"); +#endif +#if defined(HAVE_STAT_ST_BLOCKS) + duk_push_int(ctx, st.st_blocks); + duk_put_prop_string(ctx, -2, "blocks"); +#endif +#if defined(HAVE_STAT_ST_CTIME) + duk_push_int(ctx, st.st_ctime); + duk_put_prop_string(ctx, -2, "ctime"); +#endif +#if defined(HAVE_STAT_ST_DEV) + duk_push_int(ctx, st.st_dev); + duk_put_prop_string(ctx, -2, "dev"); +#endif +#if defined(HAVE_STAT_ST_GID) + duk_push_int(ctx, st.st_gid); + duk_put_prop_string(ctx, -2, "gid"); +#endif +#if defined(HAVE_STAT_ST_INO) + duk_push_int(ctx, st.st_ino); + duk_put_prop_string(ctx, -2, "ino"); +#endif +#if defined(HAVE_STAT_ST_MODE) + duk_push_int(ctx, st.st_mode); + duk_put_prop_string(ctx, -2, "mode"); +#endif +#if defined(HAVE_STAT_ST_MTIME) + duk_push_int(ctx, st.st_mtime); + duk_put_prop_string(ctx, -2, "mtime"); +#endif +#if defined(HAVE_STAT_ST_NLINK) + duk_push_int(ctx, st.st_nlink); + duk_put_prop_string(ctx, -2, "nlink"); +#endif +#if defined(HAVE_STAT_ST_RDEV) + duk_push_int(ctx, st.st_rdev); + duk_put_prop_string(ctx, -2, "rdev"); +#endif +#if defined(HAVE_STAT_ST_SIZE) + duk_push_int(ctx, st.st_size); + duk_put_prop_string(ctx, -2, "size"); +#endif +#if defined(HAVE_STAT_ST_UID) + duk_push_int(ctx, st.st_uid); + duk_put_prop_string(ctx, -2, "uid"); +#endif +} + +#endif // !HAVE_STAT + +// }}} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/file_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,211 @@ +/* + * file_js_api.hpp -- Irccd.File API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_FILE_JS_API_HPP +#define IRCCD_JS_FILE_JS_API_HPP + +/** + * \file file_js_api.hpp + * \brief Irccd.File Javascript API. + */ + +#include <irccd/sysconfig.hpp> + +#if defined(HAVE_STAT) +# include <sys/types.h> +# include <sys/stat.h> +#endif + +#include <cassert> +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <functional> +#include <stdexcept> +#include <string> + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Object for Javascript to perform I/O. + * + * This class can be constructed to Javascript. + * + * It is used in: + * + * - Irccd.File [constructor] + * - Irccd.System.popen (optional) + */ +class file { +private: + file(const file&) = delete; + file& operator=(const file&) = delete; + + file(file&&) = delete; + file& operator=(file&&) = delete; + +private: + std::string path_; + std::FILE* stream_; + std::function<void (std::FILE*)> destructor_; + +public: + /** + * Construct a file specified by path + * + * \param path the path + * \param mode the mode string (for std::fopen) + * \throw std::runtime_error on failures + */ + inline file(std::string path, const std::string& mode) + : path_(std::move(path)) + , destructor_([] (std::FILE* fp) { std::fclose(fp); }) + { + if ((stream_ = std::fopen(path_.c_str(), mode.c_str())) == nullptr) + throw std::runtime_error(std::strerror(errno)); + } + + /** + * Construct a file from a already created FILE pointer (e.g. popen). + * + * The class takes ownership of fp and will close it. + * + * \pre destructor must not be null + * \param fp the file pointer + * \param destructor the function to close fp (e.g. std::fclose) + */ + inline file(std::FILE* fp, std::function<void (std::FILE*)> destructor) noexcept + : stream_(fp) + , destructor_(std::move(destructor)) + { + assert(destructor_ != nullptr); + } + + /** + * Closes the file. + */ + virtual ~file() noexcept + { + close(); + } + + /** + * Get the path. + * + * \return the path + * \warning empty when constructed from the FILE constructor + */ + inline const std::string& get_path() const noexcept + { + return path_; + } + + /** + * Get the handle. + * + * \return the handle or nullptr if the stream was closed + */ + inline std::FILE* get_handle() noexcept + { + return stream_; + } + + /** + * Force close, can be safely called multiple times. + */ + inline void close() noexcept + { + if (stream_) { + destructor_(stream_); + stream_ = nullptr; + } + } +}; + +/** + * \brief Irccd.File Javascript API. + * \ingroup js_api + */ +class file_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialization for generic file type as shared_ptr. + * + * Supports push, require. + */ +template <> +struct type_traits<std::shared_ptr<file>> { + /** + * Push a file. + * + * \pre fp != nullptr + * \param ctx the the context + * \param fp the file + */ + static void push(duk_context* ctx, std::shared_ptr<file> fp); + + /** + * Require a file. Raises a JavaScript error if not a File. + * + * \param ctx the context + * \param index the index + * \return the file pointer + */ + static auto require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<file>; +}; + +#if defined(HAVE_STAT) + +/** + * \brief Specialization for struct stat. + * + * Supports push. + */ +template <> +struct type_traits<struct stat> { + /** + * Push the stat information to the stack as Javascript object. + * + * \param ctx the context + * \param st the stat structure + */ + static void push(duk_context* ctx, const struct stat& st); +}; + +#endif // !HAVE_STAT + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_FILE_JS_API_HPP
--- a/libirccd-js/irccd/js/file_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,774 +0,0 @@ -/* - * file_jsapi.cpp -- Irccd.File API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <algorithm> -#include <array> -#include <cassert> -#include <iterator> -#include <vector> - -#include <boost/filesystem.hpp> - -#include <irccd/fs_util.hpp> - -#include "file_jsapi.hpp" -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" - -namespace irccd { - -namespace { - -const char *signature("\xff""\xff""irccd-file-ptr"); -const char *prototype("\xff""\xff""irccd-file-prototype"); - -// Remove trailing \r for CRLF line style. -std::string clear_crlf(std::string input) noexcept -{ - if (input.length() > 0 && input.back() == '\r') - input.pop_back(); - - return input; -} - -std::system_error from_errno() noexcept -{ - return std::system_error(make_error_code(static_cast<std::errc>(errno))); -} - -std::shared_ptr<file> self(duk_context* ctx) -{ - dukx_stack_assert sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature); - auto ptr = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); - - return *ptr; -} - -template <typename Handler> -duk_ret_t wrap(duk_context* ctx, Handler handler) -{ - try { - return handler(); - } catch (const boost::system::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -// {{{ Irccd.File.prototype.basename - -/* - * Method: Irccd.File.prototype.basename() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.basename(path)` but with the path from the file. - * - * Returns: - * The base name. - */ -duk_ret_t File_prototype_basename(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, fs_util::base_name(self(ctx)->get_path())); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.close - -/* - * Method: Irccd.File.prototype.close() - * -------------------------------------------------------- - * - * Force close of the file, automatically called when object is collected. - */ -duk_ret_t File_prototype_close(duk_context* ctx) -{ - return wrap(ctx, [&] { - self(ctx)->close(); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.dirname - -/* - * Method: Irccd.File.prototype.dirname() - * -------------------------------------------------------- - * - * Synonym of `Irccd.File.dirname(path)` but with the path from the file. - * - * Returns: - * The directory name. - */ -duk_ret_t File_prototype_dirname(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, fs_util::dir_name(self(ctx)->get_path())); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.lines - -/* - * Method: Irccd.File.prototype.lines() - * -------------------------------------------------------- - * - * Read all lines and return an array. - * - * Returns: - * An array with all lines. - * Throws - * - Any exception on error. - */ -duk_ret_t File_prototype_lines(duk_context* ctx) -{ - return wrap(ctx, [&] { - duk_push_array(ctx); - - std::FILE* fp = self(ctx)->get_handle(); - std::string buffer; - std::array<char, 128> data; - std::int32_t i = 0; - - while (std::fgets(&data[0], data.size(), fp) != nullptr) { - buffer += data.data(); - - const auto pos = buffer.find('\n'); - - if (pos != std::string::npos) { - dukx_push(ctx, clear_crlf(buffer.substr(0, pos))); - duk_put_prop_index(ctx, -2, i++); - - buffer.erase(0, pos + 1); - } - } - - // Maybe an error in the stream. - if (std::ferror(fp)) - throw from_errno(); - - // Missing '\n' in end of file. - if (!buffer.empty()) { - dukx_push(ctx, clear_crlf(buffer)); - duk_put_prop_index(ctx, -2, i++); - } - - return 1; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.read - -/* - * Method: Irccd.File.prototype.read(amount) - * -------------------------------------------------------- - * - * Read the specified amount of characters or the whole file. - * - * Arguments: - * - amount, the amount of characters or -1 to read all (Optional, default: -1). - * Returns: - * The string. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_read(duk_context* ctx) -{ - return wrap(ctx, [&] { - const auto fp = self(ctx)->get_handle(); - const auto amount = duk_is_number(ctx, 0) ? duk_get_int(ctx, 0) : -1; - - if (amount == 0 || !fp) - return 0; - - std::string data; - std::size_t total = 0; - - if (amount < 0) { - std::array<char, 128> buffer; - std::size_t nread; - - while ((nread = std::fread(&buffer[0], sizeof (buffer[0]), buffer.size(), fp)) > 0) { - if (std::ferror(fp)) - throw from_errno(); - - std::copy(buffer.begin(), buffer.begin() + nread, std::back_inserter(data)); - total += nread; - } - } else { - data.resize(static_cast<std::size_t>(amount)); - total = std::fread(&data[0], sizeof (data[0]), static_cast<std::size_t>(amount), fp); - - if (std::ferror(fp)) - throw from_errno(); - - data.resize(total); - } - - return dukx_push(ctx, data); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.readline - -/* - * Method: Irccd.File.prototype.readline() - * -------------------------------------------------------- - * - * Read the next line available. - * - * Returns: - * The next line or undefined if eof. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_readline(duk_context* ctx) -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - - if (fp == nullptr || std::feof(fp)) - return 0; - - std::string result; - - for (int ch; (ch = std::fgetc(fp)) != EOF && ch != '\n'; ) - result += (char)ch; - if (std::ferror(fp)) - throw from_errno(); - - return dukx_push(ctx, clear_crlf(result)); - }); -} - -// }}} - -// {{{ Irccd.File.prototype.remove - -/* - * Method: Irccd.File.prototype.remove() - * -------------------------------------------------------- - * - * Synonym of Irccd.File.prototype.remove(path) but with the path from the file. - * - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_remove(duk_context* ctx) -{ - return wrap(ctx, [&] { - boost::filesystem::remove(self(ctx)->get_path()); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.seek - -/* - * Method: Irccd.File.prototype.seek(type, amount) - * -------------------------------------------------------- - * - * Sets the position in the file. - * - * Arguments: - * - type, the type of setting (File.SeekSet, File.SeekCur, File.SeekSet), - * - amount, the new offset. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_seek(duk_context* ctx) -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - auto type = duk_require_int(ctx, 0); - auto amount = duk_require_int(ctx, 1); - - if (fp != nullptr && std::fseek(fp, amount, type) != 0) - throw from_errno(); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.stat - -#if defined(HAVE_STAT) - -/* - * Method: Irccd.File.prototype.stat() [optional] - * -------------------------------------------------------- - * - * Synonym of File.stat(path) but with the path from the file. - * - * Returns: - * The stat information. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_stat(duk_context* ctx) -{ - return wrap(ctx, [&] { - auto file = self(ctx); - struct stat st; - - if (file->get_handle() == nullptr && ::stat(file->get_path().c_str(), &st) < 0) - throw from_errno(); - - dukx_push(ctx, st); - - return 1; - }); -} - -#endif // !HAVE_STAT - -// }}} - -// {{{ Irccd.File.prototype.tell - -/* - * Method: Irccd.File.prototype.tell() - * -------------------------------------------------------- - * - * Get the actual position in the file. - * - * Returns: - * The position. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_tell(duk_context* ctx) -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - long pos; - - if (fp == nullptr) - return 0; - - if ((pos = std::ftell(fp)) == -1L) - throw from_errno(); - - duk_push_int(ctx, pos); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.File.prototype.write - -/* - * Method: Irccd.File.prototype.write(data) - * -------------------------------------------------------- - * - * Write some characters to the file. - * - * Arguments: - * - data, the character to write. - * Returns: - * The number of bytes written. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_prototype_write(duk_context* ctx) -{ - return wrap(ctx, [&] { - auto fp = self(ctx)->get_handle(); - auto data = dukx_require<std::string>(ctx, 0); - - if (fp == nullptr) - return 0; - - const auto nwritten = std::fwrite(data.c_str(), 1, data.length(), fp); - - if (std::ferror(fp)) - throw from_errno(); - - duk_push_uint(ctx, nwritten); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.File [constructor] - -/* - * Function: Irccd.File(path, mode) [constructor] - * -------------------------------------------------------- - * - * Open a file specified by path with the specified mode. - * - * Arguments: - * - path, the path to the file, - * - mode, the mode string. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_constructor(duk_context* ctx) -{ - return wrap(ctx, [&] { - if (!duk_is_constructor_call(ctx)) - return 0; - - const auto path = dukx_require<std::string>(ctx, 0); - const auto mode = dukx_require<std::string>(ctx, 1); - - duk_push_this(ctx); - duk_push_pointer(ctx, new std::shared_ptr<file>(new file(path, mode))); - duk_put_prop_string(ctx, -2, signature); - duk_pop(ctx); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File [destructor] - -/* - * Function: Irccd.File() [destructor] - * ------------------------------------------------------------------ - * - * Delete the property. - */ -duk_ret_t File_destructor(duk_context* ctx) -{ - duk_get_prop_string(ctx, 0, signature); - delete static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_del_prop_string(ctx, 0, signature); - - return 0; -} - -// }}} - -// {{{ Irccd.File.basename - -/* - * Function: Irccd.File.basename(path) - * -------------------------------------------------------- - * - * duk_ret_turn the file basename as specified in `basename(3)` C function. - * - * Arguments: - * - path, the path to the file. - * Returns: - * The base name. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_basename(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, fs_util::base_name(duk_require_string(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.File.dirname - -/* - * Function: Irccd.File.dirname(path) - * -------------------------------------------------------- - * - * duk_ret_turn the file directory name as specified in `dirname(3)` C function. - * - * Arguments: - * - path, the path to the file. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_dirname(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, fs_util::dir_name(duk_require_string(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.File.exists - -/* - * Function: Irccd.File.exists(path) - * -------------------------------------------------------- - * - * Check if the file exists. - * - * Arguments: - * - path, the path to the file. - * Returns: - * True if exists. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_exists(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, boost::filesystem::exists(duk_require_string(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.File.remove - -/* - * function Irccd.File.remove(path) - * -------------------------------------------------------- - * - * Remove the file at the specified path. - * - * Arguments: - * - path, the path to the file. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_remove(duk_context* ctx) -{ - return wrap(ctx, [&] { - boost::filesystem::remove(dukx_require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.File.stat - -#if defined(HAVE_STAT) - -/* - * function Irccd.File.stat(path) [optional] - * -------------------------------------------------------- - * - * Get file information at the specified path. - * - * Arguments: - * - path, the path to the file. - * Returns: - * The stat information. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t File_stat(duk_context* ctx) -{ - return wrap(ctx, [&] { - struct stat st; - - if (::stat(duk_require_string(ctx, 0), &st) < 0) - throw from_errno(); - - return dukx_push(ctx, st); - }); -} - -#endif // !HAVE_STAT - -// }}} - -const duk_function_list_entry methods[] = { - { "basename", File_prototype_basename, 0 }, - { "close", File_prototype_close, 0 }, - { "dirname", File_prototype_dirname, 0 }, - { "lines", File_prototype_lines, 0 }, - { "read", File_prototype_read, 1 }, - { "readline", File_prototype_readline, 0 }, - { "remove", File_prototype_remove, 0 }, - { "seek", File_prototype_seek, 2 }, -#if defined(HAVE_STAT) - { "stat", File_prototype_stat, 0 }, -#endif - { "tell", File_prototype_tell, 0 }, - { "write", File_prototype_write, 1 }, - { nullptr, nullptr, 0 } -}; - -const duk_function_list_entry functions[] = { - { "basename", File_basename, 1 }, - { "dirname", File_dirname, 1 }, - { "exists", File_exists, 1 }, - { "remove", File_remove, 1 }, -#if defined(HAVE_STAT) - { "stat", File_stat, 1 }, -#endif - { nullptr, nullptr, 0 } -}; - -const duk_number_list_entry constants[] = { - { "SeekCur", SEEK_CUR }, - { "SeekEnd", SEEK_END }, - { "SeekSet", SEEK_SET }, - { nullptr, 0 } -}; - -} // !namespace - -std::string file_jsapi::get_name() const -{ - return "Irccd.File"; -} - -void file_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), File_constructor, 2); - duk_put_number_list(plugin->get_context(), -1, constants); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_push_c_function(plugin->get_context(), File_destructor, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_dup(plugin->get_context(), -1); - duk_put_global_string(plugin->get_context(), prototype); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "File"); - duk_pop(plugin->get_context()); -} - -using file_traits = dukx_type_traits<std::shared_ptr<file>>; - -void file_traits::push(duk_context* ctx, std::shared_ptr<file> fp) -{ - assert(ctx); - assert(fp); - - dukx_stack_assert sa(ctx, 1); - - duk_push_object(ctx); - duk_push_pointer(ctx, new std::shared_ptr<file>(std::move(fp))); - duk_put_prop_string(ctx, -2, signature); - duk_get_global_string(ctx, prototype); - duk_set_prototype(ctx, -2); -} - -std::shared_ptr<file> file_traits::require(duk_context* ctx, duk_idx_t index) -{ - if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a File object"); - - duk_get_prop_string(ctx, index, signature); - const auto fp = static_cast<std::shared_ptr<file>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return *fp; -} - -#if defined(HAVE_STAT) - -void dukx_type_traits<struct stat>::push(duk_context* ctx, const struct stat& st) -{ - dukx_stack_assert sa(ctx, 1); - - duk_push_object(ctx); - -#if defined(HAVE_STAT_ST_ATIME) - duk_push_int(ctx, st.st_atime); - duk_put_prop_string(ctx, -2, "atime"); -#endif -#if defined(HAVE_STAT_ST_BLKSIZE) - duk_push_int(ctx, st.st_blksize); - duk_put_prop_string(ctx, -2, "blksize"); -#endif -#if defined(HAVE_STAT_ST_BLOCKS) - duk_push_int(ctx, st.st_blocks); - duk_put_prop_string(ctx, -2, "blocks"); -#endif -#if defined(HAVE_STAT_ST_CTIME) - duk_push_int(ctx, st.st_ctime); - duk_put_prop_string(ctx, -2, "ctime"); -#endif -#if defined(HAVE_STAT_ST_DEV) - duk_push_int(ctx, st.st_dev); - duk_put_prop_string(ctx, -2, "dev"); -#endif -#if defined(HAVE_STAT_ST_GID) - duk_push_int(ctx, st.st_gid); - duk_put_prop_string(ctx, -2, "gid"); -#endif -#if defined(HAVE_STAT_ST_INO) - duk_push_int(ctx, st.st_ino); - duk_put_prop_string(ctx, -2, "ino"); -#endif -#if defined(HAVE_STAT_ST_MODE) - duk_push_int(ctx, st.st_mode); - duk_put_prop_string(ctx, -2, "mode"); -#endif -#if defined(HAVE_STAT_ST_MTIME) - duk_push_int(ctx, st.st_mtime); - duk_put_prop_string(ctx, -2, "mtime"); -#endif -#if defined(HAVE_STAT_ST_NLINK) - duk_push_int(ctx, st.st_nlink); - duk_put_prop_string(ctx, -2, "nlink"); -#endif -#if defined(HAVE_STAT_ST_RDEV) - duk_push_int(ctx, st.st_rdev); - duk_put_prop_string(ctx, -2, "rdev"); -#endif -#if defined(HAVE_STAT_ST_SIZE) - duk_push_int(ctx, st.st_size); - duk_put_prop_string(ctx, -2, "size"); -#endif -#if defined(HAVE_STAT_ST_UID) - duk_push_int(ctx, st.st_uid); - duk_put_prop_string(ctx, -2, "uid"); -#endif -} - -#endif // !HAVE_STAT - -} // !irccd
--- a/libirccd-js/irccd/js/file_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ -/* - * file_jsapi.hpp -- Irccd.File API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_FILE_JSAPI_HPP -#define IRCCD_JS_FILE_JSAPI_HPP - -/** - * \file file_jsapi.hpp - * \brief Irccd.File Javascript API. - */ - -#include <irccd/sysconfig.hpp> - -#if defined(HAVE_STAT) -# include <sys/types.h> -# include <sys/stat.h> -#endif - -#include <cassert> -#include <cerrno> -#include <cstdio> -#include <cstring> -#include <functional> -#include <stdexcept> -#include <string> - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Object for Javascript to perform I/O. - * - * This class can be constructed to Javascript. - * - * It is used in: - * - * - Irccd.File [constructor] - * - Irccd.System.popen (optional) - */ -class file { -private: - file(const file&) = delete; - file& operator=(const file&) = delete; - - file(file&&) = delete; - file& operator=(file&&) = delete; - -private: - std::string path_; - std::FILE* stream_; - std::function<void (std::FILE*)> destructor_; - -public: - /** - * Construct a file specified by path - * - * \param path the path - * \param mode the mode string (for std::fopen) - * \throw std::runtime_error on failures - */ - inline file(std::string path, const std::string& mode) - : path_(std::move(path)) - , destructor_([] (std::FILE* fp) { std::fclose(fp); }) - { - if ((stream_ = std::fopen(path_.c_str(), mode.c_str())) == nullptr) - throw std::runtime_error(std::strerror(errno)); - } - - /** - * Construct a file from a already created FILE pointer (e.g. popen). - * - * The class takes ownership of fp and will close it. - * - * \pre destructor must not be null - * \param fp the file pointer - * \param destructor the function to close fp (e.g. std::fclose) - */ - inline file(std::FILE* fp, std::function<void (std::FILE*)> destructor) noexcept - : stream_(fp) - , destructor_(std::move(destructor)) - { - assert(destructor_ != nullptr); - } - - /** - * Closes the file. - */ - virtual ~file() noexcept - { - close(); - } - - /** - * Get the path. - * - * \return the path - * \warning empty when constructed from the FILE constructor - */ - inline const std::string& get_path() const noexcept - { - return path_; - } - - /** - * Get the handle. - * - * \return the handle or nullptr if the stream was closed - */ - inline std::FILE* get_handle() noexcept - { - return stream_; - } - - /** - * Force close, can be safely called multiple times. - */ - inline void close() noexcept - { - if (stream_) { - destructor_(stream_); - stream_ = nullptr; - } - } -}; - -/** - * \brief Irccd.File Javascript API. - * \ingroup jsapi - */ -class file_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -/** - * \brief Specialization for generic file type as shared_ptr. - * - * Supports push, require. - */ -template <> -class dukx_type_traits<std::shared_ptr<file>> : public std::true_type { -public: - /** - * Push a file. - * - * \pre fp != nullptr - * \param ctx the the context - * \param fp the file - */ - static void push(duk_context* ctx, std::shared_ptr<file> fp); - - /** - * Require a file. Raises a JavaScript error if not a File. - * - * \param ctx the context - * \param index the index - * \return the file pointer - */ - static std::shared_ptr<file> require(duk_context* ctx, duk_idx_t index); -}; - -#if defined(HAVE_STAT) - -/** - * \brief Specialization for struct stat. - * - * Supports push. - */ -template <> -class dukx_type_traits<struct stat> : public std::true_type { -public: - /** - * Push the stat information to the stack as Javascript object. - * - * \param ctx the context - * \param st the stat structure - */ - static void push(duk_context* ctx, const struct stat& st); -}; - -#endif // !HAVE_STAT - -} // !irccd - -#endif // !IRCCD_JS_FILE_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/irccd_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,244 @@ +/* + * irccd_js_api.cpp -- Irccd API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/sysconfig.hpp> + +#include <cerrno> +#include <string> +#include <unordered_map> + +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" + +namespace irccd::js { + +namespace { + +// {{{ do_raise + +template <typename Error> +void do_raise(duk_context* ctx, const Error& ex) +{ + duk::stack_guard sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "SystemError"); + duk_remove(ctx, -2); + duk::push(ctx, ex.code().value()); + duk::push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + +// }}} + +// {{{ Irccd.SystemError [constructor] + +auto constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "errno"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "SystemError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + +// {{{ definitions + +const std::unordered_map<std::string, int> errors{ + { "E2BIG", E2BIG }, + { "EACCES", EACCES }, + { "EADDRINUSE", EADDRINUSE }, + { "EADDRNOTAVAIL", EADDRNOTAVAIL }, + { "EAFNOSUPPORT", EAFNOSUPPORT }, + { "EAGAIN", EAGAIN }, + { "EALREADY", EALREADY }, + { "EBADF", EBADF }, +#if defined(EBADMSG) + { "EBADMSG", EBADMSG }, +#endif + { "EBUSY", EBUSY }, + { "ECANCELED", ECANCELED }, + { "ECHILD", ECHILD }, + { "ECONNABORTED", ECONNABORTED }, + { "ECONNREFUSED", ECONNREFUSED }, + { "ECONNRESET", ECONNRESET }, + { "EDEADLK", EDEADLK }, + { "EDESTADDRREQ", EDESTADDRREQ }, + { "EDOM", EDOM }, + { "EEXIST", EEXIST }, + { "EFAULT", EFAULT }, + { "EFBIG", EFBIG }, + { "EHOSTUNREACH", EHOSTUNREACH }, +#if defined(EIDRM) + { "EIDRM", EIDRM }, +#endif + { "EILSEQ", EILSEQ }, + { "EINPROGRESS", EINPROGRESS }, + { "EINTR", EINTR }, + { "EINVAL", EINVAL }, + { "EIO", EIO }, + { "EISCONN", EISCONN }, + { "EISDIR", EISDIR }, + { "ELOOP", ELOOP }, + { "EMFILE", EMFILE }, + { "EMLINK", EMLINK }, + { "EMSGSIZE", EMSGSIZE }, + { "ENAMETOOLONG", ENAMETOOLONG }, + { "ENETDOWN", ENETDOWN }, + { "ENETRESET", ENETRESET }, + { "ENETUNREACH", ENETUNREACH }, + { "ENFILE", ENFILE }, + { "ENOBUFS", ENOBUFS }, +#if defined(ENODATA) + { "ENODATA", ENODATA }, +#endif + { "ENODEV", ENODEV }, + { "ENOENT", ENOENT }, + { "ENOEXEC", ENOEXEC }, + { "ENOLCK", ENOLCK }, +#if defined(ENOLINK) + { "ENOLINK", ENOLINK }, +#endif + { "ENOMEM", ENOMEM }, +#if defined(ENOMSG) + { "ENOMSG", ENOMSG }, +#endif + { "ENOPROTOOPT", ENOPROTOOPT }, + { "ENOSPC", ENOSPC }, +#if defined(ENOSR) + { "ENOSR", ENOSR }, +#endif +#if defined(ENOSTR) + { "ENOSTR", ENOSTR }, +#endif + { "ENOSYS", ENOSYS }, + { "ENOTCONN", ENOTCONN }, + { "ENOTDIR", ENOTDIR }, + { "ENOTEMPTY", ENOTEMPTY }, +#if defined(ENOTRECOVERABLE) + { "ENOTRECOVERABLE", ENOTRECOVERABLE }, +#endif + { "ENOTSOCK", ENOTSOCK }, + { "ENOTSUP", ENOTSUP }, + { "ENOTTY", ENOTTY }, + { "ENXIO", ENXIO }, + { "EOPNOTSUPP", EOPNOTSUPP }, + { "EOVERFLOW", EOVERFLOW }, + { "EOWNERDEAD", EOWNERDEAD }, + { "EPERM", EPERM }, + { "EPIPE", EPIPE }, + { "EPROTO", EPROTO }, + { "EPROTONOSUPPORT", EPROTONOSUPPORT }, + { "EPROTOTYPE", EPROTOTYPE }, + { "ERANGE", ERANGE }, + { "EROFS", EROFS }, + { "ESPIPE", ESPIPE }, + { "ESRCH", ESRCH }, +#if defined(ETIME) + { "ETIME", ETIME }, +#endif + { "ETIMEDOUT", ETIMEDOUT }, +#if defined(ETXTBSY) + { "ETXTBSY", ETXTBSY }, +#endif + { "EWOULDBLOCK", EWOULDBLOCK }, + { "EXDEV", EXDEV } +}; + +// }}} + +} // !namespace + +void duk::type_traits<std::system_error>::raise(duk_context* ctx, const std::system_error& ex) +{ + do_raise(ctx, ex); +} + +void duk::type_traits<boost::system::system_error>::raise(duk_context* ctx, const boost::system::system_error& ex) +{ + do_raise(ctx, ex); +} + +auto irccd_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd"; +} + +void irccd_js_api::load(irccd& irccd, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + // irccd. + duk_push_object(plugin->get_context()); + + // Version. + duk_push_object(plugin->get_context()); + duk::push(plugin->get_context(), IRCCD_VERSION_MAJOR); + duk_put_prop_string(plugin->get_context(), -2, "major"); + duk::push(plugin->get_context(), IRCCD_VERSION_MINOR); + duk_put_prop_string(plugin->get_context(), -2, "minor"); + duk::push(plugin->get_context(), IRCCD_VERSION_PATCH); + duk_put_prop_string(plugin->get_context(), -2, "patch"); + duk_put_prop_string(plugin->get_context(), -2, "version"); + + // Create the system_error that inherits from Error. + duk_push_c_function(plugin->get_context(), constructor, 2); + + // Put errno codes into the irccd.system_error object. + for (const auto& [k, v] : errors) { + duk_push_int(plugin->get_context(), v); + duk_put_prop_string(plugin->get_context(), -2, k.c_str()); + } + + duk_push_object(plugin->get_context()); + duk_get_global_string(plugin->get_context(), "Error"); + duk_get_prop_string(plugin->get_context(), -1, "prototype"); + duk_remove(plugin->get_context(), -2); + duk_set_prototype(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "SystemError"); + + // Set irccd as global. + duk_put_global_string(plugin->get_context(), "Irccd"); + + // Store global instance. + duk_push_pointer(plugin->get_context(), &irccd); + duk_put_global_string(plugin->get_context(), "\xff""\xff""irccd-ref"); +} + +auto duk::type_traits<irccd>::self(duk_context *ctx) -> irccd& +{ + duk::stack_guard sa(ctx); + + duk_get_global_string(ctx, "\xff""\xff""irccd-ref"); + const auto ptr = static_cast<irccd*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *ptr; +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/irccd_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,103 @@ +/* + * irccd_js_api.hpp -- Irccd API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_IRCCD_JS_API_HPP +#define IRCCD_JS_IRCCD_JS_API_HPP + +/** + * \file irccd_js_api.hpp + * \brief irccd Javascript API. + */ + +#include <cerrno> +#include <cstring> +#include <string> +#include <system_error> + +#include <boost/system/system_error.hpp> + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd Javascript API. + * \ingroup js_api + */ +class irccd_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialize dukx_type_traits for irccd. + */ +template <> +struct type_traits<irccd> { + /** + * Get irccd instance stored in this context. + * + * \param ctx the context + * \return the irccd reference + */ + static auto self(duk_context* ctx) -> irccd&; +}; + +/** + * \brief Specialize dukx_type_traits for std::system_error. + */ +template <> +struct type_traits<std::system_error> { + /** + * Raise an Irccd.SystemError. + * + * \param ctx the context + * param ex the exception + */ + static void raise(duk_context* ctx, const std::system_error& ex); +}; + +/** + * \brief Specialize dukx_type_traits for boost::system::system_error. + */ +template <> +struct type_traits<boost::system::system_error> { + /** + * Raise an Irccd.SystemError. + * + * \param ctx the context + * param ex the exception + */ + static void raise(duk_context* ctx, const boost::system::system_error& ex); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_IRCCD_JS_API_HPP
--- a/libirccd-js/irccd/js/irccd_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,232 +0,0 @@ -/* - * irccd_jsapi.cpp -- Irccd API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/sysconfig.hpp> - -#include <cerrno> -#include <string> -#include <unordered_map> - -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" - -namespace irccd { - -namespace { - -template <typename Error> -void do_raise(duk_context* ctx, const Error& ex) -{ - dukx_stack_assert sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "SystemError"); - duk_remove(ctx, -2); - dukx_push(ctx, ex.code().value()); - dukx_push(ctx, ex.code().message()); - duk_new(ctx, 2); - - (void)duk_throw(ctx); -} - -const std::unordered_map<std::string, int> errors{ - { "E2BIG", E2BIG }, - { "EACCES", EACCES }, - { "EADDRINUSE", EADDRINUSE }, - { "EADDRNOTAVAIL", EADDRNOTAVAIL }, - { "EAFNOSUPPORT", EAFNOSUPPORT }, - { "EAGAIN", EAGAIN }, - { "EALREADY", EALREADY }, - { "EBADF", EBADF }, -#if defined(EBADMSG) - { "EBADMSG", EBADMSG }, -#endif - { "EBUSY", EBUSY }, - { "ECANCELED", ECANCELED }, - { "ECHILD", ECHILD }, - { "ECONNABORTED", ECONNABORTED }, - { "ECONNREFUSED", ECONNREFUSED }, - { "ECONNRESET", ECONNRESET }, - { "EDEADLK", EDEADLK }, - { "EDESTADDRREQ", EDESTADDRREQ }, - { "EDOM", EDOM }, - { "EEXIST", EEXIST }, - { "EFAULT", EFAULT }, - { "EFBIG", EFBIG }, - { "EHOSTUNREACH", EHOSTUNREACH }, -#if defined(EIDRM) - { "EIDRM", EIDRM }, -#endif - { "EILSEQ", EILSEQ }, - { "EINPROGRESS", EINPROGRESS }, - { "EINTR", EINTR }, - { "EINVAL", EINVAL }, - { "EIO", EIO }, - { "EISCONN", EISCONN }, - { "EISDIR", EISDIR }, - { "ELOOP", ELOOP }, - { "EMFILE", EMFILE }, - { "EMLINK", EMLINK }, - { "EMSGSIZE", EMSGSIZE }, - { "ENAMETOOLONG", ENAMETOOLONG }, - { "ENETDOWN", ENETDOWN }, - { "ENETRESET", ENETRESET }, - { "ENETUNREACH", ENETUNREACH }, - { "ENFILE", ENFILE }, - { "ENOBUFS", ENOBUFS }, -#if defined(ENODATA) - { "ENODATA", ENODATA }, -#endif - { "ENODEV", ENODEV }, - { "ENOENT", ENOENT }, - { "ENOEXEC", ENOEXEC }, - { "ENOLCK", ENOLCK }, -#if defined(ENOLINK) - { "ENOLINK", ENOLINK }, -#endif - { "ENOMEM", ENOMEM }, -#if defined(ENOMSG) - { "ENOMSG", ENOMSG }, -#endif - { "ENOPROTOOPT", ENOPROTOOPT }, - { "ENOSPC", ENOSPC }, -#if defined(ENOSR) - { "ENOSR", ENOSR }, -#endif -#if defined(ENOSTR) - { "ENOSTR", ENOSTR }, -#endif - { "ENOSYS", ENOSYS }, - { "ENOTCONN", ENOTCONN }, - { "ENOTDIR", ENOTDIR }, - { "ENOTEMPTY", ENOTEMPTY }, -#if defined(ENOTRECOVERABLE) - { "ENOTRECOVERABLE", ENOTRECOVERABLE }, -#endif - { "ENOTSOCK", ENOTSOCK }, - { "ENOTSUP", ENOTSUP }, - { "ENOTTY", ENOTTY }, - { "ENXIO", ENXIO }, - { "EOPNOTSUPP", EOPNOTSUPP }, - { "EOVERFLOW", EOVERFLOW }, - { "EOWNERDEAD", EOWNERDEAD }, - { "EPERM", EPERM }, - { "EPIPE", EPIPE }, - { "EPROTO", EPROTO }, - { "EPROTONOSUPPORT", EPROTONOSUPPORT }, - { "EPROTOTYPE", EPROTOTYPE }, - { "ERANGE", ERANGE }, - { "EROFS", EROFS }, - { "ESPIPE", ESPIPE }, - { "ESRCH", ESRCH }, -#if defined(ETIME) - { "ETIME", ETIME }, -#endif - { "ETIMEDOUT", ETIMEDOUT }, -#if defined(ETXTBSY) - { "ETXTBSY", ETXTBSY }, -#endif - { "EWOULDBLOCK", EWOULDBLOCK }, - { "EXDEV", EXDEV } -}; - -duk_ret_t constructor(duk_context* ctx) -{ - duk_push_this(ctx); - duk_push_int(ctx, duk_require_int(ctx, 0)); - duk_put_prop_string(ctx, -2, "errno"); - duk_push_string(ctx, duk_require_string(ctx, 1)); - duk_put_prop_string(ctx, -2, "message"); - duk_push_string(ctx, "SystemError"); - duk_put_prop_string(ctx, -2, "name"); - duk_pop(ctx); - - return 0; -} - -} // !namespace - -void dukx_type_traits<std::system_error>::raise(duk_context* ctx, const std::system_error& ex) -{ - do_raise(ctx, ex); -} - -void dukx_type_traits<boost::system::system_error>::raise(duk_context* ctx, const boost::system::system_error& ex) -{ - do_raise(ctx, ex); -} - -std::string irccd_jsapi::get_name() const -{ - return "Irccd"; -} - -void irccd_jsapi::load(irccd& irccd, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - // irccd. - duk_push_object(plugin->get_context()); - - // Version. - duk_push_object(plugin->get_context()); - dukx_push(plugin->get_context(), IRCCD_VERSION_MAJOR); - duk_put_prop_string(plugin->get_context(), -2, "major"); - dukx_push(plugin->get_context(), IRCCD_VERSION_MINOR); - duk_put_prop_string(plugin->get_context(), -2, "minor"); - dukx_push(plugin->get_context(), IRCCD_VERSION_PATCH); - duk_put_prop_string(plugin->get_context(), -2, "patch"); - duk_put_prop_string(plugin->get_context(), -2, "version"); - - // Create the system_error that inherits from Error. - duk_push_c_function(plugin->get_context(), constructor, 2); - - // Put errno codes into the irccd.system_error object. - for (const auto& pair : errors) { - duk_push_int(plugin->get_context(), pair.second); - duk_put_prop_string(plugin->get_context(), -2, pair.first.c_str()); - } - - duk_push_object(plugin->get_context()); - duk_get_global_string(plugin->get_context(), "Error"); - duk_get_prop_string(plugin->get_context(), -1, "prototype"); - duk_remove(plugin->get_context(), -2); - duk_set_prototype(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "SystemError"); - - // Set irccd as global. - duk_put_global_string(plugin->get_context(), "Irccd"); - - // Store global instance. - duk_push_pointer(plugin->get_context(), &irccd); - duk_put_global_string(plugin->get_context(), "\xff""\xff""irccd-ref"); -} - -irccd& dukx_type_traits<irccd>::self(duk_context *ctx) -{ - dukx_stack_assert sa(ctx); - - duk_get_global_string(ctx, "\xff""\xff""irccd-ref"); - const auto ptr = static_cast<irccd*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return *ptr; -} - -} // !irccd
--- a/libirccd-js/irccd/js/irccd_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/* - * irccd_jsapi.hpp -- Irccd API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_IRCCD_JSAPI_HPP -#define IRCCD_JS_IRCCD_JSAPI_HPP - -/** - * \file irccd_jsapi.hpp - * \brief irccd Javascript API. - */ - -#include <cerrno> -#include <cstring> -#include <string> -#include <system_error> - -#include <boost/system/system_error.hpp> - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd Javascript API. - * \ingroup jsapi - */ -class irccd_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -/** - * \brief Specialize dukx_type_traits for irccd. - */ -template <> -class dukx_type_traits<irccd> : public std::true_type { -public: - /** - * Get irccd instance stored in this context. - * - * \param ctx the context - * \return the irccd reference - */ - static irccd& self(duk_context* ctx); -}; - -/** - * \brief Specialize dukx_type_traits for std::system_error. - */ -template <> -class dukx_type_traits<std::system_error> : public std::true_type { -public: - /** - * Raise an Irccd.SystemError. - * - * \param ctx the context - * param ex the exception - */ - static void raise(duk_context* ctx, const std::system_error& ex); -}; - -/** - * \brief Specialize dukx_type_traits for boost::system::system_error. - */ -template <> -class dukx_type_traits<boost::system::system_error> : public std::true_type { -public: - /** - * Raise an Irccd.SystemError. - * - * \param ctx the context - * param ex the exception - */ - static void raise(duk_context* ctx, const boost::system::system_error& ex); -}; - -} // !irccd - -#endif // !IRCCD_JS_IRCCD_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,60 @@ +/* + * js_api.cpp -- Javascript API module + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "directory_js_api.hpp" +#include "elapsed_timer_js_api.hpp" +#include "file_js_api.hpp" +#include "irccd_js_api.hpp" +#include "logger_js_api.hpp" +#include "plugin_js_api.hpp" +#include "server_js_api.hpp" +#include "system_js_api.hpp" +#include "timer_js_api.hpp" +#include "unicode_js_api.hpp" +#include "util_js_api.hpp" + +namespace irccd::js { + +namespace { + +template <typename T> +auto bind() noexcept -> js_api::factory +{ + return [] () noexcept { + return std::make_unique<T>(); + }; +} + +} // !namespace + +const std::vector<js_api::factory> js_api::registry{ + // Irccd API must be loaded first. + bind<irccd_js_api>(), + bind<directory_js_api>(), + bind<elapsed_timer_js_api>(), + bind<file_js_api>(), + bind<logger_js_api>(), + bind<plugin_js_api>(), + bind<server_js_api>(), + bind<system_js_api>(), + bind<timer_js_api>(), + bind<unicode_js_api>(), + bind<util_js_api>() +}; + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,92 @@ +/* + * js_api.hpp -- Javascript API module + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_JS_API_HPP +#define IRCCD_JS_JS_API_HPP + +/** + * \file js_api.hpp + * \brief Javascript API module. + */ + +/** + * \defgroup Javascript modules. + * \brief Modules for the Javascript API. + */ + +#include <functional> +#include <memory> +#include <string_view> +#include <vector> + +#include "duk.hpp" + +namespace irccd { + +class irccd; + +namespace js { + +class js_plugin; + +/** + * \brief Javascript API module. + */ +class js_api { +public: + /** + * \brief Command constructor factory. + */ + using factory = std::function<auto () -> std::unique_ptr<js_api>>; + + /** + * \brief Registry of all commands. + */ + static const std::vector<factory> registry; + + /** + * Default constructor. + */ + js_api() noexcept = default; + + /** + * Virtual destructor defaulted. + */ + virtual ~js_api() noexcept = default; + + /** + * Get the module name. + * + * \return the name + */ + virtual auto get_name() const noexcept -> std::string_view = 0; + + /** + * Load the module into the Javascript plugin. + * + * \param irccd the irccd instance + * \param plugin the plugin + */ + virtual void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) = 0; +}; + +} // !js + +} // !irccd + +#endif // !IRCCD_JS_JS_API_HPP
--- a/libirccd-js/irccd/js/js_plugin.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/libirccd-js/irccd/js/js_plugin.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -23,31 +23,20 @@ #include <stdexcept> #include <irccd/daemon/irccd.hpp> -#include <irccd/daemon/logger.hpp> -#include "directory_jsapi.hpp" -#include "duktape_vector.hpp" -#include "elapsed_timer_jsapi.hpp" -#include "file_jsapi.hpp" -#include "irccd_jsapi.hpp" +#include "js_api.hpp" #include "js_plugin.hpp" -#include "logger_jsapi.hpp" -#include "plugin_jsapi.hpp" -#include "server_jsapi.hpp" -#include "system_jsapi.hpp" -#include "timer_jsapi.hpp" -#include "unicode_jsapi.hpp" -#include "util_jsapi.hpp" +#include "server_js_api.hpp" -namespace irccd { +namespace irccd::js { namespace { -auto get_metadata(dukx_context& ctx, std::string_view name) -> std::string_view +auto get_metadata(duk::context& ctx, std::string_view name) -> std::string_view { std::string_view ret("unknown"); - dukx_stack_assert guard(ctx); + duk::stack_guard guard(ctx); duk_get_global_string(ctx, "info"); if (duk_get_type(ctx, -1) == DUK_TYPE_OBJECT) { @@ -64,11 +53,11 @@ return ret; } -auto get_table(dukx_context& ctx, std::string_view name) -> plugin::map +auto get_table(duk::context& ctx, std::string_view name) -> plugin::map { plugin::map result; - dukx_stack_assert sa(ctx); + duk::stack_guard sa(ctx); duk_get_global_string(ctx, name.data()); duk_enum(ctx, -1, 0); @@ -82,13 +71,13 @@ return result; } -void set_table(dukx_context& ctx, std::string_view name, const plugin::map& vars) +void set_table(duk::context& ctx, std::string_view name, const plugin::map& vars) { - dukx_stack_assert sa(ctx); + duk::stack_guard sa(ctx); duk_get_global_string(ctx, name.data()); for (const auto& pair : vars) { - dukx_push(ctx, pair.second); + duk::push(ctx, pair.second); duk_put_prop_string(ctx, -2, pair.first.c_str()); } @@ -104,14 +93,14 @@ template <typename Value, typename... Args> void js_plugin::push(Value&& value, Args&&... args) { - dukx_push(context_, std::forward<Value>(value)); + duk::push(context_, std::forward<Value>(value)); push(std::forward<Args>(args)...); } template <typename... Args> void js_plugin::call(const std::string& func, Args&&... args) { - dukx_stack_assert sa(context_); + duk::stack_guard sa(context_); duk_get_global_string(context_, func.c_str()); @@ -123,7 +112,7 @@ push(std::forward<Args>(args)...); if (duk_pcall(context_, sizeof... (Args)) != 0) - throw plugin_error(plugin_error::exec_error, get_name(), dukx_stack(context_, -1).stack()); + throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); duk_pop(context_); } @@ -132,7 +121,7 @@ : plugin(std::move(id)) , path_(path) { - dukx_stack_assert sa(context_); + duk::stack_guard sa(context_); /* * Create two special tables for configuration and formats, they are @@ -153,11 +142,11 @@ duk_push_pointer(context_, this); duk_put_global_string(context_, "\xff""\xff""plugin"); - dukx_push(context_, path); + duk::push(context_, path); duk_put_global_string(context_, "\xff""\xff""path"); } -auto js_plugin::get_context() noexcept -> dukx_context& +auto js_plugin::get_context() noexcept -> duk::context& { return context_; } @@ -230,7 +219,7 @@ ); if (duk_peval_string(context_, data.c_str())) - throw plugin_error(plugin_error::exec_error, get_name(), dukx_stack(context_, -1).stack()); + throw plugin_error(plugin_error::exec_error, get_name(), duk::get_stack(context_, -1).get_stack()); } void js_plugin::handle_command(irccd&, const message_event& event) @@ -357,19 +346,19 @@ return plugin; } -void dukx_type_traits<whois_info>::push(duk_context* ctx, const whois_info& whois) +void duk::type_traits<whois_info>::push(duk_context* ctx, const whois_info& whois) { duk_push_object(ctx); - dukx_push(ctx, whois.nick); + duk::push(ctx, whois.nick); duk_put_prop_string(ctx, -2, "nickname"); - dukx_push(ctx, whois.user); + duk::push(ctx, whois.user); duk_put_prop_string(ctx, -2, "username"); - dukx_push(ctx, whois.realname); + duk::push(ctx, whois.realname); duk_put_prop_string(ctx, -2, "realname"); - dukx_push(ctx, whois.host); + duk::push(ctx, whois.host); duk_put_prop_string(ctx, -2, "host"); - dukx_push(ctx, whois.channels); + duk::push(ctx, whois.channels); duk_put_prop_string(ctx, -2, "channels"); } -} // !irccd +} // !irccd::js
--- a/libirccd-js/irccd/js/js_plugin.hpp Mon Aug 06 21:27:00 2018 +0200 +++ b/libirccd-js/irccd/js/js_plugin.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -27,11 +27,11 @@ #include <irccd/daemon/plugin.hpp> #include <irccd/daemon/server.hpp> -#include "duktape.hpp" +#include "duk.hpp" -namespace irccd { +namespace irccd::js { -class jsapi; +class js_api; /** * \brief JavaScript plugins for irccd. @@ -56,14 +56,11 @@ private: // JavaScript context. - mutable dukx_context context_; + mutable duk::context context_; // Path to Javascript script file. std::string path_; - /* - * Helpers to call a Javascript function. - */ void push() noexcept; template <typename Value, typename... Args> @@ -86,7 +83,7 @@ * * \return the context */ - auto get_context() noexcept -> dukx_context&; + auto get_context() noexcept -> duk::context&; /** * Open the script file associated. @@ -244,7 +241,7 @@ */ class js_plugin_loader : public plugin_loader { public: - using modules = std::vector<std::unique_ptr<jsapi>>; + using modules = std::vector<std::unique_ptr<js_api>>; private: irccd& irccd_; @@ -283,12 +280,24 @@ auto open(std::string_view id, std::string_view path) -> std::shared_ptr<plugin>; }; +namespace duk { + +/** + * \brief Specialization for type_traits<whois_info> + */ template <> -class dukx_type_traits<whois_info> : public std::true_type { -public: +struct type_traits<whois_info> : public std::true_type { + /** + * Push a whois_info. + * + * \param ctx the Duktape context + * \param who the information + */ static void push(duk_context* ctx, const whois_info& who); }; -} // !irccd +} // !duk + +} // !irccd::js #endif // !IRCCD_PLUGIN_JS_HPP
--- a/libirccd-js/irccd/js/jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * jsapi.cpp -- Javascript API module - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "directory_jsapi.hpp" -#include "elapsed_timer_jsapi.hpp" -#include "file_jsapi.hpp" -#include "irccd_jsapi.hpp" -#include "logger_jsapi.hpp" -#include "plugin_jsapi.hpp" -#include "server_jsapi.hpp" -#include "system_jsapi.hpp" -#include "timer_jsapi.hpp" -#include "unicode_jsapi.hpp" -#include "util_jsapi.hpp" - -namespace irccd { - -namespace { - -template <typename T> -auto bind() noexcept -> jsapi::factory -{ - return [] () noexcept { - return std::make_unique<T>(); - }; -} - -} // !namespace - -const std::vector<jsapi::factory> jsapi::registry{ - // Irccd API must be loaded first. - bind<irccd_jsapi>(), - bind<directory_jsapi>(), - bind<elapsed_timer_jsapi>(), - bind<file_jsapi>(), - bind<logger_jsapi>(), - bind<plugin_jsapi>(), - bind<server_jsapi>(), - bind<system_jsapi>(), - bind<timer_jsapi>(), - bind<unicode_jsapi>(), - bind<util_jsapi>() -}; - -} // !irccd
--- a/libirccd-js/irccd/js/jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -/* - * jsapi.hpp -- Javascript API module - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_JSAPI_HPP -#define IRCCD_JS_JSAPI_HPP - -/** - * \file jsapi.hpp - * \brief Javascript API module. - */ - -/** - * \defgroup Javascript modules. - * \brief Modules for the Javascript API. - */ - -#include <functional> -#include <memory> -#include <string> -#include <vector> - -#include "duktape.hpp" - -namespace irccd { - -class irccd; -class js_plugin; - -/** - * \brief Javascript API module. - */ -class jsapi { -public: - /** - * \brief Command constructor factory. - */ - using factory = std::function<auto () -> std::unique_ptr<jsapi>>; - - /** - * \brief Registry of all commands. - */ - static const std::vector<factory> registry; - - /** - * Default constructor. - */ - jsapi() noexcept = default; - - /** - * Virtual destructor defaulted. - */ - virtual ~jsapi() noexcept = default; - - /** - * Get the module name. - * - * \return the name - */ - virtual std::string get_name() const = 0; - - /** - * Load the module into the Javascript plugin. - * - * \param irccd the irccd instance - * \param plugin the plugin - */ - virtual void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) = 0; -}; - -} // !irccd - -#endif // !IRCCD_JS_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/logger_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,147 @@ +/* + * logger_js_api.cpp -- Irccd.Logger API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/logger.hpp> +#include <irccd/daemon/plugin_service.hpp> + +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" +#include "logger_js_api.hpp" +#include "plugin_js_api.hpp" + +namespace irccd::js { + +namespace { + +// {{{ print + +auto print(duk_context* ctx, unsigned level) -> duk_ret_t +{ + assert(level <= 2); + + try { + auto& sink = duk::type_traits<irccd>::self(ctx).get_log(); + auto& self = duk::type_traits<js_plugin>::self(ctx); + + switch (level) { + case 0: + sink.debug<plugin>(self) << duk_require_string(ctx, 0) << std::endl; + break; + case 1: + sink.info<plugin>(self) << duk_require_string(ctx, 0) << std::endl; + break; + default: + sink.warning<plugin>(self) << duk_require_string(ctx, 0) << std::endl; + break; + } + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.Logger.info + +/* + * Function: Irccd.Logger.info(message) + * -------------------------------------------------------- + * + * Write a verbose message. + * + * Arguments: + * - message, the message. + * Throws: + * - Irccd.SystemError on errors + */ +auto Logger_info(duk_context* ctx) -> duk_ret_t +{ + return print(ctx, 1); +} + +// }}} + +// {{{ Irccd.Logger.warning + +/* + * Function: Irccd.Logger.warning(message) + * -------------------------------------------------------- + * + * Write a warning message. + * + * Arguments: + * - message, the warning. + * Throws: + * - Irccd.SystemError on errors + */ +auto Logger_warning(duk_context* ctx) -> duk_ret_t +{ + return print(ctx, 2); +} + +// }}} + +// {{{ Irccd.Logger.debug + +/* + * Function: Irccd.Logger.debug(message) + * -------------------------------------------------------- + * + * Write a debug message, only shown if irccd is compiled in debug. + * + * Arguments: + * - message, the message. + * Throws: + * - Irccd.SystemError on errors + */ +auto Logger_debug(duk_context* ctx) -> duk_ret_t +{ + return print(ctx, 0); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "info", Logger_info, 1 }, + { "warning", Logger_warning, 1 }, + { "debug", Logger_debug, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto logger_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Logger"; +} + +void logger_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "Logger"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/logger_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * logger_js_api.hpp -- Irccd.Logger API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_LOGGER_JS_API_HPP +#define IRCCD_JS_LOGGER_JS_API_HPP + +/** + * \file logger_js_api.hpp + * \brief Irccd.Logger Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief irccd.Logger Javascript API. + * \ingroup js_api + */ +class logger_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc Module::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_LOGGER_JS_API_HPP
--- a/libirccd-js/irccd/js/logger_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -/* - * logger_jsapi.cpp -- Irccd.Logger API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/irccd.hpp> -#include <irccd/daemon/logger.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" -#include "logger_jsapi.hpp" -#include "plugin_jsapi.hpp" - -namespace irccd { - -namespace { - -// {{{ print - -duk_ret_t print(duk_context* ctx, unsigned level) -{ - assert(level <= 2); - - try { - auto& sink = dukx_type_traits<irccd>::self(ctx).get_log(); - auto self = dukx_type_traits<js_plugin>::self(ctx); - - switch (level) { - case 0: - sink.debug<plugin>(*self) << duk_require_string(ctx, 0) << std::endl; - break; - case 1: - sink.info<plugin>(*self) << duk_require_string(ctx, 0) << std::endl; - break; - default: - sink.warning<plugin>(*self) << duk_require_string(ctx, 0) << std::endl; - break; - } - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.Logger.info - -/* - * Function: Irccd.Logger.info(message) - * -------------------------------------------------------- - * - * Write a verbose message. - * - * Arguments: - * - message, the message. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Logger_info(duk_context* ctx) -{ - return print(ctx, 1); -} - -// }}} - -// {{{ Irccd.Logger.warning - -/* - * Function: Irccd.Logger.warning(message) - * -------------------------------------------------------- - * - * Write a warning message. - * - * Arguments: - * - message, the warning. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Logger_warning(duk_context* ctx) -{ - return print(ctx, 2); -} - -// }}} - -// {{{ Irccd.Logger.debug - -/* - * Function: Irccd.Logger.debug(message) - * -------------------------------------------------------- - * - * Write a debug message, only shown if irccd is compiled in debug. - * - * Arguments: - * - message, the message. - * Throws: - * - Irccd.SystemError on errors - */ -duk_ret_t Logger_debug(duk_context* ctx) -{ - return print(ctx, 0); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "info", Logger_info, 1 }, - { "warning", Logger_warning, 1 }, - { "debug", Logger_debug, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string logger_jsapi::get_name() const -{ - return "Irccd.Logger"; -} - -void logger_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "Logger"); - duk_pop(plugin->get_context()); -} - -} // !irccd
--- a/libirccd-js/irccd/js/logger_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * logger_jsapi.hpp -- Irccd.Logger API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_LOGGER_JSAPI_HPP -#define IRCCD_JS_LOGGER_JSAPI_HPP - -/** - * \file logger_jsapi.hpp - * \brief Irccd.Logger Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief irccd.Logger Javascript API. - * \ingroup jsapi - */ -class logger_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc Module::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_LOGGER_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/plugin_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,458 @@ +/* + * plugin_js_api.cpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/plugin_service.hpp> + +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" +#include "plugin_js_api.hpp" + +namespace irccd::js { + +namespace { + +const std::string_view signature("\xff""\xff""Irccd.Plugin"); + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_idx_t +{ + try { + return handler(); + } catch (const plugin_error& ex) { + duk::raise(ctx, ex); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +/* + * set + * ------------------------------------------------------------------ + * + * This setter is used to replace the Irccd.Plugin.(config|format|paths) + * property when the plugin assign a new one. + * + * Because the plugin configuration always has higher priority, when a new + * object is assigned to 'config' or to the 'format' property, the plugin + * configuration is merged to the assigned one, adding or replacing any values. + * + * Example: + * + * Plugin 'xyz' does: + * + * Irccd.Plugin.config = { + * mode: "simple", + * level: "123" + * }; + * + * The user configuration is: + * + * [plugin.xyz] + * mode = "hard" + * path = "/var" + * + * The final user table looks like this: + * + * Irccd.Plugin.Config = { + * mode: "hard", + * level: "123", + * path: "/var" + * }; + */ +auto set(duk_context* ctx, std::string_view name) -> duk_ret_t +{ + if (!duk_is_object(ctx, 0)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name.data()); + + // Merge old table with new one. + duk_get_global_string(ctx, name.data()); + duk_enum(ctx, -1, 0); + + while (duk_next(ctx, -1, true)) + duk_put_prop(ctx, 0); + + // Pop enum and old table. + duk_pop_2(ctx); + + // Replace the old table with the new assigned one. + duk_put_global_string(ctx, name.data()); + + return 0; +} + +/* + * get + * ------------------------------------------------------------------ + * + * Get the Irccd.Plugin.(config|format|paths) property. + */ +auto get(duk_context* ctx, std::string_view name) -> duk_ret_t +{ + duk_get_global_string(ctx, name.data()); + + return 1; +} + +/* + * set_config + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.config property. + */ +auto set_config(duk_context* ctx) -> duk_ret_t +{ + return set(ctx, js_plugin::config_property); +} + +/* + * get_config + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.config property. + */ +auto get_config(duk_context* ctx) -> duk_ret_t +{ + return get(ctx, js_plugin::config_property); +} + +/* + * set_format + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.format property. + */ +auto set_format(duk_context* ctx) -> duk_ret_t +{ + return set(ctx, js_plugin::format_property); +} + +/* + * get_format + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.format property. + */ +auto get_format(duk_context* ctx) -> duk_ret_t +{ + return get(ctx, js_plugin::format_property); +} + +/* + * set_paths + * ------------------------------------------------------------------ + * + * Wrap setter for Irccd.Plugin.format property. + */ +auto set_paths(duk_context* ctx) -> duk_ret_t +{ + return set(ctx, js_plugin::paths_property); +} + +/* + * get_paths + * ------------------------------------------------------------------ + * + * Wrap getter for Irccd.Plugin.format property. + */ +auto get_paths(duk_context* ctx) -> duk_ret_t +{ + return get(ctx, js_plugin::paths_property); +} + +// {{{ Irccd.Plugin.info + +/* + * Function: Irccd.Plugin.info([name]) + * ------------------------------------------------------------------ + * + * Get information about a plugin. + * + * The returned object as the following properties: + * + * - name: (string) the plugin identifier, + * - author: (string) the author, + * - license: (string) the license, + * - summary: (string) a short description, + * - version: (string) the version + * + * Arguments: + * - name, the plugin identifier, if not specified the current plugin is + * selected. + * Returns: + * The plugin information or undefined if the plugin was not found. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Plugin_info(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + plugin* plugin; + + if (duk_get_top(ctx) >= 1) + plugin = duk::type_traits<irccd>::self(ctx).plugins().get(duk_require_string(ctx, 0)).get(); + else + plugin = std::addressof(duk::type_traits<js_plugin>::self(ctx)); + + if (!plugin) + return 0; + + duk_push_object(ctx); + duk::push(ctx, plugin->get_name()); + duk_put_prop_string(ctx, -2, "name"); + duk::push(ctx, plugin->get_author()); + duk_put_prop_string(ctx, -2, "author"); + duk::push(ctx, plugin->get_license()); + duk_put_prop_string(ctx, -2, "license"); + duk::push(ctx, plugin->get_summary()); + duk_put_prop_string(ctx, -2, "summary"); + duk::push(ctx, plugin->get_version()); + duk_put_prop_string(ctx, -2, "version"); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.Plugin.list + +/* + * Function: Irccd.Plugin.list() + * ------------------------------------------------------------------ + * + * Get the list of plugins, the array returned contains all plugin names. + * + * Returns: + * The list of all plugin names. + */ +auto Plugin_list(duk_context* ctx) -> duk_idx_t +{ + int i = 0; + + duk_push_array(ctx); + + for (const auto& plg : duk::type_traits<irccd>::self(ctx).plugins().all()) { + duk::push(ctx, plg->get_id()); + duk_put_prop_index(ctx, -2, i++); + } + + return 1; +} + +// }}} + +// {{{ Irccd.Plugin.load + +/* + * Function: Irccd.Plugin.load(name) + * ------------------------------------------------------------------ + * + * Load a plugin by name. This function will search through the standard + * directories. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. + */ +auto Plugin_load(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + duk::type_traits<irccd>::self(ctx).plugins().load( + duk::require<std::string_view>(ctx, 0), ""); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Plugin.reload + +/* + * Function: Irccd.Plugin.reload(name) + * ------------------------------------------------------------------ + * + * Reload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. + */ +auto Plugin_reload(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + duk::type_traits<irccd>::self(ctx).plugins().reload(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Plugin.unload + +/* + * Function: Irccd.Plugin.unload(name) + * ------------------------------------------------------------------ + * + * Unload a plugin by name. + * + * Arguments: + * - name, the plugin identifier. + * Throws: + * - Irccd.PluginError on plugin related errors, + * - Irccd.SystemError on other errors. + */ +auto Plugin_unload(duk_context* ctx) -> duk_idx_t +{ + return wrap(ctx, [&] { + duk::type_traits<irccd>::self(ctx).plugins().unload(duk::require<std::string>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.PluginError [constructor] + +/* + * Function: Irccd.PluginError(code, message) + * ------------------------------------------------------------------ + * + * Create an Irccd.PluginError object. + * + * Arguments: + * - code, the error code, + * - message, the error message. + */ +auto PluginError_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "code"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "PluginError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + +const duk_function_list_entry functions[] = { + { "info", Plugin_info, DUK_VARARGS }, + { "list", Plugin_list, 0 }, + { "load", Plugin_load, 1 }, + { "reload", Plugin_reload, 1 }, + { "unload", Plugin_unload, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto plugin_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Plugin"; +} + +void plugin_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + // Store plugin. + duk_push_pointer(plugin->get_context(), plugin.get()); + duk_put_global_string(plugin->get_context(), signature.data()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + + // 'config' property. + duk_push_string(plugin->get_context(), "config"); + duk_push_c_function(plugin->get_context(), get_config, 0); + duk_push_c_function(plugin->get_context(), set_config, 1); + duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // 'format' property. + duk_push_string(plugin->get_context(), "format"); + duk_push_c_function(plugin->get_context(), get_format, 0); + duk_push_c_function(plugin->get_context(), set_format, 1); + duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // 'format' property. + duk_push_string(plugin->get_context(), "paths"); + duk_push_c_function(plugin->get_context(), get_paths, 0); + duk_push_c_function(plugin->get_context(), set_paths, 1); + duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); + + // PluginError function. + duk_push_c_function(plugin->get_context(), PluginError_constructor, 2); + duk_push_object(plugin->get_context()); + duk_get_global_string(plugin->get_context(), "Error"); + duk_get_prop_string(plugin->get_context(), -1, "prototype"); + duk_remove(plugin->get_context(), -2); + duk_set_prototype(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "PluginError"); + + duk_put_prop_string(plugin->get_context(), -2, "Plugin"); + duk_pop(plugin->get_context()); +} + +namespace duk { + +auto type_traits<js_plugin>::self(duk_context* ctx) -> js_plugin& +{ + duk::stack_guard sa(ctx); + + duk_get_global_string(ctx, signature.data()); + auto plugin = static_cast<js_plugin*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return *plugin; +} + +void type_traits<plugin_error>::raise(duk_context* ctx, const plugin_error& ex) +{ + duk::stack_guard sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "PluginError"); + duk_remove(ctx, -2); + duk::push(ctx, ex.code().value()); + duk::push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + +} // !duk + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/plugin_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,83 @@ +/* + * plugin_js_api.hpp -- Irccd.Plugin API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_PLUGIN_JS_API_HPP +#define IRCCD_JS_PLUGIN_JS_API_HPP + +/** + * \file plugin_js_api.hpp + * \brief Irccd.Plugin Javascript API. + */ + +#include "js_api.hpp" +#include "js_plugin.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.Plugin Javascript API. + * \ingroup js_api + */ +class plugin_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc Module::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialize dukx_type_traits for plugin. + */ +template <> +struct type_traits<js_plugin> { + /** + * Access the plugin stored in this context. + * + * \param ctx the context + * \return the plugin + */ + static auto self(duk_context* ctx) -> js_plugin&; +}; + +/** + * \brief Specialization for plugin_error. + */ +template <> +struct type_traits<plugin_error> { + /** + * Raise a plugin_error. + * + * \param ctx the context + * \param error the error + */ + static void raise(duk_context* ctx, const plugin_error& error); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_PLUGIN_JS_API_HPP
--- a/libirccd-js/irccd/js/plugin_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,466 +0,0 @@ -/* - * plugin_jsapi.cpp -- Irccd.Plugin API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/irccd.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" -#include "plugin_jsapi.hpp" - -namespace irccd { - -namespace { - -const char plugin_ref[] = "\xff""\xff""irccd-plugin-ptr"; - -template <typename Handler> -duk_idx_t wrap(duk_context* ctx, Handler handler) -{ - try { - return handler(); - } catch (const plugin_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -/* - * set - * ------------------------------------------------------------------ - * - * This setter is used to replace the Irccd.Plugin.(config|format|paths) - * property when the plugin assign a new one. - * - * Because the plugin configuration always has higher priority, when a new - * object is assigned to 'config' or to the 'format' property, the plugin - * configuration is merged to the assigned one, adding or replacing any values. - * - * Example: - * - * Plugin 'xyz' does: - * - * Irccd.Plugin.config = { - * mode: "simple", - * level: "123" - * }; - * - * The user configuration is: - * - * [plugin.xyz] - * mode = "hard" - * path = "/var" - * - * The final user table looks like this: - * - * Irccd.Plugin.Config = { - * mode: "hard", - * level: "123", - * path: "/var" - * }; - */ -duk_ret_t set(duk_context* ctx, std::string_view name) -{ - if (!duk_is_object(ctx, 0)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "'%s' property must be object", name.data()); - - // Merge old table with new one. - duk_get_global_string(ctx, name.data()); - duk_enum(ctx, -1, 0); - - while (duk_next(ctx, -1, true)) - duk_put_prop(ctx, 0); - - // Pop enum and old table. - duk_pop_2(ctx); - - // Replace the old table with the new assigned one. - duk_put_global_string(ctx, name.data()); - - return 0; -} - -/* - * get - * ------------------------------------------------------------------ - * - * Get the Irccd.Plugin.(config|format|paths) property. - */ -duk_ret_t get(duk_context* ctx, std::string_view name) -{ - duk_get_global_string(ctx, name.data()); - - return 1; -} - -/* - * set_config - * ------------------------------------------------------------------ - * - * Wrap setter for Irccd.Plugin.config property. - */ -duk_ret_t set_config(duk_context* ctx) -{ - return set(ctx, js_plugin::config_property); -} - -/* - * get_config - * ------------------------------------------------------------------ - * - * Wrap getter for Irccd.Plugin.config property. - */ -duk_ret_t get_config(duk_context* ctx) -{ - return get(ctx, js_plugin::config_property); -} - -/* - * set_format - * ------------------------------------------------------------------ - * - * Wrap setter for Irccd.Plugin.format property. - */ -duk_ret_t set_format(duk_context* ctx) -{ - return set(ctx, js_plugin::format_property); -} - -/* - * get_format - * ------------------------------------------------------------------ - * - * Wrap getter for Irccd.Plugin.format property. - */ -duk_ret_t get_format(duk_context* ctx) -{ - return get(ctx, js_plugin::format_property); -} - -/* - * set_paths - * ------------------------------------------------------------------ - * - * Wrap setter for Irccd.Plugin.format property. - */ -duk_ret_t set_paths(duk_context* ctx) -{ - return set(ctx, js_plugin::paths_property); -} - -/* - * get_paths - * ------------------------------------------------------------------ - * - * Wrap getter for Irccd.Plugin.format property. - */ -duk_ret_t get_paths(duk_context* ctx) -{ - return get(ctx, js_plugin::paths_property); -} - -// {{{ Irccd.Plugin.info - -/* - * Function: Irccd.Plugin.info([name]) - * ------------------------------------------------------------------ - * - * Get information about a plugin. - * - * The returned object as the following properties: - * - * - name: (string) the plugin identifier, - * - author: (string) the author, - * - license: (string) the license, - * - summary: (string) a short description, - * - version: (string) the version - * - * Arguments: - * - name, the plugin identifier, if not specified the current plugin is - * selected. - * Returns: - * The plugin information or undefined if the plugin was not found. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_idx_t Plugin_info(duk_context* ctx) -{ - return wrap(ctx, [&] { - std::shared_ptr<plugin> plugin; - - if (duk_get_top(ctx) >= 1) - plugin = dukx_type_traits<irccd>::self(ctx).plugins().get(duk_require_string(ctx, 0)); - else - plugin = dukx_type_traits<js_plugin>::self(ctx); - - if (!plugin) - return 0; - - duk_push_object(ctx); - dukx_push(ctx, plugin->get_name()); - duk_put_prop_string(ctx, -2, "name"); - dukx_push(ctx, plugin->get_author()); - duk_put_prop_string(ctx, -2, "author"); - dukx_push(ctx, plugin->get_license()); - duk_put_prop_string(ctx, -2, "license"); - dukx_push(ctx, plugin->get_summary()); - duk_put_prop_string(ctx, -2, "summary"); - dukx_push(ctx, plugin->get_version()); - duk_put_prop_string(ctx, -2, "version"); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.Plugin.list - -/* - * Function: Irccd.Plugin.list() - * ------------------------------------------------------------------ - * - * Get the list of plugins, the array returned contains all plugin names. - * - * Returns: - * The list of all plugin names. - */ -duk_idx_t Plugin_list(duk_context* ctx) -{ - int i = 0; - - duk_push_array(ctx); - - for (const auto& plg : dukx_type_traits<irccd>::self(ctx).plugins().all()) { - dukx_push(ctx, plg->get_id()); - duk_put_prop_index(ctx, -2, i++); - } - - return 1; -} - -// }}} - -// {{{ Irccd.Plugin.load - -/* - * Function: Irccd.Plugin.load(name) - * ------------------------------------------------------------------ - * - * Load a plugin by name. This function will search through the standard - * directories. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Irccd.PluginError on plugin related errors, - * - Irccd.SystemError on other errors. - */ -duk_idx_t Plugin_load(duk_context* ctx) -{ - return wrap(ctx, [&] { - dukx_type_traits<irccd>::self(ctx).plugins().load( - dukx_require<std::string_view>(ctx, 0), ""); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Plugin.reload - -/* - * Function: Irccd.Plugin.reload(name) - * ------------------------------------------------------------------ - * - * Reload a plugin by name. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Irccd.PluginError on plugin related errors, - * - Irccd.SystemError on other errors. - */ -duk_idx_t Plugin_reload(duk_context* ctx) -{ - return wrap(ctx, [&] { - dukx_type_traits<irccd>::self(ctx).plugins().reload(dukx_require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Plugin.unload - -/* - * Function: Irccd.Plugin.unload(name) - * ------------------------------------------------------------------ - * - * Unload a plugin by name. - * - * Arguments: - * - name, the plugin identifier. - * Throws: - * - Irccd.PluginError on plugin related errors, - * - Irccd.SystemError on other errors. - */ -duk_idx_t Plugin_unload(duk_context* ctx) -{ - return wrap(ctx, [&] { - dukx_type_traits<irccd>::self(ctx).plugins().unload(dukx_require<std::string>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.PluginError [constructor] - -/* - * Function: Irccd.PluginError(code, message) - * ------------------------------------------------------------------ - * - * Create an Irccd.PluginError object. - * - * Arguments: - * - code, the error code, - * - message, the error message. - */ -duk_ret_t PluginError_constructor(duk_context* ctx) -{ - duk_push_this(ctx); - duk_push_int(ctx, duk_require_int(ctx, 0)); - duk_put_prop_string(ctx, -2, "code"); - duk_push_string(ctx, duk_require_string(ctx, 1)); - duk_put_prop_string(ctx, -2, "message"); - duk_push_string(ctx, "PluginError"); - duk_put_prop_string(ctx, -2, "name"); - duk_pop(ctx); - - return 0; -} - -// }}} - -const duk_function_list_entry functions[] = { - { "info", Plugin_info, DUK_VARARGS }, - { "list", Plugin_list, 0 }, - { "load", Plugin_load, 1 }, - { "reload", Plugin_reload, 1 }, - { "unload", Plugin_unload, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string plugin_jsapi::get_name() const -{ - return "Irccd.Plugin"; -} - -void plugin_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_push_pointer(plugin->get_context(), new std::weak_ptr<js_plugin>(plugin)); - duk_push_object(plugin->get_context()); - duk_push_c_function(plugin->get_context(), [] (auto ctx) -> duk_ret_t { - duk_get_global_string(ctx, plugin_ref); - delete static_cast<std::shared_ptr<js_plugin>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_push_null(ctx); - duk_put_global_string(ctx, plugin_ref); - return 0; - }, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_put_global_string(plugin->get_context(), "\xff""\xff""dummy-shared-ptr"); - duk_put_global_string(plugin->get_context(), plugin_ref); - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - - // 'config' property. - duk_push_string(plugin->get_context(), "config"); - duk_push_c_function(plugin->get_context(), get_config, 0); - duk_push_c_function(plugin->get_context(), set_config, 1); - duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - - // 'format' property. - duk_push_string(plugin->get_context(), "format"); - duk_push_c_function(plugin->get_context(), get_format, 0); - duk_push_c_function(plugin->get_context(), set_format, 1); - duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - - // 'format' property. - duk_push_string(plugin->get_context(), "paths"); - duk_push_c_function(plugin->get_context(), get_paths, 0); - duk_push_c_function(plugin->get_context(), set_paths, 1); - duk_def_prop(plugin->get_context(), -4, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); - - // PluginError function. - duk_push_c_function(plugin->get_context(), PluginError_constructor, 2); - duk_push_object(plugin->get_context()); - duk_get_global_string(plugin->get_context(), "Error"); - duk_get_prop_string(plugin->get_context(), -1, "prototype"); - duk_remove(plugin->get_context(), -2); - duk_set_prototype(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "PluginError"); - - duk_put_prop_string(plugin->get_context(), -2, "Plugin"); - duk_pop(plugin->get_context()); -} - -using plugin_traits = dukx_type_traits<js_plugin>; -using plugin_error_traits = dukx_type_traits<plugin_error>; - -std::shared_ptr<js_plugin> dukx_type_traits<js_plugin>::self(duk_context* ctx) -{ - dukx_stack_assert sa(ctx); - - duk_get_global_string(ctx, plugin_ref); - auto plugin = static_cast<std::weak_ptr<js_plugin>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return plugin->lock(); -} - -void plugin_error_traits::raise(duk_context* ctx, const plugin_error& ex) -{ - dukx_stack_assert sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "PluginError"); - duk_remove(ctx, -2); - dukx_push(ctx, ex.code().value()); - dukx_push(ctx, ex.code().message()); - duk_new(ctx, 2); - - (void)duk_throw(ctx); -} - -} // !irccd
--- a/libirccd-js/irccd/js/plugin_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * plugin_jsapi.hpp -- Irccd.Plugin API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_PLUGIN_JSAPI_HPP -#define IRCCD_JS_PLUGIN_JSAPI_HPP - -/** - * \file plugin_jsapi.hpp - * \brief Irccd.Plugin Javascript API. - */ - -#include "jsapi.hpp" -#include "js_plugin.hpp" - -namespace irccd { - -/** - * \brief Irccd.Plugin Javascript API. - * \ingroup jsapi - */ -class plugin_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc Module::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -/** - * \brief Specialize dukx_type_traits for plugin. - */ -template <> -class dukx_type_traits<js_plugin> : public std::true_type { -public: - /** - * Access the plugin stored in this context. - * - * \param ctx the context - * \return the plugin - */ - static std::shared_ptr<js_plugin> self(duk_context* ctx); -}; - -/** - * \brief Specialization for plugin_error. - */ -template <> -class dukx_type_traits<plugin_error> : public std::true_type { -public: - /** - * Raise a plugin_error. - * - * \param ctx the context - * \param error the error - */ - static void raise(duk_context* ctx, const plugin_error& error); -}; - -} // !irccd - -#endif // !IRCCD_JS_PLUGIN_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/server_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,898 @@ +/* + * server_js_api.cpp -- Irccd.Server API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <sstream> +#include <unordered_map> + +#include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/server_service.hpp> +#include <irccd/daemon/server_util.hpp> + +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" +#include "server_js_api.hpp" + +namespace irccd::js { + +namespace { + +const std::string_view signature("\xff""\xff""Irccd.Server"); +const std::string_view prototype("\xff""\xff""Irccd.Server.prototype"); + +auto self(duk_context* ctx) -> std::shared_ptr<server> +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + auto ptr = duk_to_pointer(ctx, -1); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + return *static_cast<std::shared_ptr<server>*>(ptr); +} + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(ctx); + } catch (const server_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// {{{ Irccd.Server.prototype.info + +/* + * Method: Irccd.Server.prototype.info() + * ------------------------------------------------------------------ + * + * Get the server information as an object containing the following properties: + * + * name: the server unique name + * host: the host name + * port: the port number + * ssl: true if using ssl + * sslVerify: true if ssl was verified + * channels: an array of all channels + */ +auto Server_prototype_info(duk_context* ctx) -> duk_ret_t +{ + const auto server = self(ctx); + const auto& channels = server->get_channels(); + + duk_push_object(ctx); + duk::push(ctx, server->get_id()); + duk_put_prop_string(ctx, -2, "name"); + duk::push(ctx, server->get_host()); + duk_put_prop_string(ctx, -2, "host"); + duk_push_int(ctx, server->get_port()); + duk_put_prop_string(ctx, -2, "port"); + duk_push_boolean(ctx, + (server->get_options() & server::options::ssl) == server::options::ssl); + duk_put_prop_string(ctx, -2, "ssl"); + duk_push_boolean(ctx, + (server->get_options() & server::options::ssl_verify) == server::options::ssl_verify); + duk_put_prop_string(ctx, -2, "sslVerify"); + duk::push(ctx, server->get_command_char()); + duk_put_prop_string(ctx, -2, "commandChar"); + duk::push(ctx, server->get_realname()); + duk_put_prop_string(ctx, -2, "realname"); + duk::push(ctx, server->get_nickname()); + duk_put_prop_string(ctx, -2, "nickname"); + duk::push(ctx, server->get_username()); + duk_put_prop_string(ctx, -2, "username"); + duk::push(ctx, std::vector<std::string>(channels.begin(), channels.end())); + duk_put_prop_string(ctx, -2, "channels"); + + return 1; +} + +// }}} + +// {{{ Irccd.Server.prototype.invite + +/* + * Method: Irccd.Server.prototype.invite(target, channel) + * ------------------------------------------------------------------ + * + * Invite someone to a channel. + * + * Arguments: + * - target, the target to invite, + * - channel, the channel. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_invite(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto channel = duk::require<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->invite(std::move(target), std::move(channel)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.isSelf + +/* + * Method: Irccd.Server.prototype.isSelf(nickname) + * ------------------------------------------------------------------ + * + * Arguments: + * - nickname, the nickname to check. + * Returns: + * True if the nickname targets this server. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_prototype_isSelf(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + return duk::push(ctx, self(ctx)->is_self(duk::require<std::string>(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.join + +/* + * Method: Irccd.Server.prototype.join(channel, password = undefined) + * ------------------------------------------------------------------ + * + * Join a channel with an optional password. + * + * Arguments: + * - channel, the channel to join, + * - password, the password or undefined to not use. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_join(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto password = duk::get<std::string>(ctx, 1); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->join(std::move(channel), std::move(password)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.kick + +/* + * Method: Irccd.Server.prototype.kick(target, channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Kick someone from a channel. + * + * Arguments: + * - target, the target to kick, + * - channel, the channel, + * - reason, the optional reason or undefined to not set. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_kick(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto channel = duk::require<std::string>(ctx, 1); + auto reason = duk::get<std::string>(ctx, 2); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->kick(std::move(target), std::move(channel), std::move(reason)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.me + +/* + * Method: Irccd.Server.prototype.me(target, message) + * ------------------------------------------------------------------ + * + * Send a CTCP Action. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_me(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto message = duk::get<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->me(std::move(target), std::move(message)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.message + +/* + * Method: Irccd.Server.prototype.message(target, message) + * ------------------------------------------------------------------ + * + * Send a message. + * + * Arguments: + * - target, the target or a channel, + * - message, the message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_message(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto message = duk::get<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->message(std::move(target), std::move(message)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.mode + +/* + * Method: Irccd.Server.prototype.mode(channel, mode, limit, user, mask) + * ------------------------------------------------------------------ + * + * Change your mode. + * + * Arguments: + * - mode, the new mode. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_mode(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto mode = duk::require<std::string>(ctx, 1); + auto limit = duk::get<std::string>(ctx, 2); + auto user = duk::get<std::string>(ctx, 3); + auto mask = duk::get<std::string>(ctx, 4); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + if (mode.empty()) + throw server_error(server_error::invalid_mode); + + self(ctx)->mode( + std::move(channel), + std::move(mode), + std::move(limit), + std::move(user), + std::move(mask) + ); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.names + +/* + * Method: Irccd.Server.prototype.names(channel) + * ------------------------------------------------------------------ + * + * Get the list of names from a channel. + * + * Arguments: + * - channel, the channel. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_names(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->names(std::move(channel)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.nick + +/* + * Method: Irccd.Server.prototype.nick(nickname) + * ------------------------------------------------------------------ + * + * Change the nickname. + * + * Arguments: + * - nickname, the nickname. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_nick(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto nickname = duk::require<std::string>(ctx, 0); + + if (nickname.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->set_nickname(std::move(nickname)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.notice + +/* + * Method: Irccd.Server.prototype.notice(target, message) + * ------------------------------------------------------------------ + * + * Send a private notice. + * + * Arguments: + * - target, the target, + * - message, the notice message. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_notice(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + auto message = duk::get<std::string>(ctx, 1); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->notice(std::move(target), std::move(message)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.part + +/* + * Method: Irccd.Server.prototype.part(channel, reason = undefined) + * ------------------------------------------------------------------ + * + * Leave a channel. + * + * Arguments: + * - channel, the channel to leave, + * - reason, the optional reason, keep undefined for portability. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_part(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto reason = duk::get<std::string>(ctx, 1); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->part(std::move(channel), std::move(reason)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.send + +/* + * Method: Irccd.Server.prototype.send(raw) + * ------------------------------------------------------------------ + * + * Send a raw message to the IRC server. + * + * Arguments: + * - raw, the raw message (without terminators). + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_send(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto raw = duk::require<std::string>(ctx, 0); + + if (raw.empty()) + throw server_error(server_error::invalid_message); + + self(ctx)->send(std::move(raw)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.topic + +/* + * Method: Server.prototype.topic(channel, topic) + * ------------------------------------------------------------------ + * + * Change a channel topic. + * + * Arguments: + * - channel, the channel, + * - topic, the new topic. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_topic(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto channel = duk::require<std::string>(ctx, 0); + auto topic = duk::get<std::string>(ctx, 1); + + if (channel.empty()) + throw server_error(server_error::invalid_channel); + + self(ctx)->topic(std::move(channel), std::move(topic)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.whois + +/* + * Method: Irccd.Server.prototype.whois(target) + * ------------------------------------------------------------------ + * + * Get whois information. + * + * Arguments: + * - target, the target. + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_prototype_whois(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto target = duk::require<std::string>(ctx, 0); + + if (target.empty()) + throw server_error(server_error::invalid_nickname); + + self(ctx)->whois(std::move(target)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.prototype.toString + +/* + * Method: Irccd.Server.prototype.toString() + * ------------------------------------------------------------------ + * + * Convert the object to std::string, convenience for adding the object + * as property key. + * + * Returns: + * The server name (unique). + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_prototype_toString(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + duk::push(ctx, self(ctx)->get_id()); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.Server [constructor] + +/* + * Function: Irccd.Server(params) [constructor] + * ------------------------------------------------------------------ + * + * Construct a new server. + * + * Params must be filled with the following properties: + * + * name: the name, + * host: the host, + * ipv6: true to use ipv6, (Optional: default false) + * port: the port number, (Optional: default 6667) + * password: the password, (Optional: default none) + * channels: array of channels (Optiona: default empty) + * ssl: true to use ssl, (Optional: default false) + * sslVerify: true to verify (Optional: default true) + * nickname: "nickname", (Optional, default: irccd) + * username: "user name", (Optional, default: irccd) + * realname: "real name", (Optional, default: IRC Client Daemon) + * commandChar: "!", (Optional, the command char, default: "!") + * + * Arguments: + * - params, the server properties + * Throws: + * - Irccd.ServerError on server related errors, + * - Irccd.SystemError on other errors. + */ +auto Server_constructor(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + if (!duk_is_constructor_call(ctx)) + return 0; + + duk_check_type(ctx, 0, DUK_TYPE_OBJECT); + + auto json = nlohmann::json::parse(duk_json_encode(ctx, 0)); + auto s = server_util::from_json(duk::type_traits<irccd>::self(ctx).get_service(), json); + + duk_push_this(ctx); + duk_push_pointer(ctx, new std::shared_ptr<server>(std::move(s))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_pop(ctx); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server [destructor] + +/* + * Function: Irccd.Server() [destructor] + * ------------------------------------------------------------------ + * + * Delete the property. + */ +auto Server_destructor(duk_context* ctx) -> duk_ret_t +{ + duk_get_prop_string(ctx, 0, signature.data()); + delete static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + duk_del_prop_string(ctx, 0, signature.data()); + + return 0; +} + +// }}} + +// {{{ Irccd.Server.add + +/* + * Function: Irccd.Server.add(s) + * ------------------------------------------------------------------ + * + * Register a new server to the irccd instance. + * + * Arguments: + * - s, the server to add. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_add(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + duk::type_traits<irccd>::self(ctx).servers().add( + duk::require<std::shared_ptr<server>>(ctx, 0)); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.Server.find + +/* + * Function: Irccd.Server.find(name) + * ------------------------------------------------------------------ + * + * Find a server by name. + * + * Arguments: + * - name, the server name + * Returns: + * The server object or undefined if not found. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Server_find(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [] (auto ctx) { + auto id = duk::require<std::string>(ctx, 0); + auto server = duk::type_traits<irccd>::self(ctx).servers().get(id); + + if (!server) + return 0; + + duk::push(ctx, server); + + return 1; + }); +} + +// }}} + +// {{{ Irccd.Server.list + +/* + * Function: Irccd.Server.list() + * ------------------------------------------------------------------ + * + * Get the map of all loaded servers. + * + * Returns: + * An object with string-to-servers pairs. + */ +auto Server_list(duk_context* ctx) -> duk_ret_t +{ + duk_push_object(ctx); + + for (const auto& server : duk::type_traits<irccd>::self(ctx).servers().all()) { + duk::push(ctx, server); + duk_put_prop_string(ctx, -2, server->get_id().c_str()); + } + + return 1; +} + +// }}} + +// {{{ Irccd.Server.remove + +/* + * Function: Irccd.Server.remove(name) + * ------------------------------------------------------------------ + * + * Remove a server from the irccd instance. You can pass the server object since + * it's coercible to a string. + * + * Arguments: + * - name the server name. + */ +auto Server_remove(duk_context* ctx) -> duk_ret_t +{ + duk::type_traits<irccd>::self(ctx).servers().remove(duk_require_string(ctx, 0)); + + return 0; +} + +// }}} + +// {{{ Irccd.ServerError + +/* + * Function: Irccd.ServerError(code, message) + * ------------------------------------------------------------------ + * + * Create an Irccd.ServerError object. + * + * Arguments: + * - code, the error code, + * - message, the error message. + */ +auto ServerError_constructor(duk_context* ctx) -> duk_ret_t +{ + duk_push_this(ctx); + duk_push_int(ctx, duk_require_int(ctx, 0)); + duk_put_prop_string(ctx, -2, "code"); + duk_push_string(ctx, duk_require_string(ctx, 1)); + duk_put_prop_string(ctx, -2, "message"); + duk_push_string(ctx, "ServerError"); + duk_put_prop_string(ctx, -2, "name"); + duk_pop(ctx); + + return 0; +} + +// }}} + +const duk_function_list_entry methods[] = { + { "info", Server_prototype_info, 0 }, + { "invite", Server_prototype_invite, 2 }, + { "isSelf", Server_prototype_isSelf, 1 }, + { "join", Server_prototype_join, DUK_VARARGS }, + { "kick", Server_prototype_kick, DUK_VARARGS }, + { "me", Server_prototype_me, 2 }, + { "message", Server_prototype_message, 2 }, + { "mode", Server_prototype_mode, 1 }, + { "names", Server_prototype_names, 1 }, + { "nick", Server_prototype_nick, 1 }, + { "notice", Server_prototype_notice, 2 }, + { "part", Server_prototype_part, DUK_VARARGS }, + { "send", Server_prototype_send, 1 }, + { "topic", Server_prototype_topic, 2 }, + { "toString", Server_prototype_toString, 0 }, + { "whois", Server_prototype_whois, 1 }, + { nullptr, nullptr, 0 } +}; + +const duk_function_list_entry functions[] = { + { "add", Server_add, 1 }, + { "find", Server_find, 1 }, + { "list", Server_list, 0 }, + { "remove", Server_remove, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto server_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Server"; +} + +void server_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + + // ServerError function. + duk_push_c_function(plugin->get_context(), ServerError_constructor, 2); + duk_push_object(plugin->get_context()); + duk_get_global_string(plugin->get_context(), "Error"); + duk_get_prop_string(plugin->get_context(), -1, "prototype"); + duk_remove(plugin->get_context(), -2); + duk_set_prototype(plugin->get_context(), -2); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "ServerError"); + + // Server constructor. + duk_push_c_function(plugin->get_context(), Server_constructor, 1); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_push_c_function(plugin->get_context(), Server_destructor, 1); + duk_set_finalizer(plugin->get_context(), -2); + duk_dup_top(plugin->get_context()); + duk_put_global_string(plugin->get_context(), prototype.data()); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "Server"); + duk_pop(plugin->get_context()); +} + +namespace duk { + +void type_traits<std::shared_ptr<server>>::push(duk_context* ctx, std::shared_ptr<server> server) +{ + assert(ctx); + assert(server); + + duk::stack_guard sa(ctx, 1); + + duk_push_object(ctx); + duk_push_pointer(ctx, new std::shared_ptr<class server>(std::move(server))); + duk_put_prop_string(ctx, -2, signature.data()); + duk_get_global_string(ctx, prototype.data()); + duk_set_prototype(ctx, -2); +} + +auto type_traits<std::shared_ptr<server>>::require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<server> +{ + if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature.data())) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); + + duk_get_prop_string(ctx, index, signature.data()); + auto file = *static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + return file; +} + +void type_traits<server_error>::raise(duk_context* ctx, const server_error& ex) +{ + duk::stack_guard sa(ctx, 1); + + duk_get_global_string(ctx, "Irccd"); + duk_get_prop_string(ctx, -1, "ServerError"); + duk_remove(ctx, -2); + duk::push(ctx, ex.code().value()); + duk::push(ctx, ex.code().message()); + duk_new(ctx, 2); + + (void)duk_throw(ctx); +} + +} // !duk + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/server_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,96 @@ +/* + * server_js_api.hpp -- Irccd.Server API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_SERVER_JS_API_HPP +#define IRCCD_JS_SERVER_JS_API_HPP + +/** + * \file mod-server.hpp + * \brief irccd.Server Javascript API. + */ + +#include <irccd/daemon/server.hpp> + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief irccd.Server Javascript API. + * \ingroup js_api + */ +class server_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +namespace duk { + +/** + * \brief Specialization for servers as shared_ptr. + * + * Supports push, require. + */ +template <> +struct type_traits<std::shared_ptr<server>> { + /** + * Push a server. + * + * \pre server != nullptr + * \param ctx the context + * \param server the server + */ + static void push(duk_context* ctx, std::shared_ptr<server> server); + + /** + * Require a server. Raise a Javascript error if not a Server. + * + * \param ctx the context + * \param index the index + * \return the server + */ + static auto require(duk_context* ctx, duk_idx_t index) -> std::shared_ptr<server>; +}; + +/** + * \brief Specialization for server_error. + */ +template <> +struct type_traits<server_error> { + /** + * Raise a server_error. + * + * \param ctx the context + * \param error the error + */ + static void raise(duk_context* ctx, const server_error& error); +}; + +} // !duk + +} // !irccd::js + +#endif // !IRCCD_JS_SERVER_JS_API_HPP
--- a/libirccd-js/irccd/js/server_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,898 +0,0 @@ -/* - * server_jsapi.cpp -- Irccd.Server API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <cassert> -#include <sstream> -#include <unordered_map> - -#include <irccd/daemon/irccd.hpp> -#include <irccd/daemon/server_service.hpp> -#include <irccd/daemon/server_util.hpp> - -#include "duktape_vector.hpp" -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" -#include "server_jsapi.hpp" - -namespace irccd { - -namespace { - -const char *signature("\xff""\xff""irccd-server-ptr"); -const char *prototype("\xff""\xff""irccd-server-prototype"); - -std::shared_ptr<server> self(duk_context* ctx) -{ - dukx_stack_assert sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature); - auto ptr = duk_to_pointer(ctx, -1); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); - - return *static_cast<std::shared_ptr<server>*>(ptr); -} - -template <typename Handler> -duk_ret_t wrap(duk_context* ctx, Handler handler) -{ - try { - return handler(ctx); - } catch (const server_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -// {{{ Irccd.Server.prototype.info - -/* - * Method: Irccd.Server.prototype.info() - * ------------------------------------------------------------------ - * - * Get the server information as an object containing the following properties: - * - * name: the server unique name - * host: the host name - * port: the port number - * ssl: true if using ssl - * sslVerify: true if ssl was verified - * channels: an array of all channels - */ -duk_ret_t Server_prototype_info(duk_context* ctx) -{ - const auto server = self(ctx); - const auto& channels = server->get_channels(); - - duk_push_object(ctx); - dukx_push(ctx, server->get_id()); - duk_put_prop_string(ctx, -2, "name"); - dukx_push(ctx, server->get_host()); - duk_put_prop_string(ctx, -2, "host"); - duk_push_int(ctx, server->get_port()); - duk_put_prop_string(ctx, -2, "port"); - duk_push_boolean(ctx, - (server->get_options() & server::options::ssl) == server::options::ssl); - duk_put_prop_string(ctx, -2, "ssl"); - duk_push_boolean(ctx, - (server->get_options() & server::options::ssl_verify) == server::options::ssl_verify); - duk_put_prop_string(ctx, -2, "sslVerify"); - dukx_push(ctx, server->get_command_char()); - duk_put_prop_string(ctx, -2, "commandChar"); - dukx_push(ctx, server->get_realname()); - duk_put_prop_string(ctx, -2, "realname"); - dukx_push(ctx, server->get_nickname()); - duk_put_prop_string(ctx, -2, "nickname"); - dukx_push(ctx, server->get_username()); - duk_put_prop_string(ctx, -2, "username"); - dukx_push(ctx, std::vector<std::string>(channels.begin(), channels.end())); - duk_put_prop_string(ctx, -2, "channels"); - - return 1; -} - -// }}} - -// {{{ Irccd.Server.prototype.invite - -/* - * Method: Irccd.Server.prototype.invite(target, channel) - * ------------------------------------------------------------------ - * - * Invite someone to a channel. - * - * Arguments: - * - target, the target to invite, - * - channel, the channel. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_invite(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto target = dukx_require<std::string>(ctx, 0); - auto channel = dukx_require<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->invite(std::move(target), std::move(channel)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.isSelf - -/* - * Method: Irccd.Server.prototype.isSelf(nickname) - * ------------------------------------------------------------------ - * - * Arguments: - * - nickname, the nickname to check. - * Returns: - * True if the nickname targets this server. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Server_prototype_isSelf(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - return dukx_push(ctx, self(ctx)->is_self(dukx_require<std::string>(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.join - -/* - * Method: Irccd.Server.prototype.join(channel, password = undefined) - * ------------------------------------------------------------------ - * - * Join a channel with an optional password. - * - * Arguments: - * - channel, the channel to join, - * - password, the password or undefined to not use. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_join(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto channel = dukx_require<std::string>(ctx, 0); - auto password = dukx_get<std::string>(ctx, 1); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->join(std::move(channel), std::move(password)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.kick - -/* - * Method: Irccd.Server.prototype.kick(target, channel, reason = undefined) - * ------------------------------------------------------------------ - * - * Kick someone from a channel. - * - * Arguments: - * - target, the target to kick, - * - channel, the channel, - * - reason, the optional reason or undefined to not set. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_kick(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto target = dukx_require<std::string>(ctx, 0); - auto channel = dukx_require<std::string>(ctx, 1); - auto reason = dukx_get<std::string>(ctx, 2); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->kick(std::move(target), std::move(channel), std::move(reason)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.me - -/* - * Method: Irccd.Server.prototype.me(target, message) - * ------------------------------------------------------------------ - * - * Send a CTCP Action. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_me(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto target = dukx_require<std::string>(ctx, 0); - auto message = dukx_get<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->me(std::move(target), std::move(message)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.message - -/* - * Method: Irccd.Server.prototype.message(target, message) - * ------------------------------------------------------------------ - * - * Send a message. - * - * Arguments: - * - target, the target or a channel, - * - message, the message. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_message(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto target = dukx_require<std::string>(ctx, 0); - auto message = dukx_get<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->message(std::move(target), std::move(message)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.mode - -/* - * Method: Irccd.Server.prototype.mode(channel, mode, limit, user, mask) - * ------------------------------------------------------------------ - * - * Change your mode. - * - * Arguments: - * - mode, the new mode. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_mode(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto channel = dukx_require<std::string>(ctx, 0); - auto mode = dukx_require<std::string>(ctx, 1); - auto limit = dukx_get<std::string>(ctx, 2); - auto user = dukx_get<std::string>(ctx, 3); - auto mask = dukx_get<std::string>(ctx, 4); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - if (mode.empty()) - throw server_error(server_error::invalid_mode); - - self(ctx)->mode( - std::move(channel), - std::move(mode), - std::move(limit), - std::move(user), - std::move(mask) - ); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.names - -/* - * Method: Irccd.Server.prototype.names(channel) - * ------------------------------------------------------------------ - * - * Get the list of names from a channel. - * - * Arguments: - * - channel, the channel. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_names(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto channel = dukx_require<std::string>(ctx, 0); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->names(std::move(channel)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.nick - -/* - * Method: Irccd.Server.prototype.nick(nickname) - * ------------------------------------------------------------------ - * - * Change the nickname. - * - * Arguments: - * - nickname, the nickname. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_nick(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto nickname = dukx_require<std::string>(ctx, 0); - - if (nickname.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->set_nickname(std::move(nickname)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.notice - -/* - * Method: Irccd.Server.prototype.notice(target, message) - * ------------------------------------------------------------------ - * - * Send a private notice. - * - * Arguments: - * - target, the target, - * - message, the notice message. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_notice(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto target = dukx_require<std::string>(ctx, 0); - auto message = dukx_get<std::string>(ctx, 1); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->notice(std::move(target), std::move(message)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.part - -/* - * Method: Irccd.Server.prototype.part(channel, reason = undefined) - * ------------------------------------------------------------------ - * - * Leave a channel. - * - * Arguments: - * - channel, the channel to leave, - * - reason, the optional reason, keep undefined for portability. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_part(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto channel = dukx_require<std::string>(ctx, 0); - auto reason = dukx_get<std::string>(ctx, 1); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->part(std::move(channel), std::move(reason)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.send - -/* - * Method: Irccd.Server.prototype.send(raw) - * ------------------------------------------------------------------ - * - * Send a raw message to the IRC server. - * - * Arguments: - * - raw, the raw message (without terminators). - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_send(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto raw = dukx_require<std::string>(ctx, 0); - - if (raw.empty()) - throw server_error(server_error::invalid_message); - - self(ctx)->send(std::move(raw)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.topic - -/* - * Method: Server.prototype.topic(channel, topic) - * ------------------------------------------------------------------ - * - * Change a channel topic. - * - * Arguments: - * - channel, the channel, - * - topic, the new topic. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_topic(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto channel = dukx_require<std::string>(ctx, 0); - auto topic = dukx_get<std::string>(ctx, 1); - - if (channel.empty()) - throw server_error(server_error::invalid_channel); - - self(ctx)->topic(std::move(channel), std::move(topic)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.whois - -/* - * Method: Irccd.Server.prototype.whois(target) - * ------------------------------------------------------------------ - * - * Get whois information. - * - * Arguments: - * - target, the target. - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_prototype_whois(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto target = dukx_require<std::string>(ctx, 0); - - if (target.empty()) - throw server_error(server_error::invalid_nickname); - - self(ctx)->whois(std::move(target)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.prototype.toString - -/* - * Method: Irccd.Server.prototype.toString() - * ------------------------------------------------------------------ - * - * Convert the object to std::string, convenience for adding the object - * as property key. - * - * Returns: - * The server name (unique). - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Server_prototype_toString(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - dukx_push(ctx, self(ctx)->get_id()); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.Server [constructor] - -/* - * Function: Irccd.Server(params) [constructor] - * ------------------------------------------------------------------ - * - * Construct a new server. - * - * Params must be filled with the following properties: - * - * name: the name, - * host: the host, - * ipv6: true to use ipv6, (Optional: default false) - * port: the port number, (Optional: default 6667) - * password: the password, (Optional: default none) - * channels: array of channels (Optiona: default empty) - * ssl: true to use ssl, (Optional: default false) - * sslVerify: true to verify (Optional: default true) - * nickname: "nickname", (Optional, default: irccd) - * username: "user name", (Optional, default: irccd) - * realname: "real name", (Optional, default: IRC Client Daemon) - * commandChar: "!", (Optional, the command char, default: "!") - * - * Arguments: - * - params, the server properties - * Throws: - * - Irccd.ServerError on server related errors, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Server_constructor(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - if (!duk_is_constructor_call(ctx)) - return 0; - - duk_check_type(ctx, 0, DUK_TYPE_OBJECT); - - auto json = nlohmann::json::parse(duk_json_encode(ctx, 0)); - auto s = server_util::from_json(dukx_type_traits<irccd>::self(ctx).get_service(), json); - - duk_push_this(ctx); - duk_push_pointer(ctx, new std::shared_ptr<server>(std::move(s))); - duk_put_prop_string(ctx, -2, signature); - duk_pop(ctx); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server [destructor] - -/* - * Function: Irccd.Server() [destructor] - * ------------------------------------------------------------------ - * - * Delete the property. - */ -duk_ret_t Server_destructor(duk_context* ctx) -{ - duk_get_prop_string(ctx, 0, signature); - delete static_cast<std::shared_ptr<server>*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - duk_del_prop_string(ctx, 0, signature); - - return 0; -} - -// }}} - -// {{{ Irccd.Server.add - -/* - * Function: Irccd.Server.add(s) - * ------------------------------------------------------------------ - * - * Register a new server to the irccd instance. - * - * Arguments: - * - s, the server to add. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Server_add(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - dukx_type_traits<irccd>::self(ctx).servers().add( - dukx_require<std::shared_ptr<server>>(ctx, 0)); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.Server.find - -/* - * Function: Irccd.Server.find(name) - * ------------------------------------------------------------------ - * - * Find a server by name. - * - * Arguments: - * - name, the server name - * Returns: - * The server object or undefined if not found. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Server_find(duk_context* ctx) -{ - return wrap(ctx, [] (auto ctx) { - auto id = dukx_require<std::string>(ctx, 0); - auto server = dukx_type_traits<irccd>::self(ctx).servers().get(id); - - if (!server) - return 0; - - dukx_push(ctx, server); - - return 1; - }); -} - -// }}} - -// {{{ Irccd.Server.list - -/* - * Function: Irccd.Server.list() - * ------------------------------------------------------------------ - * - * Get the map of all loaded servers. - * - * Returns: - * An object with string-to-servers pairs. - */ -duk_ret_t Server_list(duk_context* ctx) -{ - duk_push_object(ctx); - - for (const auto& server : dukx_type_traits<irccd>::self(ctx).servers().all()) { - dukx_push(ctx, server); - duk_put_prop_string(ctx, -2, server->get_id().c_str()); - } - - return 1; -} - -// }}} - -// {{{ Irccd.Server.remove - -/* - * Function: Irccd.Server.remove(name) - * ------------------------------------------------------------------ - * - * Remove a server from the irccd instance. You can pass the server object since - * it's coercible to a string. - * - * Arguments: - * - name the server name. - */ -duk_ret_t Server_remove(duk_context* ctx) -{ - dukx_type_traits<irccd>::self(ctx).servers().remove(duk_require_string(ctx, 0)); - - return 0; -} - -// }}} - -// {{{ Irccd.ServerError - -/* - * Function: Irccd.ServerError(code, message) - * ------------------------------------------------------------------ - * - * Create an Irccd.ServerError object. - * - * Arguments: - * - code, the error code, - * - message, the error message. - */ -duk_ret_t ServerError_constructor(duk_context* ctx) -{ - duk_push_this(ctx); - duk_push_int(ctx, duk_require_int(ctx, 0)); - duk_put_prop_string(ctx, -2, "code"); - duk_push_string(ctx, duk_require_string(ctx, 1)); - duk_put_prop_string(ctx, -2, "message"); - duk_push_string(ctx, "ServerError"); - duk_put_prop_string(ctx, -2, "name"); - duk_pop(ctx); - - return 0; -} - -// }}} - -const duk_function_list_entry methods[] = { - { "info", Server_prototype_info, 0 }, - { "invite", Server_prototype_invite, 2 }, - { "isSelf", Server_prototype_isSelf, 1 }, - { "join", Server_prototype_join, DUK_VARARGS }, - { "kick", Server_prototype_kick, DUK_VARARGS }, - { "me", Server_prototype_me, 2 }, - { "message", Server_prototype_message, 2 }, - { "mode", Server_prototype_mode, 1 }, - { "names", Server_prototype_names, 1 }, - { "nick", Server_prototype_nick, 1 }, - { "notice", Server_prototype_notice, 2 }, - { "part", Server_prototype_part, DUK_VARARGS }, - { "send", Server_prototype_send, 1 }, - { "topic", Server_prototype_topic, 2 }, - { "toString", Server_prototype_toString, 0 }, - { "whois", Server_prototype_whois, 1 }, - { nullptr, nullptr, 0 } -}; - -const duk_function_list_entry functions[] = { - { "add", Server_add, 1 }, - { "find", Server_find, 1 }, - { "list", Server_list, 0 }, - { "remove", Server_remove, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string server_jsapi::get_name() const -{ - return "Irccd.Server"; -} - -void server_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - - // ServerError function. - duk_push_c_function(plugin->get_context(), ServerError_constructor, 2); - duk_push_object(plugin->get_context()); - duk_get_global_string(plugin->get_context(), "Error"); - duk_get_prop_string(plugin->get_context(), -1, "prototype"); - duk_remove(plugin->get_context(), -2); - duk_set_prototype(plugin->get_context(), -2); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "ServerError"); - - // Server constructor. - duk_push_c_function(plugin->get_context(), Server_constructor, 1); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_push_c_function(plugin->get_context(), Server_destructor, 1); - duk_set_finalizer(plugin->get_context(), -2); - duk_dup_top(plugin->get_context()); - duk_put_global_string(plugin->get_context(), prototype); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "Server"); - duk_pop(plugin->get_context()); -} - -using server_traits = dukx_type_traits<std::shared_ptr<server>>; -using server_error_traits = dukx_type_traits<server_error>; - -void server_traits::push(duk_context* ctx, std::shared_ptr<server> server) -{ - assert(ctx); - assert(server); - - dukx_stack_assert sa(ctx, 1); - - duk_push_object(ctx); - duk_push_pointer(ctx, new std::shared_ptr<class server>(std::move(server))); - duk_put_prop_string(ctx, -2, signature); - duk_get_global_string(ctx, prototype); - duk_set_prototype(ctx, -2); -} - -std::shared_ptr<server> server_traits::require(duk_context* ctx, duk_idx_t index) -{ - if (!duk_is_object(ctx, index) || !duk_has_prop_string(ctx, index, signature)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); - - duk_get_prop_string(ctx, index, signature); - auto file = *static_cast<std::shared_ptr<server> *>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - return file; -} - -void server_error_traits::raise(duk_context* ctx, const server_error& ex) -{ - dukx_stack_assert sa(ctx, 1); - - duk_get_global_string(ctx, "Irccd"); - duk_get_prop_string(ctx, -1, "ServerError"); - duk_remove(ctx, -2); - dukx_push(ctx, ex.code().value()); - dukx_push(ctx, ex.code().message()); - duk_new(ctx, 2); - - (void)duk_throw(ctx); -} - -} // !irccd
--- a/libirccd-js/irccd/js/server_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* - * server_jsapi.hpp -- Irccd.Server API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_SERVER_JSAPI_HPP -#define IRCCD_JS_SERVER_JSAPI_HPP - -/** - * \file mod-server.hpp - * \brief irccd.Server Javascript API. - */ - -#include <irccd/daemon/server.hpp> - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief irccd.Server Javascript API. - * \ingroup jsapi - */ -class server_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -/** - * \brief Specialization for servers as shared_ptr. - * - * Supports push, require. - */ -template <> -class dukx_type_traits<std::shared_ptr<server>> : public std::true_type { -public: - /** - * Push a server. - * - * \pre server != nullptr - * \param ctx the context - * \param server the server - */ - static void push(duk_context* ctx, std::shared_ptr<server> server); - - /** - * Require a server. Raise a Javascript error if not a Server. - * - * \param ctx the context - * \param index the index - * \return the server - */ - static std::shared_ptr<server> require(duk_context* ctx, duk_idx_t index); -}; - -/** - * \brief Specialization for server_error. - */ -template <> -class dukx_type_traits<server_error> : public std::true_type { -public: - /** - * Raise a server_error. - * - * \param ctx the context - * \param error the error - */ - static void raise(duk_context* ctx, const server_error& error); -}; - -} // !irccd - -#endif // !IRCCD_JS_SERVER_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/system_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,330 @@ +/* + * system_js_api.cpp -- Irccd.System API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <irccd/sysconfig.hpp> + +#include <chrono> +#include <cstdlib> +#include <thread> + +#if defined(HAVE_POPEN) +# include <cstdio> +#endif + +#include <irccd/system.hpp> + +#include "file_js_api.hpp" +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" +#include "system_js_api.hpp" + +namespace irccd::js { + +namespace { + +// {{{ wrap + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.System.env + +/* + * Function: Irccd.System.env(key) + * ------------------------------------------------------------------ + * + * Get an environment system variable. + * + * Arguments: + * - key, the environment variable. + * Returns: + * The value. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_env(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::env(duk::get<std::string>(ctx, 0))); + }); +} + +// }}} + +// {{{ Irccd.System.exec + +/* + * Function: Irccd.System.exec(cmd) + * ------------------------------------------------------------------ + * + * Execute a system command. + * + * Arguments: + * - cmd, the command to execute. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_exec(duk_context* ctx) -> duk_ret_t +{ + std::system(duk_require_string(ctx, 0)); + + return 0; +} + +// }}} + +// {{{ Irccd.System.home + +/* + * Function: Irccd.System.home() + * ------------------------------------------------------------------ + * + * Get the operating system user's home. + * + * Returns: + * The user home directory. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_home(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::home()); + }); +} + +// }}} + +// {{{ Irccd.System.name + +/* + * Function: Irccd.System.name() + * ------------------------------------------------------------------ + * + * Get the operating system name. + * + * Returns: + * The system name. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_name(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::name()); + }); +} + +// }}} + +// {{{ Irccd.System.popen + +#if defined(HAVE_POPEN) + +/* + * Function: Irccd.System.popen(cmd, mode) [optional] + * ------------------------------------------------------------------ + * + * Wrapper for popen(3) if the function is available. + * + * Arguments: + * - cmd, the command to execute, + * - mode, the mode (e.g. "r"). + * Returns: + * A irccd.File object. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_popen(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); + + if (fp == nullptr) + throw std::system_error(make_error_code(static_cast<std::errc>(errno))); + + return duk::push(ctx, std::make_shared<file>(fp, [] (auto fp) { ::pclose(fp); })); + }); +} + +#endif // !HAVE_POPEN + +// }}} + +// {{{ Icrcd.System.sleep + +/* + * Function: Irccd.System.sleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in seconds. + * + * Arguments: + * - delay, the delay in seconds. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_sleep(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.System.ticks + +/* + * Function: Irccd.System.ticks() + * ------------------------------------------------------------------ + * + * Get the number of milliseconds since irccd was started. + * + * Returns: + * The number of milliseconds. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_ticks(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push<unsigned>(ctx, sys::ticks()); + }); +} + +// }}} + +// {{{ Irccd.System.usleep + +/* + * Function: Irccd.System.usleep(delay) + * ------------------------------------------------------------------ + * + * Sleep the main loop for the specific delay in microseconds. + * + * Arguments: + * - delay, the delay in microseconds. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_usleep(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); + + return 0; + }); +} + +// }}} + +// {{{ Irccd.System.uptime + +/* + * Function: Irccd.System.uptime() + * ------------------------------------------------------------------ + * + * Get the system uptime. + * + * Returns: + * The system uptime. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_uptime(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push<unsigned>(ctx, sys::uptime()); + }); +} + +// }}} + +// {{{ Irccd.System.version + +/* + * Function: Irccd.System.version() + * ------------------------------------------------------------------ + * + * Get the operating system version. + * + * Returns: + * The system version. + * Throws: + * - Irccd.SystemError on errors. + */ +auto System_version(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, sys::version()); + }); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "env", System_env, 1 }, + { "exec", System_exec, 1 }, + { "home", System_home, 0 }, + { "name", System_name, 0 }, +#if defined(HAVE_POPEN) + { "popen", System_popen, 2 }, +#endif + { "sleep", System_sleep, 1 }, + { "ticks", System_ticks, 0 }, + { "uptime", System_uptime, 0 }, + { "usleep", System_usleep, 1 }, + { "version", System_version, 0 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto system_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.System"; +} + +void system_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "System"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/system_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * system_js_api.hpp -- Irccd.System API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_SYSTEM_JS_API_HPP +#define IRCCD_JS_SYSTEM_JS_API_HPP + +/** + * \file system_js_api.hpp + * \brief Irccd.System Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.System Javascript API. + * \ingroup js_api + */ +class system_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_SYSTEM_JS_API_HPP
--- a/libirccd-js/irccd/js/system_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,330 +0,0 @@ -/* - * system_jsapi.cpp -- Irccd.System API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/sysconfig.hpp> - -#include <chrono> -#include <cstdlib> -#include <thread> - -#if defined(HAVE_POPEN) -# include <cstdio> -#endif - -#include <irccd/system.hpp> - -#include "file_jsapi.hpp" -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" -#include "system_jsapi.hpp" - -namespace irccd { - -namespace { - -// {{{ wrap - -template <typename Handler> -duk_ret_t wrap(duk_context* ctx, Handler handler) -{ - try { - return handler(); - } catch (const std::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.System.env - -/* - * Function: Irccd.System.env(key) - * ------------------------------------------------------------------ - * - * Get an environment system variable. - * - * Arguments: - * - key, the environment variable. - * Returns: - * The value. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_env(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, sys::env(dukx_get<std::string>(ctx, 0))); - }); -} - -// }}} - -// {{{ Irccd.System.exec - -/* - * Function: Irccd.System.exec(cmd) - * ------------------------------------------------------------------ - * - * Execute a system command. - * - * Arguments: - * - cmd, the command to execute. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_exec(duk_context* ctx) -{ - std::system(duk_require_string(ctx, 0)); - - return 0; -} - -// }}} - -// {{{ Irccd.System.home - -/* - * Function: Irccd.System.home() - * ------------------------------------------------------------------ - * - * Get the operating system user's home. - * - * Returns: - * The user home directory. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_home(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, sys::home()); - }); -} - -// }}} - -// {{{ Irccd.System.name - -/* - * Function: Irccd.System.name() - * ------------------------------------------------------------------ - * - * Get the operating system name. - * - * Returns: - * The system name. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_name(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, sys::name()); - }); -} - -// }}} - -// {{{ Irccd.System.popen - -#if defined(HAVE_POPEN) - -/* - * Function: Irccd.System.popen(cmd, mode) [optional] - * ------------------------------------------------------------------ - * - * Wrapper for popen(3) if the function is available. - * - * Arguments: - * - cmd, the command to execute, - * - mode, the mode (e.g. "r"). - * Returns: - * A irccd.File object. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_popen(duk_context* ctx) -{ - return wrap(ctx, [&] { - auto fp = ::popen(duk_require_string(ctx, 0), duk_require_string(ctx, 1)); - - if (fp == nullptr) - throw std::system_error(make_error_code(static_cast<std::errc>(errno))); - - return dukx_push(ctx, std::make_shared<file>(fp, [] (auto fp) { ::pclose(fp); })); - }); -} - -#endif // !HAVE_POPEN - -// }}} - -// {{{ Icrcd.System.sleep - -/* - * Function: Irccd.System.sleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in seconds. - * - * Arguments: - * - delay, the delay in seconds. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_sleep(duk_context* ctx) -{ - return wrap(ctx, [&] { - std::this_thread::sleep_for(std::chrono::seconds(duk_get_int(ctx, 0))); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.System.ticks - -/* - * Function: Irccd.System.ticks() - * ------------------------------------------------------------------ - * - * Get the number of milliseconds since irccd was started. - * - * Returns: - * The number of milliseconds. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_ticks(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push<unsigned>(ctx, sys::ticks()); - }); -} - -// }}} - -// {{{ Irccd.System.usleep - -/* - * Function: Irccd.System.usleep(delay) - * ------------------------------------------------------------------ - * - * Sleep the main loop for the specific delay in microseconds. - * - * Arguments: - * - delay, the delay in microseconds. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_usleep(duk_context* ctx) -{ - return wrap(ctx, [&] { - std::this_thread::sleep_for(std::chrono::microseconds(duk_get_int(ctx, 0))); - - return 0; - }); -} - -// }}} - -// {{{ Irccd.System.uptime - -/* - * Function: Irccd.System.uptime() - * ------------------------------------------------------------------ - * - * Get the system uptime. - * - * Returns: - * The system uptime. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_uptime(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push<unsigned>(ctx, sys::uptime()); - }); -} - -// }}} - -// {{{ Irccd.System.version - -/* - * Function: Irccd.System.version() - * ------------------------------------------------------------------ - * - * Get the operating system version. - * - * Returns: - * The system version. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t System_version(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, sys::version()); - }); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "env", System_env, 1 }, - { "exec", System_exec, 1 }, - { "home", System_home, 0 }, - { "name", System_name, 0 }, -#if defined(HAVE_POPEN) - { "popen", System_popen, 2 }, -#endif - { "sleep", System_sleep, 1 }, - { "ticks", System_ticks, 0 }, - { "uptime", System_uptime, 0 }, - { "usleep", System_usleep, 1 }, - { "version", System_version, 0 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string system_jsapi::get_name() const -{ - return "Irccd.System"; -} - -void system_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "System"); - duk_pop(plugin->get_context()); -} - -} // !irccd
--- a/libirccd-js/irccd/js/system_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * system_jsapi.hpp -- Irccd.System API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_SYSTEM_JSAPI_HPP -#define IRCCD_JS_SYSTEM_JSAPI_HPP - -/** - * \file system_jsapi.hpp - * \brief Irccd.System Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd.System Javascript API. - * \ingroup jsapi - */ -class system_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_SYSTEM_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/timer_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,314 @@ +/* + * timer_js_api.cpp -- Irccd.timer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/asio.hpp> + +#include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/logger.hpp> +#include <irccd/daemon/plugin_service.hpp> + +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" +#include "plugin_js_api.hpp" +#include "timer_js_api.hpp" + +namespace asio = boost::asio; + +namespace irccd::js { + +namespace { + +const std::string_view signature("\xff""\xff""Irccd.Timer"); +const std::string_view table("\xff""\xff""Irccd.Timer.callbacks"); + +// {{{ timer + +class timer : public std::enable_shared_from_this<timer> { +public: + enum class type { + single, + repeat + }; + +private: + boost::asio::deadline_timer handle_; + js_plugin& plugin_; + + std::string key_; + type type_; + int delay_; + + bool is_running_{false}; + bool is_waiting_{false}; + + void handle(); + +public: + timer(boost::asio::io_service&, js_plugin&, type, int); + + auto key() const noexcept -> const std::string&; + + void start(); + + void stop(); +}; + +void timer::handle() +{ + duk::stack_guard sa(plugin_.get_context()); + + duk_push_global_stash(plugin_.get_context()); + duk_get_prop_string(plugin_.get_context(), -1, table.data()); + duk_remove(plugin_.get_context(), -2); + duk_get_prop_string(plugin_.get_context(), -1, key_.c_str()); + duk_remove(plugin_.get_context(), -2); + + if (duk_pcall(plugin_.get_context(), 0)) { + auto& log = duk::type_traits<irccd>::self(plugin_.get_context()).get_log(); + + log.warning(static_cast<const plugin&>(plugin_)) << "timer error:" << std::endl; + log.warning(static_cast<const plugin&>(plugin_)) << " " << duk::get_stack(plugin_.get_context(), -1).what() << std::endl; + } else + duk_pop(plugin_.get_context()); +} + +timer::timer(boost::asio::io_service& service, js_plugin& plugin, type type, int delay) + : handle_(service) + , plugin_(plugin) + , type_(type) + , delay_(delay) +{ +} + +auto timer::key() const noexcept -> const std::string& +{ + return key_; +} + +void timer::start() +{ + if (is_waiting_) + return; + + is_running_ = is_waiting_ = true; + + handle_.expires_from_now(boost::posix_time::milliseconds(delay_)); + handle_.async_wait([this] (auto code) { + is_waiting_ = false; + + if (code) { + is_running_ = false; + return; + } + + handle(); + + if (is_running_ && type_ == type::repeat) + start(); + }); +} + +void timer::stop() +{ + if (is_running_) { + handle_.cancel(); + is_running_ = false; + } +} + +// }}} + +// {{{ self + +auto self(duk_context* ctx) -> timer* +{ + duk::stack_guard sa(ctx); + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, signature.data()); + auto ptr = duk_to_pointer(ctx, -1); + duk_pop_2(ctx); + + if (!ptr) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); + + return static_cast<timer*>(ptr); +} + +// }}} + +// {{{ Irccd.Timer.prototype.start + +/* + * Method: Irccd.Timer.prototype.start() + * -------------------------------------------------------- + * + * Start the timer. If the timer is already started the method is a no-op. + */ +auto Timer_prototype_start(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->start(); + + return 0; +} + +// }}} + +// {{{ Irccd.Timer.prototype.stop + +/* + * Method: Irccd.Timer.prototype.stop() + * -------------------------------------------------------- + * + * Stop the timer. + */ +auto Timer_prototype_stop(duk_context* ctx) -> duk_ret_t +{ + self(ctx)->stop(); + + return 0; +} + +// }}} + +// {{{ Irccd.Timer [destructor] + +/* + * Function: Irccd.Timer() [destructor] + * ------------------------------------------------------------------ + * + * Deleter the timer. + */ +auto Timer_destructor(duk_context* ctx) -> duk_ret_t +{ + duk::stack_guard sa(ctx); + + // Get timer from this. + duk_get_prop_string(ctx, 0, signature.data()); + auto ptr = static_cast<timer*>(duk_to_pointer(ctx, -1)); + duk_pop(ctx); + + // Remove callback from timer table. + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, table.data()); + duk_remove(ctx, -2); + duk_del_prop_string(ctx, -1, ptr->key().c_str()); + duk_pop(ctx); + + delete ptr; + + return 0; +} + +// }}} + +// {{{ Irccd.Timer [constructor] + +/* + * Function: Irccd.Timer(type, delay, callback) [constructor] + * -------------------------------------------------------- + * + * Create a new timer object. + * + * Arguments: + * - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat), + * - delay, the interval in milliseconds, + * - callback, the function to call. + */ +auto Timer_constructor(duk_context* ctx) -> duk_ret_t +{ + if (!duk_is_constructor_call(ctx)) + return 0; + + try { + // Check parameters. + const auto type = duk_require_int(ctx, 0); + const auto delay = duk_require_int(ctx, 1); + + if (type < static_cast<int>(timer::type::single) || type > static_cast<int>(timer::type::repeat)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); + if (delay < 0) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); + if (!duk_is_callable(ctx, 2)) + duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); + + auto& plugin = duk::type_traits<js_plugin>::self(ctx); + auto& daemon = duk::type_traits<irccd>::self(ctx); + auto object = new timer(daemon.get_service(), plugin, static_cast<timer::type>(type), delay); + + duk_push_this(ctx); + duk_push_pointer(ctx, object); + duk_put_prop_string(ctx, -2, signature.data()); + duk_push_c_function(ctx, Timer_destructor, 1); + duk_set_finalizer(ctx, -2); + duk_pop(ctx); + + // Store the function in a table to be called later. + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, table.data()); + duk_remove(ctx, -2); + duk_dup(ctx, 2); + duk_put_prop_string(ctx, -2, object->key().c_str()); + duk_pop(ctx); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +const duk_function_list_entry methods[] = { + { "start", Timer_prototype_start, 0 }, + { "stop", Timer_prototype_stop, 0 }, + { nullptr, nullptr, 0 } +}; + +const duk_number_list_entry constants[] = { + { "Single", static_cast<int>(timer::type::single) }, + { "Repeat", static_cast<int>(timer::type::repeat) }, + { nullptr, 0 } +}; + +} // !namespace + +auto timer_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Timer"; +} + +void timer_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_c_function(plugin->get_context(), Timer_constructor, 3); + duk_put_number_list(plugin->get_context(), -1, constants); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, methods); + duk_put_prop_string(plugin->get_context(), -2, "prototype"); + duk_put_prop_string(plugin->get_context(), -2, "Timer"); + duk_pop(plugin->get_context()); + duk_push_global_stash(plugin->get_context()); + duk_push_object(plugin->get_context()); + duk_put_prop_string(plugin->get_context(), -2, table.data()); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/timer_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * timer_js_api.hpp -- Irccd.Timer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_TIMER_JS_API_HPP +#define IRCCD_JS_TIMER_JS_API_HPP + +/** + * \file timer_js_api + * \brief Irccd.Timer Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.Timer Javascript API. + * \ingroup js_api + */ +class timer_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_TIMER_JS_API_HPP
--- a/libirccd-js/irccd/js/timer_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,301 +0,0 @@ -/* - * timer_jsapi.cpp -- Irccd.timer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/asio.hpp> - -#include <irccd/daemon/irccd.hpp> -#include <irccd/daemon/logger.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" -#include "plugin_jsapi.hpp" -#include "timer_jsapi.hpp" - -namespace irccd { - -namespace { - -const char* signature("\xff""\xff""irccd-timer-ptr"); -const char* table("\xff""\xff""irccd-timer-callbacks"); - -// {{{ timer - -class timer { -public: - enum class type_t { - single, - repeat - }; - -private: - boost::asio::deadline_timer handle_; - std::weak_ptr<js_plugin> plugin_; - std::uintmax_t delay_; - type_t type_; - bool is_running_{false}; - bool is_waiting_{false}; - - void handle(); - -public: - inline timer(boost::asio::io_service& service, - std::weak_ptr<js_plugin> plugin, - std::uintmax_t delay, - type_t type) noexcept - : handle_(service) - , plugin_(plugin) - , delay_(delay) - , type_(type) - { - } - - inline std::string key() const - { - return std::to_string(reinterpret_cast<std::uintptr_t>(this)); - } - - void start(); - void stop(); -}; - -void timer::handle() -{ - auto plg = plugin_.lock(); - - if (!plg) - return; - - auto& ctx = plg->get_context(); - - duk_get_global_string(ctx, table); - duk_get_prop_string(ctx, -1, key().c_str()); - duk_remove(ctx, -2); - - if (duk_pcall(ctx, 0)) { - auto& log = dukx_type_traits<irccd>::self(ctx).get_log(); - - log.warning(static_cast<const plugin&>(*plg)) << "timer error:" << std::endl; - log.warning(static_cast<const plugin&>(*plg)) << " " << dukx_stack(ctx, -1).what() << std::endl; - } else - duk_pop(ctx); -} - -void timer::start() -{ - if (is_waiting_) - return; - - is_running_ = is_waiting_ = true; - - handle_.expires_from_now(boost::posix_time::milliseconds(delay_)); - handle_.async_wait([this] (auto code) { - is_waiting_ = false; - - if (code) - return; - - handle(); - - if (is_running_ && type_ == type_t::repeat) - start(); - }); -} - -void timer::stop() -{ - if (is_running_) { - handle_.cancel(); - is_running_ = false; - } -} - -// }}} - -// {{{ self - -timer* self(duk_context* ctx) -{ - dukx_stack_assert sa(ctx); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, signature); - auto ptr = duk_to_pointer(ctx, -1); - duk_pop_2(ctx); - - if (!ptr) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Timer object"); - - return static_cast<timer*>(ptr); -} - -// }}} - -// {{{ Irccd.Timer.prototype.start - -/* - * Method: Irccd.Timer.prototype.start() - * -------------------------------------------------------- - * - * Start the timer. If the timer is already started the method is a no-op. - */ -duk_ret_t Timer_prototype_start(duk_context* ctx) -{ - self(ctx)->start(); - - return 0; -} - -// }}} - -// {{{ Irccd.Timer.prototype.stop - -/* - * Method: Irccd.Timer.prototype.stop() - * -------------------------------------------------------- - * - * Stop the timer. - */ -duk_ret_t Timer_prototype_stop(duk_context* ctx) -{ - self(ctx)->stop(); - - return 0; -} - -// }}} - -// {{{ Irccd.Timer [destructor] - -/* - * Function: Irccd.Timer() [destructor] - * ------------------------------------------------------------------ - * - * Deleter the timer. - */ -duk_ret_t Timer_destructor(duk_context* ctx) -{ - dukx_stack_assert sa(ctx); - - // Get timer from this. - duk_get_prop_string(ctx, 0, signature); - auto ptr = static_cast<timer*>(duk_to_pointer(ctx, -1)); - duk_pop(ctx); - - // Remove callback from timer table. - duk_get_global_string(ctx, table); - duk_del_prop_string(ctx, -1, ptr->key().c_str()); - duk_pop(ctx); - - delete ptr; - - return 0; -} - -// }}} - -// {{{ Irccd.Timer [constructor] - -/* - * Function: Irccd.Timer(type, delay, callback) [constructor] - * -------------------------------------------------------- - * - * Create a new timer object. - * - * Arguments: - * - type, the type of timer (Irccd.Timer.Single or Irccd.Timer.Repeat), - * - delay, the interval in milliseconds, - * - callback, the function to call. - */ -duk_ret_t Timer_constructor(duk_context* ctx) -{ - if (!duk_is_constructor_call(ctx)) - return 0; - - try { - // Check parameters. - const auto type = duk_require_int(ctx, 0); - const auto delay = duk_require_int(ctx, 1); - - if (type < static_cast<int>(timer::type_t::single) || type > static_cast<int>(timer::type_t::repeat)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid timer type"); - if (delay < 0) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "negative delay given"); - if (!duk_is_callable(ctx, 2)) - duk_error(ctx, DUK_ERR_TYPE_ERROR, "missing callback function"); - - auto plugin = dukx_type_traits<js_plugin>::self(ctx); - auto& daemon = dukx_type_traits<irccd>::self(ctx); - auto object = new timer(daemon.get_service(), plugin, delay, static_cast<timer::type_t>(type)); - - duk_push_this(ctx); - duk_push_pointer(ctx, object); - duk_put_prop_string(ctx, -2, signature); - duk_push_c_function(ctx, Timer_destructor, 1); - duk_set_finalizer(ctx, -2); - duk_pop(ctx); - - // Store the function in a table to be called later. - duk_get_global_string(ctx, table); - duk_dup(ctx, 2); - duk_put_prop_string(ctx, -2, object->key().c_str()); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -// }}} - -const duk_function_list_entry methods[] = { - { "start", Timer_prototype_start, 0 }, - { "stop", Timer_prototype_stop, 0 }, - { nullptr, nullptr, 0 } -}; - -const duk_number_list_entry constants[] = { - { "Single", static_cast<int>(timer::type_t::single) }, - { "Repeat", static_cast<int>(timer::type_t::repeat) }, - { nullptr, 0 } -}; - -} // !namespace - -std::string timer_jsapi::get_name() const -{ - return "Irccd.Timer"; -} - -void timer_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_c_function(plugin->get_context(), Timer_constructor, 3); - duk_put_number_list(plugin->get_context(), -1, constants); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, methods); - duk_put_prop_string(plugin->get_context(), -2, "prototype"); - duk_put_prop_string(plugin->get_context(), -2, "Timer"); - duk_pop(plugin->get_context()); - duk_push_object(plugin->get_context()); - duk_put_global_string(plugin->get_context(), table); -} - -} // !irccd
--- a/libirccd-js/irccd/js/timer_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * timer_jsapi.hpp -- Irccd.Timer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_TIMER_JSAPI_HPP -#define IRCCD_JS_TIMER_JSAPI_HPP - -/** - * \file timer_jsapi - * \brief Irccd.Timer Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd.Timer Javascript API. - * \ingroup jsapi - */ -class timer_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_TIMER_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/unicode_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,163 @@ +/* + * unicode_js_api.cpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "js_plugin.hpp" +#include "unicode.hpp" +#include "unicode_js_api.hpp" + +namespace irccd::js { + +namespace { + +// {{{ Irccd.Unicode.isDigit + +/* + * Function: Irccd.Unicode.isDigit(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the digit category. + */ +auto Unicode_isDigit(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isdigit(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isLetter + +/* + * Function: Irccd.Unicode.isLetter(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the letter category. + */ +auto Unicode_isLetter(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isalpha(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isLower + +/* + * Function: Irccd.Unicode.isLower(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is lower case. + */ +auto Unicode_isLower(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::islower(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isSpace + +/* + * Function: Irccd.Unicode.isSpace(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is in the space category. + */ +auto Unicode_isSpace(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isspace(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isTitle + +/* + * Function: Irccd.Unicode.isTitle(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is title case. + */ +auto Unicode_isTitle(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::istitle(duk_get_int(ctx, 0))); +} + +// }}} + +// {{{ Irccd.Unicode.isUpper + +/* + * Function: Irccd.Unicode.isUpper(code) + * -------------------------------------------------------- + * + * Arguments: + * - code, the code point. + * Returns: + * True if the code is upper case. + */ +auto Unicode_isUpper(duk_context* ctx) noexcept -> duk_ret_t +{ + return duk::push(ctx, unicode::isupper(duk_get_int(ctx, 0))); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "isDigit", Unicode_isDigit, 1 }, + { "isLetter", Unicode_isLetter, 1 }, + { "isLower", Unicode_isLower, 1 }, + { "isSpace", Unicode_isSpace, 1 }, + { "isTitle", Unicode_isTitle, 1 }, + { "isUpper", Unicode_isUpper, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto unicode_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Unicode"; +} + +void unicode_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "Unicode"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/unicode_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * unicode_js_api.hpp -- Irccd.Unicode API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_UNICODE_JS_API_HPP +#define IRCCD_JS_UNICODE_JS_API_HPP + +/** + * \file unicode_js_api.hpp + * \brief Irccd.Unicode Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.Unicode Javascript API. + * \ingroup js_api + */ +class unicode_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_UNICODE_JS_API_HPP
--- a/libirccd-js/irccd/js/unicode_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/* - * unicode_jsapi.cpp -- Irccd.Unicode API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "js_plugin.hpp" -#include "unicode.hpp" -#include "unicode_jsapi.hpp" - -namespace irccd { - -namespace { - -// {{{ Irccd.Unicode.isDigit - -/* - * Function: Irccd.Unicode.isDigit(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the digit category. - */ -duk_ret_t Unicode_isDigit(duk_context* ctx) noexcept -{ - return dukx_push(ctx, unicode::isdigit(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isLetter - -/* - * Function: Irccd.Unicode.isLetter(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the letter category. - */ -duk_ret_t Unicode_isLetter(duk_context* ctx) noexcept -{ - return dukx_push(ctx, unicode::isalpha(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isLower - -/* - * Function: Irccd.Unicode.isLower(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is lower case. - */ -duk_ret_t Unicode_isLower(duk_context* ctx) noexcept -{ - return dukx_push(ctx, unicode::islower(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isSpace - -/* - * Function: Irccd.Unicode.isSpace(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is in the space category. - */ -duk_ret_t Unicode_isSpace(duk_context* ctx) noexcept -{ - return dukx_push(ctx, unicode::isspace(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isTitle - -/* - * Function: Irccd.Unicode.isTitle(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is title case. - */ -duk_ret_t Unicode_isTitle(duk_context* ctx) noexcept -{ - return dukx_push(ctx, unicode::istitle(duk_get_int(ctx, 0))); -} - -// }}} - -// {{{ Irccd.Unicode.isUpper - -/* - * Function: Irccd.Unicode.isUpper(code) - * -------------------------------------------------------- - * - * Arguments: - * - code, the code point. - * Returns: - * True if the code is upper case. - */ -duk_ret_t Unicode_isUpper(duk_context* ctx) noexcept -{ - return dukx_push(ctx, unicode::isupper(duk_get_int(ctx, 0))); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "isDigit", Unicode_isDigit, 1 }, - { "isLetter", Unicode_isLetter, 1 }, - { "isLower", Unicode_isLower, 1 }, - { "isSpace", Unicode_isSpace, 1 }, - { "isTitle", Unicode_isTitle, 1 }, - { "isUpper", Unicode_isUpper, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string unicode_jsapi::get_name() const -{ - return "Irccd.Unicode"; -} - -void unicode_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "Unicode"); - duk_pop(plugin->get_context()); -} - -} // !irccd
--- a/libirccd-js/irccd/js/unicode_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * unicode_jsapi.hpp -- Irccd.Unicode API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_UNICODE_JSAPI_HPP -#define IRCCD_JS_UNICODE_JSAPI_HPP - -/** - * \file unicode_jsapi.hpp - * \brief Irccd.Unicode Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd.Unicode Javascript API. - * \ingroup jsapi - */ -class unicode_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_UNICODE_JSAPI_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/util_js_api.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,351 @@ +/* + * util_js_api.cpp -- Irccd.Util API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <climits> + +#include <irccd/string_util.hpp> + +#include "irccd_js_api.hpp" +#include "js_plugin.hpp" +#include "util_js_api.hpp" + +namespace irccd::js { + +namespace { + +// {{{ subst + +/* + * Read parameters for Irccd.Util.format function, the object is defined as + * following: + * + * { + * date: the date object + * flags: the flags (not implemented yet) + * field1: a field to substitute in #{} pattern + * field2: a field to substitute in #{} pattern + * fieldn: ... + * } + */ +auto subst(duk_context* ctx, int index) -> string_util::subst +{ + string_util::subst params; + + if (!duk_is_object(ctx, index)) + return params; + + duk_enum(ctx, index, 0); + + while (duk_next(ctx, -1, true)) { + if (duk::get<std::string>(ctx, -2) == "date") + params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000); + else + params.keywords.insert({ + duk::get<std::string>(ctx, -2), + duk::get<std::string>(ctx, -1) + }); + + duk_pop_n(ctx, 2); + } + + return params; +} + +// }}} + +// {{{ split + +/* + * split (for Irccd.Util.cut) + * ------------------------------------------------------------------ + * + * Extract individual tokens in array or a whole string as a std:::vector. + */ +auto split(duk_context* ctx) -> std::vector<std::string> +{ + duk_require_type_mask(ctx, 0, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_STRING); + + std::vector<std::string> result; + std::string pattern = " \t\n"; + + if (duk_is_string(ctx, 0)) + result = string_util::split(duk::get<std::string>(ctx, 0), pattern); + else if (duk_is_array(ctx, 0)) { + duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY); + + while (duk_next(ctx, -1, 1)) { + // Split individual tokens as array if spaces are found. + const auto tmp = string_util::split(duk_to_string(ctx, -1), pattern); + + result.insert(result.end(), tmp.begin(), tmp.end()); + duk_pop_2(ctx); + } + } + + return result; +} + +// }}} + +// {{{ limit + +/* + * limit (for Irccd.Util.cut) + * ------------------------------------------------------------------ + * + * Get the maxl/maxc argument. + * + * The argument value is the default and also used as the result returned. + */ +auto limit(duk_context* ctx, int index, const char* name, int value) -> int +{ + if (duk_get_top(ctx) < index || !duk_is_number(ctx, index)) + return value; + + value = duk_to_int(ctx, index); + + if (value <= 0) + duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument %d (%s) must be positive", index, name); + + return value; +} + +// }}} + +// {{{ lines + +/* + * lines (for Irccd.Util.cut) + * ------------------------------------------------------------------ + * + * Build a list of lines. + * + * Several cases possible: + * + * - s is the current line + * - abc is the token to add + * + * s = "" (new line) + * s -> "abc" + * + * s = "hello world" (enough room) + * s -> "hello world abc" + * + * s = "hello world" (not enough room: maxc is smaller) + * s+1 = "abc" + */ +auto lines(duk_context* ctx, const std::vector<std::string>& tokens, int maxc) -> std::vector<std::string> +{ + std::vector<std::string> result{""}; + + for (const auto& s : tokens) { + if (s.length() > static_cast<std::size_t>(maxc)) + duk_error(ctx, DUK_ERR_RANGE_ERROR, "word '%s' could not fit in maxc limit (%d)", s.c_str(), maxc); + + // Compute the length required (prepend a space if needed) + auto required = s.length() + (result.back().empty() ? 0 : 1); + + if (result.back().length() + required > static_cast<std::size_t>(maxc)) + result.push_back(s); + else { + if (!result.back().empty()) + result.back() += ' '; + + result.back() += s; + } + } + + return result; +} + +// }}} + +// {{{ wrap + +template <typename Handler> +auto wrap(duk_context* ctx, Handler handler) -> duk_ret_t +{ + try { + return handler(); + } catch (const std::system_error& ex) { + duk::raise(ctx, ex); + } catch (const std::exception& ex) { + duk::raise(ctx, ex); + } + + return 0; +} + +// }}} + +// {{{ Irccd.Util.cut + +/* + * Function: Irccd.Util.cut(data, maxc, maxl) + * -------------------------------------------------------- + * + * Cut a piece of data into several lines. + * + * The argument data is a string or a list of strings. In any case, all strings + * are first splitted by spaces and trimmed. This ensure that useless + * whitespaces are discarded. + * + * The argument maxc controls the maximum of characters allowed per line, it can + * be a positive integer. If undefined is given, a default of 72 is used. + * + * The argument maxl controls the maximum of lines allowed. It can be a positive + * integer or undefined for an infinite list. + * + * If maxl is used as a limit and the data can not fit within the bounds, + * undefined is returned. + * + * An empty list may be returned if empty strings were found. + * + * Arguments: + * - data, a string or an array of strings, + * - maxc, max number of colums (Optional, default: 72), + * - maxl, max number of lines (Optional, default: undefined). + * Returns: + * A list of strings ready to be sent or undefined if the data is too big. + * Throws: + * - RangeError if maxl or maxc are negative numbers, + * - RangeError if one word length was bigger than maxc, + * - TypeError if data is not a string or a list of strings, + * - Irccd.SystemError on other errors. + */ +auto Util_cut(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + const auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); + const auto maxl = limit(ctx, 2, "maxl", INT_MAX); + + if (list.size() > static_cast<std::size_t>(maxl)) + return 0; + + // Empty list but lines() returns at least one. + if (list.size() == 1 && list[0].empty()) { + duk_push_array(ctx); + return 1; + } + + return duk::push(ctx, list); + }); +} + +// }}} + +// {{{ Irccd.Util.format + +/* + * Function: Irccd.Util.format(text, parameters) + * -------------------------------------------------------- + * + * Format a string with templates. + * + * Arguments: + * - input, the text to update, + * - params, the parameters. + * Returns: + * The converted text. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Util_format(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, string_util::format(duk::get<std::string>(ctx, 0), subst(ctx, 1))); + }); +} + +// }}} + +// {{{ Irccd.Util.splituser + +/* + * Function: Irccd.Util.splituser(ident) + * -------------------------------------------------------- + * + * Return the nickname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The nickname. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Util_splituser(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, irc::user::parse(duk::require<std::string>(ctx, 0)).nick); + }); +} + +// }}} + +// {{{ Irccd.Util.splithost + +/* + * Function: Irccd.Util.splithost(ident) + * -------------------------------------------------------- + * + * Return the hostname part from a full username. + * + * Arguments: + * - ident, the full identity. + * Returns: + * The hostname. + * Throws: + * - Irccd.SystemError on errors. + */ +auto Util_splithost(duk_context* ctx) -> duk_ret_t +{ + return wrap(ctx, [&] { + return duk::push(ctx, irc::user::parse(duk::require<std::string>(ctx, 0)).host); + }); +} + +// }}} + +const duk_function_list_entry functions[] = { + { "cut", Util_cut, DUK_VARARGS }, + { "format", Util_format, DUK_VARARGS }, + { "splituser", Util_splituser, 1 }, + { "splithost", Util_splithost, 1 }, + { nullptr, nullptr, 0 } +}; + +} // !namespace + +auto util_js_api::get_name() const noexcept -> std::string_view +{ + return "Irccd.Util"; +} + +void util_js_api::load(irccd&, std::shared_ptr<js_plugin> plugin) +{ + duk::stack_guard sa(plugin->get_context()); + + duk_get_global_string(plugin->get_context(), "Irccd"); + duk_push_object(plugin->get_context()); + duk_put_function_list(plugin->get_context(), -1, functions); + duk_put_prop_string(plugin->get_context(), -2, "Util"); + duk_pop(plugin->get_context()); +} + +} // !irccd::js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-js/irccd/js/util_js_api.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,50 @@ +/* + * util_js_api.hpp -- Irccd.Util API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JS_UTIL_JS_API_HPP +#define IRCCD_JS_UTIL_JS_API_HPP + +/** + * \file util_js_api.hpp + * \brief Irccd.Util Javascript API. + */ + +#include "js_api.hpp" + +namespace irccd::js { + +/** + * \brief Irccd.Util Javascript API. + * \ingroup js_api + */ +class util_js_api : public js_api { +public: + /** + * \copydoc js_api::get_name + */ + auto get_name() const noexcept -> std::string_view override; + + /** + * \copydoc js_api::load + */ + void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; +}; + +} // !irccd::js + +#endif // !IRCCD_JS_UTIL_JS_API_HPP
--- a/libirccd-js/irccd/js/util_jsapi.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,352 +0,0 @@ -/* - * util_jsapi.cpp -- Irccd.Util API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <climits> - -#include <irccd/string_util.hpp> - -#include "duktape_vector.hpp" -#include "irccd_jsapi.hpp" -#include "js_plugin.hpp" -#include "util_jsapi.hpp" - -namespace irccd { - -namespace { - -// {{{ subst - -/* - * Read parameters for Irccd.Util.format function, the object is defined as - * following: - * - * { - * date: the date object - * flags: the flags (not implemented yet) - * field1: a field to substitute in #{} pattern - * field2: a field to substitute in #{} pattern - * fieldn: ... - * } - */ -string_util::subst subst(duk_context* ctx, int index) -{ - string_util::subst params; - - if (!duk_is_object(ctx, index)) - return params; - - duk_enum(ctx, index, 0); - - while (duk_next(ctx, -1, true)) { - if (dukx_get<std::string>(ctx, -2) == "date") - params.time = static_cast<time_t>(duk_get_number(ctx, -1) / 1000); - else - params.keywords.insert({ - dukx_get<std::string>(ctx, -2), - dukx_get<std::string>(ctx, -1) - }); - - duk_pop_n(ctx, 2); - } - - return params; -} - -// }}} - -// {{{ split - -/* - * split (for Irccd.Util.cut) - * ------------------------------------------------------------------ - * - * Extract individual tokens in array or a whole string as a std:::vector. - */ -std::vector<std::string> split(duk_context* ctx) -{ - duk_require_type_mask(ctx, 0, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_STRING); - - std::vector<std::string> result; - std::string pattern = " \t\n"; - - if (duk_is_string(ctx, 0)) - result = string_util::split(dukx_get<std::string>(ctx, 0), pattern); - else if (duk_is_array(ctx, 0)) { - duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY); - - while (duk_next(ctx, -1, 1)) { - // Split individual tokens as array if spaces are found. - const auto tmp = string_util::split(duk_to_string(ctx, -1), pattern); - - result.insert(result.end(), tmp.begin(), tmp.end()); - duk_pop_2(ctx); - } - } - - return result; -} - -// }}} - -// {{{ limit - -/* - * limit (for Irccd.Util.cut) - * ------------------------------------------------------------------ - * - * Get the maxl/maxc argument. - * - * The argument value is the default and also used as the result returned. - */ -int limit(duk_context* ctx, int index, const char* name, int value) -{ - if (duk_get_top(ctx) < index || !duk_is_number(ctx, index)) - return value; - - value = duk_to_int(ctx, index); - - if (value <= 0) - duk_error(ctx, DUK_ERR_RANGE_ERROR, "argument %d (%s) must be positive", index, name); - - return value; -} - -// }}} - -// {{{ lines - -/* - * lines (for Irccd.Util.cut) - * ------------------------------------------------------------------ - * - * Build a list of lines. - * - * Several cases possible: - * - * - s is the current line - * - abc is the token to add - * - * s = "" (new line) - * s -> "abc" - * - * s = "hello world" (enough room) - * s -> "hello world abc" - * - * s = "hello world" (not enough room: maxc is smaller) - * s+1 = "abc" - */ -std::vector<std::string> lines(duk_context* ctx, const std::vector<std::string>& tokens, int maxc) -{ - std::vector<std::string> result{""}; - - for (const auto& s : tokens) { - if (s.length() > static_cast<std::size_t>(maxc)) - duk_error(ctx, DUK_ERR_RANGE_ERROR, "word '%s' could not fit in maxc limit (%d)", s.c_str(), maxc); - - // Compute the length required (prepend a space if needed) - auto required = s.length() + (result.back().empty() ? 0 : 1); - - if (result.back().length() + required > static_cast<std::size_t>(maxc)) - result.push_back(s); - else { - if (!result.back().empty()) - result.back() += ' '; - - result.back() += s; - } - } - - return result; -} - -// }}} - -// {{{ wrap - -template <typename Handler> -duk_ret_t wrap(duk_context* ctx, Handler handler) -{ - try { - return handler(); - } catch (const std::system_error& ex) { - dukx_throw(ctx, ex); - } catch (const std::exception& ex) { - dukx_throw(ctx, ex); - } - - return 0; -} - -// }}} - -// {{{ Irccd.Util.cut - -/* - * Function: Irccd.Util.cut(data, maxc, maxl) - * -------------------------------------------------------- - * - * Cut a piece of data into several lines. - * - * The argument data is a string or a list of strings. In any case, all strings - * are first splitted by spaces and trimmed. This ensure that useless - * whitespaces are discarded. - * - * The argument maxc controls the maximum of characters allowed per line, it can - * be a positive integer. If undefined is given, a default of 72 is used. - * - * The argument maxl controls the maximum of lines allowed. It can be a positive - * integer or undefined for an infinite list. - * - * If maxl is used as a limit and the data can not fit within the bounds, - * undefined is returned. - * - * An empty list may be returned if empty strings were found. - * - * Arguments: - * - data, a string or an array of strings, - * - maxc, max number of colums (Optional, default: 72), - * - maxl, max number of lines (Optional, default: undefined). - * Returns: - * A list of strings ready to be sent or undefined if the data is too big. - * Throws: - * - RangeError if maxl or maxc are negative numbers, - * - RangeError if one word length was bigger than maxc, - * - TypeError if data is not a string or a list of strings, - * - Irccd.SystemError on other errors. - */ -duk_ret_t Util_cut(duk_context* ctx) -{ - return wrap(ctx, [&] { - const auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); - const auto maxl = limit(ctx, 2, "maxl", INT_MAX); - - if (list.size() > static_cast<std::size_t>(maxl)) - return 0; - - // Empty list but lines() returns at least one. - if (list.size() == 1 && list[0].empty()) { - duk_push_array(ctx); - return 1; - } - - return dukx_push(ctx, list); - }); -} - -// }}} - -// {{{ Irccd.Util.format - -/* - * Function: Irccd.Util.format(text, parameters) - * -------------------------------------------------------- - * - * Format a string with templates. - * - * Arguments: - * - input, the text to update, - * - params, the parameters. - * Returns: - * The converted text. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Util_format(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, string_util::format(dukx_get<std::string>(ctx, 0), subst(ctx, 1))); - }); -} - -// }}} - -// {{{ Irccd.Util.splituser - -/* - * Function: Irccd.Util.splituser(ident) - * -------------------------------------------------------- - * - * Return the nickname part from a full username. - * - * Arguments: - * - ident, the full identity. - * Returns: - * The nickname. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Util_splituser(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, irc::user::parse(dukx_require<std::string>(ctx, 0)).nick); - }); -} - -// }}} - -// {{{ Irccd.Util.splithost - -/* - * Function: Irccd.Util.splithost(ident) - * -------------------------------------------------------- - * - * Return the hostname part from a full username. - * - * Arguments: - * - ident, the full identity. - * Returns: - * The hostname. - * Throws: - * - Irccd.SystemError on errors. - */ -duk_ret_t Util_splithost(duk_context* ctx) -{ - return wrap(ctx, [&] { - return dukx_push(ctx, irc::user::parse(dukx_require<std::string>(ctx, 0)).host); - }); -} - -// }}} - -const duk_function_list_entry functions[] = { - { "cut", Util_cut, DUK_VARARGS }, - { "format", Util_format, DUK_VARARGS }, - { "splituser", Util_splituser, 1 }, - { "splithost", Util_splithost, 1 }, - { nullptr, nullptr, 0 } -}; - -} // !namespace - -std::string util_jsapi::get_name() const -{ - return "Irccd.Util"; -} - -void util_jsapi::load(irccd&, std::shared_ptr<js_plugin> plugin) -{ - dukx_stack_assert sa(plugin->get_context()); - - duk_get_global_string(plugin->get_context(), "Irccd"); - duk_push_object(plugin->get_context()); - duk_put_function_list(plugin->get_context(), -1, functions); - duk_put_prop_string(plugin->get_context(), -2, "Util"); - duk_pop(plugin->get_context()); -} - -} // !irccd
--- a/libirccd-js/irccd/js/util_jsapi.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * util_jsapi.hpp -- Irccd.Util API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_JS_UTIL_JSAPI_HPP -#define IRCCD_JS_UTIL_JSAPI_HPP - -/** - * \file util_jsapi.hpp - * \brief Irccd.Util Javascript API. - */ - -#include "jsapi.hpp" - -namespace irccd { - -/** - * \brief Irccd.Util Javascript API. - * \ingroup jsapi - */ -class util_jsapi : public jsapi { -public: - /** - * \copydoc jsapi::get_name - */ - std::string get_name() const override; - - /** - * \copydoc jsapi::load - */ - void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override; -}; - -} // !irccd - -#endif // !IRCCD_JS_UTIL_JSAPI_HPP
--- a/libirccd-test/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ b/libirccd-test/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -20,34 +20,28 @@ set( HEADERS - ${libirccd-test_SOURCE_DIR}/irccd/test/cli_test.hpp + ${libirccd-test_SOURCE_DIR}/irccd/test/cli_fixture.hpp ${libirccd-test_SOURCE_DIR}/irccd/test/command_fixture.hpp + ${libirccd-test_SOURCE_DIR}/irccd/test/debug_server.hpp ${libirccd-test_SOURCE_DIR}/irccd/test/irccd_fixture.hpp - ${libirccd-test_SOURCE_DIR}/irccd/test/debug_server.hpp ${libirccd-test_SOURCE_DIR}/irccd/test/mock.hpp ${libirccd-test_SOURCE_DIR}/irccd/test/mock_plugin.hpp ${libirccd-test_SOURCE_DIR}/irccd/test/mock_server.hpp - ${libirccd-test_SOURCE_DIR}/irccd/test/plugin_cli_test.hpp - ${libirccd-test_SOURCE_DIR}/irccd/test/rule_cli_test.hpp - ${libirccd-test_SOURCE_DIR}/irccd/test/server_cli_test.hpp + $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/js_fixture.hpp> $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/plugin_test.hpp> - $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/javascript_fixture.hpp> ) set( SOURCES + ${libirccd-test_SOURCE_DIR}/irccd/test/cli_fixture.cpp ${libirccd-test_SOURCE_DIR}/irccd/test/command_fixture.cpp + ${libirccd-test_SOURCE_DIR}/irccd/test/debug_server.cpp ${libirccd-test_SOURCE_DIR}/irccd/test/irccd_fixture.cpp - ${libirccd-test_SOURCE_DIR}/irccd/test/cli_test.cpp - ${libirccd-test_SOURCE_DIR}/irccd/test/debug_server.cpp ${libirccd-test_SOURCE_DIR}/irccd/test/mock.cpp ${libirccd-test_SOURCE_DIR}/irccd/test/mock_plugin.cpp ${libirccd-test_SOURCE_DIR}/irccd/test/mock_server.cpp - ${libirccd-test_SOURCE_DIR}/irccd/test/plugin_cli_test.cpp - ${libirccd-test_SOURCE_DIR}/irccd/test/rule_cli_test.cpp - ${libirccd-test_SOURCE_DIR}/irccd/test/server_cli_test.cpp + $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/js_fixture.cpp> $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/plugin_test.cpp> - $<$<BOOL:${IRCCD_HAVE_JS}>:${libirccd-test_SOURCE_DIR}/irccd/test/javascript_fixture.cpp> ) if (${IRCCD_HAVE_JS})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-test/irccd/test/cli_fixture.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,97 @@ +/* + * cli_fixture.cpp -- test fixture for irccdctl frontend + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <chrono> +#include <sstream> + +#include <boost/process.hpp> + +#include <irccd/string_util.hpp> +#include <irccd/socket_acceptor.hpp> + +#include <irccd/daemon/command.hpp> +#include <irccd/daemon/transport_service.hpp> +#include <irccd/daemon/transport_server.hpp> + +#include "cli_fixture.hpp" + +namespace proc = boost::process; + +namespace irccd::test { + +cli_fixture::cli_fixture() + : server_(new mock_server(irccd_.get_service(), "test", "localhost")) +{ + std::remove(CMAKE_BINARY_DIR "/tmp/irccd.sock"); + + io::local_acceptor::endpoint endpoint(CMAKE_BINARY_DIR "/tmp/irccd.sock"); + io::local_acceptor::acceptor acceptor(service_, std::move(endpoint)); + + for (const auto& f : command::registry) + irccd_.transports().get_commands().push_back(f()); + + irccd_.servers().add(server_); + irccd_.transports().add(std::make_unique<transport_server>( + std::make_unique<io::local_acceptor>(std::move(acceptor)))); + server_->clear(); +} + +cli_fixture::~cli_fixture() +{ + service_.stop(); + thread_.join(); +} + +void cli_fixture::start() +{ + thread_ = std::thread([this] { service_.run(); }); + + // Let irccd bind correctly. + std::this_thread::sleep_for(std::chrono::milliseconds(250)); +} + +auto cli_fixture::exec(const std::vector<std::string>& args) -> result +{ + static const std::string irccdctl = IRCCDCTL_EXECUTABLE; + static const std::string conf = CMAKE_BINARY_DIR "/tmp/irccdctl.conf"; + + std::ostringstream oss; + + oss << irccdctl << " -c " << conf << " "; + oss << string_util::join(args, " "); + + proc::ipstream stream_out, stream_err; + + const auto ret = proc::system( + oss.str(), + proc::std_in.close(), + proc::std_out > stream_out, + proc::std_err > stream_err + ); + + outputs out, err; + + for (std::string line; stream_out && std::getline(stream_out, line); ) + out.push_back(line); + for (std::string line; stream_err && std::getline(stream_err, line); ) + err.push_back(line); + + return { ret, out, err }; +} + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-test/irccd/test/cli_fixture.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,112 @@ +/* + * cli_fixture.hpp -- test fixture for irccdctl frontend + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_TEST_CLI_FIXTURE_HPP +#define IRCCD_TEST_CLI_FIXTURE_HPP + +/** + * \file cli_fixture.hpp + * \brief Test fixture for irccdctl frontend. + */ + +#include <thread> +#include <tuple> +#include <vector> + +#include <boost/asio.hpp> + +#include <irccd/daemon/irccd.hpp> +#include <irccd/daemon/plugin_service.hpp> +#include <irccd/daemon/rule_service.hpp> +#include <irccd/daemon/server_service.hpp> + +#include <irccd/test/mock_server.hpp> + +namespace irccd::test { + +/** + * \brief Test fixture for irccdctl frontend. + * + * This class will run irccd daemon in a thread when member function `start` is + * called. + * + * Before starting the daemon, the test can manually modify irccd instance + * through `irccd_` member variable. Once started, call `exec` with arguments + * you want to pass through irccdctl utility. + */ +class cli_fixture { +private: + using io_service = boost::asio::io_service; + + std::thread thread_; + io_service service_; + +protected: + /** + * Irccd instance. + * + * \warning Do not modify once `start()` has been called. + */ + irccd irccd_{service_}; + + /** + * Server automatically added as "test". + */ + std::shared_ptr<mock_server> server_; + +public: + /** + * Type for all lines printed. + */ + using outputs = std::vector<std::string>; + + /** + * Collection of output from stdout/stderr respectively. + */ + using result = std::tuple<int, outputs, outputs>; + + /** + * Construct and initialize and irccd daemon running in a thread. + */ + cli_fixture(); + + /** + * Stop irccd and close everything. + */ + ~cli_fixture(); + + /** + * Start irccd daemon. + * + * A thread will be running and closed when the destructor is called, you + * MUST not modify irccd while running. + */ + void start(); + + /** + * Execute irccdctl. + * + * \param args the arguments to irccdctl + * \return the stdout/stderr and exit code + */ + auto exec(const std::vector<std::string>& args) -> result; +}; + +} // !irccd::test + +#endif // !IRCCD_TEST_CLI_FIXTURE_HPP
--- a/libirccd-test/irccd/test/cli_test.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* - * cli_test.cpp -- test fixture for irccdctl frontend - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <chrono> -#include <sstream> - -#include <irccd/string_util.hpp> -#include <irccd/socket_acceptor.hpp> - -#include <irccd/daemon/transport_service.hpp> -#include <irccd/daemon/transport_server.hpp> - -#include "cli_test.hpp" - -namespace proc = boost::process; - -namespace irccd { - -cli_test::cli_test() -{ - std::remove(CMAKE_BINARY_DIR "/tmp/irccd.sock"); - - io::local_acceptor::endpoint endpoint(CMAKE_BINARY_DIR "/tmp/irccd.sock"); - io::local_acceptor::acceptor acceptor(service_, std::move(endpoint)); - - irccd_.transports().add(std::make_unique<transport_server>( - std::make_unique<io::local_acceptor>(std::move(acceptor)))); -} - -cli_test::~cli_test() -{ - service_.stop(); - thread_.join(); -} - -void cli_test::start() -{ - thread_ = std::thread([this] { service_.run(); }); - - // Let irccd bind correctly. - std::this_thread::sleep_for(std::chrono::milliseconds(250)); -} - -auto cli_test::exec(const std::vector<std::string>& args) -> result -{ - static const std::string irccdctl = IRCCDCTL_EXECUTABLE; - static const std::string conf = CMAKE_BINARY_DIR "/tmp/irccdctl.conf"; - - std::ostringstream oss; - - oss << irccdctl << " -c " << conf << " "; - oss << string_util::join(args, " "); - - proc::ipstream stream_out, stream_err; - - const auto ret = proc::system( - oss.str(), - proc::std_in.close(), - proc::std_out > stream_out, - proc::std_err > stream_err - ); - - outputs out, err; - - for (std::string line; stream_out && std::getline(stream_out, line); ) - out.push_back(line); - for (std::string line; stream_err && std::getline(stream_err, line); ) - err.push_back(line); - - return {ret, out, err}; -} - -} // !irccd
--- a/libirccd-test/irccd/test/cli_test.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/* - * cli_test.hpp -- test fixture for irccdctl frontend - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TEST_CLI_TEST_HPP -#define IRCCD_TEST_CLI_TEST_HPP - -/** - * \file cli_test.hpp - * \brief Test fixture for irccdctl frontend. - */ - -#include <thread> -#include <tuple> -#include <vector> - -#include <irccd/daemon/irccd.hpp> - -#include <boost/asio.hpp> -#include <boost/process.hpp> - -namespace irccd { - -/** - * \brief Test fixture for irccdctl frontend. - * - * This class will run irccd daemon in a thread when member function `start` is - * called. - * - * Before starting the daemon, the test can manually modify irccd instance - * through `irccd_` member variable. Once started, call `exec` with arguments - * you want to pass through irccdctl utility. - */ -class cli_test { -private: - using io_service = boost::asio::io_service; - - std::thread thread_; - io_service service_; - -protected: - /** - * Irccd instance. - * - * \warning Do not modify once `start()` has been called. - */ - irccd irccd_{service_}; - -public: - /** - * Type for all lines printed. - */ - using outputs = std::vector<std::string>; - - /** - * Collection of output from stdout/stderr respectively. - */ - using result = std::tuple<int, outputs, outputs>; - - /** - * Construct and initialize and irccd daemon running in a thread. - */ - cli_test(); - - /** - * Stop irccd and close everything. - */ - ~cli_test(); - - /** - * Start irccd daemon. - * - * A thread will be running and closed when the destructor is called, you - * MUST not modify irccd while running. - */ - void start(); - - /** - * Execute irccdctl. - * - * \param args the arguments to irccdctl - * \return the stdout/stderr and exit code - */ - auto exec(const std::vector<std::string>& args) -> result; -}; - -} // !irccd - -#endif // !IRCCD_TEST_CLI_TEST_HPP
--- a/libirccd-test/irccd/test/javascript_fixture.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - * javascript_fixture.cpp -- test fixture helper for Javascript modules - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "javascript_fixture.hpp" - -namespace irccd::test { - -javascript_fixture::javascript_fixture(const std::string& path) - : plugin_(new js_plugin("test", path)) -{ - for (const auto& f : jsapi::registry) - f()->load(irccd_, plugin_); - - if (!path.empty()) - plugin_->open(); -} - -} // !irccd::test
--- a/libirccd-test/irccd/test/javascript_fixture.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* - * javascript_fixture.hpp -- test fixture helper for Javascript modules - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TEST_JAVASCRIPT_FIXTURE_HPP -#define IRCCD_TEST_JAVASCRIPT_FIXTURE_HPP - -/** - * \file javascript_fixture.hpp - * \brief Test fixture helper for Javascript modules. - */ - -#include "irccd_fixture.hpp" - -#include <irccd/js/js_plugin.hpp> -#include <irccd/js/jsapi.hpp> - -#include <irccd/js/directory_jsapi.hpp> -#include <irccd/js/elapsed_timer_jsapi.hpp> -#include <irccd/js/file_jsapi.hpp> -#include <irccd/js/irccd_jsapi.hpp> -#include <irccd/js/jsapi.hpp> -#include <irccd/js/logger_jsapi.hpp> -#include <irccd/js/plugin_jsapi.hpp> -#include <irccd/js/server_jsapi.hpp> -#include <irccd/js/system_jsapi.hpp> -#include <irccd/js/timer_jsapi.hpp> -#include <irccd/js/unicode_jsapi.hpp> -#include <irccd/js/util_jsapi.hpp> - -namespace irccd::test { - -/** - * \brief Test fixture helper for Javascript modules. - */ -class javascript_fixture : public irccd_fixture { -protected: - /** - * \brief Javascript plugin. - */ - std::shared_ptr<js_plugin> plugin_; - - /** - * Constructor. - * - * Initialize a Javascript plugin with all Javascript API modules. - * - * \param path the path to a Javascript file if required - */ - javascript_fixture(const std::string& path = ""); -}; - -} // !irccd::test - -#endif // !IRCCD_TEST_JAVASCRIPT_FIXTURE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-test/irccd/test/js_fixture.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,33 @@ +/* + * js.cpp -- test fixture helper for Javascript modules + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "js_fixture.hpp" + +namespace irccd::test { + +js_fixture::js_fixture(const std::string& path) + : plugin_(new js::js_plugin("test", path)) +{ + for (const auto& f : js::js_api::registry) + f()->load(irccd_, plugin_); + + if (!path.empty()) + plugin_->open(); +} + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libirccd-test/irccd/test/js_fixture.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,58 @@ +/* + * js_fixture.hpp -- test fixture helper for Javascript modules + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_TEST_JS_FIXTURE_HPP +#define IRCCD_TEST_JS_FIXTURE_HPP + +/** + * \file js_fixture.hpp + * \brief Test fixture helper for Javascript modules. + */ + +#include "irccd_fixture.hpp" + +#include <irccd/js/js_plugin.hpp> +#include <irccd/js/js_api.hpp> + +#include <irccd/js/irccd_js_api.hpp> + +namespace irccd::test { + +/** + * \brief Test fixture helper for Javascript modules. + */ +class js_fixture : public irccd_fixture { +protected: + /** + * \brief Javascript plugin. + */ + std::shared_ptr<js::js_plugin> plugin_; + + /** + * Constructor. + * + * Initialize a Javascript plugin with all Javascript API modules. + * + * \param path the path to a Javascript file if required + */ + js_fixture(const std::string& path = ""); +}; + +} // !irccd::test + +#endif // !IRCCD_TEST_JS_FIXTURE_HPP
--- a/libirccd-test/irccd/test/plugin_cli_test.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * plugin_cli_test.cpp -- test fixture for irccdctl frontend (plugins support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/command.hpp> -#include <irccd/daemon/transport_service.hpp> - -#include "plugin_cli_test.hpp" - -namespace irccd { - -plugin_cli_test::plugin_cli_test() -{ - irccd_.transports().get_commands().push_back(std::make_unique<plugin_config_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<plugin_info_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<plugin_list_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<plugin_load_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<plugin_reload_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<plugin_unload_command>()); -} - -} // !irccd
--- a/libirccd-test/irccd/test/plugin_cli_test.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * plugin_cli_test.hpp -- test fixture for irccdctl frontend (plugins support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TEST_PLUGIN_CLI_TEST_HPP -#define IRCCD_TEST_PLUGIN_CLI_TEST_HPP - -/** - * \file plugin_cli_test.hpp - * \brief Test fixture for irccdctl frontend (plugins support). - */ - -#include <irccd/daemon/plugin.hpp> -#include <irccd/daemon/plugin_service.hpp> - -#include "cli_test.hpp" - -namespace irccd { - -/** - * \file plugin_cli_test.hpp - * - * This class adds all plugin related transport commands to irccd. - */ -class plugin_cli_test : public cli_test { -public: - /** - * Default constructor. - */ - plugin_cli_test(); -}; - -} // !irccd - -#endif // !IRCCD_TEST_PLUGIN_CLI_TEST_HPP
--- a/libirccd-test/irccd/test/plugin_test.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/libirccd-test/irccd/test/plugin_test.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -22,18 +22,18 @@ #include <irccd/daemon/plugin_service.hpp> #include <irccd/daemon/server_service.hpp> -#include <irccd/js/directory_jsapi.hpp> -#include <irccd/js/elapsed_timer_jsapi.hpp> -#include <irccd/js/file_jsapi.hpp> -#include <irccd/js/irccd_jsapi.hpp> +#include <irccd/js/directory_js_api.hpp> +#include <irccd/js/elapsed_timer_js_api.hpp> +#include <irccd/js/file_js_api.hpp> +#include <irccd/js/irccd_js_api.hpp> #include <irccd/js/js_plugin.hpp> -#include <irccd/js/logger_jsapi.hpp> -#include <irccd/js/plugin_jsapi.hpp> -#include <irccd/js/server_jsapi.hpp> -#include <irccd/js/system_jsapi.hpp> -#include <irccd/js/timer_jsapi.hpp> -#include <irccd/js/unicode_jsapi.hpp> -#include <irccd/js/util_jsapi.hpp> +#include <irccd/js/logger_js_api.hpp> +#include <irccd/js/plugin_js_api.hpp> +#include <irccd/js/server_js_api.hpp> +#include <irccd/js/system_js_api.hpp> +#include <irccd/js/timer_js_api.hpp> +#include <irccd/js/unicode_js_api.hpp> +#include <irccd/js/util_js_api.hpp> #include "plugin_test.hpp" @@ -42,7 +42,7 @@ plugin_test::plugin_test(std::string path) : server_(std::make_shared<mock_server>(service_, "test", "local")) { - plugin_ = std::make_unique<js_plugin>("test", std::move(path)); + plugin_ = std::make_unique<js::js_plugin>("test", std::move(path)); irccd_.set_log(std::make_unique<logger::silent_sink>()); irccd_.get_log().set_verbose(false); @@ -52,17 +52,8 @@ server_->set_nickname("irccd"); server_->clear(); - irccd_jsapi().load(irccd_, plugin_); - directory_jsapi().load(irccd_, plugin_); - elapsed_timer_jsapi().load(irccd_, plugin_); - file_jsapi().load(irccd_, plugin_); - logger_jsapi().load(irccd_, plugin_); - plugin_jsapi().load(irccd_, plugin_); - server_jsapi().load(irccd_, plugin_); - system_jsapi().load(irccd_, plugin_); - timer_jsapi().load(irccd_, plugin_); - unicode_jsapi().load(irccd_, plugin_); - util_jsapi().load(irccd_, plugin_); + for (const auto& f : js::js_api::registry) + f()->load(irccd_, plugin_); plugin_->open(); }
--- a/libirccd-test/irccd/test/plugin_test.hpp Mon Aug 06 21:27:00 2018 +0200 +++ b/libirccd-test/irccd/test/plugin_test.hpp Thu Aug 09 13:07:19 2018 +0200 @@ -41,7 +41,7 @@ protected: boost::asio::io_service service_; irccd irccd_{service_}; - std::shared_ptr<js_plugin> plugin_; + std::shared_ptr<js::js_plugin> plugin_; std::shared_ptr<mock_server> server_; public:
--- a/libirccd-test/irccd/test/rule_cli_test.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * rule_cli_test.cpp -- test fixture for irccdctl frontend (rule support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/command.hpp> -#include <irccd/daemon/transport_service.hpp> - -#include "rule_cli_test.hpp" - -namespace irccd { - -rule_cli_test::rule_cli_test() -{ - irccd_.transports().get_commands().push_back(std::make_unique<rule_add_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<rule_edit_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<rule_info_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<rule_list_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<rule_move_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<rule_remove_command>()); -} - -} // !irccd
--- a/libirccd-test/irccd/test/rule_cli_test.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * rule_cli_test.hpp -- test fixture for irccdctl frontend (rule support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TEST_RULE_CLI_TEST_HPP -#define IRCCD_TEST_RULE_CLI_TEST_HPP - -/** - * \file rule_cli_test.hpp - * \brief Test fixture for irccdctl frontend (rule support). - */ - -#include <irccd/daemon/rule.hpp> -#include <irccd/daemon/rule_service.hpp> - -#include "cli_test.hpp" - -namespace irccd { - -/** - * \file rule_cli_test.hpp - * - * This class adds all rule related transport commands to irccd. - */ -class rule_cli_test : public cli_test { -public: - /** - * Default constructor. - */ - rule_cli_test(); -}; - -} // !irccd - -#endif // !IRCCD_TEST_RULE_CLI_TEST_HPP
--- a/libirccd-test/irccd/test/server_cli_test.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * server_cli_test.cpp -- test fixture for irccdctl frontend (server support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <irccd/daemon/command.hpp> -#include <irccd/daemon/transport_service.hpp> - -#include "server_cli_test.hpp" - -namespace irccd { - -server_cli_test::server_cli_test() - : server_(new mock_server(irccd_.get_service(), "test", "localhost")) -{ - irccd_.servers().add(server_); - server_->clear(); - - irccd_.transports().get_commands().push_back(std::make_unique<server_connect_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_disconnect_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_info_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_invite_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_join_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_kick_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_list_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_me_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_message_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_mode_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_nick_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_notice_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_part_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_reconnect_command>()); - irccd_.transports().get_commands().push_back(std::make_unique<server_topic_command>()); -} - -} // !irccd
--- a/libirccd-test/irccd/test/server_cli_test.hpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * server_cli_test.hpp -- test fixture for irccdctl frontend (server support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TEST_SERVER_CLI_TEST_HPP -#define IRCCD_TEST_SERVER_CLI_TEST_HPP - -/** - * \file server_cli_test.hpp - * \brief Test fixture for irccdctl frontend (server support). - */ - -#include <irccd/daemon/server_service.hpp> - -#include <irccd/test/mock_server.hpp> - -#include "cli_test.hpp" - -namespace irccd { - -/** - * This class adds all server related transport commands to irccd and a unique - * mock server with id "test". - */ -class server_cli_test : public cli_test { -protected: - /** - * Server automatically added as "test". - */ - std::shared_ptr<mock_server> server_; - -public: - /** - * Default constructor. - */ - server_cli_test(); -}; - -} // !irccd - -#endif // !IRCCD_TEST_SERVER_CLI_TEST_HPP
--- a/tests/src/irccdctl/cli-plugin-config/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-plugin-config/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,16 +19,16 @@ #define BOOST_TEST_MODULE "irccdctl plugin-config" #include <boost/test/unit_test.hpp> -#include <irccd/test/plugin_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> #include <irccd/test/mock_plugin.hpp> -namespace irccd { +namespace irccd::test { namespace { -class configurable_plugin_cli_test : public plugin_cli_test { +class configurable_plugin_cli_fixture : public cli_fixture { public: - configurable_plugin_cli_test() + configurable_plugin_cli_fixture() { auto conf1 = std::make_unique<mock_plugin>("conf1"); auto conf2 = std::make_unique<mock_plugin>("conf2"); @@ -43,7 +43,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(plugin_config_suite, configurable_plugin_cli_test) +BOOST_FIXTURE_TEST_SUITE(plugin_config_suite, configurable_plugin_cli_fixture) BOOST_AUTO_TEST_CASE(set_and_get) { @@ -115,4 +115,4 @@ } // !namespace -} // !irccd +} // !irccd::test
--- a/tests/src/irccdctl/cli-plugin-info/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-plugin-info/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,14 +19,16 @@ #define BOOST_TEST_MODULE "irccdctl plugin-info" #include <boost/test/unit_test.hpp> -#include <irccd/test/plugin_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> #include <irccd/test/mock_plugin.hpp> +using namespace irccd::test; + namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(plugin_info_suite, plugin_cli_test) +BOOST_FIXTURE_TEST_SUITE(plugin_info_suite, cli_fixture) BOOST_AUTO_TEST_CASE(simple) {
--- a/tests/src/irccdctl/cli-plugin-list/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-plugin-list/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,14 +19,16 @@ #define BOOST_TEST_MODULE "irccdctl plugin-list" #include <boost/test/unit_test.hpp> -#include <irccd/test/plugin_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> #include <irccd/test/mock_plugin.hpp> +using namespace irccd::test; + namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(plugin_list_suite, plugin_cli_test) +BOOST_FIXTURE_TEST_SUITE(plugin_list_suite, cli_fixture) BOOST_AUTO_TEST_CASE(output) {
--- a/tests/src/irccdctl/cli-plugin-load/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-plugin-load/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,9 +19,11 @@ #define BOOST_TEST_MODULE "irccdctl plugin-load" #include <boost/test/unit_test.hpp> -#include <irccd/test/plugin_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> #include <irccd/test/mock_plugin.hpp> +using namespace irccd::test; + namespace irccd { namespace { @@ -46,7 +48,7 @@ } // !namespace -BOOST_FIXTURE_TEST_SUITE(plugin_load_suite, plugin_cli_test) +BOOST_FIXTURE_TEST_SUITE(plugin_load_suite, cli_fixture) BOOST_AUTO_TEST_CASE(simple) {
--- a/tests/src/irccdctl/cli-plugin-reload/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-plugin-reload/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,9 +19,11 @@ #define BOOST_TEST_MODULE "irccdctl plugin-reload" #include <boost/test/unit_test.hpp> -#include <irccd/test/plugin_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> #include <irccd/test/mock.hpp> +using namespace irccd::test; + namespace irccd { namespace { @@ -44,7 +46,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(plugin_reload_suite, plugin_cli_test) +BOOST_FIXTURE_TEST_SUITE(plugin_reload_suite, cli_fixture) BOOST_AUTO_TEST_CASE(simple) {
--- a/tests/src/irccdctl/cli-plugin-unload/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-plugin-unload/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,9 +19,11 @@ #define BOOST_TEST_MODULE "irccdctl plugin-unload" #include <boost/test/unit_test.hpp> -#include <irccd/test/plugin_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> #include <irccd/test/mock.hpp> +using namespace irccd::test; + namespace irccd { namespace { @@ -44,7 +46,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(plugin_unload_suite, plugin_cli_test) +BOOST_FIXTURE_TEST_SUITE(plugin_unload_suite, cli_fixture) BOOST_AUTO_TEST_CASE(simple) {
--- a/tests/src/irccdctl/cli-rule-add/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-rule-add/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl rule-add" #include <boost/test/unit_test.hpp> -#include <irccd/test/rule_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(rule_add_suite, rule_cli_test) +BOOST_FIXTURE_TEST_SUITE(rule_add_suite, cli_fixture) BOOST_AUTO_TEST_CASE(all) {
--- a/tests/src/irccdctl/cli-rule-edit/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-rule-edit/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,15 +19,17 @@ #define BOOST_TEST_MODULE "irccdctl rule-edit" #include <boost/test/unit_test.hpp> -#include <irccd/test/rule_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -class custom_rule_cli_test : public rule_cli_test { +class custom_cli_fixture : public cli_fixture { public: - custom_rule_cli_test() + custom_cli_fixture() { irccd_.rules().add({ { "s1", "s2" }, @@ -40,7 +42,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(rule_edit_suite, custom_rule_cli_test) +BOOST_FIXTURE_TEST_SUITE(rule_edit_suite, custom_cli_fixture) BOOST_AUTO_TEST_CASE(server) {
--- a/tests/src/irccdctl/cli-rule-info/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-rule-info/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl rule-info" #include <boost/test/unit_test.hpp> -#include <irccd/test/rule_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(rule_info_suite, rule_cli_test) +BOOST_FIXTURE_TEST_SUITE(rule_info_suite, cli_fixture) BOOST_AUTO_TEST_CASE(info) {
--- a/tests/src/irccdctl/cli-rule-list/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-rule-list/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl rule-list" #include <boost/test/unit_test.hpp> -#include <irccd/test/rule_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(rule_list_suite, rule_cli_test) +BOOST_FIXTURE_TEST_SUITE(rule_list_suite, cli_fixture) BOOST_AUTO_TEST_CASE(simple) {
--- a/tests/src/irccdctl/cli-rule-move/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-rule-move/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,15 +19,17 @@ #define BOOST_TEST_MODULE "irccdctl rule-move" #include <boost/test/unit_test.hpp> -#include <irccd/test/rule_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -class custom_rule_cli_test : public rule_cli_test { +class custom_cli_fixture : public cli_fixture { public: - custom_rule_cli_test() + custom_cli_fixture() { irccd_.rules().add({ { "s1" }, @@ -56,7 +58,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(rule_move_suite, custom_rule_cli_test) +BOOST_FIXTURE_TEST_SUITE(rule_move_suite, custom_cli_fixture) BOOST_AUTO_TEST_CASE(from_0_to_1) {
--- a/tests/src/irccdctl/cli-rule-remove/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-rule-remove/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,15 +19,17 @@ #define BOOST_TEST_MODULE "irccdctl rule-remove" #include <boost/test/unit_test.hpp> -#include <irccd/test/rule_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -class custom_rule_cli_test : public rule_cli_test { +class custom_cli_fixture : public cli_fixture { public: - custom_rule_cli_test() + custom_cli_fixture() { irccd_.rules().add({ { "s1" }, @@ -56,7 +58,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(rule_move_suite, custom_rule_cli_test) +BOOST_FIXTURE_TEST_SUITE(rule_move_suite, custom_cli_fixture) BOOST_AUTO_TEST_CASE(simple) {
--- a/tests/src/irccdctl/cli-server-disconnect/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-disconnect/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-disconnect" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_disconnect_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_disconnect_suite, cli_fixture) BOOST_AUTO_TEST_CASE(one) {
--- a/tests/src/irccdctl/cli-server-info/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-info/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-info" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_info_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_info_suite, cli_fixture) BOOST_AUTO_TEST_CASE(output) {
--- a/tests/src/irccdctl/cli-server-invite/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-invite/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-invite" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_invite_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_invite_suite, cli_fixture) BOOST_AUTO_TEST_CASE(output) {
--- a/tests/src/irccdctl/cli-server-join/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-join/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-join" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_join_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_join_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-kick/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-kick/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-kick" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_kick_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_kick_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-list/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-list/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-list" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_list_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_list_suite, cli_fixture) BOOST_AUTO_TEST_CASE(output) {
--- a/tests/src/irccdctl/cli-server-me/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-me/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-me" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_me_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_me_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-message/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-message/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-message" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_message_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_message_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-mode/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-mode/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-mode" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_mode_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_mode_suite, cli_fixture) BOOST_AUTO_TEST_CASE(user) {
--- a/tests/src/irccdctl/cli-server-nick/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-nick/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-nick" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_nick_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_nick_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-notice/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-notice/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-notice" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_notice_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_notice_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-part/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-part/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-part" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_part_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_part_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/irccdctl/cli-server-reconnect/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-reconnect/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-reconnect" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_reconnect_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_reconnect_suite, cli_fixture) BOOST_AUTO_TEST_CASE(one) {
--- a/tests/src/irccdctl/cli-server-topic/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/irccdctl/cli-server-topic/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -19,13 +19,15 @@ #define BOOST_TEST_MODULE "irccdctl server-topic" #include <boost/test/unit_test.hpp> -#include <irccd/test/server_cli_test.hpp> +#include <irccd/test/cli_fixture.hpp> + +using namespace irccd::test; namespace irccd { namespace { -BOOST_FIXTURE_TEST_SUITE(server_topic_suite, server_cli_test) +BOOST_FIXTURE_TEST_SUITE(server_topic_suite, cli_fixture) BOOST_AUTO_TEST_CASE(basic) {
--- a/tests/src/libirccd-js/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/libirccd-js/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -17,13 +17,12 @@ # add_subdirectory(js-plugin) - -add_subdirectory(jsapi-elapsedtimer) -add_subdirectory(jsapi-directory) -add_subdirectory(jsapi-file) -add_subdirectory(jsapi-irccd) -add_subdirectory(jsapi-logger) -add_subdirectory(jsapi-system) -add_subdirectory(jsapi-timer) -add_subdirectory(jsapi-unicode) -add_subdirectory(jsapi-util) +add_subdirectory(js-api-elapsedtimer) +add_subdirectory(js-api-directory) +add_subdirectory(js-api-file) +add_subdirectory(js-api-irccd) +add_subdirectory(js-api-logger) +add_subdirectory(js-api-system) +add_subdirectory(js-api-timer) +add_subdirectory(js-api-unicode) +add_subdirectory(js-api-util)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-directory/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-directory + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-directory/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,60 @@ +/* + * main.cpp -- test Irccd.Directory API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "Directory Javascript API" +#include <boost/test/unit_test.hpp> + +#include <irccd/test/js_fixture.hpp> + +namespace irccd::test { + +using namespace irccd::js; + +namespace { + +class directory_js_fixture : public js_fixture { +public: + directory_js_fixture() + { + duk::push(plugin_->get_context(), CMAKE_SOURCE_DIR); + duk_put_global_string(plugin_->get_context(), "CMAKE_SOURCE_DIR"); + } +}; + +BOOST_FIXTURE_TEST_SUITE(directory_js_api_suite, directory_js_fixture) + +BOOST_AUTO_TEST_CASE(constructor) +{ + const std::string script( + "d = new Irccd.Directory(CMAKE_SOURCE_DIR + \"/tests/root\");" + "p = d.path;" + "l = d.entries.length;" + ); + + if (duk_peval_string(plugin_->get_context(), script.c_str()) != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + duk_get_global_string(plugin_->get_context(), "l"); + BOOST_TEST(duk_get_int(plugin_->get_context(), -1) == 3); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-elapsedtimer/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-elapsedtimer + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-elapsedtimer/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,53 @@ +/* + * main.cpp -- test Irccd.ElapsedTimer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "ElapsedTimer Javascript API" +#include <boost/test/unit_test.hpp> + +#include <thread> + +#include <irccd/test/js_fixture.hpp> + +using namespace std::chrono_literals; + +namespace irccd::test { + +namespace { + +BOOST_FIXTURE_TEST_SUITE(elapsed_timer_js_api_suite, js_fixture) + +BOOST_AUTO_TEST_CASE(standard) +{ + if (duk_peval_string(plugin_->get_context(), "timer = new Irccd.ElapsedTimer();") != 0) + throw js::duk::get_stack(plugin_->get_context(), -1); + + std::this_thread::sleep_for(300ms); + + if (duk_peval_string(plugin_->get_context(), "result = timer.elapsed();") != 0) + throw js::duk::get_stack(plugin_->get_context(), -1); + + BOOST_REQUIRE(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_REQUIRE_GE(duk_get_int(plugin_->get_context(), -1), 250); + BOOST_REQUIRE_LE(duk_get_int(plugin_->get_context(), -1), 350); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-file/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-file + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-file/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,320 @@ +/* + * main.cpp -- test Irccd.File API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "File Javascript API" +#include <boost/test/unit_test.hpp> + +#include <fstream> + +#include <irccd/test/js_fixture.hpp> + +using namespace irccd::js; + +namespace irccd::test { + +namespace { + +class file_js_fixture : public js_fixture { +public: + file_js_fixture() + { + duk::push(plugin_->get_context(), CMAKE_SOURCE_DIR); + duk_put_global_string(plugin_->get_context(), "CMAKE_SOURCE_DIR"); + } +}; + +BOOST_FIXTURE_TEST_SUITE(file_js_api_suite, file_js_fixture) + +BOOST_AUTO_TEST_CASE(function_basename) +{ + if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.basename('/usr/local/etc/irccd.conf');")) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("irccd.conf" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(function_dirname) +{ + if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');")) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("/usr/local/etc" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(function_exists) +{ + if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.exists(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt')")) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(function_exists2) +{ + if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.exists('file_which_does_not_exist.txt')")) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(function_remove) +{ + // First create a dummy file + std::ofstream("test-js-fs.remove"); + + if (duk_peval_string(plugin_->get_context(), "Irccd.File.remove('test-js-fs.remove');") != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + std::ifstream in("test-js-fs.remove"); + + BOOST_TEST(!in.is_open()); +} + +BOOST_AUTO_TEST_CASE(method_basename) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "result = f.basename();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("file-1.txt" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_basename_closed) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.close();" + "result = f.basename();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("file-1.txt" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_dirname) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "result = f.dirname();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_dirname_closed) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.close();" + "result = f.dirname();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_lines) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "result = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r').lines();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + std::vector<std::string> expected{"a", "b", "c"}; + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(expected == duk::get<std::vector<std::string>>(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_seek1) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.seek(Irccd.File.SeekSet, 6);" + "result = f.read(1);" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(".", duk::get<std::string>(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_seek1_closed) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.close();" + "f.seek(Irccd.File.SeekSet, 4);" + "result = f.read(1);" + "result = typeof (result) === \"undefined\";" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_seek2) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.seek(Irccd.File.SeekSet, 2);" + "f.seek(Irccd.File.SeekCur, 4);" + "result = f.read(1);" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("." == duk::get<std::string>(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_seek2c_losed) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.close();" + "f.seek(Irccd.File.SeekSet, 2);" + "f.seek(Irccd.File.SeekCur, 2);" + "result = f.read(1);" + "result = typeof (result) === \"undefined\";" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_seek3) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.seek(Irccd.File.SeekEnd, -2);" + "result = f.read(1);" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("t" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_seek3_closed) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "f.close();" + "f.seek(Irccd.File.SeekEnd, -2);" + "result = f.read(1);" + "result = typeof (result) === \"undefined\";" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_read1) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" + "result = f.read();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST("file-1.txt\n" == duk_get_string(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_readline) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "result = [];" + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r');" + "for (var s; s = f.readline(); ) {" + " result.push(s);" + "}" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + std::vector<std::string> expected{"a", "b", "c"}; + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(expected == duk::get<std::vector<std::string>>(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(method_readline_closed) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "result = [];" + "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r');" + "f.close();" + "for (var s; s = f.readline(); ) {" + " result.push(s);" + "}" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + std::vector<std::string> expected; + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(expected == duk::get<std::vector<std::string>>(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-irccd/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-irccd + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-irccd/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,118 @@ +/* + * main.cpp -- test Irccd API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "Irccd Javascript API" +#include <boost/test/unit_test.hpp> + +#include <irccd/test/js_fixture.hpp> + +using namespace irccd::js; + +namespace irccd::test { + +namespace { + +BOOST_FIXTURE_TEST_SUITE(irccd_js_api_suite, js_fixture) + +BOOST_AUTO_TEST_CASE(version) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "major = Irccd.version.major;" + "minor = Irccd.version.minor;" + "patch = Irccd.version.patch;" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "major")); + BOOST_TEST(IRCCD_VERSION_MAJOR == duk_get_int(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "minor")); + BOOST_TEST(IRCCD_VERSION_MINOR == duk_get_int(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "patch")); + BOOST_TEST(IRCCD_VERSION_PATCH == duk_get_int(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(from_javascript) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "try {" + " throw new Irccd.SystemError(1, 'test');" + "} catch (e) {" + " errno = e.errno;" + " name = e.name;" + " message = e.message;" + " v1 = (e instanceof Error);" + " v2 = (e instanceof Irccd.SystemError);" + "}" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "errno")); + BOOST_TEST(1 == duk_get_int(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); + BOOST_TEST("SystemError" == duk_get_string(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); + BOOST_TEST("test" == duk_get_string(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v1")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v2")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(from_native) +{ + duk_push_c_function(plugin_->get_context(), [] (duk_context *ctx) -> duk_ret_t { + duk::raise(ctx, std::system_error(make_error_code(std::errc::invalid_argument))); + + return 0; + }, 0); + + duk_put_global_string(plugin_->get_context(), "f"); + + const auto ret = duk_peval_string(plugin_->get_context(), + "try {" + " f();" + "} catch (e) {" + " errno = e.errno;" + " name = e.name;" + " v1 = (e instanceof Error);" + " v2 = (e instanceof Irccd.SystemError);" + "}" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "errno")); + BOOST_TEST(EINVAL == duk_get_int(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); + BOOST_TEST("SystemError" == duk_get_string(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v1")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v2")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-logger/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-logger + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-logger/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,105 @@ +/* + * main.cpp -- test Irccd.Logger API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "Logger Javascript API" +#include <boost/test/unit_test.hpp> + +#include <irccd/daemon/logger.hpp> + +#include <irccd/test/js_fixture.hpp> + +namespace irccd::test { + +using namespace irccd::js; + +namespace { + +class logger_fixture : public js_fixture { +protected: + std::string line_info; + std::string line_warning; + std::string line_debug; + + class sample_sink : public logger::sink { + private: + logger_fixture& test_; + + public: + sample_sink(logger_fixture& test) noexcept + : test_(test) + { + } + + void write_info(const std::string& line) override + { + test_.line_info = line; + } + + void write_warning(const std::string& line) override + { + test_.line_warning = line; + } + + void write_debug(const std::string& line) override + { + test_.line_debug = line; + } + }; + + logger_fixture() + { + irccd_.set_log(std::make_unique<sample_sink>(*this)); + irccd_.get_log().set_verbose(true); + } +}; + +BOOST_FIXTURE_TEST_SUITE(logger_js_api_suite, logger_fixture) + +BOOST_AUTO_TEST_CASE(info) +{ + if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.info(\"hello!\");") != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST("plugin test: hello!" == line_info); +} + +BOOST_AUTO_TEST_CASE(warning) +{ + if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.warning(\"FAIL!\");") != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST("plugin test: FAIL!" == line_warning); +} + +#if !defined(NDEBUG) + +BOOST_AUTO_TEST_CASE(debug) +{ + if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.debug(\"starting\");") != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST("plugin test: starting" == line_debug); +} + +#endif + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-system/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,24 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-system + SOURCES main.cpp + LIBRARIES libirccd-js + FLAGS IRCCD_EXECUTABLE=\"$<TARGET_FILE:irccd>\" +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-system/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,64 @@ +/* + * main.cpp -- test Irccd.System API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "System Javascript API" +#include <boost/test/unit_test.hpp> + +#include <irccd/system.hpp> + +#include <irccd/test/js_fixture.hpp> + +namespace irccd::test { + +using namespace irccd::js; + +namespace { + +BOOST_FIXTURE_TEST_SUITE(system_js_api_suite, js_fixture) + +BOOST_AUTO_TEST_CASE(home) +{ + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.System.home();"); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(),"result")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == sys::home()); +} + +#if defined(HAVE_POPEN) + +BOOST_AUTO_TEST_CASE(popen) +{ + auto ret = duk_peval_string(plugin_->get_context(), + "f = Irccd.System.popen(\"" IRCCD_EXECUTABLE " --version\", \"r\");" + "r = f.readline();" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "r")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == IRCCD_VERSION); +} + +#endif + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-timer/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-timer + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-timer/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,85 @@ +/* + * main.cpp -- test Irccd.Timer API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "Timer Javascript API" +#include <boost/test/unit_test.hpp> +#include <boost/timer/timer.hpp> + +#include <irccd/test/js_fixture.hpp> + +namespace irccd::test { + +namespace { + +class js_timer_fixture : public js_fixture { +public: + js_timer_fixture() + : js_fixture(CMAKE_CURRENT_SOURCE_DIR "/timer.js") + { + } + + void set_type(const std::string& name) + { + duk_get_global_string(plugin_->get_context(), "Irccd"); + duk_get_prop_string(plugin_->get_context(), -1, "Timer"); + duk_get_prop_string(plugin_->get_context(), -1, name.c_str()); + duk_put_global_string(plugin_->get_context(), "type"); + duk_pop_n(plugin_->get_context(), 2); + + plugin_->open(); + plugin_->handle_load(irccd_); + } +}; + +BOOST_FIXTURE_TEST_SUITE(js_timer_api_suite, js_timer_fixture) + +BOOST_AUTO_TEST_CASE(single) +{ + boost::timer::cpu_timer timer; + + set_type("Single"); + + while (timer.elapsed().wall / 1000000LL < 3000) { + ctx_.reset(); + ctx_.poll(); + } + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "count")); + BOOST_TEST(duk_get_int(plugin_->get_context(), -1) == 1); +} + +BOOST_AUTO_TEST_CASE(repeat) +{ + boost::timer::cpu_timer timer; + + set_type("Repeat"); + + while (timer.elapsed().wall / 1000000LL < 3000) { + ctx_.reset(); + ctx_.poll(); + } + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "count")); + BOOST_TEST(duk_get_int(plugin_->get_context(), -1) >= 5); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-timer/timer.js Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,13 @@ +count = 0; + +function onLoad() +{ + if (typeof (type) === "undefined") + throw Error("global timer type not defined"); + + t = new Irccd.Timer(type, 50, function () { + count += 1; + }); + + t.start(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-unicode/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-unicode + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-unicode/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,71 @@ +/* + * main.cpp -- test Irccd.Unicode API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * /!\ Be sure that this file is kept saved in UTF-8 /!\ + */ + +#define BOOST_TEST_MODULE "Unicode Javascript API" +#include <boost/test/unit_test.hpp> + +#include <irccd/test/js_fixture.hpp> + +namespace irccd::test { + +namespace { + +BOOST_FIXTURE_TEST_SUITE(unicode_js_api_suite, js_fixture) + +BOOST_AUTO_TEST_CASE(is_letter) +{ + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); + + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(is_lower) +{ + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLower(String('é').charCodeAt(0));"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); + + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLower(String('É').charCodeAt(0));"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(is_upper) +{ + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); + + duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-util/CMakeLists.txt Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME js-api-util + SOURCES main.cpp + LIBRARIES libirccd-js +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-js/js-api-util/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -0,0 +1,264 @@ +/* + * main.cpp -- test Irccd.Util API + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "Unicode Javascript API" +#include <boost/test/unit_test.hpp> + +#include <irccd/test/js_fixture.hpp> + +namespace irccd::test { + +using namespace irccd::js; + +namespace { + +BOOST_FIXTURE_TEST_SUITE(util_js_api_suite, js_fixture) + +/* + * Irccd.Util misc. + * ------------------------------------------------------------------ + */ + +BOOST_AUTO_TEST_CASE(format_simple) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "result = Irccd.Util.format(\"#{target}\", { target: \"markand\" })" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "markand"); +} + +BOOST_AUTO_TEST_CASE(splituser) +{ + if (duk_peval_string(plugin_->get_context(), "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "user"); +} + +BOOST_AUTO_TEST_CASE(splithost) +{ + if (duk_peval_string(plugin_->get_context(), "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "~user@hyper/super/host"); +} + +/* + * Irccd.Util.cut. + * ------------------------------------------------------------------ + */ + +BOOST_AUTO_TEST_CASE(cut_string_simple) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut('hello world');\n" + "line0 = lines[0];\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello world"); +} + +BOOST_AUTO_TEST_CASE(cut_string_double) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut('hello world', 5);\n" + "line0 = lines[0];\n" + "line1 = lines[1];\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); +} + +BOOST_AUTO_TEST_CASE(cut_string_dirty) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut(' hello world ', 5);\n" + "line0 = lines[0];\n" + "line1 = lines[1];\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); +} + +BOOST_AUTO_TEST_CASE(cut_string_too_much_lines) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut('abc def ghi jkl', 3, 3);\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "lines")); + BOOST_TEST(duk_is_undefined(plugin_->get_context(), -1)); +} + +BOOST_AUTO_TEST_CASE(cut_string_token_too_big) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "try {\n" + " lines = Irccd.Util.cut('hello world', 3);\n" + "} catch (e) {\n" + " name = e.name;\n" + " message = e.message;\n" + "}\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "word 'hello' could not fit in maxc limit (3)"); +} + +BOOST_AUTO_TEST_CASE(cut_string_negative_maxc) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "try {\n" + " lines = Irccd.Util.cut('hello world', -3);\n" + "} catch (e) {\n" + " name = e.name;\n" + " message = e.message;\n" + "}\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "argument 1 (maxc) must be positive"); +} + +BOOST_AUTO_TEST_CASE(cut_string_negative_maxl) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "try {\n" + " lines = Irccd.Util.cut('hello world', undefined, -1);\n" + "} catch (e) {\n" + " name = e.name;\n" + " message = e.message;\n" + "}\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "argument 2 (maxl) must be positive"); +} + +BOOST_AUTO_TEST_CASE(cut_array_simple) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut([ 'hello', 'world' ]);\n" + "line0 = lines[0];\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello world"); +} + +BOOST_AUTO_TEST_CASE(cut_array_double) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut([ 'hello', 'world' ], 5);\n" + "line0 = lines[0];\n" + "line1 = lines[1];\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); +} + +BOOST_AUTO_TEST_CASE(cut_array_dirty) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "lines = Irccd.Util.cut([ ' ', ' hello ', ' world ', ' '], 5);\n" + "line0 = lines[0];\n" + "line1 = lines[1];\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); +} + +BOOST_AUTO_TEST_CASE(cut_invalid_data) +{ + const auto ret = duk_peval_string(plugin_->get_context(), + "try {\n" + " lines = Irccd.Util.cut(123);\n" + "} catch (e) {\n" + " name = e.name;\n" + " message = e.message;\n" + "}\n" + ); + + if (ret != 0) + throw duk::get_stack(plugin_->get_context(), -1); + + BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); + BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "TypeError"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // !namespace + +} // !irccd::test
--- a/tests/src/libirccd-js/js-plugin/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ b/tests/src/libirccd-js/js-plugin/main.cpp Thu Aug 09 13:07:19 2018 +0200 @@ -17,38 +17,35 @@ */ #define BOOST_TEST_MODULE "Javascript plugin object" -#include <boost/asio.hpp> #include <boost/test/unit_test.hpp> -#include <irccd/daemon/irccd.hpp> #include <irccd/daemon/plugin_service.hpp> -#include <irccd/js/irccd_jsapi.hpp> +#include <irccd/js/js_api.hpp> #include <irccd/js/js_plugin.hpp> -#include <irccd/js/plugin_jsapi.hpp> -namespace irccd { +#include <irccd/test/irccd_fixture.hpp> + +namespace irccd::test { namespace { -class js_plugin_test { +class js_plugin_fixture : public irccd_fixture { protected: - boost::asio::io_service service_; - irccd irccd_{service_}; - std::shared_ptr<js_plugin> plugin_; + std::shared_ptr<js::js_plugin> plugin_; - void load(std::string path) + void load(const std::string& path) { - plugin_ = std::make_unique<js_plugin>("test", std::move(path)); + plugin_ = std::make_unique<js::js_plugin>("test", path); - irccd_jsapi().load(irccd_, plugin_); - plugin_jsapi().load(irccd_, plugin_); + for (const auto& f : js::js_api::registry) + f()->load(irccd_, plugin_); plugin_->open(); } }; -BOOST_FIXTURE_TEST_SUITE(js_plugin_test_suite, js_plugin_test) +BOOST_FIXTURE_TEST_SUITE(js_plugin_suite, js_plugin_fixture) BOOST_AUTO_TEST_CASE(assign) { @@ -97,20 +94,18 @@ BOOST_AUTO_TEST_SUITE_END() -class js_plugin_loader_test { +class js_plugin_loader_fixture : public irccd_fixture { protected: - boost::asio::io_service service_; - irccd irccd_{service_}; std::shared_ptr<plugin> plugin_; - js_plugin_loader_test() + js_plugin_loader_fixture() { irccd_.set_config(config(CMAKE_CURRENT_SOURCE_DIR "/irccd.conf")); - auto loader = std::make_unique<js_plugin_loader>(irccd_); + auto loader = std::make_unique<js::js_plugin_loader>(irccd_); - loader->get_modules().push_back(std::make_unique<irccd_jsapi>()); - loader->get_modules().push_back(std::make_unique<plugin_jsapi>()); + for (const auto& f : js::js_api::registry) + loader->get_modules().push_back(f()); irccd_.plugins().add_loader(std::move(loader)); } @@ -122,7 +117,7 @@ } }; -BOOST_FIXTURE_TEST_SUITE(js_plugin_loader_test_suite, js_plugin_loader_test) +BOOST_FIXTURE_TEST_SUITE(js_plugin_loader_test_suite, js_plugin_loader_fixture) BOOST_AUTO_TEST_CASE(assign) { @@ -155,4 +150,4 @@ } // !namespace -} // !irccd +} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-directory/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-directory - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-directory/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * main.cpp -- test Irccd.Directory API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "Directory Javascript API" -#include <boost/test/unit_test.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -class directory_javascript_fixture : public javascript_fixture { -public: - directory_javascript_fixture() - { - dukx_push(plugin_->get_context(), CMAKE_SOURCE_DIR); - duk_put_global_string(plugin_->get_context(), "CMAKE_SOURCE_DIR"); - } -}; - -BOOST_FIXTURE_TEST_SUITE(directory_jsapi_suite, directory_javascript_fixture) - -BOOST_AUTO_TEST_CASE(constructor) -{ - const std::string script( - "d = new Irccd.Directory(CMAKE_SOURCE_DIR + \"/tests/root\");" - "p = d.path;" - "l = d.entries.length;" - ); - - if (duk_peval_string(plugin_->get_context(), script.c_str()) != 0) - throw dukx_stack(plugin_->get_context(), -1); - - duk_get_global_string(plugin_->get_context(), "l"); - BOOST_TEST(duk_get_int(plugin_->get_context(), -1) == 3); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-elapsedtimer/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-elapsedtimer - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-elapsedtimer/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* - * main.cpp -- test Irccd.ElapsedTimer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "ElapsedTimer Javascript API" -#include <boost/test/unit_test.hpp> - -#include <thread> - -#include <irccd/test/javascript_fixture.hpp> - -using namespace std::chrono_literals; - -namespace irccd::test { - -namespace { - -BOOST_FIXTURE_TEST_SUITE(elapsed_timer_jsapi_suite, javascript_fixture) - -BOOST_AUTO_TEST_CASE(standard) -{ - if (duk_peval_string(plugin_->get_context(), "timer = new Irccd.ElapsedTimer();") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - std::this_thread::sleep_for(300ms); - - if (duk_peval_string(plugin_->get_context(), "result = timer.elapsed();") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_REQUIRE(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_REQUIRE_GE(duk_get_int(plugin_->get_context(), -1), 250); - BOOST_REQUIRE_LE(duk_get_int(plugin_->get_context(), -1), 350); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-file/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-file - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-file/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,320 +0,0 @@ -/* - * main.cpp -- test Irccd.File API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "File Javascript API" -#include <boost/test/unit_test.hpp> - -#include <fstream> - -#include <irccd/js/duktape_vector.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -class file_javascript_fixture : public javascript_fixture { -public: - file_javascript_fixture() - { - dukx_push(plugin_->get_context(), CMAKE_SOURCE_DIR); - duk_put_global_string(plugin_->get_context(), "CMAKE_SOURCE_DIR"); - } -}; - -BOOST_FIXTURE_TEST_SUITE(file_jsapi_suite, file_javascript_fixture) - -BOOST_AUTO_TEST_CASE(function_basename) -{ - if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.basename('/usr/local/etc/irccd.conf');")) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("irccd.conf" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(function_dirname) -{ - if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.dirname('/usr/local/etc/irccd.conf');")) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("/usr/local/etc" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(function_exists) -{ - if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.exists(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt')")) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(function_exists2) -{ - if (duk_peval_string(plugin_->get_context(), "result = Irccd.File.exists('file_which_does_not_exist.txt')")) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(function_remove) -{ - // First create a dummy file - std::ofstream("test-js-fs.remove"); - - if (duk_peval_string(plugin_->get_context(), "Irccd.File.remove('test-js-fs.remove');") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - std::ifstream in("test-js-fs.remove"); - - BOOST_TEST(!in.is_open()); -} - -BOOST_AUTO_TEST_CASE(method_basename) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "result = f.basename();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("file-1.txt" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_basename_closed) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.close();" - "result = f.basename();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("file-1.txt" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_dirname) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "result = f.dirname();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_dirname_closed) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.close();" - "result = f.dirname();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(CMAKE_SOURCE_DIR "/tests/root" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_lines) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "result = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r').lines();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - std::vector<std::string> expected{"a", "b", "c"}; - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_seek1) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.seek(Irccd.File.SeekSet, 6);" - "result = f.read(1);" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(".", dukx_get<std::string>(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_seek1_closed) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.close();" - "f.seek(Irccd.File.SeekSet, 4);" - "result = f.read(1);" - "result = typeof (result) === \"undefined\";" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_seek2) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.seek(Irccd.File.SeekSet, 2);" - "f.seek(Irccd.File.SeekCur, 4);" - "result = f.read(1);" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("." == dukx_get<std::string>(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_seek2c_losed) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.close();" - "f.seek(Irccd.File.SeekSet, 2);" - "f.seek(Irccd.File.SeekCur, 2);" - "result = f.read(1);" - "result = typeof (result) === \"undefined\";" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_seek3) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.seek(Irccd.File.SeekEnd, -2);" - "result = f.read(1);" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("t" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_seek3_closed) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "f.close();" - "f.seek(Irccd.File.SeekEnd, -2);" - "result = f.read(1);" - "result = typeof (result) === \"undefined\";" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_read1) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/file-1.txt', 'r');" - "result = f.read();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST("file-1.txt\n" == duk_get_string(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_readline) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "result = [];" - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r');" - "for (var s; s = f.readline(); ) {" - " result.push(s);" - "}" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - std::vector<std::string> expected{"a", "b", "c"}; - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(method_readline_closed) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "result = [];" - "f = new Irccd.File(CMAKE_SOURCE_DIR + '/tests/root/lines.txt', 'r');" - "f.close();" - "for (var s; s = f.readline(); ) {" - " result.push(s);" - "}" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - std::vector<std::string> expected; - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(expected == dukx_get<std::vector<std::string>>(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-irccd/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-irccd - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-irccd/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * main.cpp -- test Irccd API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "Irccd Javascript API" -#include <boost/test/unit_test.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -BOOST_FIXTURE_TEST_SUITE(irccd_jsapi_suite, javascript_fixture) - -BOOST_AUTO_TEST_CASE(version) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "major = Irccd.version.major;" - "minor = Irccd.version.minor;" - "patch = Irccd.version.patch;" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "major")); - BOOST_TEST(IRCCD_VERSION_MAJOR == duk_get_int(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "minor")); - BOOST_TEST(IRCCD_VERSION_MINOR == duk_get_int(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "patch")); - BOOST_TEST(IRCCD_VERSION_PATCH == duk_get_int(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(from_javascript) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "try {" - " throw new Irccd.SystemError(1, 'test');" - "} catch (e) {" - " errno = e.errno;" - " name = e.name;" - " message = e.message;" - " v1 = (e instanceof Error);" - " v2 = (e instanceof Irccd.SystemError);" - "}" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "errno")); - BOOST_TEST(1 == duk_get_int(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); - BOOST_TEST("SystemError" == duk_get_string(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); - BOOST_TEST("test" == duk_get_string(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v1")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v2")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(from_native) -{ - duk_push_c_function(plugin_->get_context(), [] (duk_context *ctx) -> duk_ret_t { - dukx_throw(ctx, std::system_error(make_error_code(std::errc::invalid_argument))); - - return 0; - }, 0); - - duk_put_global_string(plugin_->get_context(), "f"); - - const auto ret = duk_peval_string(plugin_->get_context(), - "try {" - " f();" - "} catch (e) {" - " errno = e.errno;" - " name = e.name;" - " v1 = (e instanceof Error);" - " v2 = (e instanceof Irccd.SystemError);" - "}" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "errno")); - BOOST_TEST(EINVAL == duk_get_int(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); - BOOST_TEST("SystemError" == duk_get_string(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v1")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "v2")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-logger/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-logger - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-logger/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/* - * main.cpp -- test Irccd.Logger API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "Logger Javascript API" -#include <boost/test/unit_test.hpp> - -#include <irccd/daemon/logger.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -class logger_fixture : public javascript_fixture { -protected: - std::string line_info; - std::string line_warning; - std::string line_debug; - - class sample_sink : public logger::sink { - private: - logger_fixture& test_; - - public: - sample_sink(logger_fixture& test) noexcept - : test_(test) - { - } - - void write_info(const std::string& line) override - { - test_.line_info = line; - } - - void write_warning(const std::string& line) override - { - test_.line_warning = line; - } - - void write_debug(const std::string& line) override - { - test_.line_debug = line; - } - }; - - logger_fixture() - { - irccd_.set_log(std::make_unique<sample_sink>(*this)); - irccd_.get_log().set_verbose(true); - } -}; - -BOOST_FIXTURE_TEST_SUITE(logger_jsapi_suite, logger_fixture) - -BOOST_AUTO_TEST_CASE(info) -{ - if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.info(\"hello!\");") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST("plugin test: hello!" == line_info); -} - -BOOST_AUTO_TEST_CASE(warning) -{ - if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.warning(\"FAIL!\");") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST("plugin test: FAIL!" == line_warning); -} - -#if !defined(NDEBUG) - -BOOST_AUTO_TEST_CASE(debug) -{ - if (duk_peval_string(plugin_->get_context(), "Irccd.Logger.debug(\"starting\");") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST("plugin test: starting" == line_debug); -} - -#endif - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-system/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-system - SOURCES main.cpp - LIBRARIES libirccd-js - FLAGS IRCCD_EXECUTABLE=\"$<TARGET_FILE:irccd>\" -)
--- a/tests/src/libirccd-js/jsapi-system/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * main.cpp -- test Irccd.System API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "System Javascript API" -#include <boost/test/unit_test.hpp> - -#include <irccd/system.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -BOOST_FIXTURE_TEST_SUITE(system_jsapi_suite, javascript_fixture) - -BOOST_AUTO_TEST_CASE(home) -{ - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.System.home();"); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(),"result")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == sys::home()); -} - -#if defined(HAVE_POPEN) - -BOOST_AUTO_TEST_CASE(popen) -{ - auto ret = duk_peval_string(plugin_->get_context(), - "f = Irccd.System.popen(\"" IRCCD_EXECUTABLE " --version\", \"r\");" - "r = f.readline();" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "r")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == IRCCD_VERSION); -} - -#endif - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-timer/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-timer - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-timer/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * main.cpp -- test Irccd.Timer API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "Timer Javascript API" -#include <boost/test/unit_test.hpp> -#include <boost/timer/timer.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -class javascript_timer_fixture : public javascript_fixture { -public: - javascript_timer_fixture() - : javascript_fixture(CMAKE_CURRENT_SOURCE_DIR "/timer.js") - { - } - - void set_type(const std::string& name) - { - duk_get_global_string(plugin_->get_context(), "Irccd"); - duk_get_prop_string(plugin_->get_context(), -1, "Timer"); - duk_get_prop_string(plugin_->get_context(), -1, name.c_str()); - duk_put_global_string(plugin_->get_context(), "type"); - duk_pop_n(plugin_->get_context(), 2); - - plugin_->open(); - plugin_->handle_load(irccd_); - } -}; - -BOOST_FIXTURE_TEST_SUITE(js_timer_test_suite, javascript_timer_fixture) - -BOOST_AUTO_TEST_CASE(single) -{ - boost::timer::cpu_timer timer; - - set_type("Single"); - - while (timer.elapsed().wall / 1000000LL < 3000) { - ctx_.reset(); - ctx_.poll(); - } - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "count")); - BOOST_TEST(duk_get_int(plugin_->get_context(), -1) == 1); -} - -BOOST_AUTO_TEST_CASE(repeat) -{ - boost::timer::cpu_timer timer; - - set_type("Repeat"); - - while (timer.elapsed().wall / 1000000LL < 3000) { - ctx_.reset(); - ctx_.poll(); - } - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "count")); - BOOST_TEST(duk_get_int(plugin_->get_context(), -1) >= 5); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-timer/timer.js Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -count = 0; - -function onLoad() -{ - if (typeof (type) === "undefined") - throw Error("global timer type not defined"); - - t = new Irccd.Timer(type, 50, function () { - count += 1; - }); - - t.start(); -}
--- a/tests/src/libirccd-js/jsapi-unicode/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-unicode - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-unicode/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * main.cpp -- test Irccd.Unicode API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * /!\ Be sure that this file is kept saved in UTF-8 /!\ - */ - -#define BOOST_TEST_MODULE "Unicode Javascript API" -#include <boost/test/unit_test.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -BOOST_FIXTURE_TEST_SUITE(unicode_jsapi_suite, javascript_fixture) - -BOOST_AUTO_TEST_CASE(is_letter) -{ - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLetter(String('é').charCodeAt(0));"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); - - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLetter(String('€').charCodeAt(0));"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(is_lower) -{ - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLower(String('é').charCodeAt(0));"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); - - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isLower(String('É').charCodeAt(0));"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(is_upper) -{ - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isUpper(String('É').charCodeAt(0));"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_boolean(plugin_->get_context(), -1)); - - duk_peval_string_noresult(plugin_->get_context(), "result = Irccd.Unicode.isUpper(String('é').charCodeAt(0));"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(!duk_get_boolean(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test
--- a/tests/src/libirccd-js/jsapi-util/CMakeLists.txt Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME jsapi-util - SOURCES main.cpp - LIBRARIES libirccd-js -)
--- a/tests/src/libirccd-js/jsapi-util/main.cpp Mon Aug 06 21:27:00 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,262 +0,0 @@ -/* - * main.cpp -- test Irccd.Util API - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "Unicode Javascript API" -#include <boost/test/unit_test.hpp> - -#include <irccd/test/javascript_fixture.hpp> - -namespace irccd::test { - -namespace { - -BOOST_FIXTURE_TEST_SUITE(util_jsapi_suite, javascript_fixture) - -/* - * Irccd.Util misc. - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_CASE(format_simple) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "result = Irccd.Util.format(\"#{target}\", { target: \"markand\" })" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "markand"); -} - -BOOST_AUTO_TEST_CASE(splituser) -{ - if (duk_peval_string(plugin_->get_context(), "result = Irccd.Util.splituser(\"user!~user@hyper/super/host\");") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "user"); -} - -BOOST_AUTO_TEST_CASE(splithost) -{ - if (duk_peval_string(plugin_->get_context(), "result = Irccd.Util.splithost(\"user!~user@hyper/super/host\");") != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "result")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "~user@hyper/super/host"); -} - -/* - * Irccd.Util.cut. - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_CASE(cut_string_simple) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut('hello world');\n" - "line0 = lines[0];\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello world"); -} - -BOOST_AUTO_TEST_CASE(cut_string_double) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut('hello world', 5);\n" - "line0 = lines[0];\n" - "line1 = lines[1];\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); -} - -BOOST_AUTO_TEST_CASE(cut_string_dirty) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut(' hello world ', 5);\n" - "line0 = lines[0];\n" - "line1 = lines[1];\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); -} - -BOOST_AUTO_TEST_CASE(cut_string_too_much_lines) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut('abc def ghi jkl', 3, 3);\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "lines")); - BOOST_TEST(duk_is_undefined(plugin_->get_context(), -1)); -} - -BOOST_AUTO_TEST_CASE(cut_string_token_too_big) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "try {\n" - " lines = Irccd.Util.cut('hello world', 3);\n" - "} catch (e) {\n" - " name = e.name;\n" - " message = e.message;\n" - "}\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "word 'hello' could not fit in maxc limit (3)"); -} - -BOOST_AUTO_TEST_CASE(cut_string_negative_maxc) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "try {\n" - " lines = Irccd.Util.cut('hello world', -3);\n" - "} catch (e) {\n" - " name = e.name;\n" - " message = e.message;\n" - "}\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "argument 1 (maxc) must be positive"); -} - -BOOST_AUTO_TEST_CASE(cut_string_negative_maxl) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "try {\n" - " lines = Irccd.Util.cut('hello world', undefined, -1);\n" - "} catch (e) {\n" - " name = e.name;\n" - " message = e.message;\n" - "}\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "RangeError"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "message")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "argument 2 (maxl) must be positive"); -} - -BOOST_AUTO_TEST_CASE(cut_array_simple) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut([ 'hello', 'world' ]);\n" - "line0 = lines[0];\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello world"); -} - -BOOST_AUTO_TEST_CASE(cut_array_double) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut([ 'hello', 'world' ], 5);\n" - "line0 = lines[0];\n" - "line1 = lines[1];\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); -} - -BOOST_AUTO_TEST_CASE(cut_array_dirty) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "lines = Irccd.Util.cut([ ' ', ' hello ', ' world ', ' '], 5);\n" - "line0 = lines[0];\n" - "line1 = lines[1];\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line0")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "hello"); - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "line1")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "world"); -} - -BOOST_AUTO_TEST_CASE(cut_invalid_data) -{ - const auto ret = duk_peval_string(plugin_->get_context(), - "try {\n" - " lines = Irccd.Util.cut(123);\n" - "} catch (e) {\n" - " name = e.name;\n" - " message = e.message;\n" - "}\n" - ); - - if (ret != 0) - throw dukx_stack(plugin_->get_context(), -1); - - BOOST_TEST(duk_get_global_string(plugin_->get_context(), "name")); - BOOST_TEST(duk_get_string(plugin_->get_context(), -1) == "TypeError"); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !namespace - -} // !irccd::test