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}