Skip to main content

zrx_id/id/expression/
builder.rs

1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Expression builder.
27
28use super::error::Result;
29use super::operand::{Operand, Operator, TryIntoOperand};
30use super::Expression;
31
32// ----------------------------------------------------------------------------
33// Structs
34// ----------------------------------------------------------------------------
35
36/// Expression builder.
37#[derive(Debug)]
38pub struct Builder {
39    /// Expression operator.
40    operator: Operator,
41    /// Expression operands.
42    operands: Vec<Operand>,
43}
44
45// ----------------------------------------------------------------------------
46// Implementations
47// ----------------------------------------------------------------------------
48
49impl Expression {
50    /// Creates an expression for which any operand must match.
51    ///
52    /// # Errors
53    ///
54    /// Returns [`Error::Id`][] if any of the operands are invalid.
55    ///
56    /// [`Error::Id`]: crate::id::expression::Error::Id
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// # use std::error::Error;
62    /// # fn main() -> Result<(), Box<dyn Error>> {
63    /// use zrx_id::{selector, Expression};
64    ///
65    /// // Create expression
66    /// let expr = Expression::any(|expr| {
67    ///     expr.with(selector!(location = "**/*.jpg")?)?
68    ///         .with(selector!(location = "**/*.png")?)
69    /// })?;
70    /// # Ok(())
71    /// # }
72    /// ```
73    #[inline]
74    pub fn any<F>(f: F) -> Result<Self>
75    where
76        F: FnOnce(Builder) -> Result<Builder>,
77    {
78        f(Builder {
79            operator: Operator::Any,
80            operands: Vec::new(),
81        })
82        .map(Builder::build)
83    }
84
85    /// Creates an expression for which all operands must match.
86    ///
87    /// # Errors
88    ///
89    /// Returns [`Error::Id`][] if any of the operands are invalid.
90    ///
91    /// [`Error::Id`]: crate::id::expression::Error::Id
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// # use std::error::Error;
97    /// # fn main() -> Result<(), Box<dyn Error>> {
98    /// use zrx_id::{selector, Expression};
99    ///
100    /// // Create expression
101    /// let expr = Expression::all(|expr| {
102    ///     expr.with(selector!(location = "**/*.md")?)?
103    ///         .with(selector!(provider = "file")?)
104    /// })?;
105    /// # Ok(())
106    /// # }
107    /// ```
108    #[inline]
109    pub fn all<F>(f: F) -> Result<Self>
110    where
111        F: FnOnce(Builder) -> Result<Builder>,
112    {
113        f(Builder {
114            operator: Operator::All,
115            operands: Vec::new(),
116        })
117        .map(Builder::build)
118    }
119
120    /// Creates an expression for which no operand must match.
121    ///
122    /// # Errors
123    ///
124    /// Returns [`Error::Id`][] if any of the operands are invalid.
125    ///
126    /// [`Error::Id`]: crate::id::expression::Error::Id
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// # use std::error::Error;
132    /// # fn main() -> Result<(), Box<dyn Error>> {
133    /// use zrx_id::{selector, Expression};
134    ///
135    /// // Create expression
136    /// let expr = Expression::all(|expr| {
137    ///     expr.with(selector!(location = "**/*.md")?)?
138    ///         .with(Expression::not(|expr| {
139    ///             expr.with(selector!(provider = "file")?)
140    ///         })
141    ///     )
142    /// })?;
143    /// # Ok(())
144    /// # }
145    /// ```
146    #[inline]
147    pub fn not<F>(f: F) -> Result<Self>
148    where
149        F: FnOnce(Builder) -> Result<Builder>,
150    {
151        f(Builder {
152            operator: Operator::Not,
153            operands: Vec::new(),
154        })
155        .map(Builder::build)
156    }
157}
158
159// ----------------------------------------------------------------------------
160
161impl Builder {
162    /// Adds an operand to the expression.
163    ///
164    /// # Errors
165    ///
166    /// Returns [`Error::Id`][] if any of the operands are invalid.
167    ///
168    /// [`Error::Id`]: crate::id::expression::Error::Id
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// # use std::error::Error;
174    /// # fn main() -> Result<(), Box<dyn Error>> {
175    /// use zrx_id::{selector, Expression};
176    ///
177    /// // Create expression
178    /// let expr = Expression::any(|expr| {
179    ///     expr.with(selector!(location = "**/*.jpg")?)?
180    ///         .with(selector!(location = "**/*.png")?)
181    /// })?;
182    /// # Ok(())
183    /// # }
184    /// ```
185    #[inline]
186    pub fn with<T>(mut self, operand: T) -> Result<Self>
187    where
188        T: TryIntoOperand,
189    {
190        self.operands.push(operand.try_into_operand()?);
191        Ok(self)
192    }
193
194    /// Builds the expression.
195    ///
196    /// This method is private, as building is done implicitly through the
197    /// construction methods defined as part of [`Expression`].
198    #[inline]
199    #[must_use]
200    fn build(self) -> Expression {
201        Expression {
202            operator: self.operator,
203            operands: self.operands,
204        }
205    }
206}