cqrs_rust_lib/read/query.rs
1use crate::read::Sorter;
2use crate::{MaybeSend, MaybeSync};
3use serde::Serialize;
4use serde_json::Value as JsonValue;
5use std::fmt::Debug;
6
7#[derive(Debug, Clone, Default)]
8pub struct Pagination {
9 pub skip: Option<i64>,
10 pub limit: Option<i64>,
11}
12
13/// Query abstraction for read-side storage.
14///
15/// All three methods have default implementations so minimal structs need zero
16/// boilerplate:
17///
18/// ```rust,ignore
19/// #[derive(Debug, Serialize, Deserialize)]
20/// struct GameQuery { category: Option<String>, available: Option<bool> }
21/// impl Query for GameQuery {}
22/// ```
23///
24/// The default `filter()` converts every non-`None` field to an equality
25/// constraint (`field == value`) ANDed together. Override only when you need
26/// non-equality operators (`Gte`, `Like`, …) or field-name remapping.
27pub trait Query: Debug + Serialize + MaybeSend + MaybeSync {
28 /// Returns a filter derived from the struct's serializable fields.
29 /// Override when you need operators other than `==` or custom field names.
30 fn filter(&self) -> Option<rest_sql::RestSql> {
31 derive_filter_from_serde(self)
32 }
33
34 /// Pagination hint for the storage layer. Defaults to `None` (let the
35 /// storage use its own defaults or rely on `CqrsHttpQuery` page params).
36 fn pagination(&self) -> Option<Pagination> {
37 None
38 }
39
40 /// Static default sort for this view type, applied when no explicit sort
41 /// is requested (neither HTTP `sort` param nor `sort()` override).
42 /// Use this instead of `sort()` when the sort is unconditional.
43 fn default_sort() -> Option<Vec<Sorter>>
44 where
45 Self: Sized,
46 {
47 None
48 }
49
50 /// Dynamic sort order. Falls back to `default_sort()`.
51 /// Override only when the sort depends on query field values.
52 fn sort(&self) -> Option<Vec<Sorter>>
53 where
54 Self: Sized,
55 {
56 Self::default_sort()
57 }
58}
59
60/// Converts every non-`null` scalar field of a serializable struct into an
61/// equality constraint and ANDs them together.
62///
63/// Useful in `Query::filter()` overrides that need to combine the auto-derived
64/// filter with custom logic.
65pub fn derive_filter_from_serde<T: Serialize + ?Sized>(val: &T) -> Option<rest_sql::RestSql> {
66 use rest_sql::{Ast, Constraint, Operator};
67
68 let json = serde_json::to_value(val).ok()?;
69 let JsonValue::Object(map) = json else {
70 return None;
71 };
72
73 let constraints: Vec<Ast> = map
74 .into_iter()
75 .filter(|(_, v)| !v.is_null())
76 .filter_map(|(k, v)| {
77 json_to_rsql_value(v).map(|rv| {
78 Ast::Constraint(Constraint {
79 field: k,
80 operator: Operator::Eq,
81 value: rv,
82 })
83 })
84 })
85 .collect();
86
87 Ast::try_and(constraints).and_then(|ast| rest_sql::RestSql::from_ast(ast).ok())
88}
89
90fn json_to_rsql_value(v: JsonValue) -> Option<rest_sql::Value> {
91 use rest_sql::Value;
92 match v {
93 JsonValue::Bool(b) => Some(Value::Bool(b)),
94 JsonValue::Number(n) => {
95 if let Some(i) = n.as_i64() {
96 Some(Value::Int(i))
97 } else {
98 n.as_f64().map(Value::Float)
99 }
100 }
101 JsonValue::String(s) => Some(Value::String(s)),
102 JsonValue::Array(arr) => {
103 let items: Option<Vec<_>> = arr.into_iter().map(json_to_rsql_value).collect();
104 items.map(Value::List)
105 }
106 JsonValue::Null | JsonValue::Object(_) => None,
107 }
108}