1use crate::{
16 ast,
17 utils::{
18 duplicate_config_err,
19 WhitelistedAttributes,
20 },
21};
22
23#[derive(Debug, Default, PartialEq, Eq)]
25pub struct Config {
26 env: Option<Environment>,
32 whitelisted_attributes: WhitelistedAttributes,
34}
35
36impl TryFrom<ast::AttributeArgs> for Config {
37 type Error = syn::Error;
38
39 fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
40 let mut env: Option<(Environment, ast::MetaNameValue)> = None;
41 let mut whitelisted_attributes = WhitelistedAttributes::default();
42
43 for arg in args.into_iter() {
44 if arg.name().is_ident("env") {
45 if let Some((_, ast)) = env {
46 return Err(duplicate_config_err(ast, arg, "env", "contract"));
47 }
48 let env_info = arg
49 .name_value()
50 .zip(arg.value().and_then(ast::MetaValue::as_path));
51 if let Some((name_value, path)) = env_info {
52 env = Some((Environment { path: path.clone() }, name_value.clone()))
53 } else {
54 return Err(format_err_spanned!(
55 arg,
56 "expected a path value for `env` ink! configuration argument",
57 ));
58 }
59 } else if arg.name().is_ident("keep_attr") {
60 if let Some(name_value) = arg.name_value() {
61 whitelisted_attributes.parse_arg_value(name_value)?;
62 } else {
63 return Err(format_err_spanned!(
64 arg,
65 "expected a string literal value for `keep_attr` ink! configuration argument",
66 ));
67 }
68 } else {
69 return Err(format_err_spanned!(
70 arg,
71 "encountered unknown or unsupported ink! configuration argument",
72 ));
73 }
74 }
75 Ok(Config {
76 env: env.map(|(value, _)| value),
77 whitelisted_attributes,
78 })
79 }
80}
81
82impl Config {
83 pub fn env(&self) -> syn::Path {
87 self.env
88 .as_ref()
89 .map(|env| &env.path)
90 .cloned()
91 .unwrap_or(Environment::default().path)
92 }
93
94 pub fn whitelisted_attributes(&self) -> &WhitelistedAttributes {
96 &self.whitelisted_attributes
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
102pub struct Environment {
103 pub path: syn::Path,
105}
106
107impl Default for Environment {
108 fn default() -> Self {
109 Self {
110 path: syn::parse_quote! { ::ink::env::DefaultEnvironment },
111 }
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
117pub enum Abi {
118 #[default]
120 Ink,
121 Solidity,
123 All,
125}
126
127impl Abi {
128 pub fn is_ink(&self) -> bool {
129 matches!(self, Self::Ink | Self::All)
130 }
131
132 pub fn is_solidity(&self) -> bool {
133 matches!(self, Self::Solidity | Self::All)
134 }
135
136 pub fn is_all(&self) -> bool {
137 matches!(self, Self::All)
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 fn assert_try_from(
148 input: ast::AttributeArgs,
149 expected: Result<Config, &'static str>,
150 ) {
151 assert_eq!(
152 <Config as TryFrom<ast::AttributeArgs>>::try_from(input)
153 .map_err(|err| err.to_string()),
154 expected.map_err(ToString::to_string),
155 );
156 }
157
158 #[test]
159 fn empty_config_works() {
160 assert_try_from(syn::parse_quote! {}, Ok(Config::default()))
161 }
162
163 #[test]
164 fn env_works() {
165 assert_try_from(
166 syn::parse_quote! {
167 env = ::my::env::Types
168 },
169 Ok(Config {
170 env: Some(Environment {
171 path: syn::parse_quote! { ::my::env::Types },
172 }),
173 whitelisted_attributes: Default::default(),
174 }),
175 )
176 }
177
178 #[test]
179 fn env_invalid_value_fails() {
180 assert_try_from(
181 syn::parse_quote! { env = "invalid" },
182 Err("expected a path value for `env` ink! configuration argument"),
183 );
184 }
185
186 #[test]
187 fn env_missing_value_fails() {
188 assert_try_from(
189 syn::parse_quote! { env },
190 Err("expected a path value for `env` ink! configuration argument"),
191 );
192 }
193
194 #[test]
195 fn unknown_arg_fails() {
196 assert_try_from(
197 syn::parse_quote! { unknown = argument },
198 Err("encountered unknown or unsupported ink! configuration argument"),
199 );
200 }
201
202 #[test]
203 fn duplicate_args_fails() {
204 assert_try_from(
205 syn::parse_quote! {
206 env = ::my::env::Types,
207 env = ::my::other::env::Types,
208 },
209 Err("encountered duplicate ink! contract `env` configuration argument"),
210 );
211 }
212
213 #[test]
214 fn keep_attr_works() {
215 let mut attrs = WhitelistedAttributes::default();
216 attrs.0.insert("foo".to_string(), ());
217 attrs.0.insert("bar".to_string(), ());
218 assert_try_from(
219 syn::parse_quote! {
220 keep_attr = "foo, bar"
221 },
222 Ok(Config {
223 env: None,
224 whitelisted_attributes: attrs,
225 }),
226 )
227 }
228
229 #[test]
230 fn keep_attr_invalid_value_fails() {
231 assert_try_from(
232 syn::parse_quote! { keep_attr = 1u16 },
233 Err("expected a string with attributes separated by `,`"),
234 );
235 }
236
237 #[test]
238 fn keep_attr_missing_value_fails() {
239 assert_try_from(
240 syn::parse_quote! { keep_attr },
241 Err("expected a string literal value for `keep_attr` ink! configuration argument"),
242 );
243 }
244}