aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mingling/src/lib.rs31
-rw-r--r--mingling_core/src/asset/help.rs2
-rw-r--r--mingling_core/src/asset/renderer.rs2
-rw-r--r--mingling_core/src/program/collection.rs6
-rw-r--r--mingling_core/src/program/collection/mock.rs6
-rw-r--r--mingling_core/src/program/exec.rs16
-rw-r--r--mingling_core/src/program/hook.rs4
-rw-r--r--mingling_macros/src/help.rs40
-rw-r--r--mingling_macros/src/lib.rs209
-rw-r--r--mingling_macros/src/render.rs56
-rw-r--r--mingling_macros/src/renderer.rs86
-rw-r--r--mling/src/cli.rs29
-rw-r--r--mling/src/errors/io_error.rs93
-rw-r--r--mling/src/lib.rs2
-rw-r--r--mling/src/proj_mgr/generator.rs27
-rw-r--r--mling/src/proj_mgr/show_binaries.rs21
-rw-r--r--mling/src/proj_mgr/show_directories.rs21
17 files changed, 268 insertions, 383 deletions
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index ff8890c..524d061 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -33,15 +33,19 @@
//! }
//!
//! #[renderer]
-//! fn render_name(name: ResultName) {
-//! r_println!("Hello, {}!", *name);
+//! fn render_name(name: ResultName) -> RenderResult {
+//! let mut result = RenderResult::default();
+//! result.println(&format!("Hello, {}!", *name));
+//! result
//! }
//!
//! #[renderer]
-//! fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
+//! fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) -> RenderResult {
+//! let mut result = RenderResult::default();
//! if err.len() > 0 {
-//! r_println!("Command not found: [{}]", err.join(" "));
+//! result.println(&format!("Command not found: [{}]", err.join(" ")));
//! }
+//! result
//! }
//!
//! gen_program!();
@@ -84,8 +88,8 @@ pub mod picker {
/// Re-export of all macros from `mingling_macros`.
///
/// This module re-exports all macros provided by the `mingling_macros` crate,
-/// including `dispatcher!`, `chain!`, `renderer!`, `gen_program!`, `pack!`,
-/// `r_print!`, `r_println!`, and many others. These macros form the core
+/// including `dispatcher!`, `chain!`, `renderer!`,
+/// `gen_program!`, `pack!`, and many others. These macros form the core
/// building blocks of the Mingling framework.
///
/// For detailed documentation, usage examples, and the full list of available
@@ -143,10 +147,6 @@ pub mod macros {
/// `#[program_setup]` - Used to generate program setup
#[cfg(feature = "extra_macros")]
pub use mingling_macros::program_setup;
- /// `r_print!("{someting}")` - Used to print content within a `Renderer` context
- pub use mingling_macros::r_print;
- /// `r_println!("{someting}")` - Used to print content with a newline within a `Renderer` context
- pub use mingling_macros::r_println;
#[doc(hidden)]
pub use mingling_macros::register_chain;
#[doc(hidden)]
@@ -222,8 +222,6 @@ pub mod res;
/// use mingling::prelude::*;
/// ```
pub mod prelude {
- /// Re-export of the `Groupped` derive macro for grouping types.
- pub use crate::Groupped;
/// Re-export of the `chain` macro for defining a chain of commands.
pub use crate::macros::chain;
/// Re-export of the `dispatcher` macro for routing commands.
@@ -238,13 +236,12 @@ pub mod prelude {
/// Re-export of the `pack_err` macro for creating error types.
#[cfg(feature = "extra_macros")]
pub use crate::macros::pack_err;
- /// Re-export of the `r_print` macro for printing within a renderer context.
- pub use crate::macros::r_print;
- /// Re-export of the `r_println` macro for printing with a newline within a renderer
- /// context.
- pub use crate::macros::r_println;
/// Re-export of the `renderer` macro for defining renderer functions.
pub use crate::macros::renderer;
+ /// Re-export of the `Groupped` derive macro for grouping types.
+ pub use crate::Groupped;
+ /// Re-export of the `RenderResult` struct for outputting rendering result
+ pub use crate::RenderResult;
/// Like `pack_err!` but also marks the type for structured output
#[cfg(all(feature = "structural_renderer", feature = "extra_macros"))]
pub use mingling_macros::pack_err_structural;
diff --git a/mingling_core/src/asset/help.rs b/mingling_core/src/asset/help.rs
index ff2b3d4..b3742f2 100644
--- a/mingling_core/src/asset/help.rs
+++ b/mingling_core/src/asset/help.rs
@@ -6,5 +6,5 @@ pub trait HelpRequest {
type Entry;
/// Process the previous value and write the result into the provided [`RenderResult`](./struct.RenderResult.html)
- fn render_help(p: Self::Entry, r: &mut RenderResult);
+ fn render_help(p: Self::Entry) -> RenderResult;
}
diff --git a/mingling_core/src/asset/renderer.rs b/mingling_core/src/asset/renderer.rs
index 1d5a2c1..732f9b7 100644
--- a/mingling_core/src/asset/renderer.rs
+++ b/mingling_core/src/asset/renderer.rs
@@ -6,5 +6,5 @@ pub trait Renderer {
type Previous;
/// Process the previous value and write the result into the provided [`RenderResult`](./struct.RenderResult.html)
- fn render(p: Self::Previous, r: &mut RenderResult);
+ fn render(p: Self::Previous) -> RenderResult;
}
diff --git a/mingling_core/src/program/collection.rs b/mingling_core/src/program/collection.rs
index 044379c..cbb9363 100644
--- a/mingling_core/src/program/collection.rs
+++ b/mingling_core/src/program/collection.rs
@@ -7,7 +7,7 @@ use crate::Dispatcher;
use crate::{AnyOutput, ChainProcess, Groupped, RenderResult};
#[cfg(feature = "structural_renderer")]
-use crate::{StructuralRendererSetting, error::StructuralRendererSerializeError};
+use crate::{error::StructuralRendererSerializeError, StructuralRendererSetting};
#[cfg(feature = "comp")]
use crate::{ShellContext, Suggest};
@@ -53,10 +53,10 @@ pub trait ProgramCollect {
fn build_empty_result() -> AnyOutput<Self::Enum>;
/// Render the input [`AnyOutput`](./struct.AnyOutput.html)
- fn render(any: AnyOutput<Self::Enum>, r: &mut RenderResult);
+ fn render(any: AnyOutput<Self::Enum>) -> RenderResult;
/// Render help for Entry
- fn render_help(any: AnyOutput<Self::Enum>, r: &mut RenderResult);
+ fn render_help(any: AnyOutput<Self::Enum>) -> RenderResult;
/// Find a matching chain to continue execution based on the input [AnyOutput](./struct.AnyOutput.html), returning a new [AnyOutput](./struct.AnyOutput.html)
#[cfg(feature = "async")]
diff --git a/mingling_core/src/program/collection/mock.rs b/mingling_core/src/program/collection/mock.rs
index 568000a..20e6658 100644
--- a/mingling_core/src/program/collection/mock.rs
+++ b/mingling_core/src/program/collection/mock.rs
@@ -7,7 +7,7 @@ use crate::Dispatcher;
use crate::{AnyOutput, ChainProcess, Groupped, ProgramCollect, RenderResult};
#[cfg(feature = "structural_renderer")]
-use crate::{StructuralRendererSetting, error::StructuralRendererSerializeError};
+use crate::{error::StructuralRendererSerializeError, StructuralRendererSetting};
#[cfg(feature = "comp")]
use crate::{ShellContext, Suggest};
@@ -59,11 +59,11 @@ impl ProgramCollect for MockProgramCollect {
unreachable!()
}
- fn render(_any: AnyOutput<Self::Enum>, _r: &mut RenderResult) {
+ fn render(_any: AnyOutput<Self::Enum>) -> RenderResult {
unreachable!()
}
- fn render_help(_any: AnyOutput<Self::Enum>, _r: &mut RenderResult) {
+ fn render_help(_any: AnyOutput<Self::Enum>) -> RenderResult {
unreachable!()
}
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index d1983ed..3224c82 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -461,18 +461,14 @@ pub(crate) fn handle_program_control<C: ProgramCollect<Enum = C>>(
fn render<C: ProgramCollect<Enum = C>>(program: &Program<C>, any: AnyOutput<C>) -> RenderResult {
#[cfg(not(feature = "structural_renderer"))]
{
- let mut render_result = RenderResult::default();
- C::render(any, &mut render_result);
- render_result
+ C::render(any)
}
#[cfg(feature = "structural_renderer")]
{
#[allow(unreachable_patterns)]
match program.structural_renderer_name {
super::StructuralRendererSetting::Disable => {
- let mut render_result = RenderResult::default();
- C::render(any, &mut render_result);
- render_result
+ C::render(any)
}
_ => C::structural_render(any, &program.structural_renderer_name).unwrap(),
}
@@ -487,18 +483,14 @@ fn render_help<C: ProgramCollect<Enum = C>>(
) -> RenderResult {
#[cfg(not(feature = "structural_renderer"))]
{
- let mut render_result = RenderResult::default();
- C::render_help(entry, &mut render_result);
- render_result
+ C::render_help(entry)
}
#[cfg(feature = "structural_renderer")]
{
#[allow(unreachable_patterns)]
match program.structural_renderer_name {
super::StructuralRendererSetting::Disable => {
- let mut render_result = RenderResult::default();
- C::render_help(entry, &mut render_result);
- render_result
+ C::render_help(entry)
}
_ => RenderResult::default(),
}
diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs
index 630baae..56d8e0e 100644
--- a/mingling_core/src/program/hook.rs
+++ b/mingling_core/src/program/hook.rs
@@ -718,11 +718,11 @@ mod tests {
unreachable!()
}
- fn render(_any: crate::AnyOutput<MockHookEnum>, _r: &mut crate::RenderResult) {
+ fn render(_any: crate::AnyOutput<MockHookEnum>) -> crate::RenderResult {
unreachable!()
}
- fn render_help(_any: crate::AnyOutput<MockHookEnum>, _r: &mut crate::RenderResult) {
+ fn render_help(_any: crate::AnyOutput<MockHookEnum>) -> crate::RenderResult {
unreachable!()
}
diff --git a/mingling_macros/src/help.rs b/mingling_macros/src/help.rs
index 6d427b9..7f5b695 100644
--- a/mingling_macros/src/help.rs
+++ b/mingling_macros/src/help.rs
@@ -1,22 +1,34 @@
use proc_macro::TokenStream;
-use quote::{ToTokens, quote};
+use quote::{quote, ToTokens};
use syn::spanned::Spanned;
-use syn::{Ident, ItemFn, ReturnType, Signature, Type, TypePath, parse_macro_input};
+use syn::{parse_macro_input, Ident, ItemFn, ReturnType, Type, TypePath};
use crate::get_global_set;
use crate::res_injection::{extract_args_info, generate_immut_resource_bindings};
-/// Validates the return type is () or empty
-fn validate_return_type(sig: &Signature) -> syn::Result<()> {
+/// Validates that the function returns `::mingling::RenderResult`.
+fn validate_render_result_return(sig: &syn::Signature) -> syn::Result<()> {
match &sig.output {
ReturnType::Type(_, ty) => match &**ty {
- Type::Tuple(tuple) if tuple.elems.is_empty() => Ok(()),
+ Type::Path(type_path) => {
+ let last_seg = type_path.path.segments.last().map(|s| s.ident.to_string());
+ match last_seg.as_deref() {
+ Some("RenderResult") => Ok(()),
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Help function must return `RenderResult`",
+ )),
+ }
+ }
_ => Err(syn::Error::new(
ty.span(),
- "Help function must return () or have no return type",
+ "Help function must return `RenderResult`",
)),
},
- ReturnType::Default => Ok(()),
+ ReturnType::Default => Err(syn::Error::new(
+ sig.span(),
+ "Help function must have a return type `-> RenderResult`",
+ )),
}
}
@@ -37,8 +49,8 @@ pub fn help_attr(item: TokenStream) -> TokenStream {
Err(e) => return e.to_compile_error().into(),
};
- // Validate return type
- if let Err(e) = validate_return_type(&input_fn.sig) {
+ // Validate return type is RenderResult
+ if let Err(e) = validate_render_result_return(&input_fn.sig) {
return e.to_compile_error().into();
}
@@ -136,19 +148,17 @@ pub fn help_attr(item: TokenStream) -> TokenStream {
impl ::mingling::HelpRequest for #struct_name {
type Entry = #entry_type;
- fn render_help(#prev_param: Self::Entry, __renderer_inner_result: &mut ::mingling::RenderResult) {
+ fn render_help(#prev_param: Self::Entry) -> ::mingling::RenderResult {
#help_render_body
}
}
::mingling::macros::register_help!(#entry_type, #struct_name);
- // Keep the original function for internal use with original params without __renderer_inner_result
+ // Keep the original function unchanged
#(#fn_attrs)*
- #vis fn #fn_name(#original_inputs) {
- let mut dummy_r = ::mingling::RenderResult::default();
- let __renderer_inner_result = &mut dummy_r;
+ #vis fn #fn_name(#original_inputs) -> ::mingling::RenderResult {
#fn_body
}
};
@@ -164,7 +174,7 @@ fn build_help_entry(struct_name: &Ident, entry_type: &TypePath) -> proc_macro2::
// SAFETY: The member_id check ensures that `any` contains a value of type `#entry_type`,
// so downcasting to `#entry_type` is safe.
let value = unsafe { any.downcast::<#entry_type>().unwrap_unchecked() };
- <#struct_name as ::mingling::HelpRequest>::render_help(value, __renderer_inner_result);
+ <#struct_name as ::mingling::HelpRequest>::render_help(value)
}
}
}
diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs
index c82466d..242b729 100644
--- a/mingling_macros/src/lib.rs
+++ b/mingling_macros/src/lib.rs
@@ -68,7 +68,6 @@
//! | [`#[help]`](attr.help.html) | Defines help output for a command entry type |
//! | [`route!`] | Routes execution depending on a condition |
//! | [`empty_result!`] | Returns an empty result for early termination |
-//! | [`r_print!`] / [`r_println!`] | Print to the `RenderResult` buffer inside a renderer |
//! | [`#[completion]`](attr.completion.html) | Registers a shell completion handler |
//!
//! ## Phase 3: Program Generation
@@ -170,7 +169,6 @@ mod pack;
mod pack_err;
#[cfg(feature = "extra_macros")]
mod program_setup;
-mod render;
mod renderer;
mod res_injection;
#[cfg(feature = "structural_renderer")]
@@ -704,95 +702,6 @@ pub fn dispatcher(input: TokenStream) -> TokenStream {
dispatcher::dispatcher(input)
}
-/// Prints formatted text to the current [`RenderResult`] buffer within a
-/// `#[renderer]` function.
-///
-/// Unlike `print!`, this macro writes to the in-memory `RenderResult` buffer
-/// rather than directly to stdout. The buffered output is flushed automatically
-/// when the renderer returns, allowing the framework to control output timing
-/// and capture (e.g., for testing or structural rendering to JSON/YAML).
-///
-/// This macro requires a mutable reference to a [`RenderResult`] named
-/// `__renderer_inner_result` to be in scope, which is automatically provided
-/// inside `#[renderer]` and `#[help]` functions.
-///
-/// # Syntax
-///
-/// Same as [`format!`] / [`print!`]:
-///
-/// ```rust,ignore
-/// r_print!("Hello, {}!", name);
-/// r_print!("Value: {value}");
-/// ```
-///
-/// # Example
-///
-/// ```rust,ignore
-/// use mingling::macros::{renderer, r_print};
-///
-/// #[renderer]
-/// fn show_greeting(prev: Greeting) {
-/// r_print!("Hello, {}!", *prev);
-/// }
-/// ```
-///
-/// # Difference from `r_println!`
-///
-/// `r_print!` does **not** append a newline. Use [`r_println!`] for newline-terminated output.
-///
-/// # Panics
-///
-/// Does not panic under normal circumstances. The underlying `RenderResult`
-/// buffer operations are infallible.
-///
-/// [`RenderResult`]: https://docs.rs/mingling/latest/mingling/struct.RenderResult.html
-#[proc_macro]
-pub fn r_print(input: TokenStream) -> TokenStream {
- render::r_print(input)
-}
-
-/// Prints formatted text followed by a newline to the current [`RenderResult`] buffer
-/// within a `#[renderer]` or `#[help]` function.
-///
-/// Unlike `println!`, this macro writes to the in-memory `RenderResult` buffer
-/// rather than directly to stdout. The buffered output is flushed automatically
-/// when the renderer returns.
-///
-/// This macro requires a mutable reference to a [`RenderResult`] named
-/// `__renderer_inner_result` to be in scope, which is automatically provided
-/// inside `#[renderer]` and `#[help]` functions.
-///
-/// # Syntax
-///
-/// Same as [`println!`]:
-///
-/// ```rust,ignore
-/// r_println!("Hello, {}!", name);
-/// r_println!("Value: {value}");
-/// r_println!(); // just a newline
-/// ```
-///
-/// # Example
-///
-/// ```rust,ignore
-/// use mingling::macros::{renderer, r_println};
-///
-/// #[renderer]
-/// fn show_greeting(prev: Greeting) {
-/// r_println!("Hello, {}!", *prev);
-/// }
-/// ```
-///
-/// # See also
-///
-/// - [`r_print!`] for output without a trailing newline.
-///
-/// [`RenderResult`]: https://docs.rs/mingling/latest/mingling/struct.RenderResult.html
-#[proc_macro]
-pub fn r_println(input: TokenStream) -> TokenStream {
- render::r_println(input)
-}
-
/// Declares a chain processing step that transforms one type into another
/// within a Mingling pipeline.
///
@@ -893,7 +802,7 @@ pub fn r_println(input: TokenStream) -> TokenStream {
/// # Sync Example with Resource Injection
///
/// ```rust,ignore
-/// use mingling::macros::{chain, pack, gen_program, r_println};
+/// use mingling::macros::{chain, pack, gen_program};
///
/// #[derive(Default, Clone)]
/// struct UserName(String);
@@ -903,7 +812,6 @@ pub fn r_println(input: TokenStream) -> TokenStream {
///
/// #[chain]
/// fn greet(prev: HelloEntry, user_name: &UserName, count: &mut u64) -> Next {
-/// r_println!("User: {:?}", user_name);
/// *count += 1;
/// Greeting::new(format!("Hello, {}!", user_name.0))
/// }
@@ -971,32 +879,31 @@ pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream {
/// The `#[renderer]` attribute converts a function into a renderer by:
/// 1. Generating a hidden struct implementing the `Renderer` trait.
/// 2. Registering the renderer mapping in the global renderer registry.
-/// 3. Keeping the original function for direct calls. When called directly,
-/// a new `RenderResult` is created and the renderer function writes its
-/// output directly to the current terminal output buffer.
-///
-/// Inside a `#[renderer]` function, you can use `r_print!` and `r_println!`
-/// to write output to the `RenderResult` buffer.
+/// 3. Keeping the original function for direct calls.
///
/// # Syntax
///
/// ```rust,ignore
/// #[renderer]
-/// fn render_my_type(prev: MyType) {
-/// r_println!("Output: {:?}", *prev);
+/// fn render_my_type(prev: MyType) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "Output: {:?}", *prev);
+/// result
/// }
/// ```
///
/// # Example
///
/// ```rust,ignore
-/// use mingling::macros::{renderer, r_println, pack, gen_program};
+/// use mingling::macros::{renderer, pack, gen_program};
///
/// pack!(Greeting = String);
///
/// #[renderer]
-/// fn render_greeting(prev: Greeting) {
-/// r_println!("Hello, {}!", *prev);
+/// fn render_greeting(prev: Greeting) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "Hello, {}!", *prev);
+/// result
/// }
/// ```
///
@@ -1015,13 +922,17 @@ pub fn chain(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// ```rust,ignore
/// #[renderer]
-/// fn fallback_dispatcher_not_found(prev: ErrorDispatcherNotFound) {
-/// r_println!("Unknown command: {}", prev.join(", "));
+/// fn fallback_dispatcher_not_found(prev: ErrorDispatcherNotFound) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "Unknown command: {}", prev.join(", "));
+/// result
/// }
///
/// #[renderer]
-/// fn fallback_renderer_not_found(prev: ErrorRendererNotFound) {
-/// r_println!("No renderer for `{}`", *prev);
+/// fn fallback_renderer_not_found(prev: ErrorRendererNotFound) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "No renderer for `{}`", *prev);
+/// result
/// }
/// ```
#[proc_macro_attribute]
@@ -1275,33 +1186,38 @@ pub fn register_dispatcher(input: TokenStream) -> TokenStream {
/// The macro works by:
/// 1. Generating a hidden struct implementing the `HelpRequest` trait.
/// 2. Registering the help mapping in the global `HELP_REQUESTS` registry.
-/// 3. Keeping the original function for direct calls (with a dummy `RenderResult`).
+/// 3. Keeping the original function for direct calls.
///
-/// Inside a `#[help]` function, you can use [`r_print!`] and [`r_println!`]
-/// to write help text to the `RenderResult` buffer.
+/// Inside a `#[help]` function, you must manually create a `RenderResult`
+/// and return it. Use [`writeln!`](std::fmt::writeln) on the result to
+/// write help text.
///
/// # Syntax
///
/// ```rust,ignore
/// #[help]
-/// fn help_my_entry(prev: MyEntry) {
-/// r_println!("Usage: myapp myentry [options]");
-/// r_println!(" Does something useful.");
+/// fn help_my_entry(prev: MyEntry) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "Usage: myapp myentry [options]");
+/// writeln!(result, " Does something useful.");
+/// result
/// }
/// ```
///
/// # Example
///
/// ```rust,ignore
-/// use mingling::macros::{help, r_println, pack, gen_program};
-/// use mingling::{prelude::*, setup::BasicProgramSetup};
+/// use mingling::macros::{help, pack, gen_program};
+/// use mingling::{prelude::*, setup::BasicProgramSetup, RenderResult};
///
/// pack!(MyEntry = Vec<String>);
///
/// #[help]
-/// fn help_my_entry(prev: MyEntry) {
-/// r_println!("Usage: myapp greet [name]");
-/// r_println!("Greets the user.");
+/// fn help_my_entry(prev: MyEntry) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "Usage: myapp greet [name]");
+/// writeln!(result, "Greets the user.");
+/// result
/// }
///
/// fn main() {
@@ -1316,13 +1232,13 @@ pub fn register_dispatcher(input: TokenStream) -> TokenStream {
///
/// - The function must have exactly one parameter (the entry type to provide help for).
/// - The parameter type must be a single-segment type path (e.g., `MyEntry`, not `other::MyEntry`).
-/// - The function must return `()`.
+/// - The function must return [`RenderResult`].
/// - The function cannot be async.
///
/// # See also
///
/// - [`BasicProgramSetup`] — The setup that enables `--help` and `-h` flag processing.
-/// - [`r_print!`] / [`r_println!`] — For outputting help text.
+/// - [`RenderResult`] — The return type for help functions.
/// - [`#[chain]`](attr.chain.html) — For processing the dispatched entry after help.
///
/// [`BasicProgramSetup`]: https://docs.rs/mingling/latest/mingling/setup/struct.BasicProgramSetup.html
@@ -1496,7 +1412,7 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream {
/// # Example
///
/// ```rust,ignore
-/// use mingling::macros::{dispatcher, chain, renderer, gen_program};
+/// use mingling::macros::{dispatcher, chain, renderer, gen_program, RenderResult};
///
/// dispatcher!("hello", HelloCommand => HelloEntry);
///
@@ -1506,8 +1422,10 @@ pub fn derive_groupped_serialize(input: TokenStream) -> TokenStream {
/// }
///
/// #[renderer]
-/// fn render(prev: /* ... */) {
-/// r_println!("Done!");
+/// fn render(prev: /* ... */) -> RenderResult {
+/// let result = RenderResult::new();
+/// writeln!(result, "Done!");
+/// result
/// }
///
/// // Collect everything:
@@ -1612,9 +1530,11 @@ pub fn program_comp_gen(_input: TokenStream) -> TokenStream {
#[allow(unused)]
#[doc(hidden)]
#[::mingling::macros::renderer]
- pub fn __render_completion(prev: CompletionSuggest) {
+ pub fn __render_completion(prev: CompletionSuggest) -> ::mingling::RenderResult {
+ let result = ::mingling::RenderResult::default();
let (ctx, suggest) = prev.inner;
::mingling::CompletionHelper::render_suggest::<crate::ThisProgram>(ctx, suggest);
+ result
}
};
@@ -1783,9 +1703,9 @@ pub fn program_fallback_gen(_input: TokenStream) -> TokenStream {
/// impl ProgramCollect for MyProgram {
/// type Enum = MyProgram;
/// type ResultEmpty = ResultEmpty;
-/// fn render(any, r) { /* dispatches to all registered renderers */ }
+/// fn render(any) -> RenderResult { /* dispatches to all registered renderers */ }
/// fn do_chain(any) -> ChainProcess { /* dispatches to all registered chain steps */ }
-/// fn render_help(any, r) { /* dispatches to all registered help handlers */ }
+/// fn render_help(any) -> RenderResult { /* dispatches to all registered help handlers */ }
/// fn has_renderer(any) -> bool { /* checks renderer registry */ }
/// fn has_chain(any) -> bool { /* checks chain registry */ }
/// // (with comp feature) fn do_comp(...)
@@ -2020,12 +1940,15 @@ pub fn program_final_gen(_input: TokenStream) -> TokenStream {
let comp = quote! {};
// Build render function arms from stored entries
- let render_fn = if renderer_tokens.is_empty() {
- quote! {
- fn render(_any: ::mingling::AnyOutput<Self::Enum>, _renderer_inner_result: &mut ::mingling::RenderResult) {}
- }
- } else {
- let render_arms: Vec<_> = renderer_tokens.iter().map(|entry| {
+ let render_fn =
+ if renderer_tokens.is_empty() {
+ quote! {
+ fn render(_any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
+ ::mingling::RenderResult::default()
+ }
+ }
+ } else {
+ let render_arms: Vec<_> = renderer_tokens.iter().map(|entry| {
let (struct_ident, variant_ident) = parse_entry_pair(entry);
let downcast_ty = resolve_type(&variant_ident.to_string(), &pathf_map);
let resolved_struct = resolve_type(&struct_ident.to_string(), &pathf_map);
@@ -2034,19 +1957,19 @@ pub fn program_final_gen(_input: TokenStream) -> TokenStream {
// SAFETY: The `type_id` check ensures that `any` contains a value of type `#variant_ident`,
// so downcasting to `#variant_ident` is safe.
let value = unsafe { any.downcast::<#downcast_ty>().unwrap_unchecked() };
- <#resolved_struct as ::mingling::Renderer>::render(value, __renderer_inner_result);
+ <#resolved_struct as ::mingling::Renderer>::render(value)
}
}
}).collect();
- quote! {
- fn render(any: ::mingling::AnyOutput<Self::Enum>, __renderer_inner_result: &mut ::mingling::RenderResult) {
- match any.member_id {
- #(#render_arms)*
- _ => (),
+ quote! {
+ fn render(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
+ match any.member_id {
+ #(#render_arms)*
+ _ => ::mingling::RenderResult::default(),
+ }
}
}
- }
- };
+ };
// Build do_chain function (async and sync versions)
let chain_arms_async: Vec<_> = chain_tokens.iter().map(|entry| {
@@ -2162,12 +2085,12 @@ pub fn program_final_gen(_input: TokenStream) -> TokenStream {
}
#render_fn
#do_chain_fn
- fn render_help(any: ::mingling::AnyOutput<Self::Enum>, __renderer_inner_result: &mut ::mingling::RenderResult) {
+ fn render_help(any: ::mingling::AnyOutput<Self::Enum>) -> ::mingling::RenderResult {
#[allow(unused_imports)]
#(#pathf_uses)*
match any.member_id {
#(#help_tokens)*
- _ => (),
+ _ => ::mingling::RenderResult::default(),
}
}
fn has_renderer(any: &::mingling::AnyOutput<Self::Enum>) -> bool {
diff --git a/mingling_macros/src/render.rs b/mingling_macros/src/render.rs
deleted file mode 100644
index 10fce7d..0000000
--- a/mingling_macros/src/render.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use proc_macro::TokenStream;
-use quote::quote;
-use syn::parse::Parser;
-use syn::{Expr, Token};
-
-pub fn r_print(input: TokenStream) -> TokenStream {
- // Parse the input as format arguments
- let parser = syn::punctuated::Punctuated::<Expr, Token![,]>::parse_terminated;
- let format_args = match parser.parse(input) {
- Ok(args) => args,
- Err(e) => return e.to_compile_error().into(),
- };
-
- // Build the format macro call
- let format_call = if format_args.is_empty() {
- quote! { ::std::format!("") }
- } else {
- let args_iter = format_args.iter();
- quote! { ::std::format!(#(#args_iter),*) }
- };
-
- let expanded = quote! {
- {
- let formatted = #format_call;
- ::mingling::RenderResult::print(__renderer_inner_result, &formatted)
- }
- };
-
- expanded.into()
-}
-
-pub fn r_println(input: TokenStream) -> TokenStream {
- // Parse the input as format arguments
- let parser = syn::punctuated::Punctuated::<Expr, Token![,]>::parse_terminated;
- let format_args = match parser.parse(input) {
- Ok(args) => args,
- Err(e) => return e.to_compile_error().into(),
- };
-
- // Build the format macro call
- let format_call = if format_args.is_empty() {
- quote! { ::std::format!("") }
- } else {
- let args_iter = format_args.iter();
- quote! { ::std::format!(#(#args_iter),*) }
- };
-
- let expanded = quote! {
- {
- let formatted = #format_call;
- ::mingling::RenderResult::println(__renderer_inner_result, &formatted)
- }
- };
-
- expanded.into()
-}
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs
index 880c50f..9683426 100644
--- a/mingling_macros/src/renderer.rs
+++ b/mingling_macros/src/renderer.rs
@@ -1,23 +1,38 @@
use proc_macro::TokenStream;
-use quote::{ToTokens, quote};
+use quote::{quote, ToTokens};
use syn::spanned::Spanned;
-use syn::{ItemFn, ReturnType, Signature, Type, TypePath, parse_macro_input};
+use syn::{parse_macro_input, ItemFn, ReturnType, Signature, Type, TypePath};
use crate::get_global_set;
use crate::res_injection::{extract_args_info, generate_immut_resource_bindings};
-/// Extracts and returns the return type from the function signature (or None for `()` / no return type).
-fn extract_return_type(sig: &Signature) -> Option<syn::Type> {
+/// Validates that the function returns `::mingling::RenderResult`.
+fn validate_render_result_return(sig: &Signature) -> syn::Result<()> {
match &sig.output {
ReturnType::Type(_, ty) => {
+ // Check if the return type is RenderResult
match &**ty {
- // `()` means no custom return type
- Type::Tuple(tuple) if tuple.elems.is_empty() => None,
- // Any other return type is allowed
- custom_ty => Some((*custom_ty).clone()),
+ Type::Path(type_path) => {
+ let segments = &type_path.path.segments;
+ let last_seg = segments.last().map(|s| s.ident.to_string());
+ match last_seg.as_deref() {
+ Some("RenderResult") => Ok(()),
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Renderer function must return `RenderResult`",
+ )),
+ }
+ }
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Renderer function must return `RenderResult`",
+ )),
}
}
- ReturnType::Default => None,
+ ReturnType::Default => Err(syn::Error::new(
+ sig.span(),
+ "Renderer function must have a return type `-> RenderResult`",
+ )),
}
}
@@ -44,8 +59,10 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
Err(e) => return e.to_compile_error().into(),
};
- // Validate return type – now returns Some(type) if custom type, None if ()
- let return_type = extract_return_type(&input_fn.sig);
+ // Validate that the function returns RenderResult
+ if let Err(e) = validate_render_result_return(&input_fn.sig) {
+ return e.to_compile_error().into();
+ }
// Get function body statements
let fn_body_stmts: Vec<syn::Stmt> = input_fn.block.stmts.clone();
@@ -76,23 +93,6 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), program_type);
let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect();
- // Determine public return type and the expression to return dummy_r
- let (public_return_type, result_return) = if let Some(custom_ty) = &return_type {
- // User specified a custom return type (e.g. -> String)
- let ret_ty = quote! { #custom_ty };
- let expr = quote! { dummy_r.into() };
- (ret_ty, expr)
- } else {
- // Return type is () — no custom return type specified
- let ret_ty = quote! { () };
- let expr = quote! {
- if !dummy_r.is_empty() {
- ::std::println!("{}", &*dummy_r);
- }
- };
- (ret_ty, expr)
- };
-
let inner_body_with_resources = if has_mut_resources {
let mut wrapped = quote! { #(#fn_body_stmts)* };
for res in mut_resources.iter().rev() {
@@ -110,8 +110,7 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
};
// Build the Renderer::render body with resource injection
- // Renderer::render returns (), output goes through __renderer_inner_result parameter.
- // Resources are injected from the program context here.
+ // The user's body now directly creates and returns a RenderResult.
let render_fn_body = if has_resources {
quote! {
#(#immut_resource_stmts)*
@@ -121,22 +120,13 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
quote! { #inner_body_with_resources }
};
- // Build the original function body
- // The original function preserves the user's signature and return type.
+ // The original function preserves the user's exact signature and body.
// Resource parameters are passed directly by the caller, NOT injected from context.
- let original_fn_body = {
- quote! {
- let mut dummy_r = ::mingling::RenderResult::default();
- {
- let __renderer_inner_result = &mut dummy_r;
- #(#fn_body_stmts)*
- }
- #result_return
- }
- };
-
- // Keep the original function signature unchanged (same params as user wrote)
let original_inputs = input_fn.sig.inputs.clone();
+ let original_return_type = match &input_fn.sig.output {
+ ReturnType::Type(_, ty) => quote! { #ty },
+ ReturnType::Default => unreachable!("Already validated that return type is RenderResult"),
+ };
let expanded = quote! {
#(#fn_attrs)*
@@ -149,15 +139,15 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
impl ::mingling::Renderer for #struct_name {
type Previous = #previous_type;
- fn render(#prev_param: Self::Previous, __renderer_inner_result: &mut ::mingling::RenderResult) {
+ fn render(#prev_param: Self::Previous) -> ::mingling::RenderResult {
#render_fn_body
}
}
- // Keep the original function for internal use (without r parameter)
+ // Keep the original function unchanged
#(#fn_attrs)*
- #vis fn #fn_name(#original_inputs) -> #public_return_type {
- #original_fn_body
+ #vis fn #fn_name(#original_inputs) -> #original_return_type {
+ #(#fn_body_stmts)*
}
};
diff --git a/mling/src/cli.rs b/mling/src/cli.rs
index 3000645..bfe602c 100644
--- a/mling/src/cli.rs
+++ b/mling/src/cli.rs
@@ -8,13 +8,13 @@ use crate::{
};
use colored::Colorize;
use mingling::{
- Groupped, Program,
+ Groupped, Program, RenderResult,
hook::ProgramHook,
- macros::{chain, help, pack, program_setup, r_println, renderer},
+ macros::{chain, help, pack, program_setup, renderer},
res::ResExitCode,
setup::{ExitCodeSetup, HelpFlagSetup, QuietFlagSetup, StructuralRendererSetup},
};
-use std::{env::current_dir, path::PathBuf, process::exit, str::FromStr};
+use std::{env::current_dir, io::Write, path::PathBuf, process::exit, str::FromStr};
pub fn run() {
#[cfg(windows)]
@@ -167,21 +167,20 @@ pub fn handle_error_dispatcher_not_found(err: ErrorDispatcherNotFound) -> Next {
}
#[renderer]
-pub fn render_mling_help(_prev: ResultMlingHelp, ec: &mut ResExitCode) {
- r_println!("{}", markdown(include_str!("helps/mling_help.txt")));
+pub fn render_mling_help(_prev: ResultMlingHelp, ec: &mut ResExitCode) -> RenderResult {
+ let mut result = RenderResult::default();
+ writeln!(result, "{}", markdown(include_str!("helps/mling_help.txt"))).ok();
ec.exit_code = 0;
+ result
}
#[renderer]
-pub fn render_unknown_command(prev: ResultUnknownCommand, ec: &mut ResExitCode) {
- r_println!(
- "{}",
- eformat_cargo!("no such command: `{}`", prev.bright_yellow().bold())
- );
- r_println!();
- r_println!(
- "{}",
- hformat_cargo!("view all commands with `cargo help mling`")
- );
+pub fn render_unknown_command(prev: ResultUnknownCommand, ec: &mut ResExitCode) -> RenderResult {
+ let mut result = RenderResult::default();
+ writeln!(result, "{}", eformat_cargo!("no such command: `{}`", prev.bright_yellow().bold())).ok();
+ writeln!(result).ok();
+ writeln!(result, "{}", hformat_cargo!("view all commands with `cargo help mling`"))
+ .ok();
ec.exit_code = 101;
+ result
}
diff --git a/mling/src/errors/io_error.rs b/mling/src/errors/io_error.rs
index 92a3951..3b6b8fb 100644
--- a/mling/src/errors/io_error.rs
+++ b/mling/src/errors/io_error.rs
@@ -1,7 +1,9 @@
use mingling::{
- macros::{group, r_println, renderer},
+ macros::{group, renderer},
res::ResExitCode,
+ RenderResult,
};
+use std::io::Write as _;
use crate::eformat_cargo;
@@ -49,167 +51,174 @@ pub const EC_IO_ERR_OUT_OF_MEMORY: i32 = 1037;
pub const EC_IO_ERR_OTHER: i32 = 1038;
#[renderer]
-pub fn render_error_io(err: ErrorIo, ec: &mut ResExitCode) {
+pub fn render_error_io(err: ErrorIo, ec: &mut ResExitCode) -> RenderResult {
+ let mut result = RenderResult::default();
match err.kind() {
std::io::ErrorKind::NotFound => {
- r_println!("{}", eformat_cargo!("file or directory not found"));
+ writeln!(result, "{}", eformat_cargo!("file or directory not found")).ok();
ec.exit_code = EC_IO_ERR_NOT_FOUND;
}
std::io::ErrorKind::PermissionDenied => {
- r_println!("{}", eformat_cargo!("permission denied"));
+ writeln!(result, "{}", eformat_cargo!("permission denied")).ok();
ec.exit_code = EC_IO_ERR_PERMISSION_DENIED;
}
std::io::ErrorKind::ConnectionRefused => {
- r_println!("{}", eformat_cargo!("connection refused"));
+ writeln!(result, "{}", eformat_cargo!("connection refused")).ok();
ec.exit_code = EC_IO_ERR_CONNECTION_REFUSED;
}
std::io::ErrorKind::ConnectionReset => {
- r_println!("{}", eformat_cargo!("connection reset"));
+ writeln!(result, "{}", eformat_cargo!("connection reset")).ok();
ec.exit_code = EC_IO_ERR_CONNECTION_RESET;
}
std::io::ErrorKind::HostUnreachable => {
- r_println!("{}", eformat_cargo!("host unreachable"));
+ writeln!(result, "{}", eformat_cargo!("host unreachable")).ok();
ec.exit_code = EC_IO_ERR_HOST_UNREACHABLE;
}
std::io::ErrorKind::NetworkUnreachable => {
- r_println!("{}", eformat_cargo!("network unreachable"));
+ writeln!(result, "{}", eformat_cargo!("network unreachable")).ok();
ec.exit_code = EC_IO_ERR_NETWORK_UNREACHABLE;
}
std::io::ErrorKind::ConnectionAborted => {
- r_println!("{}", eformat_cargo!("connection aborted"));
+ writeln!(result, "{}", eformat_cargo!("connection aborted")).ok();
ec.exit_code = EC_IO_ERR_CONNECTION_ABORTED;
}
std::io::ErrorKind::NotConnected => {
- r_println!("{}", eformat_cargo!("not connected"));
+ writeln!(result, "{}", eformat_cargo!("not connected")).ok();
ec.exit_code = EC_IO_ERR_NOT_CONNECTED;
}
std::io::ErrorKind::AddrInUse => {
- r_println!("{}", eformat_cargo!("address in use"));
+ writeln!(result, "{}", eformat_cargo!("address in use")).ok();
ec.exit_code = EC_IO_ERR_ADDR_IN_USE;
}
std::io::ErrorKind::AddrNotAvailable => {
- r_println!("{}", eformat_cargo!("address not available"));
+ writeln!(result, "{}", eformat_cargo!("address not available")).ok();
ec.exit_code = EC_IO_ERR_ADDR_NOT_AVAILABLE;
}
std::io::ErrorKind::NetworkDown => {
- r_println!("{}", eformat_cargo!("network down"));
+ writeln!(result, "{}", eformat_cargo!("network down")).ok();
ec.exit_code = EC_IO_ERR_NETWORK_DOWN;
}
std::io::ErrorKind::BrokenPipe => {
- r_println!("{}", eformat_cargo!("broken pipe"));
+ writeln!(result, "{}", eformat_cargo!("broken pipe")).ok();
ec.exit_code = EC_IO_ERR_BROKEN_PIPE;
}
std::io::ErrorKind::AlreadyExists => {
- r_println!("{}", eformat_cargo!("file or directory already exists"));
+ writeln!(
+ result,
+ "{}",
+ eformat_cargo!("file or directory already exists")
+ )
+ .ok();
ec.exit_code = EC_IO_ERR_ALREADY_EXISTS;
}
std::io::ErrorKind::WouldBlock => {
- r_println!("{}", eformat_cargo!("operation would block"));
+ writeln!(result, "{}", eformat_cargo!("operation would block")).ok();
ec.exit_code = EC_IO_ERR_WOULD_BLOCK;
}
std::io::ErrorKind::NotADirectory => {
- r_println!("{}", eformat_cargo!("not a directory"));
+ writeln!(result, "{}", eformat_cargo!("not a directory")).ok();
ec.exit_code = EC_IO_ERR_NOT_A_DIRECTORY;
}
std::io::ErrorKind::IsADirectory => {
- r_println!("{}", eformat_cargo!("is a directory"));
+ writeln!(result, "{}", eformat_cargo!("is a directory")).ok();
ec.exit_code = EC_IO_ERR_IS_A_DIRECTORY;
}
std::io::ErrorKind::DirectoryNotEmpty => {
- r_println!("{}", eformat_cargo!("directory not empty"));
+ writeln!(result, "{}", eformat_cargo!("directory not empty")).ok();
ec.exit_code = EC_IO_ERR_DIRECTORY_NOT_EMPTY;
}
std::io::ErrorKind::ReadOnlyFilesystem => {
- r_println!("{}", eformat_cargo!("read-only filesystem"));
+ writeln!(result, "{}", eformat_cargo!("read-only filesystem")).ok();
ec.exit_code = EC_IO_ERR_READ_ONLY_FILESYSTEM;
}
std::io::ErrorKind::StaleNetworkFileHandle => {
- r_println!("{}", eformat_cargo!("stale network file handle"));
+ writeln!(result, "{}", eformat_cargo!("stale network file handle")).ok();
ec.exit_code = EC_IO_ERR_STALE_NETWORK_FILE_HANDLE;
}
std::io::ErrorKind::InvalidInput => {
- r_println!("{}", eformat_cargo!("invalid input"));
+ writeln!(result, "{}", eformat_cargo!("invalid input")).ok();
ec.exit_code = EC_IO_ERR_INVALID_INPUT;
}
std::io::ErrorKind::InvalidData => {
- r_println!("{}", eformat_cargo!("invalid data"));
+ writeln!(result, "{}", eformat_cargo!("invalid data")).ok();
ec.exit_code = EC_IO_ERR_INVALID_DATA;
}
std::io::ErrorKind::TimedOut => {
- r_println!("{}", eformat_cargo!("timed out"));
+ writeln!(result, "{}", eformat_cargo!("timed out")).ok();
ec.exit_code = EC_IO_ERR_TIMED_OUT;
}
std::io::ErrorKind::WriteZero => {
- r_println!("{}", eformat_cargo!("write zero"));
+ writeln!(result, "{}", eformat_cargo!("write zero")).ok();
ec.exit_code = EC_IO_ERR_WRITE_ZERO;
}
std::io::ErrorKind::StorageFull => {
- r_println!("{}", eformat_cargo!("storage full"));
+ writeln!(result, "{}", eformat_cargo!("storage full")).ok();
ec.exit_code = EC_IO_ERR_STORAGE_FULL;
}
std::io::ErrorKind::NotSeekable => {
- r_println!("{}", eformat_cargo!("not seekable"));
+ writeln!(result, "{}", eformat_cargo!("not seekable")).ok();
ec.exit_code = EC_IO_ERR_NOT_SEEKABLE;
}
std::io::ErrorKind::QuotaExceeded => {
- r_println!("{}", eformat_cargo!("quota exceeded"));
+ writeln!(result, "{}", eformat_cargo!("quota exceeded")).ok();
ec.exit_code = EC_IO_ERR_QUOTA_EXCEEDED;
}
std::io::ErrorKind::FileTooLarge => {
- r_println!("{}", eformat_cargo!("file too large"));
+ writeln!(result, "{}", eformat_cargo!("file too large")).ok();
ec.exit_code = EC_IO_ERR_FILE_TOO_LARGE;
}
std::io::ErrorKind::ResourceBusy => {
- r_println!("{}", eformat_cargo!("resource busy"));
+ writeln!(result, "{}", eformat_cargo!("resource busy")).ok();
ec.exit_code = EC_IO_ERR_RESOURCE_BUSY;
}
std::io::ErrorKind::ExecutableFileBusy => {
- r_println!("{}", eformat_cargo!("executable file busy"));
+ writeln!(result, "{}", eformat_cargo!("executable file busy")).ok();
ec.exit_code = EC_IO_ERR_EXECUTABLE_FILE_BUSY;
}
std::io::ErrorKind::Deadlock => {
- r_println!("{}", eformat_cargo!("deadlock"));
+ writeln!(result, "{}", eformat_cargo!("deadlock")).ok();
ec.exit_code = EC_IO_ERR_DEADLOCK;
}
std::io::ErrorKind::CrossesDevices => {
- r_println!("{}", eformat_cargo!("crosses devices"));
+ writeln!(result, "{}", eformat_cargo!("crosses devices")).ok();
ec.exit_code = EC_IO_ERR_CROSSES_DEVICES;
}
std::io::ErrorKind::TooManyLinks => {
- r_println!("{}", eformat_cargo!("too many links"));
+ writeln!(result, "{}", eformat_cargo!("too many links")).ok();
ec.exit_code = EC_IO_ERR_TOO_MANY_LINKS;
}
std::io::ErrorKind::InvalidFilename => {
- r_println!("{}", eformat_cargo!("invalid filename"));
+ writeln!(result, "{}", eformat_cargo!("invalid filename")).ok();
ec.exit_code = EC_IO_ERR_INVALID_FILENAME;
}
std::io::ErrorKind::ArgumentListTooLong => {
- r_println!("{}", eformat_cargo!("argument list too long"));
+ writeln!(result, "{}", eformat_cargo!("argument list too long")).ok();
ec.exit_code = EC_IO_ERR_ARGUMENT_LIST_TOO_LONG;
}
std::io::ErrorKind::Interrupted => {
- r_println!("{}", eformat_cargo!("interrupted"));
+ writeln!(result, "{}", eformat_cargo!("interrupted")).ok();
ec.exit_code = EC_IO_ERR_INTERRUPTED;
}
std::io::ErrorKind::Unsupported => {
- r_println!("{}", eformat_cargo!("unsupported"));
+ writeln!(result, "{}", eformat_cargo!("unsupported")).ok();
ec.exit_code = EC_IO_ERR_UNSUPPORTED;
}
std::io::ErrorKind::UnexpectedEof => {
- r_println!("{}", eformat_cargo!("unexpected end of file"));
+ writeln!(result, "{}", eformat_cargo!("unexpected end of file")).ok();
ec.exit_code = EC_IO_ERR_UNEXPECTED_EOF;
}
std::io::ErrorKind::OutOfMemory => {
- r_println!("{}", eformat_cargo!("out of memory"));
+ writeln!(result, "{}", eformat_cargo!("out of memory")).ok();
ec.exit_code = EC_IO_ERR_OUT_OF_MEMORY;
}
std::io::ErrorKind::Other => {
- r_println!("{}", eformat_cargo!(err.to_string()));
+ writeln!(result, "{}", eformat_cargo!(err.to_string())).ok();
ec.exit_code = EC_IO_ERR_OTHER;
}
_ => {
- r_println!("{}", eformat_cargo!(err.to_string()));
+ writeln!(result, "{}", eformat_cargo!(err.to_string())).ok();
ec.exit_code = EC_IO_ERR_OTHER;
}
}
+ result
}
diff --git a/mling/src/lib.rs b/mling/src/lib.rs
index 1bf38a7..1de1228 100644
--- a/mling/src/lib.rs
+++ b/mling/src/lib.rs
@@ -1,7 +1,7 @@
#![allow(unused_imports)]
use mingling::{
- macros::{chain, gen_program, pack, r_println, renderer},
+ macros::{chain, gen_program, pack, renderer},
res::ResExitCode,
};
diff --git a/mling/src/proj_mgr/generator.rs b/mling/src/proj_mgr/generator.rs
index 48a1753..e560ae0 100644
--- a/mling/src/proj_mgr/generator.rs
+++ b/mling/src/proj_mgr/generator.rs
@@ -1,11 +1,12 @@
use std::path::{self, PathBuf};
use mingling::{
- Groupped,
- macros::{chain, pack, r_println, renderer, route},
+ macros::{chain, pack, renderer, route},
+ Groupped, RenderResult,
};
+use std::io::Write as _;
-use crate::{Next, proj_mgr::EntryGenerateProject, res::ResCurrentDir};
+use crate::{proj_mgr::EntryGenerateProject, res::ResCurrentDir, Next};
pack!(StateGenerateProjectReady = PathBuf);
pack!(ResultGenerateProjectChecklistCreated = PathBuf);
@@ -35,12 +36,22 @@ pub fn handle_state_gen_proj_ready(prev: StateGenerateProjectReady) -> Next {
}
#[renderer]
-pub fn render_gen_proj_checklist_created(result: ResultGenerateProjectChecklistCreated) {
- r_println!(
+pub fn render_gen_proj_checklist_created(
+ result: ResultGenerateProjectChecklistCreated,
+) -> RenderResult {
+ let mut res = RenderResult::default();
+ writeln!(
+ res,
"Successfully create {} at \"{}\"",
CHECK_LIST_NAME,
result.to_string_lossy()
- );
- r_println!("");
- r_println!("Please fill in {CHECK_LIST_NAME} and run `mling gen` again to continue generating");
+ )
+ .ok();
+ writeln!(res).ok();
+ writeln!(
+ res,
+ "Please fill in {CHECK_LIST_NAME} and run `mling gen` again to continue generating"
+ )
+ .ok();
+ res
}
diff --git a/mling/src/proj_mgr/show_binaries.rs b/mling/src/proj_mgr/show_binaries.rs
index d6872cf..fd2f406 100644
--- a/mling/src/proj_mgr/show_binaries.rs
+++ b/mling/src/proj_mgr/show_binaries.rs
@@ -2,18 +2,19 @@ use std::path::PathBuf;
use colored::Colorize;
use mingling::{
- Groupped,
- macros::{chain, pack, r_print, r_println, renderer},
+ macros::{chain, pack, renderer},
+ Groupped, RenderResult,
};
use serde::Serialize;
+use std::io::Write as _;
use crate::{
- Next,
proj_mgr::{
+ metadata::{read_metadata, CargoLockFile},
EntryShowBinaries,
- metadata::{CargoLockFile, read_metadata},
},
res::ResManifestPath,
+ Next,
};
#[derive(Serialize, Groupped)]
@@ -58,16 +59,20 @@ pub fn handle_show_binaries(_args: EntryShowBinaries, manifest_path: &ResManifes
}
#[renderer]
-pub fn render_binaries(binaries: ResultBinaries) -> String {
- r_println!("{}", "Binaries:".bright_cyan().bold());
+pub fn render_binaries(binaries: ResultBinaries) -> RenderResult {
+ let mut result = RenderResult::default();
+ writeln!(result, "{}", "Binaries:".bright_cyan().bold()).ok();
if let Some(max_name_len) = binaries.binaries.iter().map(|b| b.name.len()).max() {
for binary in &binaries.binaries {
- r_println!(
+ writeln!(
+ result,
" {:width$} `{}`",
binary.name.bright_yellow().bold(),
binary.path.display().to_string().italic(),
width = max_name_len
- );
+ )
+ .ok();
}
}
+ result
}
diff --git a/mling/src/proj_mgr/show_directories.rs b/mling/src/proj_mgr/show_directories.rs
index e84bf69..5bfb090 100644
--- a/mling/src/proj_mgr/show_directories.rs
+++ b/mling/src/proj_mgr/show_directories.rs
@@ -1,14 +1,15 @@
use colored::Colorize;
use mingling::{
- Groupped,
- macros::{chain, pack, r_println, renderer},
+ macros::{chain, pack, renderer},
+ Groupped, RenderResult,
};
use serde::Serialize;
+use std::io::Write as _;
use crate::{
- Next,
- proj_mgr::{EntryShowTargetDirectories, EntryShowWorkspaceDirectory, metadata::read_metadata},
+ proj_mgr::{metadata::read_metadata, EntryShowTargetDirectories, EntryShowWorkspaceDirectory},
res::ResManifestPath,
+ Next,
};
#[derive(Serialize, Groupped)]
@@ -46,11 +47,15 @@ pub fn handle_show_target_directory(
}
#[renderer]
-pub fn render_workspace_directory(prev: ResultWorkspaceDirectory) {
- r_println!("{}", prev.path.bright_cyan().bold());
+pub fn render_workspace_directory(prev: ResultWorkspaceDirectory) -> RenderResult {
+ let mut result = RenderResult::default();
+ writeln!(result, "{}", prev.path.bright_cyan().bold()).ok();
+ result
}
#[renderer]
-pub fn render_target_directory(prev: ResultTargetDirectory) {
- r_println!("{}", prev.path.bright_cyan().bold());
+pub fn render_target_directory(prev: ResultTargetDirectory) -> RenderResult {
+ let mut result = RenderResult::default();
+ writeln!(result, "{}", prev.path.bright_cyan().bold()).ok();
+ result
}