diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 86ef0780..a3d3c532 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -36,11 +36,19 @@ pub fn parse_lit(input: TokenStream) -> TokenStream { let ts = TokenStream2::from(input); let span = match e { Error::UuidParse(error::Error( - error::ErrorKind::InvalidCharacter { index, .. }, + 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 mut s = proc_macro2::Literal::string(""); s.set_span(ts.span()); - s.subspan(index + 1..=index + 1).unwrap() + s.subspan(index + 1..index + width).unwrap() } Error::UuidParse(error::Error( error::ErrorKind::InvalidGroupLength { found, index, .. }, diff --git a/shared/error.rs b/shared/error.rs index b2e0efa6..765c73f3 100644 --- a/shared/error.rs +++ b/shared/error.rs @@ -81,6 +81,39 @@ impl fmt::Display for ExpectedLength { } } +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 }) + } +} + impl From for Error { fn from(kind: ErrorKind) -> Self { Error(kind) diff --git a/shared/parser.rs b/shared/parser.rs index 8cc0d284..e42a5405 100644 --- a/shared/parser.rs +++ b/shared/parser.rs @@ -36,22 +36,24 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { let mut start = 0; // Check for a URN prefixed UUID - if len == 45 && input.starts_with(URN_PREFIX) { - input = &input[URN_PREFIX.len()..]; - start += URN_PREFIX.len(); + 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 && input.starts_with("{") && input.ends_with("}") { - input = &input[1..input.len() - 1]; - start += 1; + else if len == 38 { + if let Some(stripped) = + input.strip_prefix('{').and_then(|s| s.strip_suffix('}')) + { + input = stripped; + start = 1; + } } // In other cases, check for a simple or hyphenated UUID else if !len_matches_any(len, &[36, 32]) { - return Err(ErrorKind::InvalidLength { - expected: ExpectedLength::Any(&[36, 32]), - found: len, - } - .into()); + return Err(Error::length(ExpectedLength::Any(&[36, 32]), len)); } // `digit` counts only hexadecimal digits, `i_char` counts all chars. @@ -60,21 +62,17 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { let mut acc = 0; let mut buffer = [0u8; 16]; - for (i_char, chr) in input.bytes().enumerate() { + 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(ErrorKind::InvalidLength { - expected: ExpectedLength::Any(&[36, 32]), - found: len, - } - .into()); + return Err(Error::length(ExpectedLength::Any(&[36, 32]), len)); } - return Err(ErrorKind::InvalidGroupCount { - expected: ExpectedLength::Any(&[1, 5]), - found: group + 1, - } - .into()); + return Err(Error::group_count( + ExpectedLength::Any(&[1, 5]), + group + 1, + )); } if digit % 2 == 0 { @@ -90,20 +88,17 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { // Calculate how many digits this group consists of // in the input. let found = if group > 0 { - digit - ACC_GROUP_LENS[group - 1] as u8 + digit as usize - ACC_GROUP_LENS[group - 1] } else { - digit + digit as usize }; - let index = - ACC_GROUP_LENS[group - 1] + group + 1 + start; - return Err(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(GROUP_LENS[group]), - found: found as usize, + return Err(Error::group_length( + ExpectedLength::Exact(GROUP_LENS[group]), + found, group, - index, - } - .into()); + start, + )); } // Next group, decrement digit, it is incremented again // at the bottom. @@ -111,13 +106,7 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { digit -= 1; } _ => { - return Err(ErrorKind::InvalidCharacter { - expected: "0123456789abcdefABCDEF-", - found: input[i_char..].chars().next().unwrap(), - index: i_char + start, - urn: UrnPrefix::Optional, - } - .into()); + return Err(Error::character(character, i_char, start)); } } } else { @@ -135,23 +124,17 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { digit }; - let index = ACC_GROUP_LENS[group - 1] + group + 1 + start; - return Err(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(GROUP_LENS[group]), - found: found as usize, + return Err(Error::group_length( + ExpectedLength::Exact(GROUP_LENS[group]), + found as usize, group, - index, - } - .into()); + start, + )); } _ => { - return Err(ErrorKind::InvalidCharacter { - expected: "0123456789abcdefABCDEF-", - found: input[i_char..].chars().next().unwrap(), - index: i_char + start, - urn: UrnPrefix::Optional, - } - .into()); + // 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; @@ -161,14 +144,12 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> { // Now check the last group. if ACC_GROUP_LENS[4] as u8 != digit { - let index = ACC_GROUP_LENS[group - 1] + group + 1 + start; - return Err(ErrorKind::InvalidGroupLength { - expected: ExpectedLength::Exact(GROUP_LENS[4]), - found: (digit as usize - ACC_GROUP_LENS[3]), + return Err(Error::group_length( + ExpectedLength::Exact(GROUP_LENS[4]), + digit as usize - ACC_GROUP_LENS[3], group, - index, - } - .into()); + start, + )); } Ok(buffer) diff --git a/src/parser.rs b/src/parser.rs index 46a7912f..4abf35a4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -209,9 +209,19 @@ mod tests { assert_eq!( Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41"), + Err(Error(ErrorKind::InvalidCharacter { + expected: EXPECTED_CHARS, + found: '{', + index: 0, + urn: UrnPrefix::Optional, + })) + ); + + assert_eq!( + Uuid::parse_str("{F9168C5E-CEB2-4faa9B6BFF329BF39FA1E41}"), Err(Error(ErrorKind::InvalidLength { expected: ExpectedLength::Any(&[36, 32]), - found: 38 + found: 39 })) ); diff --git a/tests/ui/compile_fail/invalid_parse.rs b/tests/ui/compile_fail/invalid_parse.rs index 27fcb932..a8c58a3e 100644 --- a/tests/ui/compile_fail/invalid_parse.rs +++ b/tests/ui/compile_fail/invalid_parse.rs @@ -31,4 +31,12 @@ const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-B0a75-32BF39FA1E4}"); const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-B6BF-329Bz39FA1E4}"); +// group 0 has invalid length +const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c"); + +const _: Uuid = uuid!("504410岡林aab1426f9247bb680e5fe0c8"); +const _: Uuid = uuid!("504410😎👍aab1426f9247bb680e5fe0c8"); + +const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-👍5-32BF39FA1E4}"); + fn main() {} diff --git a/tests/ui/compile_fail/invalid_parse.stderr b/tests/ui/compile_fail/invalid_parse.stderr index 6eff4d40..ac05c676 100644 --- a/tests/ui/compile_fail/invalid_parse.stderr +++ b/tests/ui/compile_fail/invalid_parse.stderr @@ -82,14 +82,11 @@ error: invalid character: expected an optional prefix of `urn:uuid:` followed by 19 | const _: Uuid = uuid!("67e550X410b1426f9247bb680e5fe0cd"); | ^ -error: proc macro panicked - --> tests/ui/compile_fail/invalid_parse.rs:20:17 +error: invalid group length: expected 8, found 6 in group 0 + --> tests/ui/compile_fail/invalid_parse.rs:20:24 | 20 | const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: message: attempt to subtract with overflow - = note: this error originates in the macro `uuid` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^ error: invalid group length: expected 4, found 5 in group 3 --> tests/ui/compile_fail/invalid_parse.rs:21:43 @@ -144,3 +141,27 @@ error: invalid character: expected an optional prefix of `urn:uuid:` followed by | 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 + | +35 | const _: Uuid = uuid!("67e550-4105b1426f9247bb680e5fe0c"); + | ^^^^^^ + +error: invalid character: expected an optional prefix of `urn:uuid:` followed by 0123456789abcdefABCDEF-, found 岡 at 6 + --> 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 + --> 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 + --> tests/ui/compile_fail/invalid_parse.rs:40:44 + | +40 | const _: Uuid = uuid!("{F9168C5E-CEB2-4faa-👍5-32BF39FA1E4}"); + | ^^