use super::{ValidateError, ValidateResult};
const LENGTH: usize = 36;
const GROUPS: [usize; 5] = [8, 4, 4, 4, 12];
pub fn uuid<T: AsRef<str>, D, C>(
value: &T,
_data: &D,
_context: &C,
_finalize: bool,
) -> ValidateResult {
let value = value.as_ref();
let length = value.chars().count();
if length != LENGTH {
return Err(ValidateError::new(format!(
"not a valid UUID: expected {LENGTH} characters, found {length}"
)));
}
let grouping = || {
ValidateError::new("not a valid UUID: expected 5 groups of 8-4-4-4-12 hexadecimal digits")
};
let mut groups = value.split('-');
for expected in GROUPS {
let Some(group) = groups.next() else {
return Err(grouping());
};
if let Some(char) = group.chars().find(|char| !char.is_ascii_hexdigit()) {
return Err(ValidateError::new(format!(
"not a valid UUID: invalid character `{char}`"
)));
}
if group.len() != expected {
return Err(grouping());
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn check(value: &str) -> ValidateResult {
uuid(&value, &(), &(), false)
}
fn message(value: &str) -> String {
check(value).unwrap_err().message
}
#[test]
fn accepts_canonical_uuids() {
assert!(check("67e55044-10b1-426f-9247-bb680e5fe0c8").is_ok());
assert!(check("2c5ea4c0-4067-11e9-8bad-9b1deb4d3b7d").is_ok());
assert!(check("01890a5d-ac96-774b-bcce-b302099a8057").is_ok());
}
#[test]
fn accepts_either_case() {
assert!(check("67E55044-10B1-426F-9247-BB680E5FE0C8").is_ok());
assert!(check("67e55044-10B1-426f-9247-BB680e5fe0c8").is_ok());
}
#[test]
fn accepts_the_nil_and_max_uuids() {
assert!(check("00000000-0000-0000-0000-000000000000").is_ok());
assert!(check("ffffffff-ffff-ffff-ffff-ffffffffffff").is_ok());
}
#[test]
fn rejects_the_wrong_length() {
assert_eq!(
message(""),
"not a valid UUID: expected 36 characters, found 0"
);
assert_eq!(
message("67e55044-10b1-426f-9247-bb680e5fe0c"),
"not a valid UUID: expected 36 characters, found 35"
);
assert_eq!(
message("éé7e55044-10b1-426f-9247-bb680e5fe0c8"),
"not a valid UUID: expected 36 characters, found 37"
);
}
#[test]
fn rejects_the_wrong_grouping() {
let expected = "not a valid UUID: expected 5 groups of 8-4-4-4-12 hexadecimal digits";
assert_eq!(message("67e5504-410b1-426f-9247-bb680e5fe0c8"), expected);
assert_eq!(message("67e5504410b1426f9247bb680e5fe0c8----"), expected);
assert_eq!(message("67e55044-10b1-426f-9247-bb680e5fe0-8"), expected);
}
#[test]
fn rejects_non_hexadecimal_digits() {
assert_eq!(
message("g7e55044-10b1-426f-9247-bb680e5fe0c8"),
"not a valid UUID: invalid character `g`"
);
assert_eq!(
message("é7e55044-10b1-426f-9247-bb680e5fe0c8"),
"not a valid UUID: invalid character `é`"
);
}
#[test]
fn rejects_other_common_forms() {
assert!(check("{67e55044-10b1-426f-9247-bb680e5fe0c8}").is_err());
assert!(check("urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8").is_err());
assert!(check("67e5504410b1426f9247bb680e5fe0c8").is_err());
}
}