diff --git a/engine/src/functions/concat.rs b/engine/src/functions/concat.rs index d66ffcfe..4b9ae68f 100644 --- a/engine/src/functions/concat.rs +++ b/engine/src/functions/concat.rs @@ -41,7 +41,8 @@ fn concat_bytes<'a>(mut accumulator: Cow<'a, [u8]>, args: FunctionArgs<'_, 'a>) accumulator } -const EXPECTED_TYPES: [ExpectedType; 2] = [ExpectedType::Array, ExpectedType::Type(Type::Bytes)]; +pub(crate) const EXPECTED_TYPES: [ExpectedType; 2] = + [ExpectedType::Array, ExpectedType::Type(Type::Bytes)]; impl FunctionDefinition for ConcatFunction { fn check_param( diff --git a/engine/src/functions/mod.rs b/engine/src/functions/mod.rs index f3b68dca..482794fa 100644 --- a/engine/src/functions/mod.rs +++ b/engine/src/functions/mod.rs @@ -1,6 +1,6 @@ -mod all; -mod any; -mod concat; +pub(crate) mod all; +pub(crate) mod any; +pub(crate) mod concat; use crate::{ filter::CompiledValueResult, diff --git a/engine/src/scheme.rs b/engine/src/scheme.rs index 2924eb5a..ce73c9f1 100644 --- a/engine/src/scheme.rs +++ b/engine/src/scheme.rs @@ -628,15 +628,20 @@ macro_rules! Scheme { #[test] fn test_parse_error() { - use crate::types::TypeMismatchError; + use crate::types::{ExpectedTypeList, TypeMismatchError}; + use crate::ConcatFunction; use indoc::indoc; - let scheme = &Scheme! { + let mut scheme = Scheme! { num: Int, str: Bytes, arr: Array(Bool), }; + scheme + .add_function("concat", ConcatFunction::new()) + .unwrap(); + { let err = scheme.parse("xyz").unwrap_err(); assert_eq!( @@ -768,7 +773,7 @@ fn test_parse_error() { r#" Filter parsing error (1:12): arr and arr - ^ expected value of type {Type(Bool)}, but got Array(Bool) + ^ expected value of type Bool, but got Array "# ) ); @@ -795,7 +800,7 @@ fn test_parse_error() { r#" Filter parsing error (1:2): arr[*] - ^^^^^^ expected value of type {Type(Bool)}, but got Array(Bool) + ^^^^^^ expected value of type Bool, but got Array "# ) ); @@ -872,6 +877,38 @@ fn test_parse_error() { ) ); } + + { + let err = scheme.parse(indoc!(r"concat(0, 0) == 0")).unwrap_err(); + assert_eq!( + err, + ParseError { + kind: LexErrorKind::InvalidArgumentType { + index: 0, + mismatch: TypeMismatchError { + expected: ExpectedTypeList::from( + crate::functions::concat::EXPECTED_TYPES.into_iter() + ), + actual: Type::Int, + }, + }, + input: "concat(0, 0) == 0", + line_number: 0, + span_start: 7, + span_len: 1, + } + ); + assert_eq!( + err.to_string(), + indoc!( + r#" + Filter parsing error (1:8): + concat(0, 0) == 0 + ^ invalid type of argument #0: expected value of type Bytes or Array<_>, but got Int + "# + ) + ); + } } #[test] diff --git a/engine/src/types.rs b/engine/src/types.rs index 0223e6e5..27367279 100644 --- a/engine/src/types.rs +++ b/engine/src/types.rs @@ -56,6 +56,16 @@ impl From for ExpectedType { } } +impl std::fmt::Display for ExpectedType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ExpectedType::Array => write!(f, "Array<_>"), + ExpectedType::Map => write!(f, "Map<_>"), + ExpectedType::Type(ty) => write!(f, "{}", ty), + } + } +} + /// A list of expected types. #[derive(Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct ExpectedTypeList(BTreeSet); @@ -96,7 +106,25 @@ impl> From for ExpectedTypeList { impl std::fmt::Display for ExpectedTypeList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + match self.0.len() { + 0 => unreachable!(), + 1 => write!(f, "{}", self.0.first().unwrap()), + 2 => write!( + f, + "{} or {}", + self.0.first().unwrap(), + self.0.last().unwrap() + ), + _ => { + let mut iter = self.0.iter(); + let first = iter.next().unwrap(); + write!(f, "{{{}", first)?; + for expected_type in iter { + write!(f, ", {}", expected_type)?; + } + write!(f, "}}") + } + } } } @@ -399,8 +427,8 @@ impl std::fmt::Display for Type { Self::Bytes => write!(f, "Bytes"), Self::Int => write!(f, "Int"), Self::Ip => write!(f, "Ip"), - Self::Array(ty) => write!(f, "Array({})", Type::from(*ty)), - Self::Map(ty) => write!(f, "Map({})", Type::from(*ty)), + Self::Array(ty) => write!(f, "Array<{}>", Type::from(*ty)), + Self::Map(ty) => write!(f, "Map<{}>", Type::from(*ty)), } } }