jsonbox/client/
query_builder.rs

1use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
2use serde::de::DeserializeOwned;
3use std::fmt::Display;
4
5use crate::client::{Client, Meta};
6use crate::error::Result;
7
8#[derive(Clone)]
9enum Order<'a> {
10    Asc(&'a str),
11    Desc(&'a str),
12}
13
14pub struct QueryBuilder<'a> {
15    client: &'a Client<'a>,
16    sort: Order<'a>,
17    skip: u32,
18    limit: u32,
19    q: Vec<String>,
20}
21
22impl<'a> QueryBuilder<'a> {
23    pub fn new(client: &'a Client) -> QueryBuilder<'a> {
24        QueryBuilder {
25            client,
26            sort: Order::Desc("_createdOn".into()),
27            skip: 0,
28            limit: 20,
29            q: vec![],
30        }
31    }
32
33    /// Set the field for sorting.
34    pub fn order_by(&self, field: &'a str) -> QueryBuilder {
35        QueryBuilder {
36            client: self.client,
37            sort: Order::Asc(field),
38            skip: self.skip,
39            limit: self.limit,
40            q: self.q.clone(),
41        }
42    }
43
44    /// Set reverse order. Use this with `order_by` method.
45    pub fn desc(&self) -> QueryBuilder {
46        let field = match &self.sort {
47            Order::Asc(f) => f,
48            Order::Desc(f) => f,
49        };
50        QueryBuilder {
51            client: self.client,
52            sort: Order::Desc(field.clone()),
53            skip: self.skip,
54            limit: self.limit,
55            q: self.q.clone(),
56        }
57    }
58
59    /// Limit the number of records of query result.
60    pub fn limit(&self, limit: u32) -> QueryBuilder {
61        QueryBuilder {
62            client: self.client,
63            sort: self.sort.clone(),
64            skip: self.skip,
65            limit,
66            q: self.q.clone(),
67        }
68    }
69
70    /// Specify the number of records to skip.
71    pub fn skip(&self, skip: u32) -> QueryBuilder {
72        QueryBuilder {
73            client: self.client,
74            sort: self.sort.clone(),
75            skip,
76            limit: self.limit,
77            q: self.q.clone(),
78        }
79    }
80
81    /// Set filter option, whkch is mapped `q` parameter in REST API.
82    pub fn filter_by<T: Display>(&self, format: &str, value: T) -> QueryBuilder {
83        let value = utf8_percent_encode(&format!("{}", value), NON_ALPHANUMERIC).to_string();
84        let mut q = self.q.clone();
85        q.push(format.replace("{}", &value));
86        QueryBuilder {
87            client: self.client,
88            sort: self.sort.clone(),
89            skip: self.skip,
90            limit: self.limit,
91            q,
92        }
93    }
94
95    /// Alias of `filter_by`.
96    pub fn and<T: Display>(&self, format: &str, value: T) -> QueryBuilder {
97        self.filter_by(format, value)
98    }
99
100    /// Get a single record by id.
101    pub fn id<T>(&self, id: &str) -> Result<(T, Meta)>
102    where
103        T: DeserializeOwned,
104    {
105        self.client.read_by_id(id)
106    }
107
108    /// Get all records with default query parameters.
109    pub fn all<T>(&self) -> Result<Vec<(T, Meta)>>
110    where
111        T: DeserializeOwned,
112    {
113        let default = QueryBuilder::new(self.client);
114        self.client.read_by_query(&default)
115    }
116
117    /// Run query with configured query parameters.
118    pub fn run<T>(&self) -> Result<Vec<(T, Meta)>>
119    where
120        T: DeserializeOwned,
121    {
122        self.client.read_by_query(self)
123    }
124
125    /// Generate query string.
126    pub fn to_string(&self) -> String {
127        let mut query = format!(
128            "sort={}&skip={}&limit={}",
129            self.sort_string(),
130            self.skip,
131            self.limit
132        );
133        if self.q.len() > 0 {
134            query = format!("{}&q={}", query, self.filter_string());
135        }
136        query
137    }
138
139    fn sort_string(&self) -> String {
140        match &self.sort {
141            Order::Asc(field) => field.to_string(),
142            Order::Desc(field) => format!("-{}", field),
143        }
144    }
145
146    fn filter_string(&self) -> String {
147        let mut filter = self
148            .q
149            .iter()
150            .fold(String::new(), |acc, q| format!("{}{},", acc, q));
151        filter.pop();
152        filter
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn test_to_string() {
162        let c = Client::new("xxx");
163        assert_eq!(
164            QueryBuilder::new(&c)
165                .order_by("count")
166                .desc()
167                .limit(42)
168                .skip(8)
169                .filter_by("count:>{}", 20)
170                .and("count:<{}", 40)
171                .to_string(),
172            "sort=-count&skip=8&limit=42&q=count:>20,count:<40"
173        );
174    }
175
176    #[test]
177    fn test_sort_string() {
178        let c = Client::new("xxx");
179        let q = QueryBuilder::new(&c);
180        assert_eq!(q.sort_string(), "-_createdOn");
181
182        let q = q.order_by("name");
183        assert_eq!(q.sort_string(), "name");
184
185        let q = q.desc();
186        assert_eq!(q.sort_string(), "-name");
187    }
188
189    #[test]
190    fn test_filter_string() {
191        let c = Client::new("xxx");
192        let q = QueryBuilder::new(&c);
193        assert_eq!(q.filter_string(), "");
194
195        let q = q.filter_by("name:{}", "foo bar");
196        assert_eq!(q.filter_string(), "name:foo%20bar");
197
198        let q = q.filter_by("city:{}*", "Los ");
199        assert_eq!(q.filter_string(), "name:foo%20bar,city:Los%20*");
200
201        let q = q.filter_by("count:<{}", 42);
202        assert_eq!(q.filter_string(), "name:foo%20bar,city:Los%20*,count:<42");
203
204        let q = q.filter_by("login:{}", true);
205        assert_eq!(
206            q.filter_string(),
207            "name:foo%20bar,city:Los%20*,count:<42,login:true"
208        );
209    }
210}