diff --git a/server/texture-sync-server/src/persistency/search/query_filter.rs b/server/texture-sync-server/src/persistency/search/query_filter.rs index fae15e4..22bcf98 100644 --- a/server/texture-sync-server/src/persistency/search/query_filter.rs +++ b/server/texture-sync-server/src/persistency/search/query_filter.rs @@ -1,6 +1,7 @@ use crate::model::*; use std::str::FromStr; +#[derive(Debug, PartialEq, Eq, Clone)] pub enum QueryFilter { Not(Box), Tag(String), @@ -10,15 +11,19 @@ pub enum QueryFilter { SpecialMinResolution(u64), } +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Score { RequiredMatch(bool), Match(bool), } +#[derive(Debug, PartialEq, Eq, Clone)] pub enum QueryFilterSyntaxError { UnknownSpecialFilter, ResolutionArgumentInvalid, DateArgumentInvalid, + NameArgumentInvalid, + ArgumentInvalid, } use std::ops::Not; @@ -84,7 +89,9 @@ impl FromStr for QueryFilter { const NEGATION_CHAR: char = '!'; const SPLIT_CHAR: char = ':'; - if input.starts_with(NEGATION_CHAR) { + if input.is_empty() { + return Err(QueryFilterSyntaxError::ArgumentInvalid); + } else if input.starts_with(NEGATION_CHAR) { // A Not. let inner = Self::from_str(&input[1..])?; return Ok(QueryFilter::Not(Box::new(inner))); @@ -95,7 +102,12 @@ impl FromStr for QueryFilter { let filter_arg = parts.next().unwrap(); match filter_name.as_str() { - "n" | "name" => Ok(QueryFilter::SpecialInName(filter_name.to_string())), + "n" | "name" => { + if filter_arg.is_empty() { + return Err(QueryFilterSyntaxError::NameArgumentInvalid); + } + Ok(QueryFilter::SpecialInName(filter_arg.to_string())) + } "r" | "res" | "resolution" => { let num: u64; @@ -133,3 +145,63 @@ impl FromStr for QueryFilter { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parsing() { + assert!(QueryFilter::from_str("cats:meep").is_err()); + assert!(QueryFilter::from_str("name:").is_err()); + assert!(QueryFilter::from_str("res:-400k").is_err()); + assert!(QueryFilter::from_str("res:4647846846846864864846868446864846844684784k").is_err()); + assert!(QueryFilter::from_str("!!!!a:80-50-50").is_err()); + + assert_eq!( + QueryFilter::from_str("n:hello"), + Ok(QueryFilter::SpecialInName("hello".to_string())) + ); + + assert_eq!( + QueryFilter::from_str("NaMe:hello"), + Ok(QueryFilter::SpecialInName("hello".to_string())) + ); + + assert_eq!( + QueryFilter::from_str("res:4k"), + Ok(QueryFilter::SpecialMinResolution(4096)) + ); + + assert_eq!( + QueryFilter::from_str("res:4096"), + Ok(QueryFilter::SpecialMinResolution(4096)) + ); + + assert_eq!( + QueryFilter::from_str("a:2019-10-10"), + Ok(QueryFilter::SpecialAfterDate(Date::new(2019, 10, 10))) + ); + + assert_eq!( + QueryFilter::from_str("!before:2019-10-10"), + Ok(QueryFilter::Not( + QueryFilter::SpecialBeforeDate(Date::new(2019, 10, 10)).into() + )) + ); + + assert_eq!( + QueryFilter::from_str("!before:2019-10-10"), + Ok(QueryFilter::Not( + QueryFilter::SpecialBeforeDate(Date::new(2019, 10, 10)).into() + )) + ); + + assert_eq!( + QueryFilter::from_str("!Wood"), + Ok(QueryFilter::Not( + QueryFilter::Tag("Wood".to_string()).into() + )) + ); + } +}