scale_info_derive/
utils.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
15//! Utility methods to work with `SCALE` attributes relevant for the `TypeInfo` derive..
16//!
17//! NOTE: The code here is copied verbatim from `parity-scale-codec-derive`.
18
19use proc_macro2::TokenStream;
20use quote::quote;
21use syn::{parse::Parse, spanned::Spanned, AttrStyle, Attribute, Lit, Meta, NestedMeta, Variant};
22
23/// Look for a `#[codec(index = $int)]` attribute on a variant. If no attribute
24/// is found, fall back to the discriminant or just the variant index.
25pub fn variant_index(v: &Variant, i: usize) -> TokenStream {
26    // first look for an `index` attribute…
27    let index = maybe_index(v);
28    // …then fallback to discriminant or just index
29    index.map(|i| quote! { #i }).unwrap_or_else(|| {
30        v.discriminant
31            .as_ref()
32            .map(|(_, ref expr)| quote! { #expr })
33            .unwrap_or_else(|| quote! { #i })
34    })
35}
36
37/// Look for a `#[codec(index = $int)]` outer attribute on a variant.
38/// If found, it is expected to be a parseable as a `u8` (panics otherwise).
39pub fn maybe_index(variant: &Variant) -> Option<u8> {
40    let outer_attrs = variant
41        .attrs
42        .iter()
43        .filter(|attr| attr.style == AttrStyle::Outer);
44
45    codec_meta_item(outer_attrs, |meta| {
46        if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta {
47            if nv.path.is_ident("index") {
48                if let Lit::Int(ref v) = nv.lit {
49                    let byte = v
50                        .base10_parse::<u8>()
51                        .expect("Internal error. `#[codec(index = …)]` attribute syntax must be checked in `parity-scale-codec`. This is a bug.");
52                    return Some(byte);
53                }
54            }
55        }
56
57        None
58    })
59}
60
61/// Look for a `#[codec(compact)]` outer attribute on the given `Field`.
62pub fn is_compact(field: &syn::Field) -> bool {
63    let outer_attrs = field
64        .attrs
65        .iter()
66        .filter(|attr| attr.style == AttrStyle::Outer);
67    codec_meta_item(outer_attrs, |meta| {
68        if let NestedMeta::Meta(Meta::Path(ref path)) = meta {
69            if path.is_ident("compact") {
70                return Some(());
71            }
72        }
73
74        None
75    })
76    .is_some()
77}
78
79/// Look for a `#[codec(skip)]` in the given attributes.
80pub fn should_skip(attrs: &[Attribute]) -> bool {
81    codec_meta_item(attrs.iter(), |meta| {
82        if let NestedMeta::Meta(Meta::Path(ref path)) = meta {
83            if path.is_ident("skip") {
84                return Some(path.span());
85            }
86        }
87
88        None
89    })
90    .is_some()
91}
92
93fn codec_meta_item<'a, F, R, I, M>(itr: I, pred: F) -> Option<R>
94where
95    F: FnMut(M) -> Option<R> + Clone,
96    I: Iterator<Item = &'a Attribute>,
97    M: Parse,
98{
99    find_meta_item("codec", itr, pred)
100}
101
102fn find_meta_item<'a, F, R, I, M>(kind: &str, mut itr: I, mut pred: F) -> Option<R>
103where
104    F: FnMut(M) -> Option<R> + Clone,
105    I: Iterator<Item = &'a Attribute>,
106    M: Parse,
107{
108    itr.find_map(|attr| {
109        attr.path
110            .is_ident(kind)
111            .then(|| pred(attr.parse_args().ok()?))
112            .flatten()
113    })
114}