Support colocating subfolders (#1582)

* Support colocating subfolders

* Adjust tests
This commit is contained in:
Tim Schumacher 2021-09-04 08:23:03 +02:00 committed by GitHub
parent 84b75f9725
commit 4086b0755a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 30 additions and 46 deletions

1
Cargo.lock generated
View File

@ -1261,6 +1261,7 @@ dependencies = [
"tera",
"toml",
"utils",
"walkdir",
]
[[package]]

View File

@ -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" }

View File

@ -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<PathBuf> {
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<PathBuf> {
}
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<PathBuf> {
#[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]

View File

@ -276,7 +276,7 @@ impl Page {
fn serialize_assets(&self, base_path: &Path) -> Vec<String> {
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();

View File

@ -195,7 +195,7 @@ impl Section {
fn serialize_assets(&self) -> Vec<String> {
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()

View File

@ -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<Render
code_block = None;
Event::Html("</code></pre>\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))

View File

@ -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, "<p><a href=\"https://vincent.is/about/image.jpg\">an image</a></p>\n");
assert_eq!(res.body, "<p><a href=\"image.jpg\">an image</a></p>\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,
"<p><img src=\"https://vincent.is/about/image.jpg\" alt=\"alt text\" /></p>\n"
"<p><img src=\"image.jpg\" alt=\"alt text\" /></p>\n"
);
}

View File

@ -595,7 +595,7 @@ impl Site {
self.copy_asset(
asset_path,
&current_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(&section.file.path.parent().unwrap()).expect("Failed to get asset filename for section"),
),
)?;
}