sealed_test_derive 1.1.0

A procmacro attribute to run your test in an isolated environment
Documentation
use syn::{
    bracketed, parenthesized,
    parse::{Parse, ParseStream},
    Expr, LitStr, Token,
};

pub struct SealedTestAttributes {
    pub files: Vec<String>,
    pub env: Vec<EnvVar>,
    pub before: Option<Expr>,
    pub after: Option<Expr>,
}

pub struct EnvVar {
    pub name: String,
    pub value: String,
}

impl Parse for EnvVar {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let content;
        parenthesized!(content in input);
        let name = content.parse::<LitStr>()?.value();
        let _ = content.parse::<Token!(,)>()?;
        let value = content.parse::<LitStr>()?.value();
        Ok(Self { name, value })
    }
}

impl Parse for SealedTestAttributes {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut attributes = SealedTestAttributes {
            files: vec![],
            env: vec![],
            before: None,
            after: None,
        };

        while let Ok(ident) = input.parse::<syn::Ident>() {
            input.parse::<Token!(=)>()?;

            match ident.to_string().as_str() {
                "files" => attributes.files = Self::parse_files(input)?,
                "env" => attributes.env = Self::parse_env(input)?,
                "before" => attributes.before = Some(input.parse::<Expr>()?),
                "after" => attributes.after = Some(input.parse::<Expr>()?),
                other => panic!(
                    "unexpected attribute {}, use 'files', 'env', 'setup' or 'teardown'",
                    other
                ),
            }

            if input.peek(Token!(,)) {
                input.parse::<Token!(,)>()?;
            }
        }

        Ok(attributes)
    }
}

impl SealedTestAttributes {
    fn parse_files(input: ParseStream) -> syn::Result<Vec<String>> {
        let content;
        bracketed!(content in input);

        let mut files = vec![];

        while let Ok(file) = content.parse::<LitStr>() {
            files.push(file.value());
            if !content.is_empty() {
                content.parse::<Token!(,)>()?;
            }
        }

        Ok(files)
    }

    fn parse_env(input: ParseStream) -> syn::Result<Vec<EnvVar>> {
        let content;
        bracketed!(content in input);

        let mut env_vars = vec![];

        while let Ok(env_var) = content.parse::<EnvVar>() {
            env_vars.push(env_var);
            if !content.is_empty() {
                content.parse::<Token!(,)>()?;
            }
        }

        Ok(env_vars)
    }
}