use darling::{Error, FromMeta};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::punctuated::Punctuated;
use syn::token::Bracket;
use syn::{
AttrStyle, Attribute, Expr, ExprLit, GenericArgument, GenericParam, Ident, Lit, LitBool,
LitStr, Meta, MetaNameValue, Pat, PathArguments, ReturnType, Token, Type, TypePath,
WhereClause,
};
use crate::model::FieldTransform;
#[derive(Debug, Clone, Copy)]
pub struct ImplGenerics<'a>(pub &'a Punctuated<GenericParam, Token![,]>);
#[derive(Debug, Clone, Copy)]
pub struct TypeGenerics<'a>(pub &'a Punctuated<GenericParam, Token![,]>);
#[derive(Debug, Clone, Copy)]
pub struct StructGenerics<'a>(pub &'a Punctuated<GenericParam, Token![,]>);
impl ToTokens for ImplGenerics<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
for generic in self.0.pairs() {
match generic.into_value() {
GenericParam::Lifetime(param) => param.to_tokens(tokens),
GenericParam::Type(param) => {
param.ident.to_tokens(tokens);
param.colon_token.to_tokens(tokens);
param.bounds.to_tokens(tokens);
},
GenericParam::Const(param) => {
param.const_token.to_tokens(tokens);
param.ident.to_tokens(tokens);
param.colon_token.to_tokens(tokens);
param.ty.to_tokens(tokens);
},
}
<Token![,]>::default().to_tokens(tokens);
}
}
}
impl ToTokens for TypeGenerics<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
for generic in self.0.pairs() {
match generic.into_value() {
GenericParam::Lifetime(param) => param.lifetime.to_tokens(tokens),
GenericParam::Type(param) => param.ident.to_tokens(tokens),
GenericParam::Const(param) => param.ident.to_tokens(tokens),
}
<Token![,]>::default().to_tokens(tokens);
}
}
}
impl ToTokens for StructGenerics<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
for generic in self.0.pairs() {
generic.into_value().to_tokens(tokens);
<Token![,]>::default().to_tokens(tokens);
}
}
}
#[derive(Debug)]
pub enum BoolOr<T> {
Bool(bool),
Value(T),
}
impl<T: FromMeta> FromMeta for BoolOr<T> {
fn from_expr(expr: &Expr) -> darling::Result<Self> {
if let Expr::Lit(ExprLit {
lit: Lit::Bool(LitBool { value, .. }),
..
}) = *expr
{
return Ok(BoolOr::Bool(value));
}
T::from_expr(expr).map(BoolOr::Value)
}
}
#[derive(Default, Debug)]
pub struct AnyItem(());
impl FromMeta for AnyItem {
fn from_meta(_item: &Meta) -> darling::Result<Self> {
Ok(Self(()))
}
}
pub fn into_write_errors(acc: darling::error::Accumulator) -> TokenStream {
Error::multiple(acc.into_inner()).write_errors()
}
pub fn iter_doc_attrs(attrs: &[Attribute]) -> impl Iterator<Item = &Attribute> {
attrs.iter().filter(|f| f.path().is_ident("doc"))
}
pub fn doc_str_attr(lit: &str) -> Attribute {
Attribute {
pound_token: <Token![#]>::default(),
style: AttrStyle::Outer,
bracket_token: Bracket::default(),
meta: Meta::NameValue(MetaNameValue {
path: simple_ident("doc").into(),
eq_token: <Token![=]>::default(),
value: lit_str_expr(lit),
}),
}
}
pub fn find_deprecated(attrs: &[Attribute]) -> Option<&Attribute> {
attrs.iter().find(|a| a.path().is_ident("deprecated"))
}
pub fn allow_deprecated(attr: Option<&Attribute>) -> Option<TokenStream> {
attr.map(|_| quote::quote! { #[allow(deprecated)] })
}
pub fn simple_ident(ident: &str) -> Ident {
Ident::new(ident, Span::call_site())
}
fn first_punct<T, P>(p: &Punctuated<T, P>) -> Option<&T> {
p.pairs().next().map(|p| p.into_value())
}
fn last_punct<T, P>(p: &Punctuated<T, P>) -> Option<&T> {
p.pairs().next_back().map(|p| p.into_value())
}
pub fn first_generic_arg(mut ty: &Type) -> Option<&Type> {
fn inner(ty: &TypePath) -> Option<&Type> {
let seg = last_punct(&ty.path.segments)?;
if let PathArguments::AngleBracketed(a) = &seg.arguments {
if let GenericArgument::Type(ty) = first_punct(&a.args)? {
return Some(ty);
}
}
None
}
loop {
match ty {
Type::Group(t) => ty = &t.elem,
Type::Paren(t) => ty = &t.elem,
Type::Path(t) => break inner(t),
_ => break None,
}
}
}
pub fn empty_where_clause() -> WhereClause {
WhereClause {
where_token: <Token![where]>::default(),
predicates: Punctuated::new(),
}
}
pub fn lit_str_expr(lit: &str) -> Expr {
Expr::Lit(ExprLit {
lit: Lit::Str(LitStr::new(lit, Span::call_site())),
attrs: Vec::new(),
})
}
pub fn unwrap_expr(mut expr: Expr) -> Expr {
loop {
match expr {
Expr::Group(g) => expr = *g.expr,
Expr::Paren(g) => expr = *g.expr,
o => break o,
}
}
}
pub fn to_field_transform(value: Expr, acc: &mut darling::error::Accumulator) -> FieldTransform {
let value = unwrap_expr(value);
let Expr::Closure(value) = value else {
let transform = FieldTransform {
lifetimes: None,
inputs: vec![syn::parse_quote!(invalid_setter: _)],
body: syn::parse_quote!(invalid_setter),
};
acc.push(Error::custom("expected closure").with_span(&value));
return transform;
};
for attr in &value.attrs {
let err = Error::custom("closure must not have attributes");
acc.push(err.with_span(attr));
}
if let Some(constness) = &value.constness {
let err = Error::custom("closure must not have constness, it is implicitly const here");
acc.push(err.with_span(constness));
}
if let Some(movability) = &value.movability {
let err = Error::custom("closure must not be static");
acc.push(err.with_span(movability));
}
if let Some(asyncness) = &value.asyncness {
let err = Error::custom("closure must not be async");
acc.push(err.with_span(asyncness));
}
if let Some(capture) = &value.capture {
let err = Error::custom("closure cannot have captures");
acc.push(err.with_span(capture));
}
if !matches!(value.output, ReturnType::Default) {
let err = Error::custom("closure must not have an explicit return type");
acc.push(err.with_span(&value.output));
}
let inputs = value
.inputs
.into_pairs()
.map(|pat| match pat.into_value() {
Pat::Type(pat_type) => pat_type,
pat => {
let err = Error::custom("closure inputs must all have an explicit type");
acc.push(err.with_span(&pat));
syn::parse_quote!(#pat: _)
},
})
.collect();
let lifetimes = value.lifetimes.map(|l| {
let lifetimes = l.lifetimes;
quote::quote! { < #lifetimes > }
});
FieldTransform {
lifetimes,
inputs,
body: value.body,
}
}