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