ps_uuid/methods/
new_ncs.rs1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3use crate::UUID;
4
5const NCS_EPOCH: Duration = Duration::from_secs(315_532_800); const MAX_TIMESTAMP: u64 = (1u64 << 48) - 1; const NCS_VARIANT_MASK: u8 = 0b1000_0000; #[allow(clippy::module_name_repetitions)]
10#[derive(thiserror::Error, Debug)]
11pub enum NcsUuidError {
12 #[error("Address family out of range")]
13 AddressFamilyOutOfRange,
14 #[error("Timestamp is before 1980-01-01")]
15 TimestampBeforeEpoch,
16 #[error("Timestamp is after 2015-09-05T05:58:26.842Z")]
17 TimestampOverflow,
18}
19
20impl UUID {
21 pub fn new_ncs(
50 timestamp: SystemTime,
51 address_family: u8,
52 address: &[u8; 7],
53 ) -> Result<Self, NcsUuidError> {
54 if address_family > 13 {
56 return Err(NcsUuidError::AddressFamilyOutOfRange);
57 }
58
59 let duration = timestamp
61 .duration_since(UNIX_EPOCH + NCS_EPOCH)
62 .map_err(|_| NcsUuidError::TimestampBeforeEpoch)?;
63
64 let timestamp = duration.as_micros() / 4;
66
67 if timestamp > MAX_TIMESTAMP.into() {
69 return Err(NcsUuidError::TimestampOverflow);
70 }
71
72 let mut bytes = (timestamp << 80).to_be_bytes();
75
76 bytes[8] = address_family;
78
79 bytes[9..16].copy_from_slice(address);
81
82 bytes[8] &= !NCS_VARIANT_MASK;
84
85 Ok(Self { bytes })
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_valid_ncs_uuid() -> Result<(), NcsUuidError> {
95 let time = UNIX_EPOCH + NCS_EPOCH + Duration::from_secs(3600); let address = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
97 let uuid = UUID::new_ncs(time, 2, &address)?;
98 let bytes = uuid.as_bytes();
99 let expected_timestamp = (900_000_000u64).to_be_bytes();
101 assert_eq!(&bytes[0..6], &expected_timestamp[2..8]);
102 assert_eq!(&bytes[6..8], &[0, 0]);
104 assert_eq!(bytes[8], 2);
106 assert_eq!(&bytes[9..16], &address);
108 Ok(())
109 }
110
111 #[test]
112 fn test_timestamp_before_epoch() {
113 let time = UNIX_EPOCH; let address = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
115 let result = UUID::new_ncs(time, 2, &address);
116 assert!(matches!(result, Err(NcsUuidError::TimestampBeforeEpoch)));
117 }
118
119 #[test]
120 fn test_invalid_address_family() {
121 let time = UNIX_EPOCH + NCS_EPOCH + Duration::from_secs(3600);
122 let address = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
123 let result = UUID::new_ncs(time, 14, &address);
124 assert!(matches!(result, Err(NcsUuidError::AddressFamilyOutOfRange)));
125 }
126
127 #[test]
128 fn test_nil_uuid() -> Result<(), NcsUuidError> {
129 let ncs_nil = UUID::new_ncs(UNIX_EPOCH + NCS_EPOCH, 0, &[0, 0, 0, 0, 0, 0, 0])?;
130 let nil = UUID::nil();
131
132 assert_eq!(ncs_nil, nil, "UUIDs should be equal.");
133
134 Ok(())
135 }
136}