use std::borrow::Cow;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use syn::{parse_quote, parse_quote_spanned, spanned::Spanned, Ident};
use crate::{
codegen::{ident_field, ExtractAttribute, OuterFromImpl, TraitImpl},
options::{DeriveInputShapeSet, ForwardedField},
util::PathList,
};
use super::ForwardAttrs;
pub struct FromDeriveInputImpl<'a> {
pub ident: Option<&'a ForwardedField>,
pub generics: Option<&'a ForwardedField>,
pub vis: Option<&'a Ident>,
pub data: Option<&'a ForwardedField>,
pub base: TraitImpl<'a>,
pub attr_names: &'a PathList,
pub forward_attrs: ForwardAttrs<'a>,
pub from_ident: bool,
pub supports: Option<&'a DeriveInputShapeSet>,
}
impl ToTokens for FromDeriveInputImpl<'_> {
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 Some((member, _)) = self
.base
.data
.as_struct()
.and_then(|fields| super::extract_transparent(fields, self.base.transparent))
{
self.wrap(
quote!{
fn from_derive_input(#input: &_darling::export::syn::DeriveInput) -> _darling::Result<Self> {
_darling::export::Ok(
#ty_ident { #member: _darling::FromDeriveInput::from_derive_input(#input)? }
) #post_transform
}
},
tokens,
);
return;
};
let forwarded_fields = vec![
self.ident.as_ref().map(|i| ident_field::create(i, &input)),
self.vis
.as_ref()
.map(|i| parse_quote!(#i: #input.vis.clone())),
self.forward_attrs.to_field_value(),
self.generics.map(|g| g.to_field_value()),
self.data.map(|f| f.to_field_value()),
]
.into_iter()
.flatten();
let read_generics = self.generics.map(|generics| {
let ident = &generics.ident;
let with = generics
.with
.as_ref()
.map(Cow::Borrowed)
.unwrap_or_else(|| {
Cow::Owned(
parse_quote_spanned!(generics.ty.span()=> _darling::FromGenerics::from_generics),
)
});
quote_spanned! {with.span()=>
let #ident = __errors.handle(#with(&#input.generics));
}
});
let check_shape = self
.supports
.map(|s| s.validator_path())
.unwrap_or_else(|| parse_quote!(_darling::export::Ok));
let read_data = self
.data
.as_ref()
.map(|i| match &i.with {
Some(p) => p.clone(),
None => parse_quote_spanned!(i.ty.span()=> _darling::export::TryFrom::try_from),
})
.unwrap_or_else(|| parse_quote!(_darling::export::Ok));
let supports = self.supports;
let validate_and_read_data = {
let let_binding = self.data.map(|d| {
let ident = &d.ident;
quote!(let #ident = )
});
quote! {
#supports
#let_binding __errors.handle(#check_shape(&#input.data).and_then(#read_data));
}
};
let inits = self.base.initializers();
let default = if self.from_ident {
quote!(let __default: Self = _darling::export::From::from(#input.ident.clone());)
} else {
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_derive_input(#input: &_darling::export::syn::DeriveInput) -> _darling::Result<Self> {
#declare_errors
#grab_attrs
#validate_and_read_data
#read_generics
#require_fields
#check_errors
#default
_darling::export::Ok(#ty_ident {
#(#forwarded_fields,)*
#inits
}) #post_transform
}
},
tokens,
);
}
}
impl ExtractAttribute for FromDeriveInputImpl<'_> {
fn attr_names(&self) -> &PathList {
self.attr_names
}
fn forward_attrs(&self) -> &ForwardAttrs<'_> {
&self.forward_attrs
}
fn param_name(&self) -> TokenStream {
quote!(__di)
}
fn core_loop(&self) -> TokenStream {
self.base.core_loop()
}
fn local_declarations(&self) -> TokenStream {
self.base.local_declarations()
}
}
impl<'a> OuterFromImpl<'a> for FromDeriveInputImpl<'a> {
fn trait_path(&self) -> syn::Path {
path!(_darling::FromDeriveInput)
}
fn trait_bound(&self) -> syn::Path {
path!(_darling::FromMeta)
}
fn base(&'a self) -> &'a TraitImpl<'a> {
&self.base
}
}