diff --git a/Cargo.lock b/Cargo.lock index 77d6d89..5d7f596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -419,6 +425,7 @@ dependencies = [ "flate2", "glob", "mime_guess", + "pathdiff", "proc-macro2", "quote", "sha2", diff --git a/static-serve-macro/Cargo.toml b/static-serve-macro/Cargo.toml index 475f440..eee5b6b 100644 --- a/static-serve-macro/Cargo.toml +++ b/static-serve-macro/Cargo.toml @@ -17,6 +17,7 @@ display_full_error = "1.1" flate2 = "1.1" glob = "0.3" mime_guess = "2.0.5" +pathdiff = "0.2" proc-macro2 = "1.0" quote = "1.0" sha2 = "0.11" diff --git a/static-serve-macro/src/error.rs b/static-serve-macro/src/error.rs index 01fc2e8..fe766c7 100644 --- a/static-serve-macro/src/error.rs +++ b/static-serve-macro/src/error.rs @@ -2,6 +2,7 @@ use std::{ ffi::{OsStr, OsString}, fmt::{Display, Formatter}, io, + path::PathBuf, }; use glob::{GlobError, PatternError}; @@ -17,14 +18,14 @@ pub(crate) enum Error { CannotCanonicalizeDirectory(#[source] io::Error), #[error("Cannot canonicalize asset file")] CannotCanonicalizeFile(#[source] io::Error), + #[error("Cannot make file path {0} relative to directory")] + CannotMakeFileRelative(PathBuf), #[error("File path is not utf-8")] FilePathIsNotUtf8, #[error("Invalid unicode in directory name")] InvalidUnicodeInDirectoryName, #[error("Cannot canonicalize ignore path")] CannotCanonicalizeIgnorePath(#[source] io::Error), - #[error("Invalid unicode in entry name")] - InvalidUnicodeInEntryName, #[error("Error while compressing with gzip")] Gzip(#[from] GzipType), #[error("Error while compressing with zstd")] diff --git a/static-serve-macro/src/lib.rs b/static-serve-macro/src/lib.rs index cd73b9e..3a15203 100644 --- a/static-serve-macro/src/lib.rs +++ b/static-serve-macro/src/lib.rs @@ -722,12 +722,9 @@ impl EmbeddedFileInfo { // entry_path is only needed for the router (embed_assets!) let entry_path = if let Some(dir) = assets_dir_abs_str { - let relative_entry = pathbuf - .strip_prefix(dir) - .ok() - .and_then(|p| p.to_str()) - .ok_or(Error::InvalidUnicodeInEntryName)?; - let mut web_path = normalize_web_path(relative_entry); + let relative_entry = pathdiff::diff_paths(pathbuf, dir) + .ok_or_else(|| Error::CannotMakeFileRelative(pathbuf.clone()))?; + let mut web_path = normalize_web_path(&relative_entry); if should_strip_html_ext.value && content_type == "text/html" { strip_html_ext(&mut web_path); } @@ -855,8 +852,8 @@ fn etag(contents: &[u8]) -> String { /// Path segments are normalized via [`Path::components`] so separator /// style differences across platforms do not affect route generation. /// The returned route is always absolute (starts with `/`). -fn normalize_web_path(relative_path: &str) -> String { - let normalized = Path::new(relative_path) +fn normalize_web_path(relative_path: &Path) -> String { + let normalized = relative_path .components() .filter_map(|component| match component { std::path::Component::Normal(segment) => segment.to_str(),