diff --git a/macros/src/lib.rs b/macros/src/lib.rs index a3d3c532..22c6cea9 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -34,31 +34,32 @@ pub fn parse_lit(input: TokenStream) -> TokenStream { build_uuid(input.clone()).unwrap_or_else(|e| { let msg = e.to_string(); let ts = TokenStream2::from(input); - let span = match e { - Error::UuidParse(error::Error( - error::ErrorKind::InvalidCharacter { found, index, .. }, - )) => { - // Hack to find the byte width of the char - // so we can set the span accordingly. - let mut bytes = found as u32; - let mut width = 0; - while bytes != 0 { - bytes >>= 4; - width += 1; + let span = + match e { + Error::UuidParse(error::Error(error::ErrorKind::Char { + character, + index, + })) => { + let mut bytes = character as u32; + let mut width = 0; + while bytes != 0 { + bytes >>= 4; + width += 1; + } + let mut s = proc_macro2::Literal::string(""); + s.set_span(ts.span()); + s.subspan(index..index + width - 1) } - let mut s = proc_macro2::Literal::string(""); - s.set_span(ts.span()); - s.subspan(index + 1..index + width).unwrap() - } - Error::UuidParse(error::Error( - error::ErrorKind::InvalidGroupLength { found, index, .. }, - )) => { - let mut s = proc_macro2::Literal::string(""); - s.set_span(ts.span()); - s.subspan(index..index + found).unwrap() + Error::UuidParse(error::Error( + error::ErrorKind::GroupLength { index, len, .. }, + )) => { + let mut s = proc_macro2::Literal::string(""); + s.set_span(ts.span()); + s.subspan(index..index + len) + } + _ => None, } - _ => ts.span(), - }; + .unwrap_or_else(|| ts.span()); TokenStream::from(quote_spanned! {span=> compile_error!(#msg) }) @@ -85,7 +86,8 @@ fn build_uuid(input: TokenStream) -> Result { _ => return Err(Error::NonStringLiteral), }; - let bytes = parser::parse_str(&string).map_err(Error::UuidParse)?; + let bytes = parser::try_parse(&string) + .map_err(|e| Error::UuidParse(e.into_err()))?; let tokens = bytes .iter() diff --git a/shared/error.rs b/shared/error.rs index 765c73f3..030a313d 100644 --- a/shared/error.rs +++ b/shared/error.rs @@ -9,168 +9,134 @@ pub(crate) enum ErrorKind { /// Invalid character in the [`Uuid`] string. /// /// [`Uuid`]: ../struct.Uuid.html - InvalidCharacter { - /// The expected characters. - expected: &'static str, - /// The invalid character found. - found: char, - /// The invalid character position. - index: usize, - /// Indicates the [`Uuid`] starts with `urn:uuid:`. - /// - /// This is a special case for [`Urn`] adapter parsing. - /// - /// [`Uuid`]: ../Uuid.html - urn: UrnPrefix, - }, - /// Invalid number of segments in the [`Uuid`] string. + Char { character: char, index: usize }, + /// A simple [`Uuid`] didn't contain 32 characters. /// /// [`Uuid`]: ../struct.Uuid.html - InvalidGroupCount { - /// The expected number of segments. - expected: ExpectedLength, - /// The number of segments found. - found: usize, - }, - /// Invalid length of a segment in a [`Uuid`] string. + SimpleLength { len: usize }, + /// A hyphenated [`Uuid`] didn't contain 5 groups /// /// [`Uuid`]: ../struct.Uuid.html - InvalidGroupLength { - /// The expected length of the segment. - expected: ExpectedLength, - /// The length of segment found. - found: usize, - /// The segment with invalid length. - group: usize, - /// The index of where the group starts - index: usize, - }, - /// Invalid length of the [`Uuid`] string. + GroupCount { count: usize }, + /// A hyphenated [`Uuid`] had a group that wasn't the right length /// /// [`Uuid`]: ../struct.Uuid.html - InvalidLength { - /// The expected length(s). - expected: ExpectedLength, - /// The invalid length found. - found: usize, + GroupLength { + group: usize, + len: usize, + index: usize, }, } -/// The expected length. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub(crate) enum ExpectedLength { - /// Expected any one of the given values. - Any(&'static [usize]), - /// Expected the given value. - Exact(usize), -} +/// A string that is guaranteed to fail to parse to a [`Uuid`]. +/// +/// This type acts as a lightweight error indicator, suggesting +/// that the string cannot be parsed but offering no error +/// details. To get details, use `InvalidUuid::into_err`. +/// +/// [`Uuid`]: ../struct.Uuid.html +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct InvalidUuid<'a>(pub(crate) &'a str); -/// Urn prefix value. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub(crate) enum UrnPrefix { - /// The `urn:uuid:` prefix should optionally provided. - Optional, -} +impl<'a> InvalidUuid<'a> { + /// Converts the lightweight error type into detailed diagnostics. + pub fn into_err(self) -> Error { + let (s, offset, simple) = match self.0.as_bytes() { + [b'{', s @ .., b'}'] => (s, 1, false), + [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..] => { + (s, "urn:uuid:".len(), false) + } + s => (s, 0, true), + }; -impl fmt::Display for ExpectedLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ExpectedLength::Any(crits) => write!(f, "one of {:?}", crits), - ExpectedLength::Exact(crit) => write!(f, "{}", crit), + let mut hyphen_count = 0; + let mut group_bounds = [0; 4]; + // SAFETY: the byte array came from a valid utf8 string, + // and is aligned along char boundries. + let string = unsafe { std::str::from_utf8_unchecked(s) }; + for (index, character) in string.char_indices() { + let byte = character as u8; + if character as u32 - byte as u32 > 0 { + // Multibyte char + return Error(ErrorKind::Char { + character, + index: index + offset + 1, + }); + } else if byte == b'-' { + // While we search, also count group breaks + if hyphen_count < 4 { + group_bounds[hyphen_count] = index; + } + hyphen_count += 1; + } else if !matches!(byte, b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F') { + // Non-hex char + return Error(ErrorKind::Char { + character: byte as char, + index: index + offset + 1, + }); + } } - } -} - -impl Error { - pub(crate) fn character(found: char, index: usize, offset: usize) -> Self { - Error(ErrorKind::InvalidCharacter { - expected: "0123456789abcdefABCDEF-", - found, - index: index + offset, - urn: UrnPrefix::Optional, - }) - } - pub(crate) fn group_count(expected: ExpectedLength, found: usize) -> Self { - Error(ErrorKind::InvalidGroupCount { expected, found }) - } - - pub(crate) fn group_length( - expected: ExpectedLength, - found: usize, - group: usize, - offset: usize, - ) -> Self { - Error(ErrorKind::InvalidGroupLength { - expected, - found, - group, - index: [1, 10, 15, 20, 25][group] + offset, - }) - } - - pub(crate) fn length(expected: ExpectedLength, found: usize) -> Self { - Error(ErrorKind::InvalidLength { expected, found }) - } -} + if hyphen_count == 0 && simple { + // This means that we tried and failed to parse a simple uuid. + // Since we verified that all the characters are valid, this means + // that it MUST have an invalid length. + Error(ErrorKind::SimpleLength { len: s.len() }) + } else if hyphen_count != 4 { + // We tried to parse a hyphenated variant, but there weren't + // 5 groups (4 hyphen splits). + Error(ErrorKind::GroupCount { + count: hyphen_count + 1, + }) + } else { + // There are 5 groups, one of them has an incorrect length + const BLOCK_STARTS: [usize; 5] = [0, 9, 14, 19, 24]; + for i in 0..4 { + if group_bounds[i] != BLOCK_STARTS[i + 1] - 1 { + return Error(ErrorKind::GroupLength { + group: i, + len: group_bounds[i] - BLOCK_STARTS[i], + index: offset + BLOCK_STARTS[i] + 1, + }); + } + } -impl From for Error { - fn from(kind: ErrorKind) -> Self { - Error(kind) + // The last group must be too long + Error(ErrorKind::GroupLength { + group: 4, + len: s.len() - BLOCK_STARTS[4], + index: offset + BLOCK_STARTS[4] + 1, + }) + } } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}: ", - match self.0 { - ErrorKind::InvalidCharacter { .. } => "invalid character", - ErrorKind::InvalidGroupCount { .. } => - "invalid number of groups", - ErrorKind::InvalidGroupLength { .. } => "invalid group length", - ErrorKind::InvalidLength { .. } => "invalid length", - } - )?; - + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0 { - ErrorKind::InvalidCharacter { - expected, - found, - index, - urn, + ErrorKind::Char { + character, index, .. } => { - let urn_str = match urn { - UrnPrefix::Optional => { - " an optional prefix of `urn:uuid:` followed by" - } - }; - + write!(f, "invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `{}` at {}", character, index) + } + ErrorKind::SimpleLength { len } => { + write!( + f, + "invalid length: expected length 32 for simple format, found {}", + len + ) + } + ErrorKind::GroupCount { count } => { + write!(f, "invalid group count: expected 5, found {}", count) + } + ErrorKind::GroupLength { group, len, .. } => { + let expected = [8, 4, 4, 4, 12][group]; write!( f, - "expected{} {}, found {} at {}", - urn_str, expected, found, index + "invalid group length in group {}: expected {}, found {}", + group, expected, len ) } - ErrorKind::InvalidGroupCount { - ref expected, - found, - } => write!(f, "expected {}, found {}", expected, found), - ErrorKind::InvalidGroupLength { - ref expected, - found, - group, - .. - } => write!( - f, - "expected {}, found {} in group {}", - expected, found, group, - ), - ErrorKind::InvalidLength { - ref expected, - found, - } => write!(f, "expected {}, found {}", expected, found), } } } diff --git a/shared/parser.rs b/shared/parser.rs index e42a5405..c47c93e9 100644 --- a/shared/parser.rs +++ b/shared/parser.rs @@ -9,148 +9,91 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::{error::*, std::str}; - -/// Check if the length matches any of the given criteria lengths. -fn len_matches_any(len: usize, crits: &[usize]) -> bool { - for crit in crits { - if len == *crit { - return true; +use crate::error::InvalidUuid; + +pub const fn try_parse(input: &str) -> Result<[u8; 16], InvalidUuid> { + const fn parse_blocks<'a>( + s: &'a [u8], + hyphenated: bool, + ) -> Option<[u8; 16]> { + let block_table = if hyphenated { + match (s[8], s[13], s[18], s[23]) { + (b'-', b'-', b'-', b'-') => [0, 4, 9, 14, 19, 24, 28, 32], + _ => return None, + } + } else { + [0, 4, 8, 12, 16, 20, 24, 28] + }; + + let mut buf = [0; 16]; + let mut j = 0; + while j < 8 { + let i = block_table[j]; + // Check 4 bytes at a time + let h1 = HEX_TABLE[s[i] as usize]; + let h2 = HEX_TABLE[s[i + 1] as usize]; + let h3 = HEX_TABLE[s[i + 2] as usize]; + let h4 = HEX_TABLE[s[i + 3] as usize]; + // If any of the bytes aren't valid, they will be 0xff, making this + // fail + if h1 | h2 | h3 | h4 == 0xff { + return None; + } + buf[j * 2] = SHL4_TABLE[h1 as usize] | h2; + buf[j * 2 + 1] = SHL4_TABLE[h3 as usize] | h4; + j += 1; } + Some(buf) } - false + let b = input.as_bytes(); + let maybe_parsed = match (b.len(), b) { + (32, s) => parse_blocks(s, false), + (36, s) + | (38, [b'{', s @ .., b'}']) + | ( + 45, + [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..], + ) => parse_blocks(s, true), + _ => None, + }; + match maybe_parsed { + Some(b) => Ok(b), + None => Err(InvalidUuid(input)), + } } -// Accumulated length of each hyphenated group in hex digits. -const ACC_GROUP_LENS: [usize; 5] = [8, 12, 16, 20, 32]; - -// Length of each hyphenated group in hex digits. -pub const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; - -const URN_PREFIX: &str = "urn:uuid:"; - -pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { - // Ensure length is valid for any of the supported formats - let len = input.len(); - - let mut start = 0; - // Check for a URN prefixed UUID - if len == 45 { - if let Some(stripped) = input.strip_prefix(URN_PREFIX) { - input = stripped; - start = URN_PREFIX.len(); - } - } - // Check for a Microsoft GUID wrapped in {} - else if len == 38 { - if let Some(stripped) = - input.strip_prefix('{').and_then(|s| s.strip_suffix('}')) - { - input = stripped; - start = 1; +type Table = [u8; 256]; + +const fn generate_lookup_table() -> Table { + let mut buf = [0u8; 256]; + let mut i = 0u8; + loop { + buf[i as usize] = match i { + b'0'..=b'9' => i - b'0', + b'a'..=b'f' => i - b'a' + 10, + b'A'..=b'F' => i - b'A' + 10, + _ => 0xff, + }; + if i == 255 { + return buf; } + i += 1 } - // In other cases, check for a simple or hyphenated UUID - else if !len_matches_any(len, &[36, 32]) { - return Err(Error::length(ExpectedLength::Any(&[36, 32]), len)); - } - - // `digit` counts only hexadecimal digits, `i_char` counts all chars. - let mut digit = 0; - let mut group = 0; - let mut acc = 0; - let mut buffer = [0u8; 16]; +} - for (i_char, character) in input.char_indices() { - let chr = character as u8; - if digit as usize >= 32 && group != 4 { - if group == 0 { - return Err(Error::length(ExpectedLength::Any(&[36, 32]), len)); - } +const HEX_TABLE: Table = generate_lookup_table(); - return Err(Error::group_count( - ExpectedLength::Any(&[1, 5]), - group + 1, - )); +const fn generate_shl4_table() -> Table { + let mut buf = [0u8; 256]; + let mut i = 0u8; + loop { + buf[i as usize] = i.wrapping_shl(4); + if i == 255 { + return buf; } - - if digit % 2 == 0 { - // First digit of the byte. - match chr { - // Calculate upper half. - b'0'..=b'9' => acc = chr - b'0', - b'a'..=b'f' => acc = chr - b'a' + 10, - b'A'..=b'F' => acc = chr - b'A' + 10, - // Found a group delimiter - b'-' => { - if ACC_GROUP_LENS[group] as u8 != digit { - // Calculate how many digits this group consists of - // in the input. - let found = if group > 0 { - digit as usize - ACC_GROUP_LENS[group - 1] - } else { - digit as usize - }; - - return Err(Error::group_length( - ExpectedLength::Exact(GROUP_LENS[group]), - found, - group, - start, - )); - } - // Next group, decrement digit, it is incremented again - // at the bottom. - group += 1; - digit -= 1; - } - _ => { - return Err(Error::character(character, i_char, start)); - } - } - } else { - // Second digit of the byte, shift the upper half. - acc *= 16; - match chr { - b'0'..=b'9' => acc += chr - b'0', - b'a'..=b'f' => acc += chr - b'a' + 10, - b'A'..=b'F' => acc += chr - b'A' + 10, - b'-' => { - // The byte isn't complete yet. - let found = if group > 0 { - digit - ACC_GROUP_LENS[group - 1] as u8 - } else { - digit - }; - - return Err(Error::group_length( - ExpectedLength::Exact(GROUP_LENS[group]), - found as usize, - group, - start, - )); - } - _ => { - // let found = input[i_char..].chars().next().unwrap(); - // let found = char::from(chr); - return Err(Error::character(character, i_char, start)); - } - } - buffer[(digit / 2) as usize] = acc; - } - digit += 1; - } - - // Now check the last group. - if ACC_GROUP_LENS[4] as u8 != digit { - return Err(Error::group_length( - ExpectedLength::Exact(GROUP_LENS[4]), - digit as usize - ACC_GROUP_LENS[3], - group, - start, - )); + i += 1; } - - Ok(buffer) } + +const SHL4_TABLE: Table = generate_shl4_table(); diff --git a/src/builder.rs b/src/builder.rs index 6fc39909..2e571a01 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -304,11 +304,7 @@ impl Uuid { /// ``` pub fn from_slice(b: &[u8]) -> Result { if b.len() != 16 { - return Err(ErrorKind::InvalidLength { - expected: ExpectedLength::Exact(16), - found: b.len(), - } - .into()); + return Err(Error(ErrorKind::SimpleLength { len: b.len() * 2 })); } let mut bytes: Bytes = [0; 16]; @@ -349,11 +345,7 @@ impl Uuid { /// ``` pub fn from_slice_le(b: &[u8]) -> Result { if b.len() != 16 { - return Err(ErrorKind::InvalidLength { - expected: ExpectedLength::Exact(16), - found: b.len(), - } - .into()); + return Err(Error(ErrorKind::SimpleLength { len: b.len() * 2 })); } let mut bytes: Bytes = [0; 16]; diff --git a/src/external/serde_support.rs b/src/external/serde_support.rs index c14bc4c5..3204b185 100644 --- a/src/external/serde_support.rs +++ b/src/external/serde_support.rs @@ -301,7 +301,7 @@ mod serde_tests { fn test_de_failure() { serde_test::assert_de_tokens_error::>( &[Token::Str("hello_world")], - "UUID parsing failed: invalid length: expected one of [36, 32], found 11", + "UUID parsing failed: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `h` at 1", ); serde_test::assert_de_tokens_error::>( diff --git a/src/parser.rs b/src/parser.rs index 4abf35a4..2c00ad38 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -42,8 +42,8 @@ impl Uuid { /// Parses a `Uuid` from a string of hexadecimal digits with optional /// hyphens. /// - /// Any of the formats generated by this module (simple, hyphenated, urn) - /// are supported by this parsing function. + /// Any of the formats generated by this module (simple, hyphenated, urn, + /// Microsoft GUID) are supported by this parsing function. /// /// # Examples /// @@ -60,14 +60,24 @@ impl Uuid { /// # } /// ``` pub fn parse_str(input: &str) -> Result { - Ok(Uuid::from_bytes(imp::parse_str(input)?)) + imp::try_parse(input) + .map(Uuid::from_bytes) + .map_err(InvalidUuid::into_err) + } + + /// Intended to replace `Uuid::parse_str` + pub const fn try_parse(input: &str) -> Result { + match imp::try_parse(input) { + Ok(bytes) => Ok(Uuid::from_bytes(bytes)), + Err(e) => Err(e), + } } } #[cfg(test)] mod tests { use super::*; - use crate::{fmt, std::string::ToString, tests::new}; + use crate::{std::string::ToString, tests::new}; #[test] fn test_parse_uuid_v4_valid() { @@ -112,222 +122,166 @@ mod tests { #[test] fn test_parse_uuid_v4_invalid() { - const EXPECTED_UUID_LENGTHS: ExpectedLength = ExpectedLength::Any(&[ - fmt::Hyphenated::LENGTH, - fmt::Simple::LENGTH, - ]); - - const EXPECTED_GROUP_COUNTS: ExpectedLength = - ExpectedLength::Any(&[1, 5]); - - const EXPECTED_CHARS: &'static str = "0123456789abcdefABCDEF-"; - // Invalid assert_eq!( Uuid::parse_str(""), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 0, - })) + Err(Error(ErrorKind::SimpleLength { len: 0 })) ); assert_eq!( Uuid::parse_str("!"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 1 + Err(Error(ErrorKind::Char { + character: '!', + index: 1, })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 37, + Err(Error(ErrorKind::GroupLength { + group: 4, + len: 13, + index: 25, })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 35 + Err(Error(ErrorKind::GroupLength { + group: 3, + len: 3, + index: 20, })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4"), - Err(Error(ErrorKind::InvalidCharacter { - expected: EXPECTED_CHARS, - found: 'G', - index: 20, - urn: UrnPrefix::Optional, + Err(Error(ErrorKind::Char { + character: 'G', + index: 21, })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2F4faaFB6BFF329BF39FA1E4"), - Err(Error(ErrorKind::InvalidGroupCount { - expected: EXPECTED_GROUP_COUNTS, - found: 2 - })) + Err(Error(ErrorKind::GroupCount { count: 2 })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faaFB6BFF329BF39FA1E4"), - Err(Error(ErrorKind::InvalidGroupCount { - expected: EXPECTED_GROUP_COUNTS, - found: 3, - })) + Err(Error(ErrorKind::GroupCount { count: 3 })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4"), - Err(Error(ErrorKind::InvalidGroupCount { - expected: EXPECTED_GROUP_COUNTS, - found: 4, - })) + Err(Error(ErrorKind::GroupCount { count: 4 })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faa"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 18, - })) + Err(Error(ErrorKind::GroupCount { count: 3 })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4"), - Err(Error(ErrorKind::InvalidCharacter { - expected: EXPECTED_CHARS, - found: 'X', - index: 18, - urn: UrnPrefix::Optional, + Err(Error(ErrorKind::Char { + character: 'X', + index: 19, })) ); assert_eq!( Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41"), - Err(Error(ErrorKind::InvalidCharacter { - expected: EXPECTED_CHARS, - found: '{', - index: 0, - urn: UrnPrefix::Optional, + Err(Error(ErrorKind::Char { + character: '{', + index: 1, })) ); assert_eq!( Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41}"), - Err(Error(ErrorKind::InvalidLength { - expected: ExpectedLength::Any(&[36, 32]), - found: 39 - })) + Err(Error(ErrorKind::GroupCount { count: 3 })) ); assert_eq!( Uuid::parse_str("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4"), - Err(Error(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(4), - found: 3, + Err(Error(ErrorKind::GroupLength { group: 1, + len: 3, index: 10, })) ); - // (group, found, expecting) - // + + // // (group, found, expecting) + // // assert_eq!( Uuid::parse_str("01020304-1112-2122-3132-41424344"), - Err(Error(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(12), - found: 8, + Err(Error(ErrorKind::GroupLength { group: 4, + len: 8, index: 25, })) ); assert_eq!( Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 31, - })) + Err(Error(ErrorKind::SimpleLength { len: 31 })) ); assert_eq!( Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c88"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 33, - })) + Err(Error(ErrorKind::SimpleLength { len: 33 })) ); assert_eq!( Uuid::parse_str("67e5504410b1426f9247bb680e5fe0cg8"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 33, + Err(Error(ErrorKind::Char { + character: 'g', + index: 32, })) ); assert_eq!( Uuid::parse_str("67e5504410b1426%9247bb680e5fe0c8"), - Err(Error(ErrorKind::InvalidCharacter { - expected: EXPECTED_CHARS, - found: '%', - index: 15, - urn: UrnPrefix::Optional, + Err(Error(ErrorKind::Char { + character: '%', + index: 16, })) ); assert_eq!( Uuid::parse_str("231231212212423424324323477343246663"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 36, - })) + Err(Error(ErrorKind::SimpleLength { len: 36 })) ); assert_eq!( Uuid::parse_str("{00000000000000000000000000000000}"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 34, - })) + Err(Error(ErrorKind::GroupCount { count: 1 })) ); - // Test error reporting assert_eq!( Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c"), - Err(Error(ErrorKind::InvalidLength { - expected: EXPECTED_UUID_LENGTHS, - found: 31, - })) + Err(Error(ErrorKind::SimpleLength { len: 31 })) ); + assert_eq!( Uuid::parse_str("67e550X410b1426f9247bb680e5fe0cd"), - Err(Error(ErrorKind::InvalidCharacter { - expected: EXPECTED_CHARS, - found: 'X', - index: 6, - urn: UrnPrefix::Optional, + Err(Error(ErrorKind::Char { + character: 'X', + index: 7, })) ); + assert_eq!( Uuid::parse_str("67e550-4105b1426f9247bb680e5fe0c"), - Err(Error(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(8), - found: 6, - group: 0, - index: 1, - })) + Err(Error(ErrorKind::GroupCount { count: 2 })) ); + assert_eq!( Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4"), - Err(Error(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(4), - found: 5, + Err(Error(ErrorKind::GroupLength { group: 3, + len: 5, index: 20, })) ); diff --git a/tests/ui/compile_fail.stderr b/tests/ui/compile_fail.stderr deleted file mode 100644 index 9f499ac5..00000000 --- a/tests/ui/compile_fail.stderr +++ /dev/null @@ -1 +0,0 @@ -error: couldn't read $DIR/tests/ui/compile_fail: Is a directory (os error 21) diff --git a/tests/ui/compile_fail/invalid_parse.rs b/tests/ui/compile_fail/invalid_parse.rs index a8c58a3e..f67456b9 100644 --- a/tests/ui/compile_fail/invalid_parse.rs +++ b/tests/ui/compile_fail/invalid_parse.rs @@ -39,4 +39,7 @@ const _: Uuid = uuid!("504410😎👍aab1426f9247bb680e5fe0c8"); const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-👍5-32BF39FA1E4}"); +const _: Uuid = uuid!("F916"); +const _: Uuid = uuid!("F916x"); + fn main() {} diff --git a/tests/ui/compile_fail/invalid_parse.stderr b/tests/ui/compile_fail/invalid_parse.stderr index ac05c676..7fd8a0a7 100644 --- a/tests/ui/compile_fail/invalid_parse.stderr +++ b/tests/ui/compile_fail/invalid_parse.stderr @@ -1,167 +1,179 @@ -error: invalid length: expected one of [36, 32], found 0 +error: invalid length: expected length 32 for simple format, found 0 --> tests/ui/compile_fail/invalid_parse.rs:3:23 | 3 | const _: Uuid = uuid!(""); | ^^ -error: invalid length: expected one of [36, 32], found 1 - --> tests/ui/compile_fail/invalid_parse.rs:4:23 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `!` at 1 + --> tests/ui/compile_fail/invalid_parse.rs:4:24 | 4 | const _: Uuid = uuid!("!"); - | ^^^ + | ^ -error: invalid length: expected one of [36, 32], found 37 - --> tests/ui/compile_fail/invalid_parse.rs:5:23 +error: invalid group length in group 4: expected 12, found 13 + --> tests/ui/compile_fail/invalid_parse.rs:5:48 | 5 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ -error: invalid length: expected one of [36, 32], found 35 - --> tests/ui/compile_fail/invalid_parse.rs:6:23 +error: invalid group length in group 3: expected 4, found 3 + --> tests/ui/compile_fail/invalid_parse.rs:6:43 | 6 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found G at 20 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `G` at 21 --> tests/ui/compile_fail/invalid_parse.rs:7:44 | 7 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4"); | ^ -error: invalid number of groups: expected one of [1, 5], found 4 +error: invalid group count: expected 5, found 4 --> tests/ui/compile_fail/invalid_parse.rs:8:23 | 8 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid length: expected one of [36, 32], found 18 +error: invalid group count: expected 5, found 3 --> tests/ui/compile_fail/invalid_parse.rs:9:23 | 9 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa"); | ^^^^^^^^^^^^^^^^^^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found X at 18 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `X` at 19 --> tests/ui/compile_fail/invalid_parse.rs:10:42 | 10 | const _: Uuid = uuid!("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4"); | ^ -error: invalid group length: expected 4, found 3 in group 1 +error: invalid group length in group 1: expected 4, found 3 --> tests/ui/compile_fail/invalid_parse.rs:11:33 | 11 | const _: Uuid = uuid!("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4"); | ^^^ -error: invalid group length: expected 12, found 8 in group 4 +error: invalid group length in group 4: expected 12, found 8 --> tests/ui/compile_fail/invalid_parse.rs:12:48 | 12 | const _: Uuid = uuid!("01020304-1112-2122-3132-41424344"); | ^^^^^^^^ -error: invalid length: expected one of [36, 32], found 33 +error: invalid length: expected length 32 for simple format, found 33 --> tests/ui/compile_fail/invalid_parse.rs:13:23 | 13 | const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c88"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid length: expected one of [36, 32], found 33 - --> tests/ui/compile_fail/invalid_parse.rs:14:23 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `g` at 32 + --> tests/ui/compile_fail/invalid_parse.rs:14:55 | 14 | const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0cg8"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^ -error: invalid length: expected one of [36, 32], found 31 +error: invalid length: expected length 32 for simple format, found 31 --> tests/ui/compile_fail/invalid_parse.rs:18:23 | 18 | const _: Uuid = uuid!("67e5504410b1426f9247bb680e5fe0c"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found X at 6 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `X` at 7 --> tests/ui/compile_fail/invalid_parse.rs:19:30 | 19 | const _: Uuid = uuid!("67e550X410b1426f9247bb680e5fe0cd"); | ^ -error: invalid group length: expected 8, found 6 in group 0 - --> tests/ui/compile_fail/invalid_parse.rs:20:24 +error: invalid group count: expected 5, found 2 + --> tests/ui/compile_fail/invalid_parse.rs:20:23 | 20 | const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c"); - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid group length: expected 4, found 5 in group 3 +error: invalid group length in group 3: expected 4, found 5 --> tests/ui/compile_fail/invalid_parse.rs:21:43 | 21 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4"); | ^^^^^ -error: invalid length: expected one of [36, 32], found 35 - --> tests/ui/compile_fail/invalid_parse.rs:24:23 +error: invalid group length in group 3: expected 4, found 3 + --> tests/ui/compile_fail/invalid_parse.rs:24:43 | 24 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found G at 20 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `G` at 21 --> tests/ui/compile_fail/invalid_parse.rs:25:44 | 25 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4"); | ^ -error: invalid group length: expected 12, found 8 in group 4 +error: invalid group length in group 4: expected 12, found 8 --> tests/ui/compile_fail/invalid_parse.rs:26:48 | 26 | const _: Uuid = uuid!("01020304-1112-2122-3132-41424344"); | ^^^^^^^^ -error: invalid number of groups: expected one of [1, 5], found 4 +error: invalid group count: expected 5, found 4 --> tests/ui/compile_fail/invalid_parse.rs:27:23 | 27 | const _: Uuid = uuid!("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found G at 29 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `G` at 30 --> tests/ui/compile_fail/invalid_parse.rs:28:53 | 28 | const _: Uuid = uuid!("urn:uuid:F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4"); | ^ -error: invalid group length: expected 4, found 5 in group 3 +error: invalid group length in group 3: expected 4, found 5 --> tests/ui/compile_fail/invalid_parse.rs:29:52 | 29 | const _: Uuid = uuid!("urn:uuid:F9168C5E-CEB2-4faa-B2cBF-32BF39FA1E4"); | ^^^^^ -error: invalid group length: expected 4, found 5 in group 3 +error: invalid group length in group 3: expected 4, found 5 --> tests/ui/compile_fail/invalid_parse.rs:30:44 | 30 | const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-B0a75-32BF39FA1E4}"); | ^^^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found z at 29 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `z` at 30 --> tests/ui/compile_fail/invalid_parse.rs:32:53 | 32 | const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-B6BF-329Bz39FA1E4}"); | ^ -error: invalid group length: expected 8, found 6 in group 0 - --> tests/ui/compile_fail/invalid_parse.rs:35:24 +error: invalid group count: expected 5, found 2 + --> tests/ui/compile_fail/invalid_parse.rs:35:23 | 35 | const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c"); - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found 岡 at 6 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `岡` at 7 --> tests/ui/compile_fail/invalid_parse.rs:37:30 | 37 | const _: Uuid = uuid!("504410岡林aab1426f9247bb680e5fe0c8"); | ^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found 😎 at 6 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `😎` at 7 --> tests/ui/compile_fail/invalid_parse.rs:38:30 | 38 | const _: Uuid = uuid!("504410😎👍aab1426f9247bb680e5fe0c8"); | ^^ -error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found 👍 at 20 +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `👍` at 21 --> tests/ui/compile_fail/invalid_parse.rs:40:44 | 40 | const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-👍5-32BF39FA1E4}"); | ^^ + +error: invalid length: expected length 32 for simple format, found 4 + --> tests/ui/compile_fail/invalid_parse.rs:42:23 + | +42 | const _: Uuid = uuid!("F916"); + | ^^^^^^ + +error: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `x` at 5 + --> tests/ui/compile_fail/invalid_parse.rs:43:28 + | +43 | const _: Uuid = uuid!("F916x"); + | ^