1use std::fmt;
15
16use super::errors::ParseError;
17use super::{bytes_to_u16_be, bytes_to_u16_le, bytes_to_u32_be, bytes_to_u32_le};
18
19#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
21pub struct Uuid {
22 pub part1: u32,
23 pub part2: u16,
24 pub part3: u16,
25 pub part4: u16,
26 pub part5: u64,
27}
28
29impl Uuid {
30 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn from_be_bytes(data: &[u8]) -> Self {
37 let part5_upper: u16 = bytes_to_u16_be!(data, 10);
38 let part5_lower: u32 = bytes_to_u32_be!(data, 12);
39 Self {
40 part1: bytes_to_u32_be!(data, 0),
41 part2: bytes_to_u16_be!(data, 4),
42 part3: bytes_to_u16_be!(data, 6),
43 part4: bytes_to_u16_be!(data, 8),
44 part5: ((part5_upper as u64) << 32) | (part5_lower as u64),
45 }
46 }
47
48 pub fn from_le_bytes(data: &[u8]) -> Self {
50 let part5_upper: u16 = bytes_to_u16_be!(data, 10);
51 let part5_lower: u32 = bytes_to_u32_be!(data, 12);
52 Self {
53 part1: bytes_to_u32_le!(data, 0),
54 part2: bytes_to_u16_le!(data, 4),
55 part3: bytes_to_u16_le!(data, 6),
56 part4: bytes_to_u16_be!(data, 8),
57 part5: ((part5_upper as u64) << 32) | (part5_lower as u64),
58 }
59 }
60
61 pub fn is_max(&self) -> bool {
63 self.part1 == 0xffffffff
64 && self.part2 == 0xffff
65 && self.part3 == 0xffff
66 && self.part4 == 0xffff
67 && self.part5 == 0xffffffffffff
68 }
69
70 pub fn is_nil(&self) -> bool {
72 self.part1 == 0 && self.part2 == 0 && self.part3 == 0 && self.part4 == 0 && self.part5 == 0
73 }
74
75 pub fn from_string(&mut self, mut string: &str) -> Result<(), ParseError> {
77 let mut string_length: usize = string.len();
78
79 if string.starts_with("{") && string.ends_with("}") {
80 string = &string[1..string_length - 1];
81 string_length -= 2;
82 }
83 if string_length != 36 {
84 return Err(ParseError::new(format!("Unsupported string length")));
85 }
86 if &string[8..9] != "-"
87 || &string[13..14] != "-"
88 || &string[18..19] != "-"
89 || &string[23..24] != "-"
90 {
91 return Err(ParseError::new(format!("Unsupported string")));
92 }
93 self.part1 = match u32::from_str_radix(&string[0..8], 16) {
94 Ok(value) => value,
95 Err(_) => {
96 return Err(ParseError::new(format!(
97 "Unable to parse part1: {}",
98 &string[0..8]
99 )));
100 }
101 };
102 self.part2 = match u16::from_str_radix(&string[9..13], 16) {
103 Ok(value) => value,
104 Err(_) => {
105 return Err(ParseError::new(format!(
106 "Unable to parse part2: {}",
107 &string[9..13]
108 )));
109 }
110 };
111 self.part3 = match u16::from_str_radix(&string[14..18], 16) {
112 Ok(value) => value,
113 Err(_) => {
114 return Err(ParseError::new(format!(
115 "Unable to parse part3: {}",
116 &string[14..18]
117 )));
118 }
119 };
120 self.part4 = match u16::from_str_radix(&string[19..23], 16) {
121 Ok(value) => value,
122 Err(_) => {
123 return Err(ParseError::new(format!(
124 "Unable to parse part4: {}",
125 &string[19..24]
126 )));
127 }
128 };
129 self.part5 = match u64::from_str_radix(&string[24..36], 16) {
130 Ok(value) => value,
131 Err(_) => {
132 return Err(ParseError::new(format!(
133 "Unable to parse part5: {}",
134 &string[24..36]
135 )));
136 }
137 };
138 Ok(())
139 }
140
141 pub fn to_string(&self) -> String {
143 format!(
144 "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
145 self.part1, self.part2, self.part3, self.part4, self.part5,
146 )
147 }
148}
149
150impl fmt::Display for Uuid {
151 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
153 write!(formatter, "{}", self.to_string())
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_from_be_bytes() {
163 let test_data: [u8; 16] = [
164 0xb6, 0x1f, 0x53, 0xca, 0xa7, 0x86, 0x45, 0x28, 0x90, 0xe2, 0x55, 0xba, 0x79, 0x1a,
165 0x1c, 0x4c,
166 ];
167
168 let uuid: Uuid = Uuid::from_be_bytes(&test_data);
169 assert_eq!(uuid.part1, 0xb61f53ca);
170 assert_eq!(uuid.part2, 0xa786);
171 assert_eq!(uuid.part3, 0x4528);
172 assert_eq!(uuid.part4, 0x90e2);
173 assert_eq!(uuid.part5, 0x55ba791a1c4c);
174 }
175
176 #[test]
177 fn test_from_le_bytes() {
178 let test_data: [u8; 16] = [
179 0xca, 0x53, 0x1f, 0xb6, 0x86, 0xa7, 0x28, 0x45, 0x90, 0xe2, 0x55, 0xba, 0x79, 0x1a,
180 0x1c, 0x4c,
181 ];
182
183 let uuid: Uuid = Uuid::from_le_bytes(&test_data);
184 assert_eq!(uuid.part1, 0xb61f53ca);
185 assert_eq!(uuid.part2, 0xa786);
186 assert_eq!(uuid.part3, 0x4528);
187 assert_eq!(uuid.part4, 0x90e2);
188 assert_eq!(uuid.part5, 0x55ba791a1c4c);
189 }
190
191 #[test]
192 fn test_is_max() {
193 let test_data: [u8; 16] = [
194 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
195 0xff, 0xff,
196 ];
197
198 let uuid: Uuid = Uuid::from_le_bytes(&test_data);
199 assert_eq!(uuid.is_max(), true);
200 }
201
202 #[test]
203 fn test_is_not_max() {
204 let test_data: [u8; 16] = [
205 0xca, 0x53, 0x1f, 0xb6, 0x86, 0xa7, 0x28, 0x45, 0x90, 0xe2, 0x55, 0xba, 0x79, 0x1a,
206 0x1c, 0x4c,
207 ];
208
209 let uuid: Uuid = Uuid::from_le_bytes(&test_data);
210 assert_eq!(uuid.is_max(), false);
211 }
212
213 #[test]
214 fn test_is_nil() {
215 let uuid: Uuid = Uuid::new();
216 assert_eq!(uuid.is_nil(), true);
217 }
218
219 #[test]
220 fn test_is_not_nil() {
221 let test_data: [u8; 16] = [
222 0xca, 0x53, 0x1f, 0xb6, 0x86, 0xa7, 0x28, 0x45, 0x90, 0xe2, 0x55, 0xba, 0x79, 0x1a,
223 0x1c, 0x4c,
224 ];
225
226 let uuid: Uuid = Uuid::from_le_bytes(&test_data);
227 assert_eq!(uuid.is_nil(), false);
228 }
229
230 #[test]
231 fn test_from_string() -> Result<(), ParseError> {
232 let mut uuid: Uuid = Uuid::new();
233 uuid.from_string("{b61f53ca-a786-4528-90e2-55ba791a1c4c}")?;
234
235 assert_eq!(uuid.part1, 0xb61f53ca);
236 assert_eq!(uuid.part2, 0xa786);
237 assert_eq!(uuid.part3, 0x4528);
238 assert_eq!(uuid.part4, 0x90e2);
239 assert_eq!(uuid.part5, 0x55ba791a1c4c);
240
241 Ok(())
242 }
243
244 #[test]
245 fn test_to_string() {
246 let test_data: [u8; 16] = [
247 0xca, 0x53, 0x1f, 0xb6, 0x86, 0xa7, 0x28, 0x45, 0x90, 0xe2, 0x55, 0xba, 0x79, 0x1a,
248 0x1c, 0x4c,
249 ];
250
251 let uuid: Uuid = Uuid::from_le_bytes(&test_data);
252 let uuid_string: String = uuid.to_string();
253 assert_eq!(uuid_string, "b61f53ca-a786-4528-90e2-55ba791a1c4c");
254 }
255}