aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/func/gen_program.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/func/gen_program.rs')
-rw-r--r--mingling_macros/src/func/gen_program.rs65
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()