Skip to content
31 changes: 28 additions & 3 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ extern crate proc_macro;

mod impl_wrapper;

use alloc::vec::Vec;
use alloc::{
string::{
String,
ToString,
},
vec::Vec,
};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
Expand Down Expand Up @@ -104,19 +110,38 @@ fn generate_fields(fields: &FieldsList) -> Vec<TokenStream2> {
.iter()
.map(|f| {
let (ty, ident) = (&f.ty, &f.ident);
let type_name = clean_type_string(&quote!(#ty).to_string());

if let Some(i) = ident {
quote! {
.field_of::<#ty>(stringify!(#i))
.field_of::<#ty>(stringify!(#i), #type_name)
}
} else {
quote! {
.field_of::<#ty>()
.field_of::<#ty>(#type_name)
}
}
})
.collect()
}

fn clean_type_string(input: &str) -> String {
input
.replace(" ::", "::")
.replace(":: ", "::")
.replace(" ,", ",")
.replace(" ;", ";")
.replace(" [", "[")
.replace("[ ", "[")
.replace(" ]", "]")
.replace(" (", "(")
.replace("( ", "(")
.replace(" )", ")")
.replace(" <", "<")
.replace("< ", "<")
.replace(" >", ">")
Comment on lines +129 to +142
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dq: why wouldn't .replace(" ", ""); work here? Is space ever significant?

Copy link
Contributor

@Robbepop Robbepop Nov 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, e.g. something like &'a mut dyn Foo would become &'amutdynFoo.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

fn generate_composite_type(data_struct: &DataStruct) -> TokenStream2 {
let fields = match data_struct.fields {
Fields::Named(ref fs) => {
Expand Down
42 changes: 15 additions & 27 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
//! .path(Path::new("Foo", module_path!()))
//! .type_params(vec![MetaType::new::<T>()])
//! .composite(Fields::named()
//! .field_of::<T>("bar")
//! .field_of::<u64>("data")
//! .field_of::<T>("bar", "T")
//! .field_of::<u64>("data", "u64")
//! )
//! }
//! }
Expand All @@ -57,8 +57,8 @@
//! Type::builder()
//! .path(Path::new("Foo", module_path!()))
//! .composite(Fields::unnamed()
//! .field_of::<u32>()
//! .field_of::<bool>()
//! .field_of::<u32>("u32")
//! .field_of::<bool>("bool")
//! )
//! }
//! }
Expand All @@ -84,8 +84,8 @@
//! .type_params(vec![MetaType::new::<T>()])
//! .variant(
//! Variants::with_fields()
//! .variant("A", Fields::unnamed().field_of::<T>())
//! .variant("B", Fields::named().field_of::<u32>("f"))
//! .variant("A", Fields::unnamed().field_of::<T>("T"))
//! .variant("B", Fields::named().field_of::<u32>("f", "u32"))
//! .variant("C", Fields::unit())
//! )
//! }
Expand Down Expand Up @@ -177,12 +177,12 @@ impl TypeBuilder<state::PathAssigned> {

/// Construct a "variant" type i.e an `enum`
pub fn variant<V>(self, builder: VariantsBuilder<V>) -> Type {
self.build(builder.done())
self.build(builder.finalize())
}

/// Construct a "composite" type i.e. a `struct`
pub fn composite<F>(self, fields: FieldsBuilder<F>) -> Type {
self.build(TypeDefComposite::new(fields.done()))
self.build(TypeDefComposite::new(fields.finalize()))
}
}

Expand Down Expand Up @@ -226,7 +226,7 @@ impl Fields {

/// Build a set of either all named (e.g. for a struct) or all unnamed (e.g. for a tuple struct)
pub struct FieldsBuilder<T> {
fields: Vec<Field<MetaForm>>,
fields: Vec<Field>,
marker: PhantomData<fn() -> T>,
}

Expand All @@ -241,41 +241,29 @@ impl<T> Default for FieldsBuilder<T> {

impl<T> FieldsBuilder<T> {
/// Complete building and return the set of fields
pub fn done(self) -> Vec<Field<MetaForm>> {
pub fn finalize(self) -> Vec<Field<MetaForm>> {
self.fields
}
}

impl FieldsBuilder<NamedFields> {
/// Add a named field with the given [`MetaType`](`crate::MetaType`) instance
pub fn field(mut self, name: &'static str, ty: MetaType) -> Self {
self.fields.push(Field::named(name, ty));
self
}

/// Add a named field with the type of the type parameter `T`
pub fn field_of<T>(mut self, name: &'static str) -> Self
pub fn field_of<T>(mut self, name: &'static str, type_name: &'static str) -> Self
where
T: TypeInfo + ?Sized + 'static,
{
self.fields.push(Field::named_of::<T>(name));
self.fields.push(Field::named_of::<T>(name, type_name));
self
}
}

impl FieldsBuilder<UnnamedFields> {
/// Add an unnamed field with the given [`MetaType`](`crate::MetaType`) instance
pub fn field(mut self, ty: MetaType) -> Self {
self.fields.push(Field::unnamed(ty));
self
}

/// Add an unnamed field with the type of the type parameter `T`
pub fn field_of<T>(mut self) -> Self
pub fn field_of<T>(mut self, type_name: &'static str) -> Self
where
T: TypeInfo + ?Sized + 'static,
{
self.fields.push(Field::unnamed_of::<T>());
self.fields.push(Field::unnamed_of::<T>(type_name));
self
}
}
Expand Down Expand Up @@ -341,7 +329,7 @@ impl<T> VariantsBuilder<T> {
}
}

fn done(self) -> TypeDefVariant {
fn finalize(self) -> TypeDefVariant {
TypeDefVariant::new(self.variants)
}
}
8 changes: 4 additions & 4 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ where
.variant(
Variants::with_fields()
.variant_unit("None")
.variant("Some", Fields::unnamed().field_of::<T>()),
.variant("Some", Fields::unnamed().field_of::<T>("T")),
)
}
}
Expand All @@ -139,8 +139,8 @@ where
.type_params(tuple_meta_type!(T, E))
.variant(
Variants::with_fields()
.variant("Ok", Fields::unnamed().field_of::<T>())
.variant("Err", Fields::unnamed().field_of::<E>()),
.variant("Ok", Fields::unnamed().field_of::<T>("T"))
.variant("Err", Fields::unnamed().field_of::<E>("E")),
)
}
}
Expand All @@ -156,7 +156,7 @@ where
Type::builder()
.path(Path::prelude("BTreeMap"))
.type_params(tuple_meta_type![(K, V)])
.composite(Fields::unnamed().field_of::<[(K, V)]>())
.composite(Fields::unnamed().field_of::<[(K, V)]>("[(K, V)]"))
}
}

Expand Down
11 changes: 9 additions & 2 deletions src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,17 @@ mod tests {
.path(Path::new("RecursiveRefs", module_path!()))
.composite(
Fields::named()
.field_of::<Box<RecursiveRefs>>("boxed")
.field_of::<&'static RecursiveRefs<'static>>("reference")
.field_of::<Box<RecursiveRefs>>(
"boxed",
"Box < RecursiveRefs >",
)
.field_of::<&'static RecursiveRefs<'static>>(
"reference",
"&RecursiveRefs",
)
.field_of::<&'static mut RecursiveRefs<'static>>(
"mutable_reference",
"&mut RecursiveRefs",
),
)
.into()
Expand Down
12 changes: 6 additions & 6 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn prelude_items() {
.variant(
Variants::with_fields()
.variant_unit("None")
.variant("Some", Fields::unnamed().field_of::<u128>())
.variant("Some", Fields::unnamed().field_of::<u128>("T"))
)
);
assert_type!(
Expand All @@ -70,8 +70,8 @@ fn prelude_items() {
.type_params(tuple_meta_type!(bool, String))
.variant(
Variants::with_fields()
.variant("Ok", Fields::unnamed().field_of::<bool>())
.variant("Err", Fields::unnamed().field_of::<String>())
.variant("Ok", Fields::unnamed().field_of::<bool>("T"))
.variant("Err", Fields::unnamed().field_of::<String>("E"))
)
);
assert_type!(
Expand Down Expand Up @@ -133,7 +133,7 @@ fn struct_with_generics() {
Type::builder()
.path(Path::new("MyStruct", module_path!()))
.type_params(tuple_meta_type!(T))
.composite(Fields::named().field_of::<T>("data"))
.composite(Fields::named().field_of::<T>("data", "T"))
.into()
}
}
Expand All @@ -142,7 +142,7 @@ fn struct_with_generics() {
let struct_bool_type_info = Type::builder()
.path(Path::from_segments(vec!["scale_info", "tests", "MyStruct"]).unwrap())
.type_params(tuple_meta_type!(bool))
.composite(Fields::named().field_of::<bool>("data"));
.composite(Fields::named().field_of::<bool>("data", "T"));

assert_type!(MyStruct<bool>, struct_bool_type_info);

Expand All @@ -151,6 +151,6 @@ fn struct_with_generics() {
let expected_type = Type::builder()
.path(Path::new("MyStruct", "scale_info::tests"))
.type_params(tuple_meta_type!(Box<MyStruct<bool>>))
.composite(Fields::named().field_of::<Box<MyStruct<bool>>>("data"));
.composite(Fields::named().field_of::<Box<MyStruct<bool>>>("data", "T"));
assert_type!(SelfTyped, expected_type);
}
71 changes: 51 additions & 20 deletions src/ty/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,47 @@ use serde::{
///
/// Name is optional so it can represent both named and unnamed fields.
///
/// This can be a named field of a struct type or a struct variant.
/// This can be a named field of a struct type or an enum struct variant.
///
/// # Type name
///
/// The `type_name` field contains a string which is the name of the type of the
/// field as it appears in the source code. The exact contents and format of the
/// type name are not specified, but in practice will be the name of any valid
/// type for a field e.g.
///
/// - Concrete types e.g `"u32"`, `"bool"`, `"Foo"` etc.
/// - Type parameters e.g `"T"`, `"U"`
/// - Generic types e.g `"Vec<u32>"`, `"Vec<T>"`
/// - Associated types e.g. `"T::MyType"`, `"<T as MyTrait>::MyType"`
/// - Type aliases e.g. `"MyTypeAlias"`, `"MyTypeAlias<T>"`
/// - Other built in Rust types e.g. arrays, references etc.
///
/// Note that the type name doesn't correspond to the underlying type of the
/// field, unless using a concrete type directly. Any given type may be referred
/// to by multiple field type names, when using generic type parameters and type
/// aliases.
///
/// This is intended for informational and diagnostic purposes only. Although it
/// is possible to infer certain properties e.g. whether a type name is a type alias,
/// there are no guarantees provided, and the type name representation may change.
#[derive(
PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Serialize, Deserialize, Encode, Decode,
)]
#[serde(bound(
serialize = "T::Type: Serialize, T::String: Serialize",
deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
pub struct Field<T: Form = MetaForm> {
/// The name of the field. None for unnamed fields.
#[serde(skip_serializing_if = "Option::is_none", default)]
name: Option<T::String>,
/// The type of the field.
#[serde(rename = "type")]
ty: T::Type,
/// The name of the type of the field as it appears in the source code.
type_name: T::String,
}

impl IntoCompact for Field {
Expand All @@ -63,6 +89,7 @@ impl IntoCompact for Field {
Field {
name: self.name.map(|name| name.into_compact(registry)),
ty: registry.register_type(&self.ty),
type_name: self.type_name.into_compact(registry),
}
}
}
Expand All @@ -71,43 +98,38 @@ impl Field {
/// Creates a new field.
///
/// Use this constructor if you want to instantiate from a given meta type.
pub fn new(name: Option<&'static str>, ty: MetaType) -> Self {
Self { name, ty }
}

/// Creates a new named field
pub fn named(name: &'static str, ty: MetaType) -> Self {
Self::new(Some(name), ty)
pub fn new(
name: Option<&'static str>,
ty: MetaType,
type_name: &'static str,
) -> Self {
Self {
name,
ty,
type_name,
}
}

/// Creates a new named field.
///
/// Use this constructor if you want to instantiate from a given
/// compile-time type.
pub fn named_of<T>(name: &'static str) -> Self
pub fn named_of<T>(name: &'static str, type_name: &'static str) -> Field
where
T: TypeInfo + ?Sized + 'static,
{
Self::new(Some(name), MetaType::new::<T>())
}

/// Creates a new unnamed field.
///
/// Use this constructor if you want to instantiate an unnamed field from a
/// given meta type.
pub fn unnamed(meta_type: MetaType) -> Self {
Self::new(None, meta_type)
Self::new(Some(name), MetaType::new::<T>(), type_name)
}

/// Creates a new unnamed field.
///
/// Use this constructor if you want to instantiate an unnamed field from a
/// given compile-time type.
pub fn unnamed_of<T>() -> Self
pub fn unnamed_of<T>(type_name: &'static str) -> Field
where
T: TypeInfo + ?Sized + 'static,
{
Self::new(None, MetaType::new::<T>())
Self::new(None, MetaType::new::<T>(), type_name)
}
}

Expand All @@ -124,4 +146,13 @@ where
pub fn ty(&self) -> &T::Type {
&self.ty
}

/// Returns a string which is the name of the type of the field as it
/// appears in the source code. The exact contents and format of the type
/// name are not specified, but in practice will be the name of any valid
/// type for a field. This is intended for informational and diagnostic
/// purposes only.
pub fn type_name(&self) -> &T::String {
&self.type_name
}
}
Loading