Skip to content

Commit

Permalink
Capture doc comments, add variant and field builders (#87)
Browse files Browse the repository at this point in the history
* Capture docs for types, fields, and enum variants

* Add docs to derive tests

* Fmt

* Fmt

* Make clippy happy

* Fix struct docs tests

* Fix struct docs tests

* Add missing Vec import

* Fix test

* Clear docs

* Promote build to it's own mod, split fields and variant builders

* Refactor variant construction with builder

* Fix up derive and unit tests with Variant builder

* Fmt

* Condense build module back to single file

* Fix up doc tests

* Fix up README

* Define initial field builder

* Fix up field building

* Fix tests

* Fully qualify calls to stringify

* Default impl for FieldBuilder

* Fix up default impl

* Fix spelling errors

* Remove clear_docs

* Fully qualify module_path

* Update derive/src/lib.rs

Co-authored-by: Robin Freyler <[email protected]>

* Use callback for variant builder

* Update README

* Remove leading space in doc comments

* Satisfy clippy

* Add test for doc capture

* Rename index back to discriminant, changes for this will be in #80

* Rename index back to discriminant, changes for this will be in #80

* Update src/build.rs

Co-authored-by: David <[email protected]>

* Update src/build.rs

Co-authored-by: David <[email protected]>

Co-authored-by: Robin Freyler <[email protected]>
Co-authored-by: David <[email protected]>
  • Loading branch information
3 people authored Jun 6, 2021
1 parent eda88b5 commit 700f810
Show file tree
Hide file tree
Showing 12 changed files with 628 additions and 281 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ where
.path(Path::new("Foo", module_path!()))
.type_params(vec![MetaType::new::<T>()])
.composite(Fields::named()
.field_of::<T>("bar", "T")
.field_of::<u64>("data", "u64")
.field(|f| f.ty::<T>().name("bar").type_name("T"))
.field(|f| f.ty::<u64>().name("data").type_name("u64"))
)
}
}
Expand All @@ -125,8 +125,8 @@ impl TypeInfo for Foo {
Type::builder()
.path(Path::new("Foo", module_path!()))
.composite(Fields::unnamed()
.field_of::<u32>("u32")
.field_of::<bool>("bool")
.field(|f| f.ty::<u32>().type_name("u32"))
.field(|f| f.ty::<bool>().type_name("bool"))
)
}
}
Expand Down Expand Up @@ -155,10 +155,10 @@ where
.path(Path::new("Foo", module_path!()))
.type_params(vec![MetaType::new::<T>()])
.variant(
Variants::with_fields()
.variant("A", Fields::unnamed().field_of::<T>("T"))
.variant("B", Fields::named().field_of::<u32>("f", "u32"))
.variant("C", Fields::unit())
Variants::new()
.variant("A", |v| v.fields(Fields::unnamed().field(|f| f.ty::<T>())))
.variant("B", |v| v.fields(Fields::named().field(|f| f.ty::<u32>().name("f").type_name("u32"))))
.variant_unit("C")
)
}
}
Expand All @@ -181,10 +181,10 @@ impl TypeInfo for Foo {
Type::builder()
.path(Path::new("Foo", module_path!()))
.variant(
Variants::fieldless()
.variant("A", 1)
.variant("B", 2)
.variant("C", 33)
Variants::new()
.variant("A", |v| v.index(1))
.variant("B", |v| v.index(2))
.variant("C", |v| v.index(33))
)
}
}
Expand Down
71 changes: 44 additions & 27 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;
extern crate proc_macro;

Expand Down Expand Up @@ -96,13 +94,16 @@ fn generate_type(input: TokenStream2) -> Result<TokenStream2> {
Data::Enum(ref e) => generate_variant_type(e, &scale_info),
Data::Union(_) => return Err(Error::new_spanned(input, "Unions not supported")),
};
let docs = utils::get_doc_literals(&ast.attrs);

let type_info_impl = 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(stringify!(#ident), module_path!()))
.path(:: #scale_info ::Path::new(::core::stringify!(#ident), ::core::module_path!()))
.type_params(:: #scale_info ::prelude::vec![ #( #generic_type_ids ),* ])
.docs(&[ #( #docs ),* ])
.#build_type
}
}
Expand Down Expand Up @@ -149,16 +150,25 @@ fn generate_fields(fields: &FieldsList) -> Vec<TokenStream2> {
StaticLifetimesReplace.visit_type_mut(&mut ty);

let type_name = clean_type_string(&quote!(#ty).to_string());
let method_call = if utils::is_compact(f) {
quote!(.compact_of::<#ty>)
let docs = utils::get_doc_literals(&f.attrs);
let type_of_method = if utils::is_compact(f) {
quote!(compact)
} else {
quote!(.field_of::<#ty>)
quote!(ty)
};
if let Some(ident) = ident {
quote!(#method_call(stringify!(#ident), #type_name))
let name = if let Some(ident) = ident {
quote!(.name(::core::stringify!(#ident)))
} else {
quote!(#method_call(#type_name))
}
quote!()
};
quote!(
.field(|f| f
.#type_of_method::<#ty>()
#name
.type_name(#type_name)
.docs(&[ #( #docs ),* ])
)
)
})
.collect()
}
Expand Down Expand Up @@ -214,13 +224,18 @@ fn generate_c_like_enum_def(variants: &VariantList, scale_info: &Ident) -> Token
.map(|(i, v)| {
let name = &v.ident;
let discriminant = utils::variant_index(v, i);
let docs = utils::get_doc_literals(&v.attrs);
quote! {
.variant(stringify!(#name), #discriminant as u64)
.variant(::core::stringify!(#name), |v|
v
.discriminant(#discriminant as ::core::primitive::u64)
.docs(&[ #( #docs ),* ])
)
}
});
quote! {
variant(
:: #scale_info ::build::Variants::fieldless()
:: #scale_info ::build::Variants::new()
#( #variants )*
)
}
Expand All @@ -245,39 +260,41 @@ fn generate_variant_type(data_enum: &DataEnum, scale_info: &Ident) -> TokenStrea
.filter(|v| !utils::should_skip(&v.attrs))
.map(|v| {
let ident = &v.ident;
let v_name = quote! {stringify!(#ident) };
match v.fields {
let v_name = quote! {::core::stringify!(#ident) };
let docs = utils::get_doc_literals(&v.attrs);

let fields = match v.fields {
Fields::Named(ref fs) => {
let fields = generate_fields(&fs.named);
quote! {
.variant(
#v_name,
:: #scale_info::build::Fields::named()
#( #fields)*
)
:: #scale_info::build::Fields::named()
#( #fields )*
}
}
Fields::Unnamed(ref fs) => {
let fields = generate_fields(&fs.unnamed);
quote! {
.variant(
#v_name,
:: #scale_info::build::Fields::unnamed()
#( #fields)*
)
:: #scale_info::build::Fields::unnamed()
#( #fields )*
}
}
Fields::Unit => {
quote! {
.variant_unit(#v_name)
:: #scale_info::build::Fields::unit()
}
}
};

quote! {
.variant(#v_name, |v|
v.fields(#fields).docs(&[ #( #docs ),* ])
)
}
});
quote! {
variant(
:: #scale_info ::build::Variants::with_fields()
#( #variants)*
:: #scale_info ::build::Variants::new()
#( #variants )*
)
}
}
27 changes: 27 additions & 0 deletions derive/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
//!
//! NOTE: The code here is copied verbatim from `parity-scale-codec-derive`.
use alloc::{
string::ToString,
vec::Vec,
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
parse_quote,
spanned::Spanned,
AttrStyle,
Attribute,
Expand All @@ -28,6 +33,28 @@ use syn::{
Variant,
};

/// Return all doc attributes literals found.
pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec<syn::Lit> {
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") {
let lit = &meta.lit;
let doc_lit = quote!(#lit).to_string();
let trimmed_doc_lit =
doc_lit.trim_start_matches(r#"" "#).trim_end_matches('"');
Some(parse_quote!(#trimmed_doc_lit))
} else {
None
}
} else {
None
}
})
.collect()
}

/// 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 {
Expand Down
Loading

0 comments on commit 700f810

Please sign in to comment.