Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #541 from KodrAus/feat/macros-scaffold
Scaffold out a uuid-macros crate that shares the parser
- Loading branch information
Showing
9 changed files
with
368 additions
and
301 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "uuid-macros" | ||
version = "0.0.0" | ||
edition = "2018" | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use std; | ||
|
||
#[path = "../../shared/error.rs"] | ||
#[allow(dead_code)] | ||
mod error; | ||
|
||
#[path = "../../shared/parser.rs"] | ||
#[allow(dead_code)] | ||
mod parser; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
#[test] | ||
fn it_works() { | ||
assert_eq!(2 + 2, 4); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Shared source | ||
|
||
This source is shared between `uuid` and `uuid-macros` to avoid circular dependencies or creating additional dependencies for `uuid`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use crate::std::fmt; | ||
|
||
/// A general error that can occur when working with UUIDs. | ||
#[derive(Clone, Debug, Eq, Hash, PartialEq)] | ||
pub struct Error(pub(crate) ErrorKind); | ||
|
||
#[derive(Clone, Debug, Eq, Hash, PartialEq)] | ||
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. | ||
/// | ||
/// [`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. | ||
/// | ||
/// [`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, | ||
}, | ||
/// Invalid length of the [`Uuid`] string. | ||
/// | ||
/// [`Uuid`]: ../struct.Uuid.html | ||
InvalidLength { | ||
/// The expected length(s). | ||
expected: ExpectedLength, | ||
/// The invalid length found. | ||
found: 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), | ||
} | ||
|
||
/// 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 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), | ||
} | ||
} | ||
} | ||
|
||
impl From<ErrorKind> for Error { | ||
fn from(kind: ErrorKind) -> Self { | ||
Error(kind) | ||
} | ||
} | ||
|
||
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", | ||
} | ||
)?; | ||
|
||
match self.0 { | ||
ErrorKind::InvalidCharacter { | ||
expected, | ||
found, | ||
index, | ||
urn, | ||
} => { | ||
let urn_str = match urn { | ||
UrnPrefix::Optional => { | ||
" an optional prefix of `urn:uuid:` followed by" | ||
} | ||
}; | ||
|
||
write!( | ||
f, | ||
"expected{} {}, found {} at {}", | ||
urn_str, expected, found, index | ||
) | ||
} | ||
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), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
mod std_support { | ||
use super::*; | ||
use crate::std::error; | ||
|
||
impl error::Error for Error {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// Copyright 2013-2014 The Rust Project Developers. | ||
// Copyright 2018 The Uuid Project Developers. | ||
// | ||
// See the COPYRIGHT file at the top-level directory of this distribution. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// 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; | ||
} | ||
} | ||
|
||
false | ||
} | ||
|
||
// 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. | ||
const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; | ||
|
||
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(); | ||
|
||
if len == 45 && input.starts_with("urn:uuid:") { | ||
input = &input[9..]; | ||
} else if !len_matches_any( | ||
len, | ||
&[36, 32], | ||
) { | ||
return Err(ErrorKind::InvalidLength { | ||
expected: ExpectedLength::Any(&[ | ||
36, | ||
32, | ||
]), | ||
found: len, | ||
} | ||
.into()); | ||
} | ||
|
||
// `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, chr) in input.bytes().enumerate() { | ||
if digit as usize >= 32 && group != 4 { | ||
if group == 0 { | ||
return Err(ErrorKind::InvalidLength { | ||
expected: ExpectedLength::Any(&[ | ||
36, | ||
32, | ||
]), | ||
found: len, | ||
} | ||
.into()); | ||
} | ||
|
||
return Err(ErrorKind::InvalidGroupCount { | ||
expected: ExpectedLength::Any(&[1, 5]), | ||
found: group + 1, | ||
} | ||
.into()); | ||
} | ||
|
||
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 - ACC_GROUP_LENS[group - 1] as u8 | ||
} else { | ||
digit | ||
}; | ||
|
||
return Err(ErrorKind::InvalidGroupLength { | ||
expected: ExpectedLength::Exact( | ||
GROUP_LENS[group], | ||
), | ||
found: found as usize, | ||
group, | ||
} | ||
.into()); | ||
} | ||
// Next group, decrement digit, it is incremented again | ||
// at the bottom. | ||
group += 1; | ||
digit -= 1; | ||
} | ||
_ => { | ||
return Err(ErrorKind::InvalidCharacter { | ||
expected: "0123456789abcdefABCDEF-", | ||
found: input[i_char..].chars().next().unwrap(), | ||
index: i_char, | ||
urn: UrnPrefix::Optional, | ||
} | ||
.into()); | ||
} | ||
} | ||
} 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(ErrorKind::InvalidGroupLength { | ||
expected: ExpectedLength::Exact(GROUP_LENS[group]), | ||
found: found as usize, | ||
group, | ||
} | ||
.into()); | ||
} | ||
_ => { | ||
return Err(ErrorKind::InvalidCharacter { | ||
expected: "0123456789abcdefABCDEF-", | ||
found: input[i_char..].chars().next().unwrap(), | ||
index: i_char, | ||
urn: UrnPrefix::Optional, | ||
} | ||
.into()); | ||
} | ||
} | ||
buffer[(digit / 2) as usize] = acc; | ||
} | ||
digit += 1; | ||
} | ||
|
||
// Now check the last group. | ||
if ACC_GROUP_LENS[4] as u8 != digit { | ||
return Err(ErrorKind::InvalidGroupLength { | ||
expected: ExpectedLength::Exact(GROUP_LENS[4]), | ||
found: (digit as usize - ACC_GROUP_LENS[3]), | ||
group, | ||
} | ||
.into()); | ||
} | ||
|
||
Ok(buffer) | ||
} |
Oops, something went wrong.