11#![ allow( missing_docs) ]
22// TODO: unfinished functionality
33
4- use crate :: proto:: ToArguments ;
5- use std:: borrow:: Cow ;
4+ use crate :: proto:: { Quoted , ToArguments } ;
5+ use std:: {
6+ io:: Write , // implements write for Vec
7+ borrow:: Cow
8+ } ;
69use std:: convert:: Into ;
710use std:: fmt;
811use std:: result:: Result as StdResult ;
@@ -17,16 +20,39 @@ pub enum Term<'a> {
1720 Tag ( Cow < ' a , str > ) ,
1821}
1922
23+ #[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) , serde( untagged, rename_all = "lowercase" ) ) ]
24+ pub enum Operation {
25+ Equals ,
26+ NotEquals ,
27+ Contains ,
28+ #[ cfg_attr( feature = "serde" , serde( rename = "starts_with" ) ) ]
29+ StartsWith
30+ }
31+
2032#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
2133pub struct Filter < ' a > {
2234 typ : Term < ' a > ,
2335 what : Cow < ' a , str > ,
36+ how : Operation
2437}
2538
2639impl < ' a > Filter < ' a > {
27- fn new < W > ( typ : Term < ' a > , what : W ) -> Filter
40+ pub fn new < W > ( typ : Term < ' a > , what : W ) -> Filter
2841 where W : ' a + Into < Cow < ' a , str > > {
29- Filter { typ, what : what. into ( ) }
42+ Filter {
43+ typ,
44+ what : what. into ( ) ,
45+ how : Operation :: Equals
46+ }
47+ }
48+
49+ pub fn new_with_op < W > ( typ : Term < ' a > , what : W , how : Operation ) -> Filter
50+ where W : ' a + Into < Cow < ' a , str > > {
51+ Filter {
52+ typ,
53+ what : what. into ( ) ,
54+ how
55+ }
3056 }
3157}
3258
@@ -59,6 +85,11 @@ impl<'a> Query<'a> {
5985 self . filters . push ( Filter :: new ( term, value) ) ;
6086 self
6187 }
88+
89+ pub fn and_with_op < ' b : ' a , V : ' b + Into < Cow < ' b , str > > > ( & mut self , term : Term < ' b > , op : Operation , value : V ) -> & mut Query < ' a > {
90+ self . filters . push ( Filter :: new_with_op ( term, value, op) ) ;
91+ self
92+ }
6293}
6394
6495impl < ' a > fmt:: Display for Term < ' a > {
@@ -80,21 +111,70 @@ impl<'a> ToArguments for &'a Term<'a> {
80111 }
81112}
82113
114+ impl fmt:: Display for Operation {
115+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
116+ f. write_str ( match * self {
117+ Operation :: Equals => "==" ,
118+ Operation :: NotEquals => "!=" ,
119+ Operation :: Contains => "contains" ,
120+ Operation :: StartsWith => "starts_with"
121+ } )
122+ }
123+ }
124+
125+ impl ToArguments for Operation {
126+ fn to_arguments < F , E > ( & self , f : & mut F ) -> StdResult < ( ) , E >
127+ where F : FnMut ( & str ) -> StdResult < ( ) , E > {
128+ f ( & self . to_string ( ) )
129+ }
130+ }
131+
83132impl < ' a > ToArguments for & ' a Filter < ' a > {
84133 fn to_arguments < F , E > ( & self , f : & mut F ) -> StdResult < ( ) , E >
85134 where F : FnMut ( & str ) -> StdResult < ( ) , E > {
86- ( & self . typ ) . to_arguments ( f) ?;
87- f ( & self . what )
135+ match self . typ {
136+ // For some terms, the filter clause cannot have an operation
137+ Term :: Base | Term :: LastMod => {
138+ f ( & format ! (
139+ "({} {})" ,
140+ & self . typ,
141+ & Quoted ( & self . what) . to_string( )
142+ ) )
143+ }
144+ _ => {
145+ f ( & format ! (
146+ "({} {} {})" ,
147+ & self . typ,
148+ & self . how,
149+ & Quoted ( & self . what) . to_string( ) )
150+ )
151+ }
152+ }
88153 }
89154}
90155
91156impl < ' a > ToArguments for & ' a Query < ' a > {
157+ // Use MPD 0.21+ filter syntax
92158 fn to_arguments < F , E > ( & self , f : & mut F ) -> StdResult < ( ) , E >
93159 where F : FnMut ( & str ) -> StdResult < ( ) , E > {
94- for filter in & self . filters {
95- filter. to_arguments ( f) ?
160+ // Construct the query string in its entirety first before escaping
161+ if !self . filters . is_empty ( ) {
162+ let mut qs = String :: new ( ) ;
163+ for ( i, filter) in self . filters . iter ( ) . enumerate ( ) {
164+ if i > 0 {
165+ qs. push_str ( " AND " ) ;
166+ }
167+ // Leave escaping to the filter since terms should not be escaped or quoted
168+ filter. to_arguments ( & mut |arg| {
169+ qs. push_str ( arg) ;
170+ Ok ( ( ) )
171+ } ) ?;
172+ }
173+ // println!("Singly escaped query string: {}", &qs);
174+ f ( & qs)
175+ } else {
176+ Ok ( ( ) )
96177 }
97- Ok ( ( ) )
98178 }
99179}
100180
0 commit comments