macro_rules! impl_parse_enum {
($t:ty, $v1:ident $(, $v:ident)* $(,)?) => {
impl std::str::FromStr for $t {
type Err = UnrecognizedVariant<Self>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.eq_ignore_ascii_case(stringify!($v1)) {
Ok(Self::$v1)
}
$(
else if s.eq_ignore_ascii_case(stringify!($v)) {
Ok(Self::$v)
}
)*
else {
Err(Self::Err::new(s))
}
}
}
impl crate::parse::StringVariant for $t {
const VARIANTS: &[&str] = &[stringify!($v1) $(, stringify!($v))*];
}
};
}
macro_rules! impl_display_enum {
($t:ty, $($i:ident),* $(,)?) => {
impl std::fmt::Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
$(
Self::$i => {
const UPPERCASE: &str = match str::from_utf8(
&const {
let mut buf = [0; stringify!($i).len()];
buf.copy_from_slice(stringify!($i).as_bytes());
buf.make_ascii_uppercase();
buf
},
) {
Ok(s) => s,
Err(_) => unreachable!(),
};
f.write_str(UPPERCASE)
}
)*
}
}
}
}
}
pub const fn to_ascii_lowercase<'a>(text: &[u8], buf: &'a mut [u8]) -> Option<&'a [u8]> {
let Some((lower_buf, _)) = buf.split_at_mut_checked(text.len()) else {
return None;
};
lower_buf.copy_from_slice(text);
lower_buf.make_ascii_lowercase();
Some(lower_buf)
}
macro_rules! match_ci {
(
$s:expr,
$l:literal $(| $lo:literal)* => $i:expr,
$(_ => $default:expr)? $(,)?
) => {
if $s.eq_ignore_ascii_case($l) $(|| $s.eq_ignore_ascii_case($lo))* {
$i
} $(else {
$default
})?
};
(
$s:expr,
$l_first:literal $(| $lo_first:literal)* => $i_first:expr,
$($l:literal $(| $lo:literal)* => $i:expr),*
$(, _ => $default:expr)? $(,)?
) => {
if $s.eq_ignore_ascii_case($l_first) $(|| $s.eq_ignore_ascii_case($lo_first))* {
$i_first
} $(else if $s.eq_ignore_ascii_case($l) $(|| $s.eq_ignore_ascii_case($lo))* {
$i
})* $(else {
$default
})?
};
}
#[cfg(test)]
mod tests {
#[test]
fn match_ci_is_case_insensitive() {
let result = match_ci! {"teSt",
" TEST" => " TEST",
"TEst" => "TEst",
"teSt " => "teSt ",
_ => "unmatched"
};
assert_eq!(result, "TEst");
}
}