mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Tiny robust json wrapper to get values
This commit is contained in:
parent
c594e01912
commit
397da70d83
11 changed files with 140 additions and 384 deletions
|
|
@ -20,14 +20,6 @@ BaseNode::~BaseNode()
|
|||
}
|
||||
|
||||
|
||||
std::string BaseNode::GetEntryLabel(const std::string &id)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << ".nodeEntry" << std::setw(4) << std::setfill('0') << id;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
void BaseNode::FromJson(const nlohmann::json &j)
|
||||
{
|
||||
try
|
||||
|
|
@ -35,14 +27,14 @@ void BaseNode::FromJson(const nlohmann::json &j)
|
|||
m_uuid = j["uuid"].get<std::string>();
|
||||
m_internal_data = j["internal-data"];
|
||||
m_type = j["type"].get<std::string>();
|
||||
m_title = j.value("title", "Default node");
|
||||
m_title = JsonReader::get<std::string>(j, "title", "Default node");
|
||||
nlohmann::json posJson = j["position"];
|
||||
|
||||
SetPosition(posJson["x"].get<double>(), posJson["y"].get<double>());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "ERROR: " << e.what() << std::endl;
|
||||
std::cout << "[BASE NODE] ERROR: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
#include "json_reader.h"
|
||||
#include "i_story_page.h"
|
||||
#include "i_story_project.h"
|
||||
#include "story_options.h"
|
||||
|
|
@ -67,8 +68,6 @@ public:
|
|||
BaseNode(const std::string &type, const std::string &typeName, Behavior behavior = BEHAVIOR_EXECUTION);
|
||||
virtual ~BaseNode();
|
||||
|
||||
static std::string GetEntryLabel(const std::string &id);
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
|
||||
void SetPosition(float x, float y);
|
||||
|
|
@ -77,10 +76,6 @@ public:
|
|||
virtual float GetX() const;
|
||||
virtual float GetY() const;
|
||||
|
||||
std::string GetMyEntryLabel() const {
|
||||
return GetEntryLabel(m_uuid);
|
||||
}
|
||||
|
||||
// Coded type, internal use
|
||||
std::string GetType() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ public:
|
|||
|
||||
static InputBinding FromJson(const nlohmann::json& j) {
|
||||
InputBinding ib;
|
||||
ib.paramName = j.value("paramName", "");
|
||||
std::string modeStr = j.value("mode", "connected");
|
||||
ib.paramName = JsonReader::get<std::string>(j,"paramName", "");
|
||||
std::string modeStr = JsonReader::get<std::string>(j, "mode", "connected");
|
||||
ib.mode = (modeStr == "constant") ? MODE_CONSTANT : MODE_CONNECTED;
|
||||
ib.constantValue = j.value("constantValue", "");
|
||||
ib.constantValue = JsonReader::get<std::string>(j, "constantValue", "");
|
||||
return ib;
|
||||
}
|
||||
};
|
||||
|
|
@ -48,8 +48,8 @@ public:
|
|||
|
||||
static OutputMapping FromJson(const nlohmann::json& j) {
|
||||
OutputMapping om;
|
||||
om.returnValueName = j.value("returnValueName", "");
|
||||
om.targetVariable = j.value("targetVariable", "");
|
||||
om.returnValueName = JsonReader::get<std::string>(j, "returnValueName", "");
|
||||
om.targetVariable = JsonReader::get<std::string>(j, "targetVariable", "");
|
||||
return om;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ public:
|
|||
|
||||
static Parameter FromJson(const nlohmann::json& j) {
|
||||
Parameter p;
|
||||
p.name = j.value("name", "");
|
||||
p.type = j.value("type", "int");
|
||||
p.defaultValue = j.value("defaultValue", "");
|
||||
p.name = JsonReader::get<std::string>(j, "name", "");
|
||||
p.type = JsonReader::get<std::string>(j, "type", "int");
|
||||
p.defaultValue = JsonReader::get<std::string>(j, "defaultValue", "");
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ public:
|
|||
|
||||
static ReturnValue FromJson(const nlohmann::json& j) {
|
||||
ReturnValue rv;
|
||||
rv.name = j.value("name", "");
|
||||
rv.type = j.value("type", "int");
|
||||
rv.name = JsonReader::get<std::string>(j, "name", "");
|
||||
rv.type = JsonReader::get<std::string>(j, "type", "int");
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
|
@ -32,6 +32,8 @@ public:
|
|||
SetWeight(900); // High weight, near the end
|
||||
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
|
||||
SetupExecutionPorts(true, 0, true); // Has input, no output (it's the end)
|
||||
|
||||
SaveData();
|
||||
}
|
||||
|
||||
void Initialize() override {
|
||||
|
|
@ -39,7 +41,7 @@ public:
|
|||
nlohmann::json j = GetInternalData();
|
||||
m_returnValues.clear();
|
||||
|
||||
m_exitLabel = j.value("exitLabel", "Return");
|
||||
m_exitLabel = JsonReader::get<std::string>(j, "exitLabel", "Return");
|
||||
|
||||
if (j.contains("returnValues") && j["returnValues"].is_array()) {
|
||||
for (const auto& rvJson : j["returnValues"]) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "sys_lib.h"
|
||||
#include "assembly_generator_chip32_tac.h"
|
||||
#include "nodes_factory.h"
|
||||
#include "json_reader.h"
|
||||
|
||||
StoryProject::StoryProject(ILogger &log)
|
||||
: m_log(log)
|
||||
|
|
@ -282,8 +283,6 @@ bool StoryProject::ModelFromJson(const nlohmann::json &model, NodesFactory &fact
|
|||
|
||||
std::string type = element["type"].get<std::string>();
|
||||
|
||||
std::cout << "!!!!!!!!!!!!!!!!!" << type << std::endl;
|
||||
|
||||
auto n = factory.CreateNode(type);
|
||||
if (n)
|
||||
{
|
||||
|
|
|
|||
64
core/story-manager/src/utils/json_reader.h
Normal file
64
core/story-manager/src/utils/json_reader.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <json.hpp>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class JsonReader {
|
||||
public:
|
||||
/**
|
||||
* @brief Récupère une valeur d'un objet JSON.
|
||||
* * Cette fonction est similaire à value(), mais ajoute un logging et
|
||||
* lance optionnellement une exception si la clé est manquante ou si la conversion échoue.
|
||||
* * @tparam ValueType Le type C++ de la valeur attendue.
|
||||
* @param j L'objet JSON dans lequel chercher.
|
||||
* @param key La clé (champ) à chercher.
|
||||
* @param defaultValue La valeur à retourner en cas d'échec ou de clé manquante.
|
||||
* @param logOnError Si true, affiche un message d'erreur sur cerr.
|
||||
* @param throwOnError Si true, lance une std::runtime_error en cas d'échec.
|
||||
* @return ValueType La valeur trouvée ou la defaultValue.
|
||||
*/
|
||||
template<typename ValueType>
|
||||
static ValueType get(
|
||||
const json& j,
|
||||
const std::string& key,
|
||||
const ValueType& defaultValue,
|
||||
bool logOnError = true,
|
||||
bool throwOnError = false
|
||||
) {
|
||||
// 1. Vérification de la présence de la clé
|
||||
if (!j.contains(key)) {
|
||||
const std::string error_msg = "Clé '" + key + "' manquante dans l'objet JSON. Utilisation de la valeur par défaut.";
|
||||
|
||||
if (logOnError) {
|
||||
std::cout << "⚠️ ERREUR (JsonGetter) : " << error_msg << std::endl;
|
||||
}
|
||||
if (throwOnError) {
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// 2. Récupération de la valeur avec gestion de l'erreur de type
|
||||
try {
|
||||
// Utiliser 'at()' est plus sûr car il lève une exception en cas de type incorrect
|
||||
// si la conversion n'est pas possible, ce qui est mieux que value() pour le diagnostic.
|
||||
return j.at(key).get<ValueType>();
|
||||
|
||||
} catch (const nlohmann::json::type_error& e) {
|
||||
// Le type existe, mais il est incompatible (ex: string au lieu de int)
|
||||
const std::string error_msg = "Erreur de type pour la clé '" + key + "'. Attendu: " + typeid(ValueType).name() + ". Détails: " + e.what() + ". Utilisation de la valeur par défaut.";
|
||||
|
||||
if (logOnError) {
|
||||
std::cout << "❌ ERREUR (JsonGetter) : " << error_msg << std::endl;
|
||||
}
|
||||
if (throwOnError) {
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
// Note: l'exception out_of_range est gérée par le 'contains' ci-dessus.
|
||||
}
|
||||
};
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
#include "json_wrapper.h"
|
||||
#include "json.hpp"
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class JsonWrapper::Impl {
|
||||
public:
|
||||
json j;
|
||||
};
|
||||
|
||||
JsonWrapper::JsonWrapper() : impl(std::make_unique<Impl>()) {
|
||||
impl->j = json::object();
|
||||
}
|
||||
|
||||
JsonWrapper::JsonWrapper(JsonWrapper&& other) noexcept : impl(std::move(other.impl)) {}
|
||||
|
||||
JsonWrapper& JsonWrapper::operator=(JsonWrapper&& other) noexcept {
|
||||
if (this != &other) {
|
||||
impl = std::move(other.impl);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonWrapper::JsonWrapper(const std::string& jsonString) : impl(std::make_unique<Impl>()) {
|
||||
try {
|
||||
impl->j = json::parse(jsonString);
|
||||
} catch (...) {
|
||||
impl->j = json::object();
|
||||
}
|
||||
}
|
||||
|
||||
JsonWrapper::~JsonWrapper() = default;
|
||||
|
||||
bool JsonWrapper::hasKey(const std::string& key) const {
|
||||
return impl->j.contains(key);
|
||||
}
|
||||
|
||||
std::optional<std::string> JsonWrapper::getString(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<std::string>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> JsonWrapper::getInt(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<int>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<double> JsonWrapper::getDouble(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<double>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<bool> JsonWrapper::getBool(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<bool>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void JsonWrapper::setString(const std::string& key, const std::string& value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setInt(const std::string& key, int value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setDouble(const std::string& key, double value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setBool(const std::string& key, bool value) {
|
||||
impl->j[key] = value;
|
||||
}
|
||||
|
||||
void JsonWrapper::setArrayImpl(const std::string& key, const void* values, size_t typeHash) {
|
||||
// Ici on doit caster et stocker dans json, mais sans RTTI avancé on limite
|
||||
// Par exemple on peut specialiser cette méthode pour les types courants
|
||||
if (typeHash == typeid(int).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<int>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else if (typeHash == typeid(double).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<double>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else if (typeHash == typeid(std::string).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<std::string>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else if (typeHash == typeid(bool).hash_code()) {
|
||||
const auto& v = *reinterpret_cast<const std::vector<bool>*>(values);
|
||||
impl->j[key] = v;
|
||||
} else {
|
||||
// Pour les types non pris en charge, on peut lever une exception ou ignorer
|
||||
throw std::runtime_error("Type non supporté pour setArray");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::vector<T>> JsonWrapper::getArrayImpl(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<std::vector<T>>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T JsonWrapper::getImpl(const std::string& key, std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.at(key).get<T>();
|
||||
} catch (...) {
|
||||
errorKey = key;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T JsonWrapper::asImpl(std::string& errorKey) const {
|
||||
try {
|
||||
return impl->j.get<T>();
|
||||
} catch (...) {
|
||||
errorKey = "[root]";
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
JsonWrapper JsonWrapper::fromImpl(const void* value, size_t typeHash) {
|
||||
JsonWrapper wrapper;
|
||||
if (typeHash == typeid(int).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const int*>(value);
|
||||
} else if (typeHash == typeid(double).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const double*>(value);
|
||||
} else if (typeHash == typeid(std::string).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const std::string*>(value);
|
||||
} else if (typeHash == typeid(bool).hash_code()) {
|
||||
wrapper.impl->j = *reinterpret_cast<const bool*>(value);
|
||||
} else {
|
||||
// Si c’est un type complexe, il faut une surcharge ou un specialization dans le .cpp
|
||||
// Exemple: si T a to_json/from_json, on peut faire une conversion nlohmann::json value = T;
|
||||
// Ici on fait un cast "générique" (moins safe)
|
||||
// On peut faire un throw ici pour forcer l’utilisateur à spécialiser.
|
||||
throw std::runtime_error("Type non supporté pour from()");
|
||||
}
|
||||
return std::move(wrapper);
|
||||
}
|
||||
|
||||
// Explicit instantiations for getArrayImpl, getImpl, asImpl for common types
|
||||
|
||||
template std::optional<std::vector<int>> JsonWrapper::getArrayImpl<int>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<double>> JsonWrapper::getArrayImpl<double>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<std::string>> JsonWrapper::getArrayImpl<std::string>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<bool>> JsonWrapper::getArrayImpl<bool>(const std::string&, std::string&) const;
|
||||
|
||||
template int JsonWrapper::getImpl<int>(const std::string&, std::string&) const;
|
||||
template double JsonWrapper::getImpl<double>(const std::string&, std::string&) const;
|
||||
template std::string JsonWrapper::getImpl<std::string>(const std::string&, std::string&) const;
|
||||
template bool JsonWrapper::getImpl<bool>(const std::string&, std::string&) const;
|
||||
|
||||
template int JsonWrapper::asImpl<int>(std::string&) const;
|
||||
template double JsonWrapper::asImpl<double>(std::string&) const;
|
||||
template std::string JsonWrapper::asImpl<std::string>(std::string&) const;
|
||||
template bool JsonWrapper::asImpl<bool>(std::string&) const;
|
||||
|
||||
std::string JsonWrapper::dump(int indent) const {
|
||||
if (indent < 0) return impl->j.dump();
|
||||
return impl->j.dump(indent);
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
class JsonWrapper {
|
||||
public:
|
||||
JsonWrapper();
|
||||
explicit JsonWrapper(const std::string& jsonString);
|
||||
~JsonWrapper();
|
||||
|
||||
// Ajout move constructor et move assign
|
||||
JsonWrapper(JsonWrapper&&) noexcept;
|
||||
JsonWrapper& operator=(JsonWrapper&&) noexcept;
|
||||
|
||||
// Supprimer copie pour éviter erreur
|
||||
JsonWrapper(const JsonWrapper&) = delete;
|
||||
JsonWrapper& operator=(const JsonWrapper&) = delete;
|
||||
|
||||
bool hasKey(const std::string& key) const;
|
||||
|
||||
// Valeurs simples
|
||||
std::optional<std::string> getString(const std::string& key, std::string& errorKey) const;
|
||||
std::optional<int> getInt(const std::string& key, std::string& errorKey) const;
|
||||
std::optional<double> getDouble(const std::string& key, std::string& errorKey) const;
|
||||
std::optional<bool> getBool(const std::string& key, std::string& errorKey) const;
|
||||
|
||||
void setString(const std::string& key, const std::string& value);
|
||||
void setInt(const std::string& key, int value);
|
||||
void setDouble(const std::string& key, double value);
|
||||
void setBool(const std::string& key, bool value);
|
||||
|
||||
// Array génériques
|
||||
template <typename T>
|
||||
void setArray(const std::string& key, const std::vector<T>& values) {
|
||||
setArrayImpl(key, reinterpret_cast<const void*>(&values), typeid(T).hash_code());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::vector<T>> getArray(const std::string& key, std::string& errorKey) const {
|
||||
return getArrayImpl<T>(key, errorKey);
|
||||
}
|
||||
|
||||
// Sérialisation/Désérialisation générique
|
||||
template <typename T>
|
||||
T get(const std::string& key, std::string& errorKey) const {
|
||||
return getImpl<T>(key, errorKey);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T as(std::string& errorKey) const {
|
||||
return asImpl<T>(errorKey);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static JsonWrapper from(const T& value) {
|
||||
return fromImpl(reinterpret_cast<const void*>(&value), typeid(T).hash_code());
|
||||
}
|
||||
|
||||
// Dump JSON
|
||||
std::string dump(int indent = -1) const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
// Implémentations privées non template (définies dans .cpp)
|
||||
void setArrayImpl(const std::string& key, const void* values, size_t typeHash);
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::vector<T>> getArrayImpl(const std::string& key, std::string& errorKey) const;
|
||||
|
||||
template <typename T>
|
||||
T getImpl(const std::string& key, std::string& errorKey) const;
|
||||
|
||||
template <typename T>
|
||||
T asImpl(std::string& errorKey) const;
|
||||
|
||||
static JsonWrapper fromImpl(const void* value, size_t typeHash);
|
||||
};
|
||||
|
|
@ -48,8 +48,6 @@ add_executable(${PROJECT_NAME}
|
|||
../story-manager/src/nodes/break_node.cpp
|
||||
../story-manager/src/nodes/continue_node.cpp
|
||||
|
||||
../story-manager/src/utils/json_wrapper.cpp
|
||||
|
||||
../chip32/chip32_assembler.cpp
|
||||
../chip32/chip32_vm.c
|
||||
../chip32/chip32_binary_format.c
|
||||
|
|
|
|||
|
|
@ -1,101 +1,75 @@
|
|||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include "json_wrapper.h"
|
||||
#include <catch2/matchers/catch_matchers_vector.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
#include "json_reader.h"
|
||||
#include "json.hpp"
|
||||
using Catch::Matchers::WithinRel;
|
||||
using namespace nlohmann;
|
||||
|
||||
TEST_CASE("JsonWrapper basic types", "[json]") {
|
||||
JsonWrapper json;
|
||||
json.setString("name", "Alice");
|
||||
json.setInt("age", 30);
|
||||
json.setDouble("pi", 3.14159);
|
||||
json.setBool("is_valid", true);
|
||||
using namespace Catch::Matchers;
|
||||
|
||||
std::string err;
|
||||
TEST_CASE("JsonReader::get - Scénarios de base", "[JsonReader]") {
|
||||
json data = R"({
|
||||
"nom": "Bob",
|
||||
"age": 42,
|
||||
"is_admin": true
|
||||
})"_json;
|
||||
|
||||
SECTION("Get string") {
|
||||
auto name = json.getString("name", err);
|
||||
REQUIRE(name.has_value());
|
||||
REQUIRE(name.value() == "Alice");
|
||||
// --- Scénario 1 : Succès (Clé présente, Type correct) ---
|
||||
SECTION("Succès - Récupération d'un entier") {
|
||||
int age = JsonReader::get<int>(data, "age", 0, false, false);
|
||||
REQUIRE(age == 42);
|
||||
}
|
||||
|
||||
SECTION("Get int") {
|
||||
auto age = json.getInt("age", err);
|
||||
REQUIRE(age.has_value());
|
||||
REQUIRE(age.value() == 30);
|
||||
SECTION("Succès - Récupération d'une chaîne") {
|
||||
std::string nom = JsonReader::get<std::string>(data, "nom", "N/A", false, false);
|
||||
REQUIRE(nom == "Bob");
|
||||
}
|
||||
|
||||
SECTION("Get double") {
|
||||
auto pi = json.getDouble("pi", err);
|
||||
REQUIRE(pi.has_value());
|
||||
REQUIRE_THAT(pi.value(), WithinRel(3.14159, 1e-6));
|
||||
}
|
||||
|
||||
SECTION("Get bool") {
|
||||
auto valid = json.getBool("is_valid", err);
|
||||
REQUIRE(valid.has_value());
|
||||
REQUIRE(valid.value() == true);
|
||||
// --- Scénario 2 : Clé Manquante (Retourne la valeur par défaut) ---
|
||||
SECTION("Clé Manquante - Retourne la valeur par défaut") {
|
||||
// Log désactivé pour ne pas polluer la sortie de test
|
||||
double taux = JsonReader::get<double>(data, "taux_inexistant", 1.5, false, false);
|
||||
REQUIRE(taux == 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonWrapper arrays", "[json]") {
|
||||
JsonWrapper json;
|
||||
TEST_CASE("JsonReader::get - Gestion des Erreurs", "[JsonReader][Erreur]") {
|
||||
json data_err = R"({
|
||||
"taux_texte": "0.99",
|
||||
"liste": [1, 2]
|
||||
})"_json;
|
||||
|
||||
json.setArray<int>("scores", {10, 20, 30});
|
||||
json.setArray<std::string>("tags", {"news", "tech"});
|
||||
json.setArray<double>("measurements", {1.1, 2.2, 3.3});
|
||||
json.setArray<bool>("flags", {true, false, true});
|
||||
// --- Scénario 3 : Erreur de Type (Retourne la valeur par défaut) ---
|
||||
SECTION("Erreur de Type - Retourne la valeur par défaut (Int vs String)") {
|
||||
// Tente de lire "0.99" (string) comme un int.
|
||||
int taux = JsonReader::get<int>(data_err, "taux_texte", -1, false, false);
|
||||
REQUIRE(taux == -1);
|
||||
}
|
||||
|
||||
std::string err;
|
||||
// --- Scénario 4 : Clé Manquante (Lance une exception) ---
|
||||
SECTION("Clé Manquante - Lance une runtime_error") {
|
||||
REQUIRE_THROWS_AS(
|
||||
JsonReader::get<std::string>(data_err, "cle_manquante_fatale", "N/A", false, true),
|
||||
std::runtime_error
|
||||
);
|
||||
// Vérifie le message pour s'assurer qu'il mentionne la clé
|
||||
REQUIRE_THROWS_WITH(
|
||||
JsonReader::get<std::string>(data_err, "cle_manquante_fatale", "N/A", false, true),
|
||||
ContainsSubstring("cle_manquante_fatale")
|
||||
);
|
||||
}
|
||||
|
||||
auto scores = json.getArray<int>("scores", err);
|
||||
REQUIRE(scores.has_value());
|
||||
REQUIRE(scores->size() == 3);
|
||||
REQUIRE(scores->at(1) == 20);
|
||||
|
||||
auto tags = json.getArray<std::string>("tags", err);
|
||||
REQUIRE(tags.has_value());
|
||||
REQUIRE(tags->at(0) == "news");
|
||||
|
||||
auto measurements = json.getArray<double>("measurements", err);
|
||||
REQUIRE(measurements.has_value());
|
||||
REQUIRE_THAT(measurements->at(2), WithinRel(3.3, 1e-6));
|
||||
|
||||
auto flags = json.getArray<bool>("flags", err);
|
||||
REQUIRE(flags.has_value());
|
||||
REQUIRE(flags->at(0) == true);
|
||||
}
|
||||
|
||||
struct Connection {
|
||||
std::string host;
|
||||
int port;ccM9XAGZ$mz^b*52T5p&sMA@ujPbCUNW
|
||||
};
|
||||
|
||||
// from_json / to_json must be defined for Connection
|
||||
inline void to_json(nlohmann::json& j, const Connection& c) {
|
||||
j = nlohmann::json{{"host", c.host}, {"port", c.port}};
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json& j, Connection& c) {
|
||||
j.at("host").get_to(c.host);
|
||||
j.at("port").get_to(c.port);
|
||||
}
|
||||
|
||||
template Connection JsonWrapper::asImpl<Connection>(std::string&) const;
|
||||
template Connection JsonWrapper::getImpl<Connection>(const std::string&, std::string&) const;
|
||||
template std::optional<std::vector<Connection>> JsonWrapper::getArrayImpl<Connection>(const std::string&, std::string&) const;
|
||||
|
||||
|
||||
TEST_CASE("JsonWrapper generic serialization/deserialization", "[json][struct]") {
|
||||
|
||||
|
||||
Connection original{"127.0.0.1", 5000};
|
||||
auto wrapper = JsonWrapper::from(original);
|
||||
|
||||
std::string err;
|
||||
auto restored = wrapper.as<Connection>(err);
|
||||
|
||||
REQUIRE(restored.host == "127.0.0.1");
|
||||
REQUIRE(restored.port == 5000);
|
||||
}
|
||||
// --- Scénario 5 : Erreur de Type (Lance une exception) ---
|
||||
SECTION("Erreur de Type - Lance une runtime_error") {
|
||||
// Tente de lire une [array] comme un int
|
||||
REQUIRE_THROWS_AS(
|
||||
JsonReader::get<int>(data_err, "liste", 0, false, true),
|
||||
std::runtime_error
|
||||
);
|
||||
// Vérifie le message pour s'assurer qu'il mentionne la clé
|
||||
REQUIRE_THROWS_WITH(
|
||||
JsonReader::get<int>(data_err, "liste", 0, false, true),
|
||||
ContainsSubstring("liste") && ContainsSubstring("Erreur de type")
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue