Skip to main content

zrx_id/id/specificity/
specified.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//! Specified value.
27
28use std::cmp::Ordering;
29use std::ops::Deref;
30
31use super::convert::ToSpecificity;
32use super::Specificity;
33
34// ----------------------------------------------------------------------------
35// Structs
36// ----------------------------------------------------------------------------
37
38/// Specified value.
39///
40/// This data type is a thin wrapper around a value of type `T`, augmenting the
41/// value with its computed [`Specificity`], so it can be efficiently ordered.
42///
43/// Note that specifities are ordered from lowest to highest, with the least
44/// specific value being the first in order. It would be natural to reverse
45/// this order, but this would lead to more complex implementations for when
46/// a specified value is wrapped in an [`Option`].
47#[derive(Clone, Debug, Default)]
48pub struct Specified<T> {
49    /// Inner value.
50    inner: T,
51    /// Specificity.
52    specificity: Specificity,
53}
54
55// ----------------------------------------------------------------------------
56// Implementations
57// ----------------------------------------------------------------------------
58
59impl<T> Specified<T> {
60    /// Returns the inner value, consuming the specified value.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// # use std::error::Error;
66    /// # fn main() -> Result<(), Box<dyn Error>> {
67    /// use zrx_id::selector;
68    /// use zrx_id::specificity::Specified;
69    ///
70    /// // Create selector
71    /// let selector = selector!(location = "**/*.md")?;
72    ///
73    /// // Create specified value from selector
74    /// let value = Specified::from(selector.clone());
75    /// assert_eq!(value.into_inner(), selector);
76    /// # Ok(())
77    /// # }
78    /// ```
79    #[inline]
80    pub fn into_inner(self) -> T {
81        self.inner
82    }
83}
84
85#[allow(clippy::must_use_candidate)]
86impl<T> Specified<T> {
87    /// Returns the specificity.
88    #[inline]
89    pub fn specificity(&self) -> Specificity {
90        self.specificity
91    }
92}
93
94// ----------------------------------------------------------------------------
95// Trait implementations
96// ----------------------------------------------------------------------------
97
98impl<T> From<T> for Specified<T>
99where
100    T: ToSpecificity,
101{
102    /// Creates a specified value from a value.
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// # use std::error::Error;
108    /// # fn main() -> Result<(), Box<dyn Error>> {
109    /// use zrx_id::selector;
110    /// use zrx_id::specificity::Specified;
111    ///
112    /// // Create selector
113    /// let selector = selector!(location = "**/*.md")?;
114    ///
115    /// // Create specified value from selector
116    /// let value = Specified::from(selector);
117    /// assert_eq!(value.specificity(), (0, 1, 1, 3).into());
118    /// # Ok(())
119    /// # }
120    /// ```
121    #[inline]
122    fn from(inner: T) -> Self {
123        let specificity = inner.to_specificity();
124        Specified { inner, specificity }
125    }
126}
127
128// ----------------------------------------------------------------------------
129
130impl<T> Deref for Specified<T> {
131    type Target = T;
132
133    /// Dereferences to the inner value.
134    #[inline]
135    fn deref(&self) -> &Self::Target {
136        &self.inner
137    }
138}
139
140// ----------------------------------------------------------------------------
141
142impl<T> PartialEq for Specified<T>
143where
144    T: PartialEq,
145{
146    /// Compares two specified values for equality.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// # use std::error::Error;
152    /// # fn main() -> Result<(), Box<dyn Error>> {
153    /// use zrx_id::selector;
154    /// use zrx_id::specificity::Specified;
155    ///
156    /// // Create and compare selectors
157    /// let a = Specified::from(selector!(location = "*.md")?);
158    /// let b = Specified::from(selector!(location = "*.md")?);
159    /// assert_eq!(a, b);
160    /// # Ok(())
161    /// # }
162    /// ```
163    #[inline]
164    fn eq(&self, other: &Self) -> bool {
165        self.inner == other.inner
166    }
167}
168
169impl<T> Eq for Specified<T> where T: Eq {}
170
171// ----------------------------------------------------------------------------
172
173impl<T> PartialOrd for Specified<T>
174where
175    T: Eq,
176{
177    /// Orders two values by specificity.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// # use std::error::Error;
183    /// # fn main() -> Result<(), Box<dyn Error>> {
184    /// use zrx_id::selector;
185    /// use zrx_id::specificity::Specified;
186    ///
187    /// // Create and compare selectors by specificity
188    /// let a = Specified::from(selector!(location = "**/*.md")?);
189    /// let b = Specified::from(selector!(location = "*.md")?);
190    /// assert!(a < b);
191    /// # Ok(())
192    /// # }
193    /// ```
194    #[inline]
195    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
196        Some(self.cmp(other))
197    }
198}
199
200impl<T> Ord for Specified<T>
201where
202    T: Eq,
203{
204    /// Orders two values by specificity.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// # use std::error::Error;
210    /// # fn main() -> Result<(), Box<dyn Error>> {
211    /// use zrx_id::selector;
212    /// use zrx_id::specificity::Specified;
213    ///
214    /// // Create and compare selectors by specificity
215    /// let a = Specified::from(selector!(location = "**/*.md")?);
216    /// let b = Specified::from(selector!(location = "*.md")?);
217    /// assert!(a < b);
218    /// # Ok(())
219    /// # }
220    /// ```
221    #[inline]
222    fn cmp(&self, other: &Self) -> Ordering {
223        self.specificity.cmp(&other.specificity)
224    }
225}