# `ExRatatui.Burrito`
[🔗](https://github.com/mcass19/ex_ratatui/blob/v0.13.0/lib/ex_ratatui/burrito.ex#L1)

Runtime support for TUIs packaged as single-file binaries with
[Burrito](https://github.com/burrito-elixir/burrito).

The CLI module scaffolded by `mix ex_ratatui.gen.burrito` is a thin shim
that delegates to `start_link/3`, so fixes to the entry-point protocol
ship with ex_ratatui upgrades instead of freezing in generated consumer
code.

Inside a wrapped binary the TUI runs **synchronously**, blocking OTP
application startup for its whole lifetime. Burrito boots the release
with `:elixir.start_cli`, which halts the node the moment the boot's
`-s` call returns — running the TUI in an async task loses that race and
the binary exits before drawing a frame. Blocking `Application.start`
keeps the boot (and therefore the VM) alive until the TUI exits, at
which point `main/3` stops the VM itself.
`verify_linux_nif/1` is a release step that turns the most common
packaging mistake — building the linux target without `TARGET_ABI=musl`
— into an immediate build error instead of a shipped-broken binary.

Nothing here references Burrito at compile time: the wrapped-binary
check reads the `__BURRITO` environment variable the burrito wrapper
sets when launching the payload, which its maintainers document as the
supported detection mechanism, so ex_ratatui needs no burrito
dependency.

# `main`

```elixir
@spec main(module(), [String.t()], keyword()) :: :ok
```

Runs a burrito-wrapped TUI to completion.

Boots `tui_module` (any module using `ExRatatui.App`), waits for it to
exit, then stops the VM with a matching exit code so the wrapper
returns control to the shell: 0 after a clean exit, 1 when the TUI
crashes, fails to start (no TTY, NIF/host mismatch), or the entry point
itself raises.

A no-op unless running inside a burrito-wrapped binary. Callers reach
this through `start_link/3`, which runs it synchronously in a wrapped
binary and asynchronously otherwise. A `--version` flag anywhere in
`argv` prints `name version` and exits 0 without a TTY; it first forces
the NIF `dlopen`, so a precompiled-NIF/host mismatch fails loudly
rather than silently exiting 0.

## Options

  * `:name` (required) — the binary's name, used in `--version` output
    and error messages.
  * `:version` — the version string `--version` prints.
  * `:halt` — 1-arity function invoked with the exit code instead of
    `System.halt/1`; exists for tests and embedders. Defaults to
    `System.halt/1` rather than `System.stop/1` because the TUI runs
    during application startup — a graceful `System.stop/1` would wait
    on the same still-starting application and deadlock.

# `start_link`

```elixir
@spec start_link(module(), [String.t()], keyword()) :: {:ok, pid()} | :ignore
```

Supervised entry point for a burrito-wrapped TUI — the scaffolded CLI's
`start_link/1` delegates here.

Inside a wrapped binary (`__BURRITO` set) the TUI runs **synchronously**
in the calling process, so a supervised child's `start_link` blocks OTP
boot until the TUI exits — see the moduledoc for why an async task would
lose the race against Burrito's `start_cli` halt. `main/3` stops the VM
when the TUI exits, so this never returns in a wrapped binary.

Outside a wrapped binary (a consumer's `mix test` / `iex -S mix`) it
starts an async, no-op task, so the supervised child never takes over
the session. Options are the same as `main/3`.

# `verify_linux_nif`

```elixir
@spec verify_linux_nif(
  Mix.Release.t(),
  {atom(), String.t()}
) :: Mix.Release.t()
```

Release step that fails the build when the linux burrito target bundles
a glibc NIF.

Burrito's linux wrapper runs a musl runtime, so the linux target must
bundle the musl NIF (`TARGET_ABI=musl` at `mix release` time) — a glibc
`.so` cannot load there and the shipped binary would hang at NIF load
on every end-user machine. Wire it between `:assemble` and
`Burrito.wrap/1`:

    steps: [:assemble, &ExRatatui.Burrito.verify_linux_nif/1, &Burrito.wrap/1]

A no-op unless `BURRITO_TARGET=linux`. Detection is a byte scan of the
assembled NIF: glibc-linked ELFs embed `libc.so.6` and `GLIBC_` version
references, musl ones reference `libc.so` alone.

Only the NIF this build actually loads is scanned — the single path
handed to `:erlang.load_nif/2`, which `load_from` names. `priv/native`
is a junk drawer otherwise: rustler_precompiled never evicts the
artifacts of earlier versions or ABIs, and a path dependency carries
the whole directory into the release, so scanning every `.so` there
fails on stale glibc siblings that no runtime ever opens.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
