1use crate::{
17 error::*,
18 std::{convert::TryFrom, str},
19 Uuid,
20};
21
22#[cfg(feature = "std")]
23use crate::std::string::String;
24
25impl str::FromStr for Uuid {
26 type Err = Error;
27
28 fn from_str(uuid_str: &str) -> Result<Self, Self::Err> {
29 Uuid::parse_str(uuid_str)
30 }
31}
32
33impl TryFrom<&'_ str> for Uuid {
34 type Error = Error;
35
36 fn try_from(uuid_str: &'_ str) -> Result<Self, Self::Error> {
37 Uuid::parse_str(uuid_str)
38 }
39}
40
41#[cfg(feature = "std")]
42impl TryFrom<String> for Uuid {
43 type Error = Error;
44
45 fn try_from(uuid_str: String) -> Result<Self, Self::Error> {
46 Uuid::try_from(uuid_str.as_ref())
47 }
48}
49
50impl Uuid {
51 pub fn parse_str(input: &str) -> Result<Uuid, Error> {
77 try_parse(input.as_bytes())
78 .map(Uuid::from_bytes)
79 .map_err(InvalidUuid::into_err)
80 }
81
82 pub const fn try_parse(input: &str) -> Result<Uuid, Error> {
111 Self::try_parse_ascii(input.as_bytes())
112 }
113
114 pub const fn try_parse_ascii(input: &[u8]) -> Result<Uuid, Error> {
138 match try_parse(input) {
139 Ok(bytes) => Ok(Uuid::from_bytes(bytes)),
140 Err(_) => Err(Error(ErrorKind::ParseOther)),
143 }
144 }
145}
146
147const fn try_parse(input: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
148 match (input.len(), input) {
149 (32, s) => parse_simple(s, true),
151 (36, s) | (38, [b'{', s @ .., b'}']) => parse_hyphenated(s),
156 (45, s) => {
157 let (prefix, s) = s.split_at(9);
158
159 if prefix.eq_ignore_ascii_case(b"urn:uuid:") {
160 parse_hyphenated(s)
161 } else {
162 Err(InvalidUuid(input, RequestedUuid::Any))
163 }
164 }
165 _ => Err(InvalidUuid(input, RequestedUuid::Any)),
167 }
168}
169
170#[inline]
171#[allow(dead_code)]
172pub(crate) const fn parse_braced(input: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
173 if let (38, [b'{', s @ .., b'}']) = (input.len(), input) {
174 parse_hyphenated(s)
175 } else {
176 Err(InvalidUuid(input, RequestedUuid::Braced))
177 }
178}
179
180#[inline]
181#[allow(dead_code)]
182pub(crate) const fn parse_urn(input: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
183 if input.len() == 45 && input.split_at(9).0.eq_ignore_ascii_case(b"urn:uuid:") {
184 parse_hyphenated(input.split_at(9).1)
185 } else {
186 Err(InvalidUuid(input, RequestedUuid::Urn))
187 }
188}
189
190#[inline]
191pub(crate) const fn parse_simple(
192 s: &'_ [u8],
193 speculative: bool,
194) -> Result<[u8; 16], InvalidUuid<'_>> {
195 if s.len() != 32 {
198 return Err(InvalidUuid(
199 s,
200 if speculative {
201 RequestedUuid::Any
202 } else {
203 RequestedUuid::Simple
204 },
205 ));
206 }
207
208 let mut hex = [0u8; 32];
211 let mut i = 0;
212 while i < 32 {
213 hex[i] = s[i];
214 i += 1;
215 }
216
217 match decode_hex32(&hex) {
218 Some(buf) => Ok(buf),
219 None => Err(InvalidUuid(
220 s,
221 if speculative {
222 RequestedUuid::Any
223 } else {
224 RequestedUuid::Simple
225 },
226 )),
227 }
228}
229
230#[inline]
231pub(crate) const fn parse_hyphenated(s: &'_ [u8]) -> Result<[u8; 16], InvalidUuid<'_>> {
232 if s.len() != 36 {
235 return Err(InvalidUuid(s, RequestedUuid::Hyphenated));
236 }
237
238 match [s[8], s[13], s[18], s[23]] {
248 [b'-', b'-', b'-', b'-'] => {}
249 _ => return Err(InvalidUuid(s, RequestedUuid::Hyphenated)),
250 }
251
252 let mut hex = [0u8; 32];
255 let mut i = 0;
256 while i < 8 {
257 hex[i] = s[i];
258 i += 1;
259 }
260 while i < 12 {
261 hex[i] = s[i + 1];
262 i += 1;
263 }
264 while i < 16 {
265 hex[i] = s[i + 2];
266 i += 1;
267 }
268 while i < 20 {
269 hex[i] = s[i + 3];
270 i += 1;
271 }
272 while i < 32 {
273 hex[i] = s[i + 4];
274 i += 1;
275 }
276
277 match decode_hex32(&hex) {
278 Some(buf) => Ok(buf),
279 None => Err(InvalidUuid(s, RequestedUuid::Hyphenated)),
280 }
281}
282
283#[inline]
286const fn decode_hex32(hex: &[u8; 32]) -> Option<[u8; 16]> {
287 let mut nibbles = [0u8; 32];
288 let mut bad = 0u8;
289
290 let mut i = 0;
291 while i < 32 {
292 let c = hex[i];
293
294 let digit = c.wrapping_sub(b'0');
297 let alpha = (c | 0x20).wrapping_sub(b'a');
298
299 let is_digit = digit < 10;
300 let is_alpha = alpha < 6;
301
302 nibbles[i] = if is_digit {
303 digit
304 } else {
305 alpha.wrapping_add(10)
306 };
307
308 bad |= if is_digit | is_alpha { 0 } else { 1 };
309
310 i += 1;
311 }
312
313 if bad != 0 {
314 return None;
315 }
316
317 let mut buf = [0u8; 16];
318 let mut j = 0;
319 while j < 16 {
320 buf[j] = (nibbles[j * 2] << 4) | nibbles[j * 2 + 1];
321 j += 1;
322 }
323
324 Some(buf)
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330 use crate::{
331 fmt::*,
332 std::{str::FromStr, string::ToString},
333 tests::some_uuid_iter,
334 };
335
336 #[test]
337 fn test_parse_valid() {
338 let from_hyphenated = Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
339 let from_simple = Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c8").unwrap();
340 let from_urn = Uuid::parse_str("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
341 let from_guid = Uuid::parse_str("{67e55044-10b1-426f-9247-bb680e5fe0c8}").unwrap();
342
343 assert_eq!(from_hyphenated, from_simple);
344 assert_eq!(from_hyphenated, from_urn);
345 assert_eq!(from_hyphenated, from_guid);
346
347 assert!(Uuid::parse_str("00000000000000000000000000000000").is_ok());
348 assert!(Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
349 assert!(Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok());
350 assert!(Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c8").is_ok());
351 assert!(Uuid::parse_str("01020304-1112-2122-3132-414243444546").is_ok());
352 assert!(Uuid::parse_str("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
353 assert!(Uuid::parse_str("URN:UUID:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
354 assert!(Uuid::parse_str("Urn:Uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
355 assert!(Uuid::parse_str("{6d93bade-bd9f-4e13-8914-9474e1e3567b}").is_ok());
356
357 assert_eq!(
358 from_urn,
359 Uuid::parse_str("URN:UUID:67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap()
360 );
361 assert_eq!(
362 from_urn,
363 Uuid::parse_str("urn:UUID:67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap()
364 );
365
366 let nil = Uuid::nil();
368 assert_eq!(
369 Uuid::parse_str("00000000000000000000000000000000").unwrap(),
370 nil
371 );
372 assert_eq!(
373 Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(),
374 nil
375 );
376 }
377
378 #[test]
379 fn test_parse_invalid() {
380 assert_eq!(
382 Uuid::parse_str(""),
383 Err(Error(ErrorKind::ParseLength { len: 0 }))
384 );
385
386 assert_eq!(
387 Uuid::parse_str("{}"),
388 Err(Error(ErrorKind::ParseGroupCount { count: 1 }))
389 );
390
391 assert_eq!(
392 Uuid::parse_str("!"),
393 Err(Error(ErrorKind::ParseChar {
394 character: '!',
395 index: 0,
396 }))
397 );
398
399 assert_eq!(
400 Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45"),
401 Err(Error(ErrorKind::ParseGroupLength {
402 group: 4,
403 len: 13,
404 index: 25,
405 }))
406 );
407
408 assert_eq!(
409 Uuid::parse_str("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4"),
410 Err(Error(ErrorKind::ParseGroupLength {
411 group: 3,
412 len: 3,
413 index: 20,
414 }))
415 );
416
417 assert_eq!(
418 Uuid::parse_str("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4"),
419 Err(Error(ErrorKind::ParseChar {
420 character: 'G',
421 index: 20,
422 }))
423 );
424
425 assert_eq!(
426 Uuid::parse_str("F9168C5E-CEB2F4faaFB6BFF329BF39FA1E4"),
427 Err(Error(ErrorKind::ParseGroupCount { count: 2 }))
428 );
429
430 assert_eq!(
431 Uuid::parse_str("F9168C5E-CEB2-4faaFB6BFF329BF39FA1E4"),
432 Err(Error(ErrorKind::ParseGroupCount { count: 3 }))
433 );
434
435 assert_eq!(
436 Uuid::parse_str("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4"),
437 Err(Error(ErrorKind::ParseGroupCount { count: 4 }))
438 );
439
440 assert_eq!(
441 Uuid::parse_str("F9168C5E-CEB2-4faa"),
442 Err(Error(ErrorKind::ParseGroupCount { count: 3 }))
443 );
444
445 assert_eq!(
446 Uuid::parse_str("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4"),
447 Err(Error(ErrorKind::ParseChar {
448 character: 'X',
449 index: 18,
450 }))
451 );
452
453 assert_eq!(
454 Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41"),
455 Err(Error(ErrorKind::ParseChar {
456 character: '{',
457 index: 0,
458 }))
459 );
460
461 assert_eq!(
462 Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41}"),
463 Err(Error(ErrorKind::ParseGroupCount { count: 3 }))
464 );
465
466 assert_eq!(
467 Uuid::parse_str("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4"),
468 Err(Error(ErrorKind::ParseGroupLength {
469 group: 1,
470 len: 3,
471 index: 10,
472 }))
473 );
474
475 assert_eq!(
477 Uuid::parse_str("01020304-1112-2122-3132-41424344"),
478 Err(Error(ErrorKind::ParseGroupLength {
479 group: 4,
480 len: 8,
481 index: 25,
482 }))
483 );
484
485 assert_eq!(
486 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c"),
487 Err(Error(ErrorKind::ParseLength { len: 31 }))
488 );
489
490 assert_eq!(
491 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c88"),
492 Err(Error(ErrorKind::ParseLength { len: 33 }))
493 );
494
495 assert_eq!(
496 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0cg8"),
497 Err(Error(ErrorKind::ParseChar {
498 character: 'g',
499 index: 31,
500 }))
501 );
502
503 assert_eq!(
504 Uuid::parse_str("67e5504410b1426%9247bb680e5fe0c8"),
505 Err(Error(ErrorKind::ParseChar {
506 character: '%',
507 index: 15,
508 }))
509 );
510
511 assert_eq!(
512 Uuid::parse_str("231231212212423424324323477343246663"),
513 Err(Error(ErrorKind::ParseGroupCount { count: 1 }))
514 );
515
516 assert_eq!(
517 Uuid::parse_str("{00000000000000000000000000000000}"),
518 Err(Error(ErrorKind::ParseGroupCount { count: 1 }))
519 );
520
521 assert_eq!(
522 Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c"),
523 Err(Error(ErrorKind::ParseLength { len: 31 }))
524 );
525
526 assert_eq!(
527 Uuid::parse_str("67e550X410b1426f9247bb680e5fe0cd"),
528 Err(Error(ErrorKind::ParseChar {
529 character: 'X',
530 index: 6,
531 }))
532 );
533
534 assert_eq!(
535 Uuid::parse_str("67e550-4105b1426f9247bb680e5fe0c"),
536 Err(Error(ErrorKind::ParseGroupCount { count: 2 }))
537 );
538
539 assert_eq!(
540 Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4"),
541 Err(Error(ErrorKind::ParseGroupLength {
542 group: 3,
543 len: 5,
544 index: 20,
545 }))
546 );
547
548 assert_eq!(
549 Uuid::parse_str("\u{bcf3c}"),
550 Err(Error(ErrorKind::ParseChar {
551 character: '\u{bcf3c}',
552 index: 0,
553 }))
554 );
555
556 assert_eq!(
557 Uuid::parse_str("\u{130}"),
558 Err(Error(ErrorKind::ParseChar {
559 character: '\u{130}',
560 index: 0,
561 }))
562 );
563
564 assert_eq!(
565 Err(Error(ErrorKind::ParseLength { len: 0 })),
566 Hyphenated::from_str("")
567 );
568
569 assert_eq!(
570 Err(Error(ErrorKind::ParseGroupCount { count: 1 })),
571 Hyphenated::from_str("550e8400e29b41d4a716446655440000")
572 );
573
574 assert_eq!(
575 Err(Error(ErrorKind::ParseChar {
576 character: '-',
577 index: 8
578 })),
579 Simple::from_str("550e8400-e29b-41d4-a716-446655440000")
580 );
581
582 assert_eq!(
583 Err(Error(ErrorKind::ParseChar {
584 character: '5',
585 index: 0
586 })),
587 Urn::from_str("550e8400-e29b-41d4-a716-446655440000")
588 );
589 assert_eq!(
590 Err(Error(ErrorKind::ParseChar {
591 character: ':',
592 index: 0
593 })),
594 Urn::from_str(":550e8400-e29b-41d4-a716-446655440000")
595 );
596 assert_eq!(
597 Uuid::parse_str("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap(),
598 Urn::from_str("URN:UUID:67e55044-10b1-426f-9247-bb680e5fe0c8")
599 .unwrap()
600 .into_uuid()
601 );
602
603 assert_eq!(
604 Err(Error(ErrorKind::ParseChar {
605 character: '5',
606 index: 0
607 })),
608 Braced::from_str("550e8400-e29b-41d4-a716-446655440000")
609 );
610 assert_eq!(
611 Err(Error(ErrorKind::ParseChar {
612 character: '{',
613 index: 1
614 })),
615 Braced::from_str("{{550e8400-e29b-41d4-a716-446655440000}}")
616 );
617
618 assert_eq!(
620 Uuid::from_str("{6e0----------9=4O-0e5\u{14}e0c4\u{ec2f}8}"),
621 Err(Error(ErrorKind::ParseChar {
622 character: '=',
623 index: 15,
624 }))
625 );
626
627 assert_eq!(
628 Uuid::from_str("urn:uuid:urae0c8"),
629 Err(Error(ErrorKind::ParseChar {
630 character: 'u',
631 index: 9,
632 }))
633 );
634 }
635
636 #[test]
637 fn test_roundtrip_default() {
638 for uuid_orig in some_uuid_iter() {
639 let orig_str = uuid_orig.to_string();
640 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
641 assert_eq!(uuid_orig, uuid_out);
642 }
643 }
644
645 #[test]
646 fn test_roundtrip_hyphenated() {
647 for uuid_orig in some_uuid_iter() {
648 let orig_str = uuid_orig.hyphenated().to_string();
649 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
650 assert_eq!(uuid_orig, uuid_out);
651 }
652 }
653
654 #[test]
655 fn test_roundtrip_simple() {
656 for uuid_orig in some_uuid_iter() {
657 let orig_str = uuid_orig.simple().to_string();
658 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
659 assert_eq!(uuid_orig, uuid_out);
660 }
661 }
662
663 #[test]
664 fn test_roundtrip_urn() {
665 for uuid_orig in some_uuid_iter() {
666 let orig_str = uuid_orig.urn().to_string();
667 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
668 assert_eq!(uuid_orig, uuid_out);
669 }
670 }
671
672 #[test]
673 fn test_roundtrip_braced() {
674 for uuid_orig in some_uuid_iter() {
675 let orig_str = uuid_orig.braced().to_string();
676 let uuid_out = Uuid::parse_str(&orig_str).unwrap();
677 assert_eq!(uuid_orig, uuid_out);
678 }
679 }
680
681 #[test]
682 fn test_roundtrip_parse_urn() {
683 for uuid_orig in some_uuid_iter() {
684 let orig_str = uuid_orig.urn().to_string();
685 let uuid_out = Uuid::from_bytes(parse_urn(orig_str.as_bytes()).unwrap());
686 assert_eq!(uuid_orig, uuid_out);
687 }
688 }
689
690 #[test]
691 fn test_roundtrip_parse_braced() {
692 for uuid_orig in some_uuid_iter() {
693 let orig_str = uuid_orig.braced().to_string();
694 let uuid_out = Uuid::from_bytes(parse_braced(orig_str.as_bytes()).unwrap());
695 assert_eq!(uuid_orig, uuid_out);
696 }
697 }
698
699 #[test]
700 fn test_try_parse_ascii_non_utf8() {
701 assert!(Uuid::try_parse_ascii(b"67e55044-10b1-426f-9247-bb680e5\0e0c8").is_err());
702 }
703}