Expand description
A tiny, dependency-free builder for operator-facing diagnostic messages.
Kopiur’s house style is that every message a human reads — an admission
denial, a Warning Event note, a status.conditions[].message — says
what failed, why, and how to fix it. That rule was enforced only by
discipline and code review. Diagnostic makes the shape mechanical:
it renders in one canonical order — lead → why → fix — so the specific
problem is always first.
Leading with the specific problem is not cosmetic. kubectl get truncates a
condition message to its column width, and a kubectl describe reader scans
the first clause of a wall of Events. If the lead is a generic
"reconcile failed: …" or the raw first line of a stack of kopia stderr, the
useful part is exactly what gets cut. A tight lead survives truncation; the
why and fix trail behind it where there is room.
This module is pure core::fmt — no serde, no kube, no tokio — so both
kopiur-api (validators) and the controller/mover can build messages the same
way without pulling controller-runtime into the API crate.
use kopiur_api::message::Diagnostic;
let msg = Diagnostic::new("a repository lock is held by another writer")
.fix("it usually clears on its own; retry")
.to_string();
assert_eq!(msg, "a repository lock is held by another writer. Fix: it usually clears on its own; retry");
// Lead alone is a valid message; why + fix are optional.
assert_eq!(Diagnostic::new("nothing to do").to_string(), "nothing to do");Structs§
- Diagnostic
- A structured operator-facing message rendered as lead → why → fix.
Constants§
- MAX_
MESSAGE_ CHARS - The most an operator-facing message may be: enough for a packed what/why/fix,
short enough to read at a glance in
kubectl describe. Past this a message is almost always explaining rather than saying what is wrong.
Functions§
- message_
shape_ issue - Report why
msgviolates the operator-message shape rules, orNoneif it is well-formed. Pure and always-compiled so tests in any crate (validators inkopiur-api, event/condition builders in the controller and mover) can assert their user-facing strings against one checker.