Today — Style is the only public struct in the crate with public fields, and the only public type without #[non_exhaustive]. Measured against 0.9.0:
$ # every public struct with pub fields, and whether it is non_exhaustive
screen.rs Style non_exhaustive=NO pub fields: fg, bg, bold, dim, italic,
underline, reverse, blink, conceal, strikethrough
Every public enum — Error, Key, Color, MouseMode, CursorShape, Graphics, Scroll, MouseButton, GraphicsProtocol, GraphicsAction, GraphicsFormat, DecodeError — carries the attribute. Style does not, so a consumer may write Style { fg, bg, bold, dim, italic, underline, reverse, blink, conceal, strikethrough } as a literal and destructure it exhaustively, and adding an eleventh field breaks them.
Why it is worth fixing — an eleventh field is already known to be wanted. #184 closed by documenting that overline (SGR 53) and double underline (SGR 21) reach no Style, and the README's Known limitations still says so; the natural fix for that is two more bools. cargo-semver-checks runs on every release (release.yml), so it will correctly refuse the addition as breaking, and the change will cost a version bump it should not have to.
Adding #[non_exhaustive] is itself a breaking change, so it can only get cheaper by happening sooner. At 0.9.0 the crate is pre-1.0 with a handful of consumers, and a minor bump costs a line in three manifests. After 1.0 it costs a major.
The property being protected is real rather than theoretical: Style is a description of what a terminal drew, and terminals keep growing attributes. Underline colour (SGR 58) and underline style (curly, dotted, dashed) are the next two after overline, and a backend that models more of them would want to report them.
Fix — add the attribute:
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub struct Style { … }
Reading fields is unaffected — cell.style().bold keeps working, which is what nearly every test does. What stops compiling outside the crate is constructing a literal and destructuring exhaustively. Both remain possible through Style::default():
let mut want = Style::default();
want.fg = Color::Indexed(1);
want.bold = true;
assert_eq!(*cell.style(), want);
Three things belong in the same PR. Check whether any in-workspace test constructs a Style literal and convert it to the default() form, so the pattern the docs recommend is the pattern the suite uses. Add a short rustdoc paragraph on Style saying attributes get added and how to build one for comparison. And add the same note to the CHANGELOG under [Unreleased] as a Changed entry, since a consumer building a literal today will need the one-line migration above.
Worth deciding out loud in the PR and either doing or explicitly declining: a Style::builder()-shaped helper, or leaving default() plus field assignment as the only spelling. Declining is a reasonable answer — two lines is not a hardship — but it should be a decision rather than an omission.
Done when — Style is #[non_exhaustive]; the rustdoc says attributes may be added and shows how to construct one for comparison; any in-workspace literal is converted; the CHANGELOG carries the migration line; and cargo-semver-checks reports the change (which is the point — it is the last cheap moment to make it).
Today —
Styleis the only public struct in the crate with public fields, and the only public type without#[non_exhaustive]. Measured against 0.9.0:Every public enum —
Error,Key,Color,MouseMode,CursorShape,Graphics,Scroll,MouseButton,GraphicsProtocol,GraphicsAction,GraphicsFormat,DecodeError— carries the attribute.Styledoes not, so a consumer may writeStyle { fg, bg, bold, dim, italic, underline, reverse, blink, conceal, strikethrough }as a literal and destructure it exhaustively, and adding an eleventh field breaks them.Why it is worth fixing — an eleventh field is already known to be wanted. #184 closed by documenting that overline (
SGR 53) and double underline (SGR 21) reach noStyle, and the README's Known limitations still says so; the natural fix for that is two morebools.cargo-semver-checksruns on every release (release.yml), so it will correctly refuse the addition as breaking, and the change will cost a version bump it should not have to.Adding
#[non_exhaustive]is itself a breaking change, so it can only get cheaper by happening sooner. At 0.9.0 the crate is pre-1.0 with a handful of consumers, and a minor bump costs a line in three manifests. After 1.0 it costs a major.The property being protected is real rather than theoretical:
Styleis a description of what a terminal drew, and terminals keep growing attributes. Underline colour (SGR 58) and underline style (curly, dotted, dashed) are the next two after overline, and a backend that models more of them would want to report them.Fix — add the attribute:
Reading fields is unaffected —
cell.style().boldkeeps working, which is what nearly every test does. What stops compiling outside the crate is constructing a literal and destructuring exhaustively. Both remain possible throughStyle::default():Three things belong in the same PR. Check whether any in-workspace test constructs a
Styleliteral and convert it to thedefault()form, so the pattern the docs recommend is the pattern the suite uses. Add a short rustdoc paragraph onStylesaying attributes get added and how to build one for comparison. And add the same note to the CHANGELOG under[Unreleased]as a Changed entry, since a consumer building a literal today will need the one-line migration above.Worth deciding out loud in the PR and either doing or explicitly declining: a
Style::builder()-shaped helper, or leavingdefault()plus field assignment as the only spelling. Declining is a reasonable answer — two lines is not a hardship — but it should be a decision rather than an omission.Done when —
Styleis#[non_exhaustive]; the rustdoc says attributes may be added and shows how to construct one for comparison; any in-workspace literal is converted; the CHANGELOG carries the migration line; andcargo-semver-checksreports the change (which is the point — it is the last cheap moment to make it).