diff options
| -rw-r--r-- | CHANGELOG.md | 8 | ||||
| -rw-r--r-- | mingling/src/lib.rs | 6 | ||||
| -rw-r--r-- | mingling_core/src/renderer/render_result.rs | 44 | ||||
| -rw-r--r-- | mingling_macros/src/func/r_print.rs | 57 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 30 |
5 files changed, 145 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e82d9..2bc0318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -302,6 +302,14 @@ None - Updated `try_redispatch_simple` and `try_redispatch_completion` to emit `#[#exts]` instead of bare `#exts`, ensuring proper attribute syntax in the re-dispatched token stream. - Registered `#[proc_macro_attribute] pub fn routeify` in `mingling_macros/src/lib.rs`. +12. **[`core`]** **[`macros`]** Added the `r_append!` macro and `RenderResult::append_other()` method for appending the contents of one `RenderResult` to another. The `append_other()` method on `RenderResult` merges the buffered content (text and output modes) from another `RenderResult` into the current one. If the destination result has `immediate_output` enabled but the source does not, the source's content will be immediately flushed to the appropriate output stream (stdout/stderr) while also being appended to the render buffer. The `exit_code` of the source result is **not** transferred — only the buffered content and the `immediate_output` flag are merged. + + The `r_append!` macro supports two usage forms: + - **Explicit buffer** — `r_append!(dst, src)` appends the contents of `src` into the `dst` `RenderResult`. + - **Implicit buffer** — `r_append!(src)` appends the contents of `src` into the implicit `__render_result_buffer` (available inside `#[buffer]` functions). + + The macro is re-exported as `mingling::macros::r_append` and included in `mingling::prelude::*`. + #### **BREAKING CHANGES** (API CHANGES): 1. **[`macros:renderer`]** **[`macros:help`]** Removed `r_println!` and `r_print!` macros from being implicitly injected by `#[renderer]` and `#[help]` macros. These macros still exist, but must now be used **explicitly** — either with an explicit buffer argument, or via the `#[buffer]` extension attribute that re-enables implicit buffer injection. diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index 3539647..457f393 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -98,6 +98,9 @@ pub mod macros { /// `#[program_setup]` - Used to generate program setup #[cfg(feature = "extra_macros")] pub use mingling_macros::program_setup; + /// `r_append!` - Appends the contents of one `RenderResult` to another. + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_append; /// `r_eprint!` - Prints text to a `RenderResult` error buffer (without newline). /// See the macro documentation for implicit vs. explicit buffer usage. pub use mingling_macros::r_eprint; @@ -235,6 +238,9 @@ pub mod prelude { /// Like `pack!` but also marks the type for structured output #[cfg(all(feature = "macros", feature = "structural_renderer"))] pub use mingling_macros::pack_structural; + /// `r_append!` - Appends the contents of one `RenderResult` to another. + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_append; /// `r_eprint!` - Prints text to a `RenderResult` error buffer (without newline). /// See the macro documentation for implicit vs. explicit buffer usage. pub use mingling_macros::r_eprint; diff --git a/mingling_core/src/renderer/render_result.rs b/mingling_core/src/renderer/render_result.rs index af63cdd..a495857 100644 --- a/mingling_core/src/renderer/render_result.rs +++ b/mingling_core/src/renderer/render_result.rs @@ -209,6 +209,50 @@ impl RenderResult { self.append_to_buffer("\n", mode); } + /// Appends the contents of another `RenderResult` to this one. + /// + /// If this `RenderResult` has `immediate_output` enabled but the other does not, + /// the other's content will be immediately flushed to the appropriate output stream + /// (stdout/stderr) while also being appended to the render buffer. + /// + /// The `exit_code` of the other result is **not** transferred — only the buffered + /// content and the `immediate_output` flag of the other result are merged. + /// + /// # Arguments + /// + /// * `other` - The `RenderResult` whose contents should be appended. + /// + /// # Examples + /// + /// ``` + /// use mingling_core::{RenderResult, RenderResultMode}; + /// + /// let mut dest = RenderResult::default(); + /// let mut src = RenderResult::default(); + /// + /// src.append_to_buffer("Hello", RenderResultMode::Stdout); + /// src.append_to_buffer(" Error", RenderResultMode::Stderr); + /// + /// dest.append_otehr(src); + /// assert_eq!(dest.to_string(), "Hello Error"); + /// ``` + pub fn append_other(&mut self, other: impl Into<RenderResult>) { + let other = other.into(); + + // If self has immediate output enabled, but the input does not, the input needs immediate output. + let immediate_output = !other.immediate_output && self.immediate_output; + + for i in other.render_buffer { + if immediate_output { + match &i.1 { + Stdout => print!("{}", i.0), + Stderr => eprint!("{}", i.0), + } + } + self.render_buffer.push(i); + } + } + /// Appends the given text to the rendered content. /// /// # Examples diff --git a/mingling_macros/src/func/r_print.rs b/mingling_macros/src/func/r_print.rs index 0f77b2b..86ea52f 100644 --- a/mingling_macros/src/func/r_print.rs +++ b/mingling_macros/src/func/r_print.rs @@ -68,3 +68,60 @@ pub(crate) fn r_eprintln(input: TokenStream) -> TokenStream { 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() +} diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 5e7a40f..5d8d7e0 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -1478,6 +1478,36 @@ pub fn r_eprint(input: TokenStream) -> TokenStream { func::r_print::r_eprint(input) } +/// Appends the contents of one `RenderResult` to another. +/// +/// # Implicit buffer (inside `#[buffer]` functions) +/// +/// ```rust,ignore +/// use mingling::macros::{buffer, r_append}; +/// +/// #[buffer] +/// fn render() { +/// let other = make_other_result(); +/// r_append!(other); +/// } +/// ``` +/// +/// # Explicit buffer +/// +/// ```rust,ignore +/// use mingling::macros::r_append; +/// use mingling::RenderResult; +/// +/// let mut dst = RenderResult::new(); +/// let src = RenderResult::from("hello"); +/// r_append!(dst, src); +/// assert!(!dst.is_empty()); +/// ``` +#[proc_macro] +pub fn r_append(input: TokenStream) -> TokenStream { + func::r_print::r_append(input) +} + /// Derive macro for automatically implementing the `Grouped` trait on a struct. /// /// The `#[derive(Grouped)]` macro: |
