Skip to main content

Todo CLI in Rust 5. Next step: moving from CLI to a TUI with ratatui

·3 mins
Rafael Fernandez
Author
Rafael Fernandez
Mathematics, programming, and life stuff
Todo CLI in Rust without fluff - This article is part of a series.
Part 5: This Article

We close the series with the most natural next question: now that domain, use cases, and persistence are solid, what is the next meaningful step?

Read this chapter in Spanish: ES

For me, it is clear: build the same tool as a TUI with ratatui.rs.

Not as decoration, but as product evolution.

From strict terminal commands to a rat-run kitchen service
#

The current CLI is great for direct commands:

  • add
  • list
  • done
  • todo
  • delete

But when workflow becomes interactive (browse, filter repeatedly, bulk status updates), command-by-command interaction starts adding friction.

A TUI gives you:

  • keyboard-driven navigation,
  • fast state changes with shortcuts,
  • full context on screen,
  • fewer command round-trips for daily usage.

Good news: the pantry is already stocked
#

Thanks to current architecture in todo-cli-rs:

we can swap the input/output adapter without touching business rules.

That is exactly what the chapter 1 boundary decisions were for.

Two menus in one restaurant: CLI for scripts, TUI for humans
#

The next step is coexistence, not replacement:

  • CLI mode for automation and scripting,
  • TUI mode for daily interactive usage.

Example:

  • todo-cli add "x" still works,
  • todo-cli tui launches ratatui.

What changes and what stays still
#

Stays:

  • Task invariants,
  • existing use cases,
  • JSON/in-memory repository adapters,
  • layered error model.

Changes:

  • new adapters/tui module,
  • event loop (keys, ticks, redraw),
  • UI state management (selection, focus, active filters),
  • widget rendering and layout.

Kitchen floor plan: fitting ratatui without breaking plates
#

Suggested structure:

src/tasks/adapters/
  cli/
  tui/
    app_state.rs
    events.rs
    renderer.rs
    screens/
      task_list.rs
      task_detail.rs

app_state owns UI concerns; user actions trigger existing use cases.

Examples:

  • d on selected task -> MarkTaskDoneUseCase
  • t -> MarkTaskTodoUseCase
  • a -> add flow via AddTaskUseCase

Where the rat sweats: real technical challenges
#

  1. UI state model

    • separate interface state from domain state.
  2. Error handling without killing session

    • CLI can print and exit,
    • TUI must surface errors in-place and keep running.
  3. Refresh performance and consistency

    • avoid unnecessary redraws,
    • keep view and repository state in sync.
  4. UI-focused testing

    • keep relying on domain/use-case tests,
    • add targeted event/render tests for critical flows.

Recipe steps: an implementation roadmap
#

  1. Add tui subcommand to current parser.
  2. Create adapters/tui with a basic task list screen.
  3. Wire key mappings for done/todo/delete via existing use cases.
  4. Add live status filtering.
  5. Add quick add flow.
  6. Refine UX: shortcuts, contextual help, inline error bar.

Closing: less key-punishment, more flow (with cheese)
#

Moving to ratatui is not about making things prettier. It is about increasing throughput without rewriting core logic.

If architecture boundaries are healthy, CLI-to-TUI is not major surgery. It is a new adapter layered on top of a domain that already behaves correctly.

And that was the whole bet of this series.

Todo CLI in Rust without fluff - This article is part of a series.
Part 5: This Article