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}