use crate::{
ast,
utils::{
duplicate_config_err,
WhitelistedAttributes,
},
};
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Config {
env: Option<Environment>,
whitelisted_attributes: WhitelistedAttributes,
}
impl TryFrom<ast::AttributeArgs> for Config {
type Error = syn::Error;
fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
let mut env: Option<(Environment, ast::MetaNameValue)> = None;
let mut whitelisted_attributes = WhitelistedAttributes::default();
for arg in args.into_iter() {
if arg.name().is_ident("env") {
if let Some((_, ast)) = env {
return Err(duplicate_config_err(ast, arg, "env", "contract"));
}
let env_info = arg
.name_value()
.zip(arg.value().and_then(ast::MetaValue::as_path));
if let Some((name_value, path)) = env_info {
env = Some((Environment { path: path.clone() }, name_value.clone()))
} else {
return Err(format_err_spanned!(
arg,
"expected a path value for `env` ink! configuration argument",
));
}
} else if arg.name().is_ident("keep_attr") {
if let Some(name_value) = arg.name_value() {
whitelisted_attributes.parse_arg_value(name_value)?;
} else {
return Err(format_err_spanned!(
arg,
"expected a string literal value for `keep_attr` ink! configuration argument",
));
}
} else {
return Err(format_err_spanned!(
arg,
"encountered unknown or unsupported ink! configuration argument",
));
}
}
Ok(Config {
env: env.map(|(value, _)| value),
whitelisted_attributes,
})
}
}
impl Config {
pub fn env(&self) -> syn::Path {
self.env
.as_ref()
.map(|env| &env.path)
.cloned()
.unwrap_or(Environment::default().path)
}
pub fn whitelisted_attributes(&self) -> &WhitelistedAttributes {
&self.whitelisted_attributes
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Environment {
pub path: syn::Path,
}
impl Default for Environment {
fn default() -> Self {
Self {
path: syn::parse_quote! { ::ink::env::DefaultEnvironment },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_try_from(
input: ast::AttributeArgs,
expected: Result<Config, &'static str>,
) {
assert_eq!(
<Config as TryFrom<ast::AttributeArgs>>::try_from(input)
.map_err(|err| err.to_string()),
expected.map_err(ToString::to_string),
);
}
#[test]
fn empty_config_works() {
assert_try_from(syn::parse_quote! {}, Ok(Config::default()))
}
#[test]
fn env_works() {
assert_try_from(
syn::parse_quote! {
env = ::my::env::Types
},
Ok(Config {
env: Some(Environment {
path: syn::parse_quote! { ::my::env::Types },
}),
whitelisted_attributes: Default::default(),
}),
)
}
#[test]
fn env_invalid_value_fails() {
assert_try_from(
syn::parse_quote! { env = "invalid" },
Err("expected a path value for `env` ink! configuration argument"),
);
}
#[test]
fn env_missing_value_fails() {
assert_try_from(
syn::parse_quote! { env },
Err("expected a path value for `env` ink! configuration argument"),
);
}
#[test]
fn unknown_arg_fails() {
assert_try_from(
syn::parse_quote! { unknown = argument },
Err("encountered unknown or unsupported ink! configuration argument"),
);
}
#[test]
fn duplicate_args_fails() {
assert_try_from(
syn::parse_quote! {
env = ::my::env::Types,
env = ::my::other::env::Types,
},
Err("encountered duplicate ink! contract `env` configuration argument"),
);
}
#[test]
fn keep_attr_works() {
let mut attrs = WhitelistedAttributes::default();
attrs.0.insert("foo".to_string(), ());
attrs.0.insert("bar".to_string(), ());
assert_try_from(
syn::parse_quote! {
keep_attr = "foo, bar"
},
Ok(Config {
env: None,
whitelisted_attributes: attrs,
}),
)
}
#[test]
fn keep_attr_invalid_value_fails() {
assert_try_from(
syn::parse_quote! { keep_attr = 1u16 },
Err("expected a string with attributes separated by `,`"),
);
}
#[test]
fn keep_attr_missing_value_fails() {
assert_try_from(
syn::parse_quote! { keep_attr },
Err("expected a string literal value for `keep_attr` ink! configuration argument"),
);
}
}