use syn::{
token::{Colon, Comma, Const, Star},
GenericParam, Generics,
};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use crate::utils::NoTokens;
#[derive(Debug, Copy, Clone)]
pub struct GenParamsIn<'a, AL = NoTokens> {
pub generics: &'a Generics,
pub in_what: InWhat,
pub unsized_types: bool,
pub with_bounds: bool,
skip_lifetimes: bool,
after_lifetimes: Option<AL>,
after_types: Option<AL>,
skip_consts: bool,
skip_unbounded: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum InWhat {
ImplHeader,
ItemDecl,
ItemUse,
DummyStruct,
}
impl<'a> GenParamsIn<'a> {
#[allow(dead_code)]
pub fn new(generics: &'a Generics, in_what: InWhat) -> Self {
Self {
generics,
in_what,
unsized_types: false,
with_bounds: true,
skip_lifetimes: false,
after_lifetimes: None,
after_types: None,
skip_consts: false,
skip_unbounded: false,
}
}
}
impl<'a, AL> GenParamsIn<'a, AL> {
pub fn with_after_lifetimes(
generics: &'a Generics,
in_what: InWhat,
after_lifetimes: AL,
) -> Self {
Self {
generics,
in_what,
unsized_types: false,
with_bounds: true,
skip_lifetimes: false,
after_lifetimes: Some(after_lifetimes),
after_types: None,
skip_consts: false,
skip_unbounded: false,
}
}
pub fn with_after_types(generics: &'a Generics, in_what: InWhat, after_types: AL) -> Self {
Self {
generics,
in_what,
unsized_types: false,
with_bounds: true,
skip_lifetimes: false,
after_lifetimes: None,
after_types: Some(after_types),
skip_consts: false,
skip_unbounded: false,
}
}
pub fn set_no_bounds(&mut self) {
self.with_bounds = false;
}
#[allow(dead_code)]
pub fn set_unsized_types(&mut self) {
self.unsized_types = true;
}
pub fn skip_lifetimes(&mut self) {
self.skip_lifetimes = true;
}
pub fn skip_consts(&mut self) {
self.skip_consts = true;
}
pub fn skip_unbounded(&mut self) {
self.skip_unbounded = true;
self.skip_consts();
self.skip_lifetimes();
}
pub fn skips_unbounded(&self) -> bool {
self.skip_unbounded
}
pub fn outputs_bounds(&self) -> bool {
self.with_bounds && matches!(self.in_what, InWhat::ImplHeader | InWhat::ItemDecl)
}
pub fn are_types_unsized(&self) -> bool {
self.unsized_types && matches!(self.in_what, InWhat::ItemDecl | InWhat::ImplHeader)
}
}
impl<'a, AL> ToTokens for GenParamsIn<'a, AL>
where
AL: ToTokens,
{
fn to_tokens(&self, ts: &mut TokenStream) {
let with_bounds = self.outputs_bounds();
let with_default = self.in_what == InWhat::ItemDecl;
let in_dummy_struct = self.in_what == InWhat::DummyStruct;
let unsized_types = self.are_types_unsized();
let mut iter = self.generics.params.iter().peekable();
let comma = Comma::default();
let brace = syn::token::Brace::default();
if self.skip_lifetimes {
while let Some(GenericParam::Lifetime { .. }) = iter.peek() {
iter.next();
}
} else {
while let Some(GenericParam::Lifetime(gen)) = iter.peek() {
iter.next();
if in_dummy_struct {
syn::token::And::default().to_tokens(ts);
gen.lifetime.to_tokens(ts);
syn::token::Paren::default().surround(ts, |_| ());
} else {
gen.lifetime.to_tokens(ts);
if with_bounds {
gen.colon_token.to_tokens(ts);
gen.bounds.to_tokens(ts);
}
}
comma.to_tokens(ts);
}
}
self.after_lifetimes.to_tokens(ts);
while let Some(GenericParam::Type(gen)) = iter.peek() {
iter.next();
if gen.bounds.is_empty() && self.skip_unbounded {
continue;
}
if in_dummy_struct {
Star::default().to_tokens(ts);
Const::default().to_tokens(ts);
}
gen.ident.to_tokens(ts);
if (with_bounds && gen.colon_token.is_some()) || unsized_types {
Colon::default().to_tokens(ts);
if unsized_types {
quote!(?Sized+).to_tokens(ts);
}
if with_bounds {
gen.bounds.to_tokens(ts);
}
}
if with_default {
gen.eq_token.to_tokens(ts);
gen.default.to_tokens(ts);
}
comma.to_tokens(ts);
}
self.after_types.to_tokens(ts);
if !in_dummy_struct && !self.skip_consts {
while let Some(GenericParam::Const(gen)) = iter.peek() {
iter.next();
if self.in_what != InWhat::ItemUse {
gen.const_token.to_tokens(ts);
}
if self.in_what == InWhat::ItemUse {
brace.surround(ts, |ts| {
gen.ident.to_tokens(ts);
});
} else {
gen.ident.to_tokens(ts);
}
if self.in_what != InWhat::ItemUse {
Colon::default().to_tokens(ts);
gen.ty.to_tokens(ts);
}
if with_default {
gen.eq_token.to_tokens(ts);
gen.default.to_tokens(ts);
}
comma.to_tokens(ts);
}
}
}
}