Clear key after matching on it
This commit is contained in:
@@ -116,7 +116,7 @@ void CommitRequest::on_begin_object(void *userdata) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (ctx.state_stack.empty()) {
|
if (ctx.state_stack.empty()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "Unexpected object start - empty state stack";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ void CommitRequest::on_begin_object(void *userdata) {
|
|||||||
ctx.current_operation = OperationParseState{};
|
ctx.current_operation = OperationParseState{};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "Unexpected object in invalid parse state";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,9 @@ void CommitRequest::on_end_object(void *userdata) {
|
|||||||
auto &ctx = self->parser_context_;
|
auto &ctx = self->parser_context_;
|
||||||
|
|
||||||
if (ctx.parse_error || ctx.state_stack.empty()) {
|
if (ctx.parse_error || ctx.state_stack.empty()) {
|
||||||
ctx.parse_error = true;
|
if (!ctx.parse_error) {
|
||||||
|
ctx.parse_error = "Unexpected object end - empty state stack";
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +164,8 @@ void CommitRequest::on_end_object(void *userdata) {
|
|||||||
switch (ctx.current_precondition.type) {
|
switch (ctx.current_precondition.type) {
|
||||||
case Precondition::Type::PointRead:
|
case Precondition::Type::PointRead:
|
||||||
if (!ctx.current_precondition.key.has_value()) {
|
if (!ctx.current_precondition.key.has_value()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error =
|
||||||
|
"point_read precondition missing required 'key' field";
|
||||||
} else {
|
} else {
|
||||||
self->preconditions_.push_back(
|
self->preconditions_.push_back(
|
||||||
Precondition{ctx.current_precondition.type,
|
Precondition{ctx.current_precondition.type,
|
||||||
@@ -174,7 +177,8 @@ void CommitRequest::on_end_object(void *userdata) {
|
|||||||
case Precondition::Type::RangeRead:
|
case Precondition::Type::RangeRead:
|
||||||
if (!ctx.current_precondition.begin.has_value() ||
|
if (!ctx.current_precondition.begin.has_value() ||
|
||||||
!ctx.current_precondition.end.has_value()) {
|
!ctx.current_precondition.end.has_value()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "range_read precondition missing required 'begin' "
|
||||||
|
"and/or 'end' fields";
|
||||||
} else {
|
} else {
|
||||||
self->preconditions_.push_back(
|
self->preconditions_.push_back(
|
||||||
Precondition{ctx.current_precondition.type,
|
Precondition{ctx.current_precondition.type,
|
||||||
@@ -190,7 +194,8 @@ void CommitRequest::on_end_object(void *userdata) {
|
|||||||
case Operation::Type::Write:
|
case Operation::Type::Write:
|
||||||
if (!ctx.current_operation.key.has_value() ||
|
if (!ctx.current_operation.key.has_value() ||
|
||||||
!ctx.current_operation.value.has_value()) {
|
!ctx.current_operation.value.has_value()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error =
|
||||||
|
"write operation missing required 'key' and/or 'value' fields";
|
||||||
} else {
|
} else {
|
||||||
self->operations_.push_back(Operation{
|
self->operations_.push_back(Operation{
|
||||||
ctx.current_operation.type, ctx.current_operation.key.value(),
|
ctx.current_operation.type, ctx.current_operation.key.value(),
|
||||||
@@ -199,7 +204,7 @@ void CommitRequest::on_end_object(void *userdata) {
|
|||||||
break;
|
break;
|
||||||
case Operation::Type::Delete:
|
case Operation::Type::Delete:
|
||||||
if (!ctx.current_operation.key.has_value()) {
|
if (!ctx.current_operation.key.has_value()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "delete operation missing required 'key' field";
|
||||||
} else {
|
} else {
|
||||||
self->operations_.push_back(Operation{
|
self->operations_.push_back(Operation{
|
||||||
ctx.current_operation.type, ctx.current_operation.key.value(), {}});
|
ctx.current_operation.type, ctx.current_operation.key.value(), {}});
|
||||||
@@ -208,7 +213,8 @@ void CommitRequest::on_end_object(void *userdata) {
|
|||||||
case Operation::Type::RangeDelete:
|
case Operation::Type::RangeDelete:
|
||||||
if (!ctx.current_operation.begin.has_value() ||
|
if (!ctx.current_operation.begin.has_value() ||
|
||||||
!ctx.current_operation.end.has_value()) {
|
!ctx.current_operation.end.has_value()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "range_delete operation missing required 'begin' "
|
||||||
|
"and/or 'end' fields";
|
||||||
} else {
|
} else {
|
||||||
self->operations_.push_back(Operation{
|
self->operations_.push_back(Operation{
|
||||||
ctx.current_operation.type, ctx.current_operation.begin.value(),
|
ctx.current_operation.type, ctx.current_operation.begin.value(),
|
||||||
@@ -235,7 +241,6 @@ void CommitRequest::on_string_data(void *userdata, const char *buf, int len,
|
|||||||
if (done) {
|
if (done) {
|
||||||
self->handle_completed_string();
|
self->handle_completed_string();
|
||||||
ctx.current_string.clear();
|
ctx.current_string.clear();
|
||||||
ctx.current_key.clear(); // Clear key after processing value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,11 +268,14 @@ void CommitRequest::on_begin_array(void *userdata) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (ctx.current_key == "preconditions") {
|
if (ctx.current_key == "preconditions") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.state_stack.push(ParseState::PreconditionsArray);
|
ctx.state_stack.push(ParseState::PreconditionsArray);
|
||||||
} else if (ctx.current_key == "operations") {
|
} else if (ctx.current_key == "operations") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.state_stack.push(ParseState::OperationsArray);
|
ctx.state_stack.push(ParseState::OperationsArray);
|
||||||
} else {
|
} else {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "Invalid array field - only 'preconditions' and "
|
||||||
|
"'operations' arrays are allowed";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +284,9 @@ void CommitRequest::on_end_array(void *userdata) {
|
|||||||
auto &ctx = self->parser_context_;
|
auto &ctx = self->parser_context_;
|
||||||
|
|
||||||
if (ctx.parse_error || ctx.state_stack.empty()) {
|
if (ctx.parse_error || ctx.state_stack.empty()) {
|
||||||
ctx.parse_error = true;
|
if (!ctx.parse_error) {
|
||||||
|
ctx.parse_error = "Unexpected array end - empty state stack";
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,19 +306,18 @@ void CommitRequest::on_number_data(void *userdata, const char *buf, int len,
|
|||||||
if (done) {
|
if (done) {
|
||||||
self->handle_completed_number();
|
self->handle_completed_number();
|
||||||
ctx.current_number.clear();
|
ctx.current_number.clear();
|
||||||
ctx.current_key.clear(); // Clear key after processing value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommitRequest::on_true_literal(void *userdata) {
|
void CommitRequest::on_true_literal(void *) {
|
||||||
// Not used in this API
|
// Not used in this API
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommitRequest::on_false_literal(void *userdata) {
|
void CommitRequest::on_false_literal(void *) {
|
||||||
// Not used in this API
|
// Not used in this API
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommitRequest::on_null_literal(void *userdata) {
|
void CommitRequest::on_null_literal(void *) {
|
||||||
// Not used in this API
|
// Not used in this API
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +325,7 @@ void CommitRequest::handle_completed_string() {
|
|||||||
auto &ctx = parser_context_;
|
auto &ctx = parser_context_;
|
||||||
|
|
||||||
if (ctx.state_stack.empty()) {
|
if (ctx.state_stack.empty()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "String value received with empty state stack";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,33 +334,42 @@ void CommitRequest::handle_completed_string() {
|
|||||||
switch (current_state) {
|
switch (current_state) {
|
||||||
case ParseState::Root:
|
case ParseState::Root:
|
||||||
if (ctx.current_key == "request_id") {
|
if (ctx.current_key == "request_id") {
|
||||||
|
ctx.current_key.clear();
|
||||||
request_id_ = store_string(ctx.current_string);
|
request_id_ = store_string(ctx.current_string);
|
||||||
} else if (ctx.current_key == "leader_id") {
|
} else if (ctx.current_key == "leader_id") {
|
||||||
|
ctx.current_key.clear();
|
||||||
leader_id_ = store_string(ctx.current_string);
|
leader_id_ = store_string(ctx.current_string);
|
||||||
} else if (ctx.current_key == "read_version") {
|
} else if (ctx.current_key == "read_version") {
|
||||||
|
ctx.current_key.clear();
|
||||||
// read_version should be a number, not a string
|
// read_version should be a number, not a string
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "read_version field must be a number, not a string";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ParseState::PreconditionObject:
|
case ParseState::PreconditionObject:
|
||||||
if (ctx.current_key == "type") {
|
if (ctx.current_key == "type") {
|
||||||
|
ctx.current_key.clear();
|
||||||
if (ctx.current_string == "point_read") {
|
if (ctx.current_string == "point_read") {
|
||||||
ctx.current_precondition.type = Precondition::Type::PointRead;
|
ctx.current_precondition.type = Precondition::Type::PointRead;
|
||||||
} else if (ctx.current_string == "range_read") {
|
} else if (ctx.current_string == "range_read") {
|
||||||
ctx.current_precondition.type = Precondition::Type::RangeRead;
|
ctx.current_precondition.type = Precondition::Type::RangeRead;
|
||||||
} else {
|
} else {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error =
|
||||||
|
"Invalid precondition type - must be 'point_read' or 'range_read'";
|
||||||
}
|
}
|
||||||
} else if (ctx.current_key == "key") {
|
} else if (ctx.current_key == "key") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_precondition.key = decode_base64(ctx.current_string);
|
ctx.current_precondition.key = decode_base64(ctx.current_string);
|
||||||
} else if (ctx.current_key == "begin") {
|
} else if (ctx.current_key == "begin") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_precondition.begin = decode_base64(ctx.current_string);
|
ctx.current_precondition.begin = decode_base64(ctx.current_string);
|
||||||
} else if (ctx.current_key == "end") {
|
} else if (ctx.current_key == "end") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_precondition.end = decode_base64(ctx.current_string);
|
ctx.current_precondition.end = decode_base64(ctx.current_string);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ParseState::OperationObject:
|
case ParseState::OperationObject:
|
||||||
if (ctx.current_key == "type") {
|
if (ctx.current_key == "type") {
|
||||||
|
ctx.current_key.clear();
|
||||||
if (ctx.current_string == "write") {
|
if (ctx.current_string == "write") {
|
||||||
ctx.current_operation.type = Operation::Type::Write;
|
ctx.current_operation.type = Operation::Type::Write;
|
||||||
} else if (ctx.current_string == "delete") {
|
} else if (ctx.current_string == "delete") {
|
||||||
@@ -359,15 +377,20 @@ void CommitRequest::handle_completed_string() {
|
|||||||
} else if (ctx.current_string == "range_delete") {
|
} else if (ctx.current_string == "range_delete") {
|
||||||
ctx.current_operation.type = Operation::Type::RangeDelete;
|
ctx.current_operation.type = Operation::Type::RangeDelete;
|
||||||
} else {
|
} else {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "Invalid operation type - must be 'write', 'delete', "
|
||||||
|
"or 'range_delete'";
|
||||||
}
|
}
|
||||||
} else if (ctx.current_key == "key") {
|
} else if (ctx.current_key == "key") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_operation.key = decode_base64(ctx.current_string);
|
ctx.current_operation.key = decode_base64(ctx.current_string);
|
||||||
} else if (ctx.current_key == "value") {
|
} else if (ctx.current_key == "value") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_operation.value = decode_base64(ctx.current_string);
|
ctx.current_operation.value = decode_base64(ctx.current_string);
|
||||||
} else if (ctx.current_key == "begin") {
|
} else if (ctx.current_key == "begin") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_operation.begin = decode_base64(ctx.current_string);
|
ctx.current_operation.begin = decode_base64(ctx.current_string);
|
||||||
} else if (ctx.current_key == "end") {
|
} else if (ctx.current_key == "end") {
|
||||||
|
ctx.current_key.clear();
|
||||||
ctx.current_operation.end = decode_base64(ctx.current_string);
|
ctx.current_operation.end = decode_base64(ctx.current_string);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -380,7 +403,7 @@ void CommitRequest::handle_completed_number() {
|
|||||||
auto &ctx = parser_context_;
|
auto &ctx = parser_context_;
|
||||||
|
|
||||||
if (ctx.state_stack.empty()) {
|
if (ctx.state_stack.empty()) {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "Number value received with empty state stack";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,6 +412,7 @@ void CommitRequest::handle_completed_number() {
|
|||||||
switch (current_state) {
|
switch (current_state) {
|
||||||
case ParseState::Root:
|
case ParseState::Root:
|
||||||
if (ctx.current_key == "read_version") {
|
if (ctx.current_key == "read_version") {
|
||||||
|
ctx.current_key.clear();
|
||||||
uint64_t version;
|
uint64_t version;
|
||||||
auto result = std::from_chars(
|
auto result = std::from_chars(
|
||||||
ctx.current_number.data(),
|
ctx.current_number.data(),
|
||||||
@@ -397,12 +421,13 @@ void CommitRequest::handle_completed_number() {
|
|||||||
read_version_ = version;
|
read_version_ = version;
|
||||||
has_read_version_been_set_ = true;
|
has_read_version_been_set_ = true;
|
||||||
} else {
|
} else {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error = "Invalid number format for read_version field";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ParseState::PreconditionObject:
|
case ParseState::PreconditionObject:
|
||||||
if (ctx.current_key == "version") {
|
if (ctx.current_key == "version") {
|
||||||
|
ctx.current_key.clear();
|
||||||
uint64_t version;
|
uint64_t version;
|
||||||
auto result = std::from_chars(
|
auto result = std::from_chars(
|
||||||
ctx.current_number.data(),
|
ctx.current_number.data(),
|
||||||
@@ -410,7 +435,8 @@ void CommitRequest::handle_completed_number() {
|
|||||||
if (result.ec == std::errc{}) {
|
if (result.ec == std::errc{}) {
|
||||||
ctx.current_precondition.version = version;
|
ctx.current_precondition.version = version;
|
||||||
} else {
|
} else {
|
||||||
ctx.parse_error = true;
|
ctx.parse_error =
|
||||||
|
"Invalid number format for precondition version field";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -462,7 +488,8 @@ CommitRequest::ParseStatus CommitRequest::parse_chunk(char *data, size_t len) {
|
|||||||
case WeaselJson_REJECT:
|
case WeaselJson_REJECT:
|
||||||
case WeaselJson_OVERFLOW:
|
case WeaselJson_OVERFLOW:
|
||||||
default:
|
default:
|
||||||
parser_context_.parse_error = true;
|
parser_context_.parse_error =
|
||||||
|
"JSON parsing failed - invalid or oversized JSON";
|
||||||
return ParseStatus::Error;
|
return ParseStatus::Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -483,7 +510,8 @@ CommitRequest::ParseStatus CommitRequest::finish_streaming_parse() {
|
|||||||
!parser_context_.parse_error) {
|
!parser_context_.parse_error) {
|
||||||
return ParseStatus::Complete;
|
return ParseStatus::Complete;
|
||||||
} else {
|
} else {
|
||||||
parser_context_.parse_error = true;
|
parser_context_.parse_error =
|
||||||
|
"JSON parsing incomplete or failed during finalization";
|
||||||
return ParseStatus::Error;
|
return ParseStatus::Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public:
|
|||||||
ArenaString current_string;
|
ArenaString current_string;
|
||||||
ArenaString current_number;
|
ArenaString current_number;
|
||||||
bool in_key = false;
|
bool in_key = false;
|
||||||
bool parse_error = false;
|
const char *parse_error = nullptr;
|
||||||
bool parse_complete = false;
|
bool parse_complete = false;
|
||||||
|
|
||||||
// Current objects being parsed
|
// Current objects being parsed
|
||||||
@@ -222,7 +222,15 @@ public:
|
|||||||
* @brief Check if there was a parse error.
|
* @brief Check if there was a parse error.
|
||||||
* @return true if there was a parse error
|
* @return true if there was a parse error
|
||||||
*/
|
*/
|
||||||
bool has_parse_error() const { return parser_context_.parse_error; }
|
bool has_parse_error() const {
|
||||||
|
return parser_context_.parse_error != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the parse error message if there was an error.
|
||||||
|
* @return Error message string, or nullptr if no error
|
||||||
|
*/
|
||||||
|
const char *get_parse_error() const { return parser_context_.parse_error; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the request ID if present.
|
* @brief Get the request ID if present.
|
||||||
|
|||||||
@@ -195,8 +195,8 @@ TEST_CASE("CommitRequest precondition and operation validation") {
|
|||||||
{
|
{
|
||||||
"type": "range_read",
|
"type": "range_read",
|
||||||
"version": 12340,
|
"version": 12340,
|
||||||
"begin": "",
|
"begin": "dGVzdA==",
|
||||||
"end": ""
|
"end": "dGVzdFo="
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})";
|
})";
|
||||||
@@ -206,6 +206,9 @@ TEST_CASE("CommitRequest precondition and operation validation") {
|
|||||||
INFO("Parse result: " << parse_result);
|
INFO("Parse result: " << parse_result);
|
||||||
INFO("Parse complete: " << request.is_parse_complete());
|
INFO("Parse complete: " << request.is_parse_complete());
|
||||||
INFO("Parse error: " << request.has_parse_error());
|
INFO("Parse error: " << request.has_parse_error());
|
||||||
|
const char *error_msg = request.get_parse_error();
|
||||||
|
INFO("Parse error message: " << (error_msg ? std::string(error_msg)
|
||||||
|
: "none"));
|
||||||
INFO("Leader ID: '" << request.leader_id() << "'");
|
INFO("Leader ID: '" << request.leader_id() << "'");
|
||||||
INFO("Read version: " << request.read_version());
|
INFO("Read version: " << request.read_version());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user