Skip to main content

wacore_binary/
token.rs

1pub const DICT_VERSION: u8 = 3;
2
3// --- Public Constants for Special Tags ---
4pub const LIST_EMPTY: u8 = 0;
5pub const DICTIONARY_0: u8 = 236;
6pub const DICTIONARY_1: u8 = 237;
7pub const DICTIONARY_2: u8 = 238;
8pub const DICTIONARY_3: u8 = 239;
9
10pub const JID_PAIR: u8 = 250;
11pub const HEX_8: u8 = 251;
12pub const BINARY_8: u8 = 252;
13pub const BINARY_20: u8 = 253;
14pub const BINARY_32: u8 = 254;
15pub const NIBBLE_8: u8 = 255;
16pub const INTEROP_JID: u8 = 245;
17pub const FB_JID: u8 = 246;
18pub const AD_JID: u8 = 247;
19pub const LIST_8: u8 = 248;
20pub const LIST_16: u8 = 249;
21
22pub const PACKED_MAX: u8 = 127;
23pub const SINGLE_BYTE_MAX: u16 = 256;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum TokenKind {
27    Single(u8),
28    Double(u8, u8),
29}
30
31include!(concat!(env!("OUT_DIR"), "/token_maps.rs"));
32
33pub fn index_of_token(token: &str) -> Option<TokenKind> {
34    hashify_lookup(token.as_bytes()).copied()
35}
36
37pub fn get_single_token(index: u8) -> Option<&'static str> {
38    SINGLE_BYTE_TOKENS.get(index as usize).copied()
39}
40
41pub fn get_double_token(dict: u8, index: u8) -> Option<&'static str> {
42    DOUBLE_BYTE_TOKENS
43        .get(dict as usize)
44        .and_then(|d| d.get(index as usize))
45        .copied()
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn test_single_byte_token_roundtrip() {
54        for i in 1u8..=235 {
55            if let Some(token) = get_single_token(i) {
56                let result = index_of_token(token);
57                assert!(
58                    matches!(result, Some(TokenKind::Single(idx)) if idx == i),
59                    "Token '{}' at index {} doesn't round-trip",
60                    token,
61                    i,
62                );
63            }
64        }
65    }
66
67    #[test]
68    fn test_double_byte_token_roundtrip() {
69        for dict in 0..4u8 {
70            for idx in 0..255u8 {
71                if let Some(token) = get_double_token(dict, idx) {
72                    let result = index_of_token(token);
73                    assert!(
74                        matches!(result, Some(TokenKind::Double(d, i)) if d == dict && i == idx),
75                        "Token '{}' at dict {} index {} doesn't round-trip",
76                        token,
77                        dict,
78                        idx,
79                    );
80                }
81            }
82        }
83    }
84
85    #[test]
86    fn test_unknown_string_returns_none() {
87        assert!(index_of_token("xyzzy_not_a_token_12345").is_none());
88    }
89
90    #[test]
91    fn test_empty_string_returns_none() {
92        assert!(index_of_token("").is_none());
93    }
94
95    #[test]
96    fn test_token_boundary_indices() {
97        let token_0 = get_single_token(0);
98        assert_eq!(token_0, Some(""), "Index 0 should be empty string token");
99        assert!(get_single_token(LIST_8).is_none());
100        assert!(get_single_token(LIST_16).is_none());
101        assert!(get_single_token(JID_PAIR).is_none());
102        assert!(get_single_token(HEX_8).is_none());
103        assert!(get_single_token(BINARY_8).is_none());
104        assert!(get_single_token(BINARY_20).is_none());
105        assert!(get_single_token(BINARY_32).is_none());
106        assert!(get_single_token(NIBBLE_8).is_none());
107    }
108
109    #[test]
110    fn test_almost_matching_strings() {
111        let token = get_single_token(1).expect("single token at index 1 must exist");
112        assert!(index_of_token(&format!("{}_modified", token)).is_none());
113        assert!(index_of_token(&format!("prefix_{}", token)).is_none());
114        assert!(index_of_token(&format!("{}!", token)).is_none());
115    }
116
117    #[test]
118    fn test_out_of_bounds_dictionary() {
119        assert!(get_double_token(4, 0).is_none());
120        assert!(get_double_token(5, 100).is_none());
121        assert!(get_double_token(255, 0).is_none());
122    }
123}