#include "assets/settings_document.h" #include #include #include namespace pp::assets { namespace { [[nodiscard]] bool is_valid_key_char(char value) noexcept { const auto ch = static_cast(value); return std::isalnum(ch) != 0 || value == '_' || value == '-' || value == '.'; } } std::size_t SettingsDocument::size() const noexcept { return entries_.size(); } bool SettingsDocument::empty() const noexcept { return entries_.empty(); } bool SettingsDocument::has(std::string_view key) const noexcept { return find_entry(key) != entries_.end(); } const std::vector& SettingsDocument::entries() const noexcept { return entries_; } pp::foundation::Status SettingsDocument::set(std::string_view key, SettingsValue value) { const auto key_status = validate_settings_key(key); if (!key_status.ok()) { return key_status; } const auto value_status = validate_settings_value(value); if (!value_status.ok()) { return value_status; } auto found = find_entry(key); if (found != entries_.end()) { found->value = value; return pp::foundation::Status::success(); } if (entries_.size() >= max_settings_entries) { return pp::foundation::Status::out_of_range("settings entry count exceeds the configured limit"); } entries_.push_back(SettingsEntry { .key = std::string(key), .value = value, }); return pp::foundation::Status::success(); } pp::foundation::Result SettingsDocument::get(std::string_view key) const { const auto key_status = validate_settings_key(key); if (!key_status.ok()) { return pp::foundation::Result::failure(key_status); } const auto found = find_entry(key); if (found == entries_.end()) { return pp::foundation::Result::failure( pp::foundation::Status::out_of_range("settings key was not found")); } return pp::foundation::Result::success(found->value); } pp::foundation::Status SettingsDocument::unset(std::string_view key) noexcept { const auto key_status = validate_settings_key(key); if (!key_status.ok()) { return key_status; } const auto found = find_entry(key); if (found == entries_.end()) { return pp::foundation::Status::out_of_range("settings key was not found"); } entries_.erase(found); return pp::foundation::Status::success(); } void SettingsDocument::clear() noexcept { entries_.clear(); } std::vector::iterator SettingsDocument::find_entry(std::string_view key) noexcept { return std::find_if( entries_.begin(), entries_.end(), [key](const SettingsEntry& entry) { return entry.key == key; }); } std::vector::const_iterator SettingsDocument::find_entry(std::string_view key) const noexcept { return std::find_if( entries_.begin(), entries_.end(), [key](const SettingsEntry& entry) { return entry.key == key; }); } pp::foundation::Status validate_settings_key(std::string_view key) noexcept { if (key.empty()) { return pp::foundation::Status::invalid_argument("settings key must not be empty"); } if (key.size() > max_settings_key_length) { return pp::foundation::Status::out_of_range("settings key length exceeds the configured limit"); } if (key.front() == '.' || key.back() == '.') { return pp::foundation::Status::invalid_argument("settings key must not start or end with a dot"); } for (const auto ch : key) { if (!is_valid_key_char(ch)) { return pp::foundation::Status::invalid_argument("settings key contains an unsupported character"); } } return pp::foundation::Status::success(); } pp::foundation::Status validate_settings_value(const SettingsValue& value) noexcept { if (const auto* string_value = std::get_if(&value)) { if (string_value->size() > max_settings_string_length) { return pp::foundation::Status::out_of_range("settings string length exceeds the configured limit"); } } if (const auto* double_value = std::get_if(&value)) { if (!std::isfinite(*double_value)) { return pp::foundation::Status::invalid_argument("settings floating point value must be finite"); } } return pp::foundation::Status::success(); } const char* settings_value_type_name(const SettingsValue& value) noexcept { if (std::holds_alternative(value)) { return "bool"; } if (std::holds_alternative(value)) { return "int64"; } if (std::holds_alternative(value)) { return "double"; } if (std::holds_alternative(value)) { return "string"; } return "unknown"; } }