lucene_query_syntax/ast.rs
1/// An expression term.
2#[derive(Debug, Clone, Eq, PartialEq)]
3pub enum Term {
4 /// A term searching the default field set, see [`Value`] for more information on valid syntax.
5 ///
6 /// ## Examples
7 /// - Phrase `"boil 'em, mash 'em"`
8 /// - Single Term `potatoes`
9 /// - Ranges `[3 TO 5]`
10 Default { value: Value },
11 /// A term searching a specific field. The searched value can be anything supported by [`Value`].
12 Field {
13 /// The field to be searched by this term.
14 field: String,
15 value: Value,
16 },
17 /// Searches for the opposite of the contained term.
18 Negate { term: Box<Term> },
19 /// Applies a boolean operation to the two contained terms.
20 Combine {
21 left: Box<Term>,
22 right: Box<Term>,
23 operator: Operator,
24 },
25}
26
27impl Term {
28 /// Constructs a [`Self::Default`] term.
29 ///
30 /// Purely for the syntactic convenience.
31 pub const fn new_default(value: Value) -> Self {
32 Self::Default { value }
33 }
34
35 /// Constructs a [`Self::Negate`] term.
36 ///
37 /// Purely for the syntactic convenience.
38 pub const fn new_negate(term: Box<Term>) -> Self {
39 Self::Negate { term }
40 }
41
42 /// Calls the provided visitor with this node, and a function to recurse into another nodes.
43 ///
44 /// Allows for iteration or reduction of the term tree.
45 ///
46 /// ## Example
47 /// ```rust
48 /// use lucene_query_syntax::Term;
49 ///
50 /// let root: Term = todo!("-name:Bob age:[20 TO 40]");
51 /// let complexity = root.visit(|term, recurse| match term {
52 /// Term::Combine { left, right, .. } => 1 + recurse(left) + recurse(right),
53 /// Term::Negate { term } => 1 + recurse(term),
54 /// _ => 1,
55 /// });
56 ///
57 /// assert_eq!(complexity, 3);
58 /// ```
59 pub fn visit<T>(self, visitor: impl Fn(Term, &dyn Fn(Box<Term>) -> T) -> T) -> T {
60 #[inline(always)]
61 fn inner<T>(term: Term, visitor: &dyn Fn(Term, &dyn Fn(Box<Term>) -> T) -> T) -> T {
62 visitor(term, &|node| node.visit(visitor))
63 }
64
65 inner(self, &visitor)
66 }
67}
68
69/// Describes how to combine the results of two terms.
70#[derive(Debug, Clone, Eq, PartialEq)]
71pub enum Operator {
72 /// Only include results matching all the operands.
73 #[doc(alias = "intersection")]
74 And,
75
76 /// Include results that match any of the operands.
77 #[doc(alias = "union")]
78 Or,
79}
80
81/// Value that is not composed of other values.
82///
83/// See [`Value`] for all possible values.
84#[derive(Debug, Clone, Eq, PartialEq)]
85pub enum Primitive {
86 Text(String),
87 Integer(i64),
88 Boolean(bool),
89}
90
91impl From<String> for Primitive {
92 fn from(value: String) -> Self {
93 Self::Text(value)
94 }
95}
96
97impl From<i64> for Primitive {
98 fn from(value: i64) -> Self {
99 Self::Integer(value)
100 }
101}
102
103impl From<bool> for Primitive {
104 fn from(value: bool) -> Self {
105 Self::Boolean(value)
106 }
107}
108
109/// Anything that can be used as a search term value.
110#[derive(Debug, Clone, Eq, PartialEq)]
111pub enum Value {
112 Primitive(Primitive),
113 Range(Boundary, Boundary),
114}
115
116impl<T: Into<Primitive>> From<T> for Value {
117 fn from(value: T) -> Self {
118 Value::Primitive(value.into())
119 }
120}
121
122/// How to interpret the value of a boundary.
123#[derive(Debug, Clone, Eq, PartialEq)]
124pub enum BoundaryKind {
125 /// Use inclusive comparisons such as `>=` and `<=`, the value listed in the term should match the result set.
126 Inclusive,
127 /// Use exclusive comparisons such as `>` and `<`, the value listed in the term should *not* match the result set.
128 Exclusive,
129}
130
131/// The limit of a range.
132#[derive(Debug, Clone, Eq, PartialEq)]
133pub struct Boundary {
134 pub value: i64,
135 pub kind: BoundaryKind,
136}