scale-info-derive-2.3.1/.cargo_vcs_info.json0000644000000001440000000000100143320ustar { "git": { "sha1": "31f37d05610214ce4091eb0ecf3d747f9ac201ac" }, "path_in_vcs": "derive" }scale-info-derive-2.3.1/Cargo.toml0000644000000023430000000000100123330ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56.1" name = "scale-info-derive" version = "2.3.1" authors = [ "Parity Technologies ", "Centrality Developers ", ] description = "Derive type info for SCALE encodable types" homepage = "https://www.parity.io/" documentation = "https://docs.rs/scale-info" categories = [ "no-std", "encoding", ] license = "Apache-2.0" repository = "https://github.com/paritytech/scale-info" [lib] proc-macro = true [dependencies.proc-macro-crate] version = "1" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0" features = [ "derive", "visit", "visit-mut", "extra-traits", ] [features] default = ["docs"] docs = [] scale-info-derive-2.3.1/Cargo.toml.orig000064400000000000000000000013301046102023000160070ustar 00000000000000[package] name = "scale-info-derive" version = "2.3.1" authors = [ "Parity Technologies ", "Centrality Developers ", ] edition = "2021" rust-version = "1.56.1" license = "Apache-2.0" repository = "https://github.com/paritytech/scale-info" documentation = "https://docs.rs/scale-info" homepage = "https://www.parity.io/" description = "Derive type info for SCALE encodable types" categories = ["no-std", "encoding"] [lib] proc-macro = true [dependencies] quote = "1.0" syn = { version = "1.0", features = ["derive", "visit", "visit-mut", "extra-traits"] } proc-macro2 = "1.0" proc-macro-crate = "1" [features] default = ["docs"] # Include code docs in type metadata. docs = [] scale-info-derive-2.3.1/src/attr.rs000064400000000000000000000240631046102023000152370ustar 00000000000000// Copyright 2019-2022 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use syn::{ parse::{ Parse, ParseBuffer, }, punctuated::Punctuated, spanned::Spanned, Token, }; const SCALE_INFO: &str = "scale_info"; mod keywords { syn::custom_keyword!(scale_info); syn::custom_keyword!(bounds); syn::custom_keyword!(skip_type_params); syn::custom_keyword!(capture_docs); } /// Parsed and validated set of `#[scale_info(...)]` attributes for an item. pub struct Attributes { bounds: Option, skip_type_params: Option, capture_docs: Option, crate_path: Option, } impl Attributes { /// Extract out `#[scale_info(...)]` attributes from an item. pub fn from_ast(item: &syn::DeriveInput) -> syn::Result { let mut bounds = None; let mut skip_type_params = None; let mut capture_docs = None; let mut crate_path = None; let attributes_parser = |input: &ParseBuffer| { let attrs: Punctuated = input.parse_terminated(ScaleInfoAttr::parse)?; Ok(attrs) }; for attr in &item.attrs { if !attr.path.is_ident(SCALE_INFO) { continue } let scale_info_attrs = attr.parse_args_with(attributes_parser)?; for scale_info_attr in scale_info_attrs { // check for duplicates match scale_info_attr { ScaleInfoAttr::Bounds(parsed_bounds) => { if bounds.is_some() { return Err(syn::Error::new( attr.span(), "Duplicate `bounds` attributes", )) } bounds = Some(parsed_bounds); } ScaleInfoAttr::SkipTypeParams(parsed_skip_type_params) => { if skip_type_params.is_some() { return Err(syn::Error::new( attr.span(), "Duplicate `skip_type_params` attributes", )) } skip_type_params = Some(parsed_skip_type_params); } ScaleInfoAttr::CaptureDocs(parsed_capture_docs) => { if capture_docs.is_some() { return Err(syn::Error::new( attr.span(), "Duplicate `capture_docs` attributes", )) } capture_docs = Some(parsed_capture_docs); } ScaleInfoAttr::CratePath(parsed_crate_path) => { if crate_path.is_some() { return Err(syn::Error::new( attr.span(), "Duplicate `crate` attributes", )) } crate_path = Some(parsed_crate_path); } } } } // validate type params which do not appear in custom bounds but are not skipped. if let Some(ref bounds) = bounds { for type_param in item.generics.type_params() { if !bounds.contains_type_param(type_param) { let type_param_skipped = skip_type_params .as_ref() .map(|skip| skip.skip(type_param)) .unwrap_or(false); if !type_param_skipped { let msg = format!( "Type parameter requires a `TypeInfo` bound, so either: \n \ - add it to `#[scale_info(bounds({}: TypeInfo))]` \n \ - skip it with `#[scale_info(skip_type_params({}))]`", type_param.ident, type_param.ident ); return Err(syn::Error::new(type_param.span(), msg)) } } } } Ok(Self { bounds, skip_type_params, capture_docs, crate_path, }) } /// Get the `#[scale_info(bounds(...))]` attribute, if present. pub fn bounds(&self) -> Option<&BoundsAttr> { self.bounds.as_ref() } /// Get the `#[scale_info(skip_type_params(...))]` attribute, if present. pub fn skip_type_params(&self) -> Option<&SkipTypeParamsAttr> { self.skip_type_params.as_ref() } /// Returns the value of `#[scale_info(capture_docs = "..")]`. /// /// Defaults to `CaptureDocsAttr::Default` if the attribute is not present. pub fn capture_docs(&self) -> &CaptureDocsAttr { self.capture_docs .as_ref() .unwrap_or(&CaptureDocsAttr::Default) } /// Get the `#[scale_info(crate = path::to::crate)]` attribute, if present. pub fn crate_path(&self) -> Option<&CratePathAttr> { self.crate_path.as_ref() } } /// Parsed representation of the `#[scale_info(bounds(...))]` attribute. #[derive(Clone)] pub struct BoundsAttr { predicates: Punctuated, } impl Parse for BoundsAttr { fn parse(input: &ParseBuffer) -> syn::Result { input.parse::()?; let content; syn::parenthesized!(content in input); let predicates = content.parse_terminated(syn::WherePredicate::parse)?; Ok(Self { predicates }) } } impl BoundsAttr { /// Add the predicates defined in this attribute to the given `where` clause. pub fn extend_where_clause(&self, where_clause: &mut syn::WhereClause) { where_clause.predicates.extend(self.predicates.clone()); } /// Returns true if the given type parameter appears in the custom bounds attribute. pub fn contains_type_param(&self, type_param: &syn::TypeParam) -> bool { self.predicates.iter().any(|p| { if let syn::WherePredicate::Type(ty) = p { if let syn::Type::Path(ref path) = ty.bounded_ty { path.path.get_ident() == Some(&type_param.ident) } else { false } } else { false } }) } } /// Parsed representation of the `#[scale_info(skip_type_params(...))]` attribute. #[derive(Clone)] pub struct SkipTypeParamsAttr { type_params: Punctuated, } impl Parse for SkipTypeParamsAttr { fn parse(input: &ParseBuffer) -> syn::Result { input.parse::()?; let content; syn::parenthesized!(content in input); let type_params = content.parse_terminated(syn::TypeParam::parse)?; Ok(Self { type_params }) } } impl SkipTypeParamsAttr { /// Returns `true` if the given type parameter should be skipped. pub fn skip(&self, type_param: &syn::TypeParam) -> bool { self.type_params .iter() .any(|tp| tp.ident == type_param.ident) } } /// Parsed representation of the `#[scale_info(capture_docs = "..")]` attribute. #[derive(Clone)] pub enum CaptureDocsAttr { Default, Always, Never, } impl Parse for CaptureDocsAttr { fn parse(input: &ParseBuffer) -> syn::Result { input.parse::()?; input.parse::()?; let capture_docs_lit = input.parse::()?; match capture_docs_lit.value().to_lowercase().as_str() { "default" => Ok(Self::Default), "always" => Ok(Self::Always), "never" => Ok(Self::Never), _ => { Err(syn::Error::new_spanned( capture_docs_lit, r#"Invalid capture_docs value. Expected one of: "default", "always", "never" "#, )) } } } } /// Parsed representation of the `#[scale_info(crate = "..")]` attribute. #[derive(Clone)] pub struct CratePathAttr { path: syn::Path, } impl CratePathAttr { pub fn path(&self) -> &syn::Path { &self.path } } impl Parse for CratePathAttr { fn parse(input: &ParseBuffer) -> syn::Result { input.parse::()?; input.parse::()?; let path = input.parse::()?; Ok(Self { path }) } } /// Parsed representation of one of the `#[scale_info(..)]` attributes. pub enum ScaleInfoAttr { Bounds(BoundsAttr), SkipTypeParams(SkipTypeParamsAttr), CaptureDocs(CaptureDocsAttr), CratePath(CratePathAttr), } impl Parse for ScaleInfoAttr { fn parse(input: &ParseBuffer) -> syn::Result { let lookahead = input.lookahead1(); if lookahead.peek(keywords::bounds) { let bounds = input.parse()?; Ok(Self::Bounds(bounds)) } else if lookahead.peek(keywords::skip_type_params) { let skip_type_params = input.parse()?; Ok(Self::SkipTypeParams(skip_type_params)) } else if lookahead.peek(keywords::capture_docs) { let capture_docs = input.parse()?; Ok(Self::CaptureDocs(capture_docs)) } else if lookahead.peek(Token![crate]) { let crate_path = input.parse()?; Ok(Self::CratePath(crate_path)) } else { Err(lookahead.error()) } } } scale-info-derive-2.3.1/src/lib.rs000064400000000000000000000263401046102023000150330ustar 00000000000000// Copyright 2019-2022 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. extern crate alloc; extern crate proc_macro; mod attr; mod trait_bounds; mod utils; use self::attr::{ Attributes, CaptureDocsAttr, CratePathAttr, }; use proc_macro::TokenStream; use proc_macro2::{ Span, TokenStream as TokenStream2, }; use quote::quote; use syn::{ parse::{ Error, Result, }, parse_quote, punctuated::Punctuated, token::Comma, visit_mut::VisitMut, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Ident, Lifetime, }; #[proc_macro_derive(TypeInfo, attributes(scale_info, codec))] pub fn type_info(input: TokenStream) -> TokenStream { match generate(input.into()) { Ok(output) => output.into(), Err(err) => err.to_compile_error().into(), } } fn generate(input: TokenStream2) -> Result { let type_info_impl = TypeInfoImpl::parse(input)?; let type_info_impl_toks = type_info_impl.expand()?; Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const _: () = { #type_info_impl_toks; }; }) } struct TypeInfoImpl { ast: DeriveInput, attrs: Attributes, } impl TypeInfoImpl { fn parse(input: TokenStream2) -> Result { let ast: DeriveInput = syn::parse2(input)?; let attrs = attr::Attributes::from_ast(&ast)?; Ok(Self { ast, attrs }) } fn expand(&self) -> Result { let ident = &self.ast.ident; let scale_info = crate_path(self.attrs.crate_path())?; let where_clause = trait_bounds::make_where_clause( &self.attrs, ident, &self.ast.generics, &self.ast.data, &scale_info, )?; let (impl_generics, ty_generics, _) = self.ast.generics.split_for_impl(); let type_params = self.ast.generics.type_params().map(|tp| { let ty_ident = &tp.ident; let ty = if self.attrs.skip_type_params().map_or(true, |skip| !skip.skip(tp)) { quote! { ::core::option::Option::Some(#scale_info::meta_type::<#ty_ident>()) } } else { quote! { ::core::option::Option::None } }; quote! { #scale_info::TypeParameter::new(::core::stringify!(#ty_ident), #ty) } }); let build_type = match &self.ast.data { Data::Struct(ref s) => self.generate_composite_type(s, &scale_info), Data::Enum(ref e) => self.generate_variant_type(e, &scale_info), Data::Union(_) => { return Err(Error::new_spanned(&self.ast, "Unions not supported")) } }; let docs = self.generate_docs(&self.ast.attrs); Ok(quote! { impl #impl_generics #scale_info::TypeInfo for #ident #ty_generics #where_clause { type Identity = Self; fn type_info() -> #scale_info::Type { #scale_info::Type::builder() .path(#scale_info::Path::new(::core::stringify!(#ident), ::core::module_path!())) .type_params(#scale_info::prelude::vec![ #( #type_params ),* ]) #docs .#build_type } } }) } fn generate_composite_type( &self, data_struct: &DataStruct, scale_info: &syn::Path, ) -> TokenStream2 { let fields = match data_struct.fields { Fields::Named(ref fs) => { let fields = self.generate_fields(&fs.named); quote! { named()#( #fields )* } } Fields::Unnamed(ref fs) => { let fields = self.generate_fields(&fs.unnamed); quote! { unnamed()#( #fields )* } } Fields::Unit => { quote! { unit() } } }; quote! { composite(#scale_info::build::Fields::#fields) } } fn generate_fields(&self, fields: &Punctuated) -> Vec { fields .iter() .filter(|f| !utils::should_skip(&f.attrs)) .map(|f| { let (ty, ident) = (&f.ty, &f.ident); // Replace any field lifetime params with `static to prevent "unnecessary lifetime parameter" // warning. Any lifetime parameters are specified as 'static in the type of the impl. struct StaticLifetimesReplace; impl VisitMut for StaticLifetimesReplace { fn visit_lifetime_mut(&mut self, lifetime: &mut Lifetime) { *lifetime = parse_quote!('static) } } let mut ty = match ty { // When a type is specified as part of a `macro_rules!`, the tokens passed to // the `TypeInfo` derive macro are a type `Group`, which is pretty printed with // invisible delimiters e.g. /*«*/ bool /*»*/. To avoid printing the delimiters // the inner type element is extracted. syn::Type::Group(group) => (*group.elem).clone(), _ => ty.clone(), }; StaticLifetimesReplace.visit_type_mut(&mut ty); let type_name = clean_type_string("e!(#ty).to_string()); let docs = self.generate_docs(&f.attrs); let type_of_method = if utils::is_compact(f) { quote!(compact) } else { quote!(ty) }; let name = if let Some(ident) = ident { quote!(.name(::core::stringify!(#ident))) } else { quote!() }; quote!( .field(|f| f .#type_of_method::<#ty>() #name .type_name(#type_name) #docs ) ) }) .collect() } fn generate_variant_type( &self, data_enum: &DataEnum, scale_info: &syn::Path, ) -> TokenStream2 { let variants = &data_enum.variants; let variants = variants .into_iter() .filter(|v| !utils::should_skip(&v.attrs)) .enumerate() .map(|(i, v)| { let ident = &v.ident; let v_name = quote! {::core::stringify!(#ident) }; let docs = self.generate_docs(&v.attrs); let index = utils::variant_index(v, i); let fields = match v.fields { Fields::Named(ref fs) => { let fields = self.generate_fields(&fs.named); Some(quote! { .fields(#scale_info::build::Fields::named() #( #fields )* ) }) } Fields::Unnamed(ref fs) => { let fields = self.generate_fields(&fs.unnamed); Some(quote! { .fields(#scale_info::build::Fields::unnamed() #( #fields )* ) }) } Fields::Unit => None, }; quote! { .variant(#v_name, |v| v .index(#index as ::core::primitive::u8) #fields #docs ) } }); quote! { variant( #scale_info::build::Variants::new() #( #variants )* ) } } fn generate_docs(&self, attrs: &[syn::Attribute]) -> Option { let docs_builder_fn = match self.attrs.capture_docs() { CaptureDocsAttr::Never => None, // early return if we never capture docs. CaptureDocsAttr::Default => Some(quote!(docs)), CaptureDocsAttr::Always => Some(quote!(docs_always)), }?; let docs = attrs .iter() .filter_map(|attr| { if let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() { if meta.path.get_ident().map_or(false, |ident| ident == "doc") { if let syn::Lit::Str(lit) = &meta.lit { let lit_value = lit.value(); let stripped = lit_value.strip_prefix(' ').unwrap_or(&lit_value); let lit: syn::Lit = parse_quote!(#stripped); Some(lit) } else { None } } else { None } } else { None } }) .collect::>(); if !docs.is_empty() { Some(quote! { .#docs_builder_fn(&[ #( #docs ),* ]) }) } else { None } } } /// Get the name of a crate, to be robust against renamed dependencies. fn crate_name_path(name: &str) -> Result { proc_macro_crate::crate_name(name) .map(|crate_name| { use proc_macro_crate::FoundCrate::*; match crate_name { Itself => Ident::new("self", Span::call_site()).into(), Name(name) => { let crate_ident = Ident::new(&name, Span::call_site()); parse_quote!( ::#crate_ident ) } } }) .map_err(|e| syn::Error::new(Span::call_site(), &e)) } fn crate_path(crate_path_attr: Option<&CratePathAttr>) -> Result { crate_path_attr .map(|path_attr| Ok(path_attr.path().clone())) .unwrap_or_else(|| crate_name_path("scale-info")) } fn clean_type_string(input: &str) -> String { input .replace(" ::", "::") .replace(":: ", "::") .replace(" ,", ",") .replace(" ;", ";") .replace(" [", "[") .replace("[ ", "[") .replace(" ]", "]") .replace(" (", "(") // put back a space so that `a: (u8, (bool, u8))` isn't turned into `a: (u8,(bool, u8))` .replace(",(", ", (") .replace("( ", "(") .replace(" )", ")") .replace(" <", "<") .replace("< ", "<") .replace(" >", ">") .replace("& \'", "&'") } scale-info-derive-2.3.1/src/trait_bounds.rs000064400000000000000000000163571046102023000167710ustar 00000000000000// Copyright 2019-2022 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use alloc::vec::Vec; use proc_macro2::Ident; use syn::{ parse_quote, punctuated::Punctuated, spanned::Spanned, visit::{ self, Visit, }, Generics, Result, Type, TypePath, WhereClause, }; use crate::{ attr::Attributes, utils, }; /// Generates a where clause for a `TypeInfo` impl, adding `TypeInfo + 'static` bounds to all /// relevant generic types including associated types (e.g. `T::A: TypeInfo`), correctly dealing /// with self-referential types. /// /// # Effect of attributes /// /// `#[scale_info(skip_type_params(..))]` /// /// Will not add `TypeInfo` bounds for any type parameters skipped via this attribute. /// /// `#[scale_info(bounds(..))]` /// /// Replaces *all* auto-generated trait bounds with the user-defined ones. pub fn make_where_clause<'a>( attrs: &'a Attributes, input_ident: &'a Ident, generics: &'a Generics, data: &'a syn::Data, scale_info: &syn::Path, ) -> Result { let mut where_clause = generics.where_clause.clone().unwrap_or_else(|| { WhereClause { where_token: ::default(), predicates: Punctuated::new(), } }); // Use custom bounds as where clause. if let Some(custom_bounds) = attrs.bounds() { custom_bounds.extend_where_clause(&mut where_clause); // `'static` lifetime bounds are always required for type parameters, because of the // requirement on `std::any::TypeId::of` for any field type constructor. for type_param in generics.type_params() { let ident = &type_param.ident; where_clause.predicates.push(parse_quote!(#ident: 'static)) } return Ok(where_clause) } for lifetime in generics.lifetimes() { where_clause .predicates .push(parse_quote!(#lifetime: 'static)) } let ty_params_ids = generics .type_params() .map(|type_param| type_param.ident.clone()) .collect::>(); if ty_params_ids.is_empty() { return Ok(where_clause) } let types = collect_types_to_bind(input_ident, data, &ty_params_ids)?; types.into_iter().for_each(|(ty, is_compact)| { if is_compact { where_clause .predicates .push(parse_quote!(#ty : #scale_info :: scale::HasCompact)); } else { where_clause .predicates .push(parse_quote!(#ty : #scale_info ::TypeInfo + 'static)); } }); generics.type_params().into_iter().for_each(|type_param| { let ident = type_param.ident.clone(); let mut bounds = type_param.bounds.clone(); if attrs .skip_type_params() .map_or(true, |skip| !skip.skip(type_param)) { bounds.push(parse_quote!(#scale_info ::TypeInfo)); } bounds.push(parse_quote!('static)); where_clause .predicates .push(parse_quote!( #ident : #bounds)); }); Ok(where_clause) } /// Visits the ast and checks if the given type contains one of the given /// idents. fn type_contains_idents(ty: &Type, idents: &[Ident]) -> bool { struct ContainIdents<'a> { result: bool, idents: &'a [Ident], } impl<'a, 'ast> Visit<'ast> for ContainIdents<'a> { fn visit_ident(&mut self, i: &'ast Ident) { if self.idents.iter().any(|id| id == i) { self.result = true; } } } let mut visitor = ContainIdents { result: false, idents, }; visitor.visit_type(ty); visitor.result } /// Checks if the given type or any containing type path starts with the given ident. fn type_or_sub_type_path_starts_with_ident(ty: &Type, ident: &Ident) -> bool { // Visits the ast and checks if the a type path starts with the given ident. struct TypePathStartsWithIdent<'a> { result: bool, ident: &'a Ident, } impl<'a, 'ast> Visit<'ast> for TypePathStartsWithIdent<'a> { fn visit_type_path(&mut self, i: &'ast TypePath) { if i.qself.is_none() { if let Some(segment) = i.path.segments.first() { if &segment.ident == self.ident { self.result = true; return } } } visit::visit_type_path(self, i); } } let mut visitor = TypePathStartsWithIdent { result: false, ident, }; visitor.visit_type(ty); visitor.result } /// Returns all types that must be added to the where clause with a boolean /// indicating if the field is [`scale::Compact`] or not. fn collect_types_to_bind( input_ident: &Ident, data: &syn::Data, ty_params: &[Ident], ) -> Result> { let types_from_fields = |fields: &Punctuated| -> Vec<(Type, bool)> { fields .iter() .filter(|field| { // Only add a bound if the type uses a generic. type_contains_idents(&field.ty, ty_params) && // Remove all remaining types that start/contain the input ident // to not have them in the where clause. !type_or_sub_type_path_starts_with_ident(&field.ty, input_ident) }) .map(|f| (f.ty.clone(), utils::is_compact(f))) .collect() }; let types = match *data { syn::Data::Struct(ref data) => { match &data.fields { syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => types_from_fields(fields), syn::Fields::Unit => Vec::new(), } } syn::Data::Enum(ref data) => { data.variants .iter() .flat_map(|variant| { match &variant.fields { syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => types_from_fields(fields), syn::Fields::Unit => Vec::new(), } }) .collect() } syn::Data::Union(ref data) => { return Err(syn::Error::new( data.union_token.span(), "Union types are not supported.", )) } }; Ok(types) } scale-info-derive-2.3.1/src/utils.rs000064400000000000000000000072401046102023000154230ustar 00000000000000// Copyright 2019-2022 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Utility methods to work with `SCALE` attributes relevant for the `TypeInfo` derive.. //! //! NOTE: The code here is copied verbatim from `parity-scale-codec-derive`. use proc_macro2::TokenStream; use quote::quote; use syn::{ parse::Parse, spanned::Spanned, AttrStyle, Attribute, Lit, Meta, NestedMeta, Variant, }; /// Look for a `#[codec(index = $int)]` attribute on a variant. If no attribute /// is found, fall back to the discriminant or just the variant index. pub fn variant_index(v: &Variant, i: usize) -> TokenStream { // first look for an `index` attribute… let index = maybe_index(v); // …then fallback to discriminant or just index index.map(|i| quote! { #i }).unwrap_or_else(|| { v.discriminant .as_ref() .map(|(_, ref expr)| quote! { #expr }) .unwrap_or_else(|| quote! { #i }) }) } /// Look for a `#[codec(index = $int)]` outer attribute on a variant. /// If found, it is expected to be a parseable as a `u8` (panics otherwise). pub fn maybe_index(variant: &Variant) -> Option { let outer_attrs = variant .attrs .iter() .filter(|attr| attr.style == AttrStyle::Outer); codec_meta_item(outer_attrs, |meta| { if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta { if nv.path.is_ident("index") { if let Lit::Int(ref v) = nv.lit { let byte = v .base10_parse::() .expect("Internal error. `#[codec(index = …)]` attribute syntax must be checked in `parity-scale-codec`. This is a bug."); return Some(byte) } } } None }) } /// Look for a `#[codec(compact)]` outer attribute on the given `Field`. pub fn is_compact(field: &syn::Field) -> bool { let outer_attrs = field .attrs .iter() .filter(|attr| attr.style == AttrStyle::Outer); codec_meta_item(outer_attrs, |meta| { if let NestedMeta::Meta(Meta::Path(ref path)) = meta { if path.is_ident("compact") { return Some(()) } } None }) .is_some() } /// Look for a `#[codec(skip)]` in the given attributes. pub fn should_skip(attrs: &[Attribute]) -> bool { codec_meta_item(attrs.iter(), |meta| { if let NestedMeta::Meta(Meta::Path(ref path)) = meta { if path.is_ident("skip") { return Some(path.span()) } } None }) .is_some() } fn codec_meta_item<'a, F, R, I, M>(itr: I, pred: F) -> Option where F: FnMut(M) -> Option + Clone, I: Iterator, M: Parse, { find_meta_item("codec", itr, pred) } fn find_meta_item<'a, F, R, I, M>(kind: &str, mut itr: I, mut pred: F) -> Option where F: FnMut(M) -> Option + Clone, I: Iterator, M: Parse, { itr.find_map(|attr| { attr.path .is_ident(kind) .then(|| pred(attr.parse_args().ok()?)) .flatten() }) }