2019-05-07 18:36:23 +02:00
use crate ::model ::* ;
use std ::str ::FromStr ;
2019-05-07 18:57:00 +02:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
2019-05-07 18:36:23 +02:00
pub enum QueryFilter {
Not ( Box < QueryFilter > ) ,
Tag ( String ) ,
SpecialInName ( String ) ,
SpecialBeforeDate ( Date ) ,
SpecialAfterDate ( Date ) ,
SpecialMinResolution ( u64 ) ,
}
2019-05-07 18:57:00 +02:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
2019-05-07 18:36:23 +02:00
pub enum Score {
RequiredMatch ( bool ) ,
Match ( bool ) ,
}
2019-05-07 18:57:00 +02:00
#[ derive(Debug, PartialEq, Eq, Clone) ]
2019-05-07 18:36:23 +02:00
pub enum QueryFilterSyntaxError {
UnknownSpecialFilter ,
ResolutionArgumentInvalid ,
DateArgumentInvalid ,
2019-05-07 18:57:00 +02:00
NameArgumentInvalid ,
ArgumentInvalid ,
2019-05-07 18:36:23 +02:00
}
use std ::ops ::Not ;
impl Not for Score {
type Output = Score ;
fn not ( self ) -> Score {
match self {
Score ::RequiredMatch ( b ) = > Score ::RequiredMatch ( ! b ) ,
Score ::Match ( b ) = > Score ::Match ( ! b ) ,
}
}
}
impl QueryFilter {
pub fn is_special ( & self ) -> bool {
use QueryFilter ::* ;
match self {
Not ( inner ) = > inner . is_special ( ) ,
Tag ( _ ) = > false ,
SpecialInName ( _ ) = > true ,
SpecialBeforeDate ( _ ) = > true ,
SpecialAfterDate ( _ ) = > true ,
SpecialMinResolution ( _ ) = > true ,
}
}
pub fn score ( & self , texture : & Texture ) -> Score {
use QueryFilter ::* ;
match self {
Not ( inner ) = > inner . score ( texture ) . not ( ) ,
Tag ( tag ) = > Score ::Match (
texture
. tags
. iter ( )
. find ( | tt | tt . to_lowercase ( ) = = tag . to_lowercase ( ) )
. is_some ( ) ,
) ,
SpecialInName ( name ) = > Score ::RequiredMatch (
//
texture . name . contains ( name ) ,
) ,
SpecialBeforeDate ( date ) = > Score ::RequiredMatch ( texture . added_on < = * date ) ,
SpecialAfterDate ( date ) = > Score ::RequiredMatch ( texture . added_on > * date ) ,
SpecialMinResolution ( required ) = > {
let smaller_resolution = u64 ::min ( texture . resolution . 0 , texture . resolution . 1 ) ;
Score ::RequiredMatch ( smaller_resolution > = * required )
}
}
}
}
impl FromStr for QueryFilter {
type Err = QueryFilterSyntaxError ;
fn from_str ( input : & str ) -> Result < QueryFilter , QueryFilterSyntaxError > {
const NEGATION_CHAR : char = '!' ;
const SPLIT_CHAR : char = ':' ;
2019-05-07 18:57:00 +02:00
if input . is_empty ( ) {
return Err ( QueryFilterSyntaxError ::ArgumentInvalid ) ;
} else if input . starts_with ( NEGATION_CHAR ) {
2019-05-07 18:36:23 +02:00
// A Not.
let inner = Self ::from_str ( & input [ 1 .. ] ) ? ;
return Ok ( QueryFilter ::Not ( Box ::new ( inner ) ) ) ;
} else if input . contains ( SPLIT_CHAR ) {
// A Special Filter
let mut parts = input . splitn ( 2 , SPLIT_CHAR ) ;
let filter_name = parts . next ( ) . unwrap ( ) . to_lowercase ( ) ;
let filter_arg = parts . next ( ) . unwrap ( ) ;
match filter_name . as_str ( ) {
2019-05-07 18:57:00 +02:00
" n " | " name " = > {
if filter_arg . is_empty ( ) {
return Err ( QueryFilterSyntaxError ::NameArgumentInvalid ) ;
}
Ok ( QueryFilter ::SpecialInName ( filter_arg . to_string ( ) ) )
}
2019-05-07 18:36:23 +02:00
" r " | " res " | " resolution " = > {
let num : u64 ;
if filter_arg . ends_with ( " k " ) {
num = * & filter_arg [ 0 .. ( filter_arg . len ( ) - 1 ) ]
. parse ::< u32 > ( )
. map ( | n | ( n as u64 ) * 1024 )
. map_err ( | _e | QueryFilterSyntaxError ::ResolutionArgumentInvalid ) ? ;
} else {
num = filter_arg
. parse ( )
. map_err ( | _e | QueryFilterSyntaxError ::ResolutionArgumentInvalid ) ? ;
}
Ok ( QueryFilter ::SpecialMinResolution ( num ) )
}
" a " | " after " = > {
let date = Date ::from_str ( filter_arg )
2019-05-07 18:44:59 +02:00
. map_err ( | _ | QueryFilterSyntaxError ::DateArgumentInvalid ) ? ;
2019-05-07 18:36:23 +02:00
Ok ( QueryFilter ::SpecialAfterDate ( date ) )
}
" b " | " bef " | " before " = > {
let date = Date ::from_str ( filter_arg )
2019-05-07 18:44:59 +02:00
. map_err ( | _ | QueryFilterSyntaxError ::DateArgumentInvalid ) ? ;
2019-05-07 18:36:23 +02:00
Ok ( QueryFilter ::SpecialBeforeDate ( date ) )
}
_ = > Err ( QueryFilterSyntaxError ::UnknownSpecialFilter ) ,
}
} else {
// A Tag
Ok ( QueryFilter ::Tag ( input . to_string ( ) ) )
}
}
}
2019-05-07 18:57:00 +02:00
#[ 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 ( )
) )
) ;
}
}