scale_info_derive/
attr.rs

1// Copyright 2019-2022 Parity Technologies (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 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
32/// Parsed and validated set of `#[scale_info(...)]` attributes for an item.
33pub 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    /// Extract out `#[scale_info(...)]` attributes from an item.
43    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                // check for duplicates
64                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        // validate type params which do not appear in custom bounds but are not skipped.
110        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    /// Get the `#[scale_info(bounds(...))]` attribute, if present.
140    pub fn bounds(&self) -> Option<&BoundsAttr> {
141        self.bounds.as_ref()
142    }
143
144    /// Get the `#[scale_info(skip_type_params(...))]` attribute, if present.
145    pub fn skip_type_params(&self) -> Option<&SkipTypeParamsAttr> {
146        self.skip_type_params.as_ref()
147    }
148
149    /// Returns the value of `#[scale_info(capture_docs = "..")]`.
150    ///
151    /// Defaults to `CaptureDocsAttr::Default` if the attribute is not present.
152    pub fn capture_docs(&self) -> &CaptureDocsAttr {
153        self.capture_docs
154            .as_ref()
155            .unwrap_or(&CaptureDocsAttr::Default)
156    }
157
158    /// Get the `#[scale_info(crate = path::to::crate)]` attribute, if present.
159    pub fn crate_path(&self) -> Option<&CratePathAttr> {
160        self.crate_path.as_ref()
161    }
162
163    /// Returns an iterator over the `#[scale_info(replace_segment("Hello", "world"))]` attributes.
164    pub fn replace_segments(&self) -> impl Iterator<Item = &ReplaceSegment> {
165        self.replace_segments.iter()
166    }
167}
168
169/// Parsed representation of the `#[scale_info(bounds(...))]` attribute.
170#[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    /// Add the predicates defined in this attribute to the given `where` clause.
187    pub fn extend_where_clause(&self, where_clause: &mut syn::WhereClause) {
188        where_clause.predicates.extend(self.predicates.clone());
189    }
190
191    /// Returns true if the given type parameter appears in the custom bounds attribute.
192    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/// Parsed representation of the `#[scale_info(skip_type_params(...))]` attribute.
208#[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    /// Returns `true` if the given type parameter should be skipped.
225    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/// Parsed representation of the `#[scale_info(capture_docs = "..")]` attribute.
233#[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/// Parsed representation of the `#[scale_info(crate = "..")]` attribute.
259#[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/// Parsed representation of the `#[scale_info(replace_segment("Hello", "world"))]` attribute.
281#[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
311/// Parsed representation of one of the `#[scale_info(..)]` attributes.
312pub 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}