aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-28 19:59:21 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-28 19:59:21 +0800
commite3c060b3281b6255c2cdebdcf2dda2a22a758662 (patch)
tree2e98570320de3a591acfa7af7a6c45e0842b93e5 /mingling_macros/src
parenta11628efe9288739f5066af20121a6cda114e5f5 (diff)
feat(macros): wrap gen_program output in a hidden module
Move pathf type resolution from `program_final_gen` to `gen_program` and isolate generated code inside `__this_program_impl` to prevent namespace pollution
Diffstat (limited to 'mingling_macros/src')
-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
3 files changed, 89 insertions, 35 deletions
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;