Skip to main content

zrx_scheduler/scheduler/signal/
key.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//! Key.
27
28use ahash::AHasher;
29use std::fmt::{self, Debug, Display};
30use std::hash::{Hash, Hasher};
31use std::ops::Index;
32use std::slice::Iter;
33use std::sync::Arc;
34
35use super::value::Value;
36
37mod error;
38mod id;
39
40pub use error::{Error, Result};
41pub use id::Id;
42
43// ----------------------------------------------------------------------------
44// Structs
45// ----------------------------------------------------------------------------
46
47/// Key.
48///
49/// Keys represent hierarchies of identifiers, which are a fundamental tool for
50/// representing the hierarchical structure of computations and relations. A key
51/// can consist of a single identifier, or arbitrary long chains of multiple
52/// identifiers to model derivations and dependencies.
53///
54/// # Examples
55///
56/// ```
57/// use zrx_scheduler::Key;
58///
59/// // Create and transform key
60/// let key = Key::from_iter([1, 2, 3]);
61/// assert_eq!(
62///     key.rotate_left(1),
63///     Key::from_iter([2, 3, 1])
64/// );
65/// ```
66#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
67pub struct Key<I> {
68    /// Path.
69    path: Arc<[I]>,
70    /// Precomputed hash.
71    hash: u64,
72}
73
74// ----------------------------------------------------------------------------
75// Implementations
76// ----------------------------------------------------------------------------
77
78impl<I> Key<I> {
79    /// Returns the identifier, if key has length 1.
80    ///
81    /// # Errors
82    ///
83    /// Returns [`Error::Empty`] if the key is empty, and [`Error::Depth`] if
84    /// the key is deeper than one level, containing multiple identifiers.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # use std::error::Error;
90    /// # fn main() -> Result<(), Box<dyn Error>> {
91    /// use zrx_scheduler::Key;
92    ///
93    /// // Create key
94    /// let key = Key::from(42);
95    ///
96    /// // Obtain identifier
97    /// assert_eq!(key.try_as_id()?, &42);
98    /// # Ok(())
99    /// # }
100    /// ```
101    #[inline]
102    pub fn try_as_id(&self) -> Result<&I> {
103        match &*self.path {
104            [id] => Ok(id),
105            [] => Err(Error::Empty),
106            _ => Err(Error::Depth),
107        }
108    }
109}
110
111impl<I> Key<I>
112where
113    I: Id,
114{
115    /// Concatenates the key with another key.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use zrx_scheduler::Key;
121    ///
122    /// // Create and concat keys
123    /// let key = Key::from(1);
124    /// assert_eq!(
125    ///     key.concat(Key::from_iter([2, 3])),
126    ///     Key::from_iter([1, 2, 3])
127    /// );
128    /// ```
129    #[must_use]
130    pub fn concat<K>(&self, tail: K) -> Self
131    where
132        K: AsRef<Self>,
133    {
134        let tail = tail.as_ref();
135
136        // Concatenate paths and return key
137        let iter = self.path.iter().chain(tail.path.iter());
138        let path = iter.cloned().collect();
139        Self { hash: hash(&path), path }
140    }
141
142    /// Reverses the key.
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// use zrx_scheduler::Key;
148    ///
149    /// // Create and transform key
150    /// let key = Key::from_iter([1, 2, 3]);
151    /// assert_eq!(
152    ///     key.reverse(),
153    ///     Key::from_iter([3, 2, 1])
154    /// );
155    /// ```
156    #[must_use]
157    pub fn reverse(&self) -> Self {
158        let iter = self.path.iter().rev();
159        iter.cloned().collect()
160    }
161
162    /// Rotates the key left by `n` positions.
163    ///
164    /// # Panics
165    ///
166    /// Panics if the index is out of bounds.
167    ///
168    /// # Examples
169    ///
170    /// ```
171    /// use zrx_scheduler::Key;
172    ///
173    /// // Create and transform key
174    /// let key = Key::from_iter([1, 2, 3]);
175    /// assert_eq!(
176    ///     key.rotate_left(1),
177    ///     Key::from_iter([2, 3, 1])
178    /// );
179    /// ```
180    #[must_use]
181    pub fn rotate_left(&self, n: usize) -> Self {
182        let iter = self.path[n..].iter().chain(self.path[..n].iter());
183        iter.cloned().collect()
184    }
185
186    /// Rotates the key right by `n` positions.
187    ///
188    /// # Panics
189    ///
190    /// Panics if the index is out of bounds.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// use zrx_scheduler::Key;
196    ///
197    /// // Create and transform key
198    /// let key = Key::from_iter([1, 2, 3]);
199    /// assert_eq!(
200    ///     key.rotate_right(1),
201    ///     Key::from_iter([3, 1, 2])
202    /// );
203    /// ```
204    #[inline]
205    #[must_use]
206    pub fn rotate_right(&self, n: usize) -> Self {
207        self.rotate_left(self.path.len() - n)
208    }
209
210    /// Creates an iterator over the key.
211    ///
212    /// # Examples
213    ///
214    /// ```
215    /// use zrx_scheduler::Key;
216    ///
217    /// // Create key from iterator
218    /// let key = Key::from_iter([1, 2, 3]);
219    ///
220    /// // Create iterator over key
221    /// for id in &key {
222    ///     println!("{id}");
223    /// }
224    /// ```
225    #[inline]
226    pub fn iter(&self) -> Iter<'_, I> {
227        self.path.iter()
228    }
229}
230
231// ----------------------------------------------------------------------------
232// Trait implementations
233// ----------------------------------------------------------------------------
234
235impl<I> Value for Key<I> where I: Value {}
236
237// ----------------------------------------------------------------------------
238
239impl<I> AsRef<Key<I>> for Key<I> {
240    /// Returns a reference to the key.
241    #[inline]
242    fn as_ref(&self) -> &Key<I> {
243        self
244    }
245}
246
247// ----------------------------------------------------------------------------
248
249impl<I> From<I> for Key<I>
250where
251    I: Id,
252{
253    /// Creates a key from an identifier.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// use zrx_scheduler::Key;
259    ///
260    /// // Create key
261    /// let key = Key::from(42);
262    /// ```
263    #[inline]
264    fn from(id: I) -> Self {
265        let path = Arc::from([id]);
266        Self { hash: hash(&path), path }
267    }
268}
269
270// ----------------------------------------------------------------------------
271
272impl<I> FromIterator<I> for Key<I>
273where
274    I: Id,
275{
276    /// Creates a key from an iterator.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// use zrx_scheduler::Key;
282    ///
283    /// // Create key from iterator
284    /// let key = Key::from_iter([1, 2, 3]);
285    /// ```
286    fn from_iter<T>(iter: T) -> Self
287    where
288        T: IntoIterator<Item = I>,
289    {
290        let path = iter.into_iter().collect();
291        Self { hash: hash(&path), path }
292    }
293}
294
295impl<'a, I> IntoIterator for &'a Key<I>
296where
297    I: Id,
298{
299    type Item = &'a I;
300    type IntoIter = Iter<'a, I>;
301
302    /// Creates an iterator over the key.
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// use zrx_scheduler::Key;
308    ///
309    /// // Create key from iterator
310    /// let key = Key::from_iter([1, 2, 3]);
311    ///
312    /// // Create iterator over key
313    /// for id in &key {
314    ///     println!("{id}");
315    /// }
316    /// ```
317    #[inline]
318    fn into_iter(self) -> Self::IntoIter {
319        self.iter()
320    }
321}
322
323// ----------------------------------------------------------------------------
324
325impl<I> Index<usize> for Key<I> {
326    type Output = I;
327
328    /// Returns a reference to the identifier at the index.
329    ///
330    /// # Panics
331    ///
332    /// Panics if the index is out of bounds.
333    #[inline]
334    fn index(&self, index: usize) -> &Self::Output {
335        &self.path[index]
336    }
337}
338
339// ----------------------------------------------------------------------------
340
341impl<I> Hash for Key<I> {
342    /// Hashes the key.
343    ///
344    /// Since key paths are immutable, we can use a precomputed hash for fast
345    /// hashing. This is especially useful when key paths are used as keys in
346    /// hash maps or hash sets, where hashing is a frequent operation, as the
347    /// performance gains are significant with constant time.
348    #[inline]
349    fn hash<H>(&self, state: &mut H)
350    where
351        H: Hasher,
352    {
353        state.write_u64(self.hash);
354    }
355}
356
357// ----------------------------------------------------------------------------
358
359impl<I> Display for Key<I>
360where
361    I: Display,
362{
363    /// Formats the key for display.
364    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
365        for (i, item) in self.path.iter().enumerate() {
366            Display::fmt(item, f)?;
367
368            // Write slash if not last
369            if i < self.path.len() - 1 {
370                f.write_str(" / ")?;
371            }
372        }
373
374        // No errors occurred
375        Ok(())
376    }
377}
378
379impl<I> Debug for Key<I>
380where
381    I: Debug,
382{
383    // Formats the key for debugging.
384    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
385        f.debug_list().entry(&self.path).finish()
386    }
387}
388
389// ----------------------------------------------------------------------------
390// Functions
391// ----------------------------------------------------------------------------
392
393/// Precomputes the hash for the given path - this is used for fast hashing of
394/// keys, since key paths are immutable, meaning hashes can be precomputed.
395#[inline]
396fn hash<P>(path: &P) -> u64
397where
398    P: Hash,
399{
400    let mut hasher = AHasher::default();
401    path.hash(&mut hasher);
402    hasher.finish()
403}