pub(crate) mod error;
pub(crate) use self::error::Error;
use crate::{adapter, Uuid};
fn len_matches_any(len: usize, crits: &[usize]) -> bool {
for crit in crits {
if len == *crit {
return true;
}
}
false
}
#[allow(dead_code)]
fn len_matches_range(len: usize, min: usize, max: usize) -> bool {
for crit in min..(max + 1) {
if len == crit {
return true;
}
}
false
}
const ACC_GROUP_LENS: [usize; 5] = [8, 12, 16, 20, 32];
const GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
impl Uuid {
pub fn parse_str(mut input: &str) -> Result<Uuid, crate::Error> {
let len = input.len();
if len == adapter::Urn::LENGTH && input.starts_with("urn:uuid:") {
input = &input[9..];
} else if !len_matches_any(
len,
&[adapter::Hyphenated::LENGTH, adapter::Simple::LENGTH],
) {
Err(Error::InvalidLength {
expected: error::ExpectedLength::Any(&[
adapter::Hyphenated::LENGTH,
adapter::Simple::LENGTH,
]),
found: len,
})?;
}
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 >= adapter::Simple::LENGTH && group != 4 {
if group == 0 {
Err(Error::InvalidLength {
expected: error::ExpectedLength::Any(&[
adapter::Hyphenated::LENGTH,
adapter::Simple::LENGTH,
]),
found: len,
})?;
}
Err(Error::InvalidGroupCount {
expected: error::ExpectedLength::Any(&[1, 5]),
found: group + 1,
})?;
}
if digit % 2 == 0 {
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'-' => {
if ACC_GROUP_LENS[group] as u8 != digit {
let found = if group > 0 {
digit - ACC_GROUP_LENS[group - 1] as u8
} else {
digit
};
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(
GROUP_LENS[group],
),
found: found as usize,
group,
})?;
}
group += 1;
digit -= 1;
}
_ => {
Err(Error::InvalidCharacter {
expected: "0123456789abcdefABCDEF-",
found: input[i_char..].chars().next().unwrap(),
index: i_char,
urn: error::UrnPrefix::Optional,
})?;
}
}
} else {
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'-' => {
let found = if group > 0 {
digit - ACC_GROUP_LENS[group - 1] as u8
} else {
digit
};
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(
GROUP_LENS[group],
),
found: found as usize,
group,
})?;
}
_ => {
Err(Error::InvalidCharacter {
expected: "0123456789abcdefABCDEF-",
found: input[i_char..].chars().next().unwrap(),
index: i_char,
urn: error::UrnPrefix::Optional,
})?;
}
}
buffer[(digit / 2) as usize] = acc;
}
digit += 1;
}
if ACC_GROUP_LENS[4] as u8 != digit {
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(GROUP_LENS[4]),
found: (digit as usize - ACC_GROUP_LENS[3]),
group,
})?;
}
Ok(Uuid::from_bytes(buffer))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{adapter, std::string::ToString, test_util};
#[test]
fn test_parse_uuid_v4() {
const EXPECTED_UUID_LENGTHS: error::ExpectedLength =
error::ExpectedLength::Any(&[
adapter::Hyphenated::LENGTH,
adapter::Simple::LENGTH,
]);
const EXPECTED_GROUP_COUNTS: error::ExpectedLength =
error::ExpectedLength::Any(&[1, 5]);
const EXPECTED_CHARS: &'static str = "0123456789abcdefABCDEF-";
assert_eq!(
Uuid::parse_str("").map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 0,
})
);
assert_eq!(
Uuid::parse_str("!").map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 1
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E45")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 37,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faa-BBF-329BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 35
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faa-BGBF-329BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidCharacter {
expected: EXPECTED_CHARS,
found: 'G',
index: 20,
urn: error::UrnPrefix::Optional,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2F4faaFB6BFF329BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupCount {
expected: EXPECTED_GROUP_COUNTS,
found: 2
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faaFB6BFF329BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupCount {
expected: EXPECTED_GROUP_COUNTS,
found: 3,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faa-B6BFF329BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupCount {
expected: EXPECTED_GROUP_COUNTS,
found: 4,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faa")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 18,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faaXB6BFF329BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidCharacter {
expected: EXPECTED_CHARS,
found: 'X',
index: 18,
urn: error::UrnPrefix::Optional,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB-24fa-eB6BFF32-BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(4),
found: 3,
group: 1,
})
);
assert_eq!(
Uuid::parse_str("01020304-1112-2122-3132-41424344")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(12),
found: 8,
group: 4,
})
);
assert_eq!(
Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 31,
})
);
assert_eq!(
Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c88")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 33,
})
);
assert_eq!(
Uuid::parse_str("67e5504410b1426f9247bb680e5fe0cg8")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 33,
})
);
assert_eq!(
Uuid::parse_str("67e5504410b1426%9247bb680e5fe0c8")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidCharacter {
expected: EXPECTED_CHARS,
found: '%',
index: 15,
urn: error::UrnPrefix::Optional,
})
);
assert_eq!(
Uuid::parse_str("231231212212423424324323477343246663")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 36,
})
);
assert!(Uuid::parse_str("00000000000000000000000000000000").is_ok());
assert!(Uuid::parse_str("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
assert!(Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4").is_ok());
assert!(Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c8").is_ok());
assert!(Uuid::parse_str("01020304-1112-2122-3132-414243444546").is_ok());
assert!(Uuid::parse_str(
"urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8"
)
.is_ok());
let nil = Uuid::nil();
assert_eq!(
Uuid::parse_str("00000000000000000000000000000000").unwrap(),
nil
);
assert_eq!(
Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(),
nil
);
let uuid_orig = test_util::new();
let orig_str = uuid_orig.to_string();
let uuid_out = Uuid::parse_str(&orig_str).unwrap();
assert_eq!(uuid_orig, uuid_out);
assert_eq!(
Uuid::parse_str("67e5504410b1426f9247bb680e5fe0c")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidLength {
expected: EXPECTED_UUID_LENGTHS,
found: 31,
})
);
assert_eq!(
Uuid::parse_str("67e550X410b1426f9247bb680e5fe0cd")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidCharacter {
expected: EXPECTED_CHARS,
found: 'X',
index: 6,
urn: error::UrnPrefix::Optional,
})
);
assert_eq!(
Uuid::parse_str("67e550-4105b1426f9247bb680e5fe0c")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(8),
found: 6,
group: 0,
})
);
assert_eq!(
Uuid::parse_str("F9168C5E-CEB2-4faa-B6BF1-02BF39FA1E4")
.map_err(crate::Error::expect_parser),
Err(Error::InvalidGroupLength {
expected: error::ExpectedLength::Exact(4),
found: 5,
group: 3,
})
);
}
}