Skip to content

Style is the one public struct that is not #[non_exhaustive], and two attributes are already known to be missing #259

Description

@vyncint

TodayStyle 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 whenStyle 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions