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.
This commit is contained in:
Eric Huss
2025-09-15 07:33:44 -07:00
parent e7b15274b5
commit 2474ae799b
6 changed files with 27 additions and 24 deletions

View File

@@ -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::<String>()
}
/// Write the given data to a file, creating it first if necessary
pub fn write_file<P: AsRef<Path>>(build_dir: &Path, filename: P, content: &[u8]) -> Result<()> {
let path = build_dir.join(filename);

View File

@@ -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<String, String>,
) -> Result<BTreeMap<String, String>> {
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\

View File

@@ -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("<a href=\"")?;
let tmp = Path::new(path)
.with_extension("html")
.to_str()
.unwrap()
// Hack for windows who tends to use `\` as separator instead of `/`
.replace('\\', "/");
let tmp = Path::new(path).with_extension("html").to_url_path();
// Add link
out.write(&tmp)?;

View File

@@ -1,6 +1,7 @@
use super::static_files::StaticFiles;
use crate::theme::searcher;
use anyhow::{Context, Result, bail};
use crate::utils::ToUrlPath;
use anyhow::{Result, bail};
use elasticlunr::{Index, IndexBuilder};
use mdbook_core::book::{Book, BookItem, Chapter};
use mdbook_core::config::{Search, SearchChapterSettings};
@@ -128,11 +129,9 @@ fn render_item(
.path
.as_ref()
.expect("Checked that path exists above");
let filepath = Path::new(&chapter_path).with_extension("html");
let filepath = filepath
.to_str()
.with_context(|| "Could not convert HTML path to str")?;
let anchor_base = utils::fs::normalize_path(filepath);
let anchor_base = Path::new(&chapter_path)
.with_extension("html")
.to_url_path();
let options = HtmlRenderOptions::new(&chapter_path);
let mut p = new_cmark_parser(&chapter.content, &options.markdown_options).peekable();

View File

@@ -2,6 +2,7 @@
mod html_handlebars;
pub mod theme;
pub(crate) mod utils;
pub use html_handlebars::HtmlHandlebars;
use mdbook_core::config::HtmlConfig;

View File

@@ -0,0 +1,16 @@
//! Utilities for processing HTML.
use std::path::Path;
/// Helper trait for converting a [`Path`] to a string suitable for an HTML path.
pub(crate) trait ToUrlPath {
fn to_url_path(&self) -> 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('\\', "/")
}
}