scale_info_derive/
attr.rs1use syn::{
16 parse::{Parse, ParseBuffer},
17 punctuated::Punctuated,
18 spanned::Spanned,
19 LitStr, Token,
20};
21
22const SCALE_INFO: &str = "scale_info";
23
24mod keywords {
25 syn::custom_keyword!(scale_info);
26 syn::custom_keyword!(bounds);
27 syn::custom_keyword!(skip_type_params);
28 syn::custom_keyword!(capture_docs);
29 syn::custom_keyword!(replace_segment);
30}
31
32pub struct Attributes {
34 bounds: Option<BoundsAttr>,
35 skip_type_params: Option<SkipTypeParamsAttr>,
36 capture_docs: Option<CaptureDocsAttr>,
37 crate_path: Option<CratePathAttr>,
38 replace_segments: Vec<ReplaceSegment>,
39}
40
41impl Attributes {
42 pub fn from_ast(item: &syn::DeriveInput) -> syn::Result<Self> {
44 let mut bounds = None;
45 let mut skip_type_params = None;
46 let mut capture_docs = None;
47 let mut crate_path = None;
48 let mut replace_segments = Vec::new();
49
50 let attributes_parser = |input: &ParseBuffer| {
51 let attrs: Punctuated<ScaleInfoAttr, Token![,]> =
52 input.parse_terminated(ScaleInfoAttr::parse)?;
53 Ok(attrs)
54 };
55
56 for attr in &item.attrs {
57 if !attr.path.is_ident(SCALE_INFO) {
58 continue;
59 }
60 let scale_info_attrs = attr.parse_args_with(attributes_parser)?;
61
62 for scale_info_attr in scale_info_attrs {
63 match scale_info_attr {
65 ScaleInfoAttr::Bounds(parsed_bounds) => {
66 if bounds.is_some() {
67 return Err(syn::Error::new(
68 attr.span(),
69 "Duplicate `bounds` attributes",
70 ));
71 }
72 bounds = Some(parsed_bounds);
73 }
74 ScaleInfoAttr::SkipTypeParams(parsed_skip_type_params) => {
75 if skip_type_params.is_some() {
76 return Err(syn::Error::new(
77 attr.span(),
78 "Duplicate `skip_type_params` attributes",
79 ));
80 }
81 skip_type_params = Some(parsed_skip_type_params);
82 }
83 ScaleInfoAttr::CaptureDocs(parsed_capture_docs) => {
84 if capture_docs.is_some() {
85 return Err(syn::Error::new(
86 attr.span(),
87 "Duplicate `capture_docs` attributes",
88 ));
89 }
90 capture_docs = Some(parsed_capture_docs);
91 }
92 ScaleInfoAttr::CratePath(parsed_crate_path) => {
93 if crate_path.is_some() {
94 return Err(syn::Error::new(
95 attr.span(),
96 "Duplicate `crate` attributes",
97 ));
98 }
99
100 crate_path = Some(parsed_crate_path);
101 }
102 ScaleInfoAttr::ReplaceSegment(replace_segment) => {
103 replace_segments.push(replace_segment);
104 }
105 }
106 }
107 }
108
109 if let Some(ref bounds) = bounds {
111 for type_param in item.generics.type_params() {
112 if !bounds.contains_type_param(type_param) {
113 let type_param_skipped = skip_type_params
114 .as_ref()
115 .map(|skip| skip.skip(type_param))
116 .unwrap_or(false);
117 if !type_param_skipped {
118 let msg = format!(
119 "Type parameter requires a `TypeInfo` bound, so either: \n \
120 - add it to `#[scale_info(bounds({}: TypeInfo))]` \n \
121 - skip it with `#[scale_info(skip_type_params({}))]`",
122 type_param.ident, type_param.ident
123 );
124 return Err(syn::Error::new(type_param.span(), msg));
125 }
126 }
127 }
128 }
129
130 Ok(Self {
131 bounds,
132 skip_type_params,
133 capture_docs,
134 crate_path,
135 replace_segments,
136 })
137 }
138
139 pub fn bounds(&self) -> Option<&BoundsAttr> {
141 self.bounds.as_ref()
142 }
143
144 pub fn skip_type_params(&self) -> Option<&SkipTypeParamsAttr> {
146 self.skip_type_params.as_ref()
147 }
148
149 pub fn capture_docs(&self) -> &CaptureDocsAttr {
153 self.capture_docs
154 .as_ref()
155 .unwrap_or(&CaptureDocsAttr::Default)
156 }
157
158 pub fn crate_path(&self) -> Option<&CratePathAttr> {
160 self.crate_path.as_ref()
161 }
162
163 pub fn replace_segments(&self) -> impl Iterator<Item = &ReplaceSegment> {
165 self.replace_segments.iter()
166 }
167}
168
169#[derive(Clone)]
171pub struct BoundsAttr {
172 predicates: Punctuated<syn::WherePredicate, Token![,]>,
173}
174
175impl Parse for BoundsAttr {
176 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
177 input.parse::<keywords::bounds>()?;
178 let content;
179 syn::parenthesized!(content in input);
180 let predicates = content.parse_terminated(syn::WherePredicate::parse)?;
181 Ok(Self { predicates })
182 }
183}
184
185impl BoundsAttr {
186 pub fn extend_where_clause(&self, where_clause: &mut syn::WhereClause) {
188 where_clause.predicates.extend(self.predicates.clone());
189 }
190
191 pub fn contains_type_param(&self, type_param: &syn::TypeParam) -> bool {
193 self.predicates.iter().any(|p| {
194 if let syn::WherePredicate::Type(ty) = p {
195 if let syn::Type::Path(ref path) = ty.bounded_ty {
196 path.path.get_ident() == Some(&type_param.ident)
197 } else {
198 false
199 }
200 } else {
201 false
202 }
203 })
204 }
205}
206
207#[derive(Clone)]
209pub struct SkipTypeParamsAttr {
210 type_params: Punctuated<syn::TypeParam, Token![,]>,
211}
212
213impl Parse for SkipTypeParamsAttr {
214 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
215 input.parse::<keywords::skip_type_params>()?;
216 let content;
217 syn::parenthesized!(content in input);
218 let type_params = content.parse_terminated(syn::TypeParam::parse)?;
219 Ok(Self { type_params })
220 }
221}
222
223impl SkipTypeParamsAttr {
224 pub fn skip(&self, type_param: &syn::TypeParam) -> bool {
226 self.type_params
227 .iter()
228 .any(|tp| tp.ident == type_param.ident)
229 }
230}
231
232#[derive(Clone)]
234pub enum CaptureDocsAttr {
235 Default,
236 Always,
237 Never,
238}
239
240impl Parse for CaptureDocsAttr {
241 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
242 input.parse::<keywords::capture_docs>()?;
243 input.parse::<syn::Token![=]>()?;
244 let capture_docs_lit = input.parse::<syn::LitStr>()?;
245
246 match capture_docs_lit.value().to_lowercase().as_str() {
247 "default" => Ok(Self::Default),
248 "always" => Ok(Self::Always),
249 "never" => Ok(Self::Never),
250 _ => Err(syn::Error::new_spanned(
251 capture_docs_lit,
252 r#"Invalid capture_docs value. Expected one of: "default", "always", "never" "#,
253 )),
254 }
255 }
256}
257
258#[derive(Clone)]
260pub struct CratePathAttr {
261 path: syn::Path,
262}
263
264impl CratePathAttr {
265 pub fn path(&self) -> &syn::Path {
266 &self.path
267 }
268}
269
270impl Parse for CratePathAttr {
271 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
272 input.parse::<Token![crate]>()?;
273 input.parse::<Token![=]>()?;
274 let path = input.parse::<syn::Path>()?;
275
276 Ok(Self { path })
277 }
278}
279
280#[derive(Clone)]
282pub struct ReplaceSegment {
283 search: LitStr,
284 replace: LitStr,
285}
286
287impl ReplaceSegment {
288 pub fn search(&self) -> &LitStr {
289 &self.search
290 }
291
292 pub fn replace(&self) -> &LitStr {
293 &self.replace
294 }
295}
296
297impl Parse for ReplaceSegment {
298 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
299 input.parse::<keywords::replace_segment>()?;
300 let content;
301 syn::parenthesized!(content in input);
302
303 let search = content.parse::<LitStr>()?;
304 content.parse::<Token![,]>()?;
305 let replace = content.parse::<LitStr>()?;
306
307 Ok(Self { search, replace })
308 }
309}
310
311pub enum ScaleInfoAttr {
313 Bounds(BoundsAttr),
314 SkipTypeParams(SkipTypeParamsAttr),
315 CaptureDocs(CaptureDocsAttr),
316 CratePath(CratePathAttr),
317 ReplaceSegment(ReplaceSegment),
318}
319
320impl Parse for ScaleInfoAttr {
321 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
322 let lookahead = input.lookahead1();
323 if lookahead.peek(keywords::bounds) {
324 Ok(Self::Bounds(input.parse()?))
325 } else if lookahead.peek(keywords::skip_type_params) {
326 Ok(Self::SkipTypeParams(input.parse()?))
327 } else if lookahead.peek(keywords::capture_docs) {
328 Ok(Self::CaptureDocs(input.parse()?))
329 } else if lookahead.peek(Token![crate]) {
330 Ok(Self::CratePath(input.parse()?))
331 } else if lookahead.peek(keywords::replace_segment) {
332 Ok(Self::ReplaceSegment(input.parse()?))
333 } else {
334 Err(lookahead.error())
335 }
336 }
337}