Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add FromAttributes trait and macro (#152)
With a proliferation of items that support proc-macros, it's not sustainable to create a trait and `darling::ast` type to represent each one. The `FromAttributes` trait is a middle ground; it gives macro authors access to darling's attribute parsing and error handling logic, while leaving AST traversal up to them. Fixes #151
- Loading branch information
Showing
10 changed files
with
239 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::ToTokens; | ||
|
||
use crate::{ | ||
ast::Data, | ||
codegen::{ExtractAttribute, OuterFromImpl, TraitImpl}, | ||
options::ForwardAttrs, | ||
util::PathList, | ||
}; | ||
|
||
pub struct FromAttributesImpl<'a> { | ||
pub base: TraitImpl<'a>, | ||
pub attr_names: &'a PathList, | ||
} | ||
|
||
impl ToTokens for FromAttributesImpl<'_> { | ||
fn to_tokens(&self, tokens: &mut TokenStream) { | ||
let ty_ident = self.base.ident; | ||
let input = self.param_name(); | ||
let post_transform = self.base.post_transform_call(); | ||
|
||
if let Data::Struct(ref data) = self.base.data { | ||
if data.is_newtype() { | ||
self.wrap( | ||
quote! { | ||
fn from_attributes(#input: &[::syn::Attribute]) -> ::darling::Result<Self> { | ||
::darling::export::Ok( | ||
#ty_ident(::darling::FromAttributes::from_attributes(#input)?) | ||
) #post_transform | ||
} | ||
}, | ||
tokens, | ||
); | ||
|
||
return; | ||
} | ||
} | ||
|
||
let inits = self.base.initializers(); | ||
let default = self.base.fallback_decl(); | ||
|
||
let grab_attrs = self.extractor(); | ||
|
||
let declare_errors = self.base.declare_errors(); | ||
let require_fields = self.base.require_fields(); | ||
let check_errors = self.base.check_errors(); | ||
|
||
self.wrap( | ||
quote! { | ||
fn from_attributes(#input: &[::syn::Attribute]) -> ::darling::Result<Self> { | ||
#declare_errors | ||
|
||
#grab_attrs | ||
|
||
#require_fields | ||
|
||
#check_errors | ||
|
||
#default | ||
|
||
::darling::export::Ok(#ty_ident { | ||
#inits | ||
}) #post_transform | ||
} | ||
}, | ||
tokens, | ||
); | ||
} | ||
} | ||
|
||
impl<'a> ExtractAttribute for FromAttributesImpl<'a> { | ||
fn local_declarations(&self) -> TokenStream { | ||
self.base.local_declarations() | ||
} | ||
|
||
fn immutable_declarations(&self) -> TokenStream { | ||
self.base.immutable_declarations() | ||
} | ||
|
||
fn attr_names(&self) -> &PathList { | ||
self.attr_names | ||
} | ||
|
||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs> { | ||
None | ||
} | ||
|
||
fn param_name(&self) -> TokenStream { | ||
quote!(__di) | ||
} | ||
|
||
fn attrs_accessor(&self) -> TokenStream { | ||
self.param_name() | ||
} | ||
|
||
fn core_loop(&self) -> TokenStream { | ||
self.base.core_loop() | ||
} | ||
} | ||
|
||
impl<'a> OuterFromImpl<'a> for FromAttributesImpl<'a> { | ||
fn trait_path(&self) -> syn::Path { | ||
path!(::darling::FromAttributes) | ||
} | ||
|
||
fn trait_bound(&self) -> syn::Path { | ||
path!(::darling::FromMeta) | ||
} | ||
|
||
fn base(&'a self) -> &'a TraitImpl<'a> { | ||
&self.base | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use syn::Attribute; | ||
|
||
use crate::Result; | ||
|
||
/// Create an instance by parsing a list of attributes. | ||
/// | ||
/// This trait is useful when dealing with items such as traits on traits and impl blocks, | ||
/// for which `darling` does not provide dedicated traits. | ||
pub trait FromAttributes: Sized { | ||
/// Create an instance by parsing a list of attributes. | ||
/// | ||
/// By convention, `FromAttributes` implementations should merge item | ||
/// declarations across attributes, so that the following forms are | ||
/// equivalent: | ||
/// | ||
/// ```rust,ignore | ||
/// #[derive(Serialize)] | ||
/// #[serde(rename_all = "camel_case")] | ||
/// #[serde(borrow)] | ||
/// pub struct SplitExample {} | ||
/// | ||
/// #[derive(Serialize)] | ||
/// #[serde(borrow, rename_all = "camel_case")] | ||
/// pub struct JoinedExample {} | ||
/// ``` | ||
fn from_attributes(attrs: &[Attribute]) -> Result<Self>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use quote::ToTokens; | ||
|
||
use crate::{ast::Data, codegen::FromAttributesImpl, Error, Result}; | ||
|
||
use super::{OuterFrom, ParseAttribute, ParseData}; | ||
|
||
/// Receiver for derived `FromAttributes` impls. | ||
pub struct FromAttributesOptions { | ||
// Note: FromAttributes has no behaviors beyond those common | ||
// to all the `OuterFrom` traits. | ||
pub base: OuterFrom, | ||
} | ||
|
||
impl FromAttributesOptions { | ||
pub fn new(di: &syn::DeriveInput) -> Result<Self> { | ||
let opts = (Self { | ||
base: OuterFrom::start(di)?, | ||
}) | ||
.parse_attributes(&di.attrs)? | ||
.parse_body(&di.data)?; | ||
|
||
if !opts.is_newtype() && opts.base.attr_names.is_empty() { | ||
Err(Error::custom( | ||
"FromAttributes without attributes collects nothing", | ||
)) | ||
} else { | ||
Ok(opts) | ||
} | ||
} | ||
|
||
fn is_newtype(&self) -> bool { | ||
if let Data::Struct(ref data) = self.base.container.data { | ||
data.is_newtype() | ||
} else { | ||
false | ||
} | ||
} | ||
} | ||
|
||
impl ParseAttribute for FromAttributesOptions { | ||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> { | ||
self.base.parse_nested(mi) | ||
} | ||
} | ||
|
||
impl ParseData for FromAttributesOptions { | ||
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 FromAttributesOptions> for FromAttributesImpl<'a> { | ||
fn from(v: &'a FromAttributesOptions) -> Self { | ||
FromAttributesImpl { | ||
base: (&v.base.container).into(), | ||
attr_names: &v.base.attr_names, | ||
} | ||
} | ||
} | ||
|
||
impl ToTokens for FromAttributesOptions { | ||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { | ||
FromAttributesImpl::from(self).to_tokens(tokens) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters