block_id/
alphabet.rs

1use std::{collections::HashMap, hash::Hash};
2
3use crate::transform::InvertableTransform;
4
5/// Represents a set of characters that are valid in a [`crate::BlockId`].
6///
7/// An alphabet is generic over the type of data used to represent a "character",
8/// but `Alphabet<char>` is used when generating string codes (otherwise, `BlockId`
9/// will generate a `Vec<T>`).
10///
11/// Several built-in alphabets are provided.
12///
13/// Examples:
14/// ```rust
15/// # use block_id::Alphabet;
16/// # fn main() {
17/// let alpha1: Alphabet<char> = Alphabet::alphanumeric();
18/// let hexchars: Vec<char> = "0123456789abcdef".chars().collect();
19/// let alpha2: Alphabet<char> = Alphabet::new(&hexchars);
20/// let alpha3: Alphabet<u32> = Alphabet::new(&[1234, 5678, 2345]);
21/// # }
22/// ```
23#[derive(Clone)]
24pub struct Alphabet<T: Copy + Hash + Eq> {
25    alphabet: Vec<T>,
26    inv_index: HashMap<T, u8>,
27}
28
29impl<T: Copy + Hash + Eq> Alphabet<T> {
30    /// Construct an alphabet from a list of values.
31    ///
32    /// Panics if the alphabet contains duplicates, or contains more than 256 elements.
33    pub fn new(alphabet: &[T]) -> Self {
34        let alphabet: Vec<T> = alphabet.to_vec();
35
36        assert!(
37            alphabet.len() <= u8::MAX as usize,
38            "Alphabet is too long (up to 256 elements supported)."
39        );
40
41        let inv_index: HashMap<T, u8> = alphabet
42            .iter()
43            .enumerate()
44            .map(|(k, v)| (*v, k as u8))
45            .collect();
46
47        assert_eq!(
48            alphabet.len(),
49            inv_index.len(),
50            "Alphabet contained duplicate value(s)."
51        );
52
53        Self {
54            alphabet,
55            inv_index,
56        }
57    }
58
59    #[allow(clippy::len_without_is_empty)]
60    pub fn len(&self) -> u8 {
61        self.alphabet.len() as u8
62    }
63}
64
65impl Alphabet<char> {
66    /// Returns an alphabet with lower- and upper-case letters, and numeral digits.
67    pub fn alphanumeric() -> Alphabet<char> {
68        let alpha: Vec<char> = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
69            .chars()
70            .collect();
71        Self::new(&alpha)
72    }
73
74    /// Returns an alphabet with lower-case letters and numeral digits.
75    pub fn lowercase_alphanumeric() -> Alphabet<char> {
76        let alpha: Vec<char> = "0123456789abcdefghijklmnopqrstuvwxyz".chars().collect();
77        Self::new(&alpha)
78    }
79
80    /// Returns an alphabet with lowercase letters only.
81    pub fn lowercase_alpha() -> Alphabet<char> {
82        let alpha: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
83        Self::new(&alpha)
84    }
85}
86
87impl<T: Copy + Hash + Eq> InvertableTransform for Alphabet<T> {
88    type Input = u8;
89
90    type Output = T;
91
92    fn forward(&self, index: u8) -> Option<T> {
93        self.alphabet.get(index as usize).copied()
94    }
95
96    fn backward(&self, value: T) -> Option<u8> {
97        self.inv_index.get(&value).copied()
98    }
99}
100
101#[cfg(test)]
102mod test {
103    use super::*;
104
105    #[test]
106    #[should_panic(expected = "Alphabet contained duplicate value(s).")]
107    fn test_duplicate() {
108        Alphabet::new(&['b', 'l', 'a', 'h', 'b', 'c']);
109    }
110
111    #[test]
112    #[should_panic(expected = "Alphabet is too long (up to 256 elements supported).")]
113    fn test_too_long() {
114        let chars: Vec<u16> = (0..30000).collect();
115        Alphabet::new(&chars);
116    }
117
118    #[test]
119    fn test_invalid_character() {
120        let chars = Alphabet::lowercase_alpha();
121        assert_eq!(None, chars.backward('!'));
122
123        assert_eq!(None, chars.forward(26));
124    }
125
126    #[test]
127    fn test_forward() {
128        let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
129        let alpha = Alphabet::new(&chars);
130
131        assert_eq!(26, alpha.len());
132
133        assert_eq!('a', alpha.forward(0).unwrap());
134        assert_eq!('z', alpha.forward(25).unwrap());
135    }
136
137    #[test]
138    fn test_backward() {
139        let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
140        let alpha = Alphabet::new(&chars);
141
142        assert_eq!(0, alpha.backward('a').unwrap());
143        assert_eq!(25, alpha.backward('z').unwrap());
144    }
145
146    #[test]
147    fn test_round_trip() {
148        let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
149            .chars()
150            .collect();
151        let alpha = Alphabet::new(&chars);
152
153        assert_eq!(52, alpha.len());
154
155        for i in 0..chars.len() as u8 {
156            let c = alpha.forward(i).unwrap();
157            let v = alpha.backward(c).unwrap();
158
159            assert_eq!(i, v);
160        }
161    }
162}