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