Skip to content

Commit

Permalink
Implement FromTypeParam (#30)
Browse files Browse the repository at this point in the history
* Implement FromTypeParam.

This fixes #28.
  • Loading branch information
upsuper authored and TedDriggs committed May 3, 2018
1 parent 3c0520c commit 9013579
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 1 deletion.
97 changes: 97 additions & 0 deletions core/src/codegen/from_type_param.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
#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
}
}
2 changes: 2 additions & 0 deletions core/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
26 changes: 26 additions & 0 deletions core/src/from_type_param.rs
Original file line number Diff line number Diff line change
@@ -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<Self>;
}

impl FromTypeParam for () {
fn from_type_param(_: &TypeParam) -> Result<Self> {
Ok(())
}
}

impl FromTypeParam for TypeParam {
fn from_type_param(type_param: &TypeParam) -> Result<Self> {
Ok(type_param.clone())
}
}

impl FromTypeParam for Vec<syn::Attribute> {
fn from_type_param(type_param: &TypeParam) -> Result<Self> {
Ok(type_param.attrs.clone())
}
}
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)]
Expand Down
47 changes: 47 additions & 0 deletions core/src/options/from_type_param.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
(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,
}
}
}
2 changes: 2 additions & 0 deletions core/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
58 changes: 58 additions & 0 deletions tests/from_type_param.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
}

impl From<Ident> 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(&params[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(&params[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()));
}
}
53 changes: 53 additions & 0 deletions tests/from_type_param_default.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
}

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(&params[0]);
let lorem = Lorem::from_type_param(ty).unwrap();
assert_eq!(lorem.foo, true);
assert_eq!(lorem.bar, None);
}

{
let ty = extract_type(&params[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(&params[2]);
let lorem = Lorem::from_type_param(ty).unwrap();
assert_eq!(lorem.foo, false);
assert_eq!(lorem.bar, None);
}
}

0 comments on commit 9013579

Please sign in to comment.