diff --git a/core/src/codegen/from_type_param.rs b/core/src/codegen/from_type_param.rs new file mode 100644 index 0000000..e97dc49 --- /dev/null +++ b/core/src/codegen/from_type_param.rs @@ -0,0 +1,97 @@ +use quote::{Tokens, ToTokens}; +use syn::{self, Ident}; + +use codegen::{ExtractAttribute, OuterFromImpl, TraitImpl}; +use options::ForwardAttrs; + +pub struct FromTypeParamImpl<'a> { + pub base: TraitImpl<'a>, + pub ident: Option<&'a Ident>, + pub attrs: Option<&'a Ident>, + pub attr_names: Vec<&'a str>, + pub forward_attrs: Option<&'a ForwardAttrs>, + pub from_ident: bool, +} + +impl<'a> ToTokens for FromTypeParamImpl<'a> { + fn to_tokens(&self, tokens: &mut Tokens) { + let input = self.param_name(); + + let error_declaration = self.base.declare_errors(); + let grab_attrs = self.extractor(); + let require_fields = self.base.require_fields(); + let error_check = self.base.check_errors(); + + let default = if self.from_ident { + quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());) + } else { + self.base.fallback_decl() + }; + + let passed_ident = self.ident.as_ref().map(|i| quote!(#i: #input.ident.clone(),)); + let passed_attrs = self.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,)); + let initializers = self.base.initializers(); + + let map = self.base.map_fn(); + + self.wrap(quote! { + fn from_type_param(#input: &::syn::TypeParam) -> ::darling::Result { + #error_declaration + + #grab_attrs + + #require_fields + + #error_check + + #default + + ::darling::export::Ok(Self { + #passed_ident + #passed_attrs + #initializers + }) #map + } + }, tokens); + } +} + +impl<'a> ExtractAttribute for FromTypeParamImpl<'a> { + fn attr_names(&self) -> &[&str] { + self.attr_names.as_slice() + } + + fn forwarded_attrs(&self) -> Option<&ForwardAttrs> { + self.forward_attrs + } + + fn param_name(&self) -> Tokens { + quote!(__type_param) + } + + fn core_loop(&self) -> Tokens { + self.base.core_loop() + } + + fn local_declarations(&self) -> Tokens { + self.base.local_declarations() + } + + fn immutable_declarations(&self) -> Tokens { + self.base.immutable_declarations() + } +} + +impl<'a> OuterFromImpl<'a> for FromTypeParamImpl<'a> { + fn trait_path(&self) -> syn::Path { + path!(::darling::FromTypeParam) + } + + fn trait_bound(&self) -> syn::Path { + path!(::darling::FromMetaItem) + } + + fn base(&'a self) -> &'a TraitImpl<'a> { + &self.base + } +} diff --git a/core/src/codegen/mod.rs b/core/src/codegen/mod.rs index f76572a..97f43b9 100644 --- a/core/src/codegen/mod.rs +++ b/core/src/codegen/mod.rs @@ -6,6 +6,7 @@ mod field; mod fmi_impl; mod from_derive_impl; mod from_field; +mod from_type_param; mod from_variant_impl; mod outer_from_impl; mod trait_impl; @@ -17,6 +18,7 @@ pub use self::field::Field; pub use self::fmi_impl::FmiImpl; pub use self::from_derive_impl::FromDeriveInputImpl; pub use self::from_field::FromFieldImpl; +pub use self::from_type_param::FromTypeParamImpl; pub use self::from_variant_impl::FromVariantImpl; pub use self::outer_from_impl::OuterFromImpl; pub use self::trait_impl::TraitImpl; diff --git a/core/src/from_type_param.rs b/core/src/from_type_param.rs new file mode 100644 index 0000000..91fbfc3 --- /dev/null +++ b/core/src/from_type_param.rs @@ -0,0 +1,26 @@ +use syn::{self, TypeParam}; + +use Result; + +/// Creates an instance by parsing an individual type_param and its attributes. +pub trait FromTypeParam: Sized { + fn from_type_param(type_param: &TypeParam) -> Result; +} + +impl FromTypeParam for () { + fn from_type_param(_: &TypeParam) -> Result { + Ok(()) + } +} + +impl FromTypeParam for TypeParam { + fn from_type_param(type_param: &TypeParam) -> Result { + Ok(type_param.clone()) + } +} + +impl FromTypeParam for Vec { + fn from_type_param(type_param: &TypeParam) -> Result { + Ok(type_param.attrs.clone()) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 50ac06a..771b561 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -18,6 +18,7 @@ pub mod error; mod from_field; mod from_derive_input; mod from_meta_item; +mod from_type_param; mod from_variant; pub mod options; pub mod util; @@ -26,6 +27,7 @@ pub use error::{Result, Error}; pub use from_derive_input::FromDeriveInput; pub use from_field::FromField; pub use from_meta_item::{FromMetaItem}; +pub use from_type_param::FromTypeParam; pub use from_variant::FromVariant; #[cfg(test)] diff --git a/core/src/options/from_type_param.rs b/core/src/options/from_type_param.rs new file mode 100644 index 0000000..26d0f34 --- /dev/null +++ b/core/src/options/from_type_param.rs @@ -0,0 +1,47 @@ +use syn; + +use {Result}; +use codegen::FromTypeParamImpl; +use options::{ParseAttribute, ParseData, OuterFrom}; + +#[derive(Debug)] +pub struct FromTypeParamOptions { + pub base: OuterFrom, +} + +impl FromTypeParamOptions { + pub fn new(di: &syn::DeriveInput) -> Result { + (FromTypeParamOptions { + base: OuterFrom::start(di), + }).parse_attributes(&di.attrs)?.parse_body(&di.data) + } +} + +impl ParseAttribute for FromTypeParamOptions { + fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> { + self.base.parse_nested(mi) + } +} + +impl ParseData for FromTypeParamOptions { + fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> { + self.base.parse_variant(variant) + } + + fn parse_field(&mut self, field: &syn::Field) -> Result<()> { + self.base.parse_field(field) + } +} + +impl<'a> From<&'a FromTypeParamOptions> for FromTypeParamImpl<'a> { + fn from(v: &'a FromTypeParamOptions) -> Self { + FromTypeParamImpl { + base: (&v.base.container).into(), + ident: v.base.ident.as_ref(), + attrs: v.base.attrs.as_ref(), + attr_names: v.base.attr_names.as_strs(), + forward_attrs: v.base.forward_attrs.as_ref(), + from_ident: v.base.from_ident, + } + } +} diff --git a/core/src/options/mod.rs b/core/src/options/mod.rs index e2d58c0..163ac45 100644 --- a/core/src/options/mod.rs +++ b/core/src/options/mod.rs @@ -7,6 +7,7 @@ mod forward_attrs; mod from_derive; mod from_field; mod from_meta_item; +mod from_type_param; mod from_variant; mod input_variant; mod input_field; @@ -18,6 +19,7 @@ pub use self::forward_attrs::ForwardAttrs; pub use self::from_derive::FdiOptions; pub use self::from_field::FromFieldOptions; pub use self::from_meta_item::FmiOptions; +pub use self::from_type_param::FromTypeParamOptions; pub use self::from_variant::FromVariantOptions; pub use self::input_variant::InputVariant; pub use self::input_field::InputField; diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 9f68872..8a70097 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -41,6 +41,17 @@ pub fn derive_field(input: TokenStream) -> TokenStream { result.into() } +#[proc_macro_derive(FromTypeParam, attributes(darling))] +pub fn derive_type_param(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + + let fdic = options::FromTypeParamOptions::new(&ast).unwrap(); + let trait_impl = codegen::FromTypeParamImpl::from(&fdic); + let result = quote!(#trait_impl); + + result.into() +} + #[proc_macro_derive(FromVariant, attributes(darling))] pub fn derive_variant(input: TokenStream) -> TokenStream { let ast: syn::DeriveInput = syn::parse(input).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 322f791..0b02528 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,7 +59,7 @@ extern crate darling_macro; pub use darling_macro::*; #[doc(inline)] -pub use darling_core::{FromMetaItem, FromDeriveInput, FromField, FromVariant}; +pub use darling_core::{FromMetaItem, FromDeriveInput, FromField, FromTypeParam, FromVariant}; #[doc(inline)] pub use darling_core::{Result, Error}; diff --git a/tests/from_type_param.rs b/tests/from_type_param.rs new file mode 100644 index 0000000..343a43e --- /dev/null +++ b/tests/from_type_param.rs @@ -0,0 +1,58 @@ +#[macro_use] +extern crate darling; +extern crate syn; + +use darling::FromTypeParam; +use syn::{DeriveInput, GenericParam, Ident, TypeParam}; + +#[darling(attributes(lorem), from_ident)] +#[derive(FromTypeParam)] +struct Lorem { + ident: Ident, + foo: bool, + bar: Option, +} + +impl From for Lorem { + fn from(ident: Ident) -> Self { + Lorem { + ident, + foo: false, + bar: None, + } + } +} + +fn extract_type(param: &GenericParam) -> &TypeParam { + match *param { + GenericParam::Type(ref ty) => ty, + _ => unreachable!("Not a type param"), + } +} + +#[test] +fn expand_many() { + let di: DeriveInput = syn::parse_str(r#" + struct Baz< + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized + >(T, U); + "#).unwrap(); + let params = di.generics.params; + + { + let ty = extract_type(¶ms[0]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.ident.as_ref(), "T"); + assert_eq!(lorem.foo, true); + assert_eq!(lorem.bar, None); + } + + { + let ty = extract_type(¶ms[1]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.ident.as_ref(), "U"); + assert_eq!(lorem.foo, false); + assert_eq!(lorem.bar, Some("x".to_string())); + } +} diff --git a/tests/from_type_param_default.rs b/tests/from_type_param_default.rs new file mode 100644 index 0000000..d68a5ed --- /dev/null +++ b/tests/from_type_param_default.rs @@ -0,0 +1,53 @@ +#[macro_use] +extern crate darling; +extern crate syn; + +use darling::FromTypeParam; +use syn::{DeriveInput, GenericParam, TypeParam}; + +#[darling(attributes(lorem), default)] +#[derive(Default, FromTypeParam)] +struct Lorem { + foo: bool, + bar: Option, +} + +fn extract_type(param: &GenericParam) -> &TypeParam { + match *param { + GenericParam::Type(ref ty) => ty, + _ => unreachable!("Not a type param"), + } +} + +#[test] +fn expand_many() { + let di: DeriveInput = syn::parse_str(r#" + struct Baz< + #[lorem(foo)] T, + #[lorem(bar = "x")] U: Eq + ?Sized, + #[lorem(foo = false)] V = (), + >(T, U, V); + "#).unwrap(); + let params = di.generics.params; + + { + let ty = extract_type(¶ms[0]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.foo, true); + assert_eq!(lorem.bar, None); + } + + { + let ty = extract_type(¶ms[1]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.foo, false); + assert_eq!(lorem.bar, Some("x".to_string())); + } + + { + let ty = extract_type(¶ms[2]); + let lorem = Lorem::from_type_param(ty).unwrap(); + assert_eq!(lorem.foo, false); + assert_eq!(lorem.bar, None); + } +}