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 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 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 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 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 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 pub fn and<T: Display>(&self, format: &str, value: T) -> QueryBuilder {
97 self.filter_by(format, value)
98 }
99
100 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 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 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 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}