diff options
Diffstat (limited to 'mingling_macros/src/func')
| -rw-r--r-- | mingling_macros/src/func/gen_program.rs | 65 |
1 files changed, 46 insertions, 19 deletions
diff --git a/mingling_macros/src/func/gen_program.rs b/mingling_macros/src/func/gen_program.rs index c0a7ea8..35e8352 100644 --- a/mingling_macros/src/func/gen_program.rs +++ b/mingling_macros/src/func/gen_program.rs @@ -7,6 +7,11 @@ use quote::quote; /// 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! { @@ -16,18 +21,46 @@ pub(crate) fn gen_program_impl(_input: TokenStream) -> TokenStream { #[cfg(not(feature = "comp"))] let comp_gen = quote! {}; - // When pathf is enabled, load the type_using.rs generated by the build script + // `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<proc_macro2::TokenStream> = { + // 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 file might not exist yet — emit a clear hint + // The analyzer found nothing — emit a clear hint let hint: proc_macro2::TokenStream = syn::parse_quote! { compile_error!( - "pathf: `{}` not found or empty.\n\ - Make sure `build.rs` calls `mingling::build::analyze_and_build_type_mapping().unwrap();`\n\ - with features [\"build\", \"pathf\"] enabled." + "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] @@ -47,6 +80,8 @@ pub(crate) fn gen_program_impl(_input: TokenStream) -> TokenStream { }; TokenStream::from(quote! { + #comp_build + #pathf_build pub use __this_program_impl::*; #[doc(hidden)] @@ -88,24 +123,16 @@ pub(crate) fn gen_program_impl(_input: TokenStream) -> TokenStream { }) } -/// Loads `type_using.rs` generated by the pathf build script and returns each +/// 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<proc_macro2::TokenStream> { - let out_dir = match std::env::var("OUT_DIR") { - Ok(d) => d, - Err(_) => return Vec::new(), - }; - let crate_name = match std::env::var("CARGO_PKG_NAME") { - Ok(n) => n, - Err(_) => return Vec::new(), + let Ok(output_dir) = crate::build::pathf::output_dir() else { + return Vec::new(); }; - let path = std::path::Path::new(&out_dir) - .join(&crate_name) - .join("type_using.rs"); - let content = match std::fs::read_to_string(&path) { - Ok(c) => c, - Err(_) => 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() |
