Mercurial > irccd
view libirccd-js/irccd/js_util_module.cpp @ 521:e03521cf207b
Common: split util.hpp into more appropriate files, closes #721
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 27 Oct 2017 21:45:32 +0200 |
parents | 7e273b7f4f92 |
children |
line wrap: on
line source
/* * js_util_module.cpp -- Irccd.Util API * * Copyright (c) 2013-2017 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 <libircclient.h> #include <irccd/string_util.hpp> #include "js_util_module.hpp" #include "js_plugin.hpp" namespace irccd { namespace { /* * 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 get_subst(duk_context* ctx, int index) { string_util::subst params; if (!duk_is_object(ctx, index)) return params; dukx_enumerate(ctx, index, 0, true, [&] (auto) { 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)}); }); return params; } /* * 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. 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 (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 (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; } /* * 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. */ duk_ret_t cut(duk_context* ctx) { auto list = lines(ctx, split(ctx), limit(ctx, 1, "maxc", 72)); 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; } dukx_push_array(ctx, list, dukx_push_std_string); return 1; } /* * Function: Irccd.Util.format(text, parameters) * -------------------------------------------------------- * * Format a string with templates. * * Arguments: * - input, the text to update, * - params, the parameters. * Returns: * The converted text. */ duk_ret_t format(duk_context* ctx) { try { dukx_push_std_string(ctx, string_util::format(dukx_get_std_string(ctx, 0), get_subst(ctx, 1))); } catch (const std::exception &ex) { dukx_throw(ctx, SyntaxError(ex.what())); } return 1; } /* * Function: Irccd.Util.splituser(ident) * -------------------------------------------------------- * * Return the nickname part from a full username. * * Arguments: * - ident, the full identity. * Returns: * The nickname. */ duk_ret_t splituser(duk_context* ctx) { auto target = duk_require_string(ctx, 0); char nick[32] = {0}; irc_target_get_nick(target, nick, sizeof (nick) -1); duk_push_string(ctx, nick); return 1; } /* * Function: Irccd.Util.splithost(ident) * -------------------------------------------------------- * * Return the hostname part from a full username. * * Arguments: * - ident, the full identity. * Returns: * The hostname. */ duk_ret_t splithost(duk_context* ctx) { auto target = duk_require_string(ctx, 0); char host[32] = {0}; irc_target_get_host(target, host, sizeof (host) -1); duk_push_string(ctx, host); return 1; } const duk_function_list_entry functions[] = { { "cut", cut, DUK_VARARGS }, { "format", format, DUK_VARARGS }, { "splituser", splituser, 1 }, { "splithost", splithost, 1 }, { nullptr, nullptr, 0 } }; } // !namespace js_util_module::js_util_module() noexcept : module("Irccd.Util") { } void js_util_module::load(irccd&, std::shared_ptr<js_plugin> plugin) { StackAssert sa(plugin->context()); duk_get_global_string(plugin->context(), "Irccd"); duk_push_object(plugin->context()); duk_put_function_list(plugin->context(), -1, functions); duk_put_prop_string(plugin->context(), -2, "Util"); duk_pop(plugin->context()); } } // !irccd