use crate::ast::*;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
pub(crate) fn params_to_tokens(base: &TokenStream, tp: &TyTypeParam) -> TokenStream {
let mut all = tp.params.iter().map(|(name, _)| name.clone()).collect::<Vec<_>>();
for (name, value) in &tp.bindings {
all.push(quote!(#name = #value));
}
if all.is_empty() {
return base.clone();
}
quote!(#base < #(#all),* >)
}
pub(crate) fn render_param(name: &TokenStream, bound: Option<&Ty>) -> TokenStream {
match bound {
Some(b) => {
let b_tokens = b.to_token_stream();
quote!(#name: #b_tokens)
}
None => name.clone(),
}
}
pub(crate) fn params_to_tokens_no_base(tp: &TyTypeParam) -> TokenStream {
let mut all = vec![];
for (name, bound) in &tp.params {
all.push(render_param(name, bound.as_ref()));
}
for (name, value) in &tp.bindings {
all.push(quote!(#name = #value));
}
if all.is_empty() {
return quote!();
}
quote!(<#(#all),*>)
}
fn render_optional(
inner: Option<&Ty>, payload: TokenStream, inner_first: bool,
) -> TokenStream {
match inner {
Some(i) => {
let inner = i.to_token_stream();
if inner_first {
quote!(#inner #payload)
} else {
quote!(#payload #inner)
}
}
None => payload,
}
}
impl ToTokens for Ty {
fn to_tokens(&self, out: &mut TokenStream) {
out.extend(match &self.kind {
TyKind::Primitive(p) => p.0.clone(),
TyKind::Generic(g) => params_to_tokens(&g.0.to_token_stream(), &g.1),
TyKind::Trait(t) => params_to_tokens(&t.0, &t.1),
TyKind::Array(a) => {
let elems =
a.0.iter().map(|e| e.to_token_stream()).collect::<Vec<_>>();
quote!([#(#elems),*])
}
TyKind::Tuple(t) => {
let elems =
t.0.iter().map(|e| e.to_token_stream()).collect::<Vec<_>>();
quote!((#(#elems,)*))
}
TyKind::Group(g) => {
let inner = g.0.to_token_stream();
quote!((#inner))
}
TyKind::PrimitiveArray(pa) => match (&pa.0, &pa.1) {
(Some(elem), None) => {
let inner = elem.to_token_stream();
quote!([#inner])
}
(Some(elem), Some(size)) => {
let inner = elem.to_token_stream();
quote!([#inner; #size])
}
(None, _) => quote!([]),
},
TyKind::WithPrefix(wp) => {
render_optional(wp.1.as_deref(), prefix_token(wp.0), false)
}
TyKind::Fn(f) => {
let u = f.2.then_some(quote!(unsafe));
match &f.0 {
Some(params) => {
let params = params
.iter()
.map(|p| p.to_token_stream())
.collect::<Vec<_>>();
match &f.1 {
Some(ret) => {
let ret_tokens = ret.to_token_stream();
quote!(#u fn(#(#params),*) -> #ret_tokens)
}
None => quote!(#u fn(#(#params),*)),
}
}
None => quote!(#u fn),
}
}
TyKind::TypeParam(tp) => params_to_tokens_no_base(tp),
TyKind::WithAttr(w) => {
let stream = &w.0.0;
render_optional(w.1.as_deref(), quote!(#[#stream]), false)
}
TyKind::Num(n) => {
let lit = proc_macro2::Literal::usize_unsuffixed(n.0);
quote!(#lit)
}
TyKind::Range(r) => {
let start = proc_macro2::Literal::usize_unsuffixed(r.start);
let end = proc_macro2::Literal::usize_unsuffixed(r.end);
if r.inclusive {
quote!(#start ..= #end)
} else {
quote!(#start .. #end)
}
}
TyKind::WithTrait(wt) => {
let trait_tokens = params_to_tokens(&wt.0.0, &wt.0.1);
let inner = wt.1.to_token_stream();
quote!(#trait_tokens #inner)
}
TyKind::WithType(wt) => {
let tp_tokens = params_to_tokens_no_base(&wt.0);
let inner = wt.1.to_token_stream();
quote!(#tp_tokens #inner)
}
TyKind::WithCode(wc) => {
let stream = &wc.1.0;
render_optional(wc.0.as_deref(), quote!({#stream}), true)
}
TyKind::WithWhere(ww) => {
let stream = &ww.1.0;
render_optional(ww.0.as_deref(), quote!(where #stream), true)
}
TyKind::Error(e) => e.0.clone(),
})
}
}
fn prefix_token(prefix: TyPrefix) -> TokenStream {
match prefix {
TyPrefix::Ref => quote!(&),
TyPrefix::RefMut => quote!(&mut),
TyPrefix::PtrConst => quote!(*const),
TyPrefix::PtrMut => quote!(*mut),
TyPrefix::SelfType => quote!(self),
TyPrefix::Unsafe => quote!(unsafe),
}
}