Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom error with variants to parser module #50

Merged
merged 3 commits into from
Nov 10, 2023
Merged

Conversation

CalemRoelofs
Copy link
Contributor

Closes #38

This PR adds the parser::errors submodule using thiserror to handle different variants.
In the parser::parser module, almost all calls to unwrap() have been replaced and functions now return Result<T, JsonPathParserError> instead.
Changing the signature of the public method parse_json_path from Result<JsonPath, pest::error::Error<Rule>> to Result<JsonPath, JsonPathParsingError> is considered a breaking change so I'll leave it you you @besok on how you want to handle bumping versions etc.
There's a bit of extra boilerplate added to the existing functions where Option<T> is now Result<Option<T>, JsonPathParserError> but I've tried to make it as concise as possible.

let mut expr: Option<FilterExpression> = None;
while pairs.peek().is_some() {
let next_expr = parse_logic_and(pairs.next().unwrap().into_inner());
let error_message = format!("Failed to parse logical expression: {:?}", pairs);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error_message has to be constructed before the call to pair.into_inner() as into_inner() takes ownership and moves the value

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, sure

Comment on lines 189 to 198
Rule::logic => parse_logic(pairs.next().unwrap().into_inner()),
Rule::atom => {
let left: Operand = parse_atom(pairs.next().unwrap())?;
if pairs.peek().is_none() {
Ok(FilterExpression::exists(left))
} else {
let sign: FilterSign = FilterSign::new(pairs.next().unwrap().as_str());
let right: Operand = parse_atom(pairs.next().unwrap())?;
Ok(FilterExpression::Atom(left, sign, right))
}
Copy link
Contributor Author

@CalemRoelofs CalemRoelofs Nov 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still using .unwrap() here but it should be safe as we're peeking ahead to make sure there's a value.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, no problem. But can you replace unwrap with expect("unreachable in arithmetic") or something like that

match rule.as_rule() {
Rule::path => rule
Rule::path => Ok(rule
.into_inner()
.next()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case, I would suggest

next()
.ok_or(...)
.and_then(parse_internal)

instead of

next()
.map()
.unwrap_or
.unwrap_or

.unwrap_or(JsonPath::Empty),
Rule::current => JsonPath::Current(Box::new(
.unwrap_or(Ok(JsonPath::Empty))
.unwrap_or(JsonPath::Empty)),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, since we have Result as a result we don't have to return JsonPath::Empty and can return Err instead (apparently). I would suggest replacing unwrap_or everywhere to ok_or(Err(..))

Rule::current => JsonPath::Current(Box::new(
.unwrap_or(Ok(JsonPath::Empty))
.unwrap_or(JsonPath::Empty)),
Rule::current => Ok(JsonPath::Current(Box::new(
rule.into_inner()
.next()
.map(parse_internal)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is the same:

  • we convert the first option into result using ok_or
  • then we use flatmap (and_then) instead of map

_ => JsonPath::Empty,
.unwrap_or(JsonPath::Empty)),
Rule::index => Ok(JsonPath::Index(parse_index(rule)?)),
_ => Ok(JsonPath::Empty),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we return Error instead of JsPath::Empty I think

Comment on lines 189 to 198
Rule::logic => parse_logic(pairs.next().unwrap().into_inner()),
Rule::atom => {
let left: Operand = parse_atom(pairs.next().unwrap())?;
if pairs.peek().is_none() {
Ok(FilterExpression::exists(left))
} else {
let sign: FilterSign = FilterSign::new(pairs.next().unwrap().as_str());
let right: Operand = parse_atom(pairs.next().unwrap())?;
Ok(FilterExpression::Atom(left, sign, right))
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, no problem. But can you replace unwrap with expect("unreachable in arithmetic") or something like that

@besok
Copy link
Owner

besok commented Nov 5, 2023

Closes #38

This PR adds the parser::errors submodule using thiserror to handle different variants. In the parser::parser module, almost all calls to unwrap() have been replaced and functions now return Result<T, JsonPathParserError> instead. Changing the signature of the public method parse_json_path from Result<JsonPath, pest::error::Error<Rule>> to Result<JsonPath, JsonPathParsingError> is considered a breaking change so I'll leave it you you @besok on how you want to handle bumping versions etc. There's a bit of extra boilerplate added to the existing functions where Option<T> is now Result<Option<T>, JsonPathParserError> but I've tried to make it as concise as possible.

The PR looks very good. Nice job! I added just a couple of comments, just to correct a little.

Changing the signature of the public method parse_json_path from Result<JsonPath, pest::error::Error<Rule>> to Result<JsonPath, JsonPathParsingError> is considered a breaking change so I'll leave it you you @besok on how you want to handle bumping versions etc.

Yeah, it seems fine, but I would ensure the new error enum has a to_string implementation as we convert it to string here and either change TryInto or add to_string

@CalemRoelofs
Copy link
Contributor Author

@besok thank you for the feedback! I'll implement these changes in the coming week.

.unwrap_or(JsonPath::Empty),
Rule::index => JsonPath::Index(parse_index(rule)),
_ => JsonPath::Empty,
.unwrap_or(Ok(JsonPath::Empty))?,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@besok handling the Rule::current variant with ok_or(Err()) causes the following tests to fail:

failures:

---- parser::parser::tests::path_test stdout ----
thread 'parser::parser::tests::path_test' panicked at 'parsing error expected valid Rule::current token but found nothing', src\parser\parser.rs:273:17
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- parser::parser::tests::index_filter_test stdout ----
thread 'parser::parser::tests::index_filter_test' panicked at 'parsing error expected valid Rule::current token but found nothing', src\parser\parser.rs:273:17

---- tests::no_value_chain_test stdout ----
thread 'tests::no_value_chain_test' panicked at 'the path is correct: "expected valid Rule::current token but found nothing"', src\lib.rs:1141:65

---- tests::no_value_filter_from_not_arr_filter_test stdout ----
thread 'tests::no_value_filter_from_not_arr_filter_test' panicked at 'the path is correct: "expected valid Rule::current token but found nothing"', src\lib.rs:1092:68


failures:
    parser::parser::tests::index_filter_test
    parser::parser::tests::path_test
    tests::no_value_chain_test
    tests::no_value_filter_from_not_arr_filter_test

I'm not sure if this a problem with the tests or a problem with the logic of their not being a value when calling Rule::current.into_inner(), so I've left it with the previous behavior of unwrap_or(JsonPath::Empty) which was there before.

Copy link
Owner

@besok besok Nov 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me neither :).
No problems. Let's leave it as is. I will take a look at the weekend, hopefully.

@CalemRoelofs
Copy link
Contributor Author

@besok I've refactored out most of the unwrap_or statements, but I'm a bit unsure of what the error messages returned from ok_or actually should be. Open to any and all suggestions 😄

@besok
Copy link
Owner

besok commented Nov 10, 2023

@CalemRoelofs
Yeah, looks good to me. The messages just say where is the error and we can have a look there. It is enough I think.

@besok
Copy link
Owner

besok commented Nov 10, 2023

Ah, the build error with Clippy, you need to fix a small warning
Just run locally:

cargo clippy --workspace --tests --all-features -- -D warnings

@CalemRoelofs
Copy link
Contributor Author

Ah, the build error with Clippy, you need to fix a small warning Just run locally:

cargo clippy --workspace --tests --all-features -- -D warnings

Fixed now :)

@besok besok merged commit 2cfb303 into besok:main Nov 10, 2023
5 checks passed
@besok
Copy link
Owner

besok commented Nov 10, 2023

thank you very much for the PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Shift parser module to Result process handling
2 participants