summaryrefslogtreecommitdiffstats
path: root/include/cepis/json.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/cepis/json.hpp')
-rw-r--r--include/cepis/json.hpp91
1 files changed, 91 insertions, 0 deletions
diff --git a/include/cepis/json.hpp b/include/cepis/json.hpp
new file mode 100644
index 0000000..38bde79
--- /dev/null
+++ b/include/cepis/json.hpp
@@ -0,0 +1,91 @@
+#ifndef CEPIS_JSON_HPP
+#define CEPIS_JSON_HPP
+
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace cepis {
+
+/// An immutable-by-convention JSON value, sufficient for this API's responses.
+///
+/// The library ships its own parser rather than depending on a general-purpose
+/// one: the payloads are flat objects of strings and numbers, and a client
+/// whose only dependency is libcurl is easier to drop into an existing build.
+///
+/// Objects preserve **insertion order** — the API documents a fixed field
+/// order and round-tripping it through `dump()` keeps that visible. Lookup is
+/// a linear scan, which is the faster choice at this size anyway.
+///
+/// The parser accepts UTF-8 input, decodes `\uXXXX` escapes (surrogate pairs
+/// included) into UTF-8, and rejects trailing garbage after the top-level
+/// value.
+class Json {
+ public:
+ /// The six JSON types.
+ enum class Kind : std::uint8_t { Null, Bool, Number, String, Array, Object };
+
+ using Array = std::vector<Json>;
+ using Object = std::vector<std::pair<std::string, Json>>;
+
+ /// Constructs a null value.
+ Json() = default;
+
+ /// Constructs a value of the given kind, empty for the composite kinds.
+ static Json boolean(bool value);
+ static Json number(double value);
+ static Json string(std::string value);
+ static Json array(Array value = {});
+ static Json object(Object value = {});
+
+ /// Parses `text`, returning `std::nullopt` if it is not one well-formed
+ /// JSON value. Callers in this library turn that into `InvalidResponse`.
+ static std::optional<Json> parse(std::string_view text);
+
+ /// Serialises back to compact JSON. Non-ASCII is emitted as raw UTF-8.
+ [[nodiscard]] std::string dump() const;
+
+ [[nodiscard]] Kind kind() const noexcept { return kind_; }
+ [[nodiscard]] bool is_null() const noexcept { return kind_ == Kind::Null; }
+ [[nodiscard]] bool is_bool() const noexcept { return kind_ == Kind::Bool; }
+ [[nodiscard]] bool is_number() const noexcept { return kind_ == Kind::Number; }
+ [[nodiscard]] bool is_string() const noexcept { return kind_ == Kind::String; }
+ [[nodiscard]] bool is_array() const noexcept { return kind_ == Kind::Array; }
+ [[nodiscard]] bool is_object() const noexcept { return kind_ == Kind::Object; }
+
+ /// Typed accessors. Each returns a harmless default for a mismatched kind
+ /// rather than throwing: every field of this API is optional in practice,
+ /// and a wrong type on one field must not cost the whole response.
+ [[nodiscard]] bool as_bool(bool fallback = false) const noexcept;
+ [[nodiscard]] double as_number(double fallback = 0.0) const noexcept;
+ [[nodiscard]] const std::string& as_string() const noexcept;
+ [[nodiscard]] const Array& as_array() const noexcept;
+ [[nodiscard]] const Object& as_object() const noexcept;
+
+ /// Returns the member named `key`, or `nullptr` when this is not an object
+ /// or has no such member.
+ [[nodiscard]] const Json* find(std::string_view key) const noexcept;
+
+ /// Appends a member. Replaces the value if `key` is already present, so the
+ /// original position in the order is kept.
+ void set(std::string key, Json value);
+
+ /// Appends an element. Does nothing unless this is an array.
+ void push_back(Json value);
+
+ private:
+ Kind kind_ = Kind::Null;
+ bool bool_ = false;
+ double number_ = 0.0;
+ std::string string_;
+ Array array_;
+ Object object_;
+};
+
+} // namespace cepis
+
+#endif // CEPIS_JSON_HPP