From 2ca9a9ae1330a5811c7165e404a5723984cdc0ee Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Thu, 1 Aug 2024 16:07:31 +1000 Subject: [PATCH] chore(pact_models): Improve the matching rule expression parser error messages --- .../src/matchingrules/expressions.rs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/rust/pact_models/src/matchingrules/expressions.rs b/rust/pact_models/src/matchingrules/expressions.rs index df0092429..15f0fffa5 100644 --- a/rust/pact_models/src/matchingrules/expressions.rs +++ b/rust/pact_models/src/matchingrules/expressions.rs @@ -117,7 +117,7 @@ use bytes::{BufMut, BytesMut}; use itertools::Either; use logos::{Lexer, Logos, Span}; use semver::Version; -use tracing::{trace, warn}; +use tracing::{instrument, trace, warn}; use crate::generators::Generator; use crate::matchingrules::MatchingRule; @@ -288,6 +288,7 @@ enum MatcherDefinitionToken { /// * `matching(type,'Name')` - type matcher /// * `matching(number,100)` - number matcher /// * `matching(datetime, 'yyyy-MM-dd','2000-01-01')` - datetime matcher with format string +#[instrument(level = "debug", ret)] pub fn parse_matcher_def(v: &str) -> anyhow::Result { if v.is_empty() { Err(anyhow!("Expected a matching rule definition, but got an empty string")) @@ -517,10 +518,10 @@ fn parse_each_key(lex: &mut Lexer, v: &str) -> anyhow::R // LEFT_BRACKET primitiveValue RIGHT_BRACKET fn parse_not_empty(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType)> { - let next = lex.next().ok_or_else(|| anyhow!("expected '('"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "'('"))?; if let Ok(MatcherDefinitionToken::LeftBracket) = next { let result = parse_primitive_value(lex, v)?; - let next = lex.next().ok_or_else(|| anyhow!("expected ')'"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "')'"))?; if let Ok(MatcherDefinitionToken::RightBracket) = next { Ok(result) } else { @@ -533,17 +534,17 @@ fn parse_not_empty(lex: &mut Lexer, v: &str) -> anyhow:: // LEFT_BRACKET matchingRule RIGHT_BRACKET fn parse_matching(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType, Option, Option, Option)> { - let next = lex.next().ok_or_else(|| anyhow!("expected '('"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "'('"))?; if let Ok(MatcherDefinitionToken::LeftBracket) = next { let result = parse_matching_rule(lex, v)?; - let next = lex.next().ok_or_else(|| anyhow!("expected ')'"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "')'"))?; if let Ok(MatcherDefinitionToken::RightBracket) = next { Ok(result) } else { - Err(anyhow!("expected closing bracket, got '{}'", lex.slice())) + Err(anyhow!(error_message(lex, v, "Expected a closing bracket", "Expected a closing bracket before this")?)) } } else { - Err(anyhow!("expected '(', got '{}'", lex.remainder())) + Err(anyhow!(error_message(lex, v, "Expected an opening bracket", "Expected an opening bracket before this")?)) } } @@ -721,8 +722,8 @@ fn parse_content_type(lex: &mut Lexer, v: &str) -> anyho // } // | 'null' // ; -fn parse_primitive_value(lex: &mut Lexer, _v: &str) -> anyhow::Result<(String, ValueType)> { - let next = lex.next().ok_or_else(|| anyhow!("expected a primitive value"))?; +fn parse_primitive_value(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType)> { + let next = lex.next().ok_or_else(|| end_of_expression(v, "expected a primitive value"))?; match next { Ok(MatcherDefinitionToken::String) => Ok((lex.slice().trim_matches('\'').to_string(), ValueType::String)), Ok(MatcherDefinitionToken::Null) => Ok((String::new(), ValueType::String)), @@ -731,7 +732,7 @@ fn parse_primitive_value(lex: &mut Lexer, _v: &str) -> a // remaining pattern if it is a decimal if lex.remainder().starts_with('.') { let int_part = lex.slice(); - let _ = lex.next().ok_or_else(|| anyhow!("expected a number"))?; + let _ = lex.next().ok_or_else(|| end_of_expression(v, "expected a number"))?; Ok((format!("{}{}", int_part, lex.slice()), ValueType::Decimal)) } else { Ok((lex.slice().to_string(), ValueType::Integer)) @@ -742,7 +743,7 @@ fn parse_primitive_value(lex: &mut Lexer, _v: &str) -> a // remaining pattern if it is a decimal if lex.remainder().starts_with('.') { let int_part = lex.slice(); - let _ = lex.next().ok_or_else(|| anyhow!("expected a number"))?; + let _ = lex.next().ok_or_else(|| end_of_expression(v, "expected a number"))?; Ok((format!("{}{}", int_part, lex.slice()), ValueType::Decimal)) } else { Ok((lex.slice().to_string(), ValueType::Integer)) @@ -750,7 +751,7 @@ fn parse_primitive_value(lex: &mut Lexer, _v: &str) -> a }, Ok(MatcherDefinitionToken::Decimal) => Ok((lex.slice().to_string(), ValueType::Decimal)), Ok(MatcherDefinitionToken::Boolean) => Ok((lex.slice().to_string(), ValueType::Boolean)), - _ => Err(anyhow!("expected a primitive value, got '{}'", lex.slice())) + _ => Err(anyhow!(error_message(lex, v, "Expected a primitive value", "Expected a primitive value here")?)) } } @@ -758,7 +759,7 @@ fn parse_primitive_value(lex: &mut Lexer, _v: &str) -> a #[allow(clippy::if_same_then_else)] fn parse_number(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType, Option, Option, Option)> { parse_comma(lex, v)?; - let next = lex.next().ok_or_else(|| anyhow!("expected a number"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "expected a number"))?; if let Ok(MatcherDefinitionToken::Decimal) = next { Ok((lex.slice().to_string(), ValueType::Number, Some(MatchingRule::Number), None, None)) } else if let Ok(MatcherDefinitionToken::Int(_) | MatcherDefinitionToken::Num(_)) = next { @@ -766,24 +767,24 @@ fn parse_number(lex: &mut Lexer, v: &str) -> anyhow::Res // remaining pattern if it is a decimal if lex.remainder().starts_with('.') { let int_part = lex.slice(); - let _ = lex.next().ok_or_else(|| anyhow!("expected a number"))?; + let _ = lex.next().ok_or_else(|| end_of_expression(v, "expected a number"))?; Ok((format!("{}{}", int_part, lex.slice()), ValueType::Number, Some(MatchingRule::Number), None, None)) } else { Ok((lex.slice().to_string(), ValueType::Number, Some(MatchingRule::Number), None, None)) } } else { - Err(anyhow!("expected a number, got '{}'", lex.slice())) + Err(anyhow!(error_message(lex, v, "Expected a number", "Expected a number here")?)) } } // COMMA val=INTEGER_LITERAL { $value = $val.getText(); $type = ValueType.Integer; } fn parse_integer(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType, Option, Option, Option)> { parse_comma(lex, v)?; - let next = lex.next().ok_or_else(|| anyhow!("expected an integer"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "expected an integer"))?; if let Ok(MatcherDefinitionToken::Int(_) | MatcherDefinitionToken::Num(_)) = next { Ok((lex.slice().to_string(), ValueType::Integer, Some(MatchingRule::Integer), None, None)) } else { - Err(anyhow!("expected an integer, got '{}'", lex.slice())) + Err(anyhow!(error_message(lex, v, "Expected an integer", "Expected an integer here")?)) } } @@ -791,13 +792,13 @@ fn parse_integer(lex: &mut Lexer, v: &str) -> anyhow::Re #[allow(clippy::if_same_then_else)] fn parse_decimal(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType, Option, Option, Option)> { parse_comma(lex, v)?; - let next = lex.next().ok_or_else(|| anyhow!("expected a decimal number"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "expected a decimal number"))?; if let Ok(MatcherDefinitionToken::Int(_) | MatcherDefinitionToken::Num(_)) = next { // Logos is returning an INT token when a Decimal should match. We need to now parse the // remaining pattern if it is a decimal if lex.remainder().starts_with('.') { let int_part = lex.slice(); - let _ = lex.next().ok_or_else(|| anyhow!("expected a number"))?; + let _ = lex.next().ok_or_else(|| end_of_expression(v, "expected a number"))?; Ok((format!("{}{}", int_part, lex.slice()), ValueType::Decimal, Some(MatchingRule::Decimal), None, None)) } else { Ok((lex.slice().to_string(), ValueType::Decimal, Some(MatchingRule::Decimal), None, None)) @@ -805,18 +806,18 @@ fn parse_decimal(lex: &mut Lexer, v: &str) -> anyhow::Re } else if let Ok(MatcherDefinitionToken::Decimal) = next { Ok((lex.slice().to_string(), ValueType::Decimal, Some(MatchingRule::Decimal), None, None)) } else { - Err(anyhow!("expected a decimal number, got '{}'", lex.slice())) + Err(anyhow!(error_message(lex, v, "Expected a decimal number", "Expected a decimal number here")?)) } } // COMMA BOOLEAN_LITERAL { $rule = BooleanMatcher.INSTANCE; $value = $BOOLEAN_LITERAL.getText(); $type = ValueType.Boolean; } fn parse_boolean(lex: &mut Lexer, v: &str) -> anyhow::Result<(String, ValueType, Option, Option, Option)> { parse_comma(lex, v)?; - let next = lex.next().ok_or_else(|| anyhow!("expected a boolean"))?; + let next = lex.next().ok_or_else(|| end_of_expression(v, "expected a boolean"))?; if let Ok(MatcherDefinitionToken::Boolean) = next { Ok((lex.slice().to_string(), ValueType::Boolean, Some(MatchingRule::Boolean), None, None)) } else { - Err(anyhow!("expected a boolean, got '{}'", lex.slice())) + Err(anyhow!(error_message(lex, v, "Expected a boolean", "Expected a boolean here")?)) } }