// JSON for packutils' requests, results and state files. The reader is strict: one document, // duplicate members and bad escapes refused, numbers kept as their text. The writer prints // Packwand's canonical form (2-space indent, `": "`, LF, a trailing newline, raw UTF-8, only `"`, // `\` and control characters escaped), so a state file written here has the bytes Packwand's // own writer gives. AI-authored (Claude). #pragma once #include #include #include #include #include #include #include namespace packutils::json { struct Member; struct Value { enum class Type : std::uint8_t { null, boolean, number, string, array, object }; Type type = Type::null; bool boolean = false; /// A number's text, or a string's decoded UTF-8. std::string text; std::vector items; /// In document order. std::vector members; [[nodiscard]] static Value of_string(std::string_view text); [[nodiscard]] static Value of_integer(std::int64_t number); [[nodiscard]] static Value of_bool(bool on); [[nodiscard]] static Value array(); [[nodiscard]] static Value object(); /// An object's member, or null. [[nodiscard]] const Value *get(std::string_view key) const; /// Appends a member to an object (the caller keeps keys unique). void put(std::string_view key, Value value); void push(Value value); [[nodiscard]] bool is(Type wanted) const { return type == wanted; } /// A string member's text, or nothing when it is missing or not a string. [[nodiscard]] std::optional string_at(std::string_view key) const; /// A whole number in `[min, max]` (its text parsed exactly), or nothing. [[nodiscard]] std::optional integer(std::int64_t min, std::int64_t max) const; }; struct Member { std::string key; Value value; }; /// Why a document did not parse, with its byte offset. struct ParseError { std::string message; std::size_t offset = 0; }; [[nodiscard]] std::expected parse(std::string_view bytes); /// The canonical document with its trailing newline. [[nodiscard]] std::string canonical(const Value &value); /// One line, no spaces: for documents only a program reads. [[nodiscard]] std::string compact(const Value &value); } // namespace packutils::json