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 TyPrimitiveArray(
pub(crate) Option<Box<Ty>>,
pub(crate) Option<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)>,
}
impl TyTypeParam {
pub(crate) fn single(arg: &Ty) -> Self {
TyTypeParam {
params: vec![(arg.to_token_stream(), None)],
bindings: 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);
}
}
#[derive(Clone, Debug)]
pub(crate) struct TyCodeBlock(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithCode(pub(crate) Option<Box<Ty>>, pub(crate) TyCodeBlock);
#[derive(Copy, Clone, Debug)]
pub(crate) enum TyPrefix {
Ref,
RefMut,
PtrConst,
PtrMut,
SelfType,
Unsafe,
}
#[derive(Clone, Debug)]
pub(crate) struct TyWithPrefix(pub(crate) TyPrefix, pub(crate) Option<Box<Ty>>);
#[derive(Clone, Debug)]
pub(crate) struct TyFn(
pub(crate) Option<Vec<Ty>>,
pub(crate) Option<Box<Ty>>,
pub(crate) bool,
);
#[derive(Clone, Debug)]
pub(crate) struct TyAttr(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithAttr(pub(crate) TyAttr, pub(crate) Option<Box<Ty>>);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TyNum(pub(crate) usize);
#[derive(Copy, Clone, Debug)]
pub(crate) struct TyRange {
pub(crate) start: usize,
pub(crate) end: usize,
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) struct TyWhere(pub(crate) TokenStream);
#[derive(Clone, Debug)]
pub(crate) struct TyWithWhere(pub(crate) Option<Box<Ty>>, pub(crate) TyWhere);
#[derive(Clone, Debug)]
pub(crate) enum Ty {
Array(TyArray),
Tuple(TyTuple),
Group(TyGroup),
PrimitiveArray(TyPrimitiveArray),
Primitive(TyPrimitive),
Generic(TyGeneric),
Trait(TyTrait),
TypeParam(TyTypeParam),
Fn(TyFn),
WithPrefix(TyWithPrefix),
WithAttr(TyWithAttr),
WithTrait(TyWithTrait),
WithType(TyWithType),
WithCode(TyWithCode),
WithWhere(TyWithWhere),
Num(TyNum),
Range(TyRange),
Error(TyError),
}
pub(crate) enum Expand {
Leaf(Ty),
Many(Vec<Ty>),
}
fn expand_wrapped<F>(make: F, inner: Option<Box<Ty>>) -> Expand
where
F: Fn(Option<Box<Ty>>) -> Ty,
{
match inner {
Some(i) => match i.expand() {
Expand::Many(v) => {
Expand::Many(v.into_iter().map(|e| make(Some(e.into()))).collect())
}
Expand::Leaf(l) => Expand::Leaf(make(Some(l.into()))),
},
None => Expand::Leaf(make(None)),
}
}
fn expand_rebuild<F>(make: F, inner: Ty) -> Expand
where
F: Fn(Box<Ty>) -> Ty,
{
match inner.expand() {
Expand::Many(v) => {
Expand::Many(v.into_iter().map(|e| make(e.into())).collect())
}
Expand::Leaf(l) => Expand::Leaf(make(l.into())),
}
}
impl Ty {
pub(crate) fn expand(self) -> Expand {
match self {
Ty::Array(ty) => Expand::Many(ty.0),
Ty::WithCode(wc) => {
let TyWithCode(inner, payload) = wc;
expand_wrapped(move |i| TyWithCode(i, payload.clone()).into(), inner)
}
Ty::WithWhere(ww) => {
let TyWithWhere(inner, payload) = ww;
expand_wrapped(move |i| TyWithWhere(i, payload.clone()).into(), inner)
}
Ty::WithType(wt) => {
let TyWithType(params, inner) = wt;
expand_rebuild(move |e| TyWithType(params.clone(), e).into(), *inner)
}
Ty::WithTrait(wt) => {
let TyWithTrait(t, inner) = wt;
expand_rebuild(move |e| TyWithTrait(t.clone(), e).into(), *inner)
}
Ty::WithAttr(wa) => {
let TyWithAttr(attr, inner) = wa;
expand_wrapped(move |i| TyWithAttr(attr.clone(), i).into(), inner)
}
Ty::WithPrefix(wp) => {
let TyWithPrefix(prefix, inner) = wp;
expand_wrapped(move |i| TyWithPrefix(prefix, i).into(), inner)
}
Ty::Group(g) => (*g.0).expand(),
other => Expand::Leaf(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(value.into())
}
}
impl From<$struct> for Option<Ty> {
fn from(value: $struct) -> Self {
Some(value.into())
}
}
impl From<$struct> for Option<Box<Ty>> {
fn from(value: $struct) -> Self {
Some(value.into())
}
}
)*
};
}
impl From<Ty> for Option<Box<Ty>> {
fn from(ty: Ty) -> Self {
Some(ty.into())
}
}
impl_from_for_ty! {
TyArray => Array,
TyTuple => Tuple,
TyGroup => Group,
TyPrimitiveArray => PrimitiveArray,
TyPrimitive => Primitive,
TyGeneric => Generic,
TyTrait => Trait,
TyTypeParam => TypeParam,
TyFn => Fn,
TyWithPrefix => WithPrefix,
TyWithAttr => WithAttr,
TyWithTrait => WithTrait,
TyWithType => WithType,
TyWithCode => WithCode,
TyWithWhere => WithWhere,
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 => &[],
}
}
}
pub(crate) const MAX_EXPAND: usize = 1024;
pub(crate) fn count_leaves(ty: &Ty) -> usize {
match ty {
Ty::Array(a) => a.0.iter().map(count_leaves).sum(),
_ => 1,
}
}
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)
})
}