Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
152
tools/EVerest-main/lib/everest/yaml/lib/yaml_loader.cpp
Normal file
152
tools/EVerest-main/lib/everest/yaml/lib/yaml_loader.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include <utils/yaml_loader.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <ryml.hpp>
|
||||
#include <ryml_std.hpp>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
namespace {
|
||||
template <typename T, typename U> T constexpr clamp_to(U len) {
|
||||
return (len <= std::numeric_limits<T>::max()) ? static_cast<T>(len) : std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
void yaml_error_handler(const char* msg, std::size_t len, ryml::Location loc, void*) {
|
||||
std::stringstream error_msg;
|
||||
error_msg << "YAML parsing error: ";
|
||||
|
||||
if (loc) {
|
||||
if (not loc.name.empty()) {
|
||||
error_msg.write(loc.name.str, clamp_to<std::streamsize>(loc.name.len));
|
||||
error_msg << ":";
|
||||
}
|
||||
error_msg << loc.line << ":";
|
||||
if (loc.col) {
|
||||
error_msg << loc.col << ":";
|
||||
}
|
||||
if (loc.offset) {
|
||||
error_msg << " (" << loc.offset << "B):";
|
||||
}
|
||||
}
|
||||
error_msg.write(msg, clamp_to<std::streamsize>(len));
|
||||
|
||||
throw std::runtime_error(error_msg.str());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
struct RymlCallbackInitializer {
|
||||
RymlCallbackInitializer() {
|
||||
ryml::set_callbacks({nullptr, nullptr, nullptr, yaml_error_handler});
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
// NOLINTNEXTLINE(misc-no-recursion): recursive parsing preferred for simplicity
|
||||
nlohmann::ordered_json ryml_to_nlohmann_json(const c4::yml::ConstNodeRef& ryml_node) {
|
||||
if (ryml_node.is_map()) {
|
||||
// handle object
|
||||
auto object = nlohmann::ordered_json::object();
|
||||
for (const auto& child : ryml_node) {
|
||||
object[std::string(child.key().data(), child.key().len)] = ryml_to_nlohmann_json(child);
|
||||
}
|
||||
return object;
|
||||
} else if (ryml_node.is_seq()) {
|
||||
// handle array
|
||||
auto array = nlohmann::ordered_json::array();
|
||||
for (const auto& child : ryml_node) {
|
||||
array.emplace_back(ryml_to_nlohmann_json(child));
|
||||
}
|
||||
return array;
|
||||
} else if (ryml_node.empty() or ryml_node.val_is_null()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
// check type of data
|
||||
const auto& value = ryml_node.val();
|
||||
std::string value_string(value.data(), value.len);
|
||||
const auto value_quoted = ryml_node.is_val_quoted();
|
||||
if (!value_quoted) {
|
||||
// check for numbers and booleans
|
||||
if (ryml_node.val().is_integer()) {
|
||||
return std::stoi(value_string);
|
||||
} else if (ryml_node.val().is_number()) {
|
||||
return std::stod(value_string);
|
||||
} else if (value_string == "true") {
|
||||
return true;
|
||||
} else if (value_string == "false") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// nothing matched so far, should be string
|
||||
return value_string;
|
||||
}
|
||||
}
|
||||
|
||||
std::string load_yaml_content(std::filesystem::path path) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
if (path.extension().string() == ".json") {
|
||||
EVLOG_info << fmt::format("Deprecated: called load_yaml() with .json extension ('{}')", path.string());
|
||||
// try yaml first
|
||||
path.replace_extension(".yaml");
|
||||
} else if (path.extension().string() != ".yaml") {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Trying to load a yaml file without yaml extension (path was '{}')", path.string()));
|
||||
}
|
||||
|
||||
// first check for yaml, if not found try fall back to json and evlog debug deprecated
|
||||
if (fs::exists(path)) {
|
||||
std::ifstream ifs(path.string());
|
||||
return std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
path.replace_extension(".json");
|
||||
|
||||
if (fs::exists(path)) {
|
||||
EVLOG_info << "Deprecated: loaded file in json format";
|
||||
std::ifstream ifs(path.string());
|
||||
return std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
// failed to find yaml and json
|
||||
throw std::runtime_error(fmt::format("File '{}.(yaml|json)' does not exist", path.stem().string()));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace Everest {
|
||||
|
||||
nlohmann::ordered_json load_yaml(const std::filesystem::path& path) {
|
||||
// FIXME (aw): using the static here this isn't a perfect solution
|
||||
const static RymlCallbackInitializer ryml_callback_initializer;
|
||||
|
||||
const auto content = load_yaml_content(path);
|
||||
// FIXME (aw): using parse_in_place would be faster but that will need the file as a whole char buffer
|
||||
const auto tree = ryml::parse_in_arena(ryml::to_csubstr(content));
|
||||
return ryml_to_nlohmann_json(tree.rootref());
|
||||
}
|
||||
|
||||
void save_yaml(const nlohmann::ordered_json& data, const std::filesystem::path& path) {
|
||||
if (!std::filesystem::exists(path.parent_path())) {
|
||||
std::filesystem::create_directory(path.parent_path());
|
||||
}
|
||||
|
||||
// FIXME: saving yaml seems to be quite complicated, but we should be able to just emit json here...
|
||||
std::ofstream ofs(path);
|
||||
ofs << data << std::endl;
|
||||
ofs.close();
|
||||
|
||||
if (!ofs) {
|
||||
const int ec = errno;
|
||||
throw std::runtime_error(
|
||||
fmt::format("Writing user-config to '{}' failed with ec: {}", path.string(), std::strerror(ec)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Everest
|
||||
Reference in New Issue
Block a user