Skip to main content

Todo CLI in Rust 3. Building the CLI with clap

·2 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 3: This Article

With domain and use cases in place, we moved to the layer users actually touch: the CLI.

Read this chapter in Spanish: ES

The key question was straightforward: do we want a CLI that merely works, or one that remains predictable under real usage?

Code references:

Strong parsing with clap
#

Subcommands:

  • add {title}
  • list {--status}
  • done {id}
  • todo {id}
  • delete {id}

Plus global --output table|json.

Why ValueEnum instead of manual parsing
#

StatusArg and OutputFormat are typed enums. We could parse raw strings and match manually, but that duplicates validation and custom error text.

With clap, invalid values fail at the input boundary before use-case execution.

UUID as input type, not delayed string parsing
#

done/todo/delete receive Uuid directly.

Alternative: accept String and parse later in application.

Trade-off:

  • delayed parsing leaks input concerns into business flow,
  • earlier parsing gives immediate, precise user feedback.

Dual output contract: table for humans, json for machines
#

Many CLIs are nice for humans but brittle for automation.

We intentionally support both:

  • table for terminal readability,
  • json for scripts and integration.

Examples:

cargo run -- list
cargo run -- --output json list --status done

Interesting case: delete
#

delete is idempotent in behavior:

  • deleted = true if task existed,
  • deleted = false if not found.

In JSON mode we return structured payload (id, deleted, message) so scripts do not need fragile text parsing.

main.rs as wiring only
#

run() does three things:

  1. parse CLI,
  2. build repository,
  3. dispatch to use case.

No business rules live there.

Commit that captures this transition
#

You can see the shift from “functional CLI” to “stable interface contract” very clearly.

Closing
#

A mature CLI is less about command count and more about predictable behavior.

Next chapter closes the core loop: JSON persistence, tests, and the explicit technical debt we kept visible.

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