Fix issue with None source_path

This fixes an issue where mdbook would panic if a non-draft chapter has
a None source_path when generating the search index. The code was
assuming that only draft chapters would have that behavior. However, API
users can inject synthetic chapters that have no path on disk.

This updates it to fall back to the path, or skip if neither is set.
This commit is contained in:
Eric Huss 2025-02-17 09:41:52 -08:00
parent 53c3a92285
commit 5777a0edc4
3 changed files with 13 additions and 11 deletions

View file

@ -173,7 +173,8 @@ pub struct Chapter {
/// `index.md` via the [`Chapter::path`] field. The `source_path` field
/// exists if you need access to the true file path.
///
/// This is `None` for a draft chapter.
/// This is `None` for a draft chapter, or a synthetically generated
/// chapter that has no file on disk.
pub source_path: Option<PathBuf>,
/// An ordered list of the names of each chapter above this one in the hierarchy.
pub parent_names: Vec<String>,

View file

@ -43,10 +43,11 @@ pub fn create_files(search_config: &Search, destination: &Path, book: &Book) ->
BookItem::Chapter(ch) if !ch.is_draft_chapter() => ch,
_ => continue,
};
let chapter_settings =
get_chapter_settings(&chapter_configs, chapter.source_path.as_ref().unwrap());
if !chapter_settings.enable.unwrap_or(true) {
continue;
if let Some(path) = settings_path(chapter) {
let chapter_settings = get_chapter_settings(&chapter_configs, path);
if !chapter_settings.enable.unwrap_or(true) {
continue;
}
}
render_item(&mut index, search_config, &mut doc_urls, chapter)?;
}
@ -321,6 +322,10 @@ fn clean_html(html: &str) -> String {
AMMONIA.clean(html).to_string()
}
fn settings_path(ch: &Chapter) -> Option<&Path> {
ch.source_path.as_deref().or_else(|| ch.path.as_deref())
}
fn validate_chapter_config(
chapter_configs: &[(PathBuf, SearchChapterSettings)],
book: &Book,
@ -329,13 +334,10 @@ fn validate_chapter_config(
let found = book
.iter()
.filter_map(|item| match item {
BookItem::Chapter(ch) if !ch.is_draft_chapter() => Some(ch),
BookItem::Chapter(ch) if !ch.is_draft_chapter() => settings_path(ch),
_ => None,
})
.any(|chapter| {
let ch_path = chapter.source_path.as_ref().unwrap();
ch_path.starts_with(path)
});
.any(|source_path| source_path.starts_with(path));
if !found {
bail!(
"[output.html.search.chapter] key `{}` does not match any chapter paths",

View file

@ -1034,7 +1034,6 @@ fn custom_header_attributes() {
}
#[test]
#[should_panic]
fn with_no_source_path() {
// Test for a regression where search would fail if source_path is None.
let temp = DummyBook::new().build().unwrap();