From 557d7c680e3f491cdee05411d970b0a2d5d0c941 Mon Sep 17 00:00:00 2001 From: kageru Date: Fri, 27 Jan 2023 14:25:06 +0100 Subject: [PATCH] add NotEqual filter --- src/filter.rs | 15 +++++++++------ src/parser.rs | 38 +++++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/filter.rs b/src/filter.rs index ec87ce8..1c97a96 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -46,7 +46,7 @@ pub fn fallback_filter(query: &str) -> Result { return Err(format!("Invalid query: {query}")); } let q = query.to_lowercase(); - Ok((Field::Name, Operator::Equals, Value::String(q))) + Ok((Field::Name, Operator::Equal, Value::String(q))) } pub fn build_filter(query: RawCardFilter) -> Result { @@ -60,11 +60,14 @@ pub fn build_filter(query: RawCardFilter) -> Result { Box::new(move |card| card.def.is_none() && card.link_rating.is_none() && card.card_type.contains("monster")) } (Field::Level, op, Value::Numerical(n)) => Box::new(move |card| op.filter_number(card.level, n)), - (Field::Type, Operator::Equals, Value::String(s)) => Box::new(move |card| card.r#type == s), - (Field::Attribute, Operator::Equals, Value::String(s)) => Box::new(move |card| card.attribute.contains(&s)), - (Field::Class, Operator::Equals, Value::String(s)) => Box::new(move |card| card.card_type.contains(&s)), - (Field::Text, Operator::Equals, Value::String(s)) => Box::new(move |card| card.text.contains(&s)), - (Field::Name, Operator::Equals, Value::String(s)) => Box::new(move |card| card.name.contains(&s)), + (Field::Type, Operator::Equal, Value::String(s)) => Box::new(move |card| card.r#type == s), + (Field::Type, Operator::NotEqual, Value::String(s)) => Box::new(move |card| card.r#type != s), + (Field::Attribute, Operator::Equal, Value::String(s)) => Box::new(move |card| card.attribute.contains(&s)), + (Field::Attribute, Operator::NotEqual, Value::String(s)) => Box::new(move |card| !card.attribute.contains(&s)), + (Field::Class, Operator::Equal, Value::String(s)) => Box::new(move |card| card.card_type.contains(&s)), + (Field::Class, Operator::NotEqual, Value::String(s)) => Box::new(move |card| !card.card_type.contains(&s)), + (Field::Text, Operator::Equal, Value::String(s)) => Box::new(move |card| card.text.contains(&s)), + (Field::Name, Operator::Equal, Value::String(s)) => Box::new(move |card| card.name.contains(&s)), q => Err(format!("unknown query: {q:?}"))?, }) } diff --git a/src/parser.rs b/src/parser.rs index 512e129..bb7135c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -38,7 +38,7 @@ fn field(input: &str) -> IResult<&str, Field> { map_res(take_while(char::is_alphabetic), str::parse)(input) } -pub const OPERATOR_CHARS: &[char] = &['=', '<', '>', ':']; +pub const OPERATOR_CHARS: &[char] = &['=', '<', '>', ':', '!']; fn operator(input: &str) -> IResult<&str, Operator> { map_res(take_while_m_n(1, 2, |c| OPERATOR_CHARS.contains(&c)), str::parse)(input) @@ -84,7 +84,8 @@ impl FromStr for Field { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Operator { - Equals, + Equal, + NotEqual, Less, LessEqual, Greater, @@ -95,14 +96,15 @@ impl Operator { pub fn filter_number(&self, a: Option, b: i32) -> bool { if let Some(a) = a { match self { - Self::Equals => a == b, + Self::Equal => a == b, Self::Less => a < b, Self::LessEqual => a <= b, Self::Greater => a > b, Self::GreaterEqual => a >= b, + Self::NotEqual => a != b, } } else { - false + self == &Self::NotEqual } } } @@ -111,11 +113,12 @@ impl FromStr for Operator { type Err = String; fn from_str(s: &str) -> Result { Ok(match s { - "=" | "==" | ":" => Self::Equals, + "=" | "==" | ":" => Self::Equal, ">=" | "=>" => Self::GreaterEqual, "<=" | "=<" => Self::LessEqual, ">" => Self::Greater, "<" => Self::Less, + "!=" => Self::NotEqual, _ => Err(s.to_owned())?, }) } @@ -132,13 +135,14 @@ mod tests { use super::*; use test_case::test_case; - #[test_case("t=pyro" => Ok(("", (Field::Type, Operator::Equals, Value::String("pyro".into())))))] - #[test_case("t:PYro" => Ok(("", (Field::Type, Operator::Equals, Value::String("pyro".into())))); "input is lowercased")] - #[test_case("t==warrior" => Ok(("", (Field::Type, Operator::Equals, Value::String("warrior".into())))))] + #[test_case("t=pyro" => Ok(("", (Field::Type, Operator::Equal, Value::String("pyro".into())))))] + #[test_case("t:PYro" => Ok(("", (Field::Type, Operator::Equal, Value::String("pyro".into())))); "input is lowercased")] + #[test_case("t==warrior" => Ok(("", (Field::Type, Operator::Equal, Value::String("warrior".into())))))] #[test_case("atk>=100" => Ok(("", (Field::Atk, Operator::GreaterEqual, Value::Numerical(100)))))] - #[test_case("Necrovalley" => Ok(("", (Field::Name, Operator::Equals, Value::String("necrovalley".into())))))] - #[test_case("l=10" => Ok(("", (Field::Level, Operator::Equals, Value::Numerical(10)))))] - #[test_case("Ib" => Ok(("", (Field::Name, Operator::Equals, Value::String("ib".to_owned())))))] + #[test_case("Necrovalley" => Ok(("", (Field::Name, Operator::Equal, Value::String("necrovalley".into())))))] + #[test_case("l=10" => Ok(("", (Field::Level, Operator::Equal, Value::Numerical(10)))))] + #[test_case("Ib" => Ok(("", (Field::Name, Operator::Equal, Value::String("ib".to_owned())))))] + #[test_case("c!=synchro" => Ok(("", (Field::Class, Operator::NotEqual, Value::String("synchro".to_owned())))))] fn successful_parsing_test(input: &str) -> IResult<&str, RawCardFilter> { parse_raw_filter(input) } @@ -156,13 +160,13 @@ mod tests { fn sequential_parsing_test() { let (rest, filter) = parse_raw_filter("atk>=100 l:4").unwrap(); assert_eq!(filter, (Field::Atk, Operator::GreaterEqual, Value::Numerical(100))); - assert_eq!(parse_raw_filter(rest), Ok(("", (Field::Level, Operator::Equals, Value::Numerical(4))))); + assert_eq!(parse_raw_filter(rest), Ok(("", (Field::Level, Operator::Equal, Value::Numerical(4))))); assert_eq!( parse_raw_filters("atk>=100 l=4"), Ok(( "", - vec![(Field::Atk, Operator::GreaterEqual, Value::Numerical(100)), (Field::Level, Operator::Equals, Value::Numerical(4))] + vec![(Field::Atk, Operator::GreaterEqual, Value::Numerical(100)), (Field::Level, Operator::Equal, Value::Numerical(4))] )) ); @@ -171,9 +175,9 @@ mod tests { Ok(( "", vec![ - (Field::Type, Operator::Equals, Value::String("counter".into())), - (Field::Class, Operator::Equals, Value::String("trap".into())), - (Field::Text, Operator::Equals, Value::String("negate the summon".into())), + (Field::Type, Operator::Equal, Value::String("counter".into())), + (Field::Class, Operator::Equal, Value::String("trap".into())), + (Field::Text, Operator::Equal, Value::String("negate the summon".into())), ] )) ); @@ -183,6 +187,6 @@ mod tests { fn quoted_value_test() { let (rest, filter) = parse_raw_filter(r#"o:"destroy that target""#).unwrap(); assert_eq!(rest, ""); - assert_eq!(filter, (Field::Text, Operator::Equals, Value::String("destroy that target".into()))); + assert_eq!(filter, (Field::Text, Operator::Equal, Value::String("destroy that target".into()))); } }