1use std::convert::{Into, TryFrom};
2use std::io;
3
4pub static CHARSET_ALPHA_LOWER: [char; 26] = [
6 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
7 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
8];
9
10pub static CHARSET_ALPHA_UPPER: [char; 26] = [
12 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
13 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
14];
15
16pub static CHARSET_NUMERIC: [char; 10] =
18 ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
19
20pub static CHARSET_PROSE: [char; 9] =
22 ['.', ':', ',', ';', '!', '?', ' ', '\'', '"'];
23
24pub static CHARSET_MATHOPS: [char; 7] = ['+', '-', '*', '/', '=', '<', '>'];
26
27pub static CHARSET_DELIM: [char; 6] = ['(', ')', '[', ']', '{', '}'];
29
30pub static CHARSET_MISC_SPECIAL: [char; 11] =
33 ['#', '@', '$', '%', '&', '|', '\\', '~', '^', '_', '`'];
34
35#[derive(Debug, PartialEq)]
60pub enum CharsetName {
61 AlphaLower,
63 AlphaUpper,
64 Numeric,
65 Mathops,
66 Prose,
67 Delim,
68 MiscSpecial,
69 Alpha,
71 Special,
72}
73
74impl TryFrom<char> for CharsetName {
75 type Error = io::Error;
76
77 fn try_from(c: char) -> io::Result<Self> {
78 match c {
79 'U' => Ok(Self::AlphaUpper),
81 'L' => Ok(Self::AlphaLower),
82 'N' => Ok(Self::Numeric),
83 'M' => Ok(Self::Mathops),
84 'P' => Ok(Self::Prose),
85 'D' => Ok(Self::Delim),
86 'X' => Ok(Self::MiscSpecial),
87 'A' => Ok(Self::Alpha),
89 'S' => Ok(Self::Special),
90 _ => Err(io::Error::new(
92 io::ErrorKind::InvalidInput,
93 format!("Invalid character set abbreviation: {}", c),
94 )),
95 }
96 }
97}
98
99#[derive(Debug)]
121pub struct CharsetSpec {
122 alpha_lower: bool,
123 alpha_upper: bool,
124 numeric: bool,
125 mathops: bool,
126 prose: bool,
127 delim: bool,
128 misc_special: bool,
129 additions: Vec<char>,
130}
131
132impl CharsetSpec {
133 pub fn construct(mut self) -> Vec<char> {
136 let mut set = vec![];
137 if self.alpha_lower {
138 set.append(&mut CHARSET_ALPHA_LOWER.to_vec());
139 }
140 if self.alpha_upper {
141 set.append(&mut CHARSET_ALPHA_UPPER.to_vec());
142 }
143 if self.numeric {
144 set.append(&mut CHARSET_NUMERIC.to_vec());
145 }
146 if self.mathops {
147 set.append(&mut CHARSET_MATHOPS.to_vec());
148 }
149 if self.prose {
150 set.append(&mut CHARSET_PROSE.to_vec());
151 }
152 if self.delim {
153 set.append(&mut CHARSET_DELIM.to_vec());
154 }
155 if self.misc_special {
156 set.append(&mut CHARSET_MISC_SPECIAL.to_vec());
157 }
158 set.append(&mut self.additions);
159 set.sort();
160 set.dedup();
161 set
162 }
163
164 pub fn empty() -> Self {
172 Self {
173 alpha_lower: false,
174 alpha_upper: false,
175 numeric: false,
176 mathops: false,
177 prose: false,
178 delim: false,
179 misc_special: false,
180 additions: vec![],
181 }
182 }
183
184 pub fn std64() -> Self {
196 Self {
197 alpha_lower: true,
198 alpha_upper: true,
199 numeric: true,
200 mathops: false,
201 prose: false,
202 delim: false,
203 misc_special: false,
204 additions: vec!['-', '_'],
205 }
206 }
207
208 pub fn printable_ascii() -> Self {
217 Self {
218 alpha_lower: true,
219 alpha_upper: true,
220 numeric: true,
221 mathops: true,
222 prose: true,
223 delim: true,
224 misc_special: true,
225 additions: vec![],
226 }
227 }
228}
229
230impl std::str::FromStr for CharsetSpec {
231 type Err = io::Error;
232
233 fn from_str(s: &str) -> io::Result<Self> {
234 let mut spec = Self::empty();
235 for c in s.chars() {
236 let name = CharsetName::try_from(c)?;
237 spec += name;
238 }
239 Ok(spec)
240 }
241}
242
243impl Into<Vec<char>> for CharsetSpec {
244 #[inline]
245 fn into(self) -> Vec<char> { self.construct() }
246}
247
248impl std::ops::AddAssign<&str> for CharsetSpec {
249 #[inline]
250 fn add_assign(&mut self, more: &str) {
251 for c in more.chars() {
252 (*self) += c;
253 }
254 }
255}
256
257impl std::ops::AddAssign<char> for CharsetSpec {
258 #[inline]
259 fn add_assign(&mut self, c: char) { self.additions.push(c); }
260}
261
262impl std::ops::AddAssign<CharsetName> for CharsetSpec {
263 fn add_assign(&mut self, name: CharsetName) {
264 match name {
265 CharsetName::AlphaLower => self.alpha_lower = true,
267 CharsetName::AlphaUpper => self.alpha_upper = true,
268 CharsetName::Numeric => self.numeric = true,
269 CharsetName::Mathops => self.mathops = true,
270 CharsetName::Prose => self.prose = true,
271 CharsetName::Delim => self.delim = true,
272 CharsetName::MiscSpecial => self.misc_special = true,
273 CharsetName::Alpha => {
275 self.alpha_lower = true;
276 self.alpha_upper = true;
277 },
278 CharsetName::Special => {
279 self.mathops = true;
280 self.prose = true;
281 self.delim = true;
282 self.misc_special = true;
283 },
284 }
285 }
286}
287
288impl std::ops::SubAssign<CharsetName> for CharsetSpec {
289 fn sub_assign(&mut self, name: CharsetName) {
290 match name {
291 CharsetName::AlphaLower => self.alpha_lower = false,
293 CharsetName::AlphaUpper => self.alpha_upper = false,
294 CharsetName::Numeric => self.numeric = false,
295 CharsetName::Mathops => self.mathops = false,
296 CharsetName::Prose => self.prose = false,
297 CharsetName::Delim => self.delim = false,
298 CharsetName::MiscSpecial => self.misc_special = false,
299 CharsetName::Alpha => {
301 self.alpha_lower = false;
302 self.alpha_upper = false;
303 },
304 CharsetName::Special => {
305 self.mathops = false;
306 self.prose = false;
307 self.delim = false;
308 self.misc_special = false;
309 },
310 }
311 }
312}
313
314#[cfg(test)]
316mod tests {
317 use std::convert::TryFrom;
318
319 use super::CharsetName::*;
320 use super::{CharsetName, CharsetSpec};
321
322 #[test]
323 fn parsing_charset_names() {
324 assert_eq!(CharsetName::try_from('U').unwrap(), AlphaUpper);
326 assert_eq!(CharsetName::try_from('L').unwrap(), AlphaLower);
327 assert_eq!(CharsetName::try_from('N').unwrap(), Numeric);
328 assert_eq!(CharsetName::try_from('M').unwrap(), Mathops);
329 assert_eq!(CharsetName::try_from('P').unwrap(), Prose);
330 assert_eq!(CharsetName::try_from('D').unwrap(), Delim);
331 assert_eq!(CharsetName::try_from('X').unwrap(), MiscSpecial);
332 assert_eq!(CharsetName::try_from('A').unwrap(), Alpha);
334 assert_eq!(CharsetName::try_from('S').unwrap(), Special);
335 assert!(CharsetName::try_from('Z').is_err());
337 }
338
339 #[test]
340 fn parsing_charset_specs() {
341 let (alpha, alnum) = {
342 let mut alpha = [
343 super::CHARSET_ALPHA_UPPER.to_vec(),
344 super::CHARSET_ALPHA_LOWER.to_vec(),
345 ]
346 .concat();
347 let mut alnum = [
348 super::CHARSET_ALPHA_UPPER.to_vec(),
349 super::CHARSET_ALPHA_LOWER.to_vec(),
350 super::CHARSET_NUMERIC.to_vec(),
351 ]
352 .concat();
353 alpha.sort();
354 alnum.sort();
355 (alpha, alnum)
356 };
357
358 assert_eq!("LU".parse::<CharsetSpec>().unwrap().construct(), alpha);
359 assert_eq!("LUN".parse::<CharsetSpec>().unwrap().construct(), alnum);
360 }
361
362 #[test]
363 fn adding_charset_to_spec() {
364 let mut spec = CharsetSpec::empty();
365 spec += Mathops;
366 assert_eq!(spec.construct(), vec!['*', '+', '-', '/', '<', '=', '>'])
367 }
368
369 #[test]
370 fn subtracting_charset_from_spec() {
371 let mut spec = CharsetSpec::std64();
372 spec -= Alpha;
373 spec -= Numeric;
374 assert_eq!(spec.construct(), vec!['-', '_'])
375 }
376
377 #[test]
378 fn adding_chars_to_spec() {
379 let mut spec = CharsetSpec::empty();
380 spec += 'a';
381 spec += 'b';
382 spec += 'c';
383 spec += 'd';
384 assert_eq!(spec.construct(), vec!['a', 'b', 'c', 'd']);
385 }
386
387 #[test]
388 fn adding_strings_to_spec() {
389 let mut spec = CharsetSpec::empty();
390 spec += "abcd";
391 assert_eq!(spec.construct(), vec!['a', 'b', 'c', 'd']);
392 }
393}