#[macro_export]
macro_rules! char_property {
(
$(#[$prop_meta:meta])*
pub enum $prop_name:ident {
abbr => $prop_abbr:expr;
long => $prop_long:expr;
human => $prop_human:expr;
$(
$(#[$variant_meta:meta])*
$variant_name:ident {
abbr => $variant_abbr:ident,
long => $variant_long:ident,
human => $variant_human:expr,
}
)*
}
$(#[$abbr_mod_meta:meta])*
pub mod $abbr_mod:ident for abbr;
$(#[$long_mod_meta:meta])*
pub mod $long_mod:ident for long;
) => {
$(#[$prop_meta])*
#[allow(bad_style)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum $prop_name {
$( $(#[$variant_meta])* $variant_name, )*
}
$(#[$abbr_mod_meta])*
#[allow(bad_style)]
pub mod $abbr_mod {
$( pub use super::$prop_name::$variant_name as $variant_abbr; )*
}
$(#[$long_mod_meta])*
#[allow(bad_style)]
pub mod $long_mod {
$( pub use super::$prop_name::$variant_name as $variant_long; )*
}
char_property! {
__impl FromStr for $prop_name;
$(
$variant_abbr => $prop_name::$variant_name;
$variant_long => $prop_name::$variant_name;
)*
}
char_property! {
__impl CharProperty for $prop_name;
$prop_abbr;
$prop_long;
$prop_human;
}
char_property! {
__impl Display for $prop_name by EnumeratedCharProperty
}
impl $crate::EnumeratedCharProperty for $prop_name {
fn all_values() -> &'static [$prop_name] {
const VALUES: &[$prop_name] = &[
$( $prop_name::$variant_name, )*
];
VALUES
}
fn abbr_name(&self) -> &'static str {
match *self {
$( $prop_name::$variant_name => stringify!($variant_abbr), )*
}
}
fn long_name(&self) -> &'static str {
match *self {
$( $prop_name::$variant_name => stringify!($variant_long), )*
}
}
fn human_name(&self) -> &'static str {
match *self {
$( $prop_name::$variant_name => $variant_human, )*
}
}
}
};
(
$(#[$prop_meta:meta])*
pub struct $prop_name:ident(bool) {
abbr => $prop_abbr:expr;
long => $prop_long:expr;
human => $prop_human:expr;
data_table_path => $data_path:expr;
}
$(#[$is_fn_meta:meta])*
pub fn $is_fn:ident(char) -> bool;
) => {
$(#[$prop_meta])*
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct $prop_name(bool);
$(#[$is_fn_meta])*
pub fn $is_fn(ch: char) -> bool {
$prop_name::of(ch).as_bool()
}
impl $prop_name {
pub fn of(ch: char) -> Self {
use $crate::tables::CharDataTable;
const TABLE: CharDataTable<()> = include!($data_path);
$prop_name(TABLE.contains(ch))
}
pub fn as_bool(&self) -> bool { self.0 }
}
char_property! {
__impl FromStr for $prop_name;
y => $prop_name(true);
yes => $prop_name(true);
t => $prop_name(true);
true => $prop_name(true);
n => $prop_name(false);
no => $prop_name(false);
f => $prop_name(false);
false => $prop_name(false);
}
char_property! {
__impl CharProperty for $prop_name;
$prop_abbr;
$prop_long;
$prop_human;
}
impl $crate::TotalCharProperty for $prop_name {
fn of(ch: char) -> Self { Self::of(ch) }
}
impl $crate::BinaryCharProperty for $prop_name {
fn as_bool(&self) -> bool { self.as_bool() }
}
impl From<$prop_name> for bool {
fn from(prop: $prop_name) -> bool { prop.as_bool() }
}
char_property! {
__impl Display for $prop_name by BinaryCharProperty
}
};
(
__impl CharProperty for $prop_name:ident;
$prop_abbr:expr;
$prop_long:expr;
$prop_human:expr;
) => {
impl $crate::CharProperty for $prop_name {
fn prop_abbr_name() -> &'static str { $prop_abbr }
fn prop_long_name() -> &'static str { $prop_long }
fn prop_human_name() -> &'static str { $prop_human }
}
};
(
__impl FromStr for $prop_name:ident;
$( $id:ident => $value:expr; )*
) => {
#[allow(unreachable_patterns)]
impl $crate::__str::FromStr for $prop_name {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
pub fn str_eq_ignore_ascii_case(this: &str, that: &str) -> bool {
this.len() == that.len() &&
this.bytes().zip(that.bytes()).all(|(a, b)| {
u8_to_ascii_lowercase(a) == u8_to_ascii_lowercase(b)
})
}
static ASCII_LOWERCASE_MAP: [u8; 256] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
b'@',
b'a', b'b', b'c', b'd', b'e', b'f', b'g',
b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
b'x', b'y', b'z',
b'[', b'\\', b']', b'^', b'_',
b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g',
b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
b'x', b'y', b'z', b'{', b'|', b'}', b'~', 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];
#[inline]
pub fn u8_to_ascii_lowercase(this: u8) -> u8 {
ASCII_LOWERCASE_MAP[this as usize]
}
match s {
$( stringify!($id) => Ok($value), )*
$( s if str_eq_ignore_ascii_case(s, stringify!($id)) => Ok($value), )*
_ => Err(()),
}
}
}
};
( __impl Display for $prop_name:ident by $trait:ident ) => {
impl $crate::__fmt::Display for $prop_name {
fn fmt(&self, f: &mut $crate::__fmt::Formatter) -> $crate::__fmt::Result {
$crate::$trait::human_name(self).fmt(f)
}
}
};
}