From 2474ae799bae7d052a82c2fc95fd98cea0b7fb3b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 15 Sep 2025 07:33:44 -0700 Subject: [PATCH] Add ToUrlPath helper trait This adds the `ToUrlPath` helper trait to convert a Path to a path suitable for use in HTML (replacing `normalize_path`). This also fixes a minor bug where on Windows the next/prev links were using a double forward slash. I don't think this is possible, since chapter links are derived from the summary, but I'm noting just in case. It's also not too much of an issue since double slashes are normally just treated as a single. --- crates/mdbook-core/src/utils/fs.rs | 8 -------- .../src/html_handlebars/hbs_renderer.rs | 7 +++---- .../src/html_handlebars/helpers/toc.rs | 8 ++------ crates/mdbook-html/src/html_handlebars/search.rs | 11 +++++------ crates/mdbook-html/src/lib.rs | 1 + crates/mdbook-html/src/utils.rs | 16 ++++++++++++++++ 6 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 crates/mdbook-html/src/utils.rs diff --git a/crates/mdbook-core/src/utils/fs.rs b/crates/mdbook-core/src/utils/fs.rs index d32f5705..ebc7dc63 100644 --- a/crates/mdbook-core/src/utils/fs.rs +++ b/crates/mdbook-core/src/utils/fs.rs @@ -6,14 +6,6 @@ use std::io::Write; use std::path::{Component, Path, PathBuf}; use tracing::{debug, trace}; -/// Naively replaces any path separator with a forward-slash '/' -pub fn normalize_path(path: &str) -> String { - use std::path::is_separator; - path.chars() - .map(|ch| if is_separator(ch) { '/' } else { ch }) - .collect::() -} - /// Write the given data to a file, creating it first if necessary pub fn write_file>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> { let path = build_dir.join(filename); diff --git a/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs index a0f01c7a..373b5e1e 100644 --- a/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs +++ b/crates/mdbook-html/src/html_handlebars/hbs_renderer.rs @@ -1,6 +1,7 @@ use super::helpers; use super::static_files::StaticFiles; use crate::theme::Theme; +use crate::utils::ToUrlPath; use anyhow::{Context, Result, bail}; use handlebars::Handlebars; use mdbook_core::book::{Book, BookItem, Chapter}; @@ -122,9 +123,7 @@ impl HtmlHandlebars { .as_ref() .unwrap() .with_extension("html") - .to_str() - .unwrap() - .replace('\\', "//"); + .to_url_path(); let obj = json!( { "title": ch.name, "link": path, @@ -1054,7 +1053,7 @@ fn collect_redirects_for_path( path: &Path, redirects: &HashMap, ) -> Result> { - let path = format!("/{}", path.display().to_string().replace('\\', "/")); + let path = format!("/{}", path.to_url_path()); if redirects.contains_key(&path) { bail!( "redirect found for existing chapter at `{path}`\n\ diff --git a/crates/mdbook-html/src/html_handlebars/helpers/toc.rs b/crates/mdbook-html/src/html_handlebars/helpers/toc.rs index cd876e08..d299803d 100644 --- a/crates/mdbook-html/src/html_handlebars/helpers/toc.rs +++ b/crates/mdbook-html/src/html_handlebars/helpers/toc.rs @@ -1,3 +1,4 @@ +use crate::utils::ToUrlPath; use std::path::Path; use std::{cmp::Ordering, collections::BTreeMap}; @@ -109,12 +110,7 @@ impl HelperDef for RenderToc { let path_exists = match item.get("path") { Some(path) if !path.is_empty() => { out.write(" String; +} + +impl ToUrlPath for Path { + fn to_url_path(&self) -> String { + // We're generally assuming that all paths we deal with are utf-8. + // The replace here is to handle Windows paths. + self.to_str().unwrap().replace('\\', "/") + } +}