use crate::generator_state::GeneratorStateInfo;
use crate::hir::{HirFunction, Type};
use crate::rust_gen::CodeGenContext;
use crate::rust_gen::type_gen::rust_type_to_syn;
use anyhow::Result;
use quote::quote;
fn generate_state_fields(
state_info: &GeneratorStateInfo,
ctx: &mut CodeGenContext,
) -> Result<Vec<proc_macro2::TokenStream>> {
state_info
.state_variables
.iter()
.map(|var| {
let field_name = syn::Ident::new(&var.name, proc_macro2::Span::call_site());
let rust_type = ctx.type_mapper.map_type(&var.ty);
let field_type = rust_type_to_syn(&rust_type)?;
Ok(quote! { #field_name: #field_type })
})
.collect()
}
fn generate_param_fields(
func: &HirFunction,
state_info: &GeneratorStateInfo,
ctx: &mut CodeGenContext,
) -> Result<Vec<proc_macro2::TokenStream>> {
func.params
.iter()
.filter(|p| state_info.captured_params.contains(&p.name))
.map(|param| {
let field_name = syn::Ident::new(¶m.name, proc_macro2::Span::call_site());
let rust_type = ctx.type_mapper.map_type(¶m.ty);
let field_type = rust_type_to_syn(&rust_type)?;
Ok(quote! { #field_name: #field_type })
})
.collect()
}
#[inline]
fn extract_generator_item_type(
rust_ret_type: &crate::type_mapper::RustType,
) -> Result<syn::Type> {
rust_type_to_syn(rust_ret_type)
}
fn generate_state_initializers(
state_info: &GeneratorStateInfo,
) -> Vec<proc_macro2::TokenStream> {
state_info
.state_variables
.iter()
.map(|var| {
let field_name = syn::Ident::new(&var.name, proc_macro2::Span::call_site());
let default_value = get_default_value_for_type(&var.ty);
quote! { #field_name: #default_value }
})
.collect()
}
fn generate_param_initializers(
func: &HirFunction,
state_info: &GeneratorStateInfo,
) -> Vec<proc_macro2::TokenStream> {
func.params
.iter()
.filter(|p| state_info.captured_params.contains(&p.name))
.map(|param| {
let field_name = syn::Ident::new(¶m.name, proc_macro2::Span::call_site());
quote! { #field_name: #field_name }
})
.collect()
}
#[inline]
fn default_int() -> proc_macro2::TokenStream {
quote! { 0 }
}
#[inline]
fn default_float() -> proc_macro2::TokenStream {
quote! { 0.0 }
}
#[inline]
fn default_bool() -> proc_macro2::TokenStream {
quote! { false }
}
#[inline]
fn default_string() -> proc_macro2::TokenStream {
quote! { String::new() }
}
#[inline]
fn default_generic() -> proc_macro2::TokenStream {
quote! { Default::default() }
}
#[inline]
fn get_default_value_for_type(ty: &Type) -> proc_macro2::TokenStream {
match ty {
Type::Int => default_int(),
Type::Float => default_float(),
Type::Bool => default_bool(),
Type::String => default_string(),
_ => default_generic(),
}
}
#[inline]
fn generate_state_struct_name(name: &syn::Ident) -> syn::Ident {
let state_struct_name = format!(
"{}State",
name.to_string()
.chars()
.next()
.map(|c| c.to_uppercase().to_string())
.unwrap_or_default()
+ &name.to_string()[1..]
);
syn::Ident::new(&state_struct_name, name.span())
}
#[inline]
fn populate_generator_state_vars(
ctx: &mut CodeGenContext,
state_info: &GeneratorStateInfo,
) {
ctx.generator_state_vars.clear();
for var in &state_info.state_variables {
ctx.generator_state_vars.insert(var.name.clone());
}
for param in &state_info.captured_params {
ctx.generator_state_vars.insert(param.clone());
}
}
#[inline]
fn generate_generator_body(
func: &HirFunction,
ctx: &mut CodeGenContext,
) -> Result<Vec<proc_macro2::TokenStream>> {
use crate::rust_gen::RustCodeGen;
ctx.in_generator = true;
let generator_body_stmts: Vec<_> = func
.body
.iter()
.map(|stmt| stmt.to_rust_tokens(ctx))
.collect::<Result<Vec<_>>>()?;
ctx.in_generator = false;
ctx.generator_state_vars.clear();
Ok(generator_body_stmts)
}
#[inline]
#[allow(clippy::too_many_arguments)] pub fn codegen_generator_function(
func: &HirFunction,
name: &syn::Ident,
generic_params: &proc_macro2::TokenStream,
where_clause: &proc_macro2::TokenStream,
params: &[proc_macro2::TokenStream],
attrs: &[proc_macro2::TokenStream],
rust_ret_type: &crate::type_mapper::RustType,
ctx: &mut CodeGenContext,
) -> Result<proc_macro2::TokenStream> {
let state_info = GeneratorStateInfo::analyze(func);
let state_ident = generate_state_struct_name(name);
let state_fields = generate_state_fields(&state_info, ctx)?;
let param_fields = generate_param_fields(func, &state_info, ctx)?;
let all_fields = [state_fields, param_fields].concat();
let state_inits = generate_state_initializers(&state_info);
let param_inits = generate_param_initializers(func, &state_info);
let all_inits = [state_inits, param_inits].concat();
let state_machine_field = quote! {
state: usize
};
let item_type = extract_generator_item_type(rust_ret_type)?;
populate_generator_state_vars(ctx, &state_info);
let generator_body_stmts = generate_generator_body(func, ctx)?;
Ok(quote! {
#(#attrs)*
#[doc = " Generator state struct"]
#[derive(Debug)]
struct #state_ident {
#state_machine_field,
#(#all_fields),*
}
#[doc = " Generator function - returns Iterator"]
pub fn #name #generic_params(#(#params),*) -> impl Iterator<Item = #item_type> #where_clause {
#state_ident {
state: 0,
#(#all_inits),*
}
}
impl Iterator for #state_ident {
type Item = #item_type;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
0 => {
self.state = 1;
#(#generator_body_stmts)*
None
}
_ => None
}
}
}
})
}