aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/func
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/func')
-rw-r--r--mingling_macros/src/func/dispatcher.rs2
-rw-r--r--mingling_macros/src/func/gen_program.rs112
-rw-r--r--mingling_macros/src/func/group.rs6
-rw-r--r--mingling_macros/src/func/pack.rs6
-rw-r--r--mingling_macros/src/func/pack_err.rs4
-rw-r--r--mingling_macros/src/func/r_print.rs127
6 files changed, 209 insertions, 48 deletions
diff --git a/mingling_macros/src/func/dispatcher.rs b/mingling_macros/src/func/dispatcher.rs
index 808df75..a61dd26 100644
--- a/mingling_macros/src/func/dispatcher.rs
+++ b/mingling_macros/src/func/dispatcher.rs
@@ -135,7 +135,7 @@ pub(crate) fn dispatcher(input: TokenStream) -> TokenStream {
}
fn begin(&self, args: Vec<String>) -> ::mingling::ChainProcess<#program_type> {
use ::mingling::Grouped;
- #pack::new(args).to_chain()
+ ::mingling::Routable::to_chain(#pack::new(args))
}
fn clone_dispatcher(&self) -> Box<dyn ::mingling::Dispatcher<#program_type>> {
Box::new(#command_struct)
diff --git a/mingling_macros/src/func/gen_program.rs b/mingling_macros/src/func/gen_program.rs
index 7128a9a..db82504 100644
--- a/mingling_macros/src/func/gen_program.rs
+++ b/mingling_macros/src/func/gen_program.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
@@ -38,35 +40,33 @@ fn parse_entry_pair(entry: &proc_macro2::TokenStream) -> (proc_macro2::Ident, pr
}
/// Loads the pathf type mapping from `$OUT_DIR/{crate}/type_using.rs`.
-/// Always compiled; returns empty map when pathf feature is not enabled.
-fn load_pathf_map() -> std::collections::HashMap<String, String> {
+/// Always compiled; returns None when pathf feature is not enabled,
+/// or when the file does not exist / fails to load.
+fn load_pathf_map() -> Option<std::collections::HashMap<String, String>> {
if !cfg!(feature = "pathf") {
- return std::collections::HashMap::new();
- }
- let out_dir = std::env::var("OUT_DIR").ok();
- let crate_name = std::env::var("CARGO_PKG_NAME").ok();
- match (out_dir, crate_name) {
- (Some(dir), Some(name)) => {
- let path = std::path::Path::new(&dir).join(&name).join("type_using.rs");
- match std::fs::read_to_string(&path) {
- Ok(content) => content
- .lines()
- .filter_map(|line| {
- let line = line.trim();
- if let Some(rest) = line.strip_prefix("use ") {
- let path = rest.strip_suffix(';').unwrap_or(rest);
- if let Some((_mod, type_name)) = path.rsplit_once("::") {
- return Some((type_name.to_string(), path.to_string()));
- }
- }
- None
- })
- .collect(),
- Err(_) => std::collections::HashMap::new(),
- }
- }
- _ => std::collections::HashMap::new(),
+ return None;
}
+ let out_dir = std::env::var("OUT_DIR").ok()?;
+ let crate_name = std::env::var("CARGO_PKG_NAME").ok()?;
+ let path = std::path::Path::new(&out_dir)
+ .join(&crate_name)
+ .join("type_using.rs");
+ let content = std::fs::read_to_string(&path).ok()?;
+ Some(
+ content
+ .lines()
+ .filter_map(|line| {
+ let line = line.trim();
+ if let Some(rest) = line.strip_prefix("use ") {
+ let path = rest.strip_suffix(';').unwrap_or(rest);
+ if let Some((_mod, type_name)) = path.rsplit_once("::") {
+ return Some((type_name.to_string(), path.to_string()));
+ }
+ }
+ None
+ })
+ .collect(),
+ )
}
/// Resolves a type name to its full path token stream using the pathf mapping.
@@ -138,7 +138,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);
- crate::CompletionSuggest::new((ctx, suggest)).to_render()
+ ::mingling::Routable::<crate::ThisProgram>::to_render(crate::CompletionSuggest::new((ctx, suggest)))
}
Err(_) => std::process::exit(1),
}
@@ -156,7 +156,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);
- crate::CompletionSuggest::new((ctx, suggest)).to_render()
+ ::mingling::Routable::<crate::ThisProgram>::to_render(crate::CompletionSuggest::new((ctx, suggest)))
}
Err(_) => std::process::exit(1),
}
@@ -289,10 +289,34 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
.map(|s| syn::parse_str::<proc_macro2::TokenStream>(s).unwrap())
.collect();
- let pathf_map: std::collections::HashMap<String, String> = if cfg!(feature = "pathf") {
- load_pathf_map()
+ let pathf_map: Option<HashMap<String, String>> = load_pathf_map();
+
+ #[cfg(feature = "pathf")]
+ let pathf_hint: proc_macro2::TokenStream = if pathf_map.is_none() {
+ quote! {
+ compile_error!(
+"Cannot load type mapping computed by `pathf`.
+If not yet configured, execute `mingling::build::analyze_and_build_type_mapping()` in your `build.rs`.
+
+fn main() {
+ // Require features: [\"builds\", \"pathf\"]
+ mingling::build::analyze_and_build_type_mapping().unwrap();
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\__ Write to `build.rs`
+
+}
+ ");
+ }
} else {
- std::collections::HashMap::new()
+ quote! {}
+ };
+
+ #[cfg(not(feature = "pathf"))]
+ let pathf_hint: proc_macro2::TokenStream = quote! {};
+
+ let pathf_map: HashMap<String, String> = if cfg!(feature = "pathf") {
+ pathf_map.unwrap_or_default()
+ } else {
+ HashMap::new()
};
let pathf_uses: Vec<proc_macro2::TokenStream> = if cfg!(feature = "pathf") {
@@ -318,7 +342,7 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
) -> Result<::mingling::RenderResult, ::mingling::error::StructuralRendererSerializeError> {
#[allow(unused_imports)]
#(#pathf_uses)*
- match any.member_id {
+ match any.member_id() {
#(#structural_renderer_tokens)*
_ => {
// Non-structural types: render ResultEmpty (which implements
@@ -384,7 +408,7 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
fn do_comp(any: &::mingling::AnyOutput<Self::Enum>, ctx: &::mingling::ShellContext) -> ::mingling::Suggest {
#[allow(unused_imports)]
#(#pathf_uses)*
- match any.member_id {
+ match any.member_id() {
#(#completion_tokens)*
_ => ::mingling::Suggest::FileCompletion,
}
@@ -418,7 +442,7 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
}).collect();
quote! {
fn render(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
- match any.member_id {
+ match any.member_id() {
#(#render_arms)*
_ => ::mingling::RenderResult::default(),
}
@@ -470,9 +494,9 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
fn do_chain(
any: ::mingling::AnyOutput<Self::Enum>,
) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::mingling::ChainProcess<Self::Enum>> + ::std::marker::Send>> {
- match any.member_id {
+ match any.member_id() {
#(#chain_arms_async)*
- _ => ::core::panic!("No chain found for type id: {:?}", any.type_id),
+ _ => ::core::panic!("No chain found for type id: {:?}", any.type_id()),
}
}
}
@@ -481,9 +505,9 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
fn do_chain(
any: ::mingling::AnyOutput<Self::Enum>,
) -> ::mingling::ChainProcess<Self::Enum> {
- match any.member_id {
+ match any.member_id() {
#(#chain_arms_sync)*
- _ => ::core::panic!("No chain found for type id: {:?}", any.type_id),
+ _ => ::core::panic!("No chain found for type id: {:?}", any.type_id()),
}
}
}
@@ -509,7 +533,9 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
};
let expanded = quote! {
- #[derive(Debug, PartialEq, Eq, Clone)]
+ #pathf_hint
+
+ #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(#repr_type)]
#[allow(nonstandard_style)]
pub enum #name {
@@ -543,19 +569,19 @@ pub(crate) fn program_final_gen_impl(_input: TokenStream) -> TokenStream {
fn render_help(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
#[allow(unused_imports)]
#(#pathf_uses)*
- match any.member_id {
+ match any.member_id() {
#(#help_tokens)*
_ => ::mingling::RenderResult::default(),
}
}
fn has_renderer(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
- match any.member_id {
+ match any.member_id() {
#(#renderer_exist_tokens)*
_ => false
}
}
fn has_chain(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
- match any.member_id {
+ match any.member_id() {
#(#chain_exist_tokens)*
_ => false
}
diff --git a/mingling_macros/src/func/group.rs b/mingling_macros/src/func/group.rs
index b865913..edb1fe1 100644
--- a/mingling_macros/src/func/group.rs
+++ b/mingling_macros/src/func/group.rs
@@ -133,7 +133,11 @@ pub(crate) fn group_macro(input: TokenStream) -> TokenStream {
#type_use
#alias_use
- impl ::mingling::Grouped<__MinglingProgram> for #type_name {
+ /// SAFETY: This is an internal implementation of the `group!` macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<__MinglingProgram> for #type_name {
fn member_id() -> __MinglingProgram {
__MinglingProgram::#type_name
}
diff --git a/mingling_macros/src/func/pack.rs b/mingling_macros/src/func/pack.rs
index a1a7e6b..7206b8e 100644
--- a/mingling_macros/src/func/pack.rs
+++ b/mingling_macros/src/func/pack.rs
@@ -138,7 +138,11 @@ pub(crate) fn pack(input: TokenStream) -> TokenStream {
}
}
- impl ::mingling::Grouped<#group_name> for #type_name {
+ /// SAFETY: This is an internal implementation of the `pack!` macro,
+ /// guaranteeing that the enum value registered by the `register_type!` macro
+ /// is exactly the same as the actual return value,
+ /// which can be confirmed via the `Ident` in the `quote!` block.
+ unsafe impl ::mingling::Grouped<#group_name> for #type_name {
fn member_id() -> #group_name {
#group_name::#type_name
}
diff --git a/mingling_macros/src/func/pack_err.rs b/mingling_macros/src/func/pack_err.rs
index 36e550a..2a318bc 100644
--- a/mingling_macros/src/func/pack_err.rs
+++ b/mingling_macros/src/func/pack_err.rs
@@ -139,8 +139,8 @@ pub(crate) fn pack_err_structural(input: TokenStream) -> TokenStream {
.insert(type_name_str);
let structural_data = quote! {
- impl ::mingling::__private::StructuralDataSealed for #type_name {}
- impl ::mingling::__private::StructuralData for #type_name {}
+ impl ::mingling::__private::StructuralDataSealed<crate::ThisProgram> for #type_name {}
+ impl ::mingling::__private::StructuralData<crate::ThisProgram> for #type_name {}
};
// Generate the struct + impls (same as pack_err! but with Serialize derive + sealed)
diff --git a/mingling_macros/src/func/r_print.rs b/mingling_macros/src/func/r_print.rs
new file mode 100644
index 0000000..86ea52f
--- /dev/null
+++ b/mingling_macros/src/func/r_print.rs
@@ -0,0 +1,127 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use syn::parse::{Parse, ParseStream};
+use syn::{Ident, Token};
+
+/// Parsed input for `r_println!` and `r_print!`.
+///
+/// Two forms:
+/// - `(ident, format_args...)` — explicit buffer
+/// - `(format_args...)` — implicit `__render_result_buffer`
+enum PrintInput {
+ Explicit { dst: Ident, args: TokenStream2 },
+ Implicit { args: TokenStream2 },
+}
+
+impl Parse for PrintInput {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ // Peek: if the next token is an ident followed by a comma, it's the explicit form
+ if input.peek(Ident) && input.peek2(Token![,]) {
+ let dst: Ident = input.parse()?;
+ let _comma: Token![,] = input.parse()?;
+ let args: TokenStream2 = input.parse()?;
+ Ok(PrintInput::Explicit { dst, args })
+ } else {
+ let args: TokenStream2 = input.parse()?;
+ Ok(PrintInput::Implicit { args })
+ }
+ }
+}
+
+fn expand_print(input: TokenStream, method: &str) -> TokenStream {
+ let parsed: PrintInput = match syn::parse(input) {
+ Ok(p) => p,
+ Err(e) => return e.to_compile_error().into(),
+ };
+
+ let method_ident = Ident::new(method, proc_macro2::Span::call_site());
+
+ let expanded = match parsed {
+ PrintInput::Explicit { dst, args } => {
+ quote! {
+ #dst.#method_ident(format!(#args))
+ }
+ }
+ PrintInput::Implicit { args } => {
+ quote! {
+ __render_result_buffer.#method_ident(format!(#args))
+ }
+ }
+ };
+
+ expanded.into()
+}
+
+pub(crate) fn r_println(input: TokenStream) -> TokenStream {
+ expand_print(input, "println")
+}
+
+pub(crate) fn r_print(input: TokenStream) -> TokenStream {
+ expand_print(input, "print")
+}
+
+pub(crate) fn r_eprintln(input: TokenStream) -> TokenStream {
+ expand_print(input, "eprintln")
+}
+
+pub(crate) fn r_eprint(input: TokenStream) -> TokenStream {
+ expand_print(input, "eprint")
+}
+
+/// Parsed input for `r_append!`.
+///
+/// Two forms:
+/// - `(dst, src)` — explicit buffer and source
+/// - `(src)` — implicit `__render_result_buffer`
+struct AppendInput {
+ dst: Option<Ident>,
+ src: proc_macro2::TokenStream,
+}
+
+impl Parse for AppendInput {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ if input.peek(Ident) && input.peek2(Token![,]) {
+ let dst: Ident = input.parse()?;
+ let _comma: Token![,] = input.parse()?;
+ let src: TokenStream2 = input.parse()?;
+ Ok(AppendInput {
+ dst: Some(dst),
+ src,
+ })
+ } else {
+ let src: TokenStream2 = input.parse()?;
+ Ok(AppendInput { dst: None, src })
+ }
+ }
+}
+
+/// `r_append!` macro: appends the contents of another `RenderResult` to this one.
+///
+/// Two forms:
+/// - `r_append!(dst, src)` — appends `src` into `dst`
+/// - `r_append!(src)` — appends `src` into `__render_result_buffer`
+pub(crate) fn r_append(input: TokenStream) -> TokenStream {
+ let parsed: AppendInput = match syn::parse(input) {
+ Ok(p) => p,
+ Err(e) => return e.to_compile_error().into(),
+ };
+
+ let dst_ident = parsed.dst.clone();
+ let src_tokens = parsed.src;
+
+ let expanded = match dst_ident {
+ Some(dst) => {
+ quote! {
+ #dst.append_other(#src_tokens);
+ }
+ }
+ None => {
+ quote! {
+ __render_result_buffer.append_other(#src_tokens);
+ }
+ }
+ };
+
+ expanded.into()
+}