regex_charclass/
char.rs

1use std::{
2    char,
3    fmt::Display,
4    ops::{Add, AddAssign, Sub},
5};
6
7use irange::integer::Bounded;
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11pub(super) static INVALID_MIN: u32 = 0xD800;
12pub(super) static INVALID_MAX: u32 = 0xDFFF;
13pub(super) static INVALID_SIZE: u32 = 0x800;
14
15/// A structure holding a `char` to use within a `RangeSet`.
16#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, PartialOrd, Ord)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub struct Char(char);
19
20impl Char {
21    /// Create a new instance from the given `char`.
22    ///
23    /// # Example:
24    ///
25    /// ```
26    /// use regex_charclass::char::Char;
27    ///  
28    /// let c = Char::new('a');
29    /// ```
30    #[inline]
31    pub fn new(c: char) -> Self {
32        Char(c)
33    }
34
35    /// Return the `char`.
36    ///
37    /// # Example:
38    ///
39    /// ```
40    /// use regex_charclass::char::Char;
41    ///  
42    /// let c = Char::new('a');
43    /// assert_eq!('a', c.to_char());
44    /// ```
45    #[inline]
46    pub fn to_char(&self) -> char {
47        self.0
48    }
49
50    /// Create a new instance from the given `char`.
51    ///
52    /// # Example:
53    ///
54    /// ```
55    /// use regex_charclass::char::Char;
56    ///  
57    /// let c = Char::from_u32(97);
58    /// ```
59    #[inline]
60    pub fn from_u32(c: u32) -> Option<Self> {
61        Some(Char(char::from_u32(c)?))
62    }
63
64    /// Return the `char` code as a `u32`.
65    ///
66    /// # Example:
67    ///
68    /// ```
69    /// use regex_charclass::char::Char;
70    ///  
71    /// let c = Char::new('a');
72    /// assert_eq!(97, c.to_u32());
73    /// ```
74    #[inline]
75    pub fn to_u32(&self) -> u32 {
76        self.0 as u32
77    }
78}
79
80impl Display for Char {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        if ('\u{20}'..'\u{7E}').contains(&self.0) {
83            write!(f, "{}", self.0)
84        } else {
85            write!(f, "\\u{{{:04x}}}", self.to_u32())
86        }
87    }
88}
89
90impl Add<Char> for Char {
91    type Output = Char;
92
93    fn add(self, rhs: Self) -> Self::Output {
94        let mut sum = self.0 as u32 + rhs.0 as u32;
95        if sum >= INVALID_MIN && sum <= INVALID_MAX {
96            sum = INVALID_MAX + 1 + sum - INVALID_MIN;
97        }
98        if let Some(new_char) = char::from_u32(sum) {
99            Char(new_char)
100        } else {
101            panic!("attempt to add with overflow");
102        }
103    }
104}
105
106impl Sub<Char> for Char {
107    type Output = Char;
108
109    fn sub(self, rhs: Self) -> Self::Output {
110        let mut minuhend = self.0 as u32;
111        if minuhend >= INVALID_MIN {
112            minuhend -= INVALID_SIZE;
113        }
114        let mut subtrahend = rhs.0 as u32;
115        if subtrahend >= INVALID_MIN {
116            subtrahend -= INVALID_SIZE;
117        }
118        let mut sub = minuhend - subtrahend;
119        if sub >= INVALID_MIN {
120            sub += INVALID_SIZE;
121        }
122        if let Some(new_char) = char::from_u32(sub) {
123            Char(new_char)
124        } else {
125            panic!("attempt to sub with overflow");
126        }
127    }
128}
129
130impl AddAssign for Char {
131    fn add_assign(&mut self, rhs: Self) {
132        self.0 = (*self + rhs).0;
133    }
134}
135
136impl Bounded for Char {
137    fn min_value() -> Self {
138        Char('\0')
139    }
140
141    fn max_value() -> Self {
142        Char(char::MAX)
143    }
144
145    fn one() -> Self {
146        Char('\u{1}')
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn char_add() -> Result<(), String> {
156        assert_eq!(Char::new('\u{3}'), Char::new('\u{2}') + Char::one());
157        assert_eq!(Char::new('\u{E000}'), Char::new('\u{D7FF}') + Char::one());
158        assert_eq!(Char::new('\u{E001}'), Char::new('\u{E000}') + Char::one());
159
160        Ok(())
161    }
162
163    #[test]
164    fn char_add_assign() -> Result<(), String> {
165        let mut c = Char::new('\u{2}');
166        c += Char::one();
167        assert_eq!(Char::new('\u{3}'), c);
168
169        let mut c = Char::new('\u{D7FF}');
170        c += Char::one();
171        assert_eq!(Char::new('\u{E000}'), c);
172
173        let mut c = Char::new('\u{E000}');
174        c += Char::one();
175        assert_eq!(Char::new('\u{E001}'), c);
176
177        Ok(())
178    }
179
180    #[test]
181    fn char_sub() -> Result<(), String> {
182        assert_eq!(Char::new('\u{2}'), Char::new('\u{3}') - Char::one());
183        assert_eq!(Char::new('\u{D7FF}'), Char::new('\u{E000}') - Char::one());
184        assert_eq!(Char::new('\u{E000}'), Char::new('\u{E001}') - Char::one());
185
186        Ok(())
187    }
188}