1pub mod concatenation;
27pub mod field;
28pub mod field_filter;
29pub mod field_order;
30pub mod field_path;
31pub mod from_key_fields;
32pub mod predicate;
33pub mod query_token;
34pub mod query_with;
35pub mod selection;
36pub mod wildcard;
37
38use crate::query::selection::Selection;
39use std::collections::HashMap;
40use std::collections::HashSet;
41use std::fmt;
42
43use concatenation::Concatenation;
44use field::Field;
45use predicate::Predicate;
46use query_token::QueryToken;
47use query_with::QueryWith;
48use wildcard::Wildcard;
49
50use crate::sql_arg::SqlArg;
93#[derive(Debug)]
94pub struct Query<M> {
95 pub(crate) tokens: Vec<QueryToken>,
96 pub distinct: bool,
98
99 pub aux_params: HashMap<String, SqlArg>, pub where_predicates: Vec<String>,
104
105 pub where_predicate_params: Vec<SqlArg>,
107
108 pub select_columns: Vec<String>,
110
111 pub join_stmts: Vec<String>,
113
114 pub join_stmt_params: Vec<SqlArg>,
116
117 pub type_marker: std::marker::PhantomData<M>,
119}
120
121impl<M> Query<M> {
122 pub fn new() -> Self {
124 Query::<M> {
125 tokens: vec![],
126 distinct: false,
127 aux_params: HashMap::new(),
128 where_predicates: Vec::new(),
129 where_predicate_params: Vec::new(),
130 select_columns: Vec::new(),
131 join_stmts: Vec::new(),
132 join_stmt_params: Vec::new(),
133 type_marker: std::marker::PhantomData, }
135 }
136
137 pub fn from<T>(query: T) -> Self
139 where
140 T: Into<Query<M>>,
141 {
142 query.into()
143 }
144
145 pub fn clone_for_type<T>(&self) -> Query<T> {
147 Query::<T> {
148 tokens: self.tokens.clone(),
149 distinct: self.distinct,
150 aux_params: self.aux_params.clone(),
151 where_predicates: self.where_predicates.clone(),
152 where_predicate_params: self.where_predicate_params.clone(),
153 select_columns: self.select_columns.clone(),
154 join_stmts: self.join_stmts.clone(),
155 join_stmt_params: self.join_stmt_params.clone(),
156 type_marker: std::marker::PhantomData,
157 }
158 }
159 pub fn traverse<T>(&self, home_path: &str) -> Query<T> {
161 let tokens = self
162 .tokens
163 .iter()
164 .filter_map(|t| match t {
165 QueryToken::Field(field) => {
166 if field.name.starts_with(home_path) {
167 let mut field = field.clone();
168 field.name = field
169 .name
170 .trim_start_matches(home_path)
171 .trim_start_matches('_')
172 .to_string();
173 Some(QueryToken::Field(field))
174 } else {
175 None
176 }
177 }
178 QueryToken::Wildcard(wildcard) => {
179 if wildcard.path.starts_with(home_path) {
180 let mut wildcard = wildcard.clone();
181 wildcard.path = wildcard
182 .path
183 .trim_start_matches(home_path)
184 .trim_start_matches('_')
185 .to_string();
186 Some(QueryToken::Wildcard(wildcard))
187 } else {
188 None
189 }
190 }
191 _ => None,
192 })
193 .collect::<Vec<_>>();
194
195 Query::<T> {
196 tokens,
197 distinct: self.distinct,
198 aux_params: self.aux_params.clone(),
199 where_predicates: self.where_predicates.clone(),
200 where_predicate_params: self.where_predicate_params.clone(),
201 select_columns: self.select_columns.clone(),
202 join_stmts: self.join_stmts.clone(),
203 join_stmt_params: self.join_stmt_params.clone(),
204 type_marker: std::marker::PhantomData,
205 }
206 }
207
208 pub fn wildcard() -> Self {
210 Query::<M> {
211 tokens: vec![QueryToken::Wildcard(Wildcard::new())],
212 distinct: false,
213 aux_params: HashMap::new(),
215 where_predicates: Vec::new(),
216 where_predicate_params: Vec::new(),
217 select_columns: Vec::new(),
218 join_stmts: Vec::new(),
219 join_stmt_params: Vec::new(),
220 type_marker: std::marker::PhantomData, }
222 }
223 pub fn selection(name: impl Into<String>) -> Self {
224 Query::<M> {
225 tokens: vec![QueryToken::Selection(Selection::from(name.into()))],
226 distinct: false,
227 aux_params: HashMap::new(),
229 where_predicates: Vec::new(),
230 where_predicate_params: Vec::new(),
231 select_columns: Vec::new(),
232 join_stmts: Vec::new(),
233 join_stmt_params: Vec::new(),
234 type_marker: std::marker::PhantomData, }
236 }
237
238 pub fn parenthesize(mut self) -> Self {
240 if self.tokens.is_empty() {
241 return self;
242 }
243 self.tokens
244 .insert(0, QueryToken::LeftBracket(Concatenation::And));
245 self.tokens.push(QueryToken::RightBracket);
246 self
247 }
248 pub fn and<T>(mut self, query: T) -> Self
250 where
251 T: Into<Query<M>>,
252 {
253 self.tokens.append(&mut query.into().tokens);
255 self
256 }
257 pub fn and_parentized<T>(mut self, query: T) -> Self
259 where
260 T: Into<Query<M>>,
261 {
262 self.tokens
263 .push(QueryToken::LeftBracket(Concatenation::And));
264 self.tokens.append(&mut query.into().tokens);
265 self.tokens.push(QueryToken::RightBracket);
266 self
267 }
268
269 pub fn or<T>(mut self, query: T) -> Self
271 where
272 T: Into<Query<M>>,
273 {
274 let mut query = query.into();
276 if !self.tokens.is_empty() {
277 match query.tokens.get_mut(0) {
278 Some(QueryToken::LeftBracket(c)) => *c = Concatenation::Or,
279 Some(QueryToken::RightBracket) => {}
280 Some(QueryToken::Field(f)) => f.concatenation = Concatenation::Or,
281 Some(QueryToken::Wildcard(w)) => w.concatenation = Concatenation::Or,
282 Some(QueryToken::Predicate(p)) => p.concatenation = Concatenation::Or,
283 Some(QueryToken::Selection(p)) => p.concatenation = Concatenation::Or,
284 None => {}
285 }
286 }
287
288 self.tokens.append(&mut query.tokens);
289
290 self
291 }
292 pub fn or_parentized<T>(mut self, query: T) -> Self
294 where
295 T: Into<Query<M>>,
296 {
297 self.tokens.push(QueryToken::LeftBracket(Concatenation::Or));
298 self.tokens.append(&mut query.into().tokens);
299 self.tokens.push(QueryToken::RightBracket);
300 self
301 }
302
303 pub fn with(self, query_with: impl QueryWith<M>) -> Self {
305 query_with.with(self)
306 }
307
308 pub fn aux_param<S, A>(mut self, name: S, value: A) -> Self
310 where
311 A: Into<SqlArg>,
312 S: Into<String>,
313 {
314 self.aux_params.insert(name.into(), value.into());
315 self
316 }
317
318 pub fn contains_path(&self, path: &str) -> bool {
322 let p = format!("{}_", path.trim_end_matches('_')); self.tokens.iter().any(|t| {
324 let pth = p.as_str();
325 match t {
326 QueryToken::Field(field) => field.name.starts_with(pth),
327 QueryToken::Wildcard(wildcard) => {
328 path.starts_with(&wildcard.path) || wildcard.path.starts_with(pth)
329 }
330 _ => false,
331 }
332 })
333 }
334 pub fn contains_path_starts_with(&self, path: &str) -> bool {
338 let p = format!("{}_", path.trim_end_matches('_')); self.tokens.iter().any(|t| {
340 let pth = p.as_str();
341 match t {
342 QueryToken::Field(field) => field.name.starts_with(pth),
343 QueryToken::Wildcard(wildcard) => wildcard.path.starts_with(pth),
344 _ => false,
345 }
346 })
347 }
348}
349
350pub fn assert_roles(
353 provided_roles: &HashSet<String>,
354 required_roles: &HashSet<String>,
355) -> Result<(), String> {
356 for r in required_roles {
357 if !provided_roles.contains(r) {
358 return Err(r.to_owned());
359 }
360 }
361
362 Ok(())
363}
364
365impl<M> fmt::Display for Query<M> {
367 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368 fn get_concatenation(c: &Concatenation) -> char {
369 match c {
370 Concatenation::And => ',',
371 Concatenation::Or => ';',
372 }
373 }
374
375 let mut s = String::new();
376 let mut concatenation_needed = false;
377
378 for token in &self.tokens {
379 if concatenation_needed {
380 match &token {
381 QueryToken::LeftBracket(concatenation) => {
382 s.push(get_concatenation(concatenation))
383 }
384 QueryToken::Wildcard(wildcard) => {
385 s.push(get_concatenation(&wildcard.concatenation))
386 }
387 QueryToken::Field(field) => s.push(get_concatenation(&field.concatenation)),
388 QueryToken::Predicate(field) => s.push(get_concatenation(&field.concatenation)),
389 QueryToken::Selection(selection) => {
390 s.push(get_concatenation(&selection.concatenation))
391 }
392 _ => {}
393 }
394 }
395 s.push_str(&token.to_string());
396 match token {
397 QueryToken::LeftBracket(..) => concatenation_needed = false,
398 QueryToken::Field(..) => concatenation_needed = true,
399 QueryToken::Wildcard(..) => concatenation_needed = true,
400 QueryToken::Predicate(..) => concatenation_needed = true,
401 QueryToken::Selection(..) => concatenation_needed = true,
402 _ => {}
403 }
404 }
405
406 let t = s.chars().next().unwrap_or(' ');
408 if t == ',' || t == ';' {
409 s = s.trim_start_matches(',').trim_start_matches(';').to_owned();
410 }
411
412 write!(f, "{}", s)
413 }
414}
415
416impl<M> From<Field> for Query<M> {
417 fn from(field: Field) -> Query<M> {
418 let mut q = Query::new();
419 q.tokens.push(QueryToken::Field(field));
420 q
421 }
422}
423
424impl<M> From<Predicate> for Query<M> {
425 fn from(predicate: Predicate) -> Query<M> {
426 let mut q = Query::new();
427 q.tokens.push(QueryToken::Predicate(predicate));
428 q
429 }
430}
431impl<M> From<Selection> for Query<M> {
432 fn from(selection: Selection) -> Query<M> {
433 let mut q = Query::new();
434 q.tokens.push(QueryToken::Selection(selection));
435 q
436 }
437}
438
439impl<M> From<Wildcard> for Query<M> {
440 fn from(wildcard: Wildcard) -> Query<M> {
441 let mut q = Query::new();
442 q.tokens.push(QueryToken::Wildcard(wildcard));
443 q
444 }
445}
446
447impl<M> From<&str> for Query<M> {
448 fn from(string: &str) -> Query<M> {
449 let mut q = Query::new();
450 q.tokens.push(if string.ends_with('*') {
451 QueryToken::Wildcard(Wildcard::from(string))
452 } else {
453 QueryToken::Field(Field::from(string))
454 });
455 q
456 }
457}
458
459impl<M> Default for Query<M> {
460 fn default() -> Self {
461 Self::new()
462 }
463}
464
465#[cfg(test)]
466mod test {
467 use super::{query_with::QueryWith, Field, Predicate, Query, Selection, Wildcard};
468 use crate::sql_arg::SqlArg;
469
470 struct User;
471 struct Level2;
472
473 struct Item;
474
475 impl<T> QueryWith<T> for Item {
476 fn with(&self, query: Query<T>) -> Query<T> {
477 query
478 .and(Field::from("item").eq(1))
479 .aux_param("thing", "item")
480 }
481 }
482
483 #[test]
484 fn build() {
485 assert_eq!(Query::<User>::default().to_string(), "");
486 assert_eq!(Query::<User>::new().to_string(), "");
487
488 let qf = Query::<User>::from(Field::from("prop"));
489 let qp = Query::<User>::from(Predicate::from("pred"));
490 let qs = Query::<User>::from(Selection::from("std"));
491 let qw = Query::<User>::from(Wildcard::from("level1"));
492
493 assert_eq!(qf.to_string(), "prop");
494 assert_eq!(qp.to_string(), "@pred");
495 assert_eq!(qs.to_string(), "$std");
496 assert_eq!(qw.to_string(), "level1_*");
497
498 assert_eq!(
499 qf.clone_for_type::<User>()
500 .and(qp.clone_for_type())
501 .to_string(),
502 "prop,@pred"
503 );
504 assert_eq!(
505 qf.clone_for_type::<User>()
506 .and_parentized(qp.clone_for_type())
507 .to_string(),
508 "prop,(@pred)"
509 );
510 assert_eq!(
511 qf.clone_for_type::<User>()
512 .or(qp.clone_for_type())
513 .to_string(),
514 "prop;@pred"
515 );
516 assert_eq!(
517 qf.clone_for_type::<User>()
518 .or_parentized(qp.clone_for_type())
519 .to_string(),
520 "prop;(@pred)"
521 );
522
523 let q: Query<User> = Query::from(Field::from("level2_prop2"))
524 .and(Query::from(Wildcard::from("level2")))
525 .and(Query::from(Field::from("level3_prop4")))
526 .and(Query::from(Field::from("level1_prop1")))
527 .and(Query::from(Field::from("prop")));
528 assert_eq!(qw.contains_path("level1"), true);
529 assert_eq!(qw.contains_path("level4"), false);
530 assert_eq!(q.traverse::<Level2>("level2").to_string(), "prop2,*");
531
532 let q = qf.with(Item);
533 assert_eq!(q.to_string(), "prop,item EQ 1");
534 assert_eq!(q.aux_params.get("thing"), Some(&SqlArg::from("item")));
535 }
536}