1use crate::{
17 error::*,
18 std::{convert::TryFrom, str},
19 Uuid,
20};
21
22impl str::FromStr for Uuid {
23 type Err = Error;
24
25 fn from_str(uuid_str: &str) -> Result<Self, Self::Err> {
26 Uuid::parse_str(uuid_str)
27 }
28}
29
30impl TryFrom<&'_ str> for Uuid {
31 type Error = Error;
32
33 fn try_from(uuid_str: &'_ str) -> Result<Self, Self::Error> {
34 Uuid::parse_str(uuid_str)
35 }
36}
37
38impl Uuid {
39 pub fn parse_str(input: &str) -> Result<Uuid, Error> {
65 try_parse(input.as_bytes())
66 .map(Uuid::from_bytes)
67 .map_err(InvalidUuid::into_err)
68 }
69
70 pub const fn try_parse(input: &str) -> Result<Uuid, Error> {
99 Self::try_parse_ascii(input.as_bytes())
100 }
101
102 pub const fn try_parse_ascii(input: &[u8]) -> Result<Uuid, Error> {
126 match try_parse(input) {
127 Ok(bytes) => Ok(Uuid::from_bytes(bytes)),
128 Err(_) => Err(Error(ErrorKind::Other)),
131 }
132 }
133}
134
135const fn try_parse(input: &[u8]) -> Result<[u8; 16], InvalidUuid> {
136 match (input.len(), input) {
137 (32, s) => parse_simple(s),
139 (36, s)
144 | (38, [b'{', s @ .., b'}'])
145 | (45, [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..]) => {
146 parse_hyphenated(s)
147 }
148 _ => Err(InvalidUuid(input)),
150 }
151}
152
153#[inline]
154#[allow(dead_code)]
155pub(crate) const fn parse_braced(input: &[u8]) -> Result<[u8; 16], InvalidUuid> {
156 if let (38, [b'{', s @ .., b'}']) = (input.len(), input) {
157 parse_hyphenated(s)
158 } else {
159 Err(InvalidUuid(input))
160 }
161}
162
163#[inline]
164#[allow(dead_code)]
165pub(crate) const fn parse_urn(input: &[u8]) -> Result<[u8; 16], InvalidUuid> {
166 if let (45, [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..]) =
167 (input.len(), input)
168 {
169 parse_hyphenated(s)
170 } else {
171 Err(InvalidUuid(input))
172 }
173}
174
175#[inline]
176pub(crate) const fn parse_simple(s: &[u8]) -> Result<[u8; 16], InvalidUuid> {
177 if s.len() != 32 {
180 return Err(InvalidUuid(s));
181 }
182
183 let mut buf: [u8; 16] = [0; 16];
184 let mut i = 0;
185
186 while i < 16 {
187 let h1 = HEX_TABLE[s[i * 2] as usize];
190 let h2 = HEX_TABLE[s[i * 2 + 1] as usize];
191
192 if h1 | h2 == 0xff {
195 return Err(InvalidUuid(s));
196 }
197
198 buf[i] = SHL4_TABLE[h1 as usize] | h2;
201 i += 1;
202 }
203
204 Ok(buf)
205}
206
207#[inline]
208const fn parse_hyphenated(s: &[u8]) -> Result<[u8; 16], InvalidUuid> {
209 if s.len() != 36 {
212 return Err(InvalidUuid(s));
213 }
214
215 match [s[8], s[13], s[18], s[23]] {
226 [b'-', b'-', b'-', b'-'] => {}
227 _ => return Err(InvalidUuid(s)),
228 }
229
230 let positions: [u8; 8] = [0, 4, 9, 14, 19, 24, 28, 32];
231 let mut buf: [u8; 16] = [0; 16];
232 let mut j = 0;
233
234 while j < 8 {
235 let i = positions[j];
236
237 let h1 = HEX_TABLE[s[i as usize] as usize];
240 let h2 = HEX_TABLE[s[(i + 1) as usize] as usize];
241 let h3 = HEX_TABLE[s[(i + 2) as usize] as usize];
242 let h4 = HEX_TABLE[s[(i + 3) as usize] as usize];
243
244 if h1 | h2 | h3 | h4 == 0xff {
245 return Err(InvalidUuid(s));
246 }
247
248 buf[j * 2] = SHL4_TABLE[h1 as usize] | h2;
249 buf[j * 2 + 1] = SHL4_TABLE[h3 as usize] | h4;
250 j += 1;
251 }
252
253 Ok(buf)
254}
255
256const HEX_TABLE: &[u8; 256] = &{
257 let mut buf = [0; 256];
258 let mut i: u8 = 0;
259
260 loop {
261 buf[i as usize] = match i {
262 b'0'..=b'9' => i - b'0',
263 b'a'..=b'f' => i - b'a' + 10,
264 b'A'..=b'F' => i - b'A' + 10,
265 _ => 0xff,
266 };
267
268 if i == 255 {
269 break buf;
270 }
271
272 i += 1
273 }
274};
275
276const SHL4_TABLE: &[u8; 256] = &{
277 let mut buf = [0; 256];
278 let mut i: u8 = 0;
279
280 loop {
281 buf[i as usize] = i.wrapping_shl(4);
282
283 if i == 255 {
284 break buf;
285 }
286
287 i += 1;
288 }
289};
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294 use crate::{std::string::ToString, tests::new};
295
296 #[test]
297 fn test_parse_uuid_v4_valid() {
298 let from_hyphenated = Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
299 let from_simple = Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c8").unwrap();
300 let from_urn = Uuid::parse_str("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
301 let from_guid = Uuid::parse_str("{67e55044-10b1-426f-9247-bb680e5fe0c8}").unwrap();
302
303 assert_eq!(from_hyphenated, from_simple);
304 assert_eq!(from_hyphenated, from_urn);
305 assert_eq!(from_hyphenated, from_guid);
306
307 assert!(Uuid::parse_str("00000000000000000000000000000000").is_ok());
308 assert!(Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
309 assert!(Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok());
310 assert!(Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c8").is_ok());
311 assert!(Uuid::parse_str("01020304-1112-2122-3132-414243444546").is_ok());
312 assert!(Uuid::parse_str("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
313 assert!(Uuid::parse_str("{6d93bade-bd9f-4e13-8914-9474e1e3567b}").is_ok());
314
315 let nil = Uuid::nil();
317 assert_eq!(
318 Uuid::parse_str("00000000000000000000000000000000").unwrap(),
319 nil
320 );
321 assert_eq!(
322 Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(),
323 nil
324 );
325 }
326
327 #[test]
328 fn test_parse_uuid_v4_invalid() {
329 assert_eq!(
331 Uuid::parse_str(""),
332 Err(Error(ErrorKind::SimpleLength { len: 0 }))
333 );
334
335 assert_eq!(
336 Uuid::parse_str("!"),
337 Err(Error(ErrorKind::Char {
338 character: '!',
339 index: 1,
340 }))
341 );
342
343 assert_eq!(
344 Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45"),
345 Err(Error(ErrorKind::GroupLength {
346 group: 4,
347 len: 13,
348 index: 25,
349 }))
350 );
351
352 assert_eq!(
353 Uuid::parse_str("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4"),
354 Err(Error(ErrorKind::GroupLength {
355 group: 3,
356 len: 3,
357 index: 20,
358 }))
359 );
360
361 assert_eq!(
362 Uuid::parse_str("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4"),
363 Err(Error(ErrorKind::Char {
364 character: 'G',
365 index: 21,
366 }))
367 );
368
369 assert_eq!(
370 Uuid::parse_str("F9168C5E-CEB2F4faaFB6BFF329BF39FA1E4"),
371 Err(Error(ErrorKind::GroupCount { count: 2 }))
372 );
373
374 assert_eq!(
375 Uuid::parse_str("F9168C5E-CEB2-4faaFB6BFF329BF39FA1E4"),
376 Err(Error(ErrorKind::GroupCount { count: 3 }))
377 );
378
379 assert_eq!(
380 Uuid::parse_str("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4"),
381 Err(Error(ErrorKind::GroupCount { count: 4 }))
382 );
383
384 assert_eq!(
385 Uuid::parse_str("F9168C5E-CEB2-4faa"),
386 Err(Error(ErrorKind::GroupCount { count: 3 }))
387 );
388
389 assert_eq!(
390 Uuid::parse_str("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4"),
391 Err(Error(ErrorKind::Char {
392 character: 'X',
393 index: 19,
394 }))
395 );
396
397 assert_eq!(
398 Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41"),
399 Err(Error(ErrorKind::Char {
400 character: '{',
401 index: 1,
402 }))
403 );
404
405 assert_eq!(
406 Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41}"),
407 Err(Error(ErrorKind::GroupCount { count: 3 }))
408 );
409
410 assert_eq!(
411 Uuid::parse_str("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4"),
412 Err(Error(ErrorKind::GroupLength {
413 group: 1,
414 len: 3,
415 index: 10,
416 }))
417 );
418
419 assert_eq!(
422 Uuid::parse_str("01020304-1112-2122-3132-41424344"),
423 Err(Error(ErrorKind::GroupLength {
424 group: 4,
425 len: 8,
426 index: 25,
427 }))
428 );
429
430 assert_eq!(
431 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c"),
432 Err(Error(ErrorKind::SimpleLength { len: 31 }))
433 );
434
435 assert_eq!(
436 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c88"),
437 Err(Error(ErrorKind::SimpleLength { len: 33 }))
438 );
439
440 assert_eq!(
441 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0cg8"),
442 Err(Error(ErrorKind::Char {
443 character: 'g',
444 index: 32,
445 }))
446 );
447
448 assert_eq!(
449 Uuid::parse_str("67e5504410b1426%9247bb680e5fe0c8"),
450 Err(Error(ErrorKind::Char {
451 character: '%',
452 index: 16,
453 }))
454 );
455
456 assert_eq!(
457 Uuid::parse_str("231231212212423424324323477343246663"),
458 Err(Error(ErrorKind::SimpleLength { len: 36 }))
459 );
460
461 assert_eq!(
462 Uuid::parse_str("{00000000000000000000000000000000}"),
463 Err(Error(ErrorKind::GroupCount { count: 1 }))
464 );
465
466 assert_eq!(
467 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c"),
468 Err(Error(ErrorKind::SimpleLength { len: 31 }))
469 );
470
471 assert_eq!(
472 Uuid::parse_str("67e550X410b1426f9247bb680e5fe0cd"),
473 Err(Error(ErrorKind::Char {
474 character: 'X',
475 index: 7,
476 }))
477 );
478
479 assert_eq!(
480 Uuid::parse_str("67e550-4105b1426f9247bb680e5fe0c"),
481 Err(Error(ErrorKind::GroupCount { count: 2 }))
482 );
483
484 assert_eq!(
485 Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4"),
486 Err(Error(ErrorKind::GroupLength {
487 group: 3,
488 len: 5,
489 index: 20,
490 }))
491 );
492
493 assert_eq!(
494 Uuid::parse_str("\u{bcf3c}"),
495 Err(Error(ErrorKind::Char {
496 character: '\u{bcf3c}',
497 index: 1
498 }))
499 );
500 }
501
502 #[test]
503 fn test_roundtrip_default() {
504 let uuid_orig = new();
505 let orig_str = uuid_orig.to_string();
506 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
507 assert_eq!(uuid_orig, uuid_out);
508 }
509
510 #[test]
511 fn test_roundtrip_hyphenated() {
512 let uuid_orig = new();
513 let orig_str = uuid_orig.hyphenated().to_string();
514 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
515 assert_eq!(uuid_orig, uuid_out);
516 }
517
518 #[test]
519 fn test_roundtrip_simple() {
520 let uuid_orig = new();
521 let orig_str = uuid_orig.simple().to_string();
522 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
523 assert_eq!(uuid_orig, uuid_out);
524 }
525
526 #[test]
527 fn test_roundtrip_urn() {
528 let uuid_orig = new();
529 let orig_str = uuid_orig.urn().to_string();
530 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
531 assert_eq!(uuid_orig, uuid_out);
532 }
533
534 #[test]
535 fn test_roundtrip_braced() {
536 let uuid_orig = new();
537 let orig_str = uuid_orig.braced().to_string();
538 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
539 assert_eq!(uuid_orig, uuid_out);
540 }
541
542 #[test]
543 fn test_roundtrip_parse_urn() {
544 let uuid_orig = new();
545 let orig_str = uuid_orig.urn().to_string();
546 let uuid_out = Uuid::from_bytes(parse_urn(orig_str.as_bytes()).unwrap());
547 assert_eq!(uuid_orig, uuid_out);
548 }
549
550 #[test]
551 fn test_roundtrip_parse_braced() {
552 let uuid_orig = new();
553 let orig_str = uuid_orig.braced().to_string();
554 let uuid_out = Uuid::from_bytes(parse_braced(orig_str.as_bytes()).unwrap());
555 assert_eq!(uuid_orig, uuid_out);
556 }
557
558 #[test]
559 fn test_try_parse_ascii_non_utf8() {
560 assert!(Uuid::try_parse_ascii(b"67e55044-10b1-426f-9247-bb680e5\0e0c8").is_err());
561 }
562}