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