Skip to main content

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}