strum icon indicating copy to clipboard operation
strum copied to clipboard

EnumString: have a const version

Open EvolveArt opened this issue 7 months ago • 0 comments

Hello,

I was wondering why there is no EnumStringConst proc-macro since it's used in a lot of cases when wanting to do compile-time validation on a string literral.

e.g

#[allow(non_camel_case_types)]
#[derive(
    Debug,
    Clone,
    Copy,
    serde::Serialize,
    serde::Deserialize,
    strum::Display,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    utoipa::ToSchema,
    EnumString,
)]
pub enum Currency {
    USD,
    EUR,
}

impl Currency {
    pub fn to_ascii_uppercase(&self) -> String {
        format!("{}", self).to_ascii_uppercase()
    }
    
    const fn from_str_const(s: &str) -> Option<Self> {
        // Manual matching since we can't iterate in const fn
        match s.as_bytes() {
            b"USD" => Some(Currency::USD),
            b"EUR" => Some(Currency::EUR),
            _ => None,
        }
    }
}

// Macro for compile-time validation
macro_rules! currency {
    ($s:literal) => {{
        const _: () = {
            if Currency::from_str_const($s).is_none() {
                panic!(concat!("Invalid Currency: ", $s));
            }
        };
        // Use the strum-generated FromStr at runtime
        <Currency as std::str::FromStr>::from_str($s).unwrap()
    }};
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_valid_currency_creation() {
        let currency = currency!("USD");
        assert_eq!(currency, Currency::USD);

        let currency = currency!("EUR");
        assert_eq!(currency, Currency::EUR);

        // let currency = currency!("BTC");
        // assert_eq!(currency, Currency::BTC);
    }
}

I figured out that the from_str_const could easily be automatically generated in a similar fashion as the from_str method.

Any objection or reason not to do that ?

Happy to contribute if necessary.

EvolveArt avatar Aug 08 '25 16:08 EvolveArt