spruce/output

Pipeable output accumulation.

An Output threads a Spruce context and a buffer of rendered blocks through a pipeline, so several renderers compose with |> and emit together. It stays pure: nothing is printed until print or print_with, and the context is threaded for you so each renderer sees the right color level and indent depth.

import spruce
import spruce/message
import spruce/output

pub fn main() {
  let context = spruce.detect()

  output.new(context)
  |> output.append(message.start(_, "compiling"))
  |> output.group("Tests", fn(output) {
    output |> output.append(message.info(_, "running"))
  })
  |> output.print
}

append accepts any Spruce -> String renderer via a _ capture, so it works with every spruce module without per-type variants. For eager, streaming grouping that writes as work happens and can return a value, use stream_group or stream_group_with instead.

Types

An accumulator of rendered blocks plus the context they render with. Build one with new and the combinators in this module, then finish with to_string or print.

pub opaque type Output

Values

pub fn append(
  output: Output,
  render: fn(spruce.Spruce) -> String,
) -> Output

Append a rendered block produced by render, which receives the output’s context. Works with any Spruce -> String renderer via a _ capture, e.g. output.append(message.success(_, "done")).

pub fn blank(output: Output) -> Output

Append a blank line.

pub fn context(output: Output) -> spruce.Spruce

The context the output renders with. Reflects the current group depth inside a group body.

pub fn group(
  output: Output,
  heading: String,
  body: fn(Output) -> Output,
) -> Output

Append a styled group title, then run body with the output’s context indented one level deeper. Blocks appended inside body nest under the title. Unlike stream_group, this buffers output rather than printing.

pub fn new(context: spruce.Spruce) -> Output

Start an empty output that renders with context.

pub fn print(output: Output) -> Nil

Print the accumulated output to stdout.

pub fn print_with(
  output: Output,
  sink: fn(String) -> result,
) -> result

Write the accumulated output with sink, returning the sink’s result.

The sink receives the rendered output without a trailing newline. Use io.println_error to write to stderr.

pub fn stream_group(
  context: spruce.Spruce,
  heading: String,
  body: fn(spruce.Spruce) -> result,
) -> result

Print a group title, then run body with a context indented one level deeper, returning its result.

This is the eager, streaming form of grouping: the title prints immediately and body runs right away, so its output appears as work happens and it may perform IO and return any value. For deferred, pipe-composable grouping that buffers output instead, see group.

pub fn stream_group_with(
  context: spruce.Spruce,
  heading: String,
  sink: fn(String) -> Nil,
  body: fn(spruce.Spruce) -> result,
) -> result

Write a group title with sink, then run body with a context indented one level deeper, returning its result unchanged.

The sink receives the rendered title without a trailing newline. Use io.println_error to stream a group to stderr.

pub fn text(output: Output, text: String) -> Output

Append a raw string as-is, without rendering.

pub fn title(context: spruce.Spruce, heading: String) -> String

Render a group title block (styled marker + title), the same block that group and stream_group emit. Every non-empty line uses the context indentation.

pub fn to_string(output: Output) -> String

Render the accumulated output to a single string, blocks joined by newlines.

✨ Search Document