Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions source/parser/config_reader/config_node_with_preset_.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ ConfigNodeWithPresetExpected ConfigNodeWithPreset::operator[](
std::nullopt);
}

bool ConfigNodeWithPreset::IsMap() const noexcept { return m_node.IsMap(); }

std::ostream& operator<<(std::ostream& out, const ConfigNodeWithPreset& node) {
return out << node.get_node();
}
Expand Down Expand Up @@ -112,4 +114,64 @@ ConfigNodeWithPreset load_file_with_presets(std::filesystem::path path) {
return ConfigNodeWithPreset(node, node["presets"].to_optional());
}

ConfigNodeWithPreset::Iterator::Iterator(YAML::const_iterator a_it) : m_iterator(a_it) {}

ConfigNodeWithPreset::Iterator& ConfigNodeWithPreset::Iterator::operator++() {
++m_iterator;
return *this;
}

ConfigNodeWithPreset::Iterator ConfigNodeWithPreset::Iterator::operator++(int) {
Iterator iterator_copy(*this);
++(*this);
return iterator_copy;
}

bool ConfigNodeWithPreset::Iterator::operator==(const Iterator& rhs) const {
return m_iterator == rhs.m_iterator;
}

bool ConfigNodeWithPreset::Iterator::operator!=(const Iterator& rhs) const {
return m_iterator != rhs.m_iterator;
}

ConfigNodeWithPreset ConfigNodeWithPreset::Iterator::operator*() const {
// Explanation: iterator_value under *m_iterator inherited from
// YAML::Node and std::pair<YAML::Node, YAML::Node>, but only one value of
// this pair is correct (please, tell them about std::variant...)
if (m_iterator->IsDefined()) {
// iterator goes over "named" nodes like
// list:
// - value_0
// - value_1
// ...
YAML::Node node = *m_iterator;
return ConfigNodeWithPreset(ConfigNode(node, std::nullopt));
} else {
// iterator goes over "named" nodes like
// list:
// node_1: ...
// node_2: ...
// ...
YAML::Node key_node = m_iterator->first;
YAML::Node value_node = m_iterator->second;
if (!key_node || !value_node) {
throw std::runtime_error(
"Can not take value under config node iterator; all possible "
"nodes are invalid");
}
std::string key = key_node.as<std::string>();
return ConfigNodeWithPreset(ConfigNode(value_node, key));
}
}

ConfigNodeWithPreset::Iterator ConfigNodeWithPreset::begin() const {
return Iterator(m_node.get_node().begin());
}

ConfigNodeWithPreset::Iterator ConfigNodeWithPreset::end() const {
return Iterator(m_node.get_node().end());
}


} // namespace sim
24 changes: 24 additions & 0 deletions source/parser/config_reader/config_node_with_preset_.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class ConfigNodeWithPreset {

ConfigNodeWithPresetExpected operator[](std::string_view key) const;

[[nodiscard]] bool IsMap() const noexcept;

friend std::ostream& operator<<(std::ostream& out,
const ConfigNodeWithPreset& node);

Expand All @@ -42,6 +44,28 @@ class ConfigNodeWithPreset {

std::runtime_error create_parsing_error(std::string_view error) const;

class Iterator {
public:
Iterator(YAML::const_iterator a_it);
Comment thread
S1mpotyaga marked this conversation as resolved.
Outdated

Iterator& operator++();

Iterator operator++(int);

bool operator==(const Iterator& rhs) const;

bool operator!=(const Iterator& rhs) const;

ConfigNodeWithPreset operator*() const;

private:
// Invariant: m_stacktrace_node is not null
YAML::const_iterator m_iterator;
};

Iterator begin() const;
Iterator end() const;

private:
// m_node contains information about config node and probably preset name
// m_preset - preset node which that is used to supplement fields of m_node
Expand Down
93 changes: 93 additions & 0 deletions source/validate/validator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "validator.hpp"

#include <regex>
#include <unordered_set>

namespace sim{

void validate(const YAML::Node& schema_node, const ConfigNodeWithPreset& config_node){
// create set of requried fields
std::unordered_set<std::string> schema_fields;
for (auto it: schema_node){
std::string field = it.first.as<std::string>();
if (!is_meta_field(field)){
schema_fields.insert(field);
}
}

// check if config node has unknown fields
for (const auto& node: config_node){
std::string field = node.get_name_or_throw();
if (!schema_fields.contains(field)){
std::stringstream ss;
ss << "Config node: " << config_node.get_name_or_throw();
ss << "has unknown field: " << field;
throw std::runtime_error(ss.str());
}
}

// check if config node has all required fields
for (auto it: schema_node){
std::string field_schema = it.first.as<std::string>();
if (is_meta_field(field_schema)){
continue;
}
ConfigNodeWithPresetExpected field_config = config_node[field_schema];
if (!field_config.has_value()){
std::stringstream ss;
ss << "Config node: " << config_node.get_name_or_throw();
ss << "hasn't required field: " << field_schema;
throw std::runtime_error(ss.str());
}
}

for (auto it: schema_node){
std::string field = it.first.as<std::string>();
if (!is_meta_field(field)){
validate_field(it.second, config_node[field].value());
}
}
}

bool is_meta_field(const std::string& field){
return !field.empty() && field[0] == '_';
}

void validate_field(const YAML::Node schema_node, const ConfigNodeWithPreset& config_node){
if (schema_node["_type"]){
std::string type = schema_node["_type"].as<std::string>();
if (type == "size_t"){
config_node.as_or_throw<size_t>();
} else if (type == "int"){
config_node.as_or_throw<int>();
} else if (type == "double"){
config_node.as_or_throw<double>();
} else if (type == "bool"){
config_node.as_or_throw<bool>();
} else if (type == "string"){
config_node.as_or_throw<std::string>();
} else if (type == "regex"){
std::string pattern = config_node.as_or_throw<std::string>();
try {
std::regex r(pattern);
} catch (const std::regex_error&) {
throw std::runtime_error("Incorrect type specified std::regex");
}
} else if (type.ends_with(".schema")){
YAML::Node sub_schema = YAML::LoadFile(type);
if (!config_node.IsMap()){
throw std::runtime_error(config_node.get_name_or_throw() + " must be object.");
}
validate(sub_schema, config_node);
} else{
throw std::runtime_error("Unknown type: " + type);
}
} else{
if (!config_node.IsMap()){
throw std::runtime_error(config_node.get_name_or_throw() + " must be object.");
}
validate(schema_node, config_node);
}
}

} //namespace sim
16 changes: 16 additions & 0 deletions source/validate/validator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
Comment thread
S1mpotyaga marked this conversation as resolved.
Outdated

#include <yaml-cpp/yaml.h>

#include "parser/config_reader/config_node_with_preset.hpp"


namespace sim{

void validate(const std::string& path, const ConfigNodeWithPreset& config_node);

bool is_meta_field(const std::string& field);

void validate_field(const YAML::Node schema_node, const ConfigNodeWithPreset& config_node);
Comment thread
S1mpotyaga marked this conversation as resolved.
Outdated

} // namespace sim
Loading