Replace anyhow with miette #15

Open
opened 2026-08-17 22:06:09 +00:00 by rosa · 1 comment
Owner

I like miette a lot. With the fancy feature.

I like `miette` a lot. With the `fancy` feature.
Author
Owner

This was generated by AI during triage.

Agent Brief

Category: enhancement
Summary: Replace anyhow with miette, giving each library module a concrete diagnostic error type with codes, help text, and a source span on the duration parser

Current behavior:

Every fallible path in the crate returns anyhow::Result. Errors are built two
ways: with_context wrapping an underlying failure (about 29 sites) and
anyhow!/bail! raising a bare message (about 7 sites). All of them carry a
string and nothing else — no machine-readable code, no suggested next step, and
no pointer into the input that caused the failure.

Three consequences:

  • anyhow::Result appears in the public signatures of the library crate
    (status loading, config loading and saving, history loading and appending,
    the session lifecycle functions). Callers of tomate as a library get an
    erased error type with no variants to match on.
  • The binary's main returns anyhow::Result, so a failure prints a plain
    message chain.
  • The build script includes the CLI module's source as part of its own crate,
    which is why anyhow is also listed under [build-dependencies].

Desired behavior:

The crate uses miette (with the fancy feature) as its diagnostic layer, and
each library module defines its own concrete error type instead of an erased
one.

  • Every module that can fail owns an error enum deriving both
    thiserror::Error and miette::Diagnostic. Variants are named for the
    failure, not for the operation that hit it.
  • Every variant carries a diagnostic code following tomate::<module>::<variant>.
  • Any error a user can trigger by misusing the tool carries #[help] naming
    the command that resolves it. At minimum: starting a session while a break is
    running, starting a break while a session is running, acting on a session
    when none is active, and an unparseable duration string.
  • Errors wrapping an underlying failure (file I/O, TOML parsing, hook
    execution, timer scheduling) keep that failure as #[source] so the chain
    still renders.
  • The duration parser's failure carries the input string as
    #[source_code] and a #[label] span, so an unparseable duration renders
    with the offending text underlined.
  • The binary returns miette::Result from main, so the fancy reporter
    renders every failure.

Key interfaces:

  • Public library functions that currently return anyhow::Result<T> return
    Result<T, <Module>Error> with a concrete type. This is a breaking change to
    the library API and is intended.
  • The binary converts module errors into miette::Report at the call site; it
    does not define its own catch-all error enum.
  • The CLI module's duration-parsing helper is compiled twice — once into the
    library/binary and once into the build script. Whatever error type it returns
    must be available in both. Either add miette/thiserror to
    [build-dependencies] alongside the existing entries, or give the parser an
    error type that does not depend on the diagnostic crates. Either is
    acceptable; the build must succeed.
  • anyhow is removed from [dependencies] and [build-dependencies].

Acceptance criteria:

  • No anyhow entry remains in Cargo.toml under any dependency table, and
    no use anyhow or anyhow:: reference remains in the source.
  • miette is a dependency with the fancy feature enabled.
  • Every public fallible function in the library returns a concrete error
    type; no erased or boxed error type appears in the public API.
  • Every diagnostic variant has a code following
    tomate::<module>::<variant>.
  • Running tomate pomodoro start while a break is running, and
    tomate pomodoro stop with no session active, each print help text
    naming the command to run instead.
  • tomate pomodoro start --duration 25x renders a diagnostic that
    underlines the offending input rather than printing a bare message.
  • Errors wrapping an I/O or parse failure still show the underlying cause
    in the rendered output.
  • mise run ci passes — this covers cargo check, cargo audit,
    cargo clippy -- -D warnings, cargo doc, cargo fmt --check, and
    cargo test --all-targets. The build script must build.
  • CHANGELOG.md gains an entry under [Unreleased] marked **BREAKING**,
    describing the public error type change in the same style as the 0.5.0
    entry.

Out of scope:

  • Bumping the version in Cargo.toml. cargo-release owns that; only the
    changelog entry is written by hand.
  • Changing the wording of existing error messages beyond adding help text and
    labels. If a message is wrong, note it rather than rewriting it.
  • Adding error handling to paths that currently cannot fail, or introducing new
    failure cases.
  • Replacing human_panic. The panic handler stays as it is.
  • Changing the tracing setup or any logging output.
  • Restructuring modules, or merging the per-module error enums into one
    crate-wide enum.

Open decision flagged during triage: the brief assumes one error enum per
module rather than a single crate-wide enum. That matches the existing module
layout and keeps each module's failure surface local. If a crate-wide enum is
preferred, say so before starting — it changes the shape of most of the diff.

> *This was generated by AI during triage.* ## Agent Brief **Category:** enhancement **Summary:** Replace `anyhow` with `miette`, giving each library module a concrete diagnostic error type with codes, help text, and a source span on the duration parser **Current behavior:** Every fallible path in the crate returns `anyhow::Result`. Errors are built two ways: `with_context` wrapping an underlying failure (about 29 sites) and `anyhow!`/`bail!` raising a bare message (about 7 sites). All of them carry a string and nothing else — no machine-readable code, no suggested next step, and no pointer into the input that caused the failure. Three consequences: - `anyhow::Result` appears in the public signatures of the library crate (status loading, config loading and saving, history loading and appending, the session lifecycle functions). Callers of `tomate` as a library get an erased error type with no variants to match on. - The binary's `main` returns `anyhow::Result`, so a failure prints a plain message chain. - The build script includes the CLI module's source as part of its own crate, which is why `anyhow` is also listed under `[build-dependencies]`. **Desired behavior:** The crate uses `miette` (with the `fancy` feature) as its diagnostic layer, and each library module defines its own concrete error type instead of an erased one. - Every module that can fail owns an error enum deriving both `thiserror::Error` and `miette::Diagnostic`. Variants are named for the failure, not for the operation that hit it. - Every variant carries a diagnostic code following `tomate::<module>::<variant>`. - Any error a user can trigger by misusing the tool carries `#[help]` naming the command that resolves it. At minimum: starting a session while a break is running, starting a break while a session is running, acting on a session when none is active, and an unparseable duration string. - Errors wrapping an underlying failure (file I/O, TOML parsing, hook execution, timer scheduling) keep that failure as `#[source]` so the chain still renders. - The duration parser's failure carries the input string as `#[source_code]` and a `#[label]` span, so an unparseable duration renders with the offending text underlined. - The binary returns `miette::Result` from `main`, so the `fancy` reporter renders every failure. **Key interfaces:** - Public library functions that currently return `anyhow::Result<T>` return `Result<T, <Module>Error>` with a concrete type. This is a breaking change to the library API and is intended. - The binary converts module errors into `miette::Report` at the call site; it does not define its own catch-all error enum. - The CLI module's duration-parsing helper is compiled twice — once into the library/binary and once into the build script. Whatever error type it returns must be available in both. Either add `miette`/`thiserror` to `[build-dependencies]` alongside the existing entries, or give the parser an error type that does not depend on the diagnostic crates. Either is acceptable; the build must succeed. - `anyhow` is removed from `[dependencies]` and `[build-dependencies]`. **Acceptance criteria:** - [ ] No `anyhow` entry remains in `Cargo.toml` under any dependency table, and no `use anyhow` or `anyhow::` reference remains in the source. - [ ] `miette` is a dependency with the `fancy` feature enabled. - [ ] Every public fallible function in the library returns a concrete error type; no erased or boxed error type appears in the public API. - [ ] Every diagnostic variant has a `code` following `tomate::<module>::<variant>`. - [ ] Running `tomate pomodoro start` while a break is running, and `tomate pomodoro stop` with no session active, each print help text naming the command to run instead. - [ ] `tomate pomodoro start --duration 25x` renders a diagnostic that underlines the offending input rather than printing a bare message. - [ ] Errors wrapping an I/O or parse failure still show the underlying cause in the rendered output. - [ ] `mise run ci` passes — this covers `cargo check`, `cargo audit`, `cargo clippy -- -D warnings`, `cargo doc`, `cargo fmt --check`, and `cargo test --all-targets`. The build script must build. - [ ] `CHANGELOG.md` gains an entry under `[Unreleased]` marked `**BREAKING**`, describing the public error type change in the same style as the 0.5.0 entry. **Out of scope:** - Bumping the version in `Cargo.toml`. `cargo-release` owns that; only the changelog entry is written by hand. - Changing the wording of existing error messages beyond adding help text and labels. If a message is wrong, note it rather than rewriting it. - Adding error handling to paths that currently cannot fail, or introducing new failure cases. - Replacing `human_panic`. The panic handler stays as it is. - Changing the `tracing` setup or any logging output. - Restructuring modules, or merging the per-module error enums into one crate-wide enum. **Open decision flagged during triage:** the brief assumes one error enum per module rather than a single crate-wide enum. That matches the existing module layout and keeps each module's failure surface local. If a crate-wide enum is preferred, say so before starting — it changes the shape of most of the diff.
Sign in to join this conversation.
No description provided.