codepage_437/
dialect.rs

1use std::hash::{Hasher, Hash};
2use std::borrow::Cow;
3use std::{cmp, fmt};
4
5
6/// Specifier for the specific kind of cp437.
7///
8/// Dialects are instances of this type, aggregating data necessary to perform conversions.
9#[derive(Clone)]
10pub struct Cp437Dialect {
11    cp437_to_unicode: [char; 256],
12
13    overlap_unicode: fn(unicode: char) -> bool,
14    overlap_cp437: fn(cp437: u8) -> bool,
15
16    encode: fn(unicode: char) -> Option<u8>,
17
18    /// cp437, from, to
19    remaps: Cow<'static, [(u8, char, char)]>,
20}
21
22impl Cp437Dialect {
23    /// Check, whether the specified Unicode codepoint overlaps with a cp437 one.
24    #[inline]
25    pub fn overlap_unicode(&self, unicode: char) -> bool {
26        (self.overlap_unicode)(unicode) && !self.remaps.iter().rev().find(|&&(_, _, to)| to == unicode).is_some()
27    }
28
29    /// Check, whether the specified cp437 codepoint overlaps with a Unicode one.
30    #[inline]
31    pub fn overlap_cp437(&self, cp437: u8) -> bool {
32        (self.overlap_cp437)(cp437) && !self.remaps.iter().rev().find(|&&(whom, _, _)| whom == cp437).is_some()
33    }
34
35    /// Decode a single cp437 codepoint into a Unicode one.
36    #[inline(always)]
37    pub fn decode(&self, cp437: u8) -> char {
38        self.cp437_to_unicode[cp437 as usize]
39    }
40
41    /// Try to encode a single Unicode codepoint as a cp437 one.
42    #[inline]
43    pub fn encode(&self, unicode: char) -> Option<u8> {
44        self.remaps.iter().rev().find(|&&(_, _, to)| to == unicode).map(|&(whom, _, _)| whom).or_else(|| (self.encode)(unicode))
45    }
46
47    /// Map the specified cp437 codepoint mapped to the specified unicode character instead.
48    ///
49    /// # Examples
50    ///
51    /// Remap `√` to `✓`:
52    ///
53    /// ```
54    /// # use codepage_437::CP437_WINGDINGS;
55    /// let square_root_or_checkmark = CP437_WINGDINGS.encode('√').unwrap();
56    ///
57    /// let mut mapping = CP437_WINGDINGS.clone();
58    /// mapping.remap(square_root_or_checkmark, '✓');
59    /// assert_eq!(mapping.decode(square_root_or_checkmark), '✓');
60    /// ```
61    pub fn remap(&mut self, cp437: u8, unicode: char) -> &mut Cp437Dialect {
62        self.remaps.to_mut().push((cp437, self.cp437_to_unicode[cp437 as usize], unicode));
63        self.cp437_to_unicode[cp437 as usize] = unicode;
64        self
65    }
66}
67
68// These traits are implemented manually, because rustc is at a loss for big arrays (like the 256 one).
69impl fmt::Debug for Cp437Dialect {
70    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71        f.debug_struct("Cp437Dialect")
72            .field("cp437_to_unicode", &&self.cp437_to_unicode[..])
73            .field("overlap_unicode", &self.overlap_unicode)
74            .field("overlap_cp437", &self.overlap_cp437)
75            .field("encode", &self.encode)
76            .field("remaps", &self.remaps)
77            .finish()
78    }
79}
80
81impl Hash for Cp437Dialect {
82    fn hash<H: Hasher>(&self, state: &mut H) {
83        self.cp437_to_unicode[..].hash(state);
84        self.overlap_unicode.hash(state);
85        self.overlap_cp437.hash(state);
86        self.encode.hash(state);
87        self.remaps.hash(state);
88    }
89}
90
91impl cmp::Eq for Cp437Dialect {}
92
93impl cmp::PartialEq for Cp437Dialect {
94    fn eq(&self, other: &Cp437Dialect) -> bool {
95        self.cp437_to_unicode[..] == other.cp437_to_unicode[..] &&  // align
96        self.overlap_unicode == other.overlap_unicode &&            // align
97        self.overlap_cp437 == other.overlap_cp437 &&                // align
98        self.encode == other.encode &&                              // align
99        self.remaps == other.remaps
100    }
101}
102
103impl cmp::Ord for Cp437Dialect {
104    fn cmp(&self, other: &Cp437Dialect) -> cmp::Ordering {
105        self.cp437_to_unicode[..]
106            .cmp(&other.cp437_to_unicode[..])
107            .then(self.overlap_unicode.cmp(&other.overlap_unicode))
108            .then(self.overlap_cp437.cmp(&other.overlap_cp437))
109            .then(self.encode.cmp(&other.encode))
110            .then(self.remaps.cmp(&other.remaps))
111    }
112}
113
114impl cmp::PartialOrd for Cp437Dialect {
115    fn partial_cmp(&self, other: &Cp437Dialect) -> Option<cmp::Ordering> {
116        Some(self.cmp(other))
117    }
118}
119
120
121include!(concat!(env!("OUT_DIR"), "/dialects.rs"));