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}