1 use crate::std::fmt; 2 3 /// A general error that can occur when working with UUIDs. 4 #[derive(Clone, Debug, Eq, Hash, PartialEq)] 5 pub struct Error(pub(crate) ErrorKind); 6 7 #[derive(Clone, Debug, Eq, Hash, PartialEq)] 8 pub(crate) enum ErrorKind { 9 /// Invalid character in the [`Uuid`] string. 10 /// 11 /// [`Uuid`]: ../struct.Uuid.html 12 Char { character: char, index: usize }, 13 /// A simple [`Uuid`] didn't contain 32 characters. 14 /// 15 /// [`Uuid`]: ../struct.Uuid.html 16 SimpleLength { len: usize }, 17 /// A byte array didn't contain 16 bytes 18 ByteLength { len: usize }, 19 /// A hyphenated [`Uuid`] didn't contain 5 groups 20 /// 21 /// [`Uuid`]: ../struct.Uuid.html 22 GroupCount { count: usize }, 23 /// A hyphenated [`Uuid`] had a group that wasn't the right length 24 /// 25 /// [`Uuid`]: ../struct.Uuid.html 26 GroupLength { 27 group: usize, 28 len: usize, 29 index: usize, 30 }, 31 /// The input was not a valid UTF8 string 32 InvalidUTF8, 33 /// The UUID is nil. 34 Nil, 35 /// Some other error occurred. 36 Other, 37 } 38 39 /// A string that is guaranteed to fail to parse to a [`Uuid`]. 40 /// 41 /// This type acts as a lightweight error indicator, suggesting 42 /// that the string cannot be parsed but offering no error 43 /// details. To get details, use `InvalidUuid::into_err`. 44 /// 45 /// [`Uuid`]: ../struct.Uuid.html 46 #[derive(Clone, Debug, Eq, Hash, PartialEq)] 47 pub struct InvalidUuid<'a>(pub(crate) &'a [u8]); 48 49 impl<'a> InvalidUuid<'a> { 50 /// Converts the lightweight error type into detailed diagnostics. into_err(self) -> Error51 pub fn into_err(self) -> Error { 52 // Check whether or not the input was ever actually a valid UTF8 string 53 let input_str = match std::str::from_utf8(self.0) { 54 Ok(s) => s, 55 Err(_) => return Error(ErrorKind::InvalidUTF8), 56 }; 57 58 let (uuid_str, offset, simple) = match input_str.as_bytes() { 59 [b'{', s @ .., b'}'] => (s, 1, false), 60 [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..] => { 61 (s, "urn:uuid:".len(), false) 62 } 63 s => (s, 0, true), 64 }; 65 66 let mut hyphen_count = 0; 67 let mut group_bounds = [0; 4]; 68 69 // SAFETY: the byte array came from a valid utf8 string, 70 // and is aligned along char boundaries. 71 let uuid_str = unsafe { std::str::from_utf8_unchecked(uuid_str) }; 72 73 for (index, character) in uuid_str.char_indices() { 74 let byte = character as u8; 75 if character as u32 - byte as u32 > 0 { 76 // Multibyte char 77 return Error(ErrorKind::Char { 78 character, 79 index: index + offset + 1, 80 }); 81 } else if byte == b'-' { 82 // While we search, also count group breaks 83 if hyphen_count < 4 { 84 group_bounds[hyphen_count] = index; 85 } 86 hyphen_count += 1; 87 } else if !byte.is_ascii_hexdigit() { 88 // Non-hex char 89 return Error(ErrorKind::Char { 90 character: byte as char, 91 index: index + offset + 1, 92 }); 93 } 94 } 95 96 if hyphen_count == 0 && simple { 97 // This means that we tried and failed to parse a simple uuid. 98 // Since we verified that all the characters are valid, this means 99 // that it MUST have an invalid length. 100 Error(ErrorKind::SimpleLength { 101 len: input_str.len(), 102 }) 103 } else if hyphen_count != 4 { 104 // We tried to parse a hyphenated variant, but there weren't 105 // 5 groups (4 hyphen splits). 106 Error(ErrorKind::GroupCount { 107 count: hyphen_count + 1, 108 }) 109 } else { 110 // There are 5 groups, one of them has an incorrect length 111 const BLOCK_STARTS: [usize; 5] = [0, 9, 14, 19, 24]; 112 for i in 0..4 { 113 if group_bounds[i] != BLOCK_STARTS[i + 1] - 1 { 114 return Error(ErrorKind::GroupLength { 115 group: i, 116 len: group_bounds[i] - BLOCK_STARTS[i], 117 index: offset + BLOCK_STARTS[i] + 1, 118 }); 119 } 120 } 121 122 // The last group must be too long 123 Error(ErrorKind::GroupLength { 124 group: 4, 125 len: input_str.len() - BLOCK_STARTS[4], 126 index: offset + BLOCK_STARTS[4] + 1, 127 }) 128 } 129 } 130 } 131 132 // NOTE: This impl is part of the public API. Breaking changes to it should be carefully considered 133 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 135 match self.0 { 136 ErrorKind::Char { 137 character, index, .. 138 } => { 139 write!(f, "invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-fA-F-], found `{}` at {}", character, index) 140 } 141 ErrorKind::SimpleLength { len } => { 142 write!( 143 f, 144 "invalid length: expected length 32 for simple format, found {}", 145 len 146 ) 147 } 148 ErrorKind::ByteLength { len } => { 149 write!(f, "invalid length: expected 16 bytes, found {}", len) 150 } 151 ErrorKind::GroupCount { count } => { 152 write!(f, "invalid group count: expected 5, found {}", count) 153 } 154 ErrorKind::GroupLength { group, len, .. } => { 155 let expected = [8, 4, 4, 4, 12][group]; 156 write!( 157 f, 158 "invalid group length in group {}: expected {}, found {}", 159 group, expected, len 160 ) 161 } 162 ErrorKind::InvalidUTF8 => write!(f, "non-UTF8 input"), 163 ErrorKind::Nil => write!(f, "the UUID is nil"), 164 ErrorKind::Other => write!(f, "failed to parse a UUID"), 165 } 166 } 167 } 168 169 #[cfg(feature = "std")] 170 mod std_support { 171 use super::*; 172 use crate::std::error; 173 174 impl error::Error for Error {} 175 } 176