From fa1d39f576971aa7011cc068a4fa8d95ec067a35 Mon Sep 17 00:00:00 2001
From: Xidorn Quan <me@upsuper.org>
Date: Mon, 30 Apr 2018 10:12:18 +1000
Subject: [PATCH] Implement FromTypeParam.

This fixes #28.
---
 core/src/codegen/from_type_param.rs | 97 +++++++++++++++++++++++++++++
 core/src/codegen/mod.rs             |  2 +
 core/src/from_type_param.rs         | 26 ++++++++
 core/src/lib.rs                     |  2 +
 core/src/options/from_type_param.rs | 47 ++++++++++++++
 core/src/options/mod.rs             |  2 +
 macro/src/lib.rs                    | 11 ++++
 src/lib.rs                          |  2 +-
 tests/from_type_param.rs            | 58 +++++++++++++++++
 tests/from_type_param_default.rs    | 45 +++++++++++++
 10 files changed, 291 insertions(+), 1 deletion(-)
 create mode 100644 core/src/codegen/from_type_param.rs
 create mode 100644 core/src/from_type_param.rs
 create mode 100644 core/src/options/from_type_param.rs
 create mode 100644 tests/from_type_param.rs
 create mode 100644 tests/from_type_param_default.rs

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<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
+    }
+}
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<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())
+    }
+}
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<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,
+        }
+    }
+}
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<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()));
+    }
+}
diff --git a/tests/from_type_param_default.rs b/tests/from_type_param_default.rs
new file mode 100644
index 0000000..ded50d3
--- /dev/null
+++ b/tests/from_type_param_default.rs
@@ -0,0 +1,45 @@
+#[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
+        >(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.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()));
+    }
+}