Skip to main content

zrx_id/id/selector/
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//! Selector builder.
27
28use ahash::AHasher;
29use std::borrow::Cow;
30use std::hash::{Hash, Hasher};
31
32use crate::id::format::{self, Format};
33use crate::id::Result;
34
35use super::Selector;
36
37// ----------------------------------------------------------------------------
38// Structs
39// ----------------------------------------------------------------------------
40
41/// Selector builder.
42#[derive(Clone, Debug)]
43pub struct Builder<'a> {
44    /// Format builder.
45    format: format::Builder<'a, 7>,
46}
47
48// ----------------------------------------------------------------------------
49// Implementations
50// ----------------------------------------------------------------------------
51
52impl Selector {
53    /// Creates a selector builder.
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// use zrx_id::Selector;
59    ///
60    /// // Create selector builder
61    /// let builder = Selector::builder();
62    /// ```
63    #[inline]
64    #[must_use]
65    pub fn builder<'a>() -> Builder<'a> {
66        Builder::default()
67    }
68
69    /// Creates a builder from the selector.
70    ///
71    /// This method creates a builder from the current selector, which allows
72    /// to modify components and build a new selector from an existing one.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use std::error::Error;
78    /// # fn main() -> Result<(), Box<dyn Error>> {
79    /// use zrx_id::Selector;
80    ///
81    /// // Create selector from string
82    /// let selector: Selector = "zrs:::::**/*.md:".parse()?;
83    ///
84    /// // Create selector builder
85    /// let builder = selector.to_builder().location("index.md");
86    ///
87    /// // Create selector from builder
88    /// let selector = builder.build()?;
89    /// assert_eq!(selector.as_str(), "zrs:::::index.md:");
90    /// # Ok(())
91    /// # }
92    /// ```
93    #[inline]
94    #[must_use]
95    pub fn to_builder(&self) -> Builder<'_> {
96        Builder {
97            format: self.format.to_builder().with(0, "zrs"),
98        }
99    }
100}
101
102// ----------------------------------------------------------------------------
103
104impl<'a> Builder<'a> {
105    /// Sets the `provider` component.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use zrx_id::Selector;
111    ///
112    /// // Create selector builder and set provider
113    /// let builder = Selector::builder().provider("git");
114    /// ```
115    #[inline]
116    #[must_use]
117    pub fn provider<S>(mut self, value: S) -> Self
118    where
119        S: Into<Cow<'a, str>>,
120    {
121        self.format.set(1, value);
122        self
123    }
124
125    /// Sets the `resource` component.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// use zrx_id::Selector;
131    ///
132    /// // Create selector builder and set resource
133    /// let builder = Selector::builder().resource("master");
134    /// ```
135    #[inline]
136    #[must_use]
137    pub fn resource<S>(mut self, value: S) -> Self
138    where
139        S: Into<Cow<'a, str>>,
140    {
141        self.format.set(2, value);
142        self
143    }
144
145    /// Sets the `variant` component.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use zrx_id::Selector;
151    ///
152    /// // Create selector builder and set variant
153    /// let builder = Selector::builder().variant("en");
154    /// ```
155    #[inline]
156    #[must_use]
157    pub fn variant<S>(mut self, value: S) -> Self
158    where
159        S: Into<Cow<'a, str>>,
160    {
161        self.format.set(3, value);
162        self
163    }
164
165    /// Sets the `context` component.
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// use zrx_id::Selector;
171    ///
172    /// // Create selector builder and set context
173    /// let builder = Selector::builder().context("docs");
174    /// ```
175    #[inline]
176    #[must_use]
177    pub fn context<S>(mut self, value: S) -> Self
178    where
179        S: Into<Cow<'a, str>>,
180    {
181        self.format.set(4, value);
182        self
183    }
184
185    /// Sets the `location` component.
186    ///
187    /// # Examples
188    ///
189    /// ```
190    /// use zrx_id::Selector;
191    ///
192    /// // Create selector builder and set location
193    /// let builder = Selector::builder().location("docs");
194    /// ```
195    #[inline]
196    #[must_use]
197    pub fn location<S>(mut self, value: S) -> Self
198    where
199        S: Into<Cow<'a, str>>,
200    {
201        self.format.set(5, value);
202        self
203    }
204
205    /// Sets the `fragment` component.
206    ///
207    /// # Examples
208    ///
209    /// ```
210    /// use zrx_id::Selector;
211    ///
212    /// // Create selector builder and set fragment
213    /// let builder = Selector::builder().fragment("anchor");
214    /// ```
215    #[inline]
216    #[must_use]
217    pub fn fragment<S>(mut self, value: S) -> Self
218    where
219        S: Into<Cow<'a, str>>,
220    {
221        self.format.set(6, value);
222        self
223    }
224
225    /// Builds the selector.
226    ///
227    /// # Errors
228    ///
229    /// Returns [`Error::Format`][] if the format is invalid.
230    ///
231    /// [`Error::Format`]: crate::id::Error::Format
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// # use std::error::Error;
237    /// # fn main() -> Result<(), Box<dyn Error>> {
238    /// use zrx_id::Selector;
239    ///
240    /// // Create selector builder
241    /// let mut builder = Selector::builder()
242    ///     .provider("file")
243    ///     .context("docs")
244    ///     .location("**/*.md");
245    ///
246    /// // Create selector from builder
247    /// let selector = builder.build()?;
248    /// assert_eq!(selector.as_str(), "zrs:file:::docs:**/*.md:");
249    /// # Ok(())
250    /// # }
251    /// ```
252    pub fn build(self) -> Result<Selector> {
253        let format = self.format.build()?;
254
255        // Precompute hash for fast hashing
256        let hash = {
257            let mut hasher = AHasher::default();
258            format.hash(&mut hasher);
259            hasher.finish()
260        };
261
262        // No errors occurred
263        Ok(Selector { format, hash })
264    }
265}
266
267// ----------------------------------------------------------------------------
268// Trait implementations
269// ----------------------------------------------------------------------------
270
271impl Default for Builder<'_> {
272    /// Creates a selector builder.
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// use zrx_id::Selector;
278    ///
279    /// // Create selector builder
280    /// let builder = Selector::builder();
281    /// ```
282    #[inline]
283    fn default() -> Self {
284        Self {
285            format: Format::builder().with(0, "zrs"),
286        }
287    }
288}