provenant/license_detection/models/rule_id.rs
1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Newtype wrapper for rule IDs used throughout license detection.
5
6use rkyv::Archive;
7
8/// Internal rule ID used as an index into `LicenseIndex::rules_by_rid`.
9///
10/// Every rule in the license index is assigned a `RuleId` sequentially during
11/// index construction. The ID is used for fast Vec lookups and as a key in
12/// HashMap/HashSet structures on `LicenseIndex`.
13///
14/// # Sentinel value
15///
16/// `RuleId::NONE` (`usize::MAX`) represents an absent or invalid rule ID.
17/// It is used where production code previously relied on `rid: 0` as a sentinel,
18/// which was unsafe because `0` is a valid index pointing to a real rule.
19/// Use `is_valid()` to check whether a `RuleId` refers to a real rule.
20#[derive(
21 Debug,
22 Clone,
23 Copy,
24 PartialEq,
25 Eq,
26 Hash,
27 PartialOrd,
28 Ord,
29 Archive,
30 rkyv::Serialize,
31 rkyv::Deserialize,
32)]
33#[rkyv(derive(Hash, Eq, PartialEq, PartialOrd, Ord))]
34pub struct RuleId(usize);
35
36impl RuleId {
37 /// Sentinel value representing an absent or invalid rule ID.
38 ///
39 /// `usize::MAX` can never be a valid Vec index, so this is guaranteed
40 /// not to alias any real rule.
41 pub const NONE: Self = Self(usize::MAX);
42
43 /// Creates a `RuleId` from a raw `usize` index.
44 ///
45 /// # Panics
46 ///
47 /// Panics if `raw` is `usize::MAX` (the sentinel value).
48 /// Use `RuleId::NONE` to construct the sentinel.
49 pub const fn new(raw: usize) -> Self {
50 assert!(
51 raw != usize::MAX,
52 "RuleId::new(usize::MAX) is reserved for RuleId::NONE"
53 );
54 Self(raw)
55 }
56
57 /// Returns the raw `usize` index.
58 ///
59 /// Prefer using `RuleId` directly for indexing and lookups rather than
60 /// extracting the raw value.
61 pub const fn raw(self) -> usize {
62 self.0
63 }
64
65 /// Returns `true` if this `RuleId` refers to a real rule (not the sentinel).
66 pub const fn is_valid(self) -> bool {
67 self.0 != usize::MAX
68 }
69
70 /// Returns `true` if this is the `RuleId::NONE` sentinel.
71 pub const fn is_none(self) -> bool {
72 self.0 == usize::MAX
73 }
74}
75
76impl From<RuleId> for usize {
77 fn from(value: RuleId) -> Self {
78 value.0
79 }
80}
81
82impl std::fmt::Display for RuleId {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 if self.is_none() {
85 write!(f, "RuleId::NONE")
86 } else {
87 write!(f, "RuleId({})", self.0)
88 }
89 }
90}