diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bb09a1..755e17a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ target_include_directories(sled PUBLIC src/ 3party/eigen 3party/inja target_sources( sled PRIVATE src/sled/async/async.cc + src/sled/config.cc src/sled/debugging/demangle.cc src/sled/debugging/symbolize.cc src/sled/event_bus/event_bus.cc @@ -194,6 +195,7 @@ if(SLED_BUILD_TESTS) sled_add_test(NAME sled_string_view_test SRCS src/sled/nonstd/string_view_test.cc) sled_add_test(NAME sled_expected_test SRCS src/sled/nonstd/expected_test.cc) + sled_add_test(NAME sled_config_test SRCS src/sled//config_test.cc) endif(SLED_BUILD_TESTS) if(SLED_BUILD_FUZZ) diff --git a/src/sled/config.cc b/src/sled/config.cc new file mode 100644 index 0000000..14182e9 --- /dev/null +++ b/src/sled/config.cc @@ -0,0 +1,228 @@ +#include "sled/config.h" +#include "sled/log/log.h" +#include "sled/strings/utils.h" +#include + +namespace sled { +Config::Config() = default; + +Config::Config(sled::string_view name) : Config(name, "") {} + +Config::Config(sled::string_view name, sled::string_view path) : config_name_(name) +{ + config_paths_.emplace_back(path.to_string()); +} + +void +Config::SetConfigName(sled::string_view name) +{ + config_name_ = name.to_string(); +} + +void +Config::AddConfigPath(sled::string_view path) +{ + config_paths_.emplace_back(path.to_string()); +} + +bool +Config::ReadInConfig() +{ + const static std::vector extensions = {".toml"}; + for (const auto &path : config_paths_) { + auto name = path + config_name_; + for (const auto &ext : extensions) { + const std::ifstream file(name + ext); + if (file.good()) { + try { + std::stringstream ss; + ss << file.rdbuf(); + + std::istringstream stream_data(ss.str(), std::ios_base::binary | std::ios_base::in); + toml_ = toml::parse(stream_data, "string"); + return true; + // goto config_read_success; + } catch (...) { + LOGD("Failed to parse config file: {}", name + ext); + } + } + } + } + + // config_read_success: + // // pair + // for (auto &pair : default_values_) { + // toml::value value; + // if (!GetNode(pair.first, value)) { AddDefaultNode(pair.first, pair.second); } + // } + + return false; +} + +bool +Config::IsSet(sled::string_view key) const +{ + toml::value value; + return GetNode(key, value); +} + +bool +Config::GetBoolOr(sled::string_view key, const bool &def) const +{ + toml::value value; + try { + if (GetNode(key, value) && value.is_boolean()) { return value.as_boolean(); } + } catch (...) {} + return def; +} + +int +Config::GetIntOr(sled::string_view key, const int &def) const +{ + toml::value value; + try { + if (GetNode(key, value)) { + if (value.is_integer()) { + return value.as_integer(); + } else if (value.is_boolean()) { + return value.as_boolean() ? 1 : 0; + } else if (value.is_floating()) { + return static_cast(value.as_floating()); + } + } + } catch (...) {} + return def; +} + +double +Config::GetDoubleOr(sled::string_view key, const double &def) const +{ + toml::value value; + try { + if (GetNode(key, value)) { + if (value.is_floating()) { + return value.as_floating(); + } else if (value.is_integer()) { + return static_cast(value.as_integer()); + } + } + } catch (...) {} + return def; +} + +std::string +Config::GetStringOr(sled::string_view key, sled::string_view def) const +{ + toml::value value; + try { + if (GetNode(key, value) && value.is_string()) { return value.as_string(); } + } catch (...) {} + return def.to_string(); +} + +void +Config::SetDefault(sled::string_view key, const bool &value) +{ + default_values_.insert({key.to_string(), value}); +} + +void +Config::SetDefault(sled::string_view key, const char *value) +{ + SetDefault(key, std::string(value)); +} + +void +Config::SetDefault(sled::string_view key, const std::string &value) +{ + default_values_.insert({key.to_string(), value}); +} + +void +Config::SetDefault(sled::string_view key, sled::string_view value) +{ + SetDefault(key, std::string(value)); +} + +void +Config::SetDefault(sled::string_view key, const int &value) +{ + default_values_.insert({key.to_string(), value}); +} + +void +Config::SetDefault(sled::string_view key, const double &value) +{ + default_values_.insert({key.to_string(), value}); +} + +bool +Config::GetNode(sled::string_view key, toml::value &value) const +{ + auto keys = StrSplit(key.to_string(), "."); + auto cur = toml_; + for (const auto &k : keys) { + try { + auto next = toml::find(cur, k); + cur = next; + } catch (...) { + if (GetDefaultNode(key, value)) { return true; } + return false; + } + } + value = cur; + return true; +} + +bool +Config::AddDefaultNode(sled::string_view key, ValueType value) +{ + auto keys = StrSplit(key.to_string(), "."); + if (keys.size() == 1) { + auto first_key = keys[0]; + switch (value.index()) { + case 0: + toml_[first_key] = sled::get(value); + break; + case 1: + toml_[first_key] = sled::get(value); + break; + case 2: + toml_[first_key] = sled::get(value); + break; + case 3: + toml_[first_key] = sled::get(value); + break; + default: + return false; + } + return true; + } + return false; +} + +bool +Config::GetDefaultNode(sled::string_view key, toml::value &value) const +{ + auto iter = default_values_.find(key.to_string()); + if (iter == default_values_.end()) { return false; } + auto &default_value = iter->second; + switch (default_value.index()) { + case 0: + value = sled::get(default_value); + break; + case 1: + value = sled::get(default_value); + break; + case 2: + value = sled::get(default_value); + break; + case 3: + value = sled::get(default_value); + break; + default: + return false; + } + return true; +} +}// namespace sled diff --git a/src/sled/config.h b/src/sled/config.h new file mode 100644 index 0000000..ad5c469 --- /dev/null +++ b/src/sled/config.h @@ -0,0 +1,49 @@ +#ifndef SLED_CONFIG_H +#define SLED_CONFIG_H + +#include "sled/nonstd/string_view.h" +#include "sled/variant.h" +#include "toml.hpp" + +namespace sled { +class Config { +public: + using ValueType = sled::variant; + Config(); + Config(sled::string_view name); + Config(sled::string_view name, sled::string_view path); + Config(const Config &lhs) = delete; + Config(Config &&rhs) noexcept = delete; + Config &operator=(const Config &lhs) = delete; + Config &operator=(Config &&rhs) noexcept = delete; + + void SetConfigName(sled::string_view name); + void AddConfigPath(sled::string_view path); + + bool ReadInConfig(); + + bool IsSet(sled::string_view key) const; + bool GetBoolOr(sled::string_view key, const bool &def = false) const; + int GetIntOr(sled::string_view key, const int &def = 0) const; + double GetDoubleOr(sled::string_view key, const double &def = 0.0) const; + std::string GetStringOr(sled::string_view key, const sled::string_view def = "") const; + + void SetDefault(sled::string_view key, const bool &value); + void SetDefault(sled::string_view key, const char *value); + void SetDefault(sled::string_view key, const std::string &value); + void SetDefault(sled::string_view key, sled::string_view value); + void SetDefault(sled::string_view key, const int &value); + void SetDefault(sled::string_view key, const double &value); + +private: + bool GetNode(sled::string_view key, toml::value &value) const; + bool AddDefaultNode(sled::string_view, ValueType value); + bool GetDefaultNode(sled::string_view key, toml::value &value) const; + + std::unordered_map default_values_; + std::vector config_paths_; + std::string config_name_; + toml::value toml_; +}; +}// namespace sled +#endif// SLED_CONFIG_H diff --git a/src/sled/config_test.cc b/src/sled/config_test.cc new file mode 100644 index 0000000..8500e6f --- /dev/null +++ b/src/sled/config_test.cc @@ -0,0 +1,33 @@ +#include +#include + +static std::string test_config_name = "config_test"; +static std::string test_config_path = sled::StripSuffix(__FILE__, "config_test.cc").to_string(); + +TEST_SUITE("Config") +{ + TEST_CASE("config") + { + sled::Config config; + config.SetConfigName(test_config_name); + config.AddConfigPath(test_config_path); + config.SetDefault("top-string", "no effect"); + config.SetDefault("top-kk", "kk"); + CHECK(config.ReadInConfig()); + + CHECK(config.IsSet("top-string")); + CHECK_EQ(config.GetStringOr("top-string"), "bob"); + + CHECK(config.IsSet("top-kk")); + CHECK_EQ(config.GetStringOr("top-kk"), "kk"); + + CHECK(config.IsSet("top-int")); + CHECK(config.IsSet("top-bool")); + CHECK_EQ(config.GetIntOr("top-int"), 10); + CHECK_EQ(config.GetBoolOr("top-bool"), true); + CHECK_EQ(config.GetIntOr("top-bool"), 1); + + CHECK(config.IsSet("top-table.key1")); + CHECK_EQ(config.GetStringOr("top-table.key1"), "value1"); + } +} diff --git a/src/sled/config_test.toml b/src/sled/config_test.toml new file mode 100644 index 0000000..5a0fae4 --- /dev/null +++ b/src/sled/config_test.toml @@ -0,0 +1,24 @@ +top-string = "bob" +top-int = 10 +top-bool = true +top-array = [ "item1", "item2", "item3" ] +nested-array = [ + [ "nested-item1-subitem1", "nested-item1-subitem2"], + [ "nested-item2-subitem1", "nested-item2-subitem2"] +] + +[top-table] +key1 = "value1" +key2 = "value2" +key3 = "value3" + +# Nested table +[nested-table.key1] + +key1-subkey1 = "value1-1" +key1-subkey2 = "value1-2" + +[nested-table.key2] + +key2-subkey1 = "value2-1" +key2-subkey2 = "value2-2" diff --git a/src/sled/sled.h b/src/sled/sled.h index 99bd0ae..9ea95d1 100644 --- a/src/sled/sled.h +++ b/src/sled/sled.h @@ -10,6 +10,7 @@ #include "sled/nonstd/string_view.h" #include "toml.hpp" +#include "sled/config.h" // experimental #include "sled/experimental/design_patterns/dispatcher.h" diff --git a/src/sled/strings/utils.cc b/src/sled/strings/utils.cc index dd40eca..9193af1 100644 --- a/src/sled/strings/utils.cc +++ b/src/sled/strings/utils.cc @@ -17,7 +17,7 @@ ToUpper(char c) } std::string -ToLower(const std::string &str) +ToLower(sled::string_view str) { std::stringstream ss; for (auto &ch : str) { ss << ToLower(ch); } @@ -25,7 +25,7 @@ ToLower(const std::string &str) } std::string -ToUpper(const std::string &str) +ToUpper(sled::string_view str) { std::stringstream ss; for (auto &ch : str) { ss << ToUpper(ch); } @@ -102,28 +102,75 @@ TrimRight(const std::string &str, const std::string &chars) return end == std::string::npos ? "" : str.substr(0, end + 1); } +// bool +// EndsWith(const std::string &str, const std::string &suffix) +// { +// return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +// } +// +// bool +// StartsWith(const std::string &str, const std::string &prefix) +// { +// return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; +// } bool -EndsWith(const std::string &str, const std::string &suffix) -{ - return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; -} - -bool -StartsWith(const std::string &str, const std::string &prefix) +StartsWith(sled::string_view str, sled::string_view prefix) { return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; } bool -EndsWithIgnoreCase(const std::string &str, const std::string &suffix) +EndsWith(sled::string_view str, sled::string_view suffix) { - return EndsWith(ToLower(str), ToLower(suffix)); + return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +// bool +// EndsWithIgnoreCase(const std::string &str, const std::string &suffix) +// { +// return EndsWith(ToLower(str), ToLower(suffix)); +// } +// +// bool +// StartsWithIgnoreCase(const std::string &str, const std::string &prefix) +// { +// return StartsWith(ToLower(str), ToLower(prefix)); +// } + +bool +EndsWithIgnoreCase(sled::string_view str, sled::string_view suffix) +{ + if (str.size() < suffix.size()) { return false; } + + auto str_iter = str.rbegin(); + auto suffix_iter = suffix.rbegin(); + + for (; suffix_iter != suffix.rend(); ++str_iter, ++suffix_iter) { + if (ToLower(*str_iter) != ToLower(*suffix_iter)) { return false; } + } + return true; } bool -StartsWithIgnoreCase(const std::string &str, const std::string &prefix) +StartsWithIgnoreCase(sled::string_view str, sled::string_view prefix) { - return StartsWith(ToLower(str), ToLower(prefix)); + if (str.size() < prefix.size()) { return false; } + auto str_iter = str.begin(); + auto prefix_iter = prefix.begin(); + for (; prefix_iter != prefix.end(); ++str_iter, ++prefix_iter) { + if (ToLower(*str_iter) != ToLower(*prefix_iter)) { return false; } + } + return true; +} + +bool +EqualsIgnoreCase(sled::string_view lhs, sled::string_view rhs) +{ + if (lhs.size() != rhs.size()) { return false; } + for (size_t i = 0; i < lhs.size(); ++i) { + if (ToLower(lhs[i]) != ToLower(rhs[i])) { return false; } + } + return true; } bool diff --git a/src/sled/strings/utils.h b/src/sled/strings/utils.h index 5383c84..9736661 100644 --- a/src/sled/strings/utils.h +++ b/src/sled/strings/utils.h @@ -1,6 +1,7 @@ #pragma once #ifndef SLED_STRINGS_UTILS_H #define SLED_STRINGS_UTILS_H +#include "sled/nonstd/string_view.h" #include #include @@ -8,21 +9,41 @@ namespace sled { char ToLower(char c); char ToUpper(char c); -std::string ToLower(const std::string &str); -std::string ToUpper(const std::string &str); +// std::string ToLower(const std::string &str); +// std::string ToUpper(const std::string &str); +std::string ToLower(sled::string_view str); +std::string ToUpper(sled::string_view str); std::string ToHex(const std::string &str); std::string StrJoin(const std::vector &strings, const std::string &delim, bool skip_empty = false); std::vector StrSplit(const std::string &str, const std::string &delim, bool skip_empty = false); std::string Trim(const std::string &str, const std::string &chars = " \t\n\r"); std::string TrimLeft(const std::string &str, const std::string &chars = " \t\n\r"); std::string TrimRight(const std::string &str, const std::string &chars = " \t\n\r"); -bool EndsWith(const std::string &str, const std::string &suffix); -bool StartsWith(const std::string &str, const std::string &prefix); +// bool StartsWith(const std::string &str, const std::string &prefix); +// bool EndsWith(const std::string &str, const std::string &suffix); +bool StartsWith(sled::string_view str, sled::string_view prefix); +bool EndsWith(sled::string_view str, sled::string_view suffix); -bool EndsWithIgnoreCase(const std::string &str, const std::string &suffix); -bool StartsWithIgnoreCase(const std::string &str, const std::string &prefix); +// bool EndsWithIgnoreCase(const std::string &str, const std::string &suffix); +// bool StartsWithIgnoreCase(const std::string &str, const std::string &prefix); +bool EndsWithIgnoreCase(sled::string_view str, sled::string_view suffix); +bool StartsWithIgnoreCase(sled::string_view str, sled::string_view prefix); -bool EqualsIgnoreCase(const std::string &lhs, const std::string &rhs); +// bool EqualsIgnoreCase(const std::string &lhs, const std::string &rhs); +bool EqualsIgnoreCase(sled::string_view lhs, sled::string_view rhs); +inline sled::string_view +StripPrefix(sled::string_view str, sled::string_view prefix) +{ + if (sled::StartsWith(str, prefix)) str.remove_prefix(prefix.size()); + return str; +} + +inline sled::string_view +StripSuffix(sled::string_view str, sled::string_view suffix) +{ + if (sled::EndsWith(str, suffix)) str.remove_suffix(suffix.size()); + return str; +} }// namespace sled #endif// SLED_STRINGS_UTILS_H diff --git a/src/sled/uri.cc b/src/sled/uri.cc index 1b2876f..30ba8b0 100644 --- a/src/sled/uri.cc +++ b/src/sled/uri.cc @@ -33,7 +33,7 @@ URI::ParseURI(const std::string &uri_str) if (!std::regex_match(uri_str, match, uri_regex)) { return sled::MakeStatusOr(sled::StatusCode::kInvalidArgument, "Invalid URI format"); } - uri.set_scheme(sled::ToLower(match[2])); + uri.set_scheme(sled::ToLower(match[2].str())); int counter = 0; // for (auto res : match) { LOGD("match", "{}:{}", counter++, res); }