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