lucene-query-syntax 0.1.1

Parses a subset of the Apache Lucene query syntax
Documentation
/// An expression term.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Term {
    /// A term searching the default field set, see [`Value`] for more information on valid syntax.
    ///
    /// ## Examples
    /// - Phrase `"boil 'em, mash 'em"`
    /// - Single Term `potatoes`
    /// - Ranges `[3 TO 5]`
    Default { value: Value },
    /// A term searching a specific field. The searched value can be anything supported by [`Value`].
    Field {
        /// The field to be searched by this term.
        field: String,
        value: Value,
    },
    /// Searches for the opposite of the contained term.
    Negate { term: Box<Term> },
    /// Applies a boolean operation to the two contained terms.
    Combine {
        left: Box<Term>,
        right: Box<Term>,
        operator: Operator,
    },
}

impl Term {
    /// Constructs a [`Self::Default`] term.
    ///
    /// Purely for the syntactic convenience.
    pub const fn new_default(value: Value) -> Self {
        Self::Default { value }
    }

    /// Constructs a [`Self::Negate`] term.
    ///
    /// Purely for the syntactic convenience.
    pub const fn new_negate(term: Box<Term>) -> Self {
        Self::Negate { term }
    }

    /// Calls the provided visitor with this node, and a function to recurse into another nodes.
    ///
    /// Allows for iteration or reduction of the term tree.
    ///
    /// ## Example
    /// ```rust
    /// use lucene_query_syntax::Term;
    ///
    /// let root: Term = todo!("-name:Bob age:[20 TO 40]");
    /// let complexity = root.visit(|term, recurse| match term {
    ///     Term::Combine { left, right, .. } => 1 + recurse(left) + recurse(right),
    ///     Term::Negate { term } => 1 + recurse(term),
    ///     _ => 1,
    /// });
    ///
    /// assert_eq!(complexity, 3);
    /// ```
    pub  fn visit<T>(self, visitor: impl Fn(Term, &dyn Fn(Box<Term>) -> T) -> T) -> T {
        #[inline(always)]
        fn inner<T>(term: Term, visitor: &dyn Fn(Term, &dyn Fn(Box<Term>) -> T) -> T) -> T {
            visitor(term, &|node| node.visit(visitor))
        }

        inner(self, &visitor)
    }
}

/// Describes how to combine the results of two terms.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Operator {
    /// Only include results matching all the operands.
    #[doc(alias = "intersection")]
    And,

    /// Include results that match any of the operands.
    #[doc(alias = "union")]
    Or,
}

/// Value that is not composed of other values.
///
/// See [`Value`] for all possible values.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Primitive {
    Text(String),
    Integer(i64),
    Boolean(bool),
}

impl From<String> for Primitive {
    fn from(value: String) -> Self {
        Self::Text(value)
    }
}

impl From<i64> for Primitive {
    fn from(value: i64) -> Self {
        Self::Integer(value)
    }
}

impl From<bool> for Primitive {
    fn from(value: bool) -> Self {
        Self::Boolean(value)
    }
}

/// Anything that can be used as a search term value.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Value {
    Primitive(Primitive),
    Range(Boundary, Boundary),
}

impl<T: Into<Primitive>> From<T> for Value {
    fn from(value: T) -> Self {
        Value::Primitive(value.into())
    }
}

/// How to interpret the value of a boundary.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum BoundaryKind {
    /// Use inclusive comparisons such as `>=` and `<=`, the value listed in the term should match the result set.
    Inclusive,
    /// Use exclusive comparisons such as `>` and `<`, the value listed in the term should *not* match the result set.
    Exclusive,
}

/// The limit of a range.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Boundary {
    pub value: i64,
    pub kind: BoundaryKind,
}