Loading src/scord-ctl/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ add_executable(scord-ctl) target_sources( scord-ctl PRIVATE scord_ctl.cpp rpc_server.cpp rpc_server.hpp ${CMAKE_CURRENT_BINARY_DIR}/defaults.hpp config_file.hpp config_file.cpp command.hpp command.cpp ) configure_file(defaults.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/defaults.hpp @ONLY) Loading src/scord-ctl/command.cpp 0 → 100644 +121 −0 Original line number Diff line number Diff line #include <ranges> #include <regex> #include <cassert> #include "command.hpp" namespace scord_ctl { void environment::set(const std::string& key, const std::string& value) { m_env[key] = value; } std::string environment::get(const std::string& key) const { return m_env.count(key) == 0 ? std::string{} : m_env.at(key); } std::vector<std::string> environment::as_vector() const { std::vector<std::string> tmp; tmp.reserve(m_env.size()); for(const auto& [key, value] : m_env) { tmp.emplace_back(fmt::format("{}={}", key, value)); } return tmp; } std::size_t environment::size() const { return m_env.size(); } std::unordered_map<std::string, std::string>::const_iterator environment::begin() const { return m_env.begin(); } std::unordered_map<std::string, std::string>::const_iterator environment::end() const { return m_env.end(); } command::command(std::string cmdline, std::optional<environment> env) : m_cmdline(std::move(cmdline)), m_env(std::move(env)) {} const std::string& command::cmdline() const { return m_cmdline; } const std::optional<environment>& command::env() const { return m_env; } command command::eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const { // generate a regex from a map of key/value pairs constexpr auto regex_from_map = [](const std::map<std::string, std::string>& m) -> std::regex { std::string result; for(const auto& [key, value] : m) { const auto escaped_key = std::regex_replace(key, std::regex{R"([{}])"}, R"(\$&)"); result += fmt::format("{}|", escaped_key); } result.pop_back(); return std::regex{result}; }; const std::map<std::string, std::string> replacements{ {std::string{keywords.at(0)}, adhoc_id}, {std::string{keywords.at(1)}, adhoc_directory.string()}, {std::string{keywords.at(2)}, fmt::format("\"{}\"", fmt::join(adhoc_nodes, ","))}}; // make sure that we fail if we ever add a new keyword and forget to add // a replacement for it assert(replacements.size() == keywords.size()); std::string result; const auto re = regex_from_map(replacements); auto it = std::sregex_iterator(m_cmdline.begin(), m_cmdline.end(), re); auto end = std::sregex_iterator{}; std::string::size_type last_pos = 0; for(; it != end; ++it) { const auto& match = *it; result += m_cmdline.substr(last_pos, match.position() - last_pos); result += replacements.at(match.str()); last_pos = match.position() + match.length(); } result += m_cmdline.substr(last_pos, m_cmdline.length() - last_pos); return command{result, m_env}; } std::vector<std::string> command::as_vector() const { std::vector<std::string> tmp; for(auto&& r : std::views::split(m_cmdline, ' ') | std::views::transform([](auto&& v) -> std::string { auto c = v | std::views::common; return std::string{c.begin(), c.end()}; })) { tmp.emplace_back(std::move(r)); } return tmp; } } // namespace scord_ctl src/scord-ctl/command.hpp 0 → 100644 +191 −0 Original line number Diff line number Diff line /****************************************************************************** * Copyright 2021-2023, Barcelona Supercomputing Center (BSC), Spain * * This software was partially supported by the EuroHPC-funded project ADMIRE * (Project ID: 956748, https://www.admire-eurohpc.eu). * * This file is part of scord. * * scord is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * scord is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with scord. If not, see <https://www.gnu.org/licenses/>. * * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ #ifndef SCORD_CTL_COMMAND_HPP #define SCORD_CTL_COMMAND_HPP #include <string> #include <vector> #include <unordered_map> #include <optional> #include <filesystem> #include <fmt/format.h> namespace scord_ctl { /** * @brief A class representing the environment variables that * should be set when running a command. */ class environment { public: /** * @brief Set an environment variable. * * @param key The name of the environment variable. * @param value The value of the environment variable. */ void set(const std::string& key, const std::string& value); /** * @brief Get the value of an environment variable. * * @param key The name of the environment variable. * @return The value of the environment variable if it exists, an empty * string otherwise. */ std::string get(const std::string& key) const; /** * @brief Get the environment variables as a vector of strings. * Each string is of the form `key=value`. * * @return The environment variables as a vector of strings. */ std::vector<std::string> as_vector() const; /** * @brief Get the number of environment variables. * * @return The number of environment variables. */ std::size_t size() const; /** * @brief Get an iterator to the beginning of the environment variables. * * @return An iterator to the beginning of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator begin() const; /** * @brief Get an iterator to the end of the environment variables. * * @return An iterator to the end of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator end() const; private: std::unordered_map<std::string, std::string> m_env; }; /** * @brief A class representing a command to be executed. */ class command { public: /** * @brief Keywords that can be used in the command line and * will be expanded with appropriate values when calling `eval()`. */ static constexpr std::array<std::string_view, 3> keywords = { "{ADHOC_ID}", "{ADHOC_DIRECTORY}", "{ADHOC_NODES}"}; /** * @brief Construct a command. * * @param cmdline The command line to be executed. * @param env The environment variables to be set when executing the * command. */ explicit command(std::string cmdline, std::optional<environment> env = std::nullopt); /** * @brief Get the template command line to be executed (i.e. without having * keywords expanded). * * @return The command line to be executed. */ const std::string& cmdline() const; /** * @brief Get the environment variables to be set when executing the * command. * * @return The environment variables to be set when executing the command. */ const std::optional<environment>& env() const; /** * @brief Return a copy of the current `command` where all the keywords in * its command line template have been replaced with string * representations of the arguments provided. * * @param adhoc_id The ID of the adhoc storage system. * @param adhoc_directory The directory where the adhoc storage will run. * @param adhoc_nodes The nodes where the adhoc storage will run. * @return The evaluated command. */ command eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const; /** * @brief Get the command line to be executed as a vector of strings. The * command line is split on spaces with each string in the resulting * vector being a token in the command line. * * @return The command line to be executed as a vector of strings. */ std::vector<std::string> as_vector() const; private: std::string m_cmdline; std::optional<environment> m_env; }; } // namespace scord_ctl /** * @brief Formatter for `scord_ctl::config::command`. */ template <> struct fmt::formatter<scord_ctl::command> { template <typename ParseContext> constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template <typename FormatContext> auto format(const scord_ctl::command& cmd, FormatContext& ctx) { return fmt::format_to(ctx.out(), "{}", cmd.cmdline()); } }; #endif // SCORD_CTL_COMMAND_HPP src/scord-ctl/config_file.cpp +7 −123 Original line number Diff line number Diff line Loading @@ -26,8 +26,6 @@ #include <ryml.hpp> #include <fmt/ostream.h> #include <fstream> #include <regex> #include <ranges> #include "config_file.hpp" namespace { Loading Loading @@ -90,10 +88,10 @@ to_adhoc_storage_type(const ryml::csubstr& type) { * * @return The parsed `scord_ctl::config::environment` object. */ scord_ctl::config::environment scord_ctl::environment parse_environment_node(const ryml::ConstNodeRef& node) { scord_ctl::config::environment env; scord_ctl::environment env; for(const auto& child : node) { if(!child.has_key()) { Loading @@ -119,11 +117,11 @@ parse_environment_node(const ryml::ConstNodeRef& node) { * * @return The parsed `scord_ctl::config::command` object. */ command scord_ctl::command parse_command_node(const ryml::ConstNodeRef& node) { std::string cmdline; std::optional<environment> env; std::optional<scord_ctl::environment> env; for(const auto& child : node) { if(!child.has_key()) { Loading @@ -147,7 +145,7 @@ parse_command_node(const ryml::ConstNodeRef& node) { throw std::runtime_error{"missing required `command` key"}; } return command{cmdline, env}; return scord_ctl::command{cmdline, env}; } /** Loading Loading @@ -177,8 +175,8 @@ adhoc_storage_config parse_adhoc_config_node(const ryml::ConstNodeRef& node) { std::filesystem::path working_directory; std::optional<command> startup_command; std::optional<command> shutdown_command; std::optional<scord_ctl::command> startup_command; std::optional<scord_ctl::command> shutdown_command; for(const auto& child : node) { Loading Loading @@ -284,120 +282,6 @@ parse_config_node(const ryml::ConstNodeRef& node) { namespace scord_ctl::config { void environment::set(const std::string& key, const std::string& value) { m_env[key] = value; } std::string environment::get(const std::string& key) const { return m_env.count(key) == 0 ? std::string{} : m_env.at(key); } std::vector<std::string> environment::as_vector() const { std::vector<std::string> tmp; tmp.reserve(m_env.size()); for(const auto& [key, value] : m_env) { tmp.emplace_back(fmt::format("{}={}", key, value)); } return tmp; } std::size_t environment::size() const { return m_env.size(); } std::unordered_map<std::string, std::string>::const_iterator environment::begin() const { return m_env.begin(); } std::unordered_map<std::string, std::string>::const_iterator environment::end() const { return m_env.end(); } command::command(std::string cmdline, std::optional<environment> env) : m_cmdline(std::move(cmdline)), m_env(std::move(env)) {} const std::string& command::cmdline() const { return m_cmdline; } const std::optional<environment>& command::env() const { return m_env; } command command::eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const { // generate a regex from a map of key/value pairs constexpr auto regex_from_map = [](const std::map<std::string, std::string>& m) -> std::regex { std::string result; for(const auto& [key, value] : m) { const auto escaped_key = std::regex_replace(key, std::regex{R"([{}])"}, R"(\$&)"); result += fmt::format("{}|", escaped_key); } result.pop_back(); return std::regex{result}; }; const std::map<std::string, std::string> replacements{ {std::string{keywords.at(0)}, adhoc_id}, {std::string{keywords.at(1)}, adhoc_directory.string()}, {std::string{keywords.at(2)}, fmt::format("\"{}\"", fmt::join(adhoc_nodes, ","))}}; // make sure that we fail if we ever add a new keyword and forget to add // a replacement for it assert(replacements.size() == keywords.size()); std::string result; const auto re = regex_from_map(replacements); auto it = std::sregex_iterator(m_cmdline.begin(), m_cmdline.end(), re); auto end = std::sregex_iterator{}; std::string::size_type last_pos = 0; for(; it != end; ++it) { const auto& match = *it; result += m_cmdline.substr(last_pos, match.position() - last_pos); result += replacements.at(match.str()); last_pos = match.position() + match.length(); } result += m_cmdline.substr(last_pos, m_cmdline.length() - last_pos); return command{result, m_env}; } std::vector<std::string> command::as_vector() const { std::vector<std::string> tmp; for(auto&& r : std::views::split(m_cmdline, ' ') | std::views::transform([](auto&& v) -> std::string { auto c = v | std::views::common; return std::string{c.begin(), c.end()}; })) { tmp.emplace_back(std::move(r)); } return tmp; } adhoc_storage_config::adhoc_storage_config( std::filesystem::path working_directory, command startup_command, command shutdown_command) Loading src/scord-ctl/config_file.hpp +1 −153 Original line number Diff line number Diff line Loading @@ -28,143 +28,10 @@ #include <scord/types.hpp> #include <filesystem> #include "command.hpp" namespace scord_ctl::config { /** * @brief A class representing the environment variables that * should be set when running a command. */ class environment { public: /** * @brief Set an environment variable. * * @param key The name of the environment variable. * @param value The value of the environment variable. */ void set(const std::string& key, const std::string& value); /** * @brief Get the value of an environment variable. * * @param key The name of the environment variable. * @return The value of the environment variable if it exists, an empty * string otherwise. */ std::string get(const std::string& key) const; /** * @brief Get the environment variables as a vector of strings. * Each string is of the form `key=value`. * * @return The environment variables as a vector of strings. */ std::vector<std::string> as_vector() const; /** * @brief Get the number of environment variables. * * @return The number of environment variables. */ std::size_t size() const; /** * @brief Get an iterator to the beginning of the environment variables. * * @return An iterator to the beginning of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator begin() const; /** * @brief Get an iterator to the end of the environment variables. * * @return An iterator to the end of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator end() const; private: std::unordered_map<std::string, std::string> m_env; }; /** * @brief A class representing a command to be executed. */ class command { public: /** * @brief Keywords that can be used in the command line and * will be expanded with appropriate values when calling `eval()`. */ static constexpr std::array<std::string_view, 3> keywords = { "{ADHOC_ID}", "{ADHOC_DIRECTORY}", "{ADHOC_NODES}"}; /** * @brief Construct a command. * * @param cmdline The command line to be executed. * @param env The environment variables to be set when executing the * command. */ explicit command(std::string cmdline, std::optional<environment> env = std::nullopt); /** * @brief Get the template command line to be executed (i.e. without having * keywords expanded). * * @return The command line to be executed. */ const std::string& cmdline() const; /** * @brief Get the environment variables to be set when executing the * command. * * @return The environment variables to be set when executing the command. */ const std::optional<environment>& env() const; /** * @brief Return a copy of the current `command` where all the keywords in * its command line template have been replaced with string * representations of the arguments provided. * * @param adhoc_id The ID of the adhoc storage system. * @param adhoc_directory The directory where the adhoc storage will run. * @param adhoc_nodes The nodes where the adhoc storage will run. * @return The evaluated command. */ command eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const; /** * @brief Get the command line to be executed as a vector of strings. The * command line is split on spaces with each string in the resulting * vector being a token in the command line. * * @return The command line to be executed as a vector of strings. */ std::vector<std::string> as_vector() const; private: std::string m_cmdline; std::optional<environment> m_env; }; /** * @brief A class representing the configuration of an adhoc storage system. */ Loading Loading @@ -247,23 +114,4 @@ private: } // namespace scord_ctl::config /** * @brief Formatter for `scord_ctl::config::command`. */ template <> struct fmt::formatter<scord_ctl::config::command> { template <typename ParseContext> constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template <typename FormatContext> auto format(const scord_ctl::config::command& cmd, FormatContext& ctx) { return fmt::format_to(ctx.out(), "{}", cmd.cmdline()); } }; #endif // SCORD_CONFIG_HPP Loading
src/scord-ctl/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ add_executable(scord-ctl) target_sources( scord-ctl PRIVATE scord_ctl.cpp rpc_server.cpp rpc_server.hpp ${CMAKE_CURRENT_BINARY_DIR}/defaults.hpp config_file.hpp config_file.cpp command.hpp command.cpp ) configure_file(defaults.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/defaults.hpp @ONLY) Loading
src/scord-ctl/command.cpp 0 → 100644 +121 −0 Original line number Diff line number Diff line #include <ranges> #include <regex> #include <cassert> #include "command.hpp" namespace scord_ctl { void environment::set(const std::string& key, const std::string& value) { m_env[key] = value; } std::string environment::get(const std::string& key) const { return m_env.count(key) == 0 ? std::string{} : m_env.at(key); } std::vector<std::string> environment::as_vector() const { std::vector<std::string> tmp; tmp.reserve(m_env.size()); for(const auto& [key, value] : m_env) { tmp.emplace_back(fmt::format("{}={}", key, value)); } return tmp; } std::size_t environment::size() const { return m_env.size(); } std::unordered_map<std::string, std::string>::const_iterator environment::begin() const { return m_env.begin(); } std::unordered_map<std::string, std::string>::const_iterator environment::end() const { return m_env.end(); } command::command(std::string cmdline, std::optional<environment> env) : m_cmdline(std::move(cmdline)), m_env(std::move(env)) {} const std::string& command::cmdline() const { return m_cmdline; } const std::optional<environment>& command::env() const { return m_env; } command command::eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const { // generate a regex from a map of key/value pairs constexpr auto regex_from_map = [](const std::map<std::string, std::string>& m) -> std::regex { std::string result; for(const auto& [key, value] : m) { const auto escaped_key = std::regex_replace(key, std::regex{R"([{}])"}, R"(\$&)"); result += fmt::format("{}|", escaped_key); } result.pop_back(); return std::regex{result}; }; const std::map<std::string, std::string> replacements{ {std::string{keywords.at(0)}, adhoc_id}, {std::string{keywords.at(1)}, adhoc_directory.string()}, {std::string{keywords.at(2)}, fmt::format("\"{}\"", fmt::join(adhoc_nodes, ","))}}; // make sure that we fail if we ever add a new keyword and forget to add // a replacement for it assert(replacements.size() == keywords.size()); std::string result; const auto re = regex_from_map(replacements); auto it = std::sregex_iterator(m_cmdline.begin(), m_cmdline.end(), re); auto end = std::sregex_iterator{}; std::string::size_type last_pos = 0; for(; it != end; ++it) { const auto& match = *it; result += m_cmdline.substr(last_pos, match.position() - last_pos); result += replacements.at(match.str()); last_pos = match.position() + match.length(); } result += m_cmdline.substr(last_pos, m_cmdline.length() - last_pos); return command{result, m_env}; } std::vector<std::string> command::as_vector() const { std::vector<std::string> tmp; for(auto&& r : std::views::split(m_cmdline, ' ') | std::views::transform([](auto&& v) -> std::string { auto c = v | std::views::common; return std::string{c.begin(), c.end()}; })) { tmp.emplace_back(std::move(r)); } return tmp; } } // namespace scord_ctl
src/scord-ctl/command.hpp 0 → 100644 +191 −0 Original line number Diff line number Diff line /****************************************************************************** * Copyright 2021-2023, Barcelona Supercomputing Center (BSC), Spain * * This software was partially supported by the EuroHPC-funded project ADMIRE * (Project ID: 956748, https://www.admire-eurohpc.eu). * * This file is part of scord. * * scord is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * scord is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with scord. If not, see <https://www.gnu.org/licenses/>. * * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ #ifndef SCORD_CTL_COMMAND_HPP #define SCORD_CTL_COMMAND_HPP #include <string> #include <vector> #include <unordered_map> #include <optional> #include <filesystem> #include <fmt/format.h> namespace scord_ctl { /** * @brief A class representing the environment variables that * should be set when running a command. */ class environment { public: /** * @brief Set an environment variable. * * @param key The name of the environment variable. * @param value The value of the environment variable. */ void set(const std::string& key, const std::string& value); /** * @brief Get the value of an environment variable. * * @param key The name of the environment variable. * @return The value of the environment variable if it exists, an empty * string otherwise. */ std::string get(const std::string& key) const; /** * @brief Get the environment variables as a vector of strings. * Each string is of the form `key=value`. * * @return The environment variables as a vector of strings. */ std::vector<std::string> as_vector() const; /** * @brief Get the number of environment variables. * * @return The number of environment variables. */ std::size_t size() const; /** * @brief Get an iterator to the beginning of the environment variables. * * @return An iterator to the beginning of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator begin() const; /** * @brief Get an iterator to the end of the environment variables. * * @return An iterator to the end of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator end() const; private: std::unordered_map<std::string, std::string> m_env; }; /** * @brief A class representing a command to be executed. */ class command { public: /** * @brief Keywords that can be used in the command line and * will be expanded with appropriate values when calling `eval()`. */ static constexpr std::array<std::string_view, 3> keywords = { "{ADHOC_ID}", "{ADHOC_DIRECTORY}", "{ADHOC_NODES}"}; /** * @brief Construct a command. * * @param cmdline The command line to be executed. * @param env The environment variables to be set when executing the * command. */ explicit command(std::string cmdline, std::optional<environment> env = std::nullopt); /** * @brief Get the template command line to be executed (i.e. without having * keywords expanded). * * @return The command line to be executed. */ const std::string& cmdline() const; /** * @brief Get the environment variables to be set when executing the * command. * * @return The environment variables to be set when executing the command. */ const std::optional<environment>& env() const; /** * @brief Return a copy of the current `command` where all the keywords in * its command line template have been replaced with string * representations of the arguments provided. * * @param adhoc_id The ID of the adhoc storage system. * @param adhoc_directory The directory where the adhoc storage will run. * @param adhoc_nodes The nodes where the adhoc storage will run. * @return The evaluated command. */ command eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const; /** * @brief Get the command line to be executed as a vector of strings. The * command line is split on spaces with each string in the resulting * vector being a token in the command line. * * @return The command line to be executed as a vector of strings. */ std::vector<std::string> as_vector() const; private: std::string m_cmdline; std::optional<environment> m_env; }; } // namespace scord_ctl /** * @brief Formatter for `scord_ctl::config::command`. */ template <> struct fmt::formatter<scord_ctl::command> { template <typename ParseContext> constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template <typename FormatContext> auto format(const scord_ctl::command& cmd, FormatContext& ctx) { return fmt::format_to(ctx.out(), "{}", cmd.cmdline()); } }; #endif // SCORD_CTL_COMMAND_HPP
src/scord-ctl/config_file.cpp +7 −123 Original line number Diff line number Diff line Loading @@ -26,8 +26,6 @@ #include <ryml.hpp> #include <fmt/ostream.h> #include <fstream> #include <regex> #include <ranges> #include "config_file.hpp" namespace { Loading Loading @@ -90,10 +88,10 @@ to_adhoc_storage_type(const ryml::csubstr& type) { * * @return The parsed `scord_ctl::config::environment` object. */ scord_ctl::config::environment scord_ctl::environment parse_environment_node(const ryml::ConstNodeRef& node) { scord_ctl::config::environment env; scord_ctl::environment env; for(const auto& child : node) { if(!child.has_key()) { Loading @@ -119,11 +117,11 @@ parse_environment_node(const ryml::ConstNodeRef& node) { * * @return The parsed `scord_ctl::config::command` object. */ command scord_ctl::command parse_command_node(const ryml::ConstNodeRef& node) { std::string cmdline; std::optional<environment> env; std::optional<scord_ctl::environment> env; for(const auto& child : node) { if(!child.has_key()) { Loading @@ -147,7 +145,7 @@ parse_command_node(const ryml::ConstNodeRef& node) { throw std::runtime_error{"missing required `command` key"}; } return command{cmdline, env}; return scord_ctl::command{cmdline, env}; } /** Loading Loading @@ -177,8 +175,8 @@ adhoc_storage_config parse_adhoc_config_node(const ryml::ConstNodeRef& node) { std::filesystem::path working_directory; std::optional<command> startup_command; std::optional<command> shutdown_command; std::optional<scord_ctl::command> startup_command; std::optional<scord_ctl::command> shutdown_command; for(const auto& child : node) { Loading Loading @@ -284,120 +282,6 @@ parse_config_node(const ryml::ConstNodeRef& node) { namespace scord_ctl::config { void environment::set(const std::string& key, const std::string& value) { m_env[key] = value; } std::string environment::get(const std::string& key) const { return m_env.count(key) == 0 ? std::string{} : m_env.at(key); } std::vector<std::string> environment::as_vector() const { std::vector<std::string> tmp; tmp.reserve(m_env.size()); for(const auto& [key, value] : m_env) { tmp.emplace_back(fmt::format("{}={}", key, value)); } return tmp; } std::size_t environment::size() const { return m_env.size(); } std::unordered_map<std::string, std::string>::const_iterator environment::begin() const { return m_env.begin(); } std::unordered_map<std::string, std::string>::const_iterator environment::end() const { return m_env.end(); } command::command(std::string cmdline, std::optional<environment> env) : m_cmdline(std::move(cmdline)), m_env(std::move(env)) {} const std::string& command::cmdline() const { return m_cmdline; } const std::optional<environment>& command::env() const { return m_env; } command command::eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const { // generate a regex from a map of key/value pairs constexpr auto regex_from_map = [](const std::map<std::string, std::string>& m) -> std::regex { std::string result; for(const auto& [key, value] : m) { const auto escaped_key = std::regex_replace(key, std::regex{R"([{}])"}, R"(\$&)"); result += fmt::format("{}|", escaped_key); } result.pop_back(); return std::regex{result}; }; const std::map<std::string, std::string> replacements{ {std::string{keywords.at(0)}, adhoc_id}, {std::string{keywords.at(1)}, adhoc_directory.string()}, {std::string{keywords.at(2)}, fmt::format("\"{}\"", fmt::join(adhoc_nodes, ","))}}; // make sure that we fail if we ever add a new keyword and forget to add // a replacement for it assert(replacements.size() == keywords.size()); std::string result; const auto re = regex_from_map(replacements); auto it = std::sregex_iterator(m_cmdline.begin(), m_cmdline.end(), re); auto end = std::sregex_iterator{}; std::string::size_type last_pos = 0; for(; it != end; ++it) { const auto& match = *it; result += m_cmdline.substr(last_pos, match.position() - last_pos); result += replacements.at(match.str()); last_pos = match.position() + match.length(); } result += m_cmdline.substr(last_pos, m_cmdline.length() - last_pos); return command{result, m_env}; } std::vector<std::string> command::as_vector() const { std::vector<std::string> tmp; for(auto&& r : std::views::split(m_cmdline, ' ') | std::views::transform([](auto&& v) -> std::string { auto c = v | std::views::common; return std::string{c.begin(), c.end()}; })) { tmp.emplace_back(std::move(r)); } return tmp; } adhoc_storage_config::adhoc_storage_config( std::filesystem::path working_directory, command startup_command, command shutdown_command) Loading
src/scord-ctl/config_file.hpp +1 −153 Original line number Diff line number Diff line Loading @@ -28,143 +28,10 @@ #include <scord/types.hpp> #include <filesystem> #include "command.hpp" namespace scord_ctl::config { /** * @brief A class representing the environment variables that * should be set when running a command. */ class environment { public: /** * @brief Set an environment variable. * * @param key The name of the environment variable. * @param value The value of the environment variable. */ void set(const std::string& key, const std::string& value); /** * @brief Get the value of an environment variable. * * @param key The name of the environment variable. * @return The value of the environment variable if it exists, an empty * string otherwise. */ std::string get(const std::string& key) const; /** * @brief Get the environment variables as a vector of strings. * Each string is of the form `key=value`. * * @return The environment variables as a vector of strings. */ std::vector<std::string> as_vector() const; /** * @brief Get the number of environment variables. * * @return The number of environment variables. */ std::size_t size() const; /** * @brief Get an iterator to the beginning of the environment variables. * * @return An iterator to the beginning of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator begin() const; /** * @brief Get an iterator to the end of the environment variables. * * @return An iterator to the end of the environment variables. */ std::unordered_map<std::string, std::string>::const_iterator end() const; private: std::unordered_map<std::string, std::string> m_env; }; /** * @brief A class representing a command to be executed. */ class command { public: /** * @brief Keywords that can be used in the command line and * will be expanded with appropriate values when calling `eval()`. */ static constexpr std::array<std::string_view, 3> keywords = { "{ADHOC_ID}", "{ADHOC_DIRECTORY}", "{ADHOC_NODES}"}; /** * @brief Construct a command. * * @param cmdline The command line to be executed. * @param env The environment variables to be set when executing the * command. */ explicit command(std::string cmdline, std::optional<environment> env = std::nullopt); /** * @brief Get the template command line to be executed (i.e. without having * keywords expanded). * * @return The command line to be executed. */ const std::string& cmdline() const; /** * @brief Get the environment variables to be set when executing the * command. * * @return The environment variables to be set when executing the command. */ const std::optional<environment>& env() const; /** * @brief Return a copy of the current `command` where all the keywords in * its command line template have been replaced with string * representations of the arguments provided. * * @param adhoc_id The ID of the adhoc storage system. * @param adhoc_directory The directory where the adhoc storage will run. * @param adhoc_nodes The nodes where the adhoc storage will run. * @return The evaluated command. */ command eval(const std::string& adhoc_id, const std::filesystem::path& adhoc_directory, const std::vector<std::string>& adhoc_nodes) const; /** * @brief Get the command line to be executed as a vector of strings. The * command line is split on spaces with each string in the resulting * vector being a token in the command line. * * @return The command line to be executed as a vector of strings. */ std::vector<std::string> as_vector() const; private: std::string m_cmdline; std::optional<environment> m_env; }; /** * @brief A class representing the configuration of an adhoc storage system. */ Loading Loading @@ -247,23 +114,4 @@ private: } // namespace scord_ctl::config /** * @brief Formatter for `scord_ctl::config::command`. */ template <> struct fmt::formatter<scord_ctl::config::command> { template <typename ParseContext> constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template <typename FormatContext> auto format(const scord_ctl::config::command& cmd, FormatContext& ctx) { return fmt::format_to(ctx.out(), "{}", cmd.cmdline()); } }; #endif // SCORD_CONFIG_HPP