ron_uuid/
uuid.rs

1//! UUID
2
3use 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/// UUIDs are used by RON to identify types, objects, events, etc.
44///
45/// There are different kinds of UUIDs: Name, number, event and derived.
46///
47/// Any UUID has four parts, two related to its content, and two related to its type.
48///
49/// The type of an UUID is a 2-bit number called the *scheme*. The
50/// subtype of an UUID is a 4-bit number called the
51/// *variety* (currently unsupported). Together, the scheme and variety specify the kind of
52/// UUID.
53///
54/// Two other fields describe the actual content of the UUID. Both are 60-bit numbers that can be
55/// represented as ten-digit Base64 characters.
56///
57/// That leaves 2 bits to make up a 128-bit number. These 2 bits are
58/// always 0 and are provided for backwards compatibility with RFC4122
59/// (variant field).
60#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
61pub enum UUID {
62    /// Name UUIDs are often used to encode short string atoms, such as types (e.g. `lww`).
63    /// The string is read as a Base64-literal to determine the actual (numeric) UUID component.
64    /// Name UUIDs can be *global* (e.g. `lww` = `lww$0`) or *scoped* (e.g. `dbtest$client1`)
65    Name {
66        /// Local name.
67        name: u64,
68        /// Global namespace/node ID.
69        scope: u64,
70    },
71
72    /// Number UUIDs encode numbers (e.g. indices into a matrix) and hash values. The meaning of
73    /// the two values is context-dependent.
74    Number {
75        /// First value.
76        value1: u64,
77        /// Second value.
78        value2: u64,
79    },
80
81    /// Event UUIDs are Lamport-timestamps that are used to identify operations and objects.
82    /// The timestamp is clock-dependent, while the origin is a replica ID.
83    Event {
84        /// Local timestamp.
85        timestamp: u64,
86        /// Global namespace/node ID.
87        origin: u64,
88    },
89
90    /// Derived UUIDs refer to an event ID, without being the event ID.
91    Derived {
92        /// Local timestamp.
93        timestamp: u64,
94        /// Global namespace/node ID.
95        origin: u64,
96    },
97}
98
99/// This is the alphabet for the Swarm variant of Base64 encoding.
100pub const BASE_PUNCT: &'static [u8] =
101    b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
102
103/// Format a 60-bit integer as Base64.
104fn format_int(value: u64) -> String {
105    if value == 0 {
106        return "0".to_string();
107    }
108
109    // Trailing zeros are skipped.
110    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    /// Sets the default UUID origin. `s` must not be longer than 10 characters and only consist of
121    /// [0-9a-zA-Z~_].
122    pub fn set_node_id(val: u64) {
123        UUID_NODE_ID.store(val as usize, atomic::Ordering::Relaxed);
124    }
125
126    /// The current default UUID origin. Initially "0".
127    pub fn node_id() -> u64 {
128        UUID_NODE_ID.load(atomic::Ordering::Relaxed) as u64
129    }
130
131    /// New UUID with the default origin (see `node_id` and `set_node_id`) and the current time.
132    /// Ignoring leap seconds and other events that mess with the system time all calls to this
133    /// functions return unique UUID (duh).
134    pub fn now() -> Self {
135        UUID::Event { origin: Self::node_id(), timestamp: Self::timestamp() }
136    }
137
138    /// Creates a new 0 UUID
139    pub fn zero() -> Self {
140        UUID::Name { name: 0, scope: 0 }
141    }
142
143    /// Return true if and only if this is a name UUID.
144    pub fn is_name(&self) -> bool {
145        match self {
146            &UUID::Name { .. } => true,
147            _ => false,
148        }
149    }
150
151    /// Return true if and only if this is a number UUID.
152    pub fn is_number(&self) -> bool {
153        match self {
154            &UUID::Number { .. } => true,
155            _ => false,
156        }
157    }
158
159    /// Return true if and only if this is an event UUID.
160    pub fn is_event(&self) -> bool {
161        match self {
162            &UUID::Event { .. } => true,
163            _ => false,
164        }
165    }
166
167    /// Return true if and only if this is a derived UUID.
168    pub fn is_derived(&self) -> bool {
169        match self {
170            &UUID::Derived { .. } => true,
171            _ => false,
172        }
173    }
174
175    /// Returns true if `high() = low() = 0`.
176    pub fn is_zero(&self) -> bool {
177        self.high() == 0 && self.low() == 0
178    }
179
180    /// Compares `a` and `b` while ignoring the scheme.
181    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    /// Parse a single UUID and return the remaining string. The `context` argument is the pair
190    /// `previous column UUID` / `previous row UUID` or `None`.
191    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                // base64 char
200                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                // backref to column
271                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                // backref to row
327                b'`' => {
328                    prev = context.map(|x| x.1);
329                }
330
331                // hi/lo division
332                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                // unrecognized char
373                _ => {
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        // stream ended
431        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    /// Serialize this UUID the text optionally compressing it against (`previous column UUID` /
475    /// `previous row UUID`) if context is not `None`.
476    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            // don't compress UUIDs with different varities
481            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        // we wait for the clock to advance after sequence number overflows to avoid creating
634        // duplicated uuids. This can happen if we try to generate more than 4k instances in
635        // under one ms.
636        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"), //7
797        ("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}"), //12
802        ("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"), // 0
819        ("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"), // 5
824        ("1+orig", "(2-", "10002-orig"),
825        ("time+orig", "(1(2", "time1+orig2"),
826        // TODO		("name$user", "$scoped", "scoped$user"),
827        ("any-thing", "hash%here", "hash%here"),
828        ("[1s9L3-[Wj8oO", "-(2Biejq", "[1s9L3-(2Biejq"), // 9
829        ("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"),   // 00000
852        ("B", "B"),                      // 00001
853        ("(", "0123-abcdefghi"),         // 00010
854        ("(B", "0123B-abcdefghi"),       // 00011
855        ("+", "0123456789+abcdefghi"),   // 00100
856        ("+B", "0123456789+B"),          // 00101
857        ("+(", "0123456789+abcd"),       // 00110
858        ("+(B", "0123456789+abcdB"),     // 00111
859        ("A", "A"),                      // 01000 8
860        ("AB", "AB"),                    // 01001
861        ("A(", "A-abcd"),                // 01010
862        ("A(B", "A-abcdB"),              // 01011
863        ("A+", "A+abcdefghi"),           // 01100
864        ("A+B", "A+B"),                  // 01101
865        ("A+(", "A+abcd"),               // 01110
866        ("A+(B", "A+abcdB"),             // 01111
867        (")", "012345678-abcdefghi"),    // 10000 16
868        (")B", "012345678B-abcdefghi"),  // 10001
869        (")(", "012345678-abcd"),        // 10010
870        (")(B", "012345678-abcdB"),      // 10011
871        (")+", "012345678+abcdefghi"),   // 10100
872        (")+B", "012345678+B"),          // 10101
873        (")+(", "012345678+abcd"),       // 10110
874        (")+(B", "012345678+abcdB"),     // 10111
875        (")A", "012345678A-abcdefghi"),  // 11000
876        (")AB", ""),                     // 11001 error - length
877        (")A(", "012345678A-abcd"),      // 11010
878        (")A(B", "012345678A-abcdB"),    // 11011
879        (")A+", "012345678A+abcdefghi"), // 11100
880        (")A+B", "012345678A+B"),        // 11101
881        (")A+(", "012345678A+abcd"),     // 11110
882        (")A+(B", "012345678A+abcdB"),   // 11111
883    ];
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}