aven_protocol/
workspace.rs1#[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
15pub 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}