use super::error::{CharacterClass, IsinError};
pub(super) fn normalize(input: &str) -> Result<[u8; 12], IsinError> {
if input.is_empty() {
return Err(IsinError::Empty);
}
let trimmed = input.trim();
let found = trimmed.chars().count();
if found != 12 {
return Err(IsinError::InvalidLength { found });
}
let mut buf = [0u8; 12];
for (i, ch) in trimmed.chars().enumerate() {
if !ch.is_ascii() {
return Err(IsinError::InvalidCharacter {
character: ch,
position: (i + 1) as u8,
expected: CharacterClass::Alphanumeric,
});
}
buf[i] = ch.to_ascii_uppercase() as u8;
}
Ok(buf)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_empty() {
assert_eq!(normalize(""), Err(IsinError::Empty));
}
#[test]
fn trims_surrounding_whitespace() {
assert_eq!(normalize(" US0378331005 "), normalize("US0378331005"));
}
#[test]
fn uppercases_letters() {
assert_eq!(normalize("us0378331005").unwrap(), *b"US0378331005");
}
#[test]
fn rejects_wrong_length() {
assert_eq!(
normalize("US037833100"),
Err(IsinError::InvalidLength { found: 11 })
);
}
#[test]
fn whitespace_only_is_a_length_error() {
assert_eq!(normalize(" "), Err(IsinError::InvalidLength { found: 0 }));
}
#[test]
fn keeps_interior_characters_for_validation() {
assert_eq!(normalize("US 378331005").unwrap(), *b"US 378331005");
}
#[test]
fn rejects_non_ascii() {
let err = normalize("US03783310£5").unwrap_err();
assert!(matches!(
err,
IsinError::InvalidCharacter {
character: '£', ..
}
));
}
}