Skip to main content

rsonpath_syntax/
builder.rs

1//! Utility for building a [`JsonPathQuery`](`crate::JsonPathQuery`)
2//! programmatically.
3//!
4//! The entrypoint is the [`JsonPathQueryBuilder`].
5//! Consult the structs documentation for details.
6
7use crate::{
8    num::JsonInt, str::JsonString, Comparable, ComparisonExpr, ComparisonOp, Index, JsonPathQuery, Literal,
9    LogicalExpr, Segment, Selector, Selectors, SingularJsonPathQuery, SingularSegment, Slice, TestExpr,
10};
11
12/// Builder for [`JsonPathQuery`] instances.
13///
14/// # Examples
15/// ```
16/// # use rsonpath_syntax::{JsonPathQuery, builder::JsonPathQueryBuilder, str::JsonString, Slice};
17/// let mut builder = JsonPathQueryBuilder::new();
18///     
19/// builder.child_name("a")
20///     .descendant_name("b")
21///     .child_wildcard()
22///     .child_name("c")
23///     .descendant_wildcard()
24///     .child_slice(Slice::build().with_start(3).with_end(-7).with_step(2));
25///
26/// // Can also use `builder.build()` as a non-consuming version.
27/// let query: JsonPathQuery = builder.into();
28///
29/// assert_eq!(query.to_string(), "$['a']..['b'][*]['c']..[*][3:-7:2]");
30/// ```
31pub struct JsonPathQueryBuilder {
32    segments: Vec<Segment>,
33}
34
35/// Builder for a [`Selectors`] collection used by the [`JsonPathQueryBuilder`]
36/// to create multiple [`Selector`] instances within a [`Segment`].
37pub struct JsonPathSelectorsBuilder {
38    selectors: Vec<Selector>,
39}
40
41impl JsonPathQueryBuilder {
42    /// Initialize an empty builder.
43    ///
44    /// # Examples
45    /// ```
46    /// # use rsonpath_syntax::{JsonPathQuery, builder::JsonPathQueryBuilder};
47    /// let builder = JsonPathQueryBuilder::new();
48    /// let query: JsonPathQuery = builder.into();
49    ///
50    /// assert!(query.segments().is_empty());
51    /// ```
52    #[must_use]
53    #[inline(always)]
54    pub fn new() -> Self {
55        Self { segments: vec![] }
56    }
57
58    /// Add a child segment with selectors defined in the `selectors_builder` function.
59    ///
60    /// See the documentation of [`JsonPathSelectorsBuilder`] for selector building details.
61    ///
62    /// ## Examples
63    ///
64    /// ```rust
65    /// # use rsonpath_syntax::{Selector, Index, str::JsonString, num::JsonUInt, builder::JsonPathQueryBuilder};
66    /// let mut builder = JsonPathQueryBuilder::new();
67    /// builder.child(|x| x.name("abc").index(10).wildcard());
68    /// let result = builder.into_query();
69    /// assert_eq!(result.segments().len(), 1);
70    /// let segment = &result.segments()[0];
71    /// assert!(segment.is_child());
72    /// assert_eq!(&segment.selectors().as_slice(), &[
73    ///     Selector::Name(JsonString::new("abc")),
74    ///     Selector::Index(Index::FromStart(JsonUInt::from(10))),
75    ///     Selector::Wildcard,
76    /// ]);
77    /// ```
78    #[inline]
79    pub fn child<F>(&mut self, selectors_builder: F) -> &mut Self
80    where
81        F: FnOnce(&mut JsonPathSelectorsBuilder) -> &mut JsonPathSelectorsBuilder,
82    {
83        let mut builder = JsonPathSelectorsBuilder::new();
84        selectors_builder(&mut builder);
85        self.segments.push(Segment::Child(builder.build()));
86        self
87    }
88
89    /// Add a descendant segment with selectors defined in the `selectors_builder` function.
90    ///
91    /// See the documentation of [`JsonPathSelectorsBuilder`] for selector building details.
92    ///
93    /// ## Examples
94    ///
95    /// ```rust
96    /// # use rsonpath_syntax::{Selector, Index, str::JsonString, num::JsonUInt, builder::JsonPathQueryBuilder};
97    /// let mut builder = JsonPathQueryBuilder::new();
98    /// builder.descendant(|x| x.name("abc").index(10).wildcard());
99    /// let result = builder.into_query();
100    /// assert_eq!(result.segments().len(), 1);
101    /// let segment = &result.segments()[0];
102    /// assert!(segment.is_descendant());
103    /// assert_eq!(&segment.selectors().as_slice(), &[
104    ///     Selector::Name(JsonString::new("abc")),
105    ///     Selector::Index(Index::FromStart(JsonUInt::from(10))),
106    ///     Selector::Wildcard,
107    /// ]);
108    /// ```
109    #[inline]
110    pub fn descendant<F>(&mut self, selectors_builder: F) -> &mut Self
111    where
112        F: FnOnce(&mut JsonPathSelectorsBuilder) -> &mut JsonPathSelectorsBuilder,
113    {
114        let mut builder = JsonPathSelectorsBuilder::new();
115        selectors_builder(&mut builder);
116        self.segments.push(Segment::Descendant(builder.build()));
117        self
118    }
119
120    /// Add a child segment with a single name selector.
121    ///
122    /// This is a shorthand for `.child(|x| x.name(name))`.
123    #[inline(always)]
124    pub fn child_name<S: Into<JsonString>>(&mut self, name: S) -> &mut Self {
125        self.child(|x| x.name(name))
126    }
127
128    /// Add a child segment with a single wildcard selector.
129    ///
130    /// This is a shorthand for `.child(|x| x.wildcard())`.
131    #[inline(always)]
132    pub fn child_wildcard(&mut self) -> &mut Self {
133        self.child(|x| x.wildcard())
134    }
135
136    /// Add a child segment with a single index selector.
137    ///
138    /// This is a shorthand for `.child(|x| x.index(idx))`.
139    #[inline(always)]
140    pub fn child_index<N: Into<JsonInt>>(&mut self, idx: N) -> &mut Self {
141        self.child(|x| x.index(idx))
142    }
143
144    /// Add a child segment with a single slice selector.
145    ///
146    /// This is a shorthand for `.child(|x| x.slice(slice))`.
147    #[inline(always)]
148    pub fn child_slice(&mut self, slice: impl Into<Slice>) -> &mut Self {
149        self.child(|x| x.slice(slice))
150    }
151
152    /// Add a child segment with a single filter selector.
153    ///
154    /// This is a shorthand for `.child(|x| x.filter(filter_builder))`.
155    #[inline(always)]
156    pub fn child_filter<F>(&mut self, filter_builder: F) -> &mut Self
157    where
158        F: FnOnce(EmptyLogicalExprBuilder) -> LogicalExprBuilder,
159    {
160        self.child(|x| x.filter(filter_builder))
161    }
162
163    /// Add a descendant segment with a single name selector.
164    ///
165    /// This is a shorthand for `.descendant(|x| x.name(name))`.
166    #[inline(always)]
167    pub fn descendant_name<S: Into<JsonString>>(&mut self, name: S) -> &mut Self {
168        self.descendant(|x| x.name(name))
169    }
170
171    /// Add a descendant segment with a single name selector.
172    ///
173    /// This is a shorthand for `.descendant(|x| x.wildcard())`.
174    #[inline(always)]
175    pub fn descendant_wildcard(&mut self) -> &mut Self {
176        self.descendant(|x| x.wildcard())
177    }
178
179    /// Add a descendant segment with a single name selector.
180    ///
181    /// This is a shorthand for `.descendant(|x| x.index(idx))`.
182    #[inline(always)]
183    pub fn descendant_index<N: Into<JsonInt>>(&mut self, idx: N) -> &mut Self {
184        self.descendant(|x| x.index(idx))
185    }
186
187    /// Add a descendant segment with a single slice selector.
188    ///
189    /// This is a shorthand for `.descendant(|x| x.slice(slice))`.
190    #[inline(always)]
191    pub fn descendant_slice(&mut self, slice: impl Into<Slice>) -> &mut Self {
192        self.descendant(|x| x.slice(slice))
193    }
194
195    /// Add a descendant segment with a single filter selector.
196    ///
197    /// This is a shorthand for `.descendant(|x| x.filter(filter_builder))`.
198    #[inline(always)]
199    pub fn descendant_filter<F>(&mut self, filter_builder: F) -> &mut Self
200    where
201        F: FnOnce(EmptyLogicalExprBuilder) -> LogicalExprBuilder,
202    {
203        self.descendant(|x| x.filter(filter_builder))
204    }
205
206    /// Produce a [`JsonPathQuery`] from the builder.
207    ///
208    /// This clones all data in the builder to create the query.
209    /// If possible, use [`into_query`](JsonPathQueryBuilder::into_query)
210    /// to consume the builder and avoid a copy.
211    #[must_use]
212    #[inline]
213    pub fn to_query(&mut self) -> JsonPathQuery {
214        JsonPathQuery {
215            segments: self.segments.clone(),
216        }
217    }
218
219    /// Produce a [`JsonPathQuery`] by consuming this builder.
220    ///
221    /// To avoid consuming the builder use [`to_query`](JsonPathQueryBuilder::to_query).
222    #[must_use]
223    #[inline]
224    pub fn into_query(self) -> JsonPathQuery {
225        JsonPathQuery {
226            segments: self.segments,
227        }
228    }
229}
230
231impl JsonPathSelectorsBuilder {
232    fn new() -> Self {
233        Self { selectors: vec![] }
234    }
235
236    fn build(self) -> Selectors {
237        assert!(!self.selectors.is_empty(), "there must be at least one selector");
238        Selectors::many(self.selectors)
239    }
240
241    /// Add a name selector with a given `name` string to the collection.
242    ///
243    /// ## Examples
244    ///
245    /// ```rust
246    /// # use rsonpath_syntax::prelude::*;
247    /// let mut builder = JsonPathQueryBuilder::new();
248    /// builder.child(|x| x.name("book").name("journal"));
249    /// let result = builder.into_query();
250    /// assert_eq!(result.segments().len(), 1);
251    /// let segment = &result.segments()[0];
252    /// assert_eq!(segment.selectors().as_slice(), &[
253    ///     Selector::Name(JsonString::new("book")),
254    ///     Selector::Name(JsonString::new("journal")),
255    /// ]);
256    /// ```
257    #[inline(always)]
258    pub fn name<S: Into<JsonString>>(&mut self, name: S) -> &mut Self {
259        self.selectors.push(Selector::Name(name.into()));
260        self
261    }
262
263    /// Add an index selector based on a given JSON integer.
264    ///
265    /// The result is a [`Selector::Index`] with an [`Index::FromStart`]
266    /// if `idx` converts to a non-negative [`JsonInt`], and [`Index::FromEnd`]
267    /// otherwise.
268    ///
269    /// ## Examples
270    ///
271    /// ```rust
272    /// # use rsonpath_syntax::{prelude::*, num::JsonNonZeroUInt};
273    /// let mut builder = JsonPathQueryBuilder::new();
274    /// builder.child(|x| x.index(10).index(-20));
275    /// let result = builder.into_query();
276    /// assert_eq!(result.segments().len(), 1);
277    /// let segment = &result.segments()[0];
278    /// assert_eq!(segment.selectors().as_slice(), &[
279    ///     Selector::Index(Index::FromStart(JsonUInt::from(10))),
280    ///     Selector::Index(Index::FromEnd(JsonNonZeroUInt::try_from(20).unwrap())),
281    /// ]);
282    /// ```
283    #[inline(always)]
284    pub fn index<N: Into<JsonInt>>(&mut self, idx: N) -> &mut Self {
285        let json_int: JsonInt = idx.into();
286        self.selectors.push(Selector::Index(Index::from(json_int)));
287        self
288    }
289
290    /// Add a slice selector based on a given start, end, and step integers.
291    ///
292    /// The result is a [`Selector::Slice`] with given `start`, `end`, and `step`.
293    ///
294    /// ## Examples
295    ///
296    /// ```rust
297    /// # use rsonpath_syntax::{prelude::*, num::JsonNonZeroUInt};
298    /// let mut builder = JsonPathQueryBuilder::new();
299    /// builder.child(|x| x
300    ///     .slice(Slice::build().with_start(10).with_end(-20).with_step(5))
301    ///     .slice(Slice::build().with_start(-20).with_step(-30)));
302    /// let result = builder.into_query();
303    ///
304    /// assert_eq!(result.segments().len(), 1);
305    /// let segment = &result.segments()[0];
306    /// let selectors = segment.selectors().as_slice();
307    /// match (&selectors[0], &selectors[1]) {
308    ///     (Selector::Slice(s1), Selector::Slice(s2)) => {
309    ///         assert_eq!(s1.start(), Index::FromStart(10.into()));
310    ///         assert_eq!(s1.end(), Some(Index::FromEnd(JsonNonZeroUInt::try_from(20).unwrap())));
311    ///         assert_eq!(s1.step(), Step::Forward(5.into()));
312    ///         assert_eq!(s2.start(), Index::FromEnd(JsonNonZeroUInt::try_from(20).unwrap()));
313    ///         assert_eq!(s2.end(), None);
314    ///         assert_eq!(s2.step(), Step::Backward(JsonNonZeroUInt::try_from(30).unwrap()));
315    ///     }
316    ///     _ => unreachable!()
317    /// }
318    /// ```
319    #[inline(always)]
320    pub fn slice(&mut self, slice: impl Into<Slice>) -> &mut Self {
321        self.selectors.push(Selector::Slice(slice.into()));
322        self
323    }
324
325    /// Add a wildcard selector.
326    ///
327    /// ```rust
328    /// # use rsonpath_syntax::prelude::*;
329    /// let mut builder = JsonPathQueryBuilder::new();
330    /// builder.child(|x| {
331    ///     x.filter(|x| {
332    ///         x.comparison(|x| {
333    ///             x.query_relative(|x| x.name("price"))
334    ///              .less_than()
335    ///              .literal(JsonInt::from(10))
336    ///         })
337    ///     })
338    /// });
339    /// let result = builder.into_query();
340    /// assert_eq!(result.segments().len(), 1);
341    /// let segment = &result.segments()[0];
342    /// assert_eq!(segment.selectors().len(), 1);
343    /// let selector = segment.selectors().first();
344    ///
345    /// let Selector::Filter(LogicalExpr::Comparison(expr)) = selector else {
346    ///     panic!("expected comparison filter")
347    /// };
348    /// let Comparable::RelativeSingularQuery(lhs) = expr.lhs() else {
349    ///     panic!("expected lhs to be a relative singular query")
350    /// };
351    /// let lhs_segments: Vec<_> = lhs.segments().collect();
352    /// assert_eq!(&lhs_segments, &[
353    ///     &SingularSegment::Name(JsonString::new("price"))
354    /// ]);
355    /// assert_eq!(expr.op(), ComparisonOp::LessThan);
356    /// assert_eq!(expr.rhs(), &Comparable::Literal(JsonInt::from(10).into()));
357    ///
358    /// ```
359    #[inline(always)]
360    pub fn wildcard(&mut self) -> &mut Self {
361        self.selectors.push(Selector::Wildcard);
362        self
363    }
364
365    /// Add a filter selector.
366    #[inline]
367    pub fn filter<F>(&mut self, filter_builder: F) -> &mut Self
368    where
369        F: FnOnce(EmptyLogicalExprBuilder) -> LogicalExprBuilder,
370    {
371        let filter = filter_builder(EmptyLogicalExprBuilder);
372        let logical_expr = filter.into();
373        self.selectors.push(Selector::Filter(logical_expr));
374        self
375    }
376}
377
378impl Default for JsonPathQueryBuilder {
379    /// Return the empty builder.
380    #[inline(always)]
381    fn default() -> Self {
382        Self::new()
383    }
384}
385
386impl From<JsonPathQueryBuilder> for JsonPathQuery {
387    #[inline(always)]
388    fn from(value: JsonPathQueryBuilder) -> Self {
389        Self {
390            segments: value.segments,
391        }
392    }
393}
394
395/// Helper API for programmatically constructing [`Slice`] instances.
396///
397/// # Examples
398/// ```
399/// # use rsonpath_syntax::prelude::*;
400/// let mut builder = SliceBuilder::new();
401///
402/// builder.with_start(-3).with_end(1).with_step(-7);
403///
404/// let slice: Slice = builder.into();
405/// assert_eq!(slice.to_string(), "-3:1:-7");
406/// ```
407pub struct SliceBuilder {
408    inner: Slice,
409    /// We need to track if start is explicit because the default depends on step sign.
410    start_was_explicitly_given: bool,
411}
412
413impl SliceBuilder {
414    /// Create a new [`Slice`] configuration with default values.
415    ///
416    /// ```rust
417    /// # use rsonpath_syntax::prelude::*;
418    /// let slice: Slice = SliceBuilder::new().into();
419    /// assert_eq!(Slice::default(), slice);
420    /// ```
421    #[inline]
422    #[must_use]
423    pub fn new() -> Self {
424        Self {
425            inner: Slice::default(),
426            start_was_explicitly_given: false,
427        }
428    }
429
430    /// Set the start of the [`Slice`].
431    #[inline]
432    pub fn with_start<N: Into<JsonInt>>(&mut self, start: N) -> &mut Self {
433        self.inner.start = start.into().into();
434        self.start_was_explicitly_given = true;
435        self
436    }
437
438    /// Set the end of the [`Slice`].
439    #[inline]
440    pub fn with_end<N: Into<JsonInt>>(&mut self, end: N) -> &mut Self {
441        self.inner.end = Some(end.into().into());
442        self
443    }
444
445    /// Set the step of the [`Slice`].
446    #[inline]
447    pub fn with_step<N: Into<JsonInt>>(&mut self, step: N) -> &mut Self {
448        self.inner.step = step.into().into();
449        self
450    }
451
452    /// Get the configured [`Slice`] instance.
453    ///
454    /// This does not consume the builder. For a consuming variant use the `Into<Slice>` impl.
455    #[inline]
456    #[must_use]
457    pub fn to_slice(&mut self) -> Slice {
458        if !self.start_was_explicitly_given {
459            if self.inner.step.is_forward() {
460                self.inner.start = Slice::DEFAULT_START_FORWARDS;
461            } else {
462                self.inner.start = Slice::default_start_backwards();
463            }
464        }
465
466        self.inner.clone()
467    }
468}
469
470impl From<SliceBuilder> for Slice {
471    #[inline]
472    fn from(mut value: SliceBuilder) -> Self {
473        value.to_slice()
474    }
475}
476
477impl<'a> From<&'a mut SliceBuilder> for Slice {
478    #[inline]
479    fn from(value: &'a mut SliceBuilder) -> Self {
480        value.to_slice()
481    }
482}
483
484impl Default for SliceBuilder {
485    /// Create a builder configured with default values.
486    ///
487    /// ```rust
488    /// # use rsonpath_syntax::prelude::*;
489    /// let slice: Slice = SliceBuilder::default().into();
490    /// assert_eq!(Slice::default(), slice);
491    /// ```
492    #[inline(always)]
493    fn default() -> Self {
494        Self::new()
495    }
496}
497
498/// Starting point for building a filter [`LogicalExpr`].
499pub struct EmptyLogicalExprBuilder;
500
501/// Starting point for building a [`ComparisonExpr`].
502pub struct EmptyComparisonExprBuilder;
503
504/// Builder for [`ComparisonExpr`] with the [`lhs`](ComparisonExpr::lhs) already configured.
505pub struct ComparisonWithLhsBuilder {
506    lhs: Comparable,
507}
508
509/// Builder for [`ComparisonExpr`] with the [`lhs`](ComparisonExpr::lhs)
510/// and [`op`](ComparisonExpr::op) already configured.
511pub struct ComparisonWithLhsAndOpBuilder {
512    lhs: Comparable,
513    op: ComparisonOp,
514}
515
516/// Builder for a [`LogicalExpr`] that can be finalized,
517/// or boolean-combined with another [`LogicalExpr`].
518///
519/// # Examples
520/// ```rust
521/// # use rsonpath_syntax::prelude::*;
522/// let mut builder = JsonPathQueryBuilder::new();
523/// builder.child_filter(|fb|
524///     fb.test_relative(|qb| qb.child_name("book"))
525///     // We could finish here, but we can also chain another expression.
526///       .or(|fb2| fb2.test_relative(|qb2| qb2.child_name("journal")))
527/// );
528///
529/// assert_eq!(builder.to_query().to_string(), "$[?@['book'] || @['journal']]");
530/// ```
531pub struct LogicalExprBuilder {
532    current: LogicalExpr,
533}
534
535/// Builder for a [`SingularJsonPathQuery`].
536pub struct SingularJsonPathQueryBuilder {
537    segments: Vec<SingularSegment>,
538}
539
540impl SingularJsonPathQueryBuilder {
541    /// Create a new, empty builder.
542    #[inline]
543    #[must_use]
544    pub fn new() -> Self {
545        Self { segments: vec![] }
546    }
547
548    /// Add a child name segment.
549    #[inline]
550    pub fn name<S: Into<JsonString>>(&mut self, name: S) -> &mut Self {
551        self.segments.push(SingularSegment::Name(name.into()));
552        self
553    }
554
555    /// Add a child index segment.
556    #[inline]
557    pub fn index<N: Into<Index>>(&mut self, n: N) -> &mut Self {
558        self.segments.push(SingularSegment::Index(n.into()));
559        self
560    }
561}
562
563impl Default for SingularJsonPathQueryBuilder {
564    #[inline]
565    fn default() -> Self {
566        Self::new()
567    }
568}
569
570impl From<SingularJsonPathQueryBuilder> for SingularJsonPathQuery {
571    #[inline]
572    fn from(value: SingularJsonPathQueryBuilder) -> Self {
573        Self {
574            segments: value.segments,
575        }
576    }
577}
578
579impl EmptyLogicalExprBuilder {
580    /// Start building a [`ComparisonExpr`] logical expression.
581    #[inline]
582    #[must_use]
583    pub fn comparison<F>(self, cf: F) -> LogicalExprBuilder
584    where
585        F: FnOnce(EmptyComparisonExprBuilder) -> ComparisonExpr,
586    {
587        let comparison = cf(EmptyComparisonExprBuilder);
588        LogicalExprBuilder {
589            current: LogicalExpr::Comparison(comparison),
590        }
591    }
592
593    /// Start building a test query from the root.
594    #[inline]
595    pub fn test_absolute<F>(self, tf: F) -> LogicalExprBuilder
596    where
597        F: FnOnce(&mut JsonPathQueryBuilder) -> &mut JsonPathQueryBuilder,
598    {
599        let mut query = JsonPathQueryBuilder::new();
600        tf(&mut query);
601        LogicalExprBuilder {
602            current: LogicalExpr::Test(TestExpr::Absolute(query.into_query())),
603        }
604    }
605
606    /// Start building a test query from the current node.
607    #[inline]
608    pub fn test_relative<F>(self, tf: F) -> LogicalExprBuilder
609    where
610        F: FnOnce(&mut JsonPathQueryBuilder) -> &mut JsonPathQueryBuilder,
611    {
612        let mut query = JsonPathQueryBuilder::new();
613        tf(&mut query);
614        LogicalExprBuilder {
615            current: LogicalExpr::Test(TestExpr::Relative(query.into_query())),
616        }
617    }
618
619    /// Build a logical expression by negating the one created in the inner function.
620    #[inline]
621    pub fn not<F>(self, tf: F) -> LogicalExprBuilder
622    where
623        F: FnOnce(Self) -> LogicalExprBuilder,
624    {
625        let inner = tf(Self).into();
626        LogicalExprBuilder {
627            current: LogicalExpr::Not(Box::new(inner)),
628        }
629    }
630}
631
632impl EmptyComparisonExprBuilder {
633    /// Set the left-hand side of the comparison to a literal.
634    #[inline]
635    #[must_use]
636    pub fn literal<L: Into<Literal>>(self, l: L) -> ComparisonWithLhsBuilder {
637        ComparisonWithLhsBuilder {
638            lhs: Comparable::Literal(l.into()),
639        }
640    }
641
642    /// Set the left-hand side of the comparison to a root-based singular query.
643    #[inline]
644    #[must_use]
645    pub fn query_absolute<F>(self, qf: F) -> ComparisonWithLhsBuilder
646    where
647        F: FnOnce(&mut SingularJsonPathQueryBuilder) -> &mut SingularJsonPathQueryBuilder,
648    {
649        let mut query = SingularJsonPathQueryBuilder::new();
650        qf(&mut query);
651        ComparisonWithLhsBuilder {
652            lhs: Comparable::AbsoluteSingularQuery(query.into()),
653        }
654    }
655
656    /// Set the left-hand side of the comparison to a current-node-based singular query.
657    #[inline]
658    #[must_use]
659    pub fn query_relative<F>(self, qf: F) -> ComparisonWithLhsBuilder
660    where
661        F: FnOnce(&mut SingularJsonPathQueryBuilder) -> &mut SingularJsonPathQueryBuilder,
662    {
663        let mut query = SingularJsonPathQueryBuilder::new();
664        qf(&mut query);
665        ComparisonWithLhsBuilder {
666            lhs: Comparable::RelativeSingularQuery(query.into()),
667        }
668    }
669}
670
671impl ComparisonWithLhsBuilder {
672    /// Use the equality operator `==`.
673    #[inline]
674    #[must_use]
675    pub fn equal_to(self) -> ComparisonWithLhsAndOpBuilder {
676        ComparisonWithLhsAndOpBuilder {
677            lhs: self.lhs,
678            op: ComparisonOp::EqualTo,
679        }
680    }
681
682    /// Use the inequality operator `!=`.
683    #[inline]
684    #[must_use]
685    pub fn not_equal_to(self) -> ComparisonWithLhsAndOpBuilder {
686        ComparisonWithLhsAndOpBuilder {
687            lhs: self.lhs,
688            op: ComparisonOp::NotEqualTo,
689        }
690    }
691
692    /// Use the less-than operator `<`.
693    #[inline]
694    #[must_use]
695    pub fn less_than(self) -> ComparisonWithLhsAndOpBuilder {
696        ComparisonWithLhsAndOpBuilder {
697            lhs: self.lhs,
698            op: ComparisonOp::LessThan,
699        }
700    }
701
702    /// Use the less-than-or-equal operator `<=`.
703    #[inline]
704    #[must_use]
705    pub fn lesser_or_equal_to(self) -> ComparisonWithLhsAndOpBuilder {
706        ComparisonWithLhsAndOpBuilder {
707            lhs: self.lhs,
708            op: ComparisonOp::LesserOrEqualTo,
709        }
710    }
711
712    /// Use the greater-than operator `>`.
713    #[inline]
714    #[must_use]
715    pub fn greater_than(self) -> ComparisonWithLhsAndOpBuilder {
716        ComparisonWithLhsAndOpBuilder {
717            lhs: self.lhs,
718            op: ComparisonOp::GreaterThan,
719        }
720    }
721
722    /// Use the greater-than-or-equal operator `>=`.
723    #[inline]
724    #[must_use]
725    pub fn greater_or_equal_to(self) -> ComparisonWithLhsAndOpBuilder {
726        ComparisonWithLhsAndOpBuilder {
727            lhs: self.lhs,
728            op: ComparisonOp::GreaterOrEqualTo,
729        }
730    }
731}
732
733impl ComparisonWithLhsAndOpBuilder {
734    /// Set the right-hand side of the comparison to a literal and finalize the expression.
735    #[inline]
736    pub fn literal<L: Into<Literal>>(self, l: L) -> ComparisonExpr {
737        ComparisonExpr {
738            lhs: self.lhs,
739            op: self.op,
740            rhs: Comparable::Literal(l.into()),
741        }
742    }
743
744    /// Set the right-hand side of the comparison to a root-based singular query and finalize the expression.
745    #[inline]
746    #[must_use]
747    pub fn query_absolute<F>(self, qf: F) -> ComparisonExpr
748    where
749        F: FnOnce(&mut SingularJsonPathQueryBuilder) -> &mut SingularJsonPathQueryBuilder,
750    {
751        let mut query = SingularJsonPathQueryBuilder::new();
752        qf(&mut query);
753        ComparisonExpr {
754            lhs: self.lhs,
755            op: self.op,
756            rhs: Comparable::AbsoluteSingularQuery(query.into()),
757        }
758    }
759
760    /// Set the right-hand side of the comparison to a current-node-based singular query and finalize the expression.
761    #[inline]
762    #[must_use]
763    pub fn query_relative<F>(self, qf: F) -> ComparisonExpr
764    where
765        F: FnOnce(&mut SingularJsonPathQueryBuilder) -> &mut SingularJsonPathQueryBuilder,
766    {
767        let mut query = SingularJsonPathQueryBuilder::new();
768        qf(&mut query);
769        ComparisonExpr {
770            lhs: self.lhs,
771            op: self.op,
772            rhs: Comparable::RelativeSingularQuery(query.into()),
773        }
774    }
775}
776
777impl LogicalExprBuilder {
778    /// Combine the entire expression built thus far with the one built in the inner function
779    /// by using the boolean conjunction operator `&&`.
780    #[inline]
781    pub fn and<F>(self, f: F) -> Self
782    where
783        F: FnOnce(EmptyLogicalExprBuilder) -> Self,
784    {
785        let lhs = Box::new(self.current);
786        let rhs = Box::new(f(EmptyLogicalExprBuilder).into());
787        Self {
788            current: LogicalExpr::And(lhs, rhs),
789        }
790    }
791
792    /// Combine the entire expression built thus far with the one built in the inner function
793    /// by using the boolean disjunction operator `||`.
794    #[inline]
795    pub fn or<F>(self, f: F) -> Self
796    where
797        F: FnOnce(EmptyLogicalExprBuilder) -> Self,
798    {
799        let lhs = Box::new(self.current);
800        let rhs = Box::new(f(EmptyLogicalExprBuilder).into());
801        Self {
802            current: LogicalExpr::Or(lhs, rhs),
803        }
804    }
805}
806
807impl From<LogicalExprBuilder> for LogicalExpr {
808    #[inline(always)]
809    fn from(value: LogicalExprBuilder) -> Self {
810        value.current
811    }
812}
813
814#[cfg(test)]
815mod tests {
816    use super::SliceBuilder;
817    use crate::{Index, Slice, Step};
818
819    #[test]
820    fn slice_builder_default_start_forward() {
821        let mut builder = SliceBuilder::new();
822        builder.with_end(3).with_step(4);
823        let slice: Slice = builder.into();
824
825        assert_eq!(slice.start(), Index::FromStart(0.into()));
826        assert_eq!(slice.end(), Some(Index::FromStart(3.into())));
827        assert_eq!(slice.step(), Step::Forward(4.into()));
828    }
829
830    #[test]
831    fn slice_builder_default_start_backward() {
832        let mut builder = SliceBuilder::new();
833        builder.with_end(3).with_step(-4);
834        let slice: Slice = builder.into();
835
836        assert_eq!(slice.start(), Index::FromEnd(1.try_into().unwrap()));
837        assert_eq!(slice.end(), Some(Index::FromStart(3.into())));
838        assert_eq!(slice.step(), Step::Backward(4.try_into().unwrap()));
839    }
840}