]> git.lizzy.rs Git - rust.git/blob - src/libuuid/lib.rs
Ignore tests broken by failing on ICE
[rust.git] / src / libuuid / lib.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12 Generate and parse UUIDs
13
14 Provides support for Universally Unique Identifiers (UUIDs). A UUID is a
15 unique 128-bit number, stored as 16 octets.  UUIDs are used to  assign unique
16 identifiers to entities without requiring a central allocating authority.
17
18 They are particularly useful in distributed systems, though can be used in
19 disparate areas, such as databases and network protocols.  Typically a UUID is
20 displayed in a readable string form as a sequence of hexadecimal digits,
21 separated into groups by hyphens.
22
23 The uniqueness property is not strictly guaranteed, however for all practical
24 purposes, it can be assumed that an unintentional collision would be extremely
25 unlikely.
26
27 # Examples
28
29 To create a new random (V4) UUID and print it out in hexadecimal form:
30
31 ```rust
32 use uuid::Uuid;
33
34 fn main() {
35     let uuid1 = Uuid::new_v4();
36     println!("{}", uuid1.to_str());
37 }
38  ```
39
40 # Strings
41
42 Examples of string representations:
43
44 * simple: `936DA01F9ABD4d9d80C702AF85C822A8`
45 * hyphenated: `550e8400-e29b-41d4-a716-446655440000`
46 * urn: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4`
47
48 # References
49
50 * [Wikipedia: Universally Unique Identifier](
51     http://en.wikipedia.org/wiki/Universally_unique_identifier)
52 * [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](
53     http://tools.ietf.org/html/rfc4122)
54
55 */
56
57 #![crate_id = "uuid#0.11-pre"]
58 #![crate_type = "rlib"]
59 #![crate_type = "dylib"]
60 #![license = "MIT/ASL2"]
61 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
62        html_favicon_url = "http://www.rust-lang.org/favicon.ico",
63        html_root_url = "http://static.rust-lang.org/doc/master")]
64
65 #![feature(default_type_params)]
66
67 // test harness access
68 #[cfg(test)]
69 extern crate test;
70
71 extern crate rand;
72 extern crate serialize;
73
74 use std::cast::{transmute,transmute_copy};
75 use std::char::Char;
76 use std::default::Default;
77 use std::fmt;
78 use std::from_str::FromStr;
79 use std::hash::Hash;
80 use std::num::FromStrRadix;
81 use std::str;
82 use std::slice;
83
84 use rand::Rng;
85
86 use serialize::{Encoder, Encodable, Decoder, Decodable};
87
88 /// A 128-bit (16 byte) buffer containing the ID
89 pub type UuidBytes = [u8, ..16];
90
91 /// The version of the UUID, denoting the generating algorithm
92 #[deriving(Eq)]
93 pub enum UuidVersion {
94     /// Version 1: MAC address
95     Version1Mac    = 1,
96     /// Version 2: DCE Security
97     Version2Dce    = 2,
98     /// Version 3: MD5 hash
99     Version3Md5    = 3,
100     /// Version 4: Random
101     Version4Random = 4,
102     /// Version 5: SHA-1 hash
103     Version5Sha1   = 5,
104 }
105
106 /// The reserved variants of UUIDs
107 #[deriving(Eq)]
108 pub enum UuidVariant {
109     /// Reserved by the NCS for backward compatibility
110     VariantNCS,
111     /// As described in the RFC4122 Specification (default)
112     VariantRFC4122,
113     /// Reserved by Microsoft for backward compatibility
114     VariantMicrosoft,
115     /// Reserved for future expansion
116     VariantFuture,
117 }
118
119 /// A Universally Unique Identifier (UUID)
120 pub struct Uuid {
121     /// The 128-bit number stored in 16 bytes
122     bytes: UuidBytes
123 }
124
125 impl<S: Writer> Hash<S> for Uuid {
126     fn hash(&self, state: &mut S) {
127         self.bytes.hash(state)
128     }
129 }
130
131 /// A UUID stored as fields (identical to UUID, used only for conversions)
132 struct UuidFields {
133     /// First field, 32-bit word
134     data1: u32,
135     /// Second field, 16-bit short
136     data2: u16,
137     /// Third field, 16-bit short
138     data3: u16,
139     /// Fourth field, 8 bytes
140     data4: [u8, ..8]
141 }
142
143 /// Error details for string parsing failures
144 #[allow(missing_doc)]
145 pub enum ParseError {
146     ErrorInvalidLength(uint),
147     ErrorInvalidCharacter(char, uint),
148     ErrorInvalidGroups(uint),
149     ErrorInvalidGroupLength(uint, uint, uint),
150 }
151
152 /// Converts a ParseError to a string
153 impl fmt::Show for ParseError {
154     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155         match *self {
156             ErrorInvalidLength(found) =>
157                 write!(f.buf, "Invalid length; expecting 32, 36 or 45 chars, \
158                                found {}", found),
159             ErrorInvalidCharacter(found, pos) =>
160                 write!(f.buf, "Invalid character; found `{}` (0x{:02x}) at \
161                                offset {}", found, found as uint, pos),
162             ErrorInvalidGroups(found) =>
163                 write!(f.buf, "Malformed; wrong number of groups: expected 1 \
164                                or 5, found {}", found),
165             ErrorInvalidGroupLength(group, found, expecting) =>
166                 write!(f.buf, "Malformed; length of group {} was {}, \
167                                expecting {}", group, found, expecting),
168         }
169     }
170 }
171
172 // Length of each hyphenated group in hex digits
173 static UuidGroupLens: [uint, ..5] = [8u, 4u, 4u, 4u, 12u];
174
175 /// UUID support
176 impl Uuid {
177     /// Returns a nil or empty UUID (containing all zeroes)
178     pub fn nil() -> Uuid {
179         let uuid = Uuid{ bytes: [0, .. 16] };
180         uuid
181     }
182
183     /// Create a new UUID of the specified version
184     pub fn new(v: UuidVersion) -> Option<Uuid> {
185         match v {
186             Version4Random => Some(Uuid::new_v4()),
187             _ => None
188         }
189     }
190
191     /// Creates a new random UUID
192     ///
193     /// Uses the `rand` module's default RNG task as the source
194     /// of random numbers. Use the rand::Rand trait to supply
195     /// a custom generator if required.
196     pub fn new_v4() -> Uuid {
197         let ub = rand::task_rng().gen_vec(16);
198         let mut uuid = Uuid{ bytes: [0, .. 16] };
199         slice::bytes::copy_memory(uuid.bytes, ub.as_slice());
200         uuid.set_variant(VariantRFC4122);
201         uuid.set_version(Version4Random);
202         uuid
203     }
204
205     /// Creates a UUID using the supplied field values
206     ///
207     /// # Arguments
208     /// * `d1` A 32-bit word
209     /// * `d2` A 16-bit word
210     /// * `d3` A 16-bit word
211     /// * `d4` Array of 8 octets
212     pub fn from_fields(d1: u32, d2: u16, d3: u16, d4: &[u8]) -> Uuid {
213         use std::mem::{to_be16, to_be32};
214
215         // First construct a temporary field-based struct
216         let mut fields = UuidFields {
217                 data1: 0,
218                 data2: 0,
219                 data3: 0,
220                 data4: [0, ..8]
221         };
222
223         fields.data1 = to_be32(d1);
224         fields.data2 = to_be16(d2);
225         fields.data3 = to_be16(d3);
226         slice::bytes::copy_memory(fields.data4, d4);
227
228         unsafe {
229             transmute(fields)
230         }
231     }
232
233     /// Creates a UUID using the supplied bytes
234     ///
235     /// # Arguments
236     /// * `b` An array or slice of 16 bytes
237     pub fn from_bytes(b: &[u8]) -> Option<Uuid> {
238         if b.len() != 16 {
239             return None
240         }
241
242         let mut uuid = Uuid{ bytes: [0, .. 16] };
243         slice::bytes::copy_memory(uuid.bytes, b);
244         Some(uuid)
245     }
246
247     /// Specifies the variant of the UUID structure
248     fn set_variant(&mut self, v: UuidVariant) {
249         // Octet 8 contains the variant in the most significant 3 bits
250         match v {
251             VariantNCS =>        // b0xx...
252                 self.bytes[8] =  self.bytes[8] & 0x7f,
253             VariantRFC4122 =>    // b10x...
254                 self.bytes[8] = (self.bytes[8] & 0x3f) | 0x80,
255             VariantMicrosoft =>  // b110...
256                 self.bytes[8] = (self.bytes[8] & 0x1f) | 0xc0,
257             VariantFuture =>     // b111...
258                 self.bytes[8] = (self.bytes[8] & 0x1f) | 0xe0,
259         }
260     }
261
262     /// Returns the variant of the UUID structure
263     ///
264     /// This determines the interpretation of the structure of the UUID.
265     /// Currently only the RFC4122 variant is generated by this module.
266     ///
267     /// * [Variant Reference](http://tools.ietf.org/html/rfc4122#section-4.1.1)
268     pub fn get_variant(&self) -> Option<UuidVariant> {
269         if self.bytes[8] & 0x80 == 0x00 {
270             Some(VariantNCS)
271         } else if self.bytes[8] & 0xc0 == 0x80 {
272             Some(VariantRFC4122)
273         } else if self.bytes[8] & 0xe0 == 0xc0  {
274             Some(VariantMicrosoft)
275         } else if self.bytes[8] & 0xe0 == 0xe0 {
276             Some(VariantFuture)
277         } else  {
278             None
279         }
280     }
281
282     /// Specifies the version number of the UUID
283     fn set_version(&mut self, v: UuidVersion) {
284         self.bytes[6] = (self.bytes[6] & 0xF) | ((v as u8) << 4);
285     }
286
287     /// Returns the version number of the UUID
288     ///
289     /// This represents the algorithm used to generate the contents.
290     ///
291     /// Currently only the Random (V4) algorithm is supported by this
292     /// module.  There are security and privacy implications for using
293     /// older versions - see [Wikipedia: Universally Unique Identifier](
294     /// http://en.wikipedia.org/wiki/Universally_unique_identifier) for
295     /// details.
296     ///
297     /// * [Version Reference](http://tools.ietf.org/html/rfc4122#section-4.1.3)
298     pub fn get_version_num(&self) -> uint {
299         (self.bytes[6] >> 4) as uint
300     }
301
302     /// Returns the version of the UUID
303     ///
304     /// This represents the algorithm used to generate the contents
305     pub fn get_version(&self) -> Option<UuidVersion> {
306         let v = self.bytes[6] >> 4;
307         match v {
308             1 => Some(Version1Mac),
309             2 => Some(Version2Dce),
310             3 => Some(Version3Md5),
311             4 => Some(Version4Random),
312             5 => Some(Version5Sha1),
313             _ => None
314         }
315     }
316
317     /// Return an array of 16 octets containing the UUID data
318     pub fn as_bytes<'a>(&'a self) -> &'a [u8] {
319         self.bytes.as_slice()
320     }
321
322     /// Returns the UUID as a string of 16 hexadecimal digits
323     ///
324     /// Example: `936DA01F9ABD4d9d80C702AF85C822A8`
325     pub fn to_simple_str(&self) -> ~str {
326         let mut s: Vec<u8> = Vec::from_elem(32, 0u8);
327         for i in range(0u, 16u) {
328             let digit = format!("{:02x}", self.bytes[i] as uint);
329             *s.get_mut(i*2+0) = digit[0];
330             *s.get_mut(i*2+1) = digit[1];
331         }
332         str::from_utf8(s.as_slice()).unwrap().to_str()
333     }
334
335     /// Returns a string of hexadecimal digits, separated into groups with a hyphen.
336     ///
337     /// Example: `550e8400-e29b-41d4-a716-446655440000`
338     pub fn to_hyphenated_str(&self) -> ~str {
339         use std::mem::{to_be16, to_be32};
340         // Convert to field-based struct as it matches groups in output.
341         // Ensure fields are in network byte order, as per RFC.
342         let mut uf: UuidFields;
343         unsafe {
344             uf = transmute_copy(&self.bytes);
345         }
346         uf.data1 = to_be32(uf.data1);
347         uf.data2 = to_be16(uf.data2);
348         uf.data3 = to_be16(uf.data3);
349         let s = format!("{:08x}-{:04x}-{:04x}-{:02x}{:02x}-\
350                          {:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
351             uf.data1,
352             uf.data2, uf.data3,
353             uf.data4[0], uf.data4[1],
354             uf.data4[2], uf.data4[3], uf.data4[4],
355             uf.data4[5], uf.data4[6], uf.data4[7]);
356         s
357     }
358
359     /// Returns the UUID formatted as a full URN string
360     ///
361     /// This is the same as the hyphenated format, but with the "urn:uuid:" prefix.
362     ///
363     /// Example: `urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4`
364     pub fn to_urn_str(&self) -> ~str {
365         "urn:uuid:" + self.to_hyphenated_str()
366     }
367
368     /// Parses a UUID from a string of hexadecimal digits with optional hyphens
369     ///
370     /// Any of the formats generated by this module (simple, hyphenated, urn) are
371     /// supported by this parsing function.
372     pub fn parse_string(us: &str) -> Result<Uuid, ParseError> {
373
374         let mut us = us.clone();
375         let orig_len = us.len();
376
377         // Ensure length is valid for any of the supported formats
378         if orig_len != 32 && orig_len != 36 && orig_len != 45 {
379             return Err(ErrorInvalidLength(orig_len));
380         }
381
382         // Strip off URN prefix if present
383         if us.starts_with("urn:uuid:") {
384             us = us.slice(9, orig_len);
385         }
386
387         // Make sure all chars are either hex digits or hyphen
388         for (i, c) in us.chars().enumerate() {
389             match c {
390                 '0'..'9' | 'A'..'F' | 'a'..'f' | '-' => {},
391                 _ => return Err(ErrorInvalidCharacter(c, i)),
392             }
393         }
394
395         // Split string up by hyphens into groups
396         let hex_groups: Vec<&str> = us.split_str("-").collect();
397
398         // Get the length of each group
399         let group_lens: Vec<uint> = hex_groups.iter().map(|&v| v.len()).collect();
400
401         // Ensure the group lengths are valid
402         match group_lens.len() {
403             // Single group, no hyphens
404             1 => {
405                 if *group_lens.get(0) != 32 {
406                     return Err(ErrorInvalidLength(*group_lens.get(0)));
407                 }
408             },
409             // Five groups, hyphens in between each
410             5 => {
411                 // Ensure each group length matches the expected
412                 for (i, (&gl, &expected)) in
413                     group_lens.iter().zip(UuidGroupLens.iter()).enumerate() {
414                     if gl != expected {
415                         return Err(ErrorInvalidGroupLength(i, gl, expected))
416                     }
417                 }
418             },
419             _ => {
420                 return Err(ErrorInvalidGroups(group_lens.len()));
421             }
422         }
423
424         // Normalise into one long hex string
425         let vs = hex_groups.concat();
426
427         // At this point, we know we have a valid hex string, without hyphens
428         assert!(vs.len() == 32);
429         assert!(vs.chars().all(|c| c.is_digit_radix(16)));
430
431         // Allocate output UUID buffer
432         let mut ub = [0u8, ..16];
433
434         // Extract each hex digit from the string
435         for i in range(0u, 16u) {
436             ub[i] = FromStrRadix::from_str_radix(vs.slice(i*2, (i+1)*2), 16).unwrap();
437         }
438
439         Ok(Uuid::from_bytes(ub).unwrap())
440     }
441
442     /// Tests if the UUID is nil
443     pub fn is_nil(&self) -> bool {
444         return self.bytes.iter().all(|&b| b == 0);
445     }
446 }
447
448 impl Default for Uuid {
449     /// Returns the nil UUID, which is all zeroes
450     fn default() -> Uuid {
451         Uuid::nil()
452     }
453 }
454
455 impl Clone for Uuid {
456     /// Returns a copy of the UUID
457     fn clone(&self) -> Uuid { *self }
458 }
459
460 impl FromStr for Uuid {
461     /// Parse a hex string and interpret as a UUID
462     ///
463     /// Accepted formats are a sequence of 32 hexadecimal characters,
464     /// with or without hypens (grouped as 8, 4, 4, 4, 12).
465     fn from_str(us: &str) -> Option<Uuid> {
466         let result = Uuid::parse_string(us);
467         match result {
468             Ok(u) => Some(u),
469             Err(_) => None
470         }
471     }
472 }
473
474 /// Convert the UUID to a hexadecimal-based string representation
475 impl fmt::Show for Uuid {
476     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
477         write!(f.buf, "{}", self.to_simple_str())
478     }
479 }
480
481 /// Test two UUIDs for equality
482 ///
483 /// UUIDs are equal only when they are byte-for-byte identical
484 impl Eq for Uuid {
485     fn eq(&self, other: &Uuid) -> bool {
486         self.bytes == other.bytes
487     }
488 }
489
490 impl TotalEq for Uuid {}
491
492 // FIXME #9845: Test these more thoroughly
493 impl<T: Encoder<E>, E> Encodable<T, E> for Uuid {
494     /// Encode a UUID as a hypenated string
495     fn encode(&self, e: &mut T) -> Result<(), E> {
496         e.emit_str(self.to_hyphenated_str())
497     }
498 }
499
500 impl<T: Decoder<E>, E> Decodable<T, E> for Uuid {
501     /// Decode a UUID from a string
502     fn decode(d: &mut T) -> Result<Uuid, E> {
503         Ok(from_str(try!(d.read_str())).unwrap())
504     }
505 }
506
507 /// Generates a random instance of UUID (V4 conformant)
508 impl rand::Rand for Uuid {
509     #[inline]
510     fn rand<R: rand::Rng>(rng: &mut R) -> Uuid {
511         let ub = rng.gen_vec(16);
512         let mut uuid = Uuid{ bytes: [0, .. 16] };
513         slice::bytes::copy_memory(uuid.bytes, ub.as_slice());
514         uuid.set_variant(VariantRFC4122);
515         uuid.set_version(Version4Random);
516         uuid
517     }
518 }
519
520 #[cfg(test)]
521 mod test {
522     extern crate collections;
523     extern crate rand;
524
525     use super::{Uuid, VariantMicrosoft, VariantNCS, VariantRFC4122,
526                 Version1Mac, Version2Dce, Version3Md5, Version4Random,
527                 Version5Sha1};
528     use std::str;
529     use std::io::MemWriter;
530
531     #[test]
532     fn test_nil() {
533         let nil = Uuid::nil();
534         let not_nil = Uuid::new_v4();
535
536         assert!(nil.is_nil());
537         assert!(!not_nil.is_nil());
538     }
539
540     #[test]
541     fn test_new() {
542         // Supported
543         let uuid1 = Uuid::new(Version4Random).unwrap();
544         let s = uuid1.to_simple_str();
545
546         assert!(s.len() == 32);
547         assert!(uuid1.get_version().unwrap() == Version4Random);
548
549         // Test unsupported versions
550         assert!(Uuid::new(Version1Mac) == None);
551         assert!(Uuid::new(Version2Dce) == None);
552         assert!(Uuid::new(Version3Md5) == None);
553         assert!(Uuid::new(Version5Sha1) == None);
554     }
555
556     #[test]
557     fn test_new_v4() {
558         let uuid1 = Uuid::new_v4();
559
560         assert!(uuid1.get_version().unwrap() == Version4Random);
561         assert!(uuid1.get_variant().unwrap() == VariantRFC4122);
562     }
563
564     #[test]
565     fn test_get_version() {
566         let uuid1 = Uuid::new_v4();
567
568         assert!(uuid1.get_version().unwrap() == Version4Random);
569         assert!(uuid1.get_version_num() == 4);
570     }
571
572     #[test]
573     fn test_get_variant() {
574         let uuid1 = Uuid::new_v4();
575         let uuid2 = Uuid::parse_string("550e8400-e29b-41d4-a716-446655440000").unwrap();
576         let uuid3 = Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").unwrap();
577         let uuid4 = Uuid::parse_string("936DA01F9ABD4d9dC0C702AF85C822A8").unwrap();
578         let uuid5 = Uuid::parse_string("F9168C5E-CEB2-4faa-D6BF-329BF39FA1E4").unwrap();
579         let uuid6 = Uuid::parse_string("f81d4fae-7dec-11d0-7765-00a0c91e6bf6").unwrap();
580
581         assert!(uuid1.get_variant().unwrap() == VariantRFC4122);
582         assert!(uuid2.get_variant().unwrap() == VariantRFC4122);
583         assert!(uuid3.get_variant().unwrap() == VariantRFC4122);
584         assert!(uuid4.get_variant().unwrap() == VariantMicrosoft);
585         assert!(uuid5.get_variant().unwrap() == VariantMicrosoft);
586         assert!(uuid6.get_variant().unwrap() == VariantNCS);
587     }
588
589     #[test]
590     fn test_parse_uuid_v4() {
591         use super::{ErrorInvalidCharacter, ErrorInvalidGroups,
592                     ErrorInvalidGroupLength, ErrorInvalidLength};
593
594         // Invalid
595         assert!(Uuid::parse_string("").is_err());
596         assert!(Uuid::parse_string("!").is_err());
597         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45").is_err());
598         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4").is_err());
599         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4").is_err());
600         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4").is_err());
601         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa").is_err());
602         assert!(Uuid::parse_string("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4").is_err());
603         assert!(Uuid::parse_string("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4").is_err());
604         assert!(Uuid::parse_string("01020304-1112-2122-3132-41424344").is_err());
605         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c").is_err());
606         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c88").is_err());
607         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0cg8").is_err());
608         assert!(Uuid::parse_string("67e5504410b1426%9247bb680e5fe0c8").is_err());
609
610         // Valid
611         assert!(Uuid::parse_string("00000000000000000000000000000000").is_ok());
612         assert!(Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
613         assert!(Uuid::parse_string("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
614         assert!(Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok());
615         assert!(Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c8").is_ok());
616         assert!(Uuid::parse_string("01020304-1112-2122-3132-414243444546").is_ok());
617         assert!(Uuid::parse_string("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
618
619         // Nil
620         let nil = Uuid::nil();
621         assert!(Uuid::parse_string("00000000000000000000000000000000").unwrap()  == nil);
622         assert!(Uuid::parse_string("00000000-0000-0000-0000-000000000000").unwrap() == nil);
623
624         // Round-trip
625         let uuid_orig = Uuid::new_v4();
626         let orig_str = uuid_orig.to_str();
627         let uuid_out = Uuid::parse_string(orig_str).unwrap();
628         assert!(uuid_orig == uuid_out);
629
630         // Test error reporting
631         let e = Uuid::parse_string("67e5504410b1426f9247bb680e5fe0c").unwrap_err();
632         assert!(match e { ErrorInvalidLength(n) => n==31, _ => false });
633
634         let e = Uuid::parse_string("67e550X410b1426f9247bb680e5fe0cd").unwrap_err();
635         assert!(match e { ErrorInvalidCharacter(c, n) => c=='X' && n==6, _ => false });
636
637         let e = Uuid::parse_string("67e550-4105b1426f9247bb680e5fe0c").unwrap_err();
638         assert!(match e { ErrorInvalidGroups(n) => n==2, _ => false });
639
640         let e = Uuid::parse_string("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4").unwrap_err();
641         assert!(match e { ErrorInvalidGroupLength(g, n, e) => g==3 && n==5 && e==4, _ => false });
642     }
643
644     #[test]
645     fn test_to_simple_str() {
646         let uuid1 = Uuid::new_v4();
647         let s = uuid1.to_simple_str();
648
649         assert!(s.len() == 32);
650         assert!(s.chars().all(|c| c.is_digit_radix(16)));
651     }
652
653     #[test]
654     fn test_to_str() {
655         let uuid1 = Uuid::new_v4();
656         let s = uuid1.to_str();
657
658         assert!(s.len() == 32);
659         assert!(s.chars().all(|c| c.is_digit_radix(16)));
660     }
661
662     #[test]
663     fn test_to_hyphenated_str() {
664         let uuid1 = Uuid::new_v4();
665         let s = uuid1.to_hyphenated_str();
666
667         assert!(s.len() == 36);
668         assert!(s.chars().all(|c| c.is_digit_radix(16) || c == '-'));
669     }
670
671     #[test]
672     fn test_to_urn_str() {
673         let uuid1 = Uuid::new_v4();
674         let ss = uuid1.to_urn_str();
675         let s = ss.slice(9, ss.len());
676
677         assert!(ss.starts_with("urn:uuid:"));
678         assert!(s.len() == 36);
679         assert!(s.chars().all(|c| c.is_digit_radix(16) || c == '-'));
680     }
681
682     #[test]
683     fn test_to_str_matching() {
684         let uuid1 = Uuid::new_v4();
685
686         let hs = uuid1.to_hyphenated_str();
687         let ss = uuid1.to_str();
688
689         let hsn = str::from_chars(hs.chars()
690                                     .filter(|&c| c != '-')
691                                     .collect::<Vec<char>>()
692                                     .as_slice());
693
694         assert!(hsn == ss);
695     }
696
697     #[test]
698     fn test_string_roundtrip() {
699         let uuid = Uuid::new_v4();
700
701         let hs = uuid.to_hyphenated_str();
702         let uuid_hs = Uuid::parse_string(hs).unwrap();
703         assert!(uuid_hs == uuid);
704
705         let ss = uuid.to_str();
706         let uuid_ss = Uuid::parse_string(ss).unwrap();
707         assert!(uuid_ss == uuid);
708     }
709
710     #[test]
711     fn test_compare() {
712         let uuid1 = Uuid::new_v4();
713         let uuid2 = Uuid::new_v4();
714
715         assert!(uuid1 == uuid1);
716         assert!(uuid2 == uuid2);
717         assert!(uuid1 != uuid2);
718         assert!(uuid2 != uuid1);
719     }
720
721     #[test]
722     fn test_from_fields() {
723         let d1: u32 = 0xa1a2a3a4;
724         let d2: u16 = 0xb1b2;
725         let d3: u16 = 0xc1c2;
726         let d4: Vec<u8> = vec!(0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8);
727
728         let u = Uuid::from_fields(d1, d2, d3, d4.as_slice());
729
730         let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8".to_owned();
731         let result = u.to_simple_str();
732         assert!(result == expected);
733     }
734
735     #[test]
736     fn test_from_bytes() {
737         let b = vec!( 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
738                    0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 );
739
740         let u = Uuid::from_bytes(b.as_slice()).unwrap();
741         let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8".to_owned();
742
743         assert!(u.to_simple_str() == expected);
744     }
745
746     #[test]
747     fn test_as_bytes() {
748         let u = Uuid::new_v4();
749         let ub = u.as_bytes();
750
751         assert!(ub.len() == 16);
752         assert!(! ub.iter().all(|&b| b == 0));
753     }
754
755     #[test]
756     fn test_bytes_roundtrip() {
757         let b_in: [u8, ..16] = [ 0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
758                                  0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8 ];
759
760         let u = Uuid::from_bytes(b_in.clone()).unwrap();
761
762         let b_out = u.as_bytes();
763
764         assert!(b_in == b_out);
765     }
766
767     #[test]
768     fn test_operator_eq() {
769         let u1 = Uuid::new_v4();
770         let u2 = u1.clone();
771         let u3 = Uuid::new_v4();
772
773         assert!(u1 == u1);
774         assert!(u1 == u2);
775         assert!(u2 == u1);
776
777         assert!(u1 != u3);
778         assert!(u3 != u1);
779         assert!(u2 != u3);
780         assert!(u3 != u2);
781     }
782
783     #[test]
784     fn test_rand_rand() {
785         let mut rng = rand::task_rng();
786         let u: ~Uuid = rand::Rand::rand(&mut rng);
787         let ub = u.as_bytes();
788
789         assert!(ub.len() == 16);
790         assert!(! ub.iter().all(|&b| b == 0));
791     }
792
793     #[test]
794     fn test_serialize_round_trip() {
795         use serialize::ebml;
796         use serialize::{Encodable, Decodable};
797
798         let u = Uuid::new_v4();
799         let mut wr = MemWriter::new();
800         let _ = u.encode(&mut ebml::writer::Encoder(&mut wr));
801         let doc = ebml::reader::Doc(wr.get_ref());
802         let u2 = Decodable::decode(&mut ebml::reader::Decoder(doc)).unwrap();
803         assert_eq!(u, u2);
804     }
805
806     #[test]
807     fn test_iterbytes_impl_for_uuid() {
808         use self::collections::HashSet;
809         let mut set = HashSet::new();
810         let id1 = Uuid::new_v4();
811         let id2 = Uuid::new_v4();
812         set.insert(id1);
813         assert!(set.contains(&id1));
814         assert!(!set.contains(&id2));
815     }
816 }
817
818 #[cfg(test)]
819 mod bench {
820     extern crate test;
821     use self::test::Bencher;
822     use super::Uuid;
823
824     #[bench]
825     pub fn create_uuids(b: &mut Bencher) {
826         b.iter(|| {
827             Uuid::new_v4();
828         })
829     }
830
831     #[bench]
832     pub fn uuid_to_str(b: &mut Bencher) {
833         let u = Uuid::new_v4();
834         b.iter(|| {
835             u.to_str();
836         })
837     }
838
839     #[bench]
840     pub fn parse_str(b: &mut Bencher) {
841         let s = "urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4";
842         b.iter(|| {
843             Uuid::parse_string(s).unwrap();
844         })
845     }
846 }