1use std::{fmt::Display, io::Cursor, str::FromStr};
2
3use binrw::prelude::*;
4use rand::{Rng, rngs::OsRng};
5
6#[derive(BinRead, BinWrite, Clone, Copy, PartialEq, Eq, Default)]
10#[brw(little)]
11pub struct Guid(u32, u16, u16, [u8; 8]);
12
13impl Guid {
14 pub const GUID_SIZE: usize = 16;
16 const _VALIDATE_SIZE_OF: [u8; Self::GUID_SIZE] = [0; size_of::<Self>()];
17
18 pub const ZERO: Guid = Guid(0, 0, 0, [0; 8]);
19
20 pub fn generate() -> Self {
22 let mut rng = OsRng;
23 let mut bytes = [0u8; 16];
24 rng.fill(&mut bytes);
25 Self::try_from(&bytes).unwrap()
26 }
27
28 pub const MAX: Guid = Guid(u32::MAX, u16::MAX, u16::MAX, [u8::MAX; 8]);
30
31 pub const fn parse_uuid(s: &str) -> Result<Guid, &'static str> {
32 use super::util::parse_byte;
33 let b = s.as_bytes();
34 let so = if b[0] == b'{' && b[b.len() - 1] == b'}' {
35 if s.len() != 38 {
36 return Err("Invalid UUID format");
37 }
38 1
39 } else {
40 if s.len() != 36 {
41 return Err("Invalid UUID format");
42 }
43 0
44 };
45 if b[so + 8] != b'-' || b[so + 13] != b'-' || b[so + 18] != b'-' || b[so + 23] != b'-' {
46 return Err("Invalid UUID format");
47 }
48
49 macro_rules! parse_byte {
52 ($b:expr, $i:expr) => {
53 match parse_byte($b, $i) {
54 Ok(val) => val,
55 Err(e) => return Err(e),
56 }
57 };
58 }
59
60 Ok(Guid(
61 u32::from_be_bytes([
62 parse_byte!(b, so),
63 parse_byte!(b, so + 2),
64 parse_byte!(b, so + 4),
65 parse_byte!(b, so + 6),
66 ]),
67 u16::from_be_bytes([parse_byte!(b, so + 9), parse_byte!(b, so + 11)]),
68 u16::from_be_bytes([parse_byte!(b, so + 14), parse_byte!(b, so + 16)]),
69 [
70 parse_byte!(b, so + 19),
71 parse_byte!(b, so + 21),
72 parse_byte!(b, so + 24),
73 parse_byte!(b, so + 26),
74 parse_byte!(b, so + 28),
75 parse_byte!(b, so + 30),
76 parse_byte!(b, so + 32),
77 parse_byte!(b, so + 34),
78 ],
79 ))
80 }
81
82 pub fn as_u128(&self) -> u128 {
84 let mut bytes = [0u8; 16];
85 {
86 let mut cursor = Cursor::new(&mut bytes[..]);
87 self.write(&mut cursor).unwrap();
88 }
89 u128::from_le_bytes(bytes)
90 }
91}
92
93#[macro_export]
103macro_rules! guid {
104 ($s:expr) => {{
105 match $crate::Guid::parse_uuid($s) {
106 Ok(guid) => guid,
107 Err(_) => panic!("Invalid GUID format"),
108 }
109 }};
110}
111
112pub use guid as make_guid;
115
116impl From<[u8; 16]> for Guid {
117 fn from(value: [u8; 16]) -> Self {
118 Self::try_from(&value).unwrap()
119 }
120}
121
122impl TryFrom<&[u8; 16]> for Guid {
123 type Error = binrw::Error;
124
125 fn try_from(value: &[u8; 16]) -> Result<Self, Self::Error> {
126 let mut cursor = Cursor::new(value);
127 Guid::read(&mut cursor)
128 }
129}
130
131impl From<Guid> for [u8; 16] {
132 fn from(val: Guid) -> Self {
133 let mut cursor = Cursor::new(Vec::new());
134 val.write(&mut cursor).unwrap();
135 cursor.into_inner().try_into().unwrap()
136 }
137}
138
139impl FromStr for Guid {
140 type Err = &'static str;
141
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 Guid::parse_uuid(s)
144 }
145}
146
147impl Display for Guid {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 write!(
151 f,
152 "{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:12x}",
153 self.0,
154 self.1,
155 self.2,
156 self.3[0],
157 self.3[1],
158 self.3[2..]
159 .iter()
160 .fold(0u64, |acc, &x| (acc << 8) + x as u64)
161 )
162 }
163}
164
165impl std::fmt::Debug for Guid {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 write!(f, "{self}")
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use smb_tests::*;
174
175 use super::*;
176
177 const TEST_GUID_STR: &str = "065eadf1-6daf-1543-b04f-10e69084c9ae";
178 const PARSED_GUID_VALUE: Guid = Guid(
179 0x065eadf1,
180 0x6daf,
181 0x1543,
182 [0xb0, 0x4f, 0x10, 0xe6, 0x90, 0x84, 0xc9, 0xae],
183 );
184 const TEST_GUID_BYTES: &'static str = "f1ad5e06af6d4315b04f10e69084c9ae";
185
186 #[test]
187 pub fn test_guid_parse_runtime() {
188 let guid = TEST_GUID_STR.parse::<Guid>().unwrap();
189 assert_eq!(guid, PARSED_GUID_VALUE);
190 assert_eq!(guid.to_string(), TEST_GUID_STR);
191 }
192
193 #[test]
194 pub fn test_const_guid() {
195 assert_eq!(make_guid!(TEST_GUID_STR), PARSED_GUID_VALUE);
196 assert_eq!(
197 make_guid!(format!("{{{TEST_GUID_STR}}}").as_str()),
198 PARSED_GUID_VALUE
199 );
200 }
201
202 test_binrw! {
203 Guid: PARSED_GUID_VALUE => TEST_GUID_BYTES
204 }
205}