#pragma once #include "json_token_enum.hpp" #include "parser_interface.hpp" #include #include #include /** * @brief JSON-specific implementation of CommitRequestParser. * * This parser uses the weaseljson library to parse JSON-formatted * commit requests into CommitRequest objects. */ class JsonCommitRequestParser : public CommitRequestParser { public: // Parser state enum class ParseState { Root, PreconditionsArray, PreconditionObject, OperationsArray, OperationObject }; private: struct PreconditionParseState { Precondition::Type type; std::optional version; // These are owned by CommitRequest::arena std::optional key; std::optional begin; std::optional end; }; /** * @brief Internal state for parsing an operation during JSON processing. */ struct OperationParseState { Operation::Type type; // These are owned by CommitRequest::arena std::optional key; std::optional value; std::optional begin; std::optional end; }; struct ParserContext { using ArenaString = std::basic_string, ArenaStlAllocator>; ParseState current_state = ParseState::Root; JsonTokenType current_key_token; // Only used if we need to accumulate the current key ArenaString current_key; ArenaString current_string; ArenaString current_number; bool in_key = false; const char *parse_error = nullptr; bool parse_complete = false; bool has_read_version_been_set = false; // Current objects being parsed PreconditionParseState current_precondition{}; OperationParseState current_operation{}; // Parsing state for nested structures ArenaString precondition_type; ArenaString operation_type; // Constructor to initialize arena-allocated containers explicit ParserContext(ArenaAllocator *arena) : current_key(ArenaStlAllocator(arena)), current_string(ArenaStlAllocator(arena)), current_number(ArenaStlAllocator(arena)), precondition_type(ArenaStlAllocator(arena)), operation_type(ArenaStlAllocator(arena)) { has_read_version_been_set = false; } void reset_arena_memory(ArenaAllocator *arena) { current_key = ArenaString{ArenaStlAllocator(arena)}; current_string = ArenaString{ArenaStlAllocator(arena)}; current_number = ArenaString{ArenaStlAllocator(arena)}; in_key = false; current_precondition = {}; current_operation = {}; precondition_type = ArenaString{ArenaStlAllocator(arena)}; operation_type = ArenaString{ArenaStlAllocator(arena)}; current_state = ParseState::Root; } }; WeaselJsonParser *json_parser_ = nullptr; std::unique_ptr parser_context_; CommitRequest *current_request_ = nullptr; static const WeaselJsonCallbacks json_callbacks; public: /** * @brief Construct a new JsonCommitRequestParser. */ JsonCommitRequestParser(); /** * @brief Destructor - cleans up any active parser. */ ~JsonCommitRequestParser(); // Non-copyable but movable JsonCommitRequestParser(const JsonCommitRequestParser &) = delete; JsonCommitRequestParser &operator=(const JsonCommitRequestParser &) = delete; JsonCommitRequestParser(JsonCommitRequestParser &&other) noexcept; JsonCommitRequestParser &operator=(JsonCommitRequestParser &&other) noexcept; // CommitRequestParser interface implementation bool parse(CommitRequest &request, char *data, size_t len) override; bool begin_streaming_parse(CommitRequest &request) override; ParseStatus parse_chunk(char *data, size_t len) override; ParseStatus finish_streaming_parse() override; bool has_parse_error() const override; const char *get_parse_error() const override; /** * @brief Check if read version has been explicitly set during parsing. * @return true if read version was set during parsing */ bool has_read_version_been_set() const; // Weaseljson callbacks (public for global callbacks) static void on_begin_object(void *userdata); static void on_end_object(void *userdata); static void on_string_data(void *userdata, const char *buf, int len, int done); static void on_key_data(void *userdata, const char *buf, int len, int done); static void on_begin_array(void *userdata); static void on_end_array(void *userdata); static void on_number_data(void *userdata, const char *buf, int len, int done); static void on_true_literal(void *userdata); static void on_false_literal(void *userdata); static void on_null_literal(void *userdata); private: /** * @brief Decode a base64 string and store it in the arena. * @param base64_str The base64 encoded string * @return String view of decoded data, or empty view if decoding failed */ std::string_view decode_base64(std::string_view base64_str); void handle_completed_string(std::string_view s); void handle_completed_number(std::string_view s); void on_complete(); };