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