@@ -16,7 +16,7 @@ in the `docs/content` folder of the repository and the community can use [its fo | |||||
| Syntax highlighting | ✔ | ✔ | ✔ | ✔ | | | Syntax highlighting | ✔ | ✔ | ✔ | ✔ | | ||||
| Sass compilation | ✔ | ✔ | ✔ | ✔ | | | Sass compilation | ✔ | ✔ | ✔ | ✔ | | ||||
| Assets co-location | ✔ | ✔ | ✔ | ✔ | | | Assets co-location | ✔ | ✔ | ✔ | ✔ | | ||||
| i18n | ✕ | ✕ | ✔ | ✔ | | |||||
| Multilingual site | ✔ | ✕ | ✔ | ✔ | | |||||
| Image processing | ✔ | ✕ | ✔ | ✔ | | | Image processing | ✔ | ✕ | ✔ | ✔ | | ||||
| Sane & powerful template engine | ✔ | ~ | ~ | ✔ | | | Sane & powerful template engine | ✔ | ~ | ~ | ✔ | | ||||
| Themes | ✔ | ✕ | ✔ | ✔ | | | Themes | ✔ | ✕ | ✔ | ✔ | | ||||
@@ -12,7 +12,7 @@ extern crate syntect; | |||||
mod config; | mod config; | ||||
pub mod highlighting; | pub mod highlighting; | ||||
mod theme; | mod theme; | ||||
pub use config::{Config, Taxonomy, Language}; | |||||
pub use config::{Config, Language, Taxonomy}; | |||||
use std::path::Path; | use std::path::Path; | ||||
@@ -119,7 +119,7 @@ impl FileInfo { | |||||
// Go with the assumption that no one is using `.` in filenames when using i18n | // Go with the assumption that no one is using `.` in filenames when using i18n | ||||
// We can document that | // We can document that | ||||
let mut parts: Vec<String> = self.name.splitn(2,'.').map(|s| s.to_string()).collect(); | |||||
let mut parts: Vec<String> = self.name.splitn(2, '.').map(|s| s.to_string()).collect(); | |||||
// The language code is not present in the config: typo or the user forgot to add it to the | // The language code is not present in the config: typo or the user forgot to add it to the | ||||
// config | // config | ||||
@@ -155,7 +155,7 @@ mod tests { | |||||
use config::{Config, Language}; | use config::{Config, Language}; | ||||
use super::{FileInfo, find_content_components}; | |||||
use super::{find_content_components, FileInfo}; | |||||
#[test] | #[test] | ||||
fn can_find_content_components() { | fn can_find_content_components() { | ||||
@@ -165,17 +165,19 @@ mod tests { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_find_components_in_page_with_assets() { | 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", | |||||
)); | |||||
assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); | assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); | ||||
} | } | ||||
#[test] | #[test] | ||||
fn can_find_valid_language_in_page() { | fn can_find_valid_language_in_page() { | ||||
let mut config = Config::default(); | 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")); | |||||
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 res = file.find_language(&config); | let res = file.find_language(&config); | ||||
assert!(res.is_ok()); | assert!(res.is_ok()); | ||||
assert_eq!(res.unwrap(), Some(String::from("fr"))); | assert_eq!(res.unwrap(), Some(String::from("fr"))); | ||||
@@ -184,9 +186,10 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_find_valid_language_in_page_with_assets() { | fn can_find_valid_language_in_page_with_assets() { | ||||
let mut config = Config::default(); | 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")); | |||||
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", | |||||
)); | |||||
assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); | assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); | ||||
let res = file.find_language(&config); | let res = file.find_language(&config); | ||||
assert!(res.is_ok()); | assert!(res.is_ok()); | ||||
@@ -196,8 +199,9 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn do_nothing_on_unknown_language_in_page_with_i18n_off() { | fn do_nothing_on_unknown_language_in_page_with_i18n_off() { | ||||
let config = Config::default(); | 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", | |||||
)); | |||||
let res = file.find_language(&config); | let res = file.find_language(&config); | ||||
assert!(res.is_ok()); | assert!(res.is_ok()); | ||||
assert!(res.unwrap().is_none()); | assert!(res.unwrap().is_none()); | ||||
@@ -206,9 +210,10 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn errors_on_unknown_language_in_page_with_i18n_on() { | fn errors_on_unknown_language_in_page_with_i18n_on() { | ||||
let mut config = Config::default(); | 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")); | |||||
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 res = file.find_language(&config); | let res = file.find_language(&config); | ||||
assert!(res.is_err()); | assert!(res.is_err()); | ||||
} | } | ||||
@@ -216,9 +221,10 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_find_valid_language_in_section() { | fn can_find_valid_language_in_section() { | ||||
let mut config = Config::default(); | 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")); | |||||
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 res = file.find_language(&config); | let res = file.find_language(&config); | ||||
assert!(res.is_ok()); | assert!(res.is_ok()); | ||||
assert_eq!(res.unwrap(), Some(String::from("fr"))); | assert_eq!(res.unwrap(), Some(String::from("fr"))); | ||||
@@ -74,6 +74,8 @@ pub struct Page { | |||||
/// The language of that page. `None` if the user doesn't setup `languages` in config. | /// The language of that page. `None` if the user doesn't setup `languages` in config. | ||||
/// Corresponds to the lang in the {slug}.{lang}.md file scheme | /// Corresponds to the lang in the {slug}.{lang}.md file scheme | ||||
pub lang: Option<String>, | pub lang: Option<String>, | ||||
/// Contains all the translated version of that page | |||||
pub translations: Vec<Key>, | |||||
} | } | ||||
impl Page { | impl Page { | ||||
@@ -101,6 +103,7 @@ impl Page { | |||||
word_count: None, | word_count: None, | ||||
reading_time: None, | reading_time: None, | ||||
lang: None, | lang: None, | ||||
translations: Vec::new(), | |||||
} | } | ||||
} | } | ||||
@@ -300,6 +303,7 @@ impl Default for Page { | |||||
word_count: None, | word_count: None, | ||||
reading_time: None, | reading_time: None, | ||||
lang: None, | lang: None, | ||||
translations: Vec::new(), | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -577,7 +581,7 @@ Hello world | |||||
#[test] | #[test] | ||||
fn can_specify_language_in_filename() { | fn can_specify_language_in_filename() { | ||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
config.languages.push(Language {code: String::from("fr"), rss: false}); | |||||
config.languages.push(Language { code: String::from("fr"), rss: false }); | |||||
let content = r#" | let content = r#" | ||||
+++ | +++ | ||||
+++ | +++ | ||||
@@ -594,7 +598,7 @@ Bonjour le monde"# | |||||
#[test] | #[test] | ||||
fn can_specify_language_in_filename_with_date() { | fn can_specify_language_in_filename_with_date() { | ||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
config.languages.push(Language {code: String::from("fr"), rss: false}); | |||||
config.languages.push(Language { code: String::from("fr"), rss: false }); | |||||
let content = r#" | let content = r#" | ||||
+++ | +++ | ||||
+++ | +++ | ||||
@@ -612,7 +616,7 @@ Bonjour le monde"# | |||||
#[test] | #[test] | ||||
fn i18n_frontmatter_path_overrides_default_permalink() { | fn i18n_frontmatter_path_overrides_default_permalink() { | ||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
config.languages.push(Language {code: String::from("fr"), rss: false}); | |||||
config.languages.push(Language { code: String::from("fr"), rss: false }); | |||||
let content = r#" | let content = r#" | ||||
+++ | +++ | ||||
path = "bonjour" | path = "bonjour" | ||||
@@ -54,6 +54,8 @@ pub struct Section { | |||||
/// The language of that section. `None` if the user doesn't setup `languages` in config. | /// The language of that section. `None` if the user doesn't setup `languages` in config. | ||||
/// Corresponds to the lang in the _index.{lang}.md file scheme | /// Corresponds to the lang in the _index.{lang}.md file scheme | ||||
pub lang: Option<String>, | pub lang: Option<String>, | ||||
/// Contains all the translated version of that section | |||||
pub translations: Vec<Key>, | |||||
} | } | ||||
impl Section { | impl Section { | ||||
@@ -78,6 +80,7 @@ impl Section { | |||||
word_count: None, | word_count: None, | ||||
reading_time: None, | reading_time: None, | ||||
lang: None, | lang: None, | ||||
translations: Vec::new(), | |||||
} | } | ||||
} | } | ||||
@@ -235,6 +238,7 @@ impl Default for Section { | |||||
reading_time: None, | reading_time: None, | ||||
word_count: None, | word_count: None, | ||||
lang: None, | lang: None, | ||||
translations: Vec::new(), | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -302,7 +306,7 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_specify_language_in_filename() { | fn can_specify_language_in_filename() { | ||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
config.languages.push(Language {code: String::from("fr"), rss: false}); | |||||
config.languages.push(Language { code: String::from("fr"), rss: false }); | |||||
let content = r#" | let content = r#" | ||||
+++ | +++ | ||||
+++ | +++ | ||||
@@ -80,12 +80,8 @@ impl Library { | |||||
/// Find out the direct subsections of each subsection if there are some | /// Find out the direct subsections of each subsection if there are some | ||||
/// as well as the pages for each section | /// as well as the pages for each section | ||||
pub fn populate_sections(&mut self) { | pub fn populate_sections(&mut self) { | ||||
let root_path= self | |||||
.sections | |||||
.values() | |||||
.find(|s| s.is_index()) | |||||
.map(|s| s.file.parent.clone()) | |||||
.unwrap(); | |||||
let root_path = | |||||
self.sections.values().find(|s| s.is_index()).map(|s| s.file.parent.clone()).unwrap(); | |||||
// We are going to get both the ancestors and grandparents for each section in one go | // We are going to get both the ancestors and grandparents for each section in one go | ||||
let mut ancestors: HashMap<PathBuf, Vec<_>> = HashMap::new(); | let mut ancestors: HashMap<PathBuf, Vec<_>> = HashMap::new(); | ||||
let mut subsections: HashMap<PathBuf, Vec<_>> = HashMap::new(); | let mut subsections: HashMap<PathBuf, Vec<_>> = HashMap::new(); | ||||
@@ -119,7 +115,9 @@ impl Library { | |||||
if path == section.file.parent { | if path == section.file.parent { | ||||
continue; | continue; | ||||
} | } | ||||
if let Some(section_key) = self.paths_to_sections.get(&path.join(§ion.file.filename)) { | |||||
if let Some(section_key) = | |||||
self.paths_to_sections.get(&path.join(§ion.file.filename)) | |||||
{ | |||||
parents.push(*section_key); | parents.push(*section_key); | ||||
} | } | ||||
} | } | ||||
@@ -288,7 +288,7 @@ pub fn after_content_rename(site: &mut Site, old: &Path, new: &Path) -> Result<( | |||||
old.to_path_buf() | old.to_path_buf() | ||||
}; | }; | ||||
site.library.remove_page(&old_path); | site.library.remove_page(&old_path); | ||||
return handle_page_editing(site, &new_path); | |||||
handle_page_editing(site, &new_path) | |||||
} | } | ||||
/// What happens when a section or a page is created/edited | /// What happens when a section or a page is created/edited | ||||
@@ -267,9 +267,5 @@ Edite | |||||
let res = after_content_change(&mut site, &file_path); | let res = after_content_change(&mut site, &file_path); | ||||
println!("{:?}", res); | println!("{:?}", res); | ||||
assert!(res.is_ok()); | assert!(res.is_ok()); | ||||
assert!(file_contains!( | |||||
site_path, | |||||
"public/fr/blog/with-assets/index.html", | |||||
"Edite" | |||||
)); | |||||
assert!(file_contains!(site_path, "public/fr/blog/with-assets/index.html", "Edite")); | |||||
} | } |
@@ -152,7 +152,10 @@ impl Site { | |||||
fn index_section_paths(&self) -> Vec<(PathBuf, Option<String>)> { | fn index_section_paths(&self) -> Vec<(PathBuf, Option<String>)> { | ||||
let mut res = vec![(self.content_path.join("_index.md"), None)]; | let mut res = vec![(self.content_path.join("_index.md"), None)]; | ||||
for language in &self.config.languages { | for language in &self.config.languages { | ||||
res.push((self.content_path.join(format!("_index.{}.md", language.code)), Some(language.code.clone()))); | |||||
res.push(( | |||||
self.content_path.join(format!("_index.{}.md", language.code)), | |||||
Some(language.code.clone()), | |||||
)); | |||||
} | } | ||||
res | res | ||||
} | } | ||||
@@ -189,7 +192,9 @@ impl Site { | |||||
.unwrap() | .unwrap() | ||||
.filter_map(|e| e.ok()) | .filter_map(|e| e.ok()) | ||||
.filter(|e| !e.as_path().file_name().unwrap().to_str().unwrap().starts_with('.')) | .filter(|e| !e.as_path().file_name().unwrap().to_str().unwrap().starts_with('.')) | ||||
.partition(|entry| entry.as_path().file_name().unwrap().to_str().unwrap().starts_with("_index.")); | |||||
.partition(|entry| { | |||||
entry.as_path().file_name().unwrap().to_str().unwrap().starts_with("_index.") | |||||
}); | |||||
self.library = Library::new(page_entries.len(), section_entries.len()); | self.library = Library::new(page_entries.len(), section_entries.len()); | ||||
@@ -241,7 +246,8 @@ impl Site { | |||||
let mut index_section = Section::default(); | let mut index_section = Section::default(); | ||||
index_section.file.parent = self.content_path.clone(); | index_section.file.parent = self.content_path.clone(); | ||||
index_section.file.name = "_index".to_string(); | index_section.file.name = "_index".to_string(); | ||||
index_section.file.filename = index_path.file_name().unwrap().to_string_lossy().to_string(); | |||||
index_section.file.filename = | |||||
index_path.file_name().unwrap().to_string_lossy().to_string(); | |||||
if let Some(ref l) = lang { | if let Some(ref l) = lang { | ||||
index_section.permalink = self.config.make_permalink(l); | index_section.permalink = self.config.make_permalink(l); | ||||
let filename = format!("_index.{}.md", l); | let filename = format!("_index.{}.md", l); | ||||
@@ -353,7 +359,8 @@ impl Site { | |||||
pub fn add_page(&mut self, mut page: Page, render: bool) -> Result<Option<Page>> { | pub fn add_page(&mut self, mut page: Page, render: bool) -> Result<Option<Page>> { | ||||
self.permalinks.insert(page.file.relative.clone(), page.permalink.clone()); | self.permalinks.insert(page.file.relative.clone(), page.permalink.clone()); | ||||
if render { | if render { | ||||
let insert_anchor = self.find_parent_section_insert_anchor(&page.file.parent, &page.lang); | |||||
let insert_anchor = | |||||
self.find_parent_section_insert_anchor(&page.file.parent, &page.lang); | |||||
page.render_markdown(&self.permalinks, &self.tera, &self.config, insert_anchor)?; | page.render_markdown(&self.permalinks, &self.tera, &self.config, insert_anchor)?; | ||||
} | } | ||||
let prev = self.library.remove_page(&page.file.path); | let prev = self.library.remove_page(&page.file.path); | ||||
@@ -379,7 +386,11 @@ impl Site { | |||||
/// Finds the insert_anchor for the parent section of the directory at `path`. | /// Finds the insert_anchor for the parent section of the directory at `path`. | ||||
/// Defaults to `AnchorInsert::None` if no parent section found | /// Defaults to `AnchorInsert::None` if no parent section found | ||||
pub fn find_parent_section_insert_anchor(&self, parent_path: &PathBuf, lang: &Option<String>) -> InsertAnchor { | |||||
pub fn find_parent_section_insert_anchor( | |||||
&self, | |||||
parent_path: &PathBuf, | |||||
lang: &Option<String>, | |||||
) -> InsertAnchor { | |||||
let parent = if let Some(ref l) = lang { | let parent = if let Some(ref l) = lang { | ||||
parent_path.join(format!("_index.{}.md", l)) | parent_path.join(format!("_index.{}.md", l)) | ||||
} else { | } else { | ||||
@@ -746,7 +757,7 @@ impl Site { | |||||
let number_pagers = (section.pages.len() as f64 | let number_pagers = (section.pages.len() as f64 | ||||
/ section.meta.paginate_by.unwrap() as f64) | / section.meta.paginate_by.unwrap() as f64) | ||||
.ceil() as isize; | .ceil() as isize; | ||||
for i in 1..number_pagers + 1 { | |||||
for i in 1..=number_pagers { | |||||
let permalink = | let permalink = | ||||
format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i); | format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i); | ||||
sections.push(SitemapEntry::new(permalink, None)) | sections.push(SitemapEntry::new(permalink, None)) | ||||
@@ -770,7 +781,7 @@ impl Site { | |||||
let number_pagers = (item.pages.len() as f64 | let number_pagers = (item.pages.len() as f64 | ||||
/ taxonomy.kind.paginate_by.unwrap() as f64) | / taxonomy.kind.paginate_by.unwrap() as f64) | ||||
.ceil() as isize; | .ceil() as isize; | ||||
for i in 1..number_pagers + 1 { | |||||
for i in 1..=number_pagers { | |||||
let permalink = self.config.make_permalink(&format!( | let permalink = self.config.make_permalink(&format!( | ||||
"{}/{}/{}/{}", | "{}/{}/{}/{}", | ||||
name, | name, | ||||
@@ -822,7 +833,7 @@ impl Site { | |||||
context.insert("last_build_date", &pages[0].meta.date.clone()); | context.insert("last_build_date", &pages[0].meta.date.clone()); | ||||
// limit to the last n elements if the limit is set; otherwise use all. | // limit to the last n elements if the limit is set; otherwise use all. | ||||
let num_entries = self.config.rss_limit.unwrap_or(pages.len()); | |||||
let num_entries = self.config.rss_limit.unwrap_or_else(|| pages.len()); | |||||
let p = pages | let p = pages | ||||
.iter() | .iter() | ||||
.take(num_entries) | .take(num_entries) | ||||
@@ -1,5 +1,5 @@ | |||||
extern crate tempfile; | |||||
extern crate site; | extern crate site; | ||||
extern crate tempfile; | |||||
use std::env; | use std::env; | ||||
use std::path::PathBuf; | use std::path::PathBuf; | ||||
@@ -50,7 +50,10 @@ pub fn build_site(name: &str) -> (Site, TempDir, PathBuf) { | |||||
} | } | ||||
/// Same as `build_site` but has a hook to setup some config options | /// Same as `build_site` but has a hook to setup some config options | ||||
pub fn build_site_with_setup<F>(name: &str, mut setup_cb: F) -> (Site, TempDir, PathBuf) where F: FnMut(Site) -> (Site, bool) { | |||||
pub fn build_site_with_setup<F>(name: &str, mut setup_cb: F) -> (Site, TempDir, PathBuf) | |||||
where | |||||
F: FnMut(Site) -> (Site, bool), | |||||
{ | |||||
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); | ||||
path.push(name); | path.push(name); | ||||
let site = Site::new(&path, "config.toml").unwrap(); | let site = Site::new(&path, "config.toml").unwrap(); | ||||
@@ -6,9 +6,9 @@ use std::collections::HashMap; | |||||
use std::env; | use std::env; | ||||
use std::path::Path; | use std::path::Path; | ||||
use common::{build_site, build_site_with_setup}; | |||||
use config::Taxonomy; | use config::Taxonomy; | ||||
use site::Site; | use site::Site; | ||||
use common::{build_site, build_site_with_setup}; | |||||
#[test] | #[test] | ||||
fn can_parse_site() { | fn can_parse_site() { | ||||
@@ -425,7 +425,10 @@ fn can_build_site_with_pagination_for_index() { | |||||
let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { | let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { | ||||
site.load().unwrap(); | site.load().unwrap(); | ||||
{ | { | ||||
let index = site.library.get_section_mut(&site.base_path.join("content").join("_index.md")).unwrap(); | |||||
let index = site | |||||
.library | |||||
.get_section_mut(&site.base_path.join("content").join("_index.md")) | |||||
.unwrap(); | |||||
index.meta.paginate_by = Some(2); | index.meta.paginate_by = Some(2); | ||||
index.meta.template = Some("index_paginated.html".to_string()); | index.meta.template = Some("index_paginated.html".to_string()); | ||||
} | } | ||||
@@ -482,8 +485,10 @@ fn can_build_site_with_pagination_for_taxonomy() { | |||||
for (i, (_, page)) in site.library.pages_mut().iter_mut().enumerate() { | for (i, (_, page)) in site.library.pages_mut().iter_mut().enumerate() { | ||||
page.meta.taxonomies = { | page.meta.taxonomies = { | ||||
let mut taxonomies = HashMap::new(); | let mut taxonomies = HashMap::new(); | ||||
taxonomies | |||||
.insert("tags".to_string(), vec![if i % 2 == 0 { "A" } else { "B" }.to_string()]); | |||||
taxonomies.insert( | |||||
"tags".to_string(), | |||||
vec![if i % 2 == 0 { "A" } else { "B" }.to_string()], | |||||
); | |||||
taxonomies | taxonomies | ||||
}; | }; | ||||
} | } | ||||
@@ -3,8 +3,8 @@ mod common; | |||||
use std::env; | use std::env; | ||||
use site::Site; | |||||
use common::build_site; | use common::build_site; | ||||
use site::Site; | |||||
#[test] | #[test] | ||||
fn can_parse_multilingual_site() { | fn can_parse_multilingual_site() { | ||||
@@ -17,11 +17,13 @@ fn can_parse_multilingual_site() { | |||||
assert_eq!(site.library.sections().len(), 4); | assert_eq!(site.library.sections().len(), 4); | ||||
// default index sections | // default index sections | ||||
let default_index_section = site.library.get_section(&path.join("content").join("_index.md")).unwrap(); | |||||
let default_index_section = | |||||
site.library.get_section(&path.join("content").join("_index.md")).unwrap(); | |||||
assert_eq!(default_index_section.pages.len(), 1); | assert_eq!(default_index_section.pages.len(), 1); | ||||
assert!(default_index_section.ancestors.is_empty()); | assert!(default_index_section.ancestors.is_empty()); | ||||
let fr_index_section = site.library.get_section(&path.join("content").join("_index.fr.md")).unwrap(); | |||||
let fr_index_section = | |||||
site.library.get_section(&path.join("content").join("_index.fr.md")).unwrap(); | |||||
assert_eq!(fr_index_section.pages.len(), 1); | assert_eq!(fr_index_section.pages.len(), 1); | ||||
assert!(fr_index_section.ancestors.is_empty()); | assert!(fr_index_section.ancestors.is_empty()); | ||||
@@ -31,7 +33,10 @@ fn can_parse_multilingual_site() { | |||||
let default_blog = site.library.get_section(&blog_path.join("_index.md")).unwrap(); | let default_blog = site.library.get_section(&blog_path.join("_index.md")).unwrap(); | ||||
assert_eq!(default_blog.subsections.len(), 0); | assert_eq!(default_blog.subsections.len(), 0); | ||||
assert_eq!(default_blog.pages.len(), 4); | assert_eq!(default_blog.pages.len(), 4); | ||||
assert_eq!(default_blog.ancestors, vec![*site.library.get_section_key(&default_index_section.file.path).unwrap()]); | |||||
assert_eq!( | |||||
default_blog.ancestors, | |||||
vec![*site.library.get_section_key(&default_index_section.file.path).unwrap()] | |||||
); | |||||
for key in &default_blog.pages { | for key in &default_blog.pages { | ||||
let page = site.library.get_page_by_key(*key); | let page = site.library.get_page_by_key(*key); | ||||
assert_eq!(page.lang, None); | assert_eq!(page.lang, None); | ||||
@@ -40,7 +45,10 @@ fn can_parse_multilingual_site() { | |||||
let fr_blog = site.library.get_section(&blog_path.join("_index.fr.md")).unwrap(); | let fr_blog = site.library.get_section(&blog_path.join("_index.fr.md")).unwrap(); | ||||
assert_eq!(fr_blog.subsections.len(), 0); | assert_eq!(fr_blog.subsections.len(), 0); | ||||
assert_eq!(fr_blog.pages.len(), 3); | assert_eq!(fr_blog.pages.len(), 3); | ||||
assert_eq!(fr_blog.ancestors, vec![*site.library.get_section_key(&fr_index_section.file.path).unwrap()]); | |||||
assert_eq!( | |||||
fr_blog.ancestors, | |||||
vec![*site.library.get_section_key(&fr_index_section.file.path).unwrap()] | |||||
); | |||||
for key in &fr_blog.pages { | for key in &fr_blog.pages { | ||||
let page = site.library.get_page_by_key(*key); | let page = site.library.get_page_by_key(*key); | ||||
assert_eq!(page.lang, Some("fr".to_string())); | assert_eq!(page.lang, Some("fr".to_string())); | ||||
@@ -50,24 +50,24 @@ impl FromStr for OutputFormat { | |||||
type Err = Error; | type Err = Error; | ||||
fn from_str(output_format: &str) -> Result<Self> { | fn from_str(output_format: &str) -> Result<Self> { | ||||
return match output_format { | |||||
match output_format { | |||||
"toml" => Ok(OutputFormat::Toml), | "toml" => Ok(OutputFormat::Toml), | ||||
"csv" => Ok(OutputFormat::Csv), | "csv" => Ok(OutputFormat::Csv), | ||||
"json" => Ok(OutputFormat::Json), | "json" => Ok(OutputFormat::Json), | ||||
"plain" => Ok(OutputFormat::Plain), | "plain" => Ok(OutputFormat::Plain), | ||||
format => Err(format!("Unknown output format {}", format).into()), | format => Err(format!("Unknown output format {}", format).into()), | ||||
}; | |||||
} | |||||
} | } | ||||
} | } | ||||
impl OutputFormat { | impl OutputFormat { | ||||
fn as_accept_header(&self) -> header::HeaderValue { | fn as_accept_header(&self) -> header::HeaderValue { | ||||
return header::HeaderValue::from_static(match self { | |||||
header::HeaderValue::from_static(match self { | |||||
OutputFormat::Json => "application/json", | OutputFormat::Json => "application/json", | ||||
OutputFormat::Csv => "text/csv", | OutputFormat::Csv => "text/csv", | ||||
OutputFormat::Toml => "application/toml", | OutputFormat::Toml => "application/toml", | ||||
OutputFormat::Plain => "text/plain", | OutputFormat::Plain => "text/plain", | ||||
}); | |||||
}) | |||||
} | } | ||||
} | } | ||||
@@ -91,18 +91,18 @@ impl DataSource { | |||||
if let Some(url) = url_arg { | if let Some(url) = url_arg { | ||||
return Url::parse(&url) | return Url::parse(&url) | ||||
.map(|parsed_url| DataSource::Url(parsed_url)) | |||||
.map(DataSource::Url) | |||||
.map_err(|e| format!("Failed to parse {} as url: {}", url, e).into()); | .map_err(|e| format!("Failed to parse {} as url: {}", url, e).into()); | ||||
} | } | ||||
return Err(GET_DATA_ARGUMENT_ERROR_MESSAGE.into()); | |||||
Err(GET_DATA_ARGUMENT_ERROR_MESSAGE.into()) | |||||
} | } | ||||
fn get_cache_key(&self, format: &OutputFormat) -> u64 { | fn get_cache_key(&self, format: &OutputFormat) -> u64 { | ||||
let mut hasher = DefaultHasher::new(); | let mut hasher = DefaultHasher::new(); | ||||
format.hash(&mut hasher); | format.hash(&mut hasher); | ||||
self.hash(&mut hasher); | self.hash(&mut hasher); | ||||
return hasher.finish(); | |||||
hasher.finish() | |||||
} | } | ||||
} | } | ||||
@@ -123,10 +123,9 @@ fn get_data_source_from_args( | |||||
args: &HashMap<String, Value>, | args: &HashMap<String, Value>, | ||||
) -> Result<DataSource> { | ) -> Result<DataSource> { | ||||
let path_arg = optional_arg!(String, args.get("path"), GET_DATA_ARGUMENT_ERROR_MESSAGE); | let path_arg = optional_arg!(String, args.get("path"), GET_DATA_ARGUMENT_ERROR_MESSAGE); | ||||
let url_arg = optional_arg!(String, args.get("url"), GET_DATA_ARGUMENT_ERROR_MESSAGE); | let url_arg = optional_arg!(String, args.get("url"), GET_DATA_ARGUMENT_ERROR_MESSAGE); | ||||
return DataSource::from_args(path_arg, url_arg, content_path); | |||||
DataSource::from_args(path_arg, url_arg, content_path) | |||||
} | } | ||||
fn read_data_file(base_path: &PathBuf, full_path: PathBuf) -> Result<String> { | fn read_data_file(base_path: &PathBuf, full_path: PathBuf) -> Result<String> { | ||||
@@ -140,9 +139,9 @@ fn read_data_file(base_path: &PathBuf, full_path: PathBuf) -> Result<String> { | |||||
) | ) | ||||
.into()); | .into()); | ||||
} | } | ||||
return read_file(&full_path).map_err(|e| { | |||||
read_file(&full_path).map_err(|e| { | |||||
format!("`load_data`: error {} loading file {}", full_path.to_str().unwrap(), e).into() | format!("`load_data`: error {} loading file {}", full_path.to_str().unwrap(), e).into() | ||||
}); | |||||
}) | |||||
} | } | ||||
fn get_output_format_from_args( | fn get_output_format_from_args( | ||||
@@ -161,14 +160,14 @@ fn get_output_format_from_args( | |||||
let from_extension = if let DataSource::Path(path) = data_source { | let from_extension = if let DataSource::Path(path) = data_source { | ||||
let extension_result: Result<&str> = | let extension_result: Result<&str> = | ||||
path.extension().map(|extension| extension.to_str().unwrap()).ok_or( | |||||
format!("Could not determine format for {} from extension", path.display()).into(), | |||||
); | |||||
path.extension().map(|extension| extension.to_str().unwrap()).ok_or_else(|| { | |||||
format!("Could not determine format for {} from extension", path.display()).into() | |||||
}); | |||||
extension_result? | extension_result? | ||||
} else { | } else { | ||||
"plain" | "plain" | ||||
}; | }; | ||||
return OutputFormat::from_str(from_extension); | |||||
OutputFormat::from_str(from_extension) | |||||
} | } | ||||
/// A global function to load data from a file or from a URL | /// A global function to load data from a file or from a URL | ||||
@@ -231,7 +230,7 @@ pub fn make_load_data(content_path: PathBuf, base_path: PathBuf) -> GlobalFn { | |||||
fn load_json(json_data: String) -> Result<Value> { | fn load_json(json_data: String) -> Result<Value> { | ||||
let json_content: Value = | let json_content: Value = | ||||
serde_json::from_str(json_data.as_str()).map_err(|e| format!("{:?}", e))?; | serde_json::from_str(json_data.as_str()).map_err(|e| format!("{:?}", e))?; | ||||
return Ok(json_content); | |||||
Ok(json_content) | |||||
} | } | ||||
/// Parse a TOML string and convert it to a Tera Value | /// Parse a TOML string and convert it to a Tera Value | ||||
@@ -142,9 +142,11 @@ pub fn make_get_taxonomy(all_taxonomies: &[Taxonomy], library: &Library) -> Glob | |||||
let container = match taxonomies.get(&kind) { | let container = match taxonomies.get(&kind) { | ||||
Some(c) => c, | Some(c) => c, | ||||
None => { | None => { | ||||
return Err( | |||||
format!("`get_taxonomy` received an unknown taxonomy as kind: {}", kind).into() | |||||
); | |||||
return Err(format!( | |||||
"`get_taxonomy` received an unknown taxonomy as kind: {}", | |||||
kind | |||||
) | |||||
.into()); | |||||
} | } | ||||
}; | }; | ||||
@@ -502,12 +502,9 @@ fn detect_change_kind(pwd: &Path, path: &Path) -> (ChangeKind, PathBuf) { | |||||
/// Check if the directory at path contains any file | /// Check if the directory at path contains any file | ||||
fn is_folder_empty(dir: &Path) -> bool { | fn is_folder_empty(dir: &Path) -> bool { | ||||
// Can panic if we don't have the rights I guess? | // Can panic if we don't have the rights I guess? | ||||
for _ in read_dir(dir).expect("Failed to read a directory to see if it was empty") { | |||||
// If we get there, that means we have a file | |||||
return false; | |||||
} | |||||
true | |||||
let files: Vec<_> = | |||||
read_dir(dir).expect("Failed to read a directory to see if it was empty").collect(); | |||||
files.is_empty() | |||||
} | } | ||||
#[cfg(test)] | #[cfg(test)] | ||||