bc_components/id/
uuid.rs

1use std::str::FromStr;
2
3use anyhow::{Error, Result};
4use dcbor::prelude::*;
5
6use crate::tags;
7
8/// A Universally Unique Identifier (UUID).
9///
10/// UUIDs are 128-bit (16-byte) identifiers that are designed to be unique
11/// across space and time. This implementation creates type 4 (random) UUIDs,
12/// following the UUID specification:
13///
14/// - Version field (bits 48-51) is set to 4, indicating a random UUID
15/// - Variant field (bits 64-65) is set to 2, indicating RFC 4122/DCE 1.1 UUID
16///   variant
17///
18/// Unlike ARIDs, UUIDs:
19/// - Are shorter (128 bits vs 256 bits)
20/// - Contain version and variant metadata within the identifier
21/// - Have a canonical string representation with 5 groups separated by hyphens
22///
23/// The canonical textual representation of a UUID takes the form:
24/// `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` where each `x` is a hexadecimal
25/// digit.
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27pub struct UUID([u8; Self::UUID_SIZE]);
28
29impl UUID {
30    pub const UUID_SIZE: usize = 16;
31
32    /// Creates a new type 4 (random) UUID.
33    pub fn new() -> Self {
34        let mut uuid = [0u8; Self::UUID_SIZE];
35        bc_rand::fill_random_data(&mut uuid);
36        uuid[6] = (uuid[6] & 0x0f) | 0x40; // set version to 4
37        uuid[8] = (uuid[8] & 0x3f) | 0x80; // set variant to 2
38        Self(uuid)
39    }
40
41    /// Restores a UUID from data.
42    pub fn from_data(data: [u8; Self::UUID_SIZE]) -> Self { Self(data) }
43
44    /// Restores a UUID from data.
45    pub fn from_data_ref(data: impl AsRef<[u8]>) -> Option<Self> {
46        let data = data.as_ref();
47        if data.len() != Self::UUID_SIZE {
48            return None;
49        }
50        let mut arr = [0u8; Self::UUID_SIZE];
51        arr.copy_from_slice(data);
52        Some(Self::from_data(arr))
53    }
54
55    /// Returns the data of the UUID.
56    pub fn data(&self) -> &[u8; Self::UUID_SIZE] { self.into() }
57
58    /// Get the data of the UUID as a byte slice.
59    pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
60}
61
62/// Implements Default for UUID to create a new random UUID.
63impl Default for UUID {
64    fn default() -> Self { Self::new() }
65}
66
67/// Implements conversion from a UUID reference to a byte array reference.
68impl<'a> From<&'a UUID> for &'a [u8; UUID::UUID_SIZE] {
69    fn from(value: &'a UUID) -> Self { &value.0 }
70}
71
72/// Implements AsRef<[u8]> to allow UUID to be treated as a byte slice.
73impl AsRef<[u8]> for UUID {
74    fn as_ref(&self) -> &[u8] { &self.0 }
75}
76
77/// Implements CBORTagged trait to provide CBOR tag information.
78impl CBORTagged for UUID {
79    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_UUID]) }
80}
81
82/// Implements conversion from UUID to CBOR for serialization.
83impl From<UUID> for CBOR {
84    fn from(value: UUID) -> Self { value.tagged_cbor() }
85}
86
87/// Implements CBORTaggedEncodable to provide CBOR encoding functionality.
88impl CBORTaggedEncodable for UUID {
89    fn untagged_cbor(&self) -> CBOR { CBOR::to_byte_string(self.0) }
90}
91
92/// Implements `TryFrom<CBOR>` for UUID to support conversion from CBOR data.
93impl TryFrom<CBOR> for UUID {
94    type Error = dcbor::Error;
95
96    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
97        Self::from_tagged_cbor(cbor)
98    }
99}
100
101/// Implements CBORTaggedDecodable to provide CBOR decoding functionality.
102impl CBORTaggedDecodable for UUID {
103    fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
104        let bytes = CBOR::try_into_byte_string(cbor)?;
105        if bytes.len() != Self::UUID_SIZE {
106            return Err("invalid UUID size".into());
107        }
108        let mut uuid = [0u8; Self::UUID_SIZE];
109        uuid.copy_from_slice(&bytes);
110        Ok(Self::from_data(uuid))
111    }
112}
113
114/// Implements Display for UUID to format it in the standard UUID string format.
115impl std::fmt::Display for UUID {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        write!(f, "{}", String::from(self))
118    }
119}
120
121/// Implements conversion from UUID to String in the standard format with
122/// dashes.
123impl From<UUID> for String {
124    fn from(uuid: UUID) -> Self {
125        let hex = hex::encode(uuid.0);
126        format!(
127            "{}-{}-{}-{}-{}",
128            &hex[0..8],
129            &hex[8..12],
130            &hex[12..16],
131            &hex[16..20],
132            &hex[20..32]
133        )
134    }
135}
136
137/// Implements conversion from UUID reference to String.
138impl From<&UUID> for String {
139    fn from(uuid: &UUID) -> Self { String::from(*uuid) }
140}
141
142/// Implements string parsing to create a UUID.
143impl FromStr for UUID {
144    type Err = Error;
145
146    fn from_str(s: &str) -> Result<Self, Self::Err> {
147        let s = s.trim();
148        let s = s.replace('-', "");
149        let bytes = hex::decode(s).unwrap();
150        let mut uuid = [0u8; Self::UUID_SIZE];
151        uuid.copy_from_slice(&bytes);
152        Ok(Self::from_data(uuid))
153    }
154}