Skip to main content

flusso_query/handles/
scalar.rs

1//! Scalar value fields: the [`Bool`] exact-match field and the ordered
2//! [`Number`] / [`Date`] fields with their range operators.
3
4use std::marker::PhantomData;
5
6use serde_json::Value;
7
8use super::{Sort, SortOrder, exists_q, range_q, single};
9use crate::query::{Query, Root};
10
11// ---- Bool ------------------------------------------------------------------
12
13/// A boolean field.
14#[derive(Debug, Clone)]
15pub struct Bool<S = Root> {
16    path: String,
17    _scope: PhantomData<fn() -> S>,
18}
19
20impl<S> Bool<S> {
21    /// Build a handle for the field at `path`.
22    pub fn at(path: impl Into<String>) -> Self {
23        Self {
24            path: path.into(),
25            _scope: PhantomData,
26        }
27    }
28
29    /// Exact match.
30    pub fn eq(&self, value: bool) -> Query<S> {
31        single("term", &self.path, Value::Bool(value))
32    }
33
34    /// The field has a non-null value.
35    pub fn exists(&self) -> Query<S> {
36        exists_q(&self.path)
37    }
38}
39
40// ---- Number ----------------------------------------------------------------
41
42/// A numeric field. `T` is the Rust scalar; `S` is the scope (defaults to
43/// [`Root`], so `Number<i64>` is a root-scope handle).
44#[derive(Debug, Clone)]
45pub struct Number<T, S = Root> {
46    path: String,
47    _marker: PhantomData<fn() -> (T, S)>,
48}
49
50impl<T, S> Number<T, S>
51where
52    T: Into<Value> + Copy,
53{
54    /// Build a handle for the field at `path`.
55    pub fn at(path: impl Into<String>) -> Self {
56        Self {
57            path: path.into(),
58            _marker: PhantomData,
59        }
60    }
61
62    /// Exact match.
63    pub fn eq(&self, value: T) -> Query<S> {
64        single("term", &self.path, value.into())
65    }
66
67    /// Match any of the given values.
68    pub fn in_(&self, values: impl IntoIterator<Item = T>) -> Query<S> {
69        let array = values.into_iter().map(Into::into).collect();
70        single("terms", &self.path, Value::Array(array))
71    }
72
73    /// Strictly less than `value`.
74    pub fn lt(&self, value: T) -> Query<S> {
75        range_q(&self.path, vec![("lt", value.into())])
76    }
77
78    /// Less than or equal to `value`.
79    pub fn lte(&self, value: T) -> Query<S> {
80        range_q(&self.path, vec![("lte", value.into())])
81    }
82
83    /// Strictly greater than `value`.
84    pub fn gt(&self, value: T) -> Query<S> {
85        range_q(&self.path, vec![("gt", value.into())])
86    }
87
88    /// Greater than or equal to `value`.
89    pub fn gte(&self, value: T) -> Query<S> {
90        range_q(&self.path, vec![("gte", value.into())])
91    }
92
93    /// Inclusive range `[low, high]`.
94    pub fn between(&self, low: T, high: T) -> Query<S> {
95        range_q(&self.path, vec![("gte", low.into()), ("lte", high.into())])
96    }
97
98    /// The field has a non-null value.
99    pub fn exists(&self) -> Query<S> {
100        exists_q(&self.path)
101    }
102
103    /// Sort ascending on this field.
104    pub fn asc(&self) -> Sort {
105        Sort::new(&self.path, SortOrder::Asc)
106    }
107
108    /// Sort descending on this field.
109    pub fn desc(&self) -> Sort {
110        Sort::new(&self.path, SortOrder::Desc)
111    }
112}
113
114// ---- Date ------------------------------------------------------------------
115
116/// A `date`/`timestamp` field. Bounds are ISO-8601 strings.
117#[derive(Debug, Clone)]
118pub struct Date<S = Root> {
119    path: String,
120    _scope: PhantomData<fn() -> S>,
121}
122
123impl<S> Date<S> {
124    /// Build a handle for the field at `path`.
125    pub fn at(path: impl Into<String>) -> Self {
126        Self {
127            path: path.into(),
128            _scope: PhantomData,
129        }
130    }
131
132    /// Exact match.
133    pub fn eq(&self, value: impl Into<String>) -> Query<S> {
134        single("term", &self.path, Value::String(value.into()))
135    }
136
137    /// Strictly before `value`.
138    pub fn lt(&self, value: impl Into<String>) -> Query<S> {
139        range_q(&self.path, vec![("lt", Value::String(value.into()))])
140    }
141
142    /// At or before `value`.
143    pub fn lte(&self, value: impl Into<String>) -> Query<S> {
144        range_q(&self.path, vec![("lte", Value::String(value.into()))])
145    }
146
147    /// Strictly after `value`.
148    pub fn gt(&self, value: impl Into<String>) -> Query<S> {
149        range_q(&self.path, vec![("gt", Value::String(value.into()))])
150    }
151
152    /// At or after `value`.
153    pub fn gte(&self, value: impl Into<String>) -> Query<S> {
154        range_q(&self.path, vec![("gte", Value::String(value.into()))])
155    }
156
157    /// Inclusive range `[low, high]`.
158    pub fn between(&self, low: impl Into<String>, high: impl Into<String>) -> Query<S> {
159        range_q(
160            &self.path,
161            vec![
162                ("gte", Value::String(low.into())),
163                ("lte", Value::String(high.into())),
164            ],
165        )
166    }
167
168    /// The field has a non-null value.
169    pub fn exists(&self) -> Query<S> {
170        exists_q(&self.path)
171    }
172
173    /// Sort ascending on this field.
174    pub fn asc(&self) -> Sort {
175        Sort::new(&self.path, SortOrder::Asc)
176    }
177
178    /// Sort descending on this field.
179    pub fn desc(&self) -> Sort {
180        Sort::new(&self.path, SortOrder::Desc)
181    }
182}