use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use std::cell::Cell;
use syn::Ident;
#[derive(Clone, Debug)]
pub(crate) struct TyArray(pub(crate) Vec<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyTuple(pub(crate) Vec<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyGroup(pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TySlice(pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyFixedArray(pub(crate) Box<Ty>, pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyPrimitive(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyGeneric(pub(crate) Box<Ty>, pub(crate) TyTypeParam);
#[derive(Clone, Debug)]
pub(crate) struct TyTrait(pub(crate) TokenStream, pub(crate) TyTypeParam);
#[derive(Clone, Debug)]
pub(crate) struct TyTypeParam {
pub(crate) params: Vec<(TokenStream, Option<Ty>)>,
pub(crate) bindings: Vec<(TokenStream, TokenStream)>,
pub(crate) where_clauses: Vec<TokenStream>,
}
impl TyTypeParam {
pub(crate) fn single(arg: &Ty) -> Self {
TyTypeParam {
params: vec![(arg.to_token_stream(), None)],
bindings: vec![],
where_clauses: vec![],
}
}
pub(crate) fn push_arg(&mut self, arg: &Ty) {
self.params.push((arg.to_token_stream(), None));
}
pub(crate) fn extend(&mut self, other: TyTypeParam) {
self.params.extend(other.params);
self.bindings.extend(other.bindings);
self.where_clauses.extend(other.where_clauses);
}
}
#[derive(Clone, Debug)]
pub(crate) struct TyCodeBlock(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithCode(pub(crate) Box<Ty>, pub(crate) TokenStream);
#[derive(Copy, Clone, Debug)]
pub(crate) enum TyPrefix {
Ref,
RefMut,
PtrConst,
PtrMut,
SelfType,
Fn,
Unsafe,
}
#[derive(Clone, Debug)]
pub(crate) struct TyModified(pub(crate) TyPrefix, pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyFn(pub(crate) Vec<Ty>, pub(crate) Option<Box<Ty>>);
#[derive(Clone, Debug)]
pub(crate) struct TyUnsafe(pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyAttr(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithAttr(pub(crate) TyAttr, pub(crate) Box<Ty>);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TyNum(pub(crate) u8);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TyRange {
pub(crate) start: u8,
pub(crate) end: u8,
pub(crate) inclusive: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct TyWithTrait(pub(crate) TyTrait, pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyWithType(pub(crate) TyTypeParam, pub(crate) Box<Ty>);
#[derive(Clone, Debug)]
pub(crate) struct TyError(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) enum Ty {
Array(TyArray),
Tuple(TyTuple),
Group(TyGroup),
Slice(TySlice),
FixedArray(TyFixedArray),
Primitive(TyPrimitive),
Generic(TyGeneric),
Trait(TyTrait),
TypeParam(TyTypeParam),
CodeBlock(TyCodeBlock),
Prefix(TyPrefix),
Modified(TyModified),
Fn(TyFn),
Unsafe(TyUnsafe),
Attr(TyAttr),
WithAttr(TyWithAttr),
WithTrait(TyWithTrait),
WithType(TyWithType),
WithCode(TyWithCode),
Num(TyNum),
Range(TyRange),
Error(TyError),
}
impl Ty {
pub(crate) fn expand(self) -> Result<Vec<Ty>, Ty> {
match self {
Ty::Array(ty) => Ok(ty.0),
Ty::WithCode(wc) => match (*wc.0).expand() {
Ok(expanded) => Ok(expanded
.into_iter()
.map(|inner| {
Ty::WithCode(TyWithCode(
Box::new(inner),
wc.1.clone(),
))
})
.collect()),
Err(leaf) => {
Err(Ty::WithCode(TyWithCode(Box::new(leaf), wc.1)))
},
},
Ty::Group(g) => (*g.0).expand(),
other => Err(other),
}
}
}
macro_rules! impl_from_for_ty {
($($struct:ident => $variant:ident),* $(,)?) => {
$(
impl From<$struct> for Ty {
fn from(value: $struct) -> Self {
Ty::$variant(value)
}
}
impl From<$struct> for Box<Ty> {
fn from(value: $struct) -> Self {
Box::new(Ty::$variant(value))
}
}
)*
};
}
impl_from_for_ty! {
TyArray => Array,
TyTuple => Tuple,
TyGroup => Group,
TySlice => Slice,
TyFixedArray => FixedArray,
TyPrimitive => Primitive,
TyGeneric => Generic,
TyTrait => Trait,
TyTypeParam => TypeParam,
TyCodeBlock => CodeBlock,
TyPrefix => Prefix,
TyModified => Modified,
TyFn => Fn,
TyUnsafe => Unsafe,
TyAttr => Attr,
TyWithAttr => WithAttr,
TyWithTrait => WithTrait,
TyWithType => WithType,
TyWithCode => WithCode,
TyNum => Num,
TyRange => Range,
TyError => Error,
}
#[derive(Copy, Clone)]
pub(crate) enum Op {
Semi,
Comma,
Dash,
Caret,
Prim,
}
impl Op {
pub(crate) fn next(self) -> Option<Op> {
match self {
Op::Semi => Some(Op::Comma),
Op::Comma => Some(Op::Dash),
Op::Dash => Some(Op::Caret),
Op::Caret => Some(Op::Prim),
Op::Prim => None,
}
}
pub(crate) fn stop_chars(self) -> &'static [char] {
match self {
Op::Semi => &[',', ';'],
Op::Comma => &[','],
Op::Dash => &['-', ','],
Op::Caret => &['^', '-', ','],
Op::Prim => &[],
}
}
}
thread_local! {
static FRESH_COUNTER: Cell<usize> = 0.into();
}
pub(crate) fn reset_fresh_counter() {
FRESH_COUNTER.set(0);
}
pub(crate) fn fresh_param() -> TokenStream {
FRESH_COUNTER.with(|c| {
let n = c.get();
c.set(n + 1);
let name = format!("_Param_{}_BatchGen_", n);
let ident = Ident::new(&name, proc_macro2::Span::call_site());
quote!(#ident)
})
}