use std::convert::{Into, TryFrom};
use std::io;
pub static CHARSET_ALPHA_LOWER: [char; 26] = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
];
pub static CHARSET_ALPHA_UPPER: [char; 26] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
];
pub static CHARSET_NUMERIC: [char; 10] =
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
pub static CHARSET_PROSE: [char; 9] =
['.', ':', ',', ';', '!', '?', ' ', '\'', '"'];
pub static CHARSET_MATHOPS: [char; 7] = ['+', '-', '*', '/', '=', '<', '>'];
pub static CHARSET_DELIM: [char; 6] = ['(', ')', '[', ']', '{', '}'];
pub static CHARSET_MISC_SPECIAL: [char; 11] =
['#', '@', '$', '%', '&', '|', '\\', '~', '^', '_', '`'];
#[derive(Debug, PartialEq)]
pub enum CharsetName {
AlphaLower,
AlphaUpper,
Numeric,
Mathops,
Prose,
Delim,
MiscSpecial,
Alpha,
Special,
}
impl TryFrom<char> for CharsetName {
type Error = io::Error;
fn try_from(c: char) -> io::Result<Self> {
match c {
'U' => Ok(Self::AlphaUpper),
'L' => Ok(Self::AlphaLower),
'N' => Ok(Self::Numeric),
'M' => Ok(Self::Mathops),
'P' => Ok(Self::Prose),
'D' => Ok(Self::Delim),
'X' => Ok(Self::MiscSpecial),
'A' => Ok(Self::Alpha),
'S' => Ok(Self::Special),
_ => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Invalid character set abbreviation: {}", c),
)),
}
}
}
#[derive(Debug)]
pub struct CharsetSpec {
alpha_lower: bool,
alpha_upper: bool,
numeric: bool,
mathops: bool,
prose: bool,
delim: bool,
misc_special: bool,
additions: Vec<char>,
}
impl CharsetSpec {
pub fn construct(mut self) -> Vec<char> {
let mut set = vec![];
if self.alpha_lower {
set.append(&mut CHARSET_ALPHA_LOWER.to_vec());
}
if self.alpha_upper {
set.append(&mut CHARSET_ALPHA_UPPER.to_vec());
}
if self.numeric {
set.append(&mut CHARSET_NUMERIC.to_vec());
}
if self.mathops {
set.append(&mut CHARSET_MATHOPS.to_vec());
}
if self.prose {
set.append(&mut CHARSET_PROSE.to_vec());
}
if self.delim {
set.append(&mut CHARSET_DELIM.to_vec());
}
if self.misc_special {
set.append(&mut CHARSET_MISC_SPECIAL.to_vec());
}
set.append(&mut self.additions);
set.sort();
set.dedup();
set
}
pub fn empty() -> Self {
Self {
alpha_lower: false,
alpha_upper: false,
numeric: false,
mathops: false,
prose: false,
delim: false,
misc_special: false,
additions: vec![],
}
}
pub fn std64() -> Self {
Self {
alpha_lower: true,
alpha_upper: true,
numeric: true,
mathops: false,
prose: false,
delim: false,
misc_special: false,
additions: vec!['-', '_'],
}
}
pub fn printable_ascii() -> Self {
Self {
alpha_lower: true,
alpha_upper: true,
numeric: true,
mathops: true,
prose: true,
delim: true,
misc_special: true,
additions: vec![],
}
}
}
impl std::str::FromStr for CharsetSpec {
type Err = io::Error;
fn from_str(s: &str) -> io::Result<Self> {
let mut spec = Self::empty();
for c in s.chars() {
let name = CharsetName::try_from(c)?;
spec += name;
}
Ok(spec)
}
}
impl Into<Vec<char>> for CharsetSpec {
#[inline]
fn into(self) -> Vec<char> { self.construct() }
}
impl std::ops::AddAssign<&str> for CharsetSpec {
#[inline]
fn add_assign(&mut self, more: &str) {
for c in more.chars() {
(*self) += c;
}
}
}
impl std::ops::AddAssign<char> for CharsetSpec {
#[inline]
fn add_assign(&mut self, c: char) { self.additions.push(c); }
}
impl std::ops::AddAssign<CharsetName> for CharsetSpec {
fn add_assign(&mut self, name: CharsetName) {
match name {
CharsetName::AlphaLower => self.alpha_lower = true,
CharsetName::AlphaUpper => self.alpha_upper = true,
CharsetName::Numeric => self.numeric = true,
CharsetName::Mathops => self.mathops = true,
CharsetName::Prose => self.prose = true,
CharsetName::Delim => self.delim = true,
CharsetName::MiscSpecial => self.misc_special = true,
CharsetName::Alpha => {
self.alpha_lower = true;
self.alpha_upper = true;
},
CharsetName::Special => {
self.mathops = true;
self.prose = true;
self.delim = true;
self.misc_special = true;
},
}
}
}
impl std::ops::SubAssign<CharsetName> for CharsetSpec {
fn sub_assign(&mut self, name: CharsetName) {
match name {
CharsetName::AlphaLower => self.alpha_lower = false,
CharsetName::AlphaUpper => self.alpha_upper = false,
CharsetName::Numeric => self.numeric = false,
CharsetName::Mathops => self.mathops = false,
CharsetName::Prose => self.prose = false,
CharsetName::Delim => self.delim = false,
CharsetName::MiscSpecial => self.misc_special = false,
CharsetName::Alpha => {
self.alpha_lower = false;
self.alpha_upper = false;
},
CharsetName::Special => {
self.mathops = false;
self.prose = false;
self.delim = false;
self.misc_special = false;
},
}
}
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use super::CharsetName::*;
use super::{CharsetName, CharsetSpec};
#[test]
fn parsing_charset_names() {
assert_eq!(CharsetName::try_from('U').unwrap(), AlphaUpper);
assert_eq!(CharsetName::try_from('L').unwrap(), AlphaLower);
assert_eq!(CharsetName::try_from('N').unwrap(), Numeric);
assert_eq!(CharsetName::try_from('M').unwrap(), Mathops);
assert_eq!(CharsetName::try_from('P').unwrap(), Prose);
assert_eq!(CharsetName::try_from('D').unwrap(), Delim);
assert_eq!(CharsetName::try_from('X').unwrap(), MiscSpecial);
assert_eq!(CharsetName::try_from('A').unwrap(), Alpha);
assert_eq!(CharsetName::try_from('S').unwrap(), Special);
assert!(CharsetName::try_from('Z').is_err());
}
#[test]
fn parsing_charset_specs() {
let (alpha, alnum) = {
let mut alpha = [
super::CHARSET_ALPHA_UPPER.to_vec(),
super::CHARSET_ALPHA_LOWER.to_vec(),
]
.concat();
let mut alnum = [
super::CHARSET_ALPHA_UPPER.to_vec(),
super::CHARSET_ALPHA_LOWER.to_vec(),
super::CHARSET_NUMERIC.to_vec(),
]
.concat();
alpha.sort();
alnum.sort();
(alpha, alnum)
};
assert_eq!("LU".parse::<CharsetSpec>().unwrap().construct(), alpha);
assert_eq!("LUN".parse::<CharsetSpec>().unwrap().construct(), alnum);
}
#[test]
fn adding_charset_to_spec() {
let mut spec = CharsetSpec::empty();
spec += Mathops;
assert_eq!(spec.construct(), vec!['*', '+', '-', '/', '<', '=', '>'])
}
#[test]
fn subtracting_charset_from_spec() {
let mut spec = CharsetSpec::std64();
spec -= Alpha;
spec -= Numeric;
assert_eq!(spec.construct(), vec!['-', '_'])
}
#[test]
fn adding_chars_to_spec() {
let mut spec = CharsetSpec::empty();
spec += 'a';
spec += 'b';
spec += 'c';
spec += 'd';
assert_eq!(spec.construct(), vec!['a', 'b', 'c', 'd']);
}
#[test]
fn adding_strings_to_spec() {
let mut spec = CharsetSpec::empty();
spec += "abcd";
assert_eq!(spec.construct(), vec!['a', 'b', 'c', 'd']);
}
}