From 4086b0755a84e82645de3f43aab7460a1a5da6d6 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sat, 4 Sep 2021 08:23:03 +0200 Subject: [PATCH] Support colocating subfolders (#1582) * Support colocating subfolders * Adjust tests --- Cargo.lock | 1 + components/library/Cargo.toml | 1 + components/library/src/content/mod.rs | 45 +++++++++++------------ components/library/src/content/page.rs | 2 +- components/library/src/content/section.rs | 2 +- components/rendering/src/markdown.rs | 17 --------- components/rendering/tests/markdown.rs | 4 +- components/site/src/lib.rs | 4 +- 8 files changed, 30 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5148e9d6..7615a68f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1261,6 +1261,7 @@ dependencies = [ "tera", "toml", "utils", + "walkdir", ] [[package]] diff --git a/components/library/Cargo.toml b/components/library/Cargo.toml index bfaac903..d7122e0e 100644 --- a/components/library/Cargo.toml +++ b/components/library/Cargo.toml @@ -14,6 +14,7 @@ serde_derive = "1" regex = "1" lazy_static = "1" lexical-sort = "0.3" +walkdir = "2" front_matter = { path = "../front_matter" } config = { path = "../config" } diff --git a/components/library/src/content/mod.rs b/components/library/src/content/mod.rs index 161a1ab9..377a8601 100644 --- a/components/library/src/content/mod.rs +++ b/components/library/src/content/mod.rs @@ -3,9 +3,10 @@ mod page; mod section; mod ser; -use std::fs::read_dir; use std::path::{Path, PathBuf}; +use walkdir::WalkDir; + pub use self::file_info::FileInfo; pub use self::page::Page; pub use self::section::Section; @@ -32,7 +33,7 @@ pub fn has_anchor(headings: &[Heading], anchor: &str) -> bool { pub fn find_related_assets(path: &Path, config: &Config) -> Vec { let mut assets = vec![]; - for entry in read_dir(path).unwrap().filter_map(std::result::Result::ok) { + for entry in WalkDir::new(path).into_iter().filter_map(std::result::Result::ok) { let entry_path = entry.path(); if entry_path.is_file() { match entry_path.extension() { @@ -46,18 +47,11 @@ pub fn find_related_assets(path: &Path, config: &Config) -> Vec { } if let Some(ref globset) = config.ignored_content_globset { - // `find_related_assets` only scans the immediate directory (it is not recursive) so our - // filtering only needs to work against the file_name component, not the full suffix. If - // `find_related_assets` was changed to also return files in subdirectories, we could - // use `PathBuf.strip_prefix` to remove the parent directory and then glob-filter - // against the remaining path. Note that the current behaviour effectively means that - // the `ignored_content` setting in the config file is limited to single-file glob - // patterns (no "**" patterns). assets = assets .into_iter() - .filter(|path| match path.file_name() { - None => false, - Some(file) => !globset.is_match(file), + .filter(|p| match p.strip_prefix(path) { + Err(_) => false, + Ok(file) => !globset.is_match(file), }) .collect(); } @@ -68,7 +62,7 @@ pub fn find_related_assets(path: &Path, config: &Config) -> Vec { #[cfg(test)] mod tests { use super::*; - use std::fs::File; + use std::fs::{File, create_dir}; use config::Config; use tempfile::tempdir; @@ -76,17 +70,22 @@ mod tests { #[test] fn can_find_related_assets() { let tmp_dir = tempdir().expect("create temp dir"); - File::create(tmp_dir.path().join("index.md")).unwrap(); - File::create(tmp_dir.path().join("example.js")).unwrap(); - File::create(tmp_dir.path().join("graph.jpg")).unwrap(); - File::create(tmp_dir.path().join("fail.png")).unwrap(); + let path = tmp_dir.path(); + File::create(path.join("index.md")).unwrap(); + File::create(path.join("example.js")).unwrap(); + File::create(path.join("graph.jpg")).unwrap(); + File::create(path.join("fail.png")).unwrap(); + create_dir(path.join("subdir")).expect("create subdir temp dir"); + File::create(path.join("subdir").join("index.md")).unwrap(); + File::create(path.join("subdir").join("example.js")).unwrap(); - let assets = find_related_assets(tmp_dir.path(), &Config::default()); - assert_eq!(assets.len(), 3); - assert_eq!(assets.iter().filter(|p| p.extension().unwrap() != "md").count(), 3); - assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "example.js").count(), 1); - assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "graph.jpg").count(), 1); - assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1); + let assets = find_related_assets(path, &Config::default()); + assert_eq!(assets.len(), 4); + assert_eq!(assets.iter().filter(|p| p.extension().unwrap() != "md").count(), 4); + assert_eq!(assets.iter().filter(|p| p.strip_prefix(path).unwrap() == Path::new("example.js")).count(), 1); + assert_eq!(assets.iter().filter(|p| p.strip_prefix(path).unwrap() == Path::new("graph.jpg")).count(), 1); + assert_eq!(assets.iter().filter(|p| p.strip_prefix(path).unwrap() == Path::new("fail.png")).count(), 1); + assert_eq!(assets.iter().filter(|p| p.strip_prefix(path).unwrap() == Path::new("subdir/example.js")).count(), 1); } #[test] diff --git a/components/library/src/content/page.rs b/components/library/src/content/page.rs index c8bcd0aa..ab0f1cf5 100644 --- a/components/library/src/content/page.rs +++ b/components/library/src/content/page.rs @@ -276,7 +276,7 @@ impl Page { fn serialize_assets(&self, base_path: &Path) -> Vec { self.assets .iter() - .filter_map(|asset| asset.file_name()) + .filter_map(|asset| asset.strip_prefix(&self.file.path.parent().unwrap()).ok()) .filter_map(|filename| filename.to_str()) .map(|filename| { let mut path = self.file.path.clone(); diff --git a/components/library/src/content/section.rs b/components/library/src/content/section.rs index 5f9e93bf..4aa3c05a 100644 --- a/components/library/src/content/section.rs +++ b/components/library/src/content/section.rs @@ -195,7 +195,7 @@ impl Section { fn serialize_assets(&self) -> Vec { self.assets .iter() - .filter_map(|asset| asset.file_name()) + .filter_map(|asset| asset.strip_prefix(&self.file.path.parent().unwrap()).ok()) .filter_map(|filename| filename.to_str()) .map(|filename| format!("{}{}", self.path, filename)) .collect() diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index c0078e75..3605a6d3 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -74,13 +74,6 @@ fn starts_with_schema(s: &str) -> bool { PATTERN.is_match(s) } -/// Colocated asset links refers to the files in the same directory, -/// there it should be a filename only -fn is_colocated_asset_link(link: &str) -> bool { - !link.contains('/') // http://, ftp://, ../ etc - && !starts_with_schema(link) -} - /// Returns whether a link starts with an HTTP(s) scheme. fn is_external_link(link: &str) -> bool { link.starts_with("http:") || link.starts_with("https:") @@ -111,8 +104,6 @@ fn fix_link( return Err(format!("Relative link {} not found.", link).into()); } } - } else if is_colocated_asset_link(link) { - format!("{}{}", context.current_page_permalink, link) } else { if is_external_link(link) { external_links.push(link.to_owned()); @@ -222,14 +213,6 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result\n".into()) } - Event::Start(Tag::Image(link_type, src, title)) => { - if is_colocated_asset_link(&src) { - let link = format!("{}{}", context.current_page_permalink, &*src); - return Event::Start(Tag::Image(link_type, link.into(), title)); - } - - Event::Start(Tag::Image(link_type, src, title)) - } Event::Start(Tag::Link(link_type, link, title)) if link.is_empty() => { error = Some(Error::msg("There is a link that is missing a URL")); Event::Start(Tag::Link(link_type, "#".into(), title)) diff --git a/components/rendering/tests/markdown.rs b/components/rendering/tests/markdown.rs index e955e0dc..164e3b7f 100644 --- a/components/rendering/tests/markdown.rs +++ b/components/rendering/tests/markdown.rs @@ -998,7 +998,7 @@ fn can_make_permalinks_with_colocated_assets_for_link() { InsertAnchor::None, ); let res = render_content("[an image](image.jpg)", &context).unwrap(); - assert_eq!(res.body, "

an image

\n"); + assert_eq!(res.body, "

an image

\n"); } #[test] @@ -1016,7 +1016,7 @@ fn can_make_permalinks_with_colocated_assets_for_image() { let res = render_content("![alt text](image.jpg)", &context).unwrap(); assert_eq!( res.body, - "

\"alt

\n" + "

\"alt

\n" ); } diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index 040a6b1f..17d0059f 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -595,7 +595,7 @@ impl Site { self.copy_asset( asset_path, ¤t_path - .join(asset_path.file_name().expect("Couldn't get filename from page asset")), + .join(asset_path.strip_prefix(&page.file.path.parent().unwrap()).expect("Couldn't get filename from page asset")), )?; } @@ -988,7 +988,7 @@ impl Site { self.copy_asset( asset_path, &output_path.join( - asset_path.file_name().expect("Failed to get asset filename for section"), + asset_path.strip_prefix(§ion.file.path.parent().unwrap()).expect("Failed to get asset filename for section"), ), )?; }