-module(spruce@output).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spruce/output.gleam").
-export([new/1, context/1, append/2, text/2, blank/1, group/3, to_string/1, print/1]).
-export_type([output/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Pipeable output accumulation.\n"
"\n"
" An `Output` threads a `Spruce` context and a buffer of rendered blocks\n"
" through a pipeline, so several renderers compose with `|>` and emit\n"
" together. It stays pure: nothing is printed until `print`, and the context\n"
" is threaded for you so each renderer sees the right color level and indent\n"
" depth.\n"
"\n"
" ```gleam\n"
" import spruce\n"
" import spruce/message\n"
" import spruce/output\n"
"\n"
" pub fn main() {\n"
" let sp = spruce.detect()\n"
"\n"
" output.new(sp)\n"
" |> output.append(message.start(_, \"compiling\"))\n"
" |> output.group(\"Tests\", fn(o) {\n"
" o |> output.append(message.info(_, \"running\"))\n"
" })\n"
" |> output.print\n"
" }\n"
" ```\n"
"\n"
" `append` accepts any `Spruce -> String` renderer via a `_` capture, so it\n"
" works with every spruce module without per-type variants. For eager,\n"
" streaming grouping that prints as work happens and can return a value, use\n"
" `spruce/group.group` instead.\n"
).
-opaque output() :: {output, spruce:spruce(), list(binary())}.
-file("src/spruce/output.gleam", 45).
?DOC(" Start an empty output that renders with `sp`.\n").
-spec new(spruce:spruce()) -> output().
new(Sp) ->
{output, Sp, []}.
-file("src/spruce/output.gleam", 51).
?DOC(
" The context the output renders with. Reflects the current group depth inside\n"
" a `group` body.\n"
).
-spec context(output()) -> spruce:spruce().
context(Output) ->
erlang:element(2, Output).
-file("src/spruce/output.gleam", 58).
?DOC(
" Append a rendered block produced by `render`, which receives the output's\n"
" context. Works with any `Spruce -> String` renderer via a `_` capture, e.g.\n"
" `output.append(message.success(_, \"done\"))`.\n"
).
-spec append(output(), fun((spruce:spruce()) -> binary())) -> output().
append(Output, Render) ->
{output,
erlang:element(2, Output),
[Render(erlang:element(2, Output)) | erlang:element(3, Output)]}.
-file("src/spruce/output.gleam", 63).
?DOC(" Append a raw string as-is, without rendering.\n").
-spec text(output(), binary()) -> output().
text(Output, Text) ->
{output, erlang:element(2, Output), [Text | erlang:element(3, Output)]}.
-file("src/spruce/output.gleam", 68).
?DOC(" Append a blank line.\n").
-spec blank(output()) -> output().
blank(Output) ->
{output,
erlang:element(2, Output),
[<<""/utf8>> | erlang:element(3, Output)]}.
-file("src/spruce/output.gleam", 75).
?DOC(
" Append a styled group title, then run `body` with the output's context\n"
" indented one level deeper. Blocks appended inside `body` nest under the\n"
" title. Unlike `spruce/group.group`, this buffers output rather than printing.\n"
).
-spec group(output(), binary(), fun((output()) -> output())) -> output().
group(Output, Title, Body) ->
Titled = text(
Output,
spruce@group:render_title(erlang:element(2, Output), Title)
),
Body_output = Body(
{output,
spruce:indented(erlang:element(2, Output)),
erlang:element(3, Titled)}
),
{output, erlang:element(2, Output), erlang:element(3, Body_output)}.
-file("src/spruce/output.gleam", 87).
?DOC(" Render the accumulated output to a single string, blocks joined by newlines.\n").
-spec to_string(output()) -> binary().
to_string(Output) ->
_pipe = erlang:element(3, Output),
_pipe@1 = lists:reverse(_pipe),
gleam@string:join(_pipe@1, <<"\n"/utf8>>).
-file("src/spruce/output.gleam", 94).
?DOC(" Print the accumulated output to stdout.\n").
-spec print(output()) -> nil.
print(Output) ->
gleam_stdlib:println(to_string(Output)).