use super::*;
use crate::{Ident, Span, TokenStream};
use quote::quote;
pub fn facet_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
let parsed = parse_function_signature(item);
let result = generate_function_shape(parsed);
result.to_string().parse().unwrap()
}
pub fn fn_shape(input: TokenStream) -> TokenStream {
let parsed = parse_fn_shape_input(input);
let result = generate_fn_shape_call(parsed);
result.to_string().parse().unwrap()
}
pub fn generate_function_shape(parsed: FunctionSignature) -> TokenStream {
let fn_name = parsed.name;
let generics = parsed.generics;
let params = parsed.parameters;
let return_type = parsed.return_type;
let body = parsed.body;
let hidden_mod = Ident::new(&format!("__fn_shape_{fn_name}"), Span::call_site());
let shape_name = Ident::new(
&format!("{}_SHAPE", fn_name.to_string().to_uppercase()),
Span::call_site(),
);
let defs: Vec<_> = params
.iter()
.map(|p| {
let name = &p.name;
let ty = &p.param_type_tokens();
quote! { #name: #ty }
})
.collect();
let idents: Vec<_> = params
.iter()
.map(|p| {
let name = &p.name;
quote! { #name }
})
.collect();
let types: Vec<_> = params
.iter()
.map(|p| {
let ty = &p.param_type_tokens();
quote! { #ty }
})
.collect();
let names: Vec<_> = params
.iter()
.map(|p| p.name.to_string())
.collect::<Vec<_>>();
let arity = params.len();
let fn_name_str = fn_name.to_string();
let generics_type = if let Some(ref generics_ts) = generics {
extract_type_params(generics_ts.clone())
} else {
quote! { () }
};
let documentation_lines: Vec<_> = parsed
.documentation
.iter()
.map(|doc| quote! { #doc })
.collect();
let shape_definition = quote! {
pub fn shape #generics () -> FunctionShape<( #( #types ),* ), #return_type, #generics_type> {
FunctionShape::new(
#fn_name_str,
#arity,
&[ #( #names ),* ],
&[ #( #documentation_lines ),* ]
)
}
};
let out = quote! {
#[allow(non_snake_case)]
mod #hidden_mod {
use super::*;
pub(super) fn inner #generics ( #( #defs ),* ) -> #return_type #body
#[derive(Debug, Clone)]
pub struct FunctionShape<Args, Ret, Generics = ()> {
pub name: &'static str,
pub param_count: usize,
pub param_names: &'static [&'static str],
pub documentation: &'static [&'static str],
_args: core::marker::PhantomData<Args>,
_ret: core::marker::PhantomData<Ret>,
_generics: core::marker::PhantomData<Generics>,
}
impl<Args, Ret, Generics> FunctionShape<Args, Ret, Generics> {
pub const fn new(
name: &'static str,
param_count: usize,
param_names: &'static [&'static str],
documentation: &'static [&'static str],
) -> Self {
Self {
name,
param_count,
param_names,
documentation,
_args: core::marker::PhantomData,
_ret: core::marker::PhantomData,
_generics: core::marker::PhantomData,
}
}
}
#shape_definition
}
pub fn #fn_name #generics ( #( #defs ),* ) -> #return_type {
#hidden_mod::inner( #( #idents ),* )
}
pub use #hidden_mod::shape as #shape_name;
};
out
}
fn generate_fn_shape_call(parsed: ParsedFnShapeInput) -> TokenStream {
let fn_name = parsed.name;
let generic_args = parsed.generics;
let shape_name = Ident::new(
&format!("{}_SHAPE", fn_name.to_string().to_uppercase()),
Span::call_site(),
);
if let Some(generics) = generic_args {
quote! { #shape_name::#generics() }
} else {
quote! { #shape_name() }
}
}