async_mpd/client/
filter.rs
1use crate::Tag;
2use itertools::Itertools;
3
4pub trait ToFilterExpr {
5 fn equals<T: ToString>(self, s: T) -> FilterExpr;
7
8 fn contains<T: ToString>(self, s: T) -> FilterExpr;
10}
11
12impl ToFilterExpr for Tag {
13 fn equals<T: ToString>(self, s: T) -> FilterExpr {
14 FilterExpr::Equals(self, s.to_string())
15 }
16
17 fn contains<T: ToString>(self, s: T) -> FilterExpr {
18 FilterExpr::Contains(self, s.to_string())
19 }
20}
21
22pub enum FilterExpr {
24 Equals(Tag, String),
25 Contains(Tag, String),
26 Not(Box<FilterExpr>),
27}
28
29impl FilterExpr {
30 pub fn to_query(&self) -> String {
31 match self {
32 FilterExpr::Equals(tag, s) => format!("({:?} == \"{}\")", tag, s),
33 FilterExpr::Contains(tag, s) => format!("({:?} contains \"{}\")", tag, s),
34 FilterExpr::Not(exp) => format!("!{}", exp.to_query()),
35 }
36 }
37}
38
39#[derive(Default)]
41pub struct Filter {
42 exprs: Vec<FilterExpr>,
43}
44
45impl Filter {
46 pub fn new() -> Self {
47 Self { exprs: Vec::new() }
48 }
49
50 pub fn with(filter: FilterExpr) -> Self {
51 Self {
52 exprs: vec![filter],
53 }
54 }
55
56 pub fn and(mut self, other: FilterExpr) -> Filter {
57 self.exprs.push(other);
58 self
59 }
60
61 pub fn and_not(mut self, other: FilterExpr) -> Self {
62 self.exprs.push(FilterExpr::Not(Box::new(other)));
63 self
64 }
65
66 pub fn to_query(&self) -> Option<String> {
67 if self.exprs.is_empty() {
68 return None;
69 }
70
71 let joined = self
72 .exprs
73 .iter()
74 .map(|filter| filter.to_query())
75 .join(" AND ");
76
77 Some(format!("({})", escape(&joined)))
78 }
79}
80
81fn escape(s: &str) -> String {
82 s.replace('\\', "\\\\")
83 .replace('\"', "\\\"")
84 .replace('\'', "\\\'")
85}