enum-helper-derive 0.2.0

Procedural macro implementation for enum-helper
Documentation
use crate::template::{TemplateSegment, parse_template};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorMsgVar {
    Name,
    Names(ListMod),
    Aliases(ListMod),
    Input,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ListMod {
    sep: Option<String>,
    quote: Option<String>,
}

#[derive(Debug)]
pub struct ParseTemplateVarError(String);

impl std::str::FromStr for ErrorMsgVar {
    type Err = ParseTemplateVarError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.trim() {
            "name" => return Ok(Self::Name),
            "input" => return Ok(Self::Input),
            _ => {}
        }

        let v: Vec<_> = s.split(':').collect();
        if v.is_empty() {
            return Err(ParseTemplateVarError(s.to_string()));
        }

        let sep = v.get(1).map(|s| s.to_string());
        let quote = v.get(2).map(|s| s.to_string());
        let list_mod = ListMod { sep, quote };

        match v[0].trim() {
            "names" => Ok(Self::Names(list_mod)),
            "aliases" => Ok(Self::Aliases(list_mod)),
            _ => Err(ParseTemplateVarError(s.to_string())),
        }
    }
}

impl std::fmt::Display for ParseTemplateVarError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "unknown error msg template variable `{{{}}}`, expected one of `name`, `input`, `names[:sep[:quote]]`, or `aliases[:sep[:quote]]`",
            &self.0
        )
    }
}

pub fn default_error_msg() -> Vec<TemplateSegment<ErrorMsgVar>> {
    let s = "invalid {name}, expected one of {names}";
    parse_template(s).expect("hard coded error msg template")
}

impl ListMod {
    pub fn get(&self) -> (&str, &str) {
        match (&self.sep, &self.quote) {
            (None, None) => (", ", "\""),
            (None, Some(q)) => (", ", q),
            (Some(s), None) => (s, ""),
            (Some(s), Some(q)) => (s, q),
        }
    }
}

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

    #[test]
    fn error_msg_var_ok() {
        macro_rules! check_ok {
            ($input:literal, $expected:expr) => {
                let var: ErrorMsgVar = $input.parse().unwrap();
                check!(var == $expected);
            };
        }

        check_ok!("name", ErrorMsgVar::Name);
        check_ok!(" name ", ErrorMsgVar::Name);
        check_ok!(" input  ", ErrorMsgVar::Input);
        check_ok!("names", ErrorMsgVar::Names(ListMod::default()));
        check_ok!("aliases", ErrorMsgVar::Aliases(ListMod::default()));
        check_ok!(
            "names: :",
            ErrorMsgVar::Names(ListMod {
                sep: Some(" ".to_string()),
                quote: Some("".to_string())
            })
        );
        check_ok!(
            "names:, ",
            ErrorMsgVar::Names(ListMod {
                sep: Some(", ".to_string()),
                quote: None
            })
        );
        check_ok!(
            "names:|:`",
            ErrorMsgVar::Names(ListMod {
                sep: Some("|".to_string()),
                quote: Some("`".to_string())
            })
        );
        check_ok!(
            "names::",
            ErrorMsgVar::Names(ListMod {
                sep: Some("".to_string()),
                quote: Some("".to_string())
            })
        );
    }
}