1use std::marker::PhantomData;
11
12use serde_json::{Map, Value};
13
14#[derive(Debug, Clone, Copy)]
17pub struct Root;
18
19#[derive(Debug, Clone)]
26pub struct Query<S = Root> {
27 inner: Inner,
28 _scope: PhantomData<fn() -> S>,
29}
30
31#[derive(Debug, Clone)]
33enum Inner {
34 Leaf(Value),
35 Bool(BoolInner),
36}
37
38#[derive(Debug, Clone, Default)]
39pub(crate) struct BoolInner {
40 must: Vec<Inner>,
41 filter: Vec<Inner>,
42 should: Vec<Inner>,
43 must_not: Vec<Inner>,
44 minimum_should_match: Option<Value>,
45 boost: Option<f32>,
46}
47
48#[derive(Debug, Clone, Copy)]
49enum Clause {
50 Must,
51 Filter,
52 Should,
53 MustNot,
54}
55
56impl BoolInner {
57 pub(crate) fn is_empty(&self) -> bool {
58 self.must.is_empty()
59 && self.filter.is_empty()
60 && self.should.is_empty()
61 && self.must_not.is_empty()
62 }
63
64 fn push(&mut self, clause: Clause, inner: Inner) {
65 match clause {
66 Clause::Must => self.must.push(inner),
67 Clause::Filter => self.filter.push(inner),
68 Clause::Should => self.should.push(inner),
69 Clause::MustNot => self.must_not.push(inner),
70 }
71 }
72
73 fn is_pure(&self, clause: Clause) -> bool {
76 match clause {
77 Clause::Must => {
78 self.filter.is_empty() && self.should.is_empty() && self.must_not.is_empty()
79 }
80 Clause::Filter => {
81 self.must.is_empty() && self.should.is_empty() && self.must_not.is_empty()
82 }
83 Clause::Should => {
84 self.must.is_empty() && self.filter.is_empty() && self.must_not.is_empty()
85 }
86 Clause::MustNot => {
87 self.must.is_empty() && self.filter.is_empty() && self.should.is_empty()
88 }
89 }
90 }
91
92 pub(crate) fn to_value(&self) -> Value {
93 let mut body = Map::new();
94 insert_clause(&mut body, "must", &self.must);
95 insert_clause(&mut body, "filter", &self.filter);
96 insert_clause(&mut body, "should", &self.should);
97 insert_clause(&mut body, "must_not", &self.must_not);
98 if let Some(msm) = &self.minimum_should_match {
99 body.insert("minimum_should_match".to_string(), msm.clone());
100 }
101 if let Some(boost) = self.boost {
102 body.insert("boost".to_string(), Value::from(boost));
103 }
104 let mut outer = Map::new();
105 outer.insert("bool".to_string(), Value::Object(body));
106 Value::Object(outer)
107 }
108}
109
110fn insert_clause(target: &mut Map<String, Value>, key: &str, clauses: &[Inner]) {
111 if clauses.is_empty() {
112 return;
113 }
114 let array = clauses.iter().map(Inner::to_value).collect();
115 target.insert(key.to_string(), Value::Array(array));
116}
117
118impl Inner {
119 fn to_value(&self) -> Value {
120 match self {
121 Inner::Leaf(value) => value.clone(),
122 Inner::Bool(bool_inner) => bool_inner.to_value(),
123 }
124 }
125}
126
127fn combine(a: Inner, b: Inner, clause: Clause) -> Inner {
130 if let Inner::Bool(mut bool_inner) = a {
131 if bool_inner.is_pure(clause) {
132 bool_inner.push(clause, b);
133 return Inner::Bool(bool_inner);
134 }
135 let mut combined = BoolInner::default();
136 combined.push(clause, Inner::Bool(bool_inner));
137 combined.push(clause, b);
138 return Inner::Bool(combined);
139 }
140 let mut combined = BoolInner::default();
141 combined.push(clause, a);
142 combined.push(clause, b);
143 Inner::Bool(combined)
144}
145
146fn combine_opt<S>(a: Option<Query<S>>, b: Option<Query<S>>, clause: Clause) -> Query<S> {
150 match (a, b) {
151 (Some(a), Some(b)) => Query::wrap(combine(a.inner, b.inner, clause)),
152 (Some(only), None) | (None, Some(only)) => only,
153 (None, None) => Query::match_all(),
154 }
155}
156
157impl<S> Query<S> {
158 pub(crate) fn leaf(value: Value) -> Self {
160 Query {
161 inner: Inner::Leaf(value),
162 _scope: PhantomData,
163 }
164 }
165
166 pub(crate) fn match_all() -> Self {
168 Query::leaf(crate::handles::match_all_value())
169 }
170
171 fn wrap(inner: Inner) -> Self {
172 Query {
173 inner,
174 _scope: PhantomData,
175 }
176 }
177
178 #[must_use]
180 pub fn and(self, other: impl AsQuery<S>) -> Query<S> {
181 match other.into_query() {
182 Some(other) => Query::wrap(combine(self.inner, other.inner, Clause::Must)),
183 None => self,
184 }
185 }
186
187 #[must_use]
189 pub fn or(self, other: impl AsQuery<S>) -> Query<S> {
190 match other.into_query() {
191 Some(other) => Query::wrap(combine(self.inner, other.inner, Clause::Should)),
192 None => self,
193 }
194 }
195
196 #[must_use]
198 #[allow(clippy::should_implement_trait)]
199 pub fn not(self) -> Query<S> {
200 Query::wrap(Inner::Bool(BoolInner {
201 must_not: vec![self.inner],
202 ..BoolInner::default()
203 }))
204 }
205
206 #[must_use]
210 pub fn boost(mut self, boost: f32) -> Query<S> {
211 self.inner = match self.inner {
212 Inner::Bool(mut bool_inner) => {
213 bool_inner.boost = Some(boost);
214 Inner::Bool(bool_inner)
215 }
216 leaf => Inner::Bool(BoolInner {
217 must: vec![leaf],
218 boost: Some(boost),
219 ..BoolInner::default()
220 }),
221 };
222 self
223 }
224
225 #[must_use]
231 pub fn min_should_match(mut self, value: impl Into<Value>) -> Query<S> {
232 self.inner = match self.inner {
233 Inner::Bool(mut bool_inner) => {
234 bool_inner.minimum_should_match = Some(value.into());
235 Inner::Bool(bool_inner)
236 }
237 leaf => Inner::Bool(BoolInner {
238 should: vec![leaf],
239 minimum_should_match: Some(value.into()),
240 ..BoolInner::default()
241 }),
242 };
243 self
244 }
245
246 #[must_use]
248 pub fn to_value(&self) -> Value {
249 self.inner.to_value()
250 }
251
252 pub(crate) fn into_inner(self) -> InnerClause {
254 InnerClause(self.inner)
255 }
256}
257
258pub(crate) struct InnerClause(Inner);
260
261#[derive(Debug, Clone, Default)]
263pub(crate) struct BoolBuilder {
264 bool_inner: BoolInner,
265}
266
267impl BoolBuilder {
268 pub(crate) fn push_must(&mut self, clause: InnerClause) {
269 self.bool_inner.push(Clause::Must, clause.0);
270 }
271 pub(crate) fn push_filter(&mut self, clause: InnerClause) {
272 self.bool_inner.push(Clause::Filter, clause.0);
273 }
274 pub(crate) fn push_should(&mut self, clause: InnerClause) {
275 self.bool_inner.push(Clause::Should, clause.0);
276 }
277 pub(crate) fn push_must_not(&mut self, clause: InnerClause) {
278 self.bool_inner.push(Clause::MustNot, clause.0);
279 }
280 pub(crate) fn set_min_should_match(&mut self, value: Value) {
281 self.bool_inner.minimum_should_match = Some(value);
282 }
283 pub(crate) fn is_empty(&self) -> bool {
284 self.bool_inner.is_empty()
285 }
286 pub(crate) fn to_value(&self) -> Value {
287 self.bool_inner.to_value()
288 }
289}
290
291pub trait AsQuery<S> {
303 fn into_query(self) -> Option<Query<S>>;
305
306 #[must_use]
308 fn and(self, other: impl AsQuery<S>) -> Query<S>
309 where
310 Self: Sized,
311 {
312 combine_opt(self.into_query(), other.into_query(), Clause::Must)
313 }
314
315 #[must_use]
317 fn or(self, other: impl AsQuery<S>) -> Query<S>
318 where
319 Self: Sized,
320 {
321 combine_opt(self.into_query(), other.into_query(), Clause::Should)
322 }
323
324 #[must_use]
326 #[allow(clippy::should_implement_trait)]
327 fn not(self) -> Query<S>
328 where
329 Self: Sized,
330 {
331 self.into_query().map_or_else(Query::match_all, Query::not)
332 }
333
334 #[must_use]
337 fn to_value(&self) -> Value
338 where
339 Self: Sized + Clone,
340 {
341 self.clone()
342 .into_query()
343 .map_or_else(crate::handles::match_all_value, |q| q.to_value())
344 }
345}
346
347impl<S> AsQuery<S> for Query<S> {
348 fn into_query(self) -> Option<Query<S>> {
349 Some(self)
350 }
351}
352
353impl<S, T: AsQuery<S>> AsQuery<S> for Option<T> {
354 fn into_query(self) -> Option<Query<S>> {
355 self.and_then(AsQuery::into_query)
356 }
357}