#pragma once #include #include #include #include "commit_request_parser.hpp" #include "json_token_enum.hpp" /** * @brief JSON-specific implementation of CommitRequestParser. * * This parser uses the weaseljson library to parse JSON-formatted * commit requests into CommitRequest objects. */ struct JsonCommitRequestParser : 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; 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 attach_arena(ArenaAllocator *arena) { current_key = ArenaString{ArenaStlAllocator(arena)}; current_string = ArenaString{ArenaStlAllocator(arena)}; current_number = ArenaString{ArenaStlAllocator(arena)}; 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 ParseResult parse(CommitRequest &request, char *data, int64_t len) override; bool begin_streaming_parse(CommitRequest &request) override; ParseStatus parse_chunk(char *data, int64_t len) override; ParseStatus finish_streaming_parse() override; const char *get_parse_error() const override; /** * @brief Check if read version has been explicitly set during parsing. * * @note This function is primarily used for testing to verify parser * behavior. It exposes internal parser state that is not typically needed in * production code. * * @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(); };