add NotEqual filter

This commit is contained in:
kageru 2023-01-27 14:25:06 +01:00
parent 8fcb2824de
commit 557d7c680e
2 changed files with 30 additions and 23 deletions

@ -46,7 +46,7 @@ pub fn fallback_filter(query: &str) -> Result<RawCardFilter, String> {
return Err(format!("Invalid query: {query}")); return Err(format!("Invalid query: {query}"));
} }
let q = query.to_lowercase(); 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<CardFilter, String> { pub fn build_filter(query: RawCardFilter) -> Result<CardFilter, String> {
@ -60,11 +60,14 @@ pub fn build_filter(query: RawCardFilter) -> Result<CardFilter, String> {
Box::new(move |card| card.def.is_none() && card.link_rating.is_none() && card.card_type.contains("monster")) 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::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::Type, Operator::Equal, 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::Type, Operator::NotEqual, Value::String(s)) => Box::new(move |card| card.r#type != s),
(Field::Class, Operator::Equals, Value::String(s)) => Box::new(move |card| card.card_type.contains(&s)), (Field::Attribute, Operator::Equal, Value::String(s)) => Box::new(move |card| card.attribute.contains(&s)),
(Field::Text, Operator::Equals, Value::String(s)) => Box::new(move |card| card.text.contains(&s)), (Field::Attribute, Operator::NotEqual, Value::String(s)) => Box::new(move |card| !card.attribute.contains(&s)),
(Field::Name, Operator::Equals, Value::String(s)) => Box::new(move |card| card.name.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:?}"))?, q => Err(format!("unknown query: {q:?}"))?,
}) })
} }

@ -38,7 +38,7 @@ fn field(input: &str) -> IResult<&str, Field> {
map_res(take_while(char::is_alphabetic), str::parse)(input) 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> { fn operator(input: &str) -> IResult<&str, Operator> {
map_res(take_while_m_n(1, 2, |c| OPERATOR_CHARS.contains(&c)), str::parse)(input) 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)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Operator { pub enum Operator {
Equals, Equal,
NotEqual,
Less, Less,
LessEqual, LessEqual,
Greater, Greater,
@ -95,14 +96,15 @@ impl Operator {
pub fn filter_number(&self, a: Option<i32>, b: i32) -> bool { pub fn filter_number(&self, a: Option<i32>, b: i32) -> bool {
if let Some(a) = a { if let Some(a) = a {
match self { match self {
Self::Equals => a == b, Self::Equal => a == b,
Self::Less => a < b, Self::Less => a < b,
Self::LessEqual => a <= b, Self::LessEqual => a <= b,
Self::Greater => a > b, Self::Greater => a > b,
Self::GreaterEqual => a >= b, Self::GreaterEqual => a >= b,
Self::NotEqual => a != b,
} }
} else { } else {
false self == &Self::NotEqual
} }
} }
} }
@ -111,11 +113,12 @@ impl FromStr for Operator {
type Err = String; type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s { Ok(match s {
"=" | "==" | ":" => Self::Equals, "=" | "==" | ":" => Self::Equal,
">=" | "=>" => Self::GreaterEqual, ">=" | "=>" => Self::GreaterEqual,
"<=" | "=<" => Self::LessEqual, "<=" | "=<" => Self::LessEqual,
">" => Self::Greater, ">" => Self::Greater,
"<" => Self::Less, "<" => Self::Less,
"!=" => Self::NotEqual,
_ => Err(s.to_owned())?, _ => Err(s.to_owned())?,
}) })
} }
@ -132,13 +135,14 @@ mod tests {
use super::*; use super::*;
use test_case::test_case; 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::Equal, Value::String("pyro".into())))))]
#[test_case("t:PYro" => Ok(("", (Field::Type, Operator::Equals, Value::String("pyro".into())))); "input is lowercased")] #[test_case("t:PYro" => Ok(("", (Field::Type, Operator::Equal, Value::String("pyro".into())))); "input is lowercased")]
#[test_case("t==warrior" => Ok(("", (Field::Type, Operator::Equals, Value::String("warrior".into())))))] #[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("atk>=100" => Ok(("", (Field::Atk, Operator::GreaterEqual, Value::Numerical(100)))))]
#[test_case("Necrovalley" => Ok(("", (Field::Name, Operator::Equals, Value::String("necrovalley".into())))))] #[test_case("Necrovalley" => Ok(("", (Field::Name, Operator::Equal, Value::String("necrovalley".into())))))]
#[test_case("l=10" => Ok(("", (Field::Level, Operator::Equals, Value::Numerical(10)))))] #[test_case("l=10" => Ok(("", (Field::Level, Operator::Equal, Value::Numerical(10)))))]
#[test_case("Ib" => Ok(("", (Field::Name, Operator::Equals, Value::String("ib".to_owned())))))] #[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> { fn successful_parsing_test(input: &str) -> IResult<&str, RawCardFilter> {
parse_raw_filter(input) parse_raw_filter(input)
} }
@ -156,13 +160,13 @@ mod tests {
fn sequential_parsing_test() { fn sequential_parsing_test() {
let (rest, filter) = parse_raw_filter("atk>=100 l:4").unwrap(); let (rest, filter) = parse_raw_filter("atk>=100 l:4").unwrap();
assert_eq!(filter, (Field::Atk, Operator::GreaterEqual, Value::Numerical(100))); 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!( assert_eq!(
parse_raw_filters("atk>=100 l=4"), parse_raw_filters("atk>=100 l=4"),
Ok(( 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(( Ok((
"", "",
vec![ vec![
(Field::Type, Operator::Equals, Value::String("counter".into())), (Field::Type, Operator::Equal, Value::String("counter".into())),
(Field::Class, Operator::Equals, Value::String("trap".into())), (Field::Class, Operator::Equal, Value::String("trap".into())),
(Field::Text, Operator::Equals, Value::String("negate the summon".into())), (Field::Text, Operator::Equal, Value::String("negate the summon".into())),
] ]
)) ))
); );
@ -183,6 +187,6 @@ mod tests {
fn quoted_value_test() { fn quoted_value_test() {
let (rest, filter) = parse_raw_filter(r#"o:"destroy that target""#).unwrap(); let (rest, filter) = parse_raw_filter(r#"o:"destroy that target""#).unwrap();
assert_eq!(rest, ""); 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())));
} }
} }