zrx_id/id/sequence.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//! Sequence.
27
28use std::iter::once;
29use std::slice::Iter;
30use std::vec::IntoIter;
31
32use zrx_scheduler::Value;
33
34mod element;
35pub mod filter;
36
37pub use element::Element;
38pub use filter::Filter;
39
40// ----------------------------------------------------------------------------
41// Structs
42// ----------------------------------------------------------------------------
43
44/// Sequence.
45///
46/// Sequences are ordered collections of expressions that can be used to match
47/// identifiers. Sequences can contain gaps, which allow to implement flexible
48/// matching of identifiers with varying numbers of elements, including but
49/// not limited to prefix and suffix matching.
50///
51/// Each [`Element`] of a [`Sequence`] can either be an [`Expression`][] or a
52/// [`Gap`][]. An [`Expression`][] is a logical combination of one or multiple
53/// [`Id`][] and [`Selector`][] instances, while a [`Gap`][] represents a
54/// wildcard that can match any number of elements, including zero.
55///
56/// The following convenience constructors are provided:
57///
58/// - [`Sequence::prefix`]: Creates a sequence for prefix matching.
59/// - [`Sequence::suffix`]: Creates a sequence for suffix matching.
60///
61/// [`Expression`]: crate::id::expression::Expression
62/// [`Gap`]: crate::id::sequence::Element::Gap
63/// [`Id`]: crate::id::Id
64/// [`Selector`]: crate::id::selector::Selector
65///
66/// # Examples
67///
68/// ```
69/// # use std::error::Error;
70/// # fn main() -> Result<(), Box<dyn Error>> {
71/// use zrx_id::{selector, Sequence};
72///
73/// // Create sequence
74/// let sequence = Sequence::from([
75/// selector!(location = "zensical.toml")?,
76/// selector!(location = "**/*.md")?,
77/// ]);
78/// # Ok(())
79/// # }
80/// ```
81#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct Sequence {
83 /// Sequence elements.
84 elements: Box<[Element]>,
85}
86
87// ----------------------------------------------------------------------------
88// Implementations
89// ----------------------------------------------------------------------------
90
91impl Sequence {
92 /// Creates a sequence where the given elements are a prefix.
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// # use std::error::Error;
98 /// # fn main() -> Result<(), Box<dyn Error>> {
99 /// use zrx_id::{selector, Sequence};
100 ///
101 /// // Create sequence for a prefix
102 /// let sequence = Sequence::prefix([
103 /// selector!(location = "zensical.toml")?,
104 /// ]);
105 /// # Ok(())
106 /// # }
107 /// ```
108 #[inline]
109 #[must_use]
110 pub fn prefix<T>(sequence: T) -> Self
111 where
112 T: Into<Self>,
113 {
114 let sequence = sequence.into();
115 let elements = sequence.into_iter().chain(once(Element::Gap));
116 Self {
117 elements: elements.collect::<Vec<_>>().into_boxed_slice(),
118 }
119 }
120
121 /// Creates a sequence where the given elements are a suffix.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// # use std::error::Error;
127 /// # fn main() -> Result<(), Box<dyn Error>> {
128 /// use zrx_id::{selector, Sequence};
129 ///
130 /// // Create sequence for a suffix
131 /// let sequence = Sequence::suffix([
132 /// selector!(location = "**/*.md")?,
133 /// ]);
134 /// # Ok(())
135 /// # }
136 /// ```
137 #[inline]
138 #[must_use]
139 pub fn suffix<T>(sequence: T) -> Self
140 where
141 T: Into<Self>,
142 {
143 let sequence = sequence.into();
144 let elements = once(Element::Gap).chain(sequence);
145 Self {
146 elements: elements.collect::<Vec<_>>().into_boxed_slice(),
147 }
148 }
149
150 /// Creates an iterator over the sequence.
151 ///
152 /// # Examples
153 ///
154 /// ```
155 /// # use std::error::Error;
156 /// # fn main() -> Result<(), Box<dyn Error>> {
157 /// use zrx_id::{selector, Sequence};
158 ///
159 /// // Create sequence
160 /// let sequence = Sequence::from([
161 /// selector!(location = "zensical.toml")?,
162 /// selector!(location = "**/*.md")?,
163 /// ]);
164 ///
165 /// // Create iterator over sequence
166 /// for element in sequence.iter() {
167 /// println!("{element:?}");
168 /// }
169 /// # Ok(())
170 /// # }
171 /// ```
172 #[inline]
173 pub fn iter(&self) -> Iter<'_, Element> {
174 self.elements.iter()
175 }
176}
177
178#[allow(clippy::must_use_candidate)]
179impl Sequence {
180 /// Returns the number of elements.
181 #[inline]
182 pub fn len(&self) -> usize {
183 self.elements.len()
184 }
185
186 /// Returns whether there are any elements.
187 #[inline]
188 pub fn is_empty(&self) -> bool {
189 self.elements.is_empty()
190 }
191}
192
193// ----------------------------------------------------------------------------
194// Trait implementations
195// ----------------------------------------------------------------------------
196
197impl Value for Sequence {}
198
199// ----------------------------------------------------------------------------
200
201impl<E> From<E> for Sequence
202where
203 E: Into<Element>,
204{
205 /// Creates a sequence from an element.
206 ///
207 /// # Examples
208 ///
209 /// ```
210 /// # use std::error::Error;
211 /// # fn main() -> Result<(), Box<dyn Error>> {
212 /// use zrx_id::{selector, Sequence};
213 ///
214 /// // Create sequence
215 /// let sequence = Sequence::from(
216 /// selector!(location = "zensical.toml")?,
217 /// );
218 /// # Ok(())
219 /// # }
220 /// ```
221 #[inline]
222 fn from(value: E) -> Self {
223 Self::from_iter([value])
224 }
225}
226
227impl<E, const N: usize> From<[E; N]> for Sequence
228where
229 E: Into<Element>,
230{
231 /// Creates a sequence from a slice of elements.
232 ///
233 /// # Examples
234 ///
235 /// ```
236 /// # use std::error::Error;
237 /// # fn main() -> Result<(), Box<dyn Error>> {
238 /// use zrx_id::{selector, Sequence};
239 ///
240 /// // Create sequence
241 /// let sequence = Sequence::from([
242 /// selector!(location = "zensical.toml")?,
243 /// selector!(location = "**/*.md")?,
244 /// ]);
245 /// # Ok(())
246 /// # }
247 /// ```
248 #[inline]
249 fn from(value: [E; N]) -> Self {
250 Self::from_iter(value)
251 }
252}
253
254impl<E> From<&[E]> for Sequence
255where
256 E: Into<Element> + Clone,
257{
258 /// Creates a sequence from a slice of elements.
259 ///
260 /// # Examples
261 ///
262 /// ```
263 /// # use std::error::Error;
264 /// # fn main() -> Result<(), Box<dyn Error>> {
265 /// use zrx_id::{selector, Sequence};
266 ///
267 /// // Create sequence
268 /// let sequence = Sequence::from(&[
269 /// selector!(location = "zensical.toml")?,
270 /// selector!(location = "**/*.md")?,
271 /// ][..]);
272 /// # Ok(())
273 /// # }
274 /// ```
275 #[inline]
276 fn from(value: &[E]) -> Self {
277 value.iter().cloned().collect()
278 }
279}
280
281impl<E> From<Vec<E>> for Sequence
282where
283 E: Into<Element>,
284{
285 /// Creates a sequence from a vector of elements.
286 ///
287 /// # Examples
288 ///
289 /// ```
290 /// # use std::error::Error;
291 /// # fn main() -> Result<(), Box<dyn Error>> {
292 /// use zrx_id::{selector, Sequence};
293 ///
294 /// // Create sequence
295 /// let sequence = Sequence::from(vec![
296 /// selector!(location = "zensical.toml")?,
297 /// selector!(location = "**/*.md")?,
298 /// ]);
299 /// # Ok(())
300 /// # }
301 /// ```
302 #[inline]
303 fn from(value: Vec<E>) -> Self {
304 Self::from_iter(value)
305 }
306}
307
308// ----------------------------------------------------------------------------
309
310impl<E> FromIterator<E> for Sequence
311where
312 E: Into<Element>,
313{
314 /// Creates a sequence from an iterator.
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// # use std::error::Error;
320 /// # fn main() -> Result<(), Box<dyn Error>> {
321 /// use zrx_id::{selector, Sequence};
322 ///
323 /// // Create sequence from iterator
324 /// let sequence = Sequence::from_iter([
325 /// selector!(location = "zensical.toml")?,
326 /// selector!(location = "**/*.md")?,
327 /// ]);
328 /// # Ok(())
329 /// # }
330 /// ```
331 #[inline]
332 fn from_iter<T>(iter: T) -> Self
333 where
334 T: IntoIterator<Item = E>,
335 {
336 let elements = iter.into_iter().map(Into::into);
337 Self {
338 elements: elements.collect::<Vec<_>>().into_boxed_slice(),
339 }
340 }
341}
342
343impl<'a> IntoIterator for &'a Sequence {
344 type Item = &'a Element;
345 type IntoIter = Iter<'a, Element>;
346
347 /// Creates an iterator over the sequence.
348 ///
349 /// # Examples
350 ///
351 /// ```
352 /// # use std::error::Error;
353 /// # fn main() -> Result<(), Box<dyn Error>> {
354 /// use zrx_id::{selector, Sequence};
355 ///
356 /// // Create sequence
357 /// let sequence = Sequence::from([
358 /// selector!(location = "zensical.toml")?,
359 /// selector!(location = "**/*.md")?,
360 /// ]);
361 ///
362 /// // Create iterator over sequence
363 /// for element in &sequence {
364 /// println!("{element:?}");
365 /// }
366 /// # Ok(())
367 /// # }
368 /// ```
369 #[inline]
370 fn into_iter(self) -> Self::IntoIter {
371 self.iter()
372 }
373}
374
375impl IntoIterator for Sequence {
376 type Item = Element;
377 type IntoIter = IntoIter<Self::Item>;
378
379 /// Creates a consuming iterator over the sequence.
380 ///
381 /// # Examples
382 ///
383 /// ```
384 /// # use std::error::Error;
385 /// # fn main() -> Result<(), Box<dyn Error>> {
386 /// use zrx_id::{selector, Sequence};
387 ///
388 /// // Create sequence
389 /// let sequence = Sequence::from([
390 /// selector!(location = "zensical.toml")?,
391 /// selector!(location = "**/*.md")?,
392 /// ]);
393 ///
394 /// // Create iterator over sequence
395 /// for element in sequence {
396 /// println!("{element:?}");
397 /// }
398 /// # Ok(())
399 /// # }
400 /// ```
401 #[inline]
402 fn into_iter(self) -> Self::IntoIter {
403 self.elements.into_iter()
404 }
405}
406
407// ----------------------------------------------------------------------------
408
409impl Default for Sequence {
410 /// Creates a sequence that matches everything.
411 ///
412 /// # Examples
413 ///
414 /// ```
415 /// use zrx_id::Sequence;
416 ///
417 /// // Create empty sequence
418 /// let sequence = Sequence::default();
419 /// assert!(!sequence.is_empty());
420 /// ```
421 #[inline]
422 fn default() -> Self {
423 Self::from(Element::Gap)
424 }
425}