use darling::{
ast::{self, Data},
usage::{GenericsExt, LifetimeRefSet, Purpose, UsesLifetimes},
util::Flag,
Error, FromDeriveInput, FromVariant,
};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{parse_quote, WhereClause};
use crate::field::{TreeField, TreeTrait};
#[derive(Debug, FromVariant, Clone)]
#[darling(attributes(tree), supports(newtype, tuple, unit), and_then=Self::parse)]
pub struct TreeVariant {
ident: syn::Ident,
rename: Option<syn::Ident>,
skip: Flag,
fields: ast::Fields<TreeField>,
}
impl TreeVariant {
fn parse(mut self) -> darling::Result<Self> {
assert!(!self.fields.is_struct());
while self
.fields
.fields
.last()
.map(|f| f.skip.is_present())
.unwrap_or_default()
{
self.fields.fields.pop();
}
if let Some(f) = self.fields.iter().find(|f| f.skip.is_present()) {
return Err(
Error::custom("Can only `skip` terminal tuple variant fields")
.with_span(&f.skip.span()),
);
}
Ok(self)
}
fn field(&self) -> &TreeField {
assert!(self.fields.len() == 1); self.fields.fields.first().unwrap()
}
fn name(&self) -> &syn::Ident {
self.rename.as_ref().unwrap_or(&self.ident)
}
}
#[derive(Debug, FromDeriveInput, Clone)]
#[darling(attributes(tree), supports(struct_named, struct_newtype, struct_tuple, enum_newtype, enum_tuple, enum_unit), and_then=Self::parse)]
#[darling()]
pub struct Tree {
ident: syn::Ident,
generics: syn::Generics,
flatten: Flag,
data: Data<TreeVariant, TreeField>,
}
impl Tree {
fn parse(mut self) -> darling::Result<Self> {
match &mut self.data {
Data::Struct(fields) => {
while fields
.fields
.last()
.map(|f| f.ident.is_none() && f.skip.is_present())
.unwrap_or_default()
{
fields.fields.pop();
}
fields
.fields
.retain(|f| f.ident.is_none() || !f.skip.is_present());
if let Some(f) = fields.fields.iter().find(|f| f.skip.is_present()) {
return Err(
Error::custom("Can only `skip` terminal tuple struct fields")
.with_span(&f.skip.span()),
);
}
}
Data::Enum(variants) => {
variants.retain(|v| !(v.skip.is_present() || v.fields.is_empty()));
for v in variants.iter() {
if v.fields.len() != 1 {
return Err(Error::custom(
"Only newtype (single field tuple) and unit enum variants are supported.",
)
.with_span(&v.ident.span()));
}
}
}
}
if self.flatten.is_present() && self.fields().len() != 1 {
return Err(Error::custom("Can't flatten multiple fields/variants")
.with_span(&self.flatten.span()));
}
if self.fields().is_empty() {
return Err(Error::custom("Internal nodes must have at least one leaf")
.with_span(&self.ident.span()));
}
Ok(self)
}
fn fields(&self) -> Vec<&TreeField> {
match &self.data {
Data::Struct(fields) => fields.iter().collect(),
Data::Enum(variants) => variants.iter().map(|v| v.field()).collect(),
}
}
fn arms<F: FnMut(&TreeField, Option<usize>) -> TokenStream>(
&self,
mut func: F,
) -> (TokenStream, Vec<TokenStream>, TokenStream) {
match &self.data {
Data::Struct(fields) => (
quote!(index),
fields
.iter()
.enumerate()
.map(|(i, f)| {
let rhs = func(f, Some(i));
quote!(#i => #rhs)
})
.collect(),
quote!(::core::unreachable!()),
),
Data::Enum(variants) => (
quote!((self, index)),
variants
.iter()
.enumerate()
.map(|(i, v)| {
let ident = &v.ident;
let rhs = func(v.field(), None);
quote!((Self::#ident(value, ..), #i) => #rhs)
})
.collect(),
quote!(::core::result::Result::Err(
::miniconf::Traversal::Absent(0).into()
)),
),
}
}
fn bound_generics(
&self,
traite: TreeTrait,
where_clause: Option<&WhereClause>,
) -> Option<syn::WhereClause> {
let type_set = self.generics.declared_type_params();
let bounds: TokenStream = self
.fields()
.iter()
.filter_map(|f| f.bound(traite, &type_set))
.collect();
if bounds.is_empty() {
where_clause.cloned()
} else if where_clause.is_some() {
Some(parse_quote! { #where_clause #bounds })
} else {
Some(parse_quote! { where #bounds })
}
}
fn index(&self) -> TokenStream {
if self.flatten.is_present() {
quote!(::core::result::Result::<usize, ::miniconf::Traversal>::Ok(
0
))
} else {
quote!(::miniconf::Keys::next(&mut keys, &Self::__MINICONF_LOOKUP))
}
}
pub fn tree_key(&self) -> TokenStream {
let ident = &self.ident;
let (impl_generics, ty_generics, orig_where_clause) = self.generics.split_for_impl();
let where_clause = self.bound_generics(TreeTrait::Key, orig_where_clause);
let fields = self.fields();
let fields_len = fields.len();
let traverse_all_arms = fields.iter().enumerate().map(|(i, f)| {
let w = f.traverse_all();
if self.flatten.is_present() {
quote!(walk = #w;)
} else {
quote!(walk = walk.merge(&#w, Some(#i), &Self::__MINICONF_LOOKUP)?;)
}
});
let traverse_arms = fields.iter().enumerate().map(|(i, f)| f.traverse_by_key(i));
let names: Option<Vec<_>> = match &self.data {
Data::Struct(fields) if fields.style.is_struct() => Some(
fields
.iter()
.map(|f| {
let name = f.name().unwrap();
quote_spanned! { name.span()=> stringify!(#name) }
})
.collect(),
),
Data::Enum(variants) => Some(
variants
.iter()
.map(|v| {
let name = v.name();
quote_spanned! { name.span()=> stringify!(#name) }
})
.collect(),
),
_ => None,
};
let names = match names {
None => quote!(::core::option::Option::None),
Some(names) => quote!(::core::option::Option::Some(&[#(#names ,)*])),
};
let index = self.index();
let (traverse, increment) = if self.flatten.is_present() {
(None, None)
} else {
(
Some(quote! {
let name = Self::__MINICONF_LOOKUP.lookup(index)?;
func(index, name, Self::__MINICONF_LOOKUP.len)
.map_err(|err| ::miniconf::Error::Inner(1, err))?;
}),
Some(quote!(::miniconf::Error::increment_result)),
)
};
quote! {
#[automatically_derived]
impl #impl_generics #ident #ty_generics #orig_where_clause {
const __MINICONF_LOOKUP: ::miniconf::KeyLookup = ::miniconf::KeyLookup {
len: match ::core::num::NonZero::new(#fields_len) {
Some(n) => n,
None => unreachable!(),
},
names: #names,
};
}
#[automatically_derived]
impl #impl_generics ::miniconf::TreeKey for #ident #ty_generics #where_clause {
fn traverse_all<W: ::miniconf::Walk>() -> ::core::result::Result<W, W::Error> {
#[allow(unused_mut)]
let mut walk = W::internal();
#(#traverse_all_arms)*
::core::result::Result::Ok(walk)
}
fn traverse_by_key<K, F, E>(mut keys: K, mut func: F) -> ::core::result::Result<usize, ::miniconf::Error<E>>
where
K: ::miniconf::Keys,
F: ::core::ops::FnMut(usize, ::core::option::Option<&'static str>, ::core::num::NonZero<usize>) -> ::core::result::Result<(), E>,
{
let index = #index?;
#traverse
#increment(match index {
#(#traverse_arms ,)*
_ => unreachable!()
})
}
}
}
}
pub fn tree_serialize(&self) -> TokenStream {
let ident = &self.ident;
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let where_clause = self.bound_generics(TreeTrait::Serialize, where_clause);
let index = self.index();
let (mat, arms, default) = self.arms(|f, i| f.serialize_by_key(i));
let increment =
(!self.flatten.is_present()).then_some(quote!(::miniconf::Error::increment_result));
quote! {
#[automatically_derived]
impl #impl_generics ::miniconf::TreeSerialize for #ident #ty_generics #where_clause {
fn serialize_by_key<K, S>(&self, mut keys: K, ser: S) -> ::core::result::Result<usize, ::miniconf::Error<S::Error>>
where
K: ::miniconf::Keys,
S: ::miniconf::Serializer,
{
let index = #index?;
#increment(match #mat {
#(#arms ,)*
_ => #default
})
}
}
}
}
pub fn tree_deserialize(&self) -> TokenStream {
let ty_generics = self.generics.split_for_impl().1;
let lifetimes = self.generics.declared_lifetimes();
let mut de: syn::LifetimeParam = parse_quote!('de);
de.bounds.extend(
self.fields()
.iter()
.flat_map(|f| f.uses_lifetimes(&Purpose::BoundImpl.into(), &lifetimes))
.collect::<LifetimeRefSet>()
.into_iter()
.cloned(),
);
let mut generics = self.generics.clone();
generics.params.push(syn::GenericParam::Lifetime(de));
let (impl_generics, _, where_clause) = generics.split_for_impl();
let where_clause = self.bound_generics(TreeTrait::Deserialize, where_clause);
let index = self.index();
let ident = &self.ident;
let (mat, arms, default) = self.arms(|f, i| f.deserialize_by_key(i));
let increment =
(!self.flatten.is_present()).then_some(quote!(::miniconf::Error::increment_result));
quote! {
#[automatically_derived]
impl #impl_generics ::miniconf::TreeDeserialize<'de> for #ident #ty_generics #where_clause {
fn deserialize_by_key<K, D>(&mut self, mut keys: K, de: D) -> ::core::result::Result<usize, ::miniconf::Error<D::Error>>
where
K: ::miniconf::Keys,
D: ::miniconf::Deserializer<'de>,
{
let index = #index?;
#increment(match #mat {
#(#arms ,)*
_ => #default
})
}
}
}
}
pub fn tree_any(&self) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
let where_clause = self.bound_generics(TreeTrait::Any, where_clause);
let index = self.index();
let ident = &self.ident;
let (mat, ref_arms, default) = self.arms(|f, i| f.ref_any_by_key(i));
let (_, mut_arms, _) = self.arms(|f, i| f.mut_any_by_key(i));
let increment = (!self.flatten.is_present())
.then_some(quote!(.map_err(::miniconf::Traversal::increment)));
quote! {
#[automatically_derived]
impl #impl_generics ::miniconf::TreeAny for #ident #ty_generics #where_clause {
fn ref_any_by_key<K>(&self, mut keys: K) -> ::core::result::Result<&dyn ::core::any::Any, ::miniconf::Traversal>
where
K: ::miniconf::Keys,
{
let index = #index?;
let ret: ::core::result::Result<_, _> = match #mat {
#(#ref_arms ,)*
_ => #default
};
ret #increment
}
fn mut_any_by_key<K>(&mut self, mut keys: K) -> ::core::result::Result<&mut dyn ::core::any::Any, ::miniconf::Traversal>
where
K: ::miniconf::Keys,
{
let index = #index?;
let ret: ::core::result::Result<_, _> = match #mat {
#(#mut_arms ,)*
_ => #default
};
ret #increment
}
}
}
}
}