Remove non_exhaustive from Book

This removes the `non_exhaustive` attribute from the `Book` and its
inner types `BookItem` and `Chapter`. These were added in
https://github.com/rust-lang/mdBook/pull/2779. After thinking about it
more, I realized that these types cannot be extended in a
semver-compatible way, so I am fine with allowing them be exhaustive.

The problem is that with CmdPreprocessor, the `Book` will be
re-serialized by a preprocessor, which could potentially be on an older
version. Attempting to add any new fields/variants means that either the
deserialization will fail, or the new fields will be stripped by the
preprocessor.

These could potentially be structured such that they have a
`serde(flatten)` or Other/Unknown variant so that a preprocessor would
at least see the extra fields/variants and pass them along back to the
output. However, a preprocessor or renderer wouldn't know what to do
with those new fields/variants (particularly `BookItem`) which would
itself be a problem. It's still possible to do something like this in
the future, but for now I think it's fine to restrict these to
semver-major changes.
This commit is contained in:
Eric Huss 2025-08-29 18:24:44 -07:00
parent 06af133838
commit 30d3aeb691
2 changed files with 12 additions and 4 deletions

View file

@ -18,8 +18,11 @@ mod tests;
/// ///
/// [`iter()`]: #method.iter /// [`iter()`]: #method.iter
/// [`for_each_mut()`]: #method.for_each_mut /// [`for_each_mut()`]: #method.for_each_mut
#[allow(
clippy::exhaustive_structs,
reason = "This cannot be extended without breaking preprocessors."
)]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Book { pub struct Book {
/// The items in this book. /// The items in this book.
pub items: Vec<BookItem>, pub items: Vec<BookItem>,
@ -80,8 +83,11 @@ where
} }
/// Enum representing any type of item which can be added to a book. /// Enum representing any type of item which can be added to a book.
#[allow(
clippy::exhaustive_enums,
reason = "This cannot be extended without breaking preprocessors."
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum BookItem { pub enum BookItem {
/// A nested chapter. /// A nested chapter.
Chapter(Chapter), Chapter(Chapter),
@ -99,8 +105,11 @@ impl From<Chapter> for BookItem {
/// The representation of a "chapter", usually mapping to a single file on /// The representation of a "chapter", usually mapping to a single file on
/// disk however it may contain multiple sub-chapters. /// disk however it may contain multiple sub-chapters.
#[allow(
clippy::exhaustive_structs,
reason = "This cannot be extended without breaking preprocessors."
)]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Chapter { pub struct Chapter {
/// The chapter's name. /// The chapter's name.
pub name: String, pub name: String,

View file

@ -687,7 +687,6 @@ fn make_data(
BookItem::Separator => { BookItem::Separator => {
chapter.insert("spacer".to_owned(), json!("_spacer_")); chapter.insert("spacer".to_owned(), json!("_spacer_"));
} }
_ => panic!("BookItem {item:?} not covered"),
} }
chapters.push(chapter); chapters.push(chapter);