From 5e3cc5b31aeb65b7525c7cd72e2593c2e9cb637c Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 24 Feb 2025 12:19:18 +0300 Subject: [PATCH] ... --- cxx/comm_server_configfile.h | 150 ++++++++++++++++++++++++++++------ cxx/tests/configfile_test.cpp | 52 ++++++++---- 2 files changed, 160 insertions(+), 42 deletions(-) diff --git a/cxx/comm_server_configfile.h b/cxx/comm_server_configfile.h index 8c59c58..27f3bfe 100644 --- a/cxx/comm_server_configfile.h +++ b/cxx/comm_server_configfile.h @@ -34,44 +34,61 @@ public: static constexpr char VALUE_VALUE_DELIM = ','; + typedef std::variant> config_value_t; typedef std::unordered_map>> config_t; - template - MccConfigfile(T&& filename = nullptr) - requires(std::same_as, std::nullptr_t> || traits::mcc_input_char_range) + MccConfigfile() = default; + + MccConfigfile(std::derived_from> auto const& stream) : MccConfigfile() { - if constexpr (traits::mcc_input_char_range) { - if (load(std::forward(filename))) { - } - } + load(stream); + } + + MccConfigfile(traits::mcc_input_char_range auto const& filename) : MccConfigfile() + { + load(filename); } - std::error_code load(traits::mcc_input_char_range auto&& filename) + // factor for default config + static MccConfigfile generateDefault() { - std::filesystem::path pt(filename.begin(), filename.end()); + MccConfigfile cfg; - // first, check file existence + // default listen TCP port + cfg["endpoint"] = "tcp://0.0.0.0:3490"; - std::error_code ec; - auto pt_cn = std::filesystem::canonical(pt, ec); - if (ec) { - return ec; + // default minimal pointable altitude (in degrees) + cfg["min_alt"] = "10.0"; + + return cfg; + } + + + config_value_t& operator[](traits::mcc_input_char_range auto&& key) + { + if constexpr (std::convertible_to, typename config_t::key_type>) { + return _currentConfig[key]; + } else { + typename config_t::key_type skey; + std::ranges::copy(std::forward(key), std::back_inserter(skey)); + return _currentConfig[key]; } + } - std::ifstream fin(pt_cn); + const config_value_t& operator[](traits::mcc_input_char_range auto&& key) const + { + return const_cast(*this).operator[](std::forward(key)); + } - if (!fin.is_open()) { - return std::make_error_code(std::errc::no_such_file_or_directory); - // return std::make_error_code(std::errc::permission_denied); - } - _currentFilename = pt_cn.string(); + std::error_code load(std::derived_from> auto& stream) + { _currentConfig.clear(); // read line-by-line - for (std::string line; std::getline(fin, line);) { + for (std::string line; std::getline(stream, line);) { if (line.empty()) { continue; } @@ -100,14 +117,19 @@ public: _currentConfig[skey] = std::string{vals.begin(), vals.end()}; } else { // vector of values std::vector elems; - while (it_dlm != vals.end()) { - auto el = utils::trimSpaces(std::string_view{vals.begin(), it_dlm}); + // while (it_dlm != vals.end()) { + // auto el = utils::trimSpaces(std::string_view{vals.begin(), it_dlm}); + // elems.emplace_back(el.begin(), el.end()); + + // vals = {it_dlm + 1, vals.end()}; + // it_dlm = std::ranges::find(vals, VALUE_VALUE_DELIM); + // } + + for (const auto& el : + std::views::split(vals, VALUE_VALUE_DELIM) | + std::views::transform([](const auto& sv) { return utils::trimSpaces(sv); })) { elems.emplace_back(el.begin(), el.end()); - - vals = {it_dlm + 1, vals.end()}; - it_dlm = std::ranges::find(vals, VALUE_VALUE_DELIM); } - _currentConfig[skey] = elems; } } @@ -116,11 +138,85 @@ public: } } + return {}; + } + + std::error_code load(traits::mcc_input_char_range auto const& filename) + { + std::filesystem::path pt(filename.begin(), filename.end()); + + // first, check file existence + + std::error_code ec; + auto pt_cn = std::filesystem::canonical(pt, ec); + if (ec) { + return ec; + } + + std::ifstream fin(pt_cn); + + if (!fin.is_open()) { + return std::make_error_code(std::errc::no_such_file_or_directory); + // return std::make_error_code(std::errc::permission_denied); + } + + _currentFilename = pt_cn.string(); + + load(fin); + fin.close(); return {}; } + + std::error_code save() const + { + std::ofstream fst(_currentFilename, std::ios::trunc); + + if (!fst.is_open()) { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + const std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + + fst << "#\n"; + fst << "# Created by MccConfigfile class (" << std::put_time(std::localtime(&now), "[%F %T]") << ")\n"; + fst << "#\n"; + + for (auto& [key, value] : _currentConfig) { + fst << key << " = "; + + if (auto v_str = std::get_if<1>(&value)) { + fst << *v_str; + } else if (auto v_vec = std::get_if<2>(&value)) { + fst << (*v_vec)[0]; + for (size_t i = 1; i < v_vec->size(); ++i) { + fst << ", " << (*v_vec)[i]; + } + } // std::monostate here + + fst << "\n"; + } + + fst.close(); + + return {}; + } + + + std::error_code save(traits::mcc_input_char_range auto const& filename) + { + if (std::distance(filename.begin(), filename.end()) == 0) { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + std::ranges::copy(filename, std::back_inserter(_currentFilename)); + + return save(); + } + + std::string currentFilename() const { return _currentFilename; diff --git a/cxx/tests/configfile_test.cpp b/cxx/tests/configfile_test.cpp index a0f21b1..daf60ee 100644 --- a/cxx/tests/configfile_test.cpp +++ b/cxx/tests/configfile_test.cpp @@ -1,4 +1,5 @@ #include +#include #include "../comm_server_configfile.h" @@ -11,29 +12,50 @@ int main(int argc, char* argv[]) return 1; } + auto print_cfg = [](auto const& cfg) { + for (auto& [key, v] : cfg.config()) { + std::cout << key << " = "; + if (auto v_str = std::get_if<1>(&v)) { + std::cout << *v_str; + } else if (auto v_vec = std::get_if<2>(&v)) { + for (auto& el : *v_vec) { + std::cout << "<" << el << "> "; + } + } + + std::cout << "\n"; + } + }; + mcc::MccConfigfile cfg; + std::string str = R"--(key_no_val + # comment example + scalar_key = scalar-value + + vec_key = 1,2,3 , 345, 4576, 79 + )--"; + + std::istrstream ist(str.c_str()); + cfg.load(ist); + + std::cout << "From input stream: \n"; + print_cfg(cfg); + + cfg["vec_key"] = std::vector{"10", "20", "30"}; + cfg["new_key"] = "new-key-value"; + + + cfg.save(std::string_view{argv[1]}); + auto ec = cfg.load(std::string_view{argv[1]}); if (ec) { std::cout << "ERR: " << ec.message() << "\n"; return 1; } - for (auto& [key, v] : cfg.config()) { - std::cout << "<" << key << "> = "; - if (v.index()) { - if (v.index() > 1) { - for (auto& el : std::get<2>(v)) { - std::cout << "<" << el << "> "; - } - std::cout << "\n"; - } else { - std::cout << "<" << std::get<1>(v) << ">\n"; - } - } else { - std::cout << "\n"; - } - } + std::cout << "\n\nFrom file (edited):\n"; + print_cfg(cfg); return 0; }