Skip to main content

retroglyph_core/symbols/
braille.rs

1/// The empty braille cell (no dots set), `⠀` (U+2800, distinct from a plain space).
2pub const BLANK: char = '\u{2800}';
3
4/// Dot at column 0, row 0.
5pub const DOT_1: u8 = 0x01;
6/// Dot at column 0, row 1.
7pub const DOT_2: u8 = 0x02;
8/// Dot at column 0, row 2.
9pub const DOT_3: u8 = 0x04;
10/// Dot at column 1, row 0.
11pub const DOT_4: u8 = 0x08;
12/// Dot at column 1, row 1.
13pub const DOT_5: u8 = 0x10;
14/// Dot at column 1, row 2.
15pub const DOT_6: u8 = 0x20;
16/// Dot at column 0, row 3.
17pub const DOT_7: u8 = 0x40;
18/// Dot at column 1, row 3.
19pub const DOT_8: u8 = 0x80;
20
21/// The 2x4 dot-position table, indexed `[row][col]`, giving the bit for each cell in the
22/// braille dot grid.
23pub const DOTS: [[u8; 2]; 4] = [
24    [DOT_1, DOT_4],
25    [DOT_2, DOT_5],
26    [DOT_3, DOT_6],
27    [DOT_7, DOT_8],
28];
29
30/// The glyph for `pattern`, a bitmask of the eight `DOT_*` constants (or values from
31/// [`DOTS`]) OR'd together.
32///
33/// Every value of `pattern` maps to a valid glyph: `U+2800..=U+28FF` contains no surrogate
34/// code points, so this never falls back to a placeholder.
35#[must_use]
36pub const fn glyph(pattern: u8) -> char {
37    // `0x2800..=0x28FF` is entirely outside the surrogate range (`0xD800..=0xDFFF`), so this
38    // is always a valid `char`; `unwrap_or` sidesteps `Option::expect` not being `const fn`
39    // yet on our MSRV without claiming a real fallback exists.
40    // `u32::from` isn't a stable `const fn` at this MSRV, so `pattern` (already `u8`) is
41    // widened with `as` instead; this is a lossless, non-truncating widening, not the lossy
42    // narrowing cast clippy's `as_conversions`/`cast_possible_truncation` warn about.
43    #[allow(clippy::as_conversions)]
44    match char::from_u32(0x2800 + pattern as u32) {
45        Some(c) => c,
46        None => BLANK,
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn glyph_zero_is_blank() {
56        assert_eq!(glyph(0), BLANK);
57    }
58
59    #[test]
60    fn glyph_covers_the_full_byte_range() {
61        for pattern in 0..=u8::MAX {
62            let c = glyph(pattern);
63            assert_eq!(u32::from(c), 0x2800 + u32::from(pattern));
64        }
65    }
66
67    #[test]
68    fn dots_table_has_eight_distinct_bits() {
69        let mut seen = 0u8;
70        for row in DOTS {
71            for bit in row {
72                assert_eq!(seen & bit, 0, "bit {bit:#04x} reused");
73                seen |= bit;
74            }
75        }
76        assert_eq!(seen, 0xFF);
77    }
78}