Skip to main content

ps_uuid/methods/
get_node_id.rs

1use crate::{NodeId, UUID};
2
3impl UUID {
4    #[must_use]
5    pub const fn get_node_id(&self) -> Option<NodeId> {
6        match self.get_version() {
7            Some(1 | 2 | 6) => {
8                let [_, _, _, _, _, _, _, _, _, _, b1, b2, b3, b4, b5, b6] = self.bytes;
9
10                let node_id = NodeId {
11                    bytes: [b1, b2, b3, b4, b5, b6],
12                };
13
14                Some(node_id)
15            }
16            _ => None,
17        }
18    }
19}
20
21#[cfg(test)]
22mod tests {
23    #![allow(clippy::expect_used)]
24    use super::*;
25
26    /// Helper that fabricates a UUID with a specific version *and* node ID.
27    ///
28    /// - `version` is written into the high nibble of byte 6
29    ///   (see RFC 4122 §4.1.3).
30    /// - The RFC 4122 “variant 1” bits (`10xx_xxxx`) are written
31    ///   into byte 8, but the variant is irrelevant for `node_id()`.
32    fn make_uuid(version: u8, node: [u8; 6]) -> UUID {
33        let mut bytes = [0u8; 16];
34
35        // Any arbitrary values for the time / clock sequence fields:
36        for (i, item) in bytes.iter_mut().enumerate() {
37            *item = i.try_into().expect("test index should fit into u8");
38        }
39
40        // Embed the requested version.
41        bytes[6] = (version << 4) | (bytes[6] & 0x0F);
42
43        // Embed RFC-4122 variant 1 (“10xx_xxxx”).
44        bytes[8] = 0b1000_0000;
45
46        // Copy the node identifier into the last six bytes.
47        bytes[10..16].copy_from_slice(&node);
48
49        UUID { bytes }
50    }
51
52    #[test]
53    fn node_id_returns_some_for_versions_1_2_and_6() {
54        let node = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
55
56        for &v in &[1u8, 2, 6] {
57            let uuid = make_uuid(v, node);
58            assert_eq!(
59                uuid.get_node_id(),
60                Some(NodeId { bytes: node }),
61                "unexpected result for version {v}"
62            );
63        }
64    }
65
66    #[test]
67    fn node_id_returns_none_for_all_other_versions() {
68        let unwanted = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
69
70        // All versions that must *not* yield a node ID.
71        for &v in &[0u8, 3, 4, 5, 7, 8, 9, 0xF] {
72            let uuid = make_uuid(v, unwanted);
73            assert_eq!(
74                uuid.get_node_id(),
75                None,
76                "version {v} incorrectly returned Some(..)"
77            );
78        }
79    }
80
81    // -----------------------------------------------------------------------
82    // Compile-time proof: `UUID::node_id` is const-compatible
83    // -----------------------------------------------------------------------
84
85    // Build a UUID and compute its node ID entirely at compile time.
86    const SAMPLE_UUID: UUID = {
87        let mut bytes = [0u8; 16];
88
89        bytes[6] = 0x10; // version 1
90        bytes[8] = 0x80; // variant 1
91
92        // node_id
93        bytes[10] = 1;
94        bytes[11] = 2;
95        bytes[12] = 3;
96        bytes[13] = 4;
97        bytes[14] = 5;
98        bytes[15] = 6;
99
100        UUID { bytes }
101    };
102
103    const SAMPLE_NODE_ID: Option<NodeId> = SAMPLE_UUID.get_node_id();
104
105    #[test]
106    fn const_evaluation_matches_runtime_result() {
107        assert_eq!(
108            SAMPLE_NODE_ID,
109            Some(NodeId {
110                bytes: [1, 2, 3, 4, 5, 6]
111            })
112        );
113    }
114}