1use amaters_core::{CipherBlob, ColumnRef, Key, Predicate, Query, Update};
4
5pub struct FluentQueryBuilder {
7 collection: String,
8}
9
10impl FluentQueryBuilder {
11 pub fn new(collection: impl Into<String>) -> Self {
13 Self {
14 collection: collection.into(),
15 }
16 }
17
18 pub fn get(self, key: Key) -> Query {
20 Query::Get {
21 collection: self.collection,
22 key,
23 }
24 }
25
26 pub fn set(self, key: Key, value: CipherBlob) -> Query {
28 Query::Set {
29 collection: self.collection,
30 key,
31 value,
32 }
33 }
34
35 pub fn delete(self, key: Key) -> Query {
37 Query::Delete {
38 collection: self.collection,
39 key,
40 }
41 }
42
43 pub fn filter(self, predicate: Predicate) -> Query {
45 Query::Filter {
46 collection: self.collection,
47 predicate,
48 }
49 }
50
51 pub fn update(self, predicate: Predicate, updates: Vec<Update>) -> Query {
53 Query::Update {
54 collection: self.collection,
55 predicate,
56 updates,
57 }
58 }
59
60 pub fn range(self, start: Key, end: Key) -> Query {
62 Query::Range {
63 collection: self.collection,
64 start,
65 end,
66 }
67 }
68
69 pub fn where_clause(self) -> PredicateBuilder {
71 PredicateBuilder::new(self.collection)
72 }
73}
74
75pub struct PredicateBuilder {
77 collection: String,
78}
79
80impl PredicateBuilder {
81 pub fn new(collection: impl Into<String>) -> Self {
83 Self {
84 collection: collection.into(),
85 }
86 }
87
88 pub fn eq(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
90 FilterBuilder::new(self.collection, Predicate::Eq(column, value))
91 }
92
93 pub fn gt(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
95 FilterBuilder::new(self.collection, Predicate::Gt(column, value))
96 }
97
98 pub fn lt(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
100 FilterBuilder::new(self.collection, Predicate::Lt(column, value))
101 }
102
103 pub fn gte(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
105 FilterBuilder::new(self.collection, Predicate::Gte(column, value))
106 }
107
108 pub fn lte(self, column: ColumnRef, value: CipherBlob) -> FilterBuilder {
110 FilterBuilder::new(self.collection, Predicate::Lte(column, value))
111 }
112}
113
114pub struct FilterBuilder {
116 collection: String,
117 predicate: Predicate,
118}
119
120impl FilterBuilder {
121 fn new(collection: String, predicate: Predicate) -> Self {
123 Self {
124 collection,
125 predicate,
126 }
127 }
128
129 pub fn and(mut self, other: Predicate) -> Self {
131 self.predicate = Predicate::And(Box::new(self.predicate), Box::new(other));
132 self
133 }
134
135 pub fn or(mut self, other: Predicate) -> Self {
137 self.predicate = Predicate::Or(Box::new(self.predicate), Box::new(other));
138 self
139 }
140
141 #[allow(clippy::should_implement_trait)]
143 pub fn not(mut self) -> Self {
144 self.predicate = Predicate::Not(Box::new(self.predicate));
145 self
146 }
147
148 pub fn build(self) -> Query {
150 Query::Filter {
151 collection: self.collection,
152 predicate: self.predicate,
153 }
154 }
155
156 pub fn update(self, updates: Vec<Update>) -> Query {
158 Query::Update {
159 collection: self.collection,
160 predicate: self.predicate,
161 updates,
162 }
163 }
164}
165
166pub fn query(collection: impl Into<String>) -> FluentQueryBuilder {
168 FluentQueryBuilder::new(collection)
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use amaters_core::col;
175
176 #[test]
177 fn test_query_builder() {
178 let q = query("users").get(Key::from_str("user:1"));
179 match q {
180 Query::Get { collection, key } => {
181 assert_eq!(collection, "users");
182 assert_eq!(key.to_string_lossy(), "user:1");
183 }
184 _ => panic!("expected Get query"),
185 }
186 }
187
188 #[test]
189 fn test_filter_builder() {
190 let q = query("users")
191 .where_clause()
192 .eq(col("age"), CipherBlob::new(vec![1, 2, 3]))
193 .build();
194
195 match q {
196 Query::Filter {
197 collection,
198 predicate,
199 } => {
200 assert_eq!(collection, "users");
201 assert!(matches!(predicate, Predicate::Eq(_, _)));
202 }
203 _ => panic!("expected Filter query"),
204 }
205 }
206
207 #[test]
208 fn test_complex_filter() {
209 let q = query("users")
210 .where_clause()
211 .eq(col("status"), CipherBlob::new(vec![1]))
212 .and(Predicate::Gt(col("age"), CipherBlob::new(vec![18])))
213 .build();
214
215 match q {
216 Query::Filter {
217 collection,
218 predicate,
219 } => {
220 assert_eq!(collection, "users");
221 assert!(matches!(predicate, Predicate::And(_, _)));
222 }
223 _ => panic!("expected Filter query"),
224 }
225 }
226
227 #[test]
228 fn test_update_builder() {
229 let updates = vec![Update::Set(col("status"), CipherBlob::new(vec![2]))];
230
231 let q = query("users")
232 .where_clause()
233 .eq(col("id"), CipherBlob::new(vec![1]))
234 .update(updates);
235
236 match q {
237 Query::Update {
238 collection,
239 predicate,
240 updates,
241 } => {
242 assert_eq!(collection, "users");
243 assert!(matches!(predicate, Predicate::Eq(_, _)));
244 assert_eq!(updates.len(), 1);
245 }
246 _ => panic!("expected Update query"),
247 }
248 }
249
250 #[test]
251 fn test_range_query() {
252 let q = query("data").range(Key::from_str("a"), Key::from_str("z"));
253
254 match q {
255 Query::Range {
256 collection,
257 start,
258 end,
259 } => {
260 assert_eq!(collection, "data");
261 assert_eq!(start.to_string_lossy(), "a");
262 assert_eq!(end.to_string_lossy(), "z");
263 }
264 _ => panic!("expected Range query"),
265 }
266 }
267}