1use std::cmp::Ordering;
4use std::fmt;
5use std::str::FromStr;
6use std::sync::atomic::{self, AtomicUsize};
7use std::time::SystemTime;
8
9use chrono::{DateTime, Datelike, Timelike, Utc};
10use quickcheck::{Arbitrary, Gen};
11use rand::Rng;
12
13static UUID_NODE_ID: AtomicUsize = AtomicUsize::new(0);
14static UUID_SEQUENCE: AtomicUsize = AtomicUsize::new(0);
15
16#[derive(Clone, Copy, PartialEq, Eq, Debug)]
17enum Scheme {
18 Name,
19 Event,
20 Number,
21 Derived,
22}
23
24impl fmt::Display for Scheme {
25 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26 match self {
27 Scheme::Name => f.write_str("$"),
28 Scheme::Derived => f.write_str("-"),
29 Scheme::Number => f.write_str("%"),
30 Scheme::Event => f.write_str("+"),
31 }
32 }
33}
34
35#[derive(Debug)]
36enum ParserState {
37 Start,
38 NameOrValue { char_index: usize, partial: u64, is_full: bool },
39 Sign { value: u64, is_full: bool },
40 Origin { value: u64, partial: u64, char_index: usize, scheme: Scheme },
41}
42
43#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
61pub enum UUID {
62 Name {
66 name: u64,
68 scope: u64,
70 },
71
72 Number {
75 value1: u64,
77 value2: u64,
79 },
80
81 Event {
84 timestamp: u64,
86 origin: u64,
88 },
89
90 Derived {
92 timestamp: u64,
94 origin: u64,
96 },
97}
98
99pub const BASE_PUNCT: &'static [u8] =
101 b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
102
103fn format_int(value: u64) -> String {
105 if value == 0 {
106 return "0".to_string();
107 }
108
109 let tail = value.trailing_zeros() / 6;
111 let mut result = String::default();
112 for idx in 0..(10 - tail) {
113 let digit = (value >> (9 - idx) * 6) & 0x3f;
114 result.push(BASE_PUNCT[digit as usize] as char);
115 }
116 result
117}
118
119impl UUID {
120 pub fn set_node_id(val: u64) {
123 UUID_NODE_ID.store(val as usize, atomic::Ordering::Relaxed);
124 }
125
126 pub fn node_id() -> u64 {
128 UUID_NODE_ID.load(atomic::Ordering::Relaxed) as u64
129 }
130
131 pub fn now() -> Self {
135 UUID::Event { origin: Self::node_id(), timestamp: Self::timestamp() }
136 }
137
138 pub fn zero() -> Self {
140 UUID::Name { name: 0, scope: 0 }
141 }
142
143 pub fn is_name(&self) -> bool {
145 match self {
146 &UUID::Name { .. } => true,
147 _ => false,
148 }
149 }
150
151 pub fn is_number(&self) -> bool {
153 match self {
154 &UUID::Number { .. } => true,
155 _ => false,
156 }
157 }
158
159 pub fn is_event(&self) -> bool {
161 match self {
162 &UUID::Event { .. } => true,
163 _ => false,
164 }
165 }
166
167 pub fn is_derived(&self) -> bool {
169 match self {
170 &UUID::Derived { .. } => true,
171 _ => false,
172 }
173 }
174
175 pub fn is_zero(&self) -> bool {
177 self.high() == 0 && self.low() == 0
178 }
179
180 pub fn weak_cmp(a: &UUID, b: &UUID) -> Ordering {
182 if a.high() == b.high() {
183 a.low().cmp(&b.low())
184 } else {
185 a.high().cmp(&b.high())
186 }
187 }
188
189 pub fn parse<'a>(
192 input: &'a str, context: Option<(&UUID, &UUID)>,
193 ) -> Option<(UUID, &'a str)> {
194 let mut state = ParserState::Start;
195 let mut prev = context.map(|x| x.0);
196
197 for (off, ch) in input.char_indices() {
198 match ch as u8 {
199 b'0'...b'9' | b'A'...b'Z' | b'_' | b'a'...b'z' | b'~' => {
201 let val = match ch as u8 {
202 ch @ b'0'...b'9' => ch - b'0',
203 ch @ b'A'...b'Z' => ch - b'A' + 10,
204 b'_' => 36,
205 ch @ b'a'...b'z' => ch - b'a' + 37,
206 b'~' => 63,
207 _ => unreachable!(),
208 };
209
210 match state {
211 ParserState::Start => {
212 state = ParserState::NameOrValue {
213 char_index: 8,
214 partial: (val as u64) << (9 * 6),
215 is_full: true,
216 };
217 }
218 ParserState::NameOrValue {
219 partial,
220 char_index,
221 is_full,
222 } if char_index > 0 => {
223 state = ParserState::NameOrValue {
224 char_index: char_index - 1,
225 partial: partial
226 | ((val as u64) << (char_index * 6)),
227 is_full: is_full,
228 };
229 }
230 ParserState::NameOrValue {
231 partial,
232 char_index: 0,
233 is_full,
234 } => {
235 state = ParserState::Sign {
236 value: partial | (val as u64),
237 is_full: is_full,
238 };
239 }
240 ParserState::Origin {
241 value,
242 scheme,
243 partial,
244 char_index,
245 } if char_index > 0 => {
246 state = ParserState::Origin {
247 value: value,
248 scheme: scheme,
249 char_index: char_index - 1,
250 partial: partial
251 | ((val as u64) << (char_index * 6)),
252 };
253 }
254 ParserState::Origin {
255 value,
256 scheme,
257 partial,
258 char_index: 0,
259 } => {
260 let lo = partial | (val as u64);
261 let uu = UUID::new(value, lo, scheme);
262 return Some((uu, &input[off + 1..]));
263 }
264
265 _ => {
266 return None;
267 }
268 }
269 }
270 b'(' | b'[' | b'{' | b'}' | b']' | b')' if prev.is_some() => {
272 let prev = prev.unwrap();
273 let (mask, ch) = match ch as u8 {
274 b'(' => (0xffffff000000000, 5),
275 b'[' => (0xfffffffc0000000, 6),
276 b'{' => (0xfffffffff000000, 7),
277 b'}' => (0xffffffffffc0000, 8),
278 b']' => (0xffffffffffff000, 9),
279 b')' => (0xfffffffffffffc0, 10),
280 _ => unreachable!(),
281 };
282
283 match state {
284 ParserState::Start => {
285 state = ParserState::NameOrValue {
286 partial: prev.high() & mask,
287 char_index: 10 - ch,
288 is_full: false,
289 };
290 }
291 ParserState::NameOrValue { partial, .. } => {
292 state = ParserState::Origin {
293 scheme: prev.scheme(),
294 value: partial,
295 partial: prev.low() & mask,
296 char_index: 10 - ch,
297 };
298 }
299 ParserState::Sign { value, .. } => {
300 state = ParserState::Origin {
301 scheme: prev.scheme(),
302 value: value,
303 partial: prev.low() & mask,
304 char_index: 10 - ch,
305 };
306 }
307 ParserState::Origin {
308 value,
309 scheme,
310 char_index: 9,
311 ..
312 } => {
313 state = ParserState::Origin {
314 scheme: scheme,
315 value: value,
316 partial: prev.low() & mask,
317 char_index: 10 - ch,
318 };
319 }
320 _ => {
321 return None;
322 }
323 }
324 }
325
326 b'`' => {
328 prev = context.map(|x| x.1);
329 }
330
331 b'+' | b'%' | b'-' | b'$' => {
333 let sch = match ch as u8 {
334 b'+' => Scheme::Event,
335 b'%' => Scheme::Number,
336 b'-' => Scheme::Derived,
337 b'$' => Scheme::Name,
338 _ => unreachable!(),
339 };
340
341 match state {
342 ParserState::Start if prev.is_some() => {
343 state = ParserState::Origin {
344 scheme: sch,
345 value: prev.unwrap().high(),
346 partial: 0,
347 char_index: 9,
348 };
349 }
350 ParserState::NameOrValue { partial, .. } => {
351 state = ParserState::Origin {
352 scheme: sch,
353 value: partial,
354 partial: 0,
355 char_index: 9,
356 };
357 }
358 ParserState::Sign { value, .. } => {
359 state = ParserState::Origin {
360 scheme: sch,
361 value: value,
362 partial: 0,
363 char_index: 9,
364 };
365 }
366 _ => {
367 return None;
368 }
369 }
370 }
371
372 _ => {
374 match state {
375 ParserState::Start => {
376 return None;
377 }
378 ParserState::NameOrValue {
379 partial,
380 is_full: false,
381 ..
382 } if prev.is_some() => {
383 let uu = UUID::new(
384 partial,
385 prev.unwrap().low(),
386 prev.unwrap().scheme(),
387 );
388 return Some((uu, &input[off..]));
389 }
390 ParserState::NameOrValue { partial, .. } => {
391 let uu = UUID::Name { name: partial, scope: 0 };
392 return Some((uu, &input[off..]));
393 }
394 ParserState::Sign { value, is_full: false }
395 if prev.is_some() =>
396 {
397 let uu = UUID::new(
398 value,
399 prev.unwrap().low(),
400 prev.unwrap().scheme(),
401 );
402 return Some((uu, &input[off..]));
403 }
404 ParserState::Sign { value, .. } => {
405 let uu = UUID::Name { name: value, scope: 0 };
406 return Some((uu, &input[off..]));
407 }
408 ParserState::Origin {
409 value,
410 partial: 0,
411 scheme,
412 char_index: 9,
413 ..
414 } if prev.is_some() => {
415 let uu =
416 UUID::new(value, prev.unwrap().low(), scheme);
417 return Some((uu, &input[off..]));
418 }
419 ParserState::Origin {
420 value, partial, scheme, ..
421 } => {
422 let uu = UUID::new(value, partial, scheme);
423 return Some((uu, &input[off..]));
424 }
425 }
426 }
427 }
428 }
429
430 match state {
432 ParserState::Start => {
433 return None;
434 }
435 ParserState::NameOrValue { partial, is_full: false, .. }
436 if prev.is_some() =>
437 {
438 let uu = UUID::new(
439 partial,
440 prev.unwrap().low(),
441 prev.unwrap().scheme(),
442 );
443 return Some((uu, &input[0..0]));
444 }
445 ParserState::NameOrValue { partial, .. } => {
446 let uu = UUID::Name { name: partial, scope: 0 };
447 return Some((uu, &input[0..0]));
448 }
449 ParserState::Sign { value, is_full: false } if prev.is_some() => {
450 let uu = UUID::new(
451 value,
452 prev.unwrap().low(),
453 prev.unwrap().scheme(),
454 );
455 return Some((uu, &input[0..0]));
456 }
457 ParserState::Sign { value, .. } => {
458 let uu = UUID::Name { name: value, scope: 0 };
459 return Some((uu, &input[0..0]));
460 }
461 ParserState::Origin { value, scheme, char_index: 9, .. }
462 if prev.is_some() =>
463 {
464 let uu = UUID::new(value, prev.unwrap().low(), scheme);
465 return Some((uu, &input[0..0]));
466 }
467 ParserState::Origin { value, partial, scheme, .. } => {
468 let uu = UUID::new(value, partial, scheme);
469 return Some((uu, &input[0..0]));
470 }
471 }
472 }
473
474 pub fn compress(&self, context: Option<(&UUID, &UUID)>) -> String {
477 if context.is_none()
478 || self.high() >> 60 != context.unwrap().0.high() >> 60
479 {
480 return format!("{}", self);
482 } else {
483 let (prev_col, _) = context.unwrap();
484
485 let ret = Self::compress_int64(self.high(), prev_col.high());
486 if self.low() == 0 && self.scheme() == Scheme::Name {
487 if ret.is_empty() {
488 "0".to_string()
489 } else {
490 ret
491 }
492 } else {
493 let origin = Self::compress_int64(self.low(), prev_col.low());
494 let orig_is_compr = origin
495 .bytes()
496 .next()
497 .map(|c| {
498 c == b'{'
499 || c == b'['
500 || c == b'('
501 || c == b'}'
502 || c == b']'
503 || c == b')'
504 })
505 .unwrap_or(false);
506
507 if self.scheme() == prev_col.scheme()
508 && orig_is_compr
509 && !ret.is_empty()
510 {
511 ret + &origin
512 } else if self.scheme() == prev_col.scheme()
513 && origin.is_empty()
514 {
515 ret + &format!("{}", self.scheme())
516 } else {
517 ret + &format!("{}{}", self.scheme(), origin)
518 }
519 }
520 }
521 }
522
523 fn compress_int64(value: u64, ctx: u64) -> String {
524 if value == ctx {
525 return "".to_string();
526 }
527
528 let value = Self::int64_to_str(value, false);
529 let ctx = Self::int64_to_str(ctx, false);
530 let zip = value.bytes().zip(ctx.bytes()).collect::<Vec<_>>();
531 let prfx_len =
532 zip.iter().position(|(a, b)| a != b).unwrap_or(zip.len());
533
534 let ret = match prfx_len {
535 4 => format!("({}", &value[4..]),
536 5 => format!("[{}", &value[5..]),
537 6 => format!("{{{}", &value[6..]),
538 7 => format!("}}{}", &value[7..]),
539 8 => format!("]{}", &value[8..]),
540 9 => format!("){}", &value[9..]),
541 _ => value,
542 };
543
544 let ret = ret.trim_end_matches('0').to_string();
545 if ret.is_empty() {
546 "0".to_string()
547 } else {
548 ret
549 }
550 }
551
552 fn int64_to_str(int: u64, truncate: bool) -> String {
553 let mut ret = String::default();
554
555 for idx in 0..10 {
556 let idx = (9 - idx) * 6;
557 match ((int >> idx) & 63) as u8 {
558 ch @ 0...9 => {
559 ret += &format!("{}", (b'0' + ch) as char);
560 }
561 ch @ 10...35 => {
562 ret += &format!("{}", (b'A' + ch - 10) as char);
563 }
564 36 => {
565 ret.push('_');
566 }
567 ch @ 37...62 => {
568 ret += &format!("{}", (b'a' + ch - 37) as char);
569 }
570 63 => {
571 ret.push('~');
572 }
573 _ => unreachable!(),
574 }
575 }
576
577 if truncate {
578 let ret = ret.trim_end_matches('0').to_string();
579 if ret.is_empty() {
580 "0".to_string()
581 } else {
582 ret
583 }
584 } else {
585 ret
586 }
587 }
588
589 fn scheme(&self) -> Scheme {
590 match self {
591 UUID::Name { .. } => Scheme::Name,
592 UUID::Event { .. } => Scheme::Event,
593 UUID::Number { .. } => Scheme::Number,
594 UUID::Derived { .. } => Scheme::Derived,
595 }
596 }
597
598 fn high(&self) -> u64 {
599 match self {
600 &UUID::Name { name, .. } => name,
601 &UUID::Event { timestamp, .. } => timestamp,
602 &UUID::Number { value1, .. } => value1,
603 &UUID::Derived { timestamp, .. } => timestamp,
604 }
605 }
606
607 fn low(&self) -> u64 {
608 match self {
609 &UUID::Name { scope, .. } => scope,
610 &UUID::Event { origin, .. } => origin,
611 &UUID::Number { value2, .. } => value2,
612 &UUID::Derived { origin, .. } => origin,
613 }
614 }
615
616 fn new(hi: u64, lo: u64, sch: Scheme) -> Self {
617 match sch {
618 Scheme::Name => UUID::Name { name: hi, scope: lo },
619 Scheme::Event => UUID::Event { timestamp: hi, origin: lo },
620 Scheme::Derived => UUID::Derived { timestamp: hi, origin: lo },
621 Scheme::Number => UUID::Number { value1: hi, value2: lo },
622 }
623 }
624
625 fn timestamp() -> u64 {
626 use std::thread::sleep;
627 use std::time::Duration;
628
629 let now = SystemTime::now();
630 let seq = (UUID_SEQUENCE.fetch_add(1, atomic::Ordering::SeqCst)
631 & 0b111111_111111) as u64;
632
633 if seq == 0b111111_111111 {
637 while now == SystemTime::now() {
638 sleep(Duration::from_millis(1));
639 }
640 }
641
642 let dt: DateTime<Utc> = now.into();
643 let months = (2010 + dt.year() as u64 * 12 + dt.month0() as u64)
644 & 0b111111_111111;;
645 let ts = (months << (8 * 6))
646 | ((dt.day0() as u64 & 0b111111) << (7 * 6))
647 | ((dt.hour() as u64 & 0b111111) << (6 * 6))
648 | ((dt.minute() as u64 & 0b111111) << (5 * 6))
649 | ((dt.second() as u64 & 0b111111) << (4 * 6))
650 | (((dt.nanosecond() as u64 / 1_000_000) & 0b111111_111111)
651 << (2 * 6))
652 | seq;
653
654 ts
655 }
656}
657
658impl FromStr for UUID {
659 type Err = ();
660
661 fn from_str(s: &str) -> Result<Self, Self::Err> {
662 match UUID::parse(s, None) {
663 Some((uu, _)) => Ok(uu),
664 None => Err(()),
665 }
666 }
667}
668
669impl Default for UUID {
670 fn default() -> Self {
671 UUID::zero()
672 }
673}
674
675impl fmt::Display for UUID {
676 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
677 match self {
678 &UUID::Name { name: 0, scope: 0, .. } => f.write_str("0"),
679 &UUID::Name { name, scope: 0 } => f.write_str(&format_int(name)),
680 &UUID::Name { name, scope } => {
681 f.write_str(&format_int(name))?;
682 f.write_str("$")?;
683 f.write_str(&format_int(scope))
684 }
685 &UUID::Number { value1, value2 } => {
686 f.write_str(&format_int(value1))?;
687 f.write_str("%")?;
688 f.write_str(&format_int(value2))
689 }
690 &UUID::Derived { timestamp, origin } => {
691 f.write_str(&format_int(timestamp))?;
692 f.write_str("-")?;
693 f.write_str(&format_int(origin))
694 }
695 &UUID::Event { timestamp, origin } => {
696 f.write_str(&format_int(timestamp))?;
697 f.write_str("+")?;
698 f.write_str(&format_int(origin))
699 }
700 }
701 }
702}
703
704impl Arbitrary for UUID {
705 fn arbitrary<G: Gen>(g: &mut G) -> Self {
706 let hi = u64::arbitrary(g) & 0xfffffffffffffff;
707 let lo = u64::arbitrary(g) & 0xfffffffffffffff;
708
709 match g.gen_range(0, 4) {
710 0 => UUID::Name { name: hi, scope: lo },
711 1 => UUID::Number { value1: hi, value2: lo },
712 2 => UUID::Derived { timestamp: hi, origin: lo },
713 3 => UUID::Event { timestamp: hi, origin: lo },
714 _ => unreachable!(),
715 }
716 }
717}
718
719#[test]
720fn global_name_uuid() {
721 let uuid = UUID::Name { name: 824893205576155136, scope: 0 };
722
723 assert_eq!(uuid.is_name(), true);
724 assert_eq!(uuid.is_number(), false);
725 assert_eq!(uuid.is_event(), false);
726 assert_eq!(uuid.is_derived(), false);
727 assert_eq!(format!("{}", uuid), "inc");
728}
729
730#[test]
731fn scoped_name_uuid() {
732 let uuid =
733 UUID::Name { name: 1023340966896992256, scope: 893360337800134656 };
734
735 assert_eq!(uuid.is_name(), true);
736 assert_eq!(uuid.is_number(), false);
737 assert_eq!(uuid.is_event(), false);
738 assert_eq!(uuid.is_derived(), false);
739 assert_eq!(format!("{}", uuid), "todo$marcus");
740}
741
742#[test]
743fn number_uuid() {
744 let uuid = UUID::Number { value1: 10, value2: 20 };
745
746 assert_eq!(uuid.is_name(), false);
747 assert_eq!(uuid.is_number(), true);
748 assert_eq!(uuid.is_event(), false);
749 assert_eq!(uuid.is_derived(), false);
750 assert_eq!(format!("{}", uuid), "000000000A%000000000K");
751}
752
753#[test]
754fn event_uuid() {
755 let uuid = UUID::Event { timestamp: 0, origin: 0 };
756
757 assert_eq!(uuid.is_name(), false);
758 assert_eq!(uuid.is_number(), false);
759 assert_eq!(uuid.is_event(), true);
760 assert_eq!(uuid.is_derived(), false);
761 assert_eq!(format!("{}", uuid), "0+0");
762}
763
764#[test]
765fn derived_uuid() {
766 let uuid = UUID::Derived { timestamp: 0, origin: 0 };
767
768 assert_eq!(uuid.is_name(), false);
769 assert_eq!(uuid.is_number(), false);
770 assert_eq!(uuid.is_event(), false);
771 assert_eq!(uuid.is_derived(), true);
772 assert_eq!(format!("{}", uuid), "0-0");
773}
774
775#[test]
776fn well_known() {
777 let error = UUID::Name { name: 1152921504606846975, scope: 0 };
778 let never = UUID::Name { name: 1134907106097364992, scope: 0 };
779 let inc = UUID::Name { name: 824893205576155136, scope: 0 };
780
781 assert_eq!(format!("{}", error), "~~~~~~~~~~");
782 assert_eq!(format!("{}", never), "~");
783 assert_eq!(format!("{}", inc), "inc");
784}
785
786#[test]
787fn compress() {
788 let tris = vec![
789 ("}DcR-L8w", "}IYI-", "}IYI-0"),
790 ("0$author", "name$author2", "name{2"),
791 ("0", "1", "1"),
792 ("0", "123-0", "123-"),
793 ("0", "0000000001-orig", ")1-orig"),
794 ("1time01-src", "1time02+src", "{2+"),
795 ("hash%here", "hash%there", "%there"),
796 ("1", ")1", "0000000001"), ("0", "name$0", "name"),
798 ("time+orig", "time1+orig2", "(1(2"),
799 ("time-orig", "time1+orig2", "(1+(2"),
800 ("[1s9L3-[Wj8oO", "[1s9L3-(2Biejq", "-(2Biejq"),
801 ("}DcR-}L8w", "}IYI-", "}IYI}"), ("A$B", "A-B", "-"),
803 ];
804 let z = UUID::zero();
805
806 for (ctx, uu, exp) in tris {
807 let ctx = UUID::parse(ctx, Some((&z, &z))).unwrap().0;
808 let uu = UUID::parse(uu, Some((&z, &z))).unwrap().0;
809 let comp = uu.compress(Some((&ctx, &ctx)));
810
811 assert_eq!(comp, exp);
812 }
813}
814
815#[test]
816fn parse_some() {
817 let tris = vec![
818 ("0", "1", "1"), ("1-x", ")1", "1000000001-x"),
820 ("test-1", "-", "test-1"),
821 ("hello-111", "[world", "helloworld-111"),
822 ("helloworld-111", "[", "hello-111"),
823 ("100001-orig", "[", "1-orig"), ("1+orig", "(2-", "10002-orig"),
825 ("time+orig", "(1(2", "time1+orig2"),
826 ("any-thing", "hash%here", "hash%here"),
828 ("[1s9L3-[Wj8oO", "-(2Biejq", "[1s9L3-(2Biejq"), ("0123456789-abcdefghij", ")~)~", "012345678~-abcdefghi~"),
830 ("(2-[1jHH~", "-[00yAl", "(2-}yAl"),
831 ("0123G-abcdb", "(4566(efF", "01234566-abcdefF"),
832 ];
833 let z = UUID::zero();
834
835 for (ctx, uu, exp) in tris {
836 eprintln!("{}, {}, {}", ctx, uu, exp);
837 let ctx = UUID::parse(ctx, Some((&z, &z))).unwrap().0;
838 eprintln!("ctx: {:?}", ctx);
839 eprintln!("parse ctx: {}", ctx);
840 let uu = UUID::parse(uu, Some((&ctx, &ctx))).unwrap().0;
841 eprintln!("uu: {:?}", uu);
842 eprintln!("parse uu: {}", uu);
843
844 assert_eq!(UUID::compress(&uu, Some((&z, &z))), exp);
845 }
846}
847
848#[test]
849fn parse_all() {
850 let pairs = vec![
851 ("-", "0123456789-abcdefghi"), ("B", "B"), ("(", "0123-abcdefghi"), ("(B", "0123B-abcdefghi"), ("+", "0123456789+abcdefghi"), ("+B", "0123456789+B"), ("+(", "0123456789+abcd"), ("+(B", "0123456789+abcdB"), ("A", "A"), ("AB", "AB"), ("A(", "A-abcd"), ("A(B", "A-abcdB"), ("A+", "A+abcdefghi"), ("A+B", "A+B"), ("A+(", "A+abcd"), ("A+(B", "A+abcdB"), (")", "012345678-abcdefghi"), (")B", "012345678B-abcdefghi"), (")(", "012345678-abcd"), (")(B", "012345678-abcdB"), (")+", "012345678+abcdefghi"), (")+B", "012345678+B"), (")+(", "012345678+abcd"), (")+(B", "012345678+abcdB"), (")A", "012345678A-abcdefghi"), (")AB", ""), (")A(", "012345678A-abcd"), (")A(B", "012345678A-abcdB"), (")A+", "012345678A+abcdefghi"), (")A+B", "012345678A+B"), (")A+(", "012345678A+abcd"), (")A+(B", "012345678A+abcdB"), ];
884 let z = UUID::zero();
885 let ctx = UUID::parse("0123456789-abcdefghi", Some((&z, &z))).unwrap().0;
886 for (uu, exp) in pairs {
887 if exp.is_empty() {
888 assert!(UUID::parse(uu, Some((&ctx, &ctx))).is_none());
889 } else {
890 let uu = UUID::parse(uu, Some((&ctx, &ctx))).unwrap().0;
891 assert_eq!(format!("{}", uu), exp);
892 }
893 }
894}
895
896quickcheck! {
897 fn parse_roundtrip(uu: UUID) -> bool {
898 let s = format!("{}", uu);
899
900 println!("in: {:?}", uu);
901 println!("in-text: {}", s);
902
903 let uu2 = UUID::from_str(&s);
904 println!("out: {:?}", uu2);
905
906 uu2.unwrap() == uu
907 }
908}