ink_ir/ast/
attr_args.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::Meta;
16use syn::{
17    parse::{
18        Parse,
19        ParseStream,
20    },
21    punctuated::Punctuated,
22    Token,
23};
24
25/// The attribute arguments for the configuration of an ink! smart contract.
26///
27/// For example, the segment `env = ::my::env::Environment`
28/// in `#[ink::contract(env = ::my::env::Environment)]`.
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct AttributeArgs {
31    args: Punctuated<Meta, Token![,]>,
32}
33
34impl quote::ToTokens for AttributeArgs {
35    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
36        self.args.to_tokens(tokens)
37    }
38}
39
40impl IntoIterator for AttributeArgs {
41    type Item = Meta;
42    type IntoIter = syn::punctuated::IntoIter<Meta>;
43
44    fn into_iter(self) -> Self::IntoIter {
45        self.args.into_iter()
46    }
47}
48
49impl Parse for AttributeArgs {
50    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
51        Ok(Self {
52            args: Punctuated::parse_terminated(input)?,
53        })
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::ast::{
61        MetaNameValue,
62        MetaValue,
63    };
64    use quote::quote;
65
66    impl AttributeArgs {
67        /// Creates a new attribute argument list from the given arguments.
68        pub fn new<I>(args: I) -> Self
69        where
70            I: IntoIterator<Item = Meta>,
71        {
72            Self {
73                args: args.into_iter().collect(),
74            }
75        }
76    }
77
78    #[test]
79    fn empty_works() {
80        assert_eq!(
81            syn::parse2::<AttributeArgs>(quote! {}).unwrap(),
82            AttributeArgs::new(vec![])
83        )
84    }
85
86    #[test]
87    fn flag_works() {
88        assert_eq!(
89            syn::parse2::<AttributeArgs>(quote! { flag }).unwrap(),
90            AttributeArgs::new(vec![Meta::Path(syn::parse_quote! { flag })])
91        )
92    }
93
94    #[test]
95    fn literal_bool_value_works() {
96        assert_eq!(
97            syn::parse2::<AttributeArgs>(quote! { name = true }).unwrap(),
98            AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
99                name: syn::parse_quote! { name },
100                eq_token: syn::parse_quote! { = },
101                value: MetaValue::Lit(syn::parse_quote! { true }),
102            })])
103        )
104    }
105
106    #[test]
107    fn literal_str_value_works() {
108        assert_eq!(
109            syn::parse2::<AttributeArgs>(quote! { name = "string literal" }).unwrap(),
110            AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
111                name: syn::parse_quote! { name },
112                eq_token: syn::parse_quote! { = },
113                value: MetaValue::Lit(syn::parse_quote! { "string literal" }),
114            })])
115        )
116    }
117
118    #[test]
119    fn ident_value_works() {
120        assert_eq!(
121            syn::parse2::<AttributeArgs>(quote! { name = MyIdentifier }).unwrap(),
122            AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
123                name: syn::parse_quote! { name },
124                eq_token: syn::parse_quote! { = },
125                value: MetaValue::Path(syn::parse_quote! { MyIdentifier }),
126            })])
127        )
128    }
129
130    #[test]
131    fn root_path_value_works() {
132        assert_eq!(
133            syn::parse2::<AttributeArgs>(quote! { name = ::this::is::my::Path }).unwrap(),
134            AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
135                name: syn::parse_quote! { name },
136                eq_token: syn::parse_quote! { = },
137                value: MetaValue::Path(syn::parse_quote! { ::this::is::my::Path }),
138            })])
139        )
140    }
141
142    #[test]
143    fn relative_path_value_works() {
144        assert_eq!(
145            syn::parse2::<AttributeArgs>(quote! { name = this::is::my::relative::Path })
146                .unwrap(),
147            AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
148                name: syn::parse_quote! { name },
149                eq_token: syn::parse_quote! { = },
150                value: MetaValue::Path(
151                    syn::parse_quote! { this::is::my::relative::Path }
152                ),
153            })])
154        )
155    }
156
157    #[test]
158    fn trailing_comma_works() {
159        let mut expected_args = Punctuated::new();
160        expected_args.push_value(Meta::NameValue(MetaNameValue {
161            name: syn::parse_quote! { name },
162            eq_token: syn::parse_quote! { = },
163            value: MetaValue::Path(syn::parse_quote! { value }),
164        }));
165        expected_args.push_punct(<Token![,]>::default());
166        assert_eq!(
167            syn::parse2::<AttributeArgs>(quote! { name = value, }).unwrap(),
168            AttributeArgs {
169                args: expected_args,
170            }
171        )
172    }
173
174    #[test]
175    fn many_mixed_works() {
176        assert_eq!(
177            syn::parse2::<AttributeArgs>(quote! {
178                flag,
179                name1 = ::root::Path,
180                name2 = false,
181                name3 = "string literal",
182                name4 = 42,
183                name5 = 7.7
184            })
185            .unwrap(),
186            AttributeArgs::new(vec![
187                Meta::Path(syn::parse_quote! { flag }),
188                Meta::NameValue(MetaNameValue {
189                    name: syn::parse_quote! { name1 },
190                    eq_token: syn::parse_quote! { = },
191                    value: MetaValue::Path(syn::parse_quote! { ::root::Path }),
192                }),
193                Meta::NameValue(MetaNameValue {
194                    name: syn::parse_quote! { name2 },
195                    eq_token: syn::parse_quote! { = },
196                    value: MetaValue::Lit(syn::parse_quote! { false }),
197                }),
198                Meta::NameValue(MetaNameValue {
199                    name: syn::parse_quote! { name3 },
200                    eq_token: syn::parse_quote! { = },
201                    value: MetaValue::Lit(syn::parse_quote! { "string literal" }),
202                }),
203                Meta::NameValue(MetaNameValue {
204                    name: syn::parse_quote! { name4 },
205                    eq_token: syn::parse_quote! { = },
206                    value: MetaValue::Lit(syn::parse_quote! { 42 }),
207                }),
208                Meta::NameValue(MetaNameValue {
209                    name: syn::parse_quote! { name5 },
210                    eq_token: syn::parse_quote! { = },
211                    value: MetaValue::Lit(syn::parse_quote! { 7.7 }),
212                }),
213            ])
214        )
215    }
216}