use std::{fmt::Display, marker::PhantomData};
#[derive(Debug, Default)]
pub struct VarParseError<Var>(PhantomData<Var>);
impl<Var> VarParseError<Var> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<Var: Default + Display> Display for VarParseError<Var> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "String is not {}", Var::default())
}
}
#[macro_export]
macro_rules! var {
($var:ident) => {
var!(pub(self) $var);
};
($v:vis $var:ident) => {
$crate::paste::paste! {
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
$v struct [< $var:camel >];
impl [< $var:camel >] {
$v const fn name() -> &'static str {
stringify!([< $var:snake:lower >])
}
}
impl std::fmt::Display for [< $var:camel >] {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(Self::name(), f)
}
}
impl std::str::FromStr for [< $var:camel >] {
type Err = $crate::var::VarParseError<Self>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == Self::name() {
Ok([< $var:camel >])
} else {
Err($crate::var::VarParseError::new())
}
}
}
}
};
}
#[macro_export]
macro_rules! once_var {
($var:ident) => {
once_var!(pub(self) $var);
};
($v:vis $var:ident) => {
$crate::paste::paste! {
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
$v struct [< $var:camel >];
impl [< $var:camel >] {
$v fn set_name(name: &'static str) -> Result<(), &'static str> {
[< $var:snake:upper _STR >].set(name)
}
$v fn name() -> Option<&'static str> {
[< $var:snake:upper _STR >].get().map(|v| *v)
}
}
static [< $var:snake:upper _STR >]: std::sync::OnceLock<&str> = std::sync::OnceLock::new();
impl std::fmt::Display for [< $var:camel >] {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = Self::name()
.expect("Variable name not set. Use `set_name` before using the variable.");
std::fmt::Display::fmt(name, f)
}
}
impl std::str::FromStr for [< $var:camel >] {
type Err = $crate::var::VarParseError<Self>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let name = [< $var:camel >]::name()
.expect("Variable name not set. Use `set_name` before using the variable.");
if s == name {
Ok([< $var:camel >])
} else {
Err($crate::var::VarParseError::new())
}
}
}
}
};
}