// Doc Not Optimize use proc_macro::TokenStream; use quote::quote; /// Entry point for `gen_program!()`. /// /// Generates the `Next` type alias, `Routable` impl for `ChainProcess`, /// and delegates to `program_comp_gen!()`, `program_fallback_gen!()`, /// and `program_final_gen!()`. /// /// When the `comp` / `pathf` features are enabled, the expansion begins by /// invoking `build_comp!()` / `build_pathf!()`, which run the build steps /// (previously done in `build.rs`) as a compile-time side effect and expand /// to nothing. pub(crate) fn gen_program_impl(_input: TokenStream) -> TokenStream { #[cfg(feature = "comp")] let comp_gen = quote! { ::mingling::macros::program_comp_gen!(); }; #[cfg(not(feature = "comp"))] let comp_gen = quote! {}; // `build_pathf!()` / `build_comp!()` are invoked at the very beginning of the // expansion: they run the build logic at compile time and expand to nothing. #[cfg(feature = "comp")] let comp_build = quote! { ::mingling::macros::build_comp!(); }; #[cfg(not(feature = "comp"))] let comp_build = quote! {}; #[cfg(feature = "pathf")] let pathf_build = quote! { ::mingling::macros::build_pathf!(); }; #[cfg(not(feature = "pathf"))] let pathf_build = quote! {}; // When pathf is enabled, load the type_using.rs generated by the build logic // and emit its use statements so types from submodules are in scope. #[cfg(feature = "pathf")] let pathf_uses: Vec = { // The `build_pathf!()` macro emitted above will (re-)run the analysis // during expansion, but the `use` statements are needed right now, so // make sure the mapping exists before reading it. if let Err(e) = crate::build::pathf::analyze_and_build_type_mapping() { let msg = format!("pathf: type mapping analysis failed: {e}"); return syn::Error::new(proc_macro2::Span::call_site(), msg) .to_compile_error() .into(); } let uses = load_pathf_uses(); if uses.is_empty() { // The analyzer found nothing — emit a clear hint let hint: proc_macro2::TokenStream = syn::parse_quote! { compile_error!( "pathf: no types were found by the analyzer.\n\ Make sure the `pathf` feature is enabled (which also enables\n\ the `build_pathf!()` macro) and that `gen_program!()` is called\n\ in a crate with a `src/` directory." ); }; vec![hint] } else { uses } }; #[cfg(not(feature = "pathf"))] let pathf_uses: Vec = Vec::new(); #[cfg(feature = "pathf")] let super_use = quote! {}; #[cfg(not(feature = "pathf"))] let super_use = quote! { use super::*; }; TokenStream::from(quote! { #comp_build #pathf_build pub use __this_program_impl::*; #[doc(hidden)] pub mod __this_program_impl { #super_use #(#pathf_uses)* /// Alias for the current program type `ThisProgram` pub type Next = ::mingling::ChainProcess; #[derive(::mingling::Grouped, ::mingling::Wrap, Default)] pub struct Entry(pub ::std::vec::Vec<::std::string::String>); impl ::mingling::Routable for ::mingling::ChainProcess { fn to_chain(self) -> ::mingling::ChainProcess { match self { ::mingling::ChainProcess::Ok((any, _)) => { ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Chain)) } other => other, } } fn to_render(self) -> ::mingling::ChainProcess { match self { ::mingling::ChainProcess::Ok((any, _)) => { ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Renderer)) } other => other, } } } #comp_gen ::mingling::macros::program_fallback_gen!(); ::mingling::macros::program_final_gen!(); } }) } /// Loads `type_using.rs` generated by the pathf build logic and returns each /// `use ...;` line as a token stream, ready to be emitted in the generated output. #[cfg(feature = "pathf")] fn load_pathf_uses() -> Vec { let Ok(output_dir) = crate::build::pathf::output_dir() else { return Vec::new(); }; let path = output_dir.join("type_using.rs"); let Ok(content) = std::fs::read_to_string(&path) else { return Vec::new(); }; content .lines() .map(|line| line.trim().to_string()) .filter(|line| !line.is_empty()) .filter_map(|line| line.parse::().ok()) .collect() }