1use super::{
16 Selector,
17 SelectorAbi,
18};
19use crate::{
20 ast::{
21 self,
22 MetaNameValue,
23 MetaValue,
24 },
25 error::ExtError as _,
26};
27use proc_macro2::Span;
28use std::collections::HashMap;
29use syn::spanned::Spanned;
30
31pub fn ensure_pub_visibility(
38 name: &str,
39 parent_span: Span,
40 vis: &syn::Visibility,
41) -> Result<(), syn::Error> {
42 let bad_visibility = match vis {
43 syn::Visibility::Inherited => Some(parent_span),
44 syn::Visibility::Restricted(vis_restricted) => Some(vis_restricted.span()),
45 syn::Visibility::Public(_) => None,
46 };
47 if let Some(bad_visibility) = bad_visibility {
48 return Err(format_err!(
49 bad_visibility,
50 "non `pub` ink! {} are not supported",
51 name
52 ))
53 }
54 Ok(())
55}
56
57pub fn local_message_id(ident: &str) -> u32 {
64 let input = ident.as_bytes();
65 let selector = Selector::compute(input, SelectorAbi::Ink);
66 selector.into_be_u32()
67}
68
69#[derive(Debug, PartialEq, Eq)]
72pub struct WhitelistedAttributes(pub HashMap<String, ()>);
73
74impl Default for WhitelistedAttributes {
75 fn default() -> Self {
76 Self(HashMap::from([
77 ("cfg".to_string(), ()),
79 ("cfg_attr".to_string(), ()),
80 ("allow".to_string(), ()),
82 ("warn".to_string(), ()),
83 ("deny".to_string(), ()),
84 ("forbid".to_string(), ()),
85 ("deprecated".to_string(), ()),
86 ("must_use".to_string(), ()),
87 ("doc".to_string(), ()),
89 ("rustfmt".to_string(), ()),
91 ]))
92 }
93}
94
95impl WhitelistedAttributes {
96 pub fn parse_arg_value(&mut self, arg: &MetaNameValue) -> Result<(), syn::Error> {
100 if let ast::MetaValue::Lit(syn::Lit::Str(attributes)) = &arg.value {
101 attributes.value().split(',').for_each(|attribute| {
102 self.0.insert(attribute.trim().to_string(), ());
103 });
104 Ok(())
105 } else {
106 Err(format_err_spanned!(
107 arg,
108 "expected a string with attributes separated by `,`",
109 ))
110 }
111 }
112
113 pub fn filter_attr(&self, attrs: Vec<syn::Attribute>) -> Vec<syn::Attribute> {
116 attrs
117 .into_iter()
118 .filter(|attr| {
119 if let Some(ident) = attr.path().get_ident() {
120 self.0.contains_key(&ident.to_string())
121 } else {
122 false
123 }
124 })
125 .collect()
126 }
127}
128
129pub fn duplicate_config_err<F, S>(
131 first: F,
132 second: S,
133 name: &str,
134 ink_attr: &str,
135) -> syn::Error
136where
137 F: Spanned,
138 S: Spanned,
139{
140 format_err!(
141 second.span(),
142 "encountered duplicate ink! {} `{}` configuration argument",
143 ink_attr,
144 name,
145 )
146 .into_combine(format_err!(
147 first.span(),
148 "first `{}` configuration argument here",
149 name
150 ))
151}
152
153pub fn find_storage_key_salt(input: &syn::DeriveInput) -> Option<syn::TypeParam> {
157 input.generics.params.iter().find_map(|param| {
158 if let syn::GenericParam::Type(type_param) = param
159 && let Some(syn::TypeParamBound::Trait(trait_bound)) =
160 type_param.bounds.first()
161 {
162 let segments = &trait_bound.path.segments;
163 if let Some(last) = segments.last()
164 && last.ident == "StorageKey"
165 {
166 return Some(type_param.clone())
167 }
168 }
169 None
170 })
171}
172
173pub fn extract_cfg_attributes(
175 attrs: &[syn::Attribute],
176 span: Span,
177) -> Vec<proc_macro2::TokenStream> {
178 attrs
179 .iter()
180 .filter(|a| a.path().is_ident(super::CFG_IDENT))
181 .map(|a| quote::quote_spanned!(span=> #a ))
182 .collect()
183}
184
185pub fn extract_cfg_syn_attributes(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
187 attrs
188 .iter()
189 .filter(|a| a.path().is_ident(super::CFG_IDENT))
190 .cloned()
191 .collect()
192}
193
194pub fn extract_name_override(value: &MetaValue, span: Span) -> syn::Result<syn::LitStr> {
202 if let Some(lit_str) = value.as_lit_string() {
203 let name = lit_str.value();
204 if !name
205 .chars()
206 .next()
207 .map(|c| c.is_alphabetic() || c == '$' || c == '_')
208 .unwrap_or(false)
209 {
210 return Err(format_err_spanned!(
211 lit_str,
212 "`name` attribute argument value must begin with an \
213 alphabetic character, underscore or dollar sign",
214 ));
215 }
216
217 if !name
218 .chars()
219 .all(|c| c.is_alphanumeric() || c == '$' || c == '_')
220 {
221 return Err(format_err_spanned!(
222 lit_str,
223 "`name` attribute argument value can only contain \
224 alphanumeric characters, underscores and dollar signs",
225 ));
226 }
227
228 Ok(lit_str.clone())
229 } else {
230 Err(syn::Error::new(
231 span,
232 "expected a string literal value for `name` \
233 attribute argument",
234 ))
235 }
236}