| 1 | //! Glyph mapping. |
| 2 | //! |
| 3 | //! A glyph mapping defines the position of characters in a [`MonoFont`] image. This module provides |
| 4 | //! predefined mappings for common glyph subsets, but custom mappings are also supported. |
| 5 | //! |
| 6 | //! # Custom mappings |
| 7 | //! |
| 8 | //! Custom mappings can be defined in three different ways: |
| 9 | //! * The [`StrGlyphMapping`] type can be used to specify a character mapping by encoding the |
| 10 | //! mapping as a string. |
| 11 | //! * The [`GlyphMapping`] trait is implemented for all functions `Fn(char) -> usize`. |
| 12 | //! * The [`GlyphMapping`] trait can be implemented by a custom type. |
| 13 | //! |
| 14 | //! # `StrGlyphMapping` encoding |
| 15 | //! |
| 16 | //! Strings without a `\0` character can be used to directly map a character to its position in |
| 17 | //! the mapping string: |
| 18 | //! |
| 19 | //! ``` |
| 20 | //! use embedded_graphics::mono_font::mapping::{GlyphMapping, StrGlyphMapping}; |
| 21 | //! |
| 22 | //! let mapping = StrGlyphMapping::new("abcdef1234" , 0); |
| 23 | //! assert_eq!(mapping.index('a' ), 0); |
| 24 | //! assert_eq!(mapping.index('b' ), 1); |
| 25 | //! assert_eq!(mapping.index('1' ), 6); |
| 26 | //! assert_eq!(mapping.index('2' ), 7); |
| 27 | //! ``` |
| 28 | //! |
| 29 | //! This direct mapping is inefficient for mappings that map consecutive ranges of characters to |
| 30 | //! consecutive index ranges. To define a range of characters a `\0` character followed by the |
| 31 | //! start and end characters of the inclusive range can be used. This way the mapping in the previous |
| 32 | //! example can be abbreviated to: |
| 33 | //! |
| 34 | //! ``` |
| 35 | //! use embedded_graphics::mono_font::mapping::{GlyphMapping, StrGlyphMapping}; |
| 36 | //! |
| 37 | //! let mapping = StrGlyphMapping::new(" \0af \014" , 0); |
| 38 | //! assert_eq!(mapping.index('a' ), 0); |
| 39 | //! assert_eq!(mapping.index('b' ), 1); |
| 40 | //! assert_eq!(mapping.index('1' ), 6); |
| 41 | //! assert_eq!(mapping.index('2' ), 7); |
| 42 | //! ``` |
| 43 | //! |
| 44 | //! [`MonoFont`]: super::MonoFont |
| 45 | |
| 46 | use core::ops::RangeInclusive; |
| 47 | |
| 48 | /// Mapping from characters to glyph indices. |
| 49 | pub trait GlyphMapping: Sync { |
| 50 | /// Maps a character to a glyph index. |
| 51 | /// |
| 52 | /// If `c` isn't included in the font the index of a suitable replacement glyph is returned. |
| 53 | fn index(&self, c: char) -> usize; |
| 54 | } |
| 55 | |
| 56 | impl<F> GlyphMapping for F |
| 57 | where |
| 58 | F: Sync + Fn(char) -> usize, |
| 59 | { |
| 60 | fn index(&self, c: char) -> usize { |
| 61 | self(c) |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /// Glyph mapping stored as a UTF-8 string. |
| 66 | /// |
| 67 | /// See the [module-level documentation] for more details. |
| 68 | /// |
| 69 | /// [module-level documentation]: self |
| 70 | #[derive (Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 71 | pub struct StrGlyphMapping<'a> { |
| 72 | data: &'a str, |
| 73 | replacement_index: usize, |
| 74 | } |
| 75 | |
| 76 | impl<'a> StrGlyphMapping<'a> { |
| 77 | /// Creates a new glyph mapping. |
| 78 | pub const fn new(data: &'a str, replacement_index: usize) -> Self { |
| 79 | Self { |
| 80 | data, |
| 81 | replacement_index, |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | /// Returns an iterator over the character ranges. |
| 86 | pub fn ranges(&self) -> impl Iterator<Item = (usize, RangeInclusive<char>)> + '_ { |
| 87 | let mut chars = self.data.chars(); |
| 88 | let mut index = 0; |
| 89 | |
| 90 | core::iter::from_fn(move || { |
| 91 | let start_index = index; |
| 92 | |
| 93 | let range = match chars.next()? { |
| 94 | ' \0' => { |
| 95 | let start = chars.next()?; |
| 96 | let end = chars.next()?; |
| 97 | |
| 98 | index += end as usize - start as usize + 1; |
| 99 | |
| 100 | start..=end |
| 101 | } |
| 102 | c => { |
| 103 | index += 1; |
| 104 | |
| 105 | c..=c |
| 106 | } |
| 107 | }; |
| 108 | |
| 109 | Some((start_index, range)) |
| 110 | }) |
| 111 | } |
| 112 | |
| 113 | /// Returns an iterator over the characters in this mapping. |
| 114 | pub fn chars(&self) -> impl Iterator<Item = char> + '_ { |
| 115 | let mut chars = self.data.chars(); |
| 116 | |
| 117 | core::iter::from_fn(move || { |
| 118 | let range = match chars.next()? { |
| 119 | ' \0' => { |
| 120 | let start = chars.next()?; |
| 121 | let end = chars.next()?; |
| 122 | |
| 123 | start..=end |
| 124 | } |
| 125 | c => c..=c, |
| 126 | }; |
| 127 | |
| 128 | Some(range) |
| 129 | }) |
| 130 | .flatten() |
| 131 | } |
| 132 | |
| 133 | /// Returns if the mapping contains the given char. |
| 134 | pub fn contains(&self, c: char) -> bool { |
| 135 | self.chars().any(|v| v == c) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | impl GlyphMapping for StrGlyphMapping<'_> { |
| 140 | fn index(&self, c: char) -> usize { |
| 141 | // PERF: use ranges instead of chars iter |
| 142 | self.chars() |
| 143 | .enumerate() |
| 144 | .find(|(_, v)| c == *v) |
| 145 | .map(|(index, _)| index) |
| 146 | .unwrap_or(self.replacement_index) |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | macro_rules! impl_mapping { |
| 151 | ($( $(#[$meta:meta])* ($enum_variant:ident, $constant:ident, $mapping:expr), )*) => { |
| 152 | /// Mapping. |
| 153 | /// |
| 154 | /// This enum lists all mappings that are included in embedded-graphics. It is used |
| 155 | /// to automatically generate font data for all mappings and isn't normally used in |
| 156 | /// applications. |
| 157 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 158 | pub enum Mapping { |
| 159 | $( |
| 160 | $(#[$meta])* |
| 161 | $enum_variant, |
| 162 | )* |
| 163 | } |
| 164 | |
| 165 | impl Mapping { |
| 166 | /// Returns an iterator over all mappings. |
| 167 | pub fn iter() -> impl Iterator<Item = Self> { |
| 168 | const ALL: &[Mapping] = &[$(Mapping::$enum_variant),*]; |
| 169 | |
| 170 | ALL.iter().copied() |
| 171 | } |
| 172 | |
| 173 | /// Returns the MIME identifier for this mapping. |
| 174 | pub const fn mime(self) -> &'static str { |
| 175 | match self { |
| 176 | $(Mapping::$enum_variant => stringify!($constant)),* |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | /// Returns a glyph mapping for this mapping. |
| 181 | pub const fn glyph_mapping(self) -> &'static StrGlyphMapping<'static> { |
| 182 | match self { |
| 183 | $(Self::$enum_variant => &$constant),* |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | $( |
| 189 | $(#[$meta])* |
| 190 | pub const $constant: StrGlyphMapping = StrGlyphMapping::new($mapping, '?' as usize - ' ' as usize); |
| 191 | )* |
| 192 | }; |
| 193 | } |
| 194 | |
| 195 | // TODO: Add Iso8859_6 (Latin/Arabic), Iso8859_8 (Latin/Hebrew) and Iso8859_11 (Latin/Thai) when we support RTL and combining characters. |
| 196 | impl_mapping!( |
| 197 | /// ASCII. |
| 198 | (Ascii, ASCII, " \0\u{20}\u{7f}" ), |
| 199 | |
| 200 | /// ISO/IEC 8859 Part 1: Latin-1, Western European. |
| 201 | (Iso8859_1, ISO_8859_1, " \0\u{20}\u{7f}\0\u{a0}\u{ff}" ), |
| 202 | |
| 203 | /// ISO/IEC 8859 Part 2: Latin-2, Central European. |
| 204 | (Iso8859_2, ISO_8859_2, " \0\u{20}\u{7f}\u{a0}\u{104}\u{2d8}\u{141}\u{a4}\u{13d}\u{15a}\u{a7}\u{a8}\u{160}\u{15e}\u{164}\u{179}\u{ad}\u{17d}\u{17b}\u{b0}\u{105}\u{2db}\u{142}\u{b4}\u{13e}\u{15b}\u{2c7}\u{b8}\u{161}\u{15f}\u{165}\u{17a}\u{2dd}\u{17e}\u{17c}\u{154}\u{c1}\u{c2}\u{102}\u{c4}\u{139}\u{106}\u{c7}\u{10c}\u{c9}\u{118}\u{cb}\u{11a}\u{cd}\u{ce}\u{10e}\u{110}\u{143}\u{147}\u{d3}\u{d4}\u{150}\u{d6}\u{d7}\u{158}\u{16e}\u{da}\u{170}\u{dc}\u{dd}\u{162}\u{df}\u{155}\u{e1}\u{e2}\u{103}\u{e4}\u{13a}\u{107}\u{e7}\u{10d}\u{e9}\u{119}\u{eb}\u{11b}\u{ed}\u{ee}\u{10f}\u{111}\u{144}\u{148}\u{f3}\u{f4}\u{151}\u{f6}\u{f7}\u{159}\u{16f}\u{fa}\u{171}\u{fc}\u{fd}\u{163}\u{2d9}" ), |
| 205 | |
| 206 | /// ISO/IEC 8859 Part 3: Latin-3, South European. |
| 207 | (Iso8859_3, ISO_8859_3, " \0\u{20}\u{7f}\u{a0}\u{126}\u{2d8}\0\u{a3}\u{a5}\u{124}\u{a7}\u{a8}\u{130}\u{15e}\u{11e}\u{134}\u{ad}\u{ae}\u{17b}\u{b0}\u{127}\0\u{b2}\u{b5}\u{125}\u{b7}\u{b8}\u{131}\u{15f}\u{11f}\u{135}\u{bd}\u{be}\u{17c}\0\u{c0}\u{c4}\u{10a}\u{108}\0\u{c7}\u{d4}\u{120}\u{d6}\u{d7}\u{11c}\0\u{d9}\u{dc}\u{16c}\u{15c}\0\u{df}\u{e4}\u{10b}\u{109}\0\u{e7}\u{f4}\u{121}\u{f6}\u{f7}\u{11d}\0\u{f9}\u{fc}\u{16d}\u{15d}\u{2d9}" ), |
| 208 | |
| 209 | /// ISO/IEC 8859 Part 4: Latin-4, North European. |
| 210 | (Iso8859_4, ISO_8859_4, " \0\u{20}\u{7f}\u{a0}\u{104}\u{138}\u{156}\u{a4}\u{128}\u{13b}\u{a7}\u{a8}\u{160}\u{112}\u{122}\u{166}\u{ad}\u{17d}\u{af}\u{b0}\u{105}\u{2db}\u{157}\u{b4}\u{129}\u{13c}\u{2c7}\u{b8}\u{161}\u{113}\u{123}\u{167}\u{14a}\u{17e}\u{14b}\u{100}\0\u{c1}\u{c6}\u{12e}\u{10c}\u{c9}\u{118}\u{cb}\u{116}\u{cd}\u{ce}\u{12a}\u{110}\u{145}\u{14c}\u{136}\0\u{d4}\u{d8}\u{172}\0\u{da}\u{dc}\u{168}\u{16a}\u{df}\u{101}\0\u{e1}\u{e6}\u{12f}\u{10d}\u{e9}\u{119}\u{eb}\u{117}\u{ed}\u{ee}\u{12b}\u{111}\u{146}\u{14d}\u{137}\0\u{f4}\u{f8}\u{173}\0\u{fa}\u{fc}\u{169}\u{16b}\u{2d9}" ), |
| 211 | |
| 212 | /// ISO/IEC 8859 Part 5: Latin/Cyrillic. |
| 213 | (Iso8859_5, ISO_8859_5, " \0\u{20}\u{7f}\u{a0}\0\u{401}\u{40c}\u{ad}\0\u{40e}\u{44f}\u{2116}\0\u{451}\u{45c}\u{a7}\u{45e}\u{45f}" ), |
| 214 | |
| 215 | /// ISO/IEC 8859 Part 7: Latin/Greek. |
| 216 | (Iso8859_7, ISO_8859_7, " \0\u{20}\u{7f}\u{a0}\u{2018}\u{2019}\u{a3}\u{20ac}\u{20af}\0\u{a6}\u{a9}\u{37a}\0\u{ab}\u{ae}\u{2015}\0\u{b0}\u{b3}\0\u{384}\u{386}\u{b7}\0\u{388}\u{38a}\u{bb}\u{38c}\u{bd}\0\u{38e}\u{3cf}" ), |
| 217 | |
| 218 | /// ISO/IEC 8859 Part 9: Latin-5, Turkish. |
| 219 | (Iso8859_9, ISO_8859_9, " \0\u{20}\u{7f}\0\u{a0}\u{cf}\u{11e}\0\u{d1}\u{dc}\u{130}\u{15e}\0\u{df}\u{ef}\u{11f}\0\u{f1}\u{fc}\u{131}\u{15f}\u{ff}" ), |
| 220 | |
| 221 | /// ISO/IEC 8859 Part 10: Latin-6, Nordic. |
| 222 | (Iso8859_10, ISO_8859_10, " \0\u{20}\u{7f}\u{a0}\u{104}\u{112}\u{122}\u{12a}\u{128}\u{136}\u{a7}\u{13b}\u{110}\u{160}\u{166}\u{17d}\u{ad}\u{16a}\u{14a}\u{b0}\u{105}\u{113}\u{123}\u{12b}\u{129}\u{137}\u{b7}\u{13c}\u{111}\u{161}\u{167}\u{17e}\u{2015}\u{16b}\u{14b}\u{100}\0\u{c1}\u{c6}\u{12e}\u{10c}\u{c9}\u{118}\u{cb}\u{116}\0\u{cd}\u{d0}\u{145}\u{14c}\0\u{d3}\u{d6}\u{168}\u{d8}\u{172}\0\u{da}\u{df}\u{101}\0\u{e1}\u{e6}\u{12f}\u{10d}\u{e9}\u{119}\u{eb}\u{117}\0\u{ed}\u{f0}\u{146}\u{14d}\0\u{f3}\u{f6}\u{169}\u{f8}\u{173}\0\u{fa}\u{fe}\u{138}" ), |
| 223 | |
| 224 | /// ISO/IEC 8859 Part 13: Latin-7, Baltic Rim. |
| 225 | (Iso8859_13, ISO_8859_13, " \0\u{20}\u{7f}\u{a0}\u{201d}\0\u{a2}\u{a4}\u{201e}\u{a6}\u{a7}\u{d8}\u{a9}\u{156}\0\u{ab}\u{ae}\u{c6}\0\u{b0}\u{b3}\u{201c}\0\u{b5}\u{b7}\u{f8}\u{b9}\u{157}\0\u{bb}\u{be}\u{e6}\u{104}\u{12e}\u{100}\u{106}\u{c4}\u{c5}\u{118}\u{112}\u{10c}\u{c9}\u{179}\u{116}\u{122}\u{136}\u{12a}\u{13b}\u{160}\u{143}\u{145}\u{d3}\u{14c}\0\u{d5}\u{d7}\u{172}\u{141}\u{15a}\u{16a}\u{dc}\u{17b}\u{17d}\u{df}\u{105}\u{12f}\u{101}\u{107}\u{e4}\u{e5}\u{119}\u{113}\u{10d}\u{e9}\u{17a}\u{117}\u{123}\u{137}\u{12b}\u{13c}\u{161}\u{144}\u{146}\u{f3}\u{14d}\0\u{f5}\u{f7}\u{173}\u{142}\u{15b}\u{16b}\u{fc}\u{17c}\u{17e}\u{2019}" ), |
| 226 | |
| 227 | /// ISO/IEC 8859 Part 14: Latin-8, Celtic. |
| 228 | (Iso8859_14, ISO_8859_14, " \0\u{20}\u{7f}\u{a0}\u{1e02}\u{1e03}\u{a3}\u{10a}\u{10b}\u{1e0a}\u{a7}\u{1e80}\u{a9}\u{1e82}\u{1e0b}\u{1ef2}\u{ad}\u{ae}\u{178}\u{1e1e}\u{1e1f}\u{120}\u{121}\u{1e40}\u{1e41}\u{b6}\u{1e56}\u{1e81}\u{1e57}\u{1e83}\u{1e60}\u{1ef3}\u{1e84}\u{1e85}\u{1e61}\0\u{c0}\u{cf}\u{174}\0\u{d1}\u{d6}\u{1e6a}\0\u{d8}\u{dd}\u{176}\0\u{df}\u{ef}\u{175}\0\u{f1}\u{f6}\u{1e6b}\0\u{f8}\u{fd}\u{177}\u{ff}" ), |
| 229 | |
| 230 | /// ISO/IEC 8859 Part 15: Latin-9 (revised Latin-1). |
| 231 | (Iso8859_15, ISO_8859_15, " \0\u{20}\u{7f}\0\u{a0}\u{a3}\u{20ac}\u{a5}\u{160}\u{a7}\u{161}\0\u{a9}\u{b3}\u{17d}\0\u{b5}\u{b7}\u{17e}\0\u{b9}\u{bb}\u{152}\u{153}\u{178}\0\u{bf}\u{ff}" ), |
| 232 | |
| 233 | /// ISO/IEC 8859 Part 16: Latin-10: South-East European. |
| 234 | (Iso8859_16, ISO_8859_16, " \0\u{20}\u{7f}\u{a0}\u{104}\u{105}\u{141}\u{20ac}\u{201e}\u{160}\u{a7}\u{161}\u{a9}\u{218}\u{ab}\u{179}\u{ad}\u{17a}\u{17b}\u{b0}\u{b1}\u{10c}\u{142}\u{17d}\u{201d}\u{b6}\u{b7}\u{17e}\u{10d}\u{219}\u{bb}\u{152}\u{153}\u{178}\u{17c}\0\u{c0}\u{c2}\u{102}\u{c4}\u{106}\0\u{c6}\u{cf}\u{110}\u{143}\0\u{d2}\u{d4}\u{150}\u{d6}\u{15a}\u{170}\0\u{d9}\u{dc}\u{118}\u{21a}\0\u{df}\u{e2}\u{103}\u{e4}\u{107}\0\u{e6}\u{ef}\u{111}\u{144}\0\u{f2}\u{f4}\u{151}\u{f6}\u{15b}\u{171}\0\u{f9}\u{fc}\u{119}\u{21b}\u{ff}" ), |
| 235 | |
| 236 | /// JIS X 0201: Japanese katakana (halfwidth). |
| 237 | (JisX0201, JIS_X0201, " \0\u{20}\u{7f}\0\u{ff60}\u{ff9f}" ), |
| 238 | ); |
| 239 | |
| 240 | #[cfg (test)] |
| 241 | mod tests { |
| 242 | use super::*; |
| 243 | |
| 244 | #[test ] |
| 245 | fn empty() { |
| 246 | let mapping = StrGlyphMapping::new("" , 0); |
| 247 | |
| 248 | let mut iter = mapping.ranges(); |
| 249 | assert_eq!(iter.next(), None); |
| 250 | |
| 251 | let mut iter = mapping.chars(); |
| 252 | assert_eq!(iter.next(), None); |
| 253 | } |
| 254 | |
| 255 | #[test ] |
| 256 | fn one_char() { |
| 257 | let mapping = StrGlyphMapping::new("a" , 0); |
| 258 | |
| 259 | let mut iter = mapping.ranges(); |
| 260 | assert_eq!(iter.next(), Some((0, 'a' ..='a' ))); |
| 261 | assert_eq!(iter.next(), None); |
| 262 | |
| 263 | let mut iter = mapping.chars(); |
| 264 | assert_eq!(iter.next(), Some('a' )); |
| 265 | assert_eq!(iter.next(), None); |
| 266 | |
| 267 | assert_eq!(mapping.index('a' ), 0); |
| 268 | } |
| 269 | |
| 270 | #[test ] |
| 271 | fn three_chars() { |
| 272 | let mapping = StrGlyphMapping::new("abc" , 1); |
| 273 | |
| 274 | let mut iter = mapping.ranges(); |
| 275 | assert_eq!(iter.next(), Some((0, 'a' ..='a' ))); |
| 276 | assert_eq!(iter.next(), Some((1, 'b' ..='b' ))); |
| 277 | assert_eq!(iter.next(), Some((2, 'c' ..='c' ))); |
| 278 | assert_eq!(iter.next(), None); |
| 279 | |
| 280 | let mut iter = mapping.chars(); |
| 281 | assert_eq!(iter.next(), Some('a' )); |
| 282 | assert_eq!(iter.next(), Some('b' )); |
| 283 | assert_eq!(iter.next(), Some('c' )); |
| 284 | assert_eq!(iter.next(), None); |
| 285 | |
| 286 | assert_eq!(mapping.index('a' ), 0); |
| 287 | assert_eq!(mapping.index('b' ), 1); |
| 288 | assert_eq!(mapping.index('c' ), 2); |
| 289 | assert_eq!(mapping.index('$' ), 1); |
| 290 | } |
| 291 | |
| 292 | #[test ] |
| 293 | fn one_range() { |
| 294 | let mapping = StrGlyphMapping::new(" \x00ac" , 2); |
| 295 | |
| 296 | let mut iter = mapping.ranges(); |
| 297 | assert_eq!(iter.next(), Some((0, 'a' ..='c' ))); |
| 298 | assert_eq!(iter.next(), None); |
| 299 | |
| 300 | let mut iter = mapping.chars(); |
| 301 | assert_eq!(iter.next(), Some('a' )); |
| 302 | assert_eq!(iter.next(), Some('b' )); |
| 303 | assert_eq!(iter.next(), Some('c' )); |
| 304 | assert_eq!(iter.next(), None); |
| 305 | |
| 306 | assert_eq!(mapping.index('a' ), 0); |
| 307 | assert_eq!(mapping.index('b' ), 1); |
| 308 | assert_eq!(mapping.index('c' ), 2); |
| 309 | assert_eq!(mapping.index('$' ), 2); |
| 310 | } |
| 311 | |
| 312 | #[test ] |
| 313 | fn incomplete_range() { |
| 314 | let mapping = StrGlyphMapping::new(" \x00a" , 0); |
| 315 | |
| 316 | let mut iter = mapping.ranges(); |
| 317 | assert_eq!(iter.next(), None); |
| 318 | |
| 319 | let mut iter = mapping.chars(); |
| 320 | assert_eq!(iter.next(), None); |
| 321 | } |
| 322 | |
| 323 | #[test ] |
| 324 | fn mixed_ranges_and_chars() { |
| 325 | let mapping = StrGlyphMapping::new("a \x00bde" , 3); |
| 326 | |
| 327 | let mut iter = mapping.ranges(); |
| 328 | assert_eq!(iter.next(), Some((0, 'a' ..='a' ))); |
| 329 | assert_eq!(iter.next(), Some((1, 'b' ..='d' ))); |
| 330 | assert_eq!(iter.next(), Some((4, 'e' ..='e' ))); |
| 331 | assert_eq!(iter.next(), None); |
| 332 | |
| 333 | let mut iter = mapping.chars(); |
| 334 | assert_eq!(iter.next(), Some('a' )); |
| 335 | assert_eq!(iter.next(), Some('b' )); |
| 336 | assert_eq!(iter.next(), Some('c' )); |
| 337 | assert_eq!(iter.next(), Some('d' )); |
| 338 | assert_eq!(iter.next(), Some('e' )); |
| 339 | assert_eq!(iter.next(), None); |
| 340 | |
| 341 | assert_eq!(mapping.index('a' ), 0); |
| 342 | assert_eq!(mapping.index('b' ), 1); |
| 343 | assert_eq!(mapping.index('c' ), 2); |
| 344 | assert_eq!(mapping.index('d' ), 3); |
| 345 | assert_eq!(mapping.index('e' ), 4); |
| 346 | assert_eq!(mapping.index('$' ), 3); |
| 347 | } |
| 348 | |
| 349 | #[test ] |
| 350 | fn dyn_str_glyph_mapping() { |
| 351 | let mapping = StrGlyphMapping::new("ab" , 0); |
| 352 | let dyn_mapping: &dyn GlyphMapping = &mapping; |
| 353 | |
| 354 | assert_eq!(dyn_mapping.index('b' ), 1); |
| 355 | } |
| 356 | |
| 357 | #[test ] |
| 358 | fn dyn_fn_glyph_mapping() { |
| 359 | fn map(c: char) -> usize { |
| 360 | match c { |
| 361 | 'a' => 0, |
| 362 | 'b' => 1, |
| 363 | _ => 2, |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | let dyn_mapping: &dyn GlyphMapping = ↦ |
| 368 | |
| 369 | assert_eq!(dyn_mapping.index('a' ), 0); |
| 370 | assert_eq!(dyn_mapping.index('b' ), 1); |
| 371 | assert_eq!(dyn_mapping.index('?' ), 2); |
| 372 | } |
| 373 | } |
| 374 | |