diff --git a/components/library/src/content/file_info.rs b/components/library/src/content/file_info.rs index 94311b5..8d58ebe 100644 --- a/components/library/src/content/file_info.rs +++ b/components/library/src/content/file_info.rs @@ -52,11 +52,13 @@ pub struct FileInfo { } impl FileInfo { - pub fn new_page(path: &Path) -> FileInfo { + pub fn new_page(path: &Path, base_path: &PathBuf) -> FileInfo { let file_path = path.to_path_buf(); - let mut parent = file_path.parent().unwrap().to_path_buf(); + let mut parent = file_path.parent().expect("Get parent of page").to_path_buf(); let name = path.file_stem().unwrap().to_string_lossy().to_string(); - let mut components = find_content_components(&file_path); + let mut components = find_content_components( + &file_path.strip_prefix(base_path).expect("Strip base path prefix for page"), + ); let relative = if !components.is_empty() { format!("{}/{}.md", components.join("/"), name) } else { @@ -85,11 +87,13 @@ impl FileInfo { } } - pub fn new_section(path: &Path) -> FileInfo { + pub fn new_section(path: &Path, base_path: &PathBuf) -> FileInfo { let file_path = path.to_path_buf(); - let parent = path.parent().unwrap().to_path_buf(); + let parent = path.parent().expect("Get parent of section").to_path_buf(); let name = path.file_stem().unwrap().to_string_lossy().to_string(); - let components = find_content_components(path); + let components = find_content_components( + &file_path.strip_prefix(base_path).expect("Strip base path prefix for section"), + ); let relative = if !components.is_empty() { format!("{}/{}.md", components.join("/"), name) } else { @@ -158,7 +162,7 @@ impl Default for FileInfo { #[cfg(test)] mod tests { - use std::path::Path; + use std::path::{Path, PathBuf}; use config::{Config, Language}; @@ -170,11 +174,22 @@ mod tests { find_content_components("/home/vincent/code/site/content/posts/tutorials/python.md"); assert_eq!(res, ["posts".to_string(), "tutorials".to_string()]); } + #[test] fn can_find_components_in_page_with_assets() { - let file = FileInfo::new_page(&Path::new( - "/home/vincent/code/site/content/posts/tutorials/python/index.md", - )); + let file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.md"), + &PathBuf::new(), + ); + assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); + } + + #[test] + fn doesnt_fail_with_multiple_content_directories() { + let file = FileInfo::new_page( + &Path::new("/home/vincent/code/content/site/content/posts/tutorials/python/index.md"), + &PathBuf::from("/home/vincent/code/content/site"), + ); assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); } @@ -182,9 +197,10 @@ mod tests { fn can_find_valid_language_in_page() { let mut config = Config::default(); config.languages.push(Language { code: String::from("fr"), rss: false }); - let mut file = FileInfo::new_page(&Path::new( - "/home/vincent/code/site/content/posts/tutorials/python.fr.md", - )); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"), + &PathBuf::new(), + ); let res = file.find_language(&config); assert!(res.is_ok()); assert_eq!(res.unwrap(), "fr"); @@ -194,9 +210,10 @@ mod tests { fn can_find_valid_language_in_page_with_assets() { let mut config = Config::default(); config.languages.push(Language { code: String::from("fr"), rss: false }); - let mut file = FileInfo::new_page(&Path::new( - "/home/vincent/code/site/content/posts/tutorials/python/index.fr.md", - )); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.fr.md"), + &PathBuf::new(), + ); assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); let res = file.find_language(&config); assert!(res.is_ok()); @@ -206,9 +223,10 @@ mod tests { #[test] fn do_nothing_on_unknown_language_in_page_with_i18n_off() { let config = Config::default(); - let mut file = FileInfo::new_page(&Path::new( - "/home/vincent/code/site/content/posts/tutorials/python.fr.md", - )); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"), + &PathBuf::new(), + ); let res = file.find_language(&config); assert!(res.is_ok()); assert_eq!(res.unwrap(), config.default_language); @@ -218,9 +236,10 @@ mod tests { fn errors_on_unknown_language_in_page_with_i18n_on() { let mut config = Config::default(); config.languages.push(Language { code: String::from("it"), rss: false }); - let mut file = FileInfo::new_page(&Path::new( - "/home/vincent/code/site/content/posts/tutorials/python.fr.md", - )); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"), + &PathBuf::new(), + ); let res = file.find_language(&config); assert!(res.is_err()); } @@ -229,9 +248,10 @@ mod tests { fn can_find_valid_language_in_section() { let mut config = Config::default(); config.languages.push(Language { code: String::from("fr"), rss: false }); - let mut file = FileInfo::new_section(&Path::new( - "/home/vincent/code/site/content/posts/tutorials/_index.fr.md", - )); + let mut file = FileInfo::new_section( + &Path::new("/home/vincent/code/site/content/posts/tutorials/_index.fr.md"), + &PathBuf::new(), + ); let res = file.find_language(&config); assert!(res.is_ok()); assert_eq!(res.unwrap(), "fr"); diff --git a/components/library/src/content/page.rs b/components/library/src/content/page.rs index 8383e5b..679764d 100644 --- a/components/library/src/content/page.rs +++ b/components/library/src/content/page.rs @@ -79,11 +79,11 @@ pub struct Page { } impl Page { - pub fn new>(file_path: P, meta: PageFrontMatter) -> Page { + pub fn new>(file_path: P, meta: PageFrontMatter, base_path: &PathBuf) -> Page { let file_path = file_path.as_ref(); Page { - file: FileInfo::new_page(file_path), + file: FileInfo::new_page(file_path, base_path), meta, ancestors: vec![], raw_content: "".to_string(), @@ -114,9 +114,14 @@ impl Page { /// Parse a page given the content of the .md file /// Files without front matter or with invalid front matter are considered /// erroneous - pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result { + pub fn parse( + file_path: &Path, + content: &str, + config: &Config, + base_path: &PathBuf, + ) -> Result { let (meta, content) = split_page_content(file_path, content)?; - let mut page = Page::new(file_path, meta); + let mut page = Page::new(file_path, meta, base_path); page.lang = page.file.find_language(config)?; @@ -196,10 +201,14 @@ impl Page { } /// Read and parse a .md file into a Page struct - pub fn from_file>(path: P, config: &Config) -> Result { + pub fn from_file>( + path: P, + config: &Config, + base_path: &PathBuf, + ) -> Result { let path = path.as_ref(); let content = read_file(path)?; - let mut page = Page::parse(path, &content, config)?; + let mut page = Page::parse(path, &content, config, base_path)?; if page.file.name == "index" { let parent_dir = path.parent().unwrap(); @@ -329,7 +338,7 @@ mod tests { use std::collections::HashMap; use std::fs::{create_dir, File}; use std::io::Write; - use std::path::Path; + use std::path::{Path, PathBuf}; use globset::{Glob, GlobSetBuilder}; use tempfile::tempdir; @@ -348,7 +357,7 @@ description = "hey there" slug = "hello-world" +++ Hello world"#; - let res = Page::parse(Path::new("post.md"), content, &Config::default()); + let res = Page::parse(Path::new("post.md"), content, &Config::default(), &PathBuf::new()); assert!(res.is_ok()); let mut page = res.unwrap(); page.render_markdown( @@ -374,7 +383,8 @@ Hello world"#; Hello world"#; let mut conf = Config::default(); conf.base_url = "http://hello.com/".to_string(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf); + let res = + Page::parse(Path::new("content/posts/intro/start.md"), content, &conf, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "posts/intro/hello-world/"); @@ -390,7 +400,7 @@ Hello world"#; +++ Hello world"#; let config = Config::default(); - let res = Page::parse(Path::new("start.md"), content, &config); + let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "hello-world/"); @@ -406,7 +416,12 @@ Hello world"#; +++ Hello world"#; let config = Config::default(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &config); + let res = Page::parse( + Path::new("content/posts/intro/start.md"), + content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "hello-world/"); @@ -422,7 +437,12 @@ Hello world"#; +++ Hello world"#; let config = Config::default(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &config); + let res = Page::parse( + Path::new("content/posts/intro/start.md"), + content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "hello-world/"); @@ -438,14 +458,15 @@ Hello world"#; slug = "hello-world" +++ Hello world"#; - let res = Page::parse(Path::new("start.md"), content, &Config::default()); + let res = Page::parse(Path::new("start.md"), content, &Config::default(), &PathBuf::new()); assert!(res.is_err()); } #[test] fn can_make_slug_from_non_slug_filename() { let config = Config::default(); - let res = Page::parse(Path::new(" file with space.md"), "+++\n+++", &config); + let res = + Page::parse(Path::new(" file with space.md"), "+++\n+++", &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.slug, "file-with-space"); @@ -461,7 +482,7 @@ Hello world"#; Hello world "# .to_string(); - let res = Page::parse(Path::new("hello.md"), &content, &config); + let res = Page::parse(Path::new("hello.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let mut page = res.unwrap(); page.render_markdown(&HashMap::default(), &Tera::default(), &config, InsertAnchor::None) @@ -483,7 +504,11 @@ Hello world File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Page::from_file(nested_path.join("index.md").as_path(), &Config::default()); + let res = Page::from_file( + nested_path.join("index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.file.parent, path.join("content").join("posts")); @@ -506,7 +531,11 @@ Hello world File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Page::from_file(nested_path.join("index.md").as_path(), &Config::default()); + let res = Page::from_file( + nested_path.join("index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.file.parent, path.join("content").join("posts")); @@ -530,7 +559,11 @@ Hello world File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Page::from_file(nested_path.join("index.md").as_path(), &Config::default()); + let res = Page::from_file( + nested_path.join("index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.file.parent, path.join("content").join("posts")); @@ -559,7 +592,7 @@ Hello world let mut config = Config::default(); config.ignored_content_globset = Some(gsb.build().unwrap()); - let res = Page::from_file(nested_path.join("index.md").as_path(), &config); + let res = Page::from_file(nested_path.join("index.md").as_path(), &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); @@ -576,7 +609,7 @@ Hello world Hello world "# .to_string(); - let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config); + let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); @@ -593,7 +626,12 @@ Hello world Hello world "# .to_string(); - let res = Page::parse(Path::new("2018-10-02T15:00:00Z-hello.md"), &content, &config); + let res = Page::parse( + Path::new("2018-10-02T15:00:00Z-hello.md"), + &content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); @@ -611,7 +649,7 @@ date = 2018-09-09 Hello world "# .to_string(); - let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config); + let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); @@ -628,7 +666,7 @@ Hello world +++ Bonjour le monde"# .to_string(); - let res = Page::parse(Path::new("hello.fr.md"), &content, &config); + let res = Page::parse(Path::new("hello.fr.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.lang, "fr".to_string()); @@ -645,7 +683,8 @@ Bonjour le monde"# +++ Bonjour le monde"# .to_string(); - let res = Page::parse(Path::new("2018-10-08_hello.fr.md"), &content, &config); + let res = + Page::parse(Path::new("2018-10-08_hello.fr.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.meta.date, Some("2018-10-08".to_string())); @@ -664,7 +703,7 @@ path = "bonjour" +++ Bonjour le monde"# .to_string(); - let res = Page::parse(Path::new("hello.fr.md"), &content, &config); + let res = Page::parse(Path::new("hello.fr.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.lang, "fr".to_string()); diff --git a/components/library/src/content/section.rs b/components/library/src/content/section.rs index 84166e8..ebc9ca1 100644 --- a/components/library/src/content/section.rs +++ b/components/library/src/content/section.rs @@ -59,11 +59,15 @@ pub struct Section { } impl Section { - pub fn new>(file_path: P, meta: SectionFrontMatter) -> Section { + pub fn new>( + file_path: P, + meta: SectionFrontMatter, + base_path: &PathBuf, + ) -> Section { let file_path = file_path.as_ref(); Section { - file: FileInfo::new_section(file_path), + file: FileInfo::new_section(file_path, base_path), meta, ancestors: vec![], path: "".to_string(), @@ -84,9 +88,14 @@ impl Section { } } - pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result
{ + pub fn parse( + file_path: &Path, + content: &str, + config: &Config, + base_path: &PathBuf, + ) -> Result
{ let (meta, content) = split_section_content(file_path, content)?; - let mut section = Section::new(file_path, meta); + let mut section = Section::new(file_path, meta, base_path); section.lang = section.file.find_language(config)?; section.raw_content = content; let (word_count, reading_time) = get_reading_analytics(§ion.raw_content); @@ -109,10 +118,14 @@ impl Section { } /// Read and parse a .md file into a Page struct - pub fn from_file>(path: P, config: &Config) -> Result
{ + pub fn from_file>( + path: P, + config: &Config, + base_path: &PathBuf, + ) -> Result
{ let path = path.as_ref(); let content = read_file(path)?; - let mut section = Section::parse(path, &content, config)?; + let mut section = Section::parse(path, &content, config, base_path)?; let parent_dir = path.parent().unwrap(); let assets = find_related_assets(parent_dir); @@ -250,7 +263,7 @@ impl Default for Section { mod tests { use std::fs::{create_dir, File}; use std::io::Write; - use std::path::Path; + use std::path::{Path, PathBuf}; use globset::{Glob, GlobSetBuilder}; use tempfile::tempdir; @@ -272,7 +285,11 @@ mod tests { File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Section::from_file(nested_path.join("_index.md").as_path(), &Config::default()); + let res = Section::from_file( + nested_path.join("_index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let section = res.unwrap(); assert_eq!(section.assets.len(), 3); @@ -298,7 +315,8 @@ mod tests { let mut config = Config::default(); config.ignored_content_globset = Some(gsb.build().unwrap()); - let res = Section::from_file(nested_path.join("_index.md").as_path(), &config); + let res = + Section::from_file(nested_path.join("_index.md").as_path(), &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); @@ -315,7 +333,12 @@ mod tests { +++ Bonjour le monde"# .to_string(); - let res = Section::parse(Path::new("content/hello/nested/_index.fr.md"), &content, &config); + let res = Section::parse( + Path::new("content/hello/nested/_index.fr.md"), + &content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let section = res.unwrap(); assert_eq!(section.lang, "fr".to_string()); @@ -332,7 +355,8 @@ Bonjour le monde"# +++ Bonjour le monde"# .to_string(); - let res = Section::parse(Path::new("content/_index.fr.md"), &content, &config); + let res = + Section::parse(Path::new("content/_index.fr.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let section = res.unwrap(); assert_eq!(section.lang, "fr".to_string()); diff --git a/components/library/src/pagination/mod.rs b/components/library/src/pagination/mod.rs index fd7f57f..2fc9203 100644 --- a/components/library/src/pagination/mod.rs +++ b/components/library/src/pagination/mod.rs @@ -228,6 +228,7 @@ impl<'a> Paginator<'a> { #[cfg(test)] mod tests { + use std::path::PathBuf; use tera::to_value; use config::Taxonomy as TaxonomyConfig; @@ -242,7 +243,7 @@ mod tests { let mut f = SectionFrontMatter::default(); f.paginate_by = Some(2); f.paginate_path = "page".to_string(); - let mut s = Section::new("content/_index.md", f); + let mut s = Section::new("content/_index.md", f, &PathBuf::new()); if !is_index { s.path = "posts/".to_string(); s.permalink = "https://vincent.is/posts/".to_string(); diff --git a/components/library/src/sorting.rs b/components/library/src/sorting.rs index 20844d8..c6cfd67 100644 --- a/components/library/src/sorting.rs +++ b/components/library/src/sorting.rs @@ -113,6 +113,7 @@ pub fn find_siblings(sorted: Vec<(&Key, bool)>) -> Vec<(Key, Option, Option #[cfg(test)] mod tests { use slotmap::DenseSlotMap; + use std::path::PathBuf; use super::{find_siblings, sort_pages_by_date, sort_pages_by_weight}; use content::Page; @@ -122,13 +123,13 @@ mod tests { let mut front_matter = PageFrontMatter::default(); front_matter.date = Some(date.to_string()); front_matter.date_to_datetime(); - Page::new("content/hello.md", front_matter) + Page::new("content/hello.md", front_matter, &PathBuf::new()) } fn create_page_with_weight(weight: usize) -> Page { let mut front_matter = PageFrontMatter::default(); front_matter.weight = Some(weight); - Page::new("content/hello.md", front_matter) + Page::new("content/hello.md", front_matter, &PathBuf::new()) } #[test] diff --git a/components/rebuild/src/lib.rs b/components/rebuild/src/lib.rs index a93ef7f..32f8e58 100644 --- a/components/rebuild/src/lib.rs +++ b/components/rebuild/src/lib.rs @@ -131,7 +131,7 @@ fn delete_element(site: &mut Site, path: &Path, is_section: bool) -> Result<()> /// Handles a `_index.md` (a section) being edited in some ways fn handle_section_editing(site: &mut Site, path: &Path) -> Result<()> { - let section = Section::from_file(path, &site.config)?; + let section = Section::from_file(path, &site.config, &site.base_path)?; let pathbuf = path.to_path_buf(); match site.add_section(section, true)? { // Updating a section @@ -193,7 +193,7 @@ macro_rules! render_parent_sections { /// Handles a page being edited in some ways fn handle_page_editing(site: &mut Site, path: &Path) -> Result<()> { - let page = Page::from_file(path, &site.config)?; + let page = Page::from_file(path, &site.config, &site.base_path)?; let pathbuf = path.to_path_buf(); match site.add_page(page, true)? { // Updating a page diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index 643a10d..634664d 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -210,7 +210,7 @@ impl Site { .into_par_iter() .map(|entry| { let path = entry.as_path(); - Section::from_file(path, config) + Section::from_file(path, config, &self.base_path) }) .collect::>() }; @@ -222,7 +222,7 @@ impl Site { .into_par_iter() .map(|entry| { let path = entry.as_path(); - Page::from_file(path, config) + Page::from_file(path, config, &self.base_path) }) .collect::>() };