changeset 139:f94e42e8bf1c

Irccd: brand new format section, #410
author David Demelier <markand@malikania.fr>
date Thu, 19 May 2016 18:47:12 +0200
parents ff26bd33a45d
children 5cde682ce9db
files lib/irccd/config.cpp lib/irccd/config.hpp lib/irccd/mod-plugin.cpp lib/irccd/plugin-js.cpp lib/irccd/plugin-js.hpp lib/irccd/plugin.hpp lib/irccd/service-plugin.cpp lib/irccd/service-plugin.hpp
diffstat 8 files changed, 316 insertions(+), 217 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/config.cpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/config.cpp	Thu May 19 18:47:12 2016 +0200
@@ -489,6 +489,23 @@
 	return PluginConfig();
 }
 
+PluginFormats Config::findPluginFormats(const std::string &name) const
+{
+	assert(util::isIdentifierValid(name));
+
+	auto section = m_document.find(std::string("format.") + name);
+
+	if (section == m_document.end())
+		return PluginFormats();
+
+	PluginFormats formats;
+
+	for (const auto &opt : *section)
+		formats.emplace(opt.key(), opt.value());
+
+	return formats;
+}
+
 bool Config::isVerbose() const noexcept
 {
 	return util::isBoolean(get(m_document, "logs", "verbose"));
@@ -622,6 +639,7 @@
 			}
 
 			irccd.pluginService().configure(option.key(), findPluginConfig(option.key()));
+			irccd.pluginService().setFormats(option.key(), findPluginFormats(option.key()));
 			irccd.pluginService().load(option.key(), option.value());
 		}
 	}
--- a/lib/irccd/config.hpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/config.hpp	Thu May 19 18:47:12 2016 +0200
@@ -95,6 +95,14 @@
 	PluginConfig findPluginConfig(const std::string &name) const;
 
 	/**
+	 * Find plugin formats if defined.
+	 *
+	 * \pre util::isValidIdentifier(name)
+	 * \return the formats or empty one if not found
+	 */
+	PluginFormats findPluginFormats(const std::string &name) const;
+
+	/**
 	 * Get the path to the pidfile.
 	 *
 	 * \return the path or empty if not defined
--- a/lib/irccd/mod-plugin.cpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/mod-plugin.cpp	Thu May 19 18:47:12 2016 +0200
@@ -193,6 +193,8 @@
 	duk::push(plugin.context(), functions);
 	duk::push(plugin.context(), duk::Object{});
 	duk::putProperty(plugin.context(), -2, "config");
+	duk::push(plugin.context(), duk::Object{});
+	duk::putProperty(plugin.context(), -2, "format");
 	duk::putProperty(plugin.context(), -2, "Plugin");
 	duk::pop(plugin.context());
 }
--- a/lib/irccd/plugin-js.cpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/plugin-js.cpp	Thu May 19 18:47:12 2016 +0200
@@ -38,11 +38,11 @@
 {
 	duk::getGlobal<void>(m_context, name);
 
-	if (duk::type(m_context, -1) == DUK_TYPE_UNDEFINED) {
-		/* Function not defined, remove the undefined value and all arguments */
+	if (duk::type(m_context, -1) == DUK_TYPE_UNDEFINED)
+		// Function not defined, remove the undefined value and all arguments.
 		duk::pop(m_context, nargs + 1);
-	} else {
-		/* Call the function and discard the result */
+	else {
+		// Call the function and discard the result.
 		duk::insert(m_context, -nargs - 1);
 
 		if (duk::pcall(m_context, nargs) != 0) {
@@ -51,12 +51,17 @@
 			duk::pop(m_context);
 
 			throw error;
-		} else {
+		} else
 			duk::pop(m_context);
-		}
 	}
 }
 
+void JsPlugin::putModules(Irccd &irccd)
+{
+	for (const auto &module : irccd.moduleService().modules())
+		module->load(irccd, *this);
+}
+
 void JsPlugin::putVars()
 {
 	duk::StackAssert sa(m_context);
@@ -84,9 +89,8 @@
 	}
 
 	// Use the system as default.
-	if (!found) {
+	if (!found)
 		foundpath = path::clean(path::get(type, path::OwnerSystem) + append);
-	}
 
 	duk::getGlobal<void>(m_context, "Irccd");
 	duk::getProperty<void>(m_context, -1, "Plugin");
@@ -98,26 +102,35 @@
 {
 	duk::StackAssert sa(m_context);
 
-	// TODO: override dataPath, configPath, cachePath
+	// TODO: override dataPath, configPath, cachePath.
+	// TODO: verify more that these values still exist.
 
-	/* Store plugin configuration into Irccd.Plugin.config */
+	// Store plugin configuration into Irccd.Plugin.config.
 	duk::getGlobal<void>(m_context, "Irccd");
 	duk::getProperty<void>(m_context, -1, "Plugin");
 	duk::getProperty<void>(m_context, -1, "config");
 
-	if (duk::type(m_context, -1) != DUK_TYPE_OBJECT) {
-		duk::pop(m_context);
-		duk::push(m_context, duk::Object{});
-	}
-
-	for (const auto &pair : config) {
+	for (const auto &pair : config)
 		duk::putProperty(m_context, -1, pair.first, pair.second);
-	}
 
 	duk::putProperty(m_context, -2, "config");
 	duk::pop(m_context, 2);
 }
 
+void JsPlugin::putFormats()
+{
+	duk::StackAssert sa(m_context);
+
+	duk::getGlobal<void>(m_context, "Irccd");
+	duk::getProperty<void>(m_context, -1, "Plugin");
+	duk::getProperty<void>(m_context, -1, "format");
+
+	for (const auto &pair : formats())
+		duk::putProperty(m_context, -1, pair.first, pair.second);
+
+	duk::pop(m_context, 3);
+}
+
 JsPlugin::JsPlugin(std::string name, std::string path, const PluginConfig &config)
 	: Plugin(name, path, config)
 {
@@ -215,13 +228,6 @@
 	call("onKick", 5);
 }
 
-void JsPlugin::putModules(Irccd &irccd)
-{
-	for (const auto &module : irccd.moduleService().modules()) {
-		module->load(irccd, *this);
-	}
-}
-
 void JsPlugin::onLoad(Irccd &irccd)
 {
 	duk::StackAssert sa(m_context);
@@ -248,16 +254,16 @@
 	putPath("configPath", "plugin/" + name(), path::PathConfig);
 	putPath("cachePath", "plugin/" + name(), path::PathCache);
 
-	/* Try to load the file (does not call onLoad yet) */
-	if (duk::pevalFile(m_context, path()) != 0) {
+	// Try to load the file (does not call onLoad yet).
+	if (duk::pevalFile(m_context, path()) != 0)
 		throw duk::error(m_context, -1);
-	}
 
 	duk::pop(m_context);
 
 	putConfig(config());
+	putFormats();
 
-	/* Read metadata */
+	// Read metadata .
 	duk::getGlobal<void>(m_context, "info");
 
 	if (duk::type(m_context, -1) == DUK_TYPE_OBJECT) {
@@ -411,9 +417,8 @@
 
 	call("onUnload");
 
-	for (const auto &module : irccd.moduleService().modules()) {
+	for (const auto &module : irccd.moduleService().modules())
 		module->unload(irccd, *this);
-	}
 }
 
 void JsPlugin::onWhois(Irccd &, const std::shared_ptr<Server> &server, const ServerWhois &whois)
--- a/lib/irccd/plugin-js.hpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/plugin-js.hpp	Thu May 19 18:47:12 2016 +0200
@@ -50,6 +50,7 @@
 	void putVars();
 	void putPath(const std::string &varname, const std::string &append, path::Path type);
 	void putConfig(const PluginConfig &config);
+	void putFormats();
 
 public:
 	/**
@@ -61,11 +62,6 @@
 	 */
 	JsPlugin(std::string name, std::string path, const PluginConfig &config = PluginConfig());
 
-	~JsPlugin()
-	{
-		puts("~JsPlugin");
-	}
-
 	/**
 	 * Access the Duktape context.
 	 *
--- a/lib/irccd/plugin.hpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/plugin.hpp	Thu May 19 18:47:12 2016 +0200
@@ -38,11 +38,16 @@
 class ServerWhois;
 
 /**
- * Configuration map extract from config file.
+ * \brief Configuration map extract from config file.
  */
 using PluginConfig = std::unordered_map<std::string, std::string>;
 
 /**
+ * \brief Formats for plugins.
+ */
+using PluginFormats = std::unordered_map<std::string, std::string>;
+
+/**
  * \class Plugin
  * \brief JavaScript plugin
  *
@@ -61,7 +66,9 @@
 	std::string m_summary{"unknown"};
 	std::string m_version{"unknown"};
 
+	// Configuration and formats.
 	PluginConfig m_config;
+	PluginFormats m_formats;
 
 public:
 	/**
@@ -206,6 +213,36 @@
 	}
 
 	/**
+	 * Access the plugin formats.
+	 *
+	 * \return the format
+	 */
+	inline const PluginFormats &formats() const noexcept
+	{
+		return m_formats;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the formats
+	 */
+	inline PluginFormats &formats() noexcept
+	{
+		return m_formats;
+	}
+
+	/**
+	 * Set the formats.
+	 *
+	 * \param formats the formats
+	 */
+	inline void setFormats(PluginFormats formats) noexcept
+	{
+		m_formats = std::move(formats);
+	}
+
+	/**
 	 * On channel message. This event will call onMessage or
 	 * onCommand if the messages starts with the command character
 	 * plus the plugin name.
--- a/lib/irccd/service-plugin.cpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/service-plugin.cpp	Thu May 19 18:47:12 2016 +0200
@@ -1,181 +1,197 @@
-/*
- * service-plugin.cpp -- manage plugins
- *
- * Copyright (c) 2013-2016 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 <functional>
-#include <regex>
-#include <stdexcept>
-
-#include <format.h>
-
-#include "fs.hpp"
-#include "irccd.hpp"
-#include "logger.hpp"
-#include "plugin-dynlib.hpp"
-#include "plugin-js.hpp"
-#include "service-plugin.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-namespace {
-
-std::shared_ptr<Plugin> find(std::unordered_map<std::string, PluginConfig> &configs, std::string name)
-{
-	PluginConfig config = configs[name];
-
-	for (const auto &path : path::list(path::PathPlugins)) {
-		std::string jspath = path + name + ".js";
-		std::string dynlibpath = path + name + DYNLIB_SUFFIX;
-
-		if (fs::isReadable(jspath))
-			return std::make_shared<JsPlugin>(std::move(name), std::move(jspath), std::move(config));
-		if (fs::isReadable(dynlibpath))
-			return std::make_shared<DynlibPlugin>(std::move(name), std::move(dynlibpath));
-	}
-
-	throw std::runtime_error("no suitable plugin found");
-}
-
-std::shared_ptr<Plugin> open(std::string name, std::string path)
-{
-	std::regex regex(".*(\\..*)$");
-	std::smatch match;
-	std::shared_ptr<Plugin> plugin;
-
-	if (std::regex_match(path, match, regex)) {
-		std::string extension = match[1];
-
-		if (extension == DYNLIB_SUFFIX)
-			plugin = std::make_shared<DynlibPlugin>(name, path);
-		else
-			plugin = std::make_shared<JsPlugin>(name, path);
-	} else
-		throw std::runtime_error("could not deduce plugin type from {}"_format(path));
-
-	return plugin;
-}
-
-} // !namespace
-
-PluginService::PluginService(Irccd &irccd) noexcept
-	: m_irccd(irccd)
-{
-}
-
-PluginService::~PluginService()
-{
-	for (const auto &plugin : m_plugins)
-		plugin->onUnload(m_irccd);
-}
-
-bool PluginService::has(const std::string &name) const noexcept
-{
-	return std::count_if(m_plugins.cbegin(), m_plugins.cend(), [&] (const auto &plugin) {
-		return plugin->name() == name;
-	}) > 0;
-}
-
-std::shared_ptr<Plugin> PluginService::get(const std::string &name) const noexcept
-{
-	auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
-		return plugin->name() == name;
-	});
-
-	if (it == m_plugins.end())
-		return nullptr;
-
-	return *it;
-}
-
-std::shared_ptr<Plugin> PluginService::require(const std::string &name) const
-{
-	auto plugin = get(name);
-
-	if (!plugin)
-		throw std::invalid_argument("plugin {} not found"_format(name));
-
-	return plugin;
-}
-
-void PluginService::add(std::shared_ptr<Plugin> plugin)
-{
-	m_plugins.push_back(std::move(plugin));
-}
-
-void PluginService::configure(const std::string &name, PluginConfig config)
-{
-	m_config.emplace(name, std::move(config));
-}
-
-PluginConfig PluginService::config(const std::string &name) const
-{
-	auto it = m_config.find(name);
-
-	if (it != m_config.end())
-		return it->second;
-
-	return PluginConfig();
-}
-
-void PluginService::load(std::string name, std::string path)
-{
-	auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
-		return plugin->name() == name;
-	});
-
-	if (it != m_plugins.end())
-		return;
-
-	try {
-		std::shared_ptr<Plugin> plugin;
-
-		if (path.empty())
-			plugin = find(m_config, std::move(name));
-		else
-			plugin = open(std::move(name), std::move(path));
-
-		plugin->onLoad(m_irccd);
-		add(std::move(plugin));
-	} catch (const std::exception &ex) {
-		log::warning("plugin {}: {}"_format(ex.what()));
-	}
-}
-
-void PluginService::reload(const std::string &name)
-{
-	auto plugin = get(name);
-
-	if (plugin)
-		plugin->onReload(m_irccd);
-}
-
-void PluginService::unload(const std::string &name)
-{
-	auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
-		return plugin->name() == name;
-	});
-
-	if (it != m_plugins.end()) {
-		(*it)->onUnload(m_irccd);
-		m_plugins.erase(it);
-	}
-}
-
-} // !irccd
+/*
+ * service-plugin.cpp -- manage plugins
+ *
+ * Copyright (c) 2013-2016 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 <functional>
+#include <regex>
+#include <stdexcept>
+
+#include <format.h>
+
+#include "fs.hpp"
+#include "irccd.hpp"
+#include "logger.hpp"
+#include "plugin-dynlib.hpp"
+#include "plugin-js.hpp"
+#include "service-plugin.hpp"
+
+using namespace fmt::literals;
+
+namespace irccd {
+
+namespace {
+
+std::shared_ptr<Plugin> find(std::unordered_map<std::string, PluginConfig> &configs, std::string name)
+{
+	PluginConfig config = configs[name];
+
+	for (const auto &path : path::list(path::PathPlugins)) {
+		std::string jspath = path + name + ".js";
+		std::string dynlibpath = path + name + DYNLIB_SUFFIX;
+
+		if (fs::isReadable(jspath))
+			return std::make_shared<JsPlugin>(std::move(name), std::move(jspath), std::move(config));
+		if (fs::isReadable(dynlibpath))
+			return std::make_shared<DynlibPlugin>(std::move(name), std::move(dynlibpath));
+	}
+
+	throw std::runtime_error("no suitable plugin found");
+}
+
+std::shared_ptr<Plugin> open(std::string name, std::string path)
+{
+	std::regex regex(".*(\\..*)$");
+	std::smatch match;
+	std::shared_ptr<Plugin> plugin;
+
+	if (std::regex_match(path, match, regex)) {
+		std::string extension = match[1];
+
+		if (extension == DYNLIB_SUFFIX)
+			plugin = std::make_shared<DynlibPlugin>(name, path);
+		else
+			plugin = std::make_shared<JsPlugin>(name, path);
+	} else
+		throw std::runtime_error("could not deduce plugin type from {}"_format(path));
+
+	return plugin;
+}
+
+} // !namespace
+
+PluginService::PluginService(Irccd &irccd) noexcept
+	: m_irccd(irccd)
+{
+}
+
+PluginService::~PluginService()
+{
+	for (const auto &plugin : m_plugins)
+		plugin->onUnload(m_irccd);
+}
+
+bool PluginService::has(const std::string &name) const noexcept
+{
+	return std::count_if(m_plugins.cbegin(), m_plugins.cend(), [&] (const auto &plugin) {
+		return plugin->name() == name;
+	}) > 0;
+}
+
+std::shared_ptr<Plugin> PluginService::get(const std::string &name) const noexcept
+{
+	auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
+		return plugin->name() == name;
+	});
+
+	if (it == m_plugins.end())
+		return nullptr;
+
+	return *it;
+}
+
+std::shared_ptr<Plugin> PluginService::require(const std::string &name) const
+{
+	auto plugin = get(name);
+
+	if (!plugin)
+		throw std::invalid_argument("plugin {} not found"_format(name));
+
+	return plugin;
+}
+
+void PluginService::add(std::shared_ptr<Plugin> plugin)
+{
+	m_plugins.push_back(std::move(plugin));
+}
+
+void PluginService::configure(const std::string &name, PluginConfig config)
+{
+	m_config.emplace(name, std::move(config));
+}
+
+PluginConfig PluginService::config(const std::string &name) const
+{
+	auto it = m_config.find(name);
+
+	if (it != m_config.end())
+		return it->second;
+
+	return PluginConfig();
+}
+
+void PluginService::setFormats(const std::string &name, PluginFormats formats)
+{
+	m_formats.emplace(name, std::move(formats));
+}
+
+PluginFormats PluginService::formats(const std::string &name) const
+{
+	auto it = m_formats.find(name);
+
+	if (it != m_formats.end())
+		return it->second;
+
+	return PluginFormats();
+}
+
+void PluginService::load(std::string name, std::string path)
+{
+	auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
+		return plugin->name() == name;
+	});
+
+	if (it != m_plugins.end())
+		return;
+
+	try {
+		std::shared_ptr<Plugin> plugin;
+
+		if (path.empty())
+			plugin = find(m_config, std::move(name));
+		else
+			plugin = open(std::move(name), std::move(path));
+
+		plugin->setFormats(m_formats[plugin->name()]);
+		plugin->onLoad(m_irccd);
+		add(std::move(plugin));
+	} catch (const std::exception &ex) {
+		log::warning("plugin {}: {}"_format(ex.what()));
+	}
+}
+
+void PluginService::reload(const std::string &name)
+{
+	auto plugin = get(name);
+
+	if (plugin)
+		plugin->onReload(m_irccd);
+}
+
+void PluginService::unload(const std::string &name)
+{
+	auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) {
+		return plugin->name() == name;
+	});
+
+	if (it != m_plugins.end()) {
+		(*it)->onUnload(m_irccd);
+		m_plugins.erase(it);
+	}
+}
+
+} // !irccd
--- a/lib/irccd/service-plugin.hpp	Thu May 19 13:00:00 2016 +0200
+++ b/lib/irccd/service-plugin.hpp	Thu May 19 18:47:12 2016 +0200
@@ -42,6 +42,7 @@
 	Irccd &m_irccd;
 	std::vector<std::shared_ptr<Plugin>> m_plugins;
 	std::unordered_map<std::string, PluginConfig> m_config;
+	std::unordered_map<std::string, PluginFormats> m_formats;
 
 public:
 	/**
@@ -119,6 +120,22 @@
 	PluginConfig config(const std::string &name) const;
 
 	/**
+	 * Add formatting for a plugin.
+	 *
+	 * \param name the plugin name
+	 * \param formats the formats
+	 */
+	void setFormats(const std::string &name, PluginFormats formats);
+
+	/**
+	 * Get formats for a plugin.
+	 *
+	 * \param name the plugin name
+	 * \return the formats
+	 */
+	PluginFormats formats(const std::string &name) const;
+
+	/**
 	 * Convenient wrapper that loads a plugin, call onLoad and add it to the registry.
 	 *
 	 * Any errors are printed using logger.