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}