const_identify/
id.rs

1use std::fmt::{Display, Formatter, Result};
2
3use const_fnv1a_hash::fnv1a_hash_str_64;
4
5/// A unique identifier that can be created used in const contexts.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct ConstId {
8    id: u64,
9}
10
11impl Display for ConstId {
12    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
13        write!(f, "{}", self.id)
14    }
15}
16
17impl ConstId {
18    /// Builds a new id using a raw `value`.
19    ///
20    /// This should almost never be used. In fact if you are reading this you may
21    /// have already been looking at the source code because these docs are hidden.
22    #[doc(hidden)]
23    pub const fn from_raw(value: u64) -> Self {
24        Self { id: value }
25    }
26
27    /// Generates a new id by hashing a `unique_str`.
28    pub const fn generate(unique_str: &str) -> Self {
29        Self {
30            id: fnv1a_hash_str_64(unique_str),
31        }
32    }
33
34    /// Returns the inner raw value used in this id.
35    ///
36    /// This can be useful for making comparisons in the const context.
37    pub const fn raw_value(&self) -> u64 {
38        self.id
39    }
40}
41
42/// This trait is used as to mark structs with an id available in const contexts.
43///
44/// # Safety
45/// This trait should only be implemented by using the `#[derive]` macro
46/// because it requires that a unique ID is assigned to every struct.
47///
48/// If you do implement this by hand, you must ensure that every impl
49/// does not have any overlapping ids.This is done in the macro by using
50/// `ConstId::generate(concat!(module_path!(), "::", stringify!(StructName)))`
51pub unsafe trait ConstIdentify {
52    const CONST_ID: ConstId;
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use const_identify_derive::ConstIdentify;
59
60    #[derive(ConstIdentify)]
61    struct TestStruct;
62
63    #[test]
64    fn uniqueness() {
65        const ID1: ConstId = ConstId::generate("unique1");
66        const ID2: ConstId = ConstId::generate("unique2");
67        const ID3: ConstId = ConstId::generate("unique3");
68        assert_ne!(ID1, ID2);
69        assert_ne!(ID2, ID3);
70        assert_ne!(ID3, ID1);
71    }
72
73    #[test]
74    fn derive_uniqueness() {
75        #[derive(ConstIdentify)]
76        struct TestStruct;
77
78        #[derive(ConstIdentify)]
79        struct TestStruct2;
80
81        let local_id = TestStruct::CONST_ID;
82        let local_id2 = TestStruct2::CONST_ID;
83        assert_ne!(local_id, local_id2);
84
85        let global_id = tests::TestStruct::CONST_ID;
86        assert_ne!(local_id, global_id);
87    }
88
89    // this section is used to check that derive tests still hold even with wacky formatting
90    #[rustfmt::skip]
91    mod unformatted {
92        use super::*;
93
94        #[derive(ConstIdentify)]struct TestStruct;#[test]fn derive_uniqueness() {#[derive(ConstIdentify)]struct TestStruct;
95            let local_id = TestStruct::CONST_ID;
96            let global_id = tests::TestStruct::CONST_ID;
97            assert_ne!(local_id, global_id);
98        }
99    }
100}