Skip to main content

aven_protocol/
workspace.rs

1//! Exact workspace identifier validation for ordinary image requests and moves.
2
3/// A workspace identifier is not sixteen uppercase Crockford Base32 characters.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct InvalidWorkspaceId;
6
7impl std::fmt::Display for InvalidWorkspaceId {
8    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9        formatter.write_str("workspace ID must be 16 Crockford Base32 characters")
10    }
11}
12
13impl std::error::Error for InvalidWorkspaceId {}
14
15/// Validate without normalization or allocation. Identifiers locate workspace
16/// storage; valid syntax does not authorize access.
17pub fn validate(value: &str) -> Result<(), InvalidWorkspaceId> {
18    const ALPHABET: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
19    if value.len() == 16 && value.bytes().all(|byte| ALPHABET.contains(&byte)) {
20        Ok(())
21    } else {
22        Err(InvalidWorkspaceId)
23    }
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn fixed_alphabet_length_and_unicode_acceptance() {
32        for valid in ["0123456789ABCDEF", "GHJKMNPQRSTVWXYZ", "0000000000000000"] {
33            validate(valid).unwrap();
34        }
35        for byte in 0..=127_u8 {
36            let mut id = [b'0'; 16];
37            id[7] = byte;
38            let id = std::str::from_utf8(&id).unwrap();
39            let expected = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ".contains(&byte);
40            assert_eq!(validate(id).is_ok(), expected, "byte {byte}");
41        }
42        for invalid in [
43            "",
44            "0123456789ABCDE",
45            "0123456789ABCDEFG",
46            "0123456789abcdef",
47            "0123456789ABCDEI",
48            "0123456789ABCDEL",
49            "0123456789ABCDEO",
50            "0123456789ABCDEU",
51            "00000000000000é",
52            "000000000000000é",
53            "00000000000000000",
54            "000000000000000\0",
55        ] {
56            assert_eq!(validate(invalid), Err(InvalidWorkspaceId), "{invalid:?}");
57        }
58        assert_eq!(
59            InvalidWorkspaceId.to_string(),
60            "workspace ID must be 16 Crockford Base32 characters"
61        );
62    }
63}