pub enum MarkerKind {
Plain,
PromptStart,
CommandStart,
OutputStart,
CommandFinished(Option<i32>),
}Expand description
What a marker means (#158). A plain add_marker decoration carries no
semantics (MarkerKind::Plain); OSC 133 shell-integration marks carry the
command-boundary role (prompt/command/output start, or command finished with
its optional exit code). The engine only parses and anchors these — the
success/failure colour, earcon and prompt-to-prompt navigation are consumer
policy (ADR-0017), driven off the kind + exit the wire (#159) carries.
Deliberately exhaustive (#843), and the first draft of that sweep got this
one wrong — the compiler caught it. The reasoning that failed: OSC 133 has
more subcommands than the four modelled, and MarkerKind::Plain is already
the shape an unrecognised mark takes, so a consumer meeting a new member has
somewhere to put it. True, and not the whole question.
This enum rides the wire, so a new member is not a consumer’s problem
first — it is an encoder’s. justerm-wasm-decode maps every member onto a
numeric wire triple, and marking this non-exhaustive forces a _ arm there,
which converts a future compile error into a silently wrong wire value. That
is the exact trade FrameKind is left exhaustive for, one type over in this
same file: a new member here moves WIRE_VERSION (ADR-0008), and a wire bump
is a louder gate than semver, so the attribute would soften the quieter
signal while removing the loud one.
The rule, stated by mechanism rather than by symptom, because the first
phrasing — “a wire-carried enum stays exhaustive” — misclassified at both
ends. It over-captured DecodeError, which ADR-0008 makes a wire contract by
name yet which crosses the boundary through Debug (total, no arms) and is
therefore free to take the attribute; and it under-captured
crate::CursorShape, which is not obviously “wire-carried” from its own
module and is mapped exactly like this one. The mechanism:
An enum whose members are mapped onto wire values by a
matchoutside this crate stays exhaustive.
Measured over justerm-wasm-decode/src — the published encoder, and the only
place the boundary bites — that is four: crate::CursorShape
(lib.rs:198), FrameKind (:192), this one (:233), and
crate::UnderlineStyle, which joined when #831 gave the style a name on the
published surface. Every other public enum has zero such sites, so no other
call turns on this rule.
Do not read that count from here. It was “exactly three” until #831 and this
paragraph is prose, checked by nothing — the executable roster is
justerm-wasm-decode/tests/wire_enum_stays_exhaustive.rs, which derives the set
from core’s own sources and is what noticed that cell.rs was outside its scan.
Nothing but cargo test --workspace would have said so: #[non_exhaustive]
binds only across a crate boundary, so the defect compiles fine inside
justerm-core and a bare cargo test never sees it. That is the load-bearing
half of the --workspace release.md insists on.
But do not mistake that gate for a detector of this rule. It fired here by
the accident that this enum’s wire mapping lives one crate over. Color is
wire-carried too (encode_color, below) and its only exhaustive match is
inside this crate, where the attribute does nothing — so marking Color
would leave the workspace green and put the attribute on a wire-carried
enum unnoticed. The rule is currently held by this paragraph and by nothing
executable.
Why the direction is asymmetric at all, which is the fact the whole sweep
turns on and is easy to state backwards: an exhaustive enum does not force a
consumer to handle a new member — they may write _ whenever they like. It
preserves their option to be forced. #[non_exhaustive] removes that
option, and on stable Rust it is irreversible: measured on this repo’s pinned
1.96.0, #![deny(non_exhaustive_omitted_patterns)] is an unknown lint, so a
consumer cannot opt back in. One direction is a default the consumer can
change; the other is a decision taken on their behalf for good.
And #843 runs on two axes, not one. The first draft wrote down only the first, which left five calls looking arbitrary until a refuting pass named the gap. They answer different questions and neither substitutes for the other:
- Openness — can this set actually grow? This decides whether the attribute
is warranted. Putting it on a closed set states something false in the type;
crate::Sidewill not gain a third member, and saying it might is worse than saying nothing. - Direction — does a consumer ever receive one? This decides the cost, not the need. Where nothing public hands the enum outward, there is no match to preserve and the attribute costs a consumer nothing — so a genuine doubt about openness resolves toward marking. Where the enum comes outward, the option being removed is real and closure has to be shown.
Read together they explain the whole roster: crate::Key is inward and
open, so it is marked; crate::SelectionType is inward and closed by
convergence, so it is not; crate::Color comes outward and is closed, so it
is not twice over.
Variants§
Plain
A add_marker decoration anchor (#118) — no OSC-133 semantics.
PromptStart
OSC 133;A — the shell prompt begins here.
CommandStart
OSC 133;B — the typed command begins here (the prompt ended).
OutputStart
OSC 133;C — the command was submitted; its output begins here.
CommandFinished(Option<i32>)
OSC 133;D[;exit] — the command finished, with its exit code if reported
(absent, empty or non-numeric → None).
Trait Implementations§
Source§impl Clone for MarkerKind
impl Clone for MarkerKind
Source§fn clone(&self) -> MarkerKind
fn clone(&self) -> MarkerKind
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more