Skip to main content

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}