mirror of
https://github.com/rust-lang/mdBook.git
synced 2025-12-27 10:16:09 -05:00
Add an iterator over chapters
This adds the `Book::chapters` iterator (and `for_each_chapter_mut`) to iterate over non-draft chapters. This is a common pattern I keep encountering, and I figure it might simplify things. It runs a little risk that callers may not be properly handling every item type, but I think it should be ok.
This commit is contained in:
@@ -46,6 +46,14 @@ impl Book {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A depth-first iterator over each [`Chapter`], skipping draft chapters.
|
||||||
|
pub fn chapters(&self) -> impl Iterator<Item = &Chapter> {
|
||||||
|
self.iter().filter_map(|item| match item {
|
||||||
|
BookItem::Chapter(ch) if !ch.is_draft_chapter() => Some(ch),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Recursively apply a closure to each item in the book, allowing you to
|
/// Recursively apply a closure to each item in the book, allowing you to
|
||||||
/// mutate them.
|
/// mutate them.
|
||||||
///
|
///
|
||||||
@@ -61,6 +69,26 @@ impl Book {
|
|||||||
for_each_mut(&mut func, &mut self.items);
|
for_each_mut(&mut func, &mut self.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively apply a closure to each non-draft chapter in the book,
|
||||||
|
/// allowing you to mutate them.
|
||||||
|
pub fn for_each_chapter_mut<F>(&mut self, mut func: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut Chapter),
|
||||||
|
{
|
||||||
|
for_each_mut(
|
||||||
|
&mut |item| {
|
||||||
|
let BookItem::Chapter(ch) = item else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if ch.is_draft_chapter() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
func(ch)
|
||||||
|
},
|
||||||
|
&mut self.items,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Append a `BookItem` to the `Book`.
|
/// Append a `BookItem` to the `Book`.
|
||||||
pub fn push_item<I: Into<BookItem>>(&mut self, item: I) -> &mut Self {
|
pub fn push_item<I: Into<BookItem>>(&mut self, item: I) -> &mut Self {
|
||||||
self.items.push(item.into());
|
self.items.push(item.into());
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use mdbook_core::book::BookItem;
|
|
||||||
use mdbook_core::utils;
|
use mdbook_core::utils;
|
||||||
use mdbook_renderer::{RenderContext, Renderer};
|
use mdbook_renderer::{RenderContext, Renderer};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@@ -33,16 +32,12 @@ impl Renderer for MarkdownRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trace!("markdown render");
|
trace!("markdown render");
|
||||||
for item in book.iter() {
|
for ch in book.chapters() {
|
||||||
if let BookItem::Chapter(ref ch) = *item {
|
utils::fs::write_file(
|
||||||
if !ch.is_draft_chapter() {
|
&ctx.destination,
|
||||||
utils::fs::write_file(
|
ch.path.as_ref().expect("Checked path exists before"),
|
||||||
&ctx.destination,
|
ch.content.as_bytes(),
|
||||||
ch.path.as_ref().expect("Checked path exists before"),
|
)?;
|
||||||
ch.content.as_bytes(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::create_dir_all(destination)
|
fs::create_dir_all(destination)
|
||||||
|
|||||||
@@ -459,13 +459,7 @@ impl Renderer for HtmlHandlebars {
|
|||||||
utils::fs::write_file(destination, "CNAME", format!("{cname}\n").as_bytes())?;
|
utils::fs::write_file(destination, "CNAME", format!("{cname}\n").as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let chapters: Vec<_> = book
|
let chapters: Vec<_> = book.chapters().collect();
|
||||||
.iter()
|
|
||||||
.filter_map(|item| match item {
|
|
||||||
BookItem::Chapter(ch) if !ch.is_draft_chapter() => Some(ch),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
for (i, ch) in chapters.iter().enumerate() {
|
for (i, ch) in chapters.iter().enumerate() {
|
||||||
let previous = (i != 0).then(|| chapters[i - 1]);
|
let previous = (i != 0).then(|| chapters[i - 1]);
|
||||||
let next = (i != chapters.len() - 1).then(|| chapters[i + 1]);
|
let next = (i != chapters.len() - 1).then(|| chapters[i + 1]);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::static_files::StaticFiles;
|
|||||||
use crate::theme::searcher;
|
use crate::theme::searcher;
|
||||||
use anyhow::{Context, Result, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use elasticlunr::{Index, IndexBuilder};
|
use elasticlunr::{Index, IndexBuilder};
|
||||||
use mdbook_core::book::{Book, BookItem, Chapter};
|
use mdbook_core::book::{Book, Chapter};
|
||||||
use mdbook_core::config::{Search, SearchChapterSettings};
|
use mdbook_core::config::{Search, SearchChapterSettings};
|
||||||
use mdbook_core::utils;
|
use mdbook_core::utils;
|
||||||
use mdbook_markdown::HtmlRenderOptions;
|
use mdbook_markdown::HtmlRenderOptions;
|
||||||
@@ -43,11 +43,7 @@ pub(super) fn create_files(
|
|||||||
let chapter_configs = sort_search_config(&search_config.chapter);
|
let chapter_configs = sort_search_config(&search_config.chapter);
|
||||||
validate_chapter_config(&chapter_configs, book)?;
|
validate_chapter_config(&chapter_configs, book)?;
|
||||||
|
|
||||||
for item in book.iter() {
|
for chapter in book.chapters() {
|
||||||
let chapter = match item {
|
|
||||||
BookItem::Chapter(ch) if !ch.is_draft_chapter() => ch,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
if let Some(path) = settings_path(chapter) {
|
if let Some(path) = settings_path(chapter) {
|
||||||
let chapter_settings = get_chapter_settings(&chapter_configs, path);
|
let chapter_settings = get_chapter_settings(&chapter_configs, path);
|
||||||
if !chapter_settings.enable.unwrap_or(true) {
|
if !chapter_settings.enable.unwrap_or(true) {
|
||||||
@@ -349,11 +345,8 @@ fn validate_chapter_config(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for (path, _) in chapter_configs {
|
for (path, _) in chapter_configs {
|
||||||
let found = book
|
let found = book
|
||||||
.iter()
|
.chapters()
|
||||||
.filter_map(|item| match item {
|
.filter_map(|ch| settings_path(ch))
|
||||||
BookItem::Chapter(ch) if !ch.is_draft_chapter() => settings_path(ch),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.any(|source_path| source_path.starts_with(path));
|
.any(|source_path| source_path.starts_with(path));
|
||||||
if !found {
|
if !found {
|
||||||
bail!(
|
bail!(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! This is a demonstration of an mdBook preprocessor which parses markdown
|
//! This is a demonstration of an mdBook preprocessor which parses markdown
|
||||||
//! and removes any instances of emphasis.
|
//! and removes any instances of emphasis.
|
||||||
|
|
||||||
use mdbook_preprocessor::book::{Book, BookItem, Chapter};
|
use mdbook_preprocessor::book::{Book, Chapter};
|
||||||
use mdbook_preprocessor::errors::Result;
|
use mdbook_preprocessor::errors::Result;
|
||||||
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
|
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
|
||||||
use pulldown_cmark::{Event, Parser, Tag, TagEnd};
|
use pulldown_cmark::{Event, Parser, Tag, TagEnd};
|
||||||
@@ -36,17 +36,9 @@ impl Preprocessor for RemoveEmphasis {
|
|||||||
|
|
||||||
fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
|
fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
book.for_each_mut(|item| {
|
book.for_each_chapter_mut(|ch| match remove_emphasis(&mut total, ch) {
|
||||||
let BookItem::Chapter(ch) = item else {
|
Ok(s) => ch.content = s,
|
||||||
return;
|
Err(e) => eprintln!("failed to process chapter: {e:?}"),
|
||||||
};
|
|
||||||
if ch.is_draft_chapter() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
match remove_emphasis(&mut total, ch) {
|
|
||||||
Ok(s) => ch.content = s,
|
|
||||||
Err(e) => eprintln!("failed to process chapter: {e:?}"),
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
eprintln!("removed {total} emphasis");
|
eprintln!("removed {total} emphasis");
|
||||||
Ok(book)
|
Ok(book)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Preprocessor for the mdBook guide.
|
//! Preprocessor for the mdBook guide.
|
||||||
|
|
||||||
use mdbook_preprocessor::book::{Book, BookItem};
|
use mdbook_preprocessor::book::Book;
|
||||||
use mdbook_preprocessor::errors::Result;
|
use mdbook_preprocessor::errors::Result;
|
||||||
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
|
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
@@ -53,13 +53,7 @@ fn insert_version(book: &mut Book) {
|
|||||||
let manifest: toml::Value = toml::from_str(&manifest_contents).unwrap();
|
let manifest: toml::Value = toml::from_str(&manifest_contents).unwrap();
|
||||||
let version = manifest["package"]["version"].as_str().unwrap();
|
let version = manifest["package"]["version"].as_str().unwrap();
|
||||||
const MARKER: &str = "{{ mdbook-version }}";
|
const MARKER: &str = "{{ mdbook-version }}";
|
||||||
book.for_each_mut(|item| {
|
book.for_each_chapter_mut(|ch| {
|
||||||
let BookItem::Chapter(ch) = item else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if ch.is_draft_chapter() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ch.content.contains(MARKER) {
|
if ch.content.contains(MARKER) {
|
||||||
ch.content = ch.content.replace(MARKER, version);
|
ch.content = ch.content.replace(MARKER, version);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user