aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md11
-rw-r--r--mingling_macros/src/func/gen_program.rs105
-rw-r--r--mingling_macros/src/func/program_comp_gen.rs4
-rw-r--r--mingling_macros/src/func/program_final_gen.rs15
4 files changed, 96 insertions, 39 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b53d3d6..2d9198b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -60,13 +60,16 @@ None
#### Optimizations:
-1. **[`pathf`]** Moved pathf type resolution from `program_final_gen` (compile-time loading and parsing of `type_using.rs`) to `gen_program!()` (build-time `include!()`). The `program_final_gen` macro no longer loads `type_using.rs` at compile time or embeds `use` statements in generated code. Instead, `gen_program!()` now emits `include!(concat!(env!("OUT_DIR"), "/", env!("CARGO_PKG_NAME"), "/type_using.rs"))` (feature-gated behind `pathf`), which brings all discovered types into scope as bare idents.
+1. **[`pathf`]** Added `is_module` field to `AnalyzeItem` and a new constructor `AnalyzeItem::local_module(module, item_name)` which sets `is_module: true`. The `type_mapping_builder` now tracks whether an item is a module: when generating `type_using.rs`, module items produce `use path::to::module::*;` (glob import) instead of the standard `use path::to::TypeName;` direct import. Non-module items continue to use direct imports as before. The internal data structure changed from `Vec<(String, String)>` to `Vec<(String, String, bool)>` to carry the `is_module` flag through the pipeline.
- Consequently, all type resolution in `program_final_gen` (chain arms, render arms, help arms, completion arms, and dispatch tree generation) now uses simple idents (`ident_tokens(name)`) instead of the former `resolve_type(name, &pathf_map)` call that produced full qualified paths. The `resolve_type` function, `load_pathf_map`, and all associated `HashMap<String, String>` plumbing have been removed from `program_final_gen.rs`.
+2. **[`macros:gen_program`]** Wrapped all code generated by `gen_program!()` inside a `__this_program_impl` module and re-exported it with `pub use __this_program_impl::*;`. This isolates the generated internal items (type aliases, trait implementations, and pathf-generated `use` statements) from the call site's module namespace, preventing name collisions and keeping generated machinery out of the caller's direct scope.
- In `dispatch_tree_gen.rs`, `gen_get_nodes` and `gen_dispatch_args_trie` no longer accept a `pathf_map` parameter; dispatcher static names and dispatching type names are now emitted as bare idents.
+ - The `Next` type alias, `Routable` impl for `ChainProcess<ThisProgram>`, and the `program_fallback_gen!()` / `program_final_gen!()` expansions are now all inside `pub mod __this_program_impl { ... }`, then re-exported publicly.
+ - Pathf integration: when the `pathf` feature is enabled, the `type_using.rs` file (generated by the build script) is loaded at compile time via `load_pathf_uses()` and emitted as `use ...;` statements **inside** the `__this_program_impl` module. Previously, pathf uses were injected via `include!()` inside the `ProgramCollect` impl block in `program_final_gen`; now they are loaded by `gen_program` itself and placed at the top of the hidden module. A `compile_error!` hint is emitted if the pathf file is missing or empty.
+ - When `pathf` is **disabled**, `__this_program_impl` emits `use super::*;` to bring the caller's parent scope types into the generated module, preserving existing behavior for projects that don't use pathf.
+ - Completion generation: removed `crate::` prefix from `CompletionSuggest` references in `program_comp_gen.rs`, since the generated code now lives inside `__this_program_impl` and no longer has a direct `crate` path to the user's crate root. The prefix became unnecessary because `CompletionSuggest` is expected to be in scope (e.g., via pathf glob re-exports or the `use super::*;` fallback).
-2. **[`pathf`]** Added `is_module` field to `AnalyzeItem` and a new constructor `AnalyzeItem::local_module(module, item_name)` which sets `is_module: true`. The `type_mapping_builder` now tracks whether an item is a module: when generating `type_using.rs`, module items produce `use path::to::module::*;` (glob import) instead of the standard `use path::to::TypeName;` direct import. Non-module items continue to use direct imports as before. The internal data structure changed from `Vec<(String, String)>` to `Vec<(String, String, bool)>` to carry the `is_module` flag through the pipeline.
+ _No behavioral change for downstream code — all public items are re-exported with the same names. The `__this_program_impl` module is `#[doc(hidden)]` and not part of the public API._
#### Features:
diff --git a/mingling_macros/src/func/gen_program.rs b/mingling_macros/src/func/gen_program.rs
index c5358bc..0c35318 100644
--- a/mingling_macros/src/func/gen_program.rs
+++ b/mingling_macros/src/func/gen_program.rs
@@ -15,33 +15,98 @@ 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
+ // and emit its use statements so types from submodules are in scope.
+ #[cfg(feature = "pathf")]
+ let pathf_uses: Vec<proc_macro2::TokenStream> = {
+ let uses = load_pathf_uses();
+ if uses.is_empty() {
+ // The file might not exist yet — 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."
+ );
+ };
+ vec![hint]
+ } else {
+ uses
+ }
+ };
+ #[cfg(not(feature = "pathf"))]
+ let pathf_uses: Vec<proc_macro2::TokenStream> = Vec::new();
+
+ #[cfg(feature = "pathf")]
+ let super_use = quote! {};
+
+ #[cfg(not(feature = "pathf"))]
+ let super_use = quote! {
+ use super::*;
+ };
+
TokenStream::from(quote! {
- /// Alias for the current program type `crate::ThisProgram`
- pub type Next = ::mingling::ChainProcess<crate::ThisProgram>;
-
- impl ::mingling::Routable<crate::ThisProgram> for ::mingling::ChainProcess<crate::ThisProgram>
- {
- fn to_chain(self) -> ::mingling::ChainProcess<crate::ThisProgram> {
- match self {
- ::mingling::ChainProcess::Ok((any, _)) => {
- ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Chain))
+ 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<ThisProgram>;
+
+ impl ::mingling::Routable<ThisProgram> for ::mingling::ChainProcess<ThisProgram>
+ {
+ fn to_chain(self) -> ::mingling::ChainProcess<ThisProgram> {
+ match self {
+ ::mingling::ChainProcess::Ok((any, _)) => {
+ ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Chain))
+ }
+ other => other,
}
- other => other,
}
- }
- fn to_render(self) -> ::mingling::ChainProcess<crate::ThisProgram> {
- match self {
- ::mingling::ChainProcess::Ok((any, _)) => {
- ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Renderer))
+ fn to_render(self) -> ::mingling::ChainProcess<ThisProgram> {
+ match self {
+ ::mingling::ChainProcess::Ok((any, _)) => {
+ ::mingling::ChainProcess::Ok((any, mingling::NextProcess::Renderer))
+ }
+ other => other,
}
- other => other,
}
}
- }
- #comp_gen
- ::mingling::macros::program_fallback_gen!();
- ::mingling::macros::program_final_gen!();
+ #comp_gen
+ ::mingling::macros::program_fallback_gen!();
+ ::mingling::macros::program_final_gen!();
+ }
})
}
+
+/// Loads `type_using.rs` generated by the pathf build script 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 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(),
+ };
+ content
+ .lines()
+ .map(|line| line.trim().to_string())
+ .filter(|line| !line.is_empty())
+ .filter_map(|line| line.parse::<proc_macro2::TokenStream>().ok())
+ .collect()
+}
diff --git a/mingling_macros/src/func/program_comp_gen.rs b/mingling_macros/src/func/program_comp_gen.rs
index b6b2546..2fbb0e0 100644
--- a/mingling_macros/src/func/program_comp_gen.rs
+++ b/mingling_macros/src/func/program_comp_gen.rs
@@ -14,7 +14,7 @@ pub(crate) fn program_comp_gen_impl(_input: TokenStream) -> TokenStream {
match read_ctx {
Ok(ctx) => {
let suggest = ::mingling::CompletionHelper::exec_completion::<crate::ThisProgram>(&ctx);
- ::mingling::Routable::<crate::ThisProgram>::to_render(crate::CompletionSuggest::new((ctx, suggest)))
+ ::mingling::Routable::<crate::ThisProgram>::to_render(CompletionSuggest::new((ctx, suggest)))
}
Err(_) => std::process::exit(1),
}
@@ -32,7 +32,7 @@ pub(crate) fn program_comp_gen_impl(_input: TokenStream) -> TokenStream {
match read_ctx {
Ok(ctx) => {
let suggest = ::mingling::CompletionHelper::exec_completion::<crate::ThisProgram>(&ctx);
- ::mingling::Routable::<crate::ThisProgram>::to_render(crate::CompletionSuggest::new((ctx, suggest)))
+ ::mingling::Routable::<crate::ThisProgram>::to_render(CompletionSuggest::new((ctx, suggest)))
}
Err(_) => std::process::exit(1),
}
diff --git a/mingling_macros/src/func/program_final_gen.rs b/mingling_macros/src/func/program_final_gen.rs
index e427f05..0eed1db 100644
--- a/mingling_macros/src/func/program_final_gen.rs
+++ b/mingling_macros/src/func/program_final_gen.rs
@@ -36,8 +36,8 @@ fn parse_entry_pair(entry: &proc_macro2::TokenStream) -> (proc_macro2::Ident, pr
}
/// Helper: convert a string ident into a token stream for the generated code.
-/// Types are now brought into scope by `gen_program!()` via `include!()` of the
-/// pathf-generated `type_using.rs`, so bare idents suffice.
+/// Types are expected to be in scope (e.g. via pathf glob re-exports), so bare
+/// idents suffice.
fn ident_tokens(name: &str) -> proc_macro2::TokenStream {
let ident = proc_macro2::Ident::new(name, proc_macro2::Span::call_site());
quote! { #ident }
@@ -280,15 +280,6 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
quote! { u128 }
};
- // When pathf is enabled, include the type_using.rs generated by the build
- // script so that types from submodules are in scope for gen_program!().
- #[cfg(feature = "pathf")]
- let pathf_include = quote! {
- include!(concat!(env!("OUT_DIR"), "/", env!("CARGO_PKG_NAME"), "/type_using.rs"));
- };
- #[cfg(not(feature = "pathf"))]
- let pathf_include = quote! {};
-
let expanded = quote! {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(#repr_type)]
@@ -306,8 +297,6 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
}
impl ::mingling::ProgramCollect for #name {
- #pathf_include
-
type Enum = #name;
type ErrorDispatcherNotFound = ErrorDispatcherNotFound;
type ErrorRendererNotFound = ErrorRendererNotFound;