29 using ObjectType = std::map<std::string, Value, std::less<>>;
35 using DataVariant = std::variant<std::nullptr_t, bool, std::int64_t, double, std::string, std::shared_ptr<ObjectType>, std::shared_ptr<ArrayType>>;
39 static constexpr std::size_t idx_null = 0;
40 static constexpr std::size_t idx_bool = 1;
41 static constexpr std::size_t idx_int = 2;
42 static constexpr std::size_t idx_double = 3;
43 static constexpr std::size_t idx_string = 4;
44 static constexpr std::size_t idx_object = 5;
45 static constexpr std::size_t idx_array = 6;
68 : data_(static_cast<std::int64_t>(i))
86 : data_(static_cast<double>(f))
92 : data_(std::string(s))
104 : data_(std::string(s))
110 : data_(DeepCopy(other.data_))
117 if (
this != &other) {
118 data_ = DeepCopy(other.data_);
136 v.data_ = std::make_shared<ObjectType>();
144 v.data_ = std::make_shared<ArrayType>();
149 [[nodiscard]]
auto IsNull() const ->
bool {
return data_.index() == idx_null; }
152 [[nodiscard]]
auto IsBoolean() const ->
bool {
return data_.index() == idx_bool; }
155 [[nodiscard]]
auto IsInteger() const ->
bool {
return data_.index() == idx_int; }
158 [[nodiscard]]
auto IsDouble() const ->
bool {
return data_.index() == idx_double; }
164 [[nodiscard]]
auto IsString() const ->
bool {
return data_.index() == idx_string; }
167 [[nodiscard]]
auto IsObject() const ->
bool {
return data_.index() == idx_object; }
170 [[nodiscard]]
auto IsArray() const ->
bool {
return data_.index() == idx_array; }
177 template <
typename T>
178 [[nodiscard]]
auto Get() const -> T
180 if constexpr (std::is_same_v<T, bool>) {
181 return std::get<bool>(data_);
183 else if constexpr (std::is_same_v<T, int>) {
184 return static_cast<int>(std::get<std::int64_t>(data_));
186 else if constexpr (std::is_same_v<T, std::int64_t>) {
187 return std::get<std::int64_t>(data_);
189 else if constexpr (std::is_same_v<T, double>) {
191 return static_cast<double>(std::get<std::int64_t>(data_));
193 return std::get<double>(data_);
195 else if constexpr (std::is_same_v<T, float>) {
197 return static_cast<float>(std::get<std::int64_t>(data_));
199 return static_cast<float>(std::get<double>(data_));
201 else if constexpr (std::is_same_v<T, std::string>) {
202 return std::get<std::string>(data_);
205 static_assert(
sizeof(T) == 0,
"Unsupported type for Value::Get<T>()");
210 [[nodiscard]]
auto Contains(std::string_view key)
const ->
bool
215 const auto& obj = *std::get<std::shared_ptr<ObjectType>>(data_);
216 return obj.find(key) != obj.end();
223 data_ = std::make_shared<ObjectType>();
225 auto& obj = *std::get<std::shared_ptr<ObjectType>>(data_);
232 static const Value null_value;
236 const auto& obj = *std::get<std::shared_ptr<ObjectType>>(data_);
237 auto iter = obj.find(key);
238 if (iter == obj.end()) {
251 return *std::get<std::shared_ptr<ObjectType>>(data_);
259 data_ = std::make_shared<ObjectType>();
261 return *std::get<std::shared_ptr<ObjectType>>(data_);
267 const Value* current =
this;
268 std::string path_str(path);
269 std::istringstream stream(path_str);
272 while (std::getline(stream, segment,
'.')) {
274 return NotFoundError(
"Path segment '" + segment +
"' not found: parent is not an object");
277 return NotFoundError(
"Path segment '" + segment +
"' not found");
279 current = &(*current)[segment];
287 Value* current =
this;
288 std::string path_str(path);
289 std::istringstream stream(path_str);
291 std::vector<std::string> segments;
293 while (std::getline(stream, segment,
'.')) {
294 segments.push_back(segment);
297 for (std::size_t i = 0; i < segments.size() - 1; ++i) {
298 if (!current->
Contains(segments[i]) || !(*current)[segments[i]].IsObject()) {
301 current = &(*current)[segments[i]];
304 if (!segments.empty()) {
305 (*current)[segments.back()] = value;
310 [[nodiscard]]
auto HasPath(std::string_view path)
const ->
bool {
return GetAtPath(path).ok(); }
318 if (!base.IsObject() || !overlay.IsObject()) {
323 for (
const auto& [key, value] : overlay.Items()) {
325 result[key] =
Merge(result[key], value);
337 [[nodiscard]]
auto Dump(
int indent = 0) const -> std::
string
339 std::ostringstream stream;
340 DumpImpl(stream, indent, 0);
347 if (data_.index() != other.data_.index()) {
354 return Get<bool>() == other.Get<
bool>();
357 return Get<std::int64_t>() == other.Get<std::int64_t>();
360#pragma GCC diagnostic push
361#pragma GCC diagnostic ignored "-Wfloat-equal"
362 return Get<double>() == other.Get<
double>();
363#pragma GCC diagnostic pop
366 return Get<std::string>() == other.Get<std::string>();
369 return Items() == other.Items();
372 return *std::get<std::shared_ptr<ArrayType>>(data_) == *std::get<std::shared_ptr<ArrayType>>(other.data_);
381 [[nodiscard]]
static auto DeepCopy(
const DataVariant& src) -> DataVariant
383 if (
auto* obj = std::get_if<std::shared_ptr<ObjectType>>(&src)) {
384 return std::make_shared<ObjectType>(**obj);
386 if (
auto* arr = std::get_if<std::shared_ptr<ArrayType>>(&src)) {
387 return std::make_shared<ArrayType>(**arr);
392 static void EscapeString(std::ostringstream& stream,
const std::string& str)
395 for (
char ch : str) {
419 static void WriteIndent(std::ostringstream& stream,
int indent,
int depth)
423 for (
int i = 0; i < indent * depth; ++i) {
429 void DumpImpl(std::ostringstream& stream,
int indent,
int depth)
const
435 stream << (Get<bool>() ?
"true" :
"false");
438 stream << Get<std::int64_t>();
441 std::ostringstream double_stream;
442 double_stream << Get<double>();
443 auto str = double_stream.str();
446 if (str.find(
'.') == std::string::npos && str.find(
'e') == std::string::npos && str.find(
'E') == std::string::npos) {
451 EscapeString(stream, Get<std::string>());
454 const auto& obj =
Items();
457 for (
const auto& [key, val] : obj) {
462 WriteIndent(stream, indent, depth + 1);
466 stream <<
'"' << key <<
'"' <<
':';
470 val.DumpImpl(stream, indent, depth + 1);
473 WriteIndent(stream, indent, depth);
478 const auto& arr = *std::get<std::shared_ptr<ArrayType>>(data_);
481 for (
const auto& val : arr) {
486 WriteIndent(stream, indent, depth + 1);
487 val.DumpImpl(stream, indent, depth + 1);
490 WriteIndent(stream, indent, depth);
A value-or-error type, similar to std::expected (C++23).
Definition status.h:100
A self-contained, recursive value type for configuration data.
Definition value.h:26
auto operator=(const Value &other) -> Value &
Deep-copy assignment.
Definition value.h:115
Value(std::nullptr_t)
Constructs a null value.
Definition value.h:55
auto GetAtPath(std::string_view path) const -> StatusOr< Value >
Gets a value at a dot-separated path.
Definition value.h:265
auto operator==(const Value &other) const -> bool
Value equality (deep comparison for objects/arrays).
Definition value.h:345
auto IsBoolean() const -> bool
Returns true if this value is a boolean.
Definition value.h:152
auto Items() const -> const ObjectType &
Returns const reference to the object entries.
Definition value.h:245
Value()
Constructs a null value.
Definition value.h:49
static auto Merge(const Value &base, const Value &overlay) -> Value
Deep-merges two object values; overlay takes precedence.
Definition value.h:316
Value(const Value &other)
Deep-copies the value (recursive for objects / arrays).
Definition value.h:109
auto operator!=(const Value &other) const -> bool
Value inequality.
Definition value.h:378
auto Items() -> ObjectType &
Returns mutable reference to the object entries, promoting null → object.
Definition value.h:256
auto Contains(std::string_view key) const -> bool
Checks whether the given key exists in an object value.
Definition value.h:210
Value(int i)
Constructs an integer value from int.
Definition value.h:67
auto IsNumber() const -> bool
Returns true if this value is any numeric type (integer or double).
Definition value.h:161
Value(std::string_view s)
Constructs a string value from string_view.
Definition value.h:103
Value(std::int64_t i)
Constructs an integer value.
Definition value.h:73
auto IsObject() const -> bool
Returns true if this value is an object (key-value map).
Definition value.h:167
std::map< std::string, Value, std::less<> > ObjectType
Ordered map of string keys to Value children.
Definition value.h:29
static auto Array() -> Value
Creates an empty array value.
Definition value.h:141
auto IsNull() const -> bool
Returns true if this value is null.
Definition value.h:149
auto IsDouble() const -> bool
Returns true if this value is a double.
Definition value.h:158
Value(bool b)
Constructs a boolean value.
Definition value.h:61
void SetAtPath(std::string_view path, const Value &value)
Sets a value at a dot-separated path, creating intermediate objects.
Definition value.h:285
auto HasPath(std::string_view path) const -> bool
Checks if a path exists in the data.
Definition value.h:310
Value(Value &&) noexcept=default
Move constructor (default, transfers ownership).
Value(std::string s)
Constructs a string value.
Definition value.h:97
std::vector< Value > ArrayType
Ordered sequence of Value elements.
Definition value.h:32
static auto Object() -> Value
Creates an empty object value.
Definition value.h:133
auto IsInteger() const -> bool
Returns true if this value is an integer.
Definition value.h:155
auto IsString() const -> bool
Returns true if this value is a string.
Definition value.h:164
Value(const char *s)
Constructs a string value from a C string.
Definition value.h:91
auto IsArray() const -> bool
Returns true if this value is an array.
Definition value.h:170
auto Dump(int indent=0) const -> std::string
Produces a JSON-like string representation.
Definition value.h:337
Value(float f)
Constructs a double value from float.
Definition value.h:85
auto operator[](const std::string &key) -> Value &
Accesses or creates a child by key, promoting null → object.
Definition value.h:220
Value(double d)
Constructs a double value.
Definition value.h:79
auto operator[](const std::string &key) const -> const Value &
Read-only access to a child by key (returns static null for missing keys).
Definition value.h:230
auto Get() const -> T
Extracts the stored value as the requested type.
Definition value.h:178
C++20 compile-time type-safe configuration library.
Definition conf.h:13
auto NotFoundError(std::string message) -> Status
Returns a NotFound error status.
Definition status.h:55