use crate::with_origin::FXOrig;
use crate::FXInto;
use crate::FXProp;
use crate::FXSetState;
use crate::FXTryFrom;
use darling::ast::NestedMeta;
use darling::FromMeta;
use darling::ToTokens;
use getset::Getters;
use proc_macro2::TokenStream;
use quote::quote;
use quote::quote_spanned;
use std::ops::Deref;
use std::ops::DerefMut;
use syn::spanned::Spanned;
use syn::Lit;
use syn::Meta;
pub trait FromNestAttr<const WITH_LITERALS: bool = true>: FromMeta {
fn for_keyword(path: &syn::Path) -> darling::Result<Self> {
Err(darling::Error::custom("Can't be used as plain keyword, arguments required").with_span(path))
}
fn with_literals() -> bool {
WITH_LITERALS
}
fn set_literals(self, literals: &Vec<Lit>) -> darling::Result<Self> {
if WITH_LITERALS {
Err(darling::Error::custom(format!(
"{} must implement set_literals() method",
std::any::type_name_of_val(&self)
))
.with_span(&literals[0]))
}
else {
self.no_literals(literals)
}
}
fn no_literals(&self, literals: &Vec<Lit>) -> darling::Result<Self> {
Err(darling::Error::custom("Literal values are not supported here").with_span(&literals[0]))
}
}
#[derive(Debug, Clone, Getters)]
pub struct FXNestingAttr<T, const WITH_LITERALS: bool = true>
where
T: FromNestAttr<WITH_LITERALS>,
{
inner: T,
path: Option<syn::Path>,
orig: Meta,
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> FXNestingAttr<T, WITH_LITERALS> {
fn extract_literals(item: &Meta) -> darling::Result<Self> {
let Meta::List(list) = item
else {
return Err(darling::Error::custom(format!(
"internal problem: didn't expect {} here",
std::any::type_name_of_val(&item)
))
.with_span(item));
};
let nlist = NestedMeta::parse_meta_list(list.tokens.clone())?;
let mut non_lit: Vec<NestedMeta> = vec![];
let mut literals: Vec<Lit> = vec![];
for nested in nlist {
match nested {
NestedMeta::Meta(ref meta) => {
non_lit.push(NestedMeta::Meta(meta.clone()));
}
NestedMeta::Lit(ref lit) => {
literals.push(lit.clone());
}
}
}
let mut fattr = T::from_list(&non_lit)?;
if literals.len() > 0 {
fattr = fattr.set_literals(&literals)?;
}
Ok(Self {
inner: fattr,
path: None,
orig: item.clone(),
})
}
fn from_path_tokens<P: Into<syn::Path>, TT: ToTokens>(path: P, tokens: TT) -> darling::Result<Self> {
let path = path.into();
let orig = syn::parse2::<syn::Meta>(quote_spanned! {tokens.span()=> #path(#tokens) })?;
Ok(Self::from_meta(&orig)?)
}
pub fn from_tokens<TT: ToTokens>(toks: TT) -> darling::Result<Self> {
let orig = syn::parse2::<syn::Meta>(quote_spanned! {toks.span()=> #toks })?;
Ok(Self::from_meta(&orig)?)
}
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> FXOrig<syn::Meta> for FXNestingAttr<T, WITH_LITERALS> {
#[inline]
fn orig(&self) -> Option<&syn::Meta> {
Some(&self.orig)
}
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> FromMeta for FXNestingAttr<T, WITH_LITERALS> {
fn from_meta(item: &Meta) -> darling::Result<Self> {
Ok(match &item {
Meta::List(_) => {
if T::with_literals() {
Self::extract_literals(item)?
}
else {
Self {
inner: T::from_meta(item)?,
path: None,
orig: item.clone(),
}
}
}
Meta::Path(ref path) => Self {
inner: T::for_keyword(path)?,
path: None,
orig: item.clone(),
},
_ => Self {
inner: T::from_meta(item)?,
path: None,
orig: item.clone(),
},
})
}
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> Deref for FXNestingAttr<T, WITH_LITERALS> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.inner
}
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> DerefMut for FXNestingAttr<T, WITH_LITERALS> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> AsRef<T> for FXNestingAttr<T, WITH_LITERALS>
where
<FXNestingAttr<T, WITH_LITERALS> as Deref>::Target: AsRef<T>,
{
#[inline]
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T: FromNestAttr<WITH_LITERALS>, const WITH_LITERALS: bool> AsMut<T> for FXNestingAttr<T, WITH_LITERALS> {
#[inline]
fn as_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T: FromNestAttr<WITH_LITERALS> + ToTokens, const WITH_LITERALS: bool> ToTokens
for FXNestingAttr<T, WITH_LITERALS>
{
#[inline]
fn to_tokens(&self, tokens: &mut TokenStream) {
let inner_toks = self.inner.to_token_stream();
let path = self.path.as_ref().unwrap_or_else(|| self.orig.path());
tokens.extend(quote! { #path(#inner_toks) });
}
}
impl<T, P, U, const WITH_LITERALS: bool> FXTryFrom<(P, U)> for FXNestingAttr<T, WITH_LITERALS>
where
(P, U): FXInto<syn::Path>,
U: ToTokens + Clone,
T: FXTryFrom<U> + FromNestAttr<WITH_LITERALS>,
{
type Error = darling::Error;
#[inline]
fn fx_try_from(value: (P, U)) -> darling::Result<Self> {
let v1 = value.1.clone();
Ok(Self::from_path_tokens(value.fx_into(), v1)?)
}
}
impl<T, P, U, const WITH_LITERALS: bool> FXTryFrom<(P, U)> for Option<FXNestingAttr<T, WITH_LITERALS>>
where
(P, U): FXInto<syn::Path>,
U: ToTokens + Clone,
T: FXTryFrom<U> + FromNestAttr<WITH_LITERALS>,
{
type Error = darling::Error;
#[inline]
fn fx_try_from(value: (P, U)) -> darling::Result<Self> {
let v1 = value.1.clone();
Ok(Some(FXNestingAttr::from_path_tokens(value.fx_into(), v1)?))
}
}
impl<T, const WITH_LITERALS: bool> FXSetState for FXNestingAttr<T, WITH_LITERALS>
where
T: FXSetState + FromNestAttr<WITH_LITERALS>,
{
#[inline(always)]
fn is_set(&self) -> FXProp<bool> {
self.inner.is_set().respan(self.orig_span())
}
}
impl<T, U, const WITH_LITERALS: bool> From<FXNestingAttr<T, WITH_LITERALS>> for Option<FXProp<U>>
where
Option<FXProp<U>>: From<T>,
T: FromNestAttr<WITH_LITERALS>,
{
#[inline(always)]
fn from(value: FXNestingAttr<T, WITH_LITERALS>) -> Self {
let orig_span = value.orig_span();
let p: Self = value.inner.into();
if let Some(p) = p {
Some(p.respan(orig_span))
}
else {
None
}
}
}
impl<T, U, const WITH_LITERALS: bool> From<&FXNestingAttr<T, WITH_LITERALS>> for Option<FXProp<U>>
where
Option<FXProp<U>>: for<'a> From<&'a T>,
T: FromNestAttr<WITH_LITERALS>,
{
#[inline(always)]
fn from(value: &FXNestingAttr<T, WITH_LITERALS>) -> Self {
let p: Self = (&value.inner).into();
if let Some(p) = p {
Some(p.respan(value.orig_span()))
}
else {
None
}
}
}