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#[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 #[inline]
31 pub fn new(c: char) -> Self {
32 Char(c)
33 }
34
35 #[inline]
46 pub fn to_char(&self) -> char {
47 self.0
48 }
49
50 #[inline]
60 pub fn from_u32(c: u32) -> Option<Self> {
61 Some(Char(char::from_u32(c)?))
62 }
63
64 #[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}