147 lines
5.0 KiB
C++
147 lines
5.0 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
struct ikv_node_t;
|
|
|
|
namespace ikv {
|
|
|
|
/// Runtime value types exposed by iKv.
|
|
enum ValueType {
|
|
nullValue = 0,
|
|
stringValue,
|
|
intValue,
|
|
realValue,
|
|
booleanValue,
|
|
objectValue,
|
|
arrayValue
|
|
};
|
|
|
|
/// On-disk iKv format versions supported for explicit writes.
|
|
enum class Version : unsigned { v1 = 1, v2 = 2 };
|
|
|
|
/// Error raised for invalid types, unsupported operations, parse failures,
|
|
/// and I/O failures.
|
|
class Error : public std::runtime_error {
|
|
public:
|
|
using std::runtime_error::runtime_error;
|
|
};
|
|
|
|
/// RAII handle to an iKv root or a node owned by an iKv tree.
|
|
///
|
|
/// Copies share ownership of the same tree. Child handles remain valid while their
|
|
/// node exists, but should be reacquired after replacing that node or refreshing the root.
|
|
class Value {
|
|
public:
|
|
using ArrayIndex = std::uint32_t;
|
|
|
|
/// Creates an object or array root. Scalar roots are not supported by iKv.
|
|
explicit Value(ValueType type = objectValue, std::string root_name = "root");
|
|
Value(const Value&) = default;
|
|
|
|
/// Structural assignment is unavailable because iKv cannot clone arbitrary subtrees.
|
|
Value& operator=(const Value&) = delete;
|
|
|
|
/// Parses auto-detected iKv1 or iKv2 text.
|
|
static Value parse(const std::string& text);
|
|
|
|
/// Loads an auto-detected iKv1 or iKv2 text file.
|
|
static Value load(const std::string& path);
|
|
|
|
/// Parses an iKv binary buffer. The resulting tree owns any required data.
|
|
static Value fromBinary(const void* data, std::size_t size);
|
|
|
|
/// Loads an auto-detected binary iKv file.
|
|
static Value loadBinary(const std::string& path);
|
|
|
|
/// Returns the node's runtime type, or nullValue for a missing member.
|
|
ValueType type() const noexcept;
|
|
bool isNull() const noexcept;
|
|
bool isString() const noexcept;
|
|
bool isInt() const noexcept;
|
|
bool isDouble() const noexcept;
|
|
bool isBool() const noexcept;
|
|
bool isObject() const noexcept;
|
|
bool isArray() const noexcept;
|
|
bool empty() const;
|
|
std::size_t size() const;
|
|
std::string name() const;
|
|
|
|
bool isMember(const std::string& key) const;
|
|
|
|
/// Looks up an object member. A missing non-const member returns an assignable null Value.
|
|
Value operator[](const std::string& key);
|
|
Value operator[](const char* key);
|
|
Value operator[](const std::string& key) const;
|
|
Value operator[](const char* key) const;
|
|
Value operator[](ArrayIndex index);
|
|
Value operator[](int index);
|
|
Value operator[](ArrayIndex index) const;
|
|
Value operator[](int index) const;
|
|
|
|
/// Returns an object member or a shared copy of fallback when the key is absent.
|
|
Value get(const std::string& key, const Value& fallback) const;
|
|
|
|
/// Assigns a scalar to an object member returned by operator[].
|
|
Value& operator=(const std::string& value);
|
|
Value& operator=(const char* value);
|
|
Value& operator=(std::int64_t value);
|
|
Value& operator=(int value);
|
|
Value& operator=(double value);
|
|
Value& operator=(bool value);
|
|
|
|
/// Adds or replaces an object member and returns a handle to it.
|
|
Value makeObject(const std::string& key);
|
|
|
|
/// Adds an array member. nullValue creates an untyped array.
|
|
Value makeArray(const std::string& key, ValueType element_type = nullValue);
|
|
|
|
/// Appends to an array. Typed arrays reject incompatible values with Error.
|
|
Value append(const std::string& value);
|
|
Value append(const char* value);
|
|
Value append(std::int64_t value);
|
|
Value append(int value);
|
|
Value append(double value);
|
|
Value append(bool value);
|
|
Value appendObject();
|
|
|
|
std::string asString() const;
|
|
std::int64_t asInt64() const;
|
|
int asInt() const;
|
|
double asDouble() const;
|
|
bool asBool() const;
|
|
|
|
/// Writes text or binary output, using iKv2 unless a version is specified.
|
|
void write(const std::string& path, Version version = Version::v2) const;
|
|
void writeBinary(const std::string& path, Version version = Version::v2) const;
|
|
std::vector<std::uint8_t> toBinary(Version version = Version::v2) const;
|
|
|
|
/// Replaces this root in place from a text or binary path while preserving shared handles.
|
|
/// Only mutable root Values can be refreshed; failure leaves the existing tree unchanged.
|
|
void refresh(const std::string& path);
|
|
|
|
private:
|
|
struct Deleter { void operator()(ikv_node_t* node) const noexcept; };
|
|
using Owner = std::shared_ptr<ikv_node_t>;
|
|
|
|
Value(Owner owner, ikv_node_t* node, ikv_node_t* parent, std::string key, bool read_only = false);
|
|
static Value adopt(ikv_node_t* node);
|
|
void require(ValueType expected, const char* operation) const;
|
|
void requireMutable(const char* operation) const;
|
|
void requireObjectParent(const char* operation) const;
|
|
Value appended(std::size_t old_size);
|
|
|
|
Owner owner_;
|
|
ikv_node_t* node_ = nullptr;
|
|
ikv_node_t* parent_ = nullptr;
|
|
std::string key_;
|
|
bool read_only_ = false;
|
|
};
|
|
|
|
} // namespace ikv
|