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}