use darling::{
usage::{IdentSet, Purpose, UsesTypeParams},
uses_lifetimes, uses_type_params,
util::Flag,
FromField, FromMeta,
};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{parse_quote, parse_quote_spanned, spanned::Spanned};
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum TreeTrait {
Key,
Serialize,
Deserialize,
Any,
}
#[derive(Debug, FromMeta, PartialEq, Clone, Default)]
struct Deny {
traverse: Option<String>,
serialize: Option<String>,
deserialize: Option<String>,
probe: Option<String>,
ref_any: Option<String>,
mut_any: Option<String>,
}
#[derive(Debug, FromMeta, PartialEq, Clone, Default)]
struct With {
traverse: Option<syn::Path>,
traverse_all: Option<syn::Path>,
serialize: Option<syn::Expr>,
deserialize: Option<syn::Expr>,
probe: Option<syn::Path>,
ref_any: Option<syn::Expr>,
mut_any: Option<syn::Expr>,
}
#[derive(Debug, FromField, Clone)]
#[darling(attributes(tree))]
pub(crate) struct TreeField {
pub ident: Option<syn::Ident>,
ty: syn::Type,
pub skip: Flag,
typ: Option<syn::Type>, rename: Option<syn::Ident>,
defer: Option<syn::Expr>, #[darling(default)]
with: With,
#[darling(default)]
deny: Deny,
}
uses_type_params!(TreeField, ty, typ);
uses_lifetimes!(TreeField, ty, typ);
impl TreeField {
fn span(&self) -> Span {
self.ident
.as_ref()
.map(|i| i.span())
.unwrap_or(self.ty.span())
}
pub fn typ(&self) -> &syn::Type {
self.typ.as_ref().unwrap_or(&self.ty)
}
pub fn bound(&self, trtr: TreeTrait, type_set: &IdentSet) -> Option<TokenStream> {
if self
.uses_type_params(&Purpose::BoundImpl.into(), type_set)
.is_empty()
{
None
} else {
match trtr {
TreeTrait::Key => Some(parse_quote!(::miniconf::TreeKey)),
TreeTrait::Serialize => self
.deny
.serialize
.is_none()
.then_some(parse_quote!(::miniconf::TreeSerialize)),
TreeTrait::Deserialize => (self.deny.deserialize.is_none()
|| self.deny.probe.is_none())
.then_some(parse_quote!(::miniconf::TreeDeserialize<'de>)),
TreeTrait::Any => (self.deny.ref_any.is_none() || self.deny.mut_any.is_none())
.then_some(parse_quote!(::miniconf::TreeAny)),
}
.map(|bound: syn::TraitBound| {
let ty = self.typ();
quote_spanned!(self.span()=> #ty: #bound,)
})
}
}
pub fn name(&self) -> Option<&syn::Ident> {
self.rename.as_ref().or(self.ident.as_ref())
}
fn value(&self, i: Option<usize>) -> syn::Expr {
let def = if let Some(i) = i {
if let Some(name) = &self.ident {
parse_quote_spanned!(self.span()=> self.#name)
} else {
let index = syn::Index::from(i);
parse_quote_spanned!(self.span()=> self.#index)
}
} else {
parse_quote_spanned!(self.span()=> value)
};
self.defer.clone().unwrap_or(def)
}
pub fn traverse_by_key(&self, i: usize) -> TokenStream {
if let Some(msg) = &self.deny.traverse {
quote_spanned! { self.span()=> ::core::result::Result::Err(
::miniconf::Traversal::Access(0, #msg).into())
}
} else {
let typ = self.typ();
let imp = self
.with
.traverse
.as_ref()
.map(|i| i.to_token_stream())
.unwrap_or(quote!(<#typ as ::miniconf::TreeKey>::traverse_by_key));
quote_spanned!(self.span()=> #i => #imp(keys, func))
}
}
pub fn traverse_all(&self) -> TokenStream {
let typ = self.typ();
let imp = self
.with
.traverse_all
.as_ref()
.map(|i| i.to_token_stream())
.unwrap_or(quote!(<#typ as ::miniconf::TreeKey>::traverse_all));
quote_spanned!(self.span()=> #imp())
}
pub fn serialize_by_key(&self, i: Option<usize>) -> TokenStream {
if let Some(msg) = &self.deny.serialize {
quote_spanned! { self.span()=> ::core::result::Result::Err(
::miniconf::Traversal::Access(0, #msg).into())
}
} else {
let value = self.value(i);
let imp = self
.with
.serialize
.as_ref()
.map(|p| p.to_token_stream())
.unwrap_or(quote!(#value.serialize_by_key));
quote_spanned! { self.span()=> #imp(keys, ser) }
}
}
pub fn deserialize_by_key(&self, i: Option<usize>) -> TokenStream {
if let Some(msg) = &self.deny.deserialize {
quote_spanned! { self.span()=> ::core::result::Result::Err(
::miniconf::Traversal::Access(0, #msg).into())
}
} else {
let value = self.value(i);
let imp = self
.with
.deserialize
.as_ref()
.map(|p| p.to_token_stream())
.unwrap_or(quote!(#value.deserialize_by_key));
quote_spanned! { self.span()=> #imp(keys, de) }
}
}
pub fn probe_by_key(&self, i: usize) -> TokenStream {
if let Some(msg) = &self.deny.probe {
quote_spanned! { self.span()=> ::core::result::Result::Err(
::miniconf::Traversal::Access(0, #msg).into())
}
} else {
let typ = self.typ();
let imp = self
.with
.probe
.as_ref()
.map(|i| i.to_token_stream())
.unwrap_or(quote!(<#typ as ::miniconf::TreeDeserialize::<'de>>::probe_by_key));
quote_spanned!(self.span()=> #i => #imp(keys, de))
}
}
pub fn ref_any_by_key(&self, i: Option<usize>) -> TokenStream {
if let Some(msg) = &self.deny.ref_any {
quote_spanned! { self.span()=> ::core::result::Result::Err(
::miniconf::Traversal::Access(0, #msg))
}
} else {
let value = self.value(i);
let imp = self
.with
.ref_any
.as_ref()
.map(|p| p.to_token_stream())
.unwrap_or(quote!(#value.ref_any_by_key));
quote_spanned! { self.span()=> #imp(keys) }
}
}
pub fn mut_any_by_key(&self, i: Option<usize>) -> TokenStream {
if let Some(msg) = &self.deny.mut_any {
quote_spanned! { self.span()=> ::core::result::Result::Err(
::miniconf::Traversal::Access(0, #msg))
}
} else {
let value = self.value(i);
let imp = self
.with
.mut_any
.as_ref()
.map(|p| p.to_token_stream())
.unwrap_or(quote!(#value.mut_any_by_key));
quote_spanned! { self.span()=> #imp(keys) }
}
}
}