@@ -229,9 +229,10 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "bincode" | name = "bincode" | ||||
version = "1.0.1" | |||||
version = "1.1.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -1372,7 +1373,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "notify" | name = "notify" | ||||
version = "4.0.8" | |||||
version = "4.0.9" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -2260,7 +2261,7 @@ name = "syntect" | |||||
version = "3.0.2" | version = "3.0.2" | ||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"bincode 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -3040,7 +3041,7 @@ dependencies = [ | |||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"front_matter 0.1.0", | "front_matter 0.1.0", | ||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"notify 4.0.8 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"notify 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"rebuild 0.1.0", | "rebuild 0.1.0", | ||||
"site 0.1.0", | "site 0.1.0", | ||||
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -3069,7 +3070,7 @@ dependencies = [ | |||||
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" | "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" | ||||
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" | "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" | ||||
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" | "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" | ||||
"checksum bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f2fb9e29e72fd6bc12071533d5dc7664cb01480c59406f656d7ac25c7bd8ff7" | |||||
"checksum bincode 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "58470ad6460f0b0e89b0df5f17b8bd77ebae26af69dca0bd9ddc8b9e38abb2ff" | |||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" | "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" | ||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" | ||||
"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" | "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" | ||||
@@ -3191,7 +3192,7 @@ dependencies = [ | |||||
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" | "checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" | ||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" | ||||
"checksum nom 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b30adc557058ce00c9d0d7cb3c6e0b5bc6f36e2e2eabe74b0ba726d194abd588" | "checksum nom 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b30adc557058ce00c9d0d7cb3c6e0b5bc6f36e2e2eabe74b0ba726d194abd588" | ||||
"checksum notify 4.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c9b605e417814e88bb051c88a84f83655d6ad4fa32fc36d9a96296d86087692d" | |||||
"checksum notify 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9cc7ed2bd4b7edad3ee93b659c38e53dabb619f7274e127a0fab054ad2bb998d" | |||||
"checksum num-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d9fe8fcafd1b86a37ce8a1cfa15ae504817e0c8c2e7ad42767371461ac1d316d" | "checksum num-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d9fe8fcafd1b86a37ce8a1cfa15ae504817e0c8c2e7ad42767371461ac1d316d" | ||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" | "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" | ||||
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" | "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" | ||||
@@ -32,10 +32,8 @@ impl StdError for Error { | |||||
let mut source = self.source.as_ref().map(|c| &**c); | let mut source = self.source.as_ref().map(|c| &**c); | ||||
if source.is_none() { | if source.is_none() { | ||||
match self.kind { | match self.kind { | ||||
ErrorKind::Tera(ref err) => { | |||||
source = err.source() | |||||
}, | |||||
_ => () | |||||
ErrorKind::Tera(ref err) => source = err.source(), | |||||
_ => (), | |||||
}; | }; | ||||
} | } | ||||
@@ -68,7 +66,6 @@ impl Error { | |||||
} | } | ||||
} | } | ||||
impl From<&str> for Error { | impl From<&str> for Error { | ||||
fn from(e: &str) -> Self { | fn from(e: &str) -> Self { | ||||
Self::msg(e) | Self::msg(e) | ||||
@@ -12,7 +12,7 @@ extern crate toml; | |||||
extern crate errors; | extern crate errors; | ||||
extern crate utils; | extern crate utils; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use regex::Regex; | use regex::Regex; | ||||
use std::path::Path; | use std::path::Path; | ||||
@@ -72,7 +72,10 @@ pub fn split_section_content( | |||||
) -> Result<(SectionFrontMatter, String)> { | ) -> Result<(SectionFrontMatter, String)> { | ||||
let (front_matter, content) = split_content(file_path, content)?; | let (front_matter, content) = split_content(file_path, content)?; | ||||
let meta = SectionFrontMatter::parse(&front_matter).map_err(|e| { | let meta = SectionFrontMatter::parse(&front_matter).map_err(|e| { | ||||
Error::chain(format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()), e) | |||||
Error::chain( | |||||
format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()), | |||||
e, | |||||
) | |||||
})?; | })?; | ||||
Ok((meta, content)) | Ok((meta, content)) | ||||
} | } | ||||
@@ -82,7 +85,10 @@ pub fn split_section_content( | |||||
pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> { | pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> { | ||||
let (front_matter, content) = split_content(file_path, content)?; | let (front_matter, content) = split_content(file_path, content)?; | ||||
let meta = PageFrontMatter::parse(&front_matter).map_err(|e| { | let meta = PageFrontMatter::parse(&front_matter).map_err(|e| { | ||||
Error::chain(format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()), e) | |||||
Error::chain( | |||||
format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()), | |||||
e, | |||||
) | |||||
})?; | })?; | ||||
Ok((meta, content)) | Ok((meta, content)) | ||||
} | } | ||||
@@ -20,7 +20,7 @@ use image::{FilterType, GenericImageView}; | |||||
use rayon::prelude::*; | use rayon::prelude::*; | ||||
use regex::Regex; | use regex::Regex; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use utils::fs as ufs; | use utils::fs as ufs; | ||||
static RESIZED_SUBDIR: &'static str = "processed_images"; | static RESIZED_SUBDIR: &'static str = "processed_images"; | ||||
@@ -8,7 +8,7 @@ use slug::slugify; | |||||
use tera::{Context as TeraContext, Tera}; | use tera::{Context as TeraContext, Tera}; | ||||
use config::Config; | use config::Config; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use front_matter::{split_page_content, InsertAnchor, PageFrontMatter}; | use front_matter::{split_page_content, InsertAnchor, PageFrontMatter}; | ||||
use library::Library; | use library::Library; | ||||
use rendering::{render_content, Header, RenderContext}; | use rendering::{render_content, Header, RenderContext}; | ||||
@@ -126,7 +126,16 @@ impl Page { | |||||
page.reading_time = Some(reading_time); | page.reading_time = Some(reading_time); | ||||
let mut slug_from_dated_filename = None; | let mut slug_from_dated_filename = None; | ||||
if let Some(ref caps) = RFC3339_DATE.captures(&page.file.name.replace(".md", "")) { | |||||
let file_path = if page.file.name == "index" { | |||||
if let Some(parent) = page.file.path.parent() { | |||||
parent.file_name().unwrap().to_str().unwrap().to_string() | |||||
} else { | |||||
page.file.name.replace(".md", "") | |||||
} | |||||
} else { | |||||
page.file.name.replace(".md", "") | |||||
}; | |||||
if let Some(ref caps) = RFC3339_DATE.captures(&file_path) { | |||||
slug_from_dated_filename = Some(caps.name("slug").unwrap().as_str().to_string()); | slug_from_dated_filename = Some(caps.name("slug").unwrap().as_str().to_string()); | ||||
if page.meta.date.is_none() { | if page.meta.date.is_none() { | ||||
page.meta.date = Some(caps.name("datetime").unwrap().as_str().to_string()); | page.meta.date = Some(caps.name("datetime").unwrap().as_str().to_string()); | ||||
@@ -139,7 +148,11 @@ impl Page { | |||||
slug.trim().to_string() | slug.trim().to_string() | ||||
} else if page.file.name == "index" { | } else if page.file.name == "index" { | ||||
if let Some(parent) = page.file.path.parent() { | if let Some(parent) = page.file.path.parent() { | ||||
slugify(parent.file_name().unwrap().to_str().unwrap()) | |||||
if let Some(slug) = slug_from_dated_filename { | |||||
slugify(&slug) | |||||
} else { | |||||
slugify(parent.file_name().unwrap().to_str().unwrap()) | |||||
} | |||||
} else { | } else { | ||||
slugify(&page.file.name) | slugify(&page.file.name) | ||||
} | } | ||||
@@ -233,8 +246,9 @@ impl Page { | |||||
context.tera_context.insert("page", &SerializingPage::from_page_basic(self, None)); | context.tera_context.insert("page", &SerializingPage::from_page_basic(self, None)); | ||||
let res = render_content(&self.raw_content, &context) | |||||
.map_err(|e| Error::chain(format!("Failed to render content of {}", self.file.path.display()), e))?; | |||||
let res = render_content(&self.raw_content, &context).map_err(|e| { | |||||
Error::chain(format!("Failed to render content of {}", self.file.path.display()), e) | |||||
})?; | |||||
self.summary = res.summary_len.map(|l| res.body[0..l].to_owned()); | self.summary = res.summary_len.map(|l| res.body[0..l].to_owned()); | ||||
self.content = res.body; | self.content = res.body; | ||||
@@ -257,8 +271,9 @@ impl Page { | |||||
context.insert("page", &self.to_serialized(library)); | context.insert("page", &self.to_serialized(library)); | ||||
context.insert("lang", &self.lang); | context.insert("lang", &self.lang); | ||||
render_template(&tpl_name, tera, context, &config.theme) | |||||
.map_err(|e| Error::chain(format!("Failed to render page '{}'", self.file.path.display()), e)) | |||||
render_template(&tpl_name, tera, context, &config.theme).map_err(|e| { | |||||
Error::chain(format!("Failed to render page '{}'", self.file.path.display()), e) | |||||
}) | |||||
} | } | ||||
/// Creates a vectors of asset URLs. | /// Creates a vectors of asset URLs. | ||||
@@ -499,6 +514,31 @@ Hello world | |||||
assert_eq!(page.permalink, "http://a-website.com/posts/hey/"); | assert_eq!(page.permalink, "http://a-website.com/posts/hey/"); | ||||
} | } | ||||
// https://github.com/getzola/zola/issues/607 | |||||
#[test] | |||||
fn page_with_assets_and_date_in_folder_name() { | |||||
let tmp_dir = tempdir().expect("create temp dir"); | |||||
let path = tmp_dir.path(); | |||||
create_dir(&path.join("content")).expect("create content temp dir"); | |||||
create_dir(&path.join("content").join("posts")).expect("create posts temp dir"); | |||||
let nested_path = path.join("content").join("posts").join("2013-06-02_with-assets"); | |||||
create_dir(&nested_path).expect("create nested temp dir"); | |||||
let mut f = File::create(nested_path.join("index.md")).unwrap(); | |||||
f.write_all(b"+++\n\n+++\n").unwrap(); | |||||
File::create(nested_path.join("example.js")).unwrap(); | |||||
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()); | |||||
assert!(res.is_ok()); | |||||
let page = res.unwrap(); | |||||
assert_eq!(page.file.parent, path.join("content").join("posts")); | |||||
assert_eq!(page.slug, "with-assets"); | |||||
assert_eq!(page.meta.date, Some("2013-06-02".to_string())); | |||||
assert_eq!(page.assets.len(), 3); | |||||
assert_eq!(page.permalink, "http://a-website.com/posts/with-assets/"); | |||||
} | |||||
#[test] | #[test] | ||||
fn page_with_ignored_assets_filters_out_correct_files() { | fn page_with_ignored_assets_filters_out_correct_files() { | ||||
let tmp_dir = tempdir().expect("create temp dir"); | let tmp_dir = tempdir().expect("create temp dir"); | ||||
@@ -5,7 +5,7 @@ use slotmap::Key; | |||||
use tera::{Context as TeraContext, Tera}; | use tera::{Context as TeraContext, Tera}; | ||||
use config::Config; | use config::Config; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use front_matter::{split_section_content, SectionFrontMatter}; | use front_matter::{split_section_content, SectionFrontMatter}; | ||||
use rendering::{render_content, Header, RenderContext}; | use rendering::{render_content, Header, RenderContext}; | ||||
use utils::fs::{find_related_assets, read_file}; | use utils::fs::{find_related_assets, read_file}; | ||||
@@ -171,8 +171,9 @@ impl Section { | |||||
context.tera_context.insert("section", &SerializingSection::from_section_basic(self, None)); | context.tera_context.insert("section", &SerializingSection::from_section_basic(self, None)); | ||||
let res = render_content(&self.raw_content, &context) | |||||
.map_err(|e| Error::chain(format!("Failed to render content of {}", self.file.path.display()), e))?; | |||||
let res = render_content(&self.raw_content, &context).map_err(|e| { | |||||
Error::chain(format!("Failed to render content of {}", self.file.path.display()), e) | |||||
})?; | |||||
self.content = res.body; | self.content = res.body; | ||||
self.toc = res.toc; | self.toc = res.toc; | ||||
Ok(()) | Ok(()) | ||||
@@ -189,8 +190,9 @@ impl Section { | |||||
context.insert("section", &self.to_serialized(library)); | context.insert("section", &self.to_serialized(library)); | ||||
context.insert("lang", &self.lang); | context.insert("lang", &self.lang); | ||||
render_template(tpl_name, tera, context, &config.theme) | |||||
.map_err(|e| Error::chain(format!("Failed to render section '{}'", self.file.path.display()), e)) | |||||
render_template(tpl_name, tera, context, &config.theme).map_err(|e| { | |||||
Error::chain(format!("Failed to render section '{}'", self.file.path.display()), e) | |||||
}) | |||||
} | } | ||||
/// Is this the index section? | /// Is this the index section? | ||||
@@ -5,9 +5,9 @@ use slotmap::{DenseSlotMap, Key}; | |||||
use front_matter::SortBy; | use front_matter::SortBy; | ||||
use config::Config; | |||||
use content::{Page, Section}; | use content::{Page, Section}; | ||||
use sorting::{find_siblings, sort_pages_by_date, sort_pages_by_weight}; | use sorting::{find_siblings, sort_pages_by_date, sort_pages_by_weight}; | ||||
use config::Config; | |||||
/// Houses everything about pages and sections | /// Houses everything about pages and sections | ||||
/// Think of it as a database where each page and section has an id (Key here) | /// Think of it as a database where each page and section has an id (Key here) | ||||
@@ -4,7 +4,7 @@ use slotmap::Key; | |||||
use tera::{to_value, Context, Tera, Value}; | use tera::{to_value, Context, Tera, Value}; | ||||
use config::Config; | use config::Config; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use utils::templates::render_template; | use utils::templates::render_template; | ||||
use content::{Section, SerializingPage, SerializingSection}; | use content::{Section, SerializingPage, SerializingSection}; | ||||
@@ -5,7 +5,7 @@ use slug::slugify; | |||||
use tera::{Context, Tera}; | use tera::{Context, Tera}; | ||||
use config::{Config, Taxonomy as TaxonomyConfig}; | use config::{Config, Taxonomy as TaxonomyConfig}; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use utils::templates::render_template; | use utils::templates::render_template; | ||||
use content::SerializingPage; | use content::SerializingPage; | ||||
@@ -48,7 +48,13 @@ pub struct TaxonomyItem { | |||||
} | } | ||||
impl TaxonomyItem { | impl TaxonomyItem { | ||||
pub fn new(name: &str, taxonomy: &TaxonomyConfig, config: &Config, keys: Vec<Key>, library: &Library) -> Self { | |||||
pub fn new( | |||||
name: &str, | |||||
taxonomy: &TaxonomyConfig, | |||||
config: &Config, | |||||
keys: Vec<Key>, | |||||
library: &Library, | |||||
) -> Self { | |||||
// Taxonomy are almost always used for blogs so we filter by dates | // Taxonomy are almost always used for blogs so we filter by dates | ||||
// and it's not like we can sort things across sections by anything other | // and it's not like we can sort things across sections by anything other | ||||
// than dates | // than dates | ||||
@@ -145,7 +151,9 @@ impl Taxonomy { | |||||
context.insert("current_path", &format!("/{}/{}", self.kind.name, item.slug)); | context.insert("current_path", &format!("/{}/{}", self.kind.name, item.slug)); | ||||
render_template(&format!("{}/single.html", self.kind.name), tera, context, &config.theme) | render_template(&format!("{}/single.html", self.kind.name), tera, context, &config.theme) | ||||
.map_err(|e| Error::chain(format!("Failed to render single term {} page.", self.kind.name), e)) | |||||
.map_err(|e| { | |||||
Error::chain(format!("Failed to render single term {} page.", self.kind.name), e) | |||||
}) | |||||
} | } | ||||
pub fn render_all_terms( | pub fn render_all_terms( | ||||
@@ -164,7 +172,9 @@ impl Taxonomy { | |||||
context.insert("current_path", &self.kind.name); | context.insert("current_path", &self.kind.name); | ||||
render_template(&format!("{}/list.html", self.kind.name), tera, context, &config.theme) | render_template(&format!("{}/list.html", self.kind.name), tera, context, &config.theme) | ||||
.map_err(|e| Error::chain(format!("Failed to render a list of {} page.", self.kind.name), e)) | |||||
.map_err(|e| { | |||||
Error::chain(format!("Failed to render a list of {} page.", self.kind.name), e) | |||||
}) | |||||
} | } | ||||
pub fn to_serialized<'a>(&'a self, library: &'a Library) -> SerializedTaxonomy<'a> { | pub fn to_serialized<'a>(&'a self, library: &'a Library) -> SerializedTaxonomy<'a> { | ||||
@@ -232,7 +242,7 @@ mod tests { | |||||
use super::*; | use super::*; | ||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use config::{Config, Taxonomy as TaxonomyConfig, Language}; | |||||
use config::{Config, Language, Taxonomy as TaxonomyConfig}; | |||||
use content::Page; | use content::Page; | ||||
use library::Library; | use library::Library; | ||||
@@ -242,9 +252,21 @@ mod tests { | |||||
let mut library = Library::new(2, 0, false); | let mut library = Library::new(2, 0, false); | ||||
config.taxonomies = vec![ | config.taxonomies = vec![ | ||||
TaxonomyConfig { name: "categories".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }, | |||||
TaxonomyConfig { name: "tags".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }, | |||||
TaxonomyConfig { name: "authors".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }, | |||||
TaxonomyConfig { | |||||
name: "categories".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}, | |||||
TaxonomyConfig { | |||||
name: "tags".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}, | |||||
TaxonomyConfig { | |||||
name: "authors".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}, | |||||
]; | ]; | ||||
let mut page1 = Page::default(); | let mut page1 = Page::default(); | ||||
@@ -324,8 +346,11 @@ mod tests { | |||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
let mut library = Library::new(2, 0, false); | let mut library = Library::new(2, 0, false); | ||||
config.taxonomies = | |||||
vec![TaxonomyConfig { name: "authors".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }]; | |||||
config.taxonomies = vec![TaxonomyConfig { | |||||
name: "authors".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}]; | |||||
let mut page1 = Page::default(); | let mut page1 = Page::default(); | ||||
let mut taxo_page1 = HashMap::new(); | let mut taxo_page1 = HashMap::new(); | ||||
taxo_page1.insert("tags".to_string(), vec!["rust".to_string(), "db".to_string()]); | taxo_page1.insert("tags".to_string(), vec!["rust".to_string(), "db".to_string()]); | ||||
@@ -346,13 +371,25 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_make_taxonomies_in_multiple_languages() { | fn can_make_taxonomies_in_multiple_languages() { | ||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
config.languages.push(Language {rss: false, code: "fr".to_string()}); | |||||
config.languages.push(Language { rss: false, code: "fr".to_string() }); | |||||
let mut library = Library::new(2, 0, true); | let mut library = Library::new(2, 0, true); | ||||
config.taxonomies = vec![ | config.taxonomies = vec![ | ||||
TaxonomyConfig { name: "categories".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }, | |||||
TaxonomyConfig { name: "tags".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }, | |||||
TaxonomyConfig { name: "auteurs".to_string(), lang: "fr".to_string(), ..TaxonomyConfig::default() }, | |||||
TaxonomyConfig { | |||||
name: "categories".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}, | |||||
TaxonomyConfig { | |||||
name: "tags".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}, | |||||
TaxonomyConfig { | |||||
name: "auteurs".to_string(), | |||||
lang: "fr".to_string(), | |||||
..TaxonomyConfig::default() | |||||
}, | |||||
]; | ]; | ||||
let mut page1 = Page::default(); | let mut page1 = Page::default(); | ||||
@@ -410,7 +447,10 @@ mod tests { | |||||
assert_eq!(authors.items[0].name, "Vincent Prouillet"); | assert_eq!(authors.items[0].name, "Vincent Prouillet"); | ||||
assert_eq!(authors.items[0].slug, "vincent-prouillet"); | assert_eq!(authors.items[0].slug, "vincent-prouillet"); | ||||
assert_eq!(authors.items[0].permalink, "http://a-website.com/fr/auteurs/vincent-prouillet/"); | |||||
assert_eq!( | |||||
authors.items[0].permalink, | |||||
"http://a-website.com/fr/auteurs/vincent-prouillet/" | |||||
); | |||||
assert_eq!(authors.items[0].pages.len(), 1); | assert_eq!(authors.items[0].pages.len(), 1); | ||||
assert_eq!(categories.items[0].name, "Other"); | assert_eq!(categories.items[0].name, "Other"); | ||||
@@ -430,7 +470,7 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn errors_on_taxonomy_of_different_language() { | fn errors_on_taxonomy_of_different_language() { | ||||
let mut config = Config::default(); | let mut config = Config::default(); | ||||
config.languages.push(Language {rss: false, code: "fr".to_string()}); | |||||
config.languages.push(Language { rss: false, code: "fr".to_string() }); | |||||
let mut library = Library::new(2, 0, false); | let mut library = Library::new(2, 0, false); | ||||
config.taxonomies = | config.taxonomies = | ||||
@@ -155,12 +155,14 @@ fn handle_section_editing(site: &mut Site, path: &Path) -> Result<()> { | |||||
SectionChangesNeeded::Sort => { | SectionChangesNeeded::Sort => { | ||||
site.register_tera_global_fns(); | site.register_tera_global_fns(); | ||||
} | } | ||||
SectionChangesNeeded::Render => { | |||||
site.render_section(&site.library.read().unwrap().get_section(&pathbuf).unwrap(), false)? | |||||
} | |||||
SectionChangesNeeded::RenderWithPages => { | |||||
site.render_section(&site.library.read().unwrap().get_section(&pathbuf).unwrap(), true)? | |||||
} | |||||
SectionChangesNeeded::Render => site.render_section( | |||||
&site.library.read().unwrap().get_section(&pathbuf).unwrap(), | |||||
false, | |||||
)?, | |||||
SectionChangesNeeded::RenderWithPages => site.render_section( | |||||
&site.library.read().unwrap().get_section(&pathbuf).unwrap(), | |||||
true, | |||||
)?, | |||||
// not a common enough operation to make it worth optimizing | // not a common enough operation to make it worth optimizing | ||||
SectionChangesNeeded::Delete | SectionChangesNeeded::Transparent => { | SectionChangesNeeded::Delete | SectionChangesNeeded::Transparent => { | ||||
site.build()?; | site.build()?; | ||||
@@ -182,7 +184,7 @@ macro_rules! render_parent_sections { | |||||
($site: expr, $path: expr) => { | ($site: expr, $path: expr) => { | ||||
for s in $site.library.read().unwrap().find_parent_sections($path) { | for s in $site.library.read().unwrap().find_parent_sections($path) { | ||||
$site.render_section(s, false)?; | $site.render_section(s, false)?; | ||||
}; | |||||
} | |||||
}; | }; | ||||
} | } | ||||
@@ -230,7 +232,9 @@ fn handle_page_editing(site: &mut Site, path: &Path) -> Result<()> { | |||||
} | } | ||||
PageChangesNeeded::Render => { | PageChangesNeeded::Render => { | ||||
render_parent_sections!(site, path); | render_parent_sections!(site, path); | ||||
site.render_page(&site.library.read().unwrap().get_page(&path.to_path_buf()).unwrap())?; | |||||
site.render_page( | |||||
&site.library.read().unwrap().get_page(&path.to_path_buf()).unwrap(), | |||||
)?; | |||||
} | } | ||||
}; | }; | ||||
} | } | ||||
@@ -4,7 +4,7 @@ use pulldown_cmark as cmark; | |||||
use slug::slugify; | use slug::slugify; | ||||
use syntect::easy::HighlightLines; | use syntect::easy::HighlightLines; | ||||
use syntect::html::{ | use syntect::html::{ | ||||
IncludeBackground, start_highlighted_html_snippet, styled_line_to_highlighted_html, | |||||
start_highlighted_html_snippet, styled_line_to_highlighted_html, IncludeBackground, | |||||
}; | }; | ||||
use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET}; | use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET}; | ||||
@@ -12,13 +12,14 @@ use context::RenderContext; | |||||
use errors::{Error, Result}; | use errors::{Error, Result}; | ||||
use front_matter::InsertAnchor; | use front_matter::InsertAnchor; | ||||
use link_checker::check_url; | use link_checker::check_url; | ||||
use table_of_contents::{Header, make_table_of_contents}; | |||||
use table_of_contents::{make_table_of_contents, Header}; | |||||
use utils::site::resolve_internal_link; | use utils::site::resolve_internal_link; | ||||
use utils::vec::InsertMany; | use utils::vec::InsertMany; | ||||
use self::cmark::{Event, Options, Parser, Tag}; | use self::cmark::{Event, Options, Parser, Tag}; | ||||
const CONTINUE_READING: &str = "<p id=\"zola-continue-reading\"><a name=\"continue-reading\"></a></p>\n"; | |||||
const CONTINUE_READING: &str = | |||||
"<p id=\"zola-continue-reading\"><a name=\"continue-reading\"></a></p>\n"; | |||||
const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html"; | const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html"; | ||||
#[derive(Debug)] | #[derive(Debug)] | ||||
@@ -88,9 +89,7 @@ fn fix_link(link: &str, context: &RenderContext) -> Result<String> { | |||||
if res.is_valid() { | if res.is_valid() { | ||||
link.to_string() | link.to_string() | ||||
} else { | } else { | ||||
return Err( | |||||
format!("Link {} is not valid: {}", link, res.message()).into(), | |||||
); | |||||
return Err(format!("Link {} is not valid: {}", link, res.message()).into()); | |||||
} | } | ||||
} else { | } else { | ||||
link.to_string() | link.to_string() | ||||
@@ -148,78 +147,84 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
opts.insert(Options::ENABLE_FOOTNOTES); | opts.insert(Options::ENABLE_FOOTNOTES); | ||||
{ | { | ||||
let mut events = Parser::new_ext(content, opts).map(|event| { | |||||
match event { | |||||
Event::Text(text) => { | |||||
// if we are in the middle of a code block | |||||
if let Some((ref mut highlighter, in_extra)) = highlighter { | |||||
let highlighted = if in_extra { | |||||
if let Some(ref extra) = context.config.extra_syntax_set { | |||||
highlighter.highlight(&text, &extra) | |||||
let mut events = Parser::new_ext(content, opts) | |||||
.map(|event| { | |||||
match event { | |||||
Event::Text(text) => { | |||||
// if we are in the middle of a code block | |||||
if let Some((ref mut highlighter, in_extra)) = highlighter { | |||||
let highlighted = if in_extra { | |||||
if let Some(ref extra) = context.config.extra_syntax_set { | |||||
highlighter.highlight(&text, &extra) | |||||
} else { | |||||
unreachable!( | |||||
"Got a highlighter from extra syntaxes but no extra?" | |||||
); | |||||
} | |||||
} else { | } else { | ||||
unreachable!("Got a highlighter from extra syntaxes but no extra?"); | |||||
} | |||||
} else { | |||||
highlighter.highlight(&text, &SYNTAX_SET) | |||||
}; | |||||
//let highlighted = &highlighter.highlight(&text, ss); | |||||
let html = styled_line_to_highlighted_html(&highlighted, background); | |||||
return Event::Html(Owned(html)); | |||||
} | |||||
highlighter.highlight(&text, &SYNTAX_SET) | |||||
}; | |||||
//let highlighted = &highlighter.highlight(&text, ss); | |||||
let html = styled_line_to_highlighted_html(&highlighted, background); | |||||
return Event::Html(Owned(html)); | |||||
} | |||||
// Business as usual | |||||
Event::Text(text) | |||||
} | |||||
Event::Start(Tag::CodeBlock(ref info)) => { | |||||
if !context.config.highlight_code { | |||||
return Event::Html(Borrowed("<pre><code>")); | |||||
// Business as usual | |||||
Event::Text(text) | |||||
} | } | ||||
Event::Start(Tag::CodeBlock(ref info)) => { | |||||
if !context.config.highlight_code { | |||||
return Event::Html(Borrowed("<pre><code>")); | |||||
} | |||||
let theme = &THEME_SET.themes[&context.config.highlight_theme]; | |||||
highlighter = Some(get_highlighter(info, &context.config)); | |||||
// This selects the background color the same way that start_coloured_html_snippet does | |||||
let color = | |||||
theme.settings.background.unwrap_or(::syntect::highlighting::Color::WHITE); | |||||
background = IncludeBackground::IfDifferent(color); | |||||
let snippet = start_highlighted_html_snippet(theme); | |||||
Event::Html(Owned(snippet.0)) | |||||
} | |||||
Event::End(Tag::CodeBlock(_)) => { | |||||
if !context.config.highlight_code { | |||||
return Event::Html(Borrowed("</code></pre>\n")); | |||||
let theme = &THEME_SET.themes[&context.config.highlight_theme]; | |||||
highlighter = Some(get_highlighter(info, &context.config)); | |||||
// This selects the background color the same way that start_coloured_html_snippet does | |||||
let color = theme | |||||
.settings | |||||
.background | |||||
.unwrap_or(::syntect::highlighting::Color::WHITE); | |||||
background = IncludeBackground::IfDifferent(color); | |||||
let snippet = start_highlighted_html_snippet(theme); | |||||
Event::Html(Owned(snippet.0)) | |||||
} | } | ||||
// reset highlight and close the code block | |||||
highlighter = None; | |||||
Event::Html(Borrowed("</pre>")) | |||||
} | |||||
Event::Start(Tag::Image(src, title)) => { | |||||
if is_colocated_asset_link(&src) { | |||||
return Event::Start(Tag::Image( | |||||
Owned(format!("{}{}", context.current_page_permalink, src)), | |||||
title, | |||||
)); | |||||
Event::End(Tag::CodeBlock(_)) => { | |||||
if !context.config.highlight_code { | |||||
return Event::Html(Borrowed("</code></pre>\n")); | |||||
} | |||||
// reset highlight and close the code block | |||||
highlighter = None; | |||||
Event::Html(Borrowed("</pre>")) | |||||
} | } | ||||
Event::Start(Tag::Image(src, title)) | |||||
} | |||||
Event::Start(Tag::Link(link, title)) => { | |||||
let fixed_link = match fix_link(&link, context) { | |||||
Ok(fixed_link) => fixed_link, | |||||
Err(err) => { | |||||
error = Some(err); | |||||
return Event::Html(Borrowed("")) | |||||
Event::Start(Tag::Image(src, title)) => { | |||||
if is_colocated_asset_link(&src) { | |||||
return Event::Start(Tag::Image( | |||||
Owned(format!("{}{}", context.current_page_permalink, src)), | |||||
title, | |||||
)); | |||||
} | } | ||||
}; | |||||
Event::Start(Tag::Link(Owned(fixed_link), title)) | |||||
} | |||||
Event::Html(ref markup) if markup.contains("<!-- more -->") => { | |||||
has_summary = true; | |||||
Event::Html(Borrowed(CONTINUE_READING)) | |||||
Event::Start(Tag::Image(src, title)) | |||||
} | |||||
Event::Start(Tag::Link(link, title)) => { | |||||
let fixed_link = match fix_link(&link, context) { | |||||
Ok(fixed_link) => fixed_link, | |||||
Err(err) => { | |||||
error = Some(err); | |||||
return Event::Html(Borrowed("")); | |||||
} | |||||
}; | |||||
Event::Start(Tag::Link(Owned(fixed_link), title)) | |||||
} | |||||
Event::Html(ref markup) if markup.contains("<!-- more -->") => { | |||||
has_summary = true; | |||||
Event::Html(Borrowed(CONTINUE_READING)) | |||||
} | |||||
_ => event, | |||||
} | } | ||||
_ => event, | |||||
} | |||||
}).collect::<Vec<_>>(); // We need to collect the events to make a second pass | |||||
}) | |||||
.collect::<Vec<_>>(); // We need to collect the events to make a second pass | |||||
let header_refs = get_header_refs(&events); | let header_refs = get_header_refs(&events); | ||||
@@ -228,7 +233,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
for header_ref in header_refs { | for header_ref in header_refs { | ||||
let start_idx = header_ref.start_idx; | let start_idx = header_ref.start_idx; | ||||
let end_idx = header_ref.end_idx; | let end_idx = header_ref.end_idx; | ||||
let title = get_text(&events[start_idx + 1 .. end_idx]); | |||||
let title = get_text(&events[start_idx + 1..end_idx]); | |||||
let id = find_anchor(&inserted_anchors, slugify(&title), 0); | let id = find_anchor(&inserted_anchors, slugify(&title), 0); | ||||
inserted_anchors.push(id.clone()); | inserted_anchors.push(id.clone()); | ||||
@@ -246,8 +251,13 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render | |||||
let mut c = tera::Context::new(); | let mut c = tera::Context::new(); | ||||
c.insert("id", &id); | c.insert("id", &id); | ||||
let anchor_link = utils::templates::render_template(&ANCHOR_LINK_TEMPLATE, context.tera, c, &None) | |||||
.map_err(|e| Error::chain("Failed to render anchor link template", e))?; | |||||
let anchor_link = utils::templates::render_template( | |||||
&ANCHOR_LINK_TEMPLATE, | |||||
context.tera, | |||||
c, | |||||
&None, | |||||
) | |||||
.map_err(|e| Error::chain("Failed to render anchor link template", e))?; | |||||
anchors_to_insert.push((anchor_idx, Event::Html(Owned(anchor_link)))); | anchors_to_insert.push((anchor_idx, Event::Html(Owned(anchor_link)))); | ||||
} | } | ||||
@@ -114,9 +114,8 @@ fn render_shortcode( | |||||
let template_name = format!("shortcodes/{}.html", name); | let template_name = format!("shortcodes/{}.html", name); | ||||
let res = | |||||
utils::templates::render_template(&template_name, &context.tera, tera_context, &None) | |||||
.map_err(|e| Error::chain(format!("Failed to render {} shortcode", name), e))?; | |||||
let res = utils::templates::render_template(&template_name, &context.tera, tera_context, &None) | |||||
.map_err(|e| Error::chain(format!("Failed to render {} shortcode", name), e))?; | |||||
// Small hack to avoid having multiple blank lines because of Tera tags for example | // Small hack to avoid having multiple blank lines because of Tera tags for example | ||||
// A blank like will cause the markdown parser to think we're out of HTML and start looking | // A blank like will cause the markdown parser to think we're out of HTML and start looking | ||||
@@ -30,7 +30,7 @@ use sass_rs::{compile_file, Options as SassOptions, OutputStyle}; | |||||
use tera::{Context, Tera}; | use tera::{Context, Tera}; | ||||
use config::{get_config, Config}; | use config::{get_config, Config}; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
use front_matter::InsertAnchor; | use front_matter::InsertAnchor; | ||||
use library::{ | use library::{ | ||||
find_taxonomies, sort_actual_pages_by_date, Library, Page, Paginator, Section, Taxonomy, | find_taxonomies, sort_actual_pages_by_date, Library, Page, Paginator, Section, Taxonomy, | ||||
@@ -87,7 +87,8 @@ impl Site { | |||||
format!("{}/{}", path.to_string_lossy().replace("\\", "/"), "templates/**/*.*ml"); | format!("{}/{}", path.to_string_lossy().replace("\\", "/"), "templates/**/*.*ml"); | ||||
// Only parsing as we might be extending templates from themes and that would error | // Only parsing as we might be extending templates from themes and that would error | ||||
// as we haven't loaded them yet | // as we haven't loaded them yet | ||||
let mut tera = Tera::parse(&tpl_glob).map_err(|e| Error::chain("Error parsing templates", e))?; | |||||
let mut tera = | |||||
Tera::parse(&tpl_glob).map_err(|e| Error::chain("Error parsing templates", e))?; | |||||
if let Some(theme) = config.theme.clone() { | if let Some(theme) = config.theme.clone() { | ||||
// Grab data from the extra section of the theme | // Grab data from the extra section of the theme | ||||
config.merge_with_theme(&path.join("themes").join(&theme).join("theme.toml"))?; | config.merge_with_theme(&path.join("themes").join(&theme).join("theme.toml"))?; | ||||
@@ -103,8 +104,8 @@ impl Site { | |||||
path.to_string_lossy().replace("\\", "/"), | path.to_string_lossy().replace("\\", "/"), | ||||
format!("themes/{}/templates/**/*.*ml", theme) | format!("themes/{}/templates/**/*.*ml", theme) | ||||
); | ); | ||||
let mut tera_theme = | |||||
Tera::parse(&theme_tpl_glob).map_err(|e| Error::chain("Error parsing templates from themes", e))?; | |||||
let mut tera_theme = Tera::parse(&theme_tpl_glob) | |||||
.map_err(|e| Error::chain("Error parsing templates from themes", e))?; | |||||
rewrite_theme_paths(&mut tera_theme, &theme); | rewrite_theme_paths(&mut tera_theme, &theme); | ||||
// TODO: we do that twice, make it dry? | // TODO: we do that twice, make it dry? | ||||
if theme_path.join("templates").join("robots.txt").exists() { | if theme_path.join("templates").join("robots.txt").exists() { | ||||
@@ -196,8 +197,11 @@ impl Site { | |||||
entry.as_path().file_name().unwrap().to_str().unwrap().starts_with("_index.") | entry.as_path().file_name().unwrap().to_str().unwrap().starts_with("_index.") | ||||
}); | }); | ||||
self.library = | |||||
Arc::new(RwLock::new(Library::new(page_entries.len(), section_entries.len(), self.config.is_multilingual()))); | |||||
self.library = Arc::new(RwLock::new(Library::new( | |||||
page_entries.len(), | |||||
section_entries.len(), | |||||
self.config.is_multilingual(), | |||||
))); | |||||
let sections = { | let sections = { | ||||
let config = &self.config; | let config = &self.config; | ||||
@@ -349,8 +353,14 @@ impl Site { | |||||
} | } | ||||
pub fn register_tera_global_fns(&mut self) { | pub fn register_tera_global_fns(&mut self) { | ||||
self.tera.register_function("get_page", global_fns::GetPage::new(self.base_path.clone(), self.library.clone())); | |||||
self.tera.register_function("get_section", global_fns::GetSection::new(self.base_path.clone(), self.library.clone())); | |||||
self.tera.register_function( | |||||
"get_page", | |||||
global_fns::GetPage::new(self.base_path.clone(), self.library.clone()), | |||||
); | |||||
self.tera.register_function( | |||||
"get_section", | |||||
global_fns::GetSection::new(self.base_path.clone(), self.library.clone()), | |||||
); | |||||
self.tera.register_function( | self.tera.register_function( | ||||
"get_taxonomy", | "get_taxonomy", | ||||
global_fns::GetTaxonomy::new(self.taxonomies.clone(), self.library.clone()), | global_fns::GetTaxonomy::new(self.taxonomies.clone(), self.library.clone()), | ||||
@@ -475,7 +485,8 @@ impl Site { | |||||
pub fn clean(&self) -> Result<()> { | pub fn clean(&self) -> Result<()> { | ||||
if self.output_path.exists() { | if self.output_path.exists() { | ||||
// Delete current `public` directory so we can start fresh | // Delete current `public` directory so we can start fresh | ||||
remove_dir_all(&self.output_path).map_err(|e| Error::chain("Couldn't delete output directory", e))?; | |||||
remove_dir_all(&self.output_path) | |||||
.map_err(|e| Error::chain("Couldn't delete output directory", e))?; | |||||
} | } | ||||
Ok(()) | Ok(()) | ||||
@@ -544,12 +555,8 @@ impl Site { | |||||
if !lang.rss { | if !lang.rss { | ||||
continue; | continue; | ||||
} | } | ||||
let pages = library | |||||
.pages_values() | |||||
.iter() | |||||
.filter(|p| p.lang == lang.code) | |||||
.map(|p| *p) | |||||
.collect(); | |||||
let pages = | |||||
library.pages_values().iter().filter(|p| p.lang == lang.code).map(|p| *p).collect(); | |||||
self.render_rss_feed(pages, Some(&PathBuf::from(lang.code.clone())))?; | self.render_rss_feed(pages, Some(&PathBuf::from(lang.code.clone())))?; | ||||
} | } | ||||
@@ -735,7 +742,8 @@ impl Site { | |||||
} else { | } else { | ||||
self.output_path.join(&taxonomy.kind.name) | self.output_path.join(&taxonomy.kind.name) | ||||
}; | }; | ||||
let list_output = taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?; | |||||
let list_output = | |||||
taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?; | |||||
create_directory(&output_path)?; | create_directory(&output_path)?; | ||||
create_file(&output_path.join("index.html"), &self.inject_livereload(list_output))?; | create_file(&output_path.join("index.html"), &self.inject_livereload(list_output))?; | ||||
let library = self.library.read().unwrap(); | let library = self.library.read().unwrap(); | ||||
@@ -794,14 +802,20 @@ impl Site { | |||||
let mut sections = self | let mut sections = self | ||||
.library | .library | ||||
.read().unwrap() | |||||
.read() | |||||
.unwrap() | |||||
.sections_values() | .sections_values() | ||||
.iter() | .iter() | ||||
.filter(|s| s.meta.render) | .filter(|s| s.meta.render) | ||||
.map(|s| SitemapEntry::new(s.permalink.clone(), None)) | .map(|s| SitemapEntry::new(s.permalink.clone(), None)) | ||||
.collect::<Vec<_>>(); | .collect::<Vec<_>>(); | ||||
for section in | |||||
self.library.read().unwrap().sections_values().iter().filter(|s| s.meta.paginate_by.is_some()) | |||||
for section in self | |||||
.library | |||||
.read() | |||||
.unwrap() | |||||
.sections_values() | |||||
.iter() | |||||
.filter(|s| s.meta.paginate_by.is_some()) | |||||
{ | { | ||||
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) | ||||
@@ -971,9 +985,13 @@ impl Site { | |||||
} | } | ||||
if section.meta.is_paginated() { | if section.meta.is_paginated() { | ||||
self.render_paginated(&output_path, &Paginator::from_section(§ion, &self.library.read().unwrap()))?; | |||||
self.render_paginated( | |||||
&output_path, | |||||
&Paginator::from_section(§ion, &self.library.read().unwrap()), | |||||
)?; | |||||
} else { | } else { | ||||
let output = section.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; | |||||
let output = | |||||
section.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; | |||||
create_file(&output_path.join("index.html"), &self.inject_livereload(output))?; | create_file(&output_path.join("index.html"), &self.inject_livereload(output))?; | ||||
} | } | ||||
@@ -985,7 +1003,8 @@ impl Site { | |||||
self.render_section( | self.render_section( | ||||
&self | &self | ||||
.library | .library | ||||
.read().unwrap() | |||||
.read() | |||||
.unwrap() | |||||
.get_section(&self.content_path.join("_index.md")) | .get_section(&self.content_path.join("_index.md")) | ||||
.expect("Failed to get index section"), | .expect("Failed to get index section"), | ||||
false, | false, | ||||
@@ -995,7 +1014,8 @@ impl Site { | |||||
/// Renders all sections | /// Renders all sections | ||||
pub fn render_sections(&self) -> Result<()> { | pub fn render_sections(&self) -> Result<()> { | ||||
self.library | self.library | ||||
.read().unwrap() | |||||
.read() | |||||
.unwrap() | |||||
.sections_values() | .sections_values() | ||||
.into_par_iter() | .into_par_iter() | ||||
.map(|s| self.render_section(s, true)) | .map(|s| self.render_section(s, true)) | ||||
@@ -1026,8 +1046,12 @@ impl Site { | |||||
.map(|pager| { | .map(|pager| { | ||||
let page_path = folder_path.join(&format!("{}", pager.index)); | let page_path = folder_path.join(&format!("{}", pager.index)); | ||||
create_directory(&page_path)?; | create_directory(&page_path)?; | ||||
let output = | |||||
paginator.render_pager(pager, &self.config, &self.tera, &self.library.read().unwrap())?; | |||||
let output = paginator.render_pager( | |||||
pager, | |||||
&self.config, | |||||
&self.tera, | |||||
&self.library.read().unwrap(), | |||||
)?; | |||||
if pager.index > 1 { | if pager.index > 1 { | ||||
create_file(&page_path.join("index.html"), &self.inject_livereload(output))?; | create_file(&page_path.join("index.html"), &self.inject_livereload(output))?; | ||||
} else { | } else { | ||||
@@ -631,9 +631,8 @@ fn can_apply_page_templates() { | |||||
assert_eq!(changed_recursively.meta.title, Some("Changed recursively".into())); | assert_eq!(changed_recursively.meta.title, Some("Changed recursively".into())); | ||||
// But it should not have override a children page_template | // But it should not have override a children page_template | ||||
let yet_another_section = library | |||||
.get_section(&template_path.join("yet_another_section").join("_index.md")) | |||||
.unwrap(); | |||||
let yet_another_section = | |||||
library.get_section(&template_path.join("yet_another_section").join("_index.md")).unwrap(); | |||||
assert_eq!(yet_another_section.subsections.len(), 0); | assert_eq!(yet_another_section.subsections.len(), 0); | ||||
assert_eq!(yet_another_section.pages.len(), 1); | assert_eq!(yet_another_section.pages.len(), 1); | ||||
@@ -23,8 +23,7 @@ fn can_parse_multilingual_site() { | |||||
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 = | |||||
library.get_section(&path.join("content").join("_index.fr.md")).unwrap(); | |||||
let fr_index_section = 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()); | ||||
@@ -139,5 +138,4 @@ fn can_build_multilingual_site() { | |||||
assert!(!file_contains!(public, "fr/auteurs/index.html", "Queen")); | assert!(!file_contains!(public, "fr/auteurs/index.html", "Queen")); | ||||
assert!(file_contains!(public, "fr/auteurs/index.html", "Vincent")); | assert!(file_contains!(public, "fr/auteurs/index.html", "Vincent")); | ||||
assert!(!file_exists!(public, "fr/auteurs/vincent-prouillet/rss.xml")); | assert!(!file_exists!(public, "fr/auteurs/vincent-prouillet/rss.xml")); | ||||
} | } |
@@ -183,7 +183,7 @@ impl LoadData { | |||||
pub fn new(content_path: PathBuf, base_path: PathBuf) -> Self { | pub fn new(content_path: PathBuf, base_path: PathBuf) -> Self { | ||||
let client = Arc::new(Mutex::new(Client::builder().build().expect("reqwest client build"))); | let client = Arc::new(Mutex::new(Client::builder().build().expect("reqwest client build"))); | ||||
let result_cache = Arc::new(Mutex::new(HashMap::new())); | let result_cache = Arc::new(Mutex::new(HashMap::new())); | ||||
Self {content_path, base_path, client, result_cache} | |||||
Self { content_path, base_path, client, result_cache } | |||||
} | } | ||||
} | } | ||||
@@ -310,7 +310,7 @@ fn load_csv(csv_data: String) -> Result<Value> { | |||||
#[cfg(test)] | #[cfg(test)] | ||||
mod tests { | mod tests { | ||||
use super::{LoadData, DataSource, OutputFormat}; | |||||
use super::{DataSource, LoadData, OutputFormat}; | |||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::path::PathBuf; | use std::path::PathBuf; | ||||
@@ -15,7 +15,7 @@ mod macros; | |||||
mod load_data; | mod load_data; | ||||
pub use self::load_data::LoadData; | |||||
pub use self::load_data::LoadData; | |||||
#[derive(Debug)] | #[derive(Debug)] | ||||
pub struct Trans { | pub struct Trans { | ||||
@@ -23,7 +23,7 @@ pub struct Trans { | |||||
} | } | ||||
impl Trans { | impl Trans { | ||||
pub fn new(config: Config) -> Self { | pub fn new(config: Config) -> Self { | ||||
Self {config} | |||||
Self { config } | |||||
} | } | ||||
} | } | ||||
impl TeraFn for Trans { | impl TeraFn for Trans { | ||||
@@ -43,7 +43,7 @@ pub struct GetUrl { | |||||
} | } | ||||
impl GetUrl { | impl GetUrl { | ||||
pub fn new(config: Config, permalinks: HashMap<String, String>) -> Self { | pub fn new(config: Config, permalinks: HashMap<String, String>) -> Self { | ||||
Self {config, permalinks} | |||||
Self { config, permalinks } | |||||
} | } | ||||
} | } | ||||
impl TeraFn for GetUrl { | impl TeraFn for GetUrl { | ||||
@@ -88,7 +88,7 @@ pub struct ResizeImage { | |||||
} | } | ||||
impl ResizeImage { | impl ResizeImage { | ||||
pub fn new(imageproc: Arc<Mutex<imageproc::Processor>>) -> Self { | pub fn new(imageproc: Arc<Mutex<imageproc::Processor>>) -> Self { | ||||
Self {imageproc} | |||||
Self { imageproc } | |||||
} | } | ||||
} | } | ||||
@@ -154,7 +154,7 @@ impl GetTaxonomyUrl { | |||||
} | } | ||||
taxonomies.insert(taxonomy.kind.name.clone(), items); | taxonomies.insert(taxonomy.kind.name.clone(), items); | ||||
} | } | ||||
Self {taxonomies} | |||||
Self { taxonomies } | |||||
} | } | ||||
} | } | ||||
impl TeraFn for GetTaxonomyUrl { | impl TeraFn for GetTaxonomyUrl { | ||||
@@ -188,7 +188,6 @@ impl TeraFn for GetTaxonomyUrl { | |||||
} | } | ||||
} | } | ||||
#[derive(Debug)] | #[derive(Debug)] | ||||
pub struct GetPage { | pub struct GetPage { | ||||
base_path: PathBuf, | base_path: PathBuf, | ||||
@@ -196,7 +195,7 @@ pub struct GetPage { | |||||
} | } | ||||
impl GetPage { | impl GetPage { | ||||
pub fn new(base_path: PathBuf, library: Arc<RwLock<Library>>) -> Self { | pub fn new(base_path: PathBuf, library: Arc<RwLock<Library>>) -> Self { | ||||
Self {base_path: base_path.join("content"), library} | |||||
Self { base_path: base_path.join("content"), library } | |||||
} | } | ||||
} | } | ||||
impl TeraFn for GetPage { | impl TeraFn for GetPage { | ||||
@@ -209,9 +208,7 @@ impl TeraFn for GetPage { | |||||
let full_path = self.base_path.join(&path); | let full_path = self.base_path.join(&path); | ||||
let library = self.library.read().unwrap(); | let library = self.library.read().unwrap(); | ||||
match library.get_page(&full_path) { | match library.get_page(&full_path) { | ||||
Some(p) => { | |||||
Ok(to_value(p.to_serialized(&library)).unwrap()) | |||||
}, | |||||
Some(p) => Ok(to_value(p.to_serialized(&library)).unwrap()), | |||||
None => Err(format!("Page `{}` not found.", path).into()), | None => Err(format!("Page `{}` not found.", path).into()), | ||||
} | } | ||||
} | } | ||||
@@ -224,7 +221,7 @@ pub struct GetSection { | |||||
} | } | ||||
impl GetSection { | impl GetSection { | ||||
pub fn new(base_path: PathBuf, library: Arc<RwLock<Library>>) -> Self { | pub fn new(base_path: PathBuf, library: Arc<RwLock<Library>>) -> Self { | ||||
Self {base_path: base_path.join("content"), library} | |||||
Self { base_path: base_path.join("content"), library } | |||||
} | } | ||||
} | } | ||||
impl TeraFn for GetSection { | impl TeraFn for GetSection { | ||||
@@ -249,13 +246,12 @@ impl TeraFn for GetSection { | |||||
} else { | } else { | ||||
Ok(to_value(s.to_serialized(&library)).unwrap()) | Ok(to_value(s.to_serialized(&library)).unwrap()) | ||||
} | } | ||||
}, | |||||
} | |||||
None => Err(format!("Section `{}` not found.", path).into()), | None => Err(format!("Section `{}` not found.", path).into()), | ||||
} | } | ||||
} | } | ||||
} | } | ||||
#[derive(Debug)] | #[derive(Debug)] | ||||
pub struct GetTaxonomy { | pub struct GetTaxonomy { | ||||
library: Arc<RwLock<Library>>, | library: Arc<RwLock<Library>>, | ||||
@@ -267,7 +263,7 @@ impl GetTaxonomy { | |||||
for taxo in all_taxonomies { | for taxo in all_taxonomies { | ||||
taxonomies.insert(taxo.kind.name.clone(), taxo); | taxonomies.insert(taxo.kind.name.clone(), taxo); | ||||
} | } | ||||
Self {taxonomies, library} | |||||
Self { taxonomies, library } | |||||
} | } | ||||
} | } | ||||
impl TeraFn for GetTaxonomy { | impl TeraFn for GetTaxonomy { | ||||
@@ -278,16 +274,10 @@ impl TeraFn for GetTaxonomy { | |||||
"`get_taxonomy` requires a `kind` argument with a string value" | "`get_taxonomy` requires a `kind` argument with a string value" | ||||
); | ); | ||||
match self.taxonomies.get(&kind) { | |||||
Some(t) => { | |||||
Ok(to_value(t.to_serialized(&self.library.read().unwrap())).unwrap()) | |||||
}, | |||||
match self.taxonomies.get(&kind) { | |||||
Some(t) => Ok(to_value(t.to_serialized(&self.library.read().unwrap())).unwrap()), | |||||
None => { | None => { | ||||
Err(format!( | |||||
"`get_taxonomy` received an unknown taxonomy as kind: {}", | |||||
kind | |||||
) | |||||
.into()) | |||||
Err(format!("`get_taxonomy` received an unknown taxonomy as kind: {}", kind).into()) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -298,9 +288,9 @@ mod tests { | |||||
use super::{GetTaxonomy, GetTaxonomyUrl, GetUrl, Trans}; | use super::{GetTaxonomy, GetTaxonomyUrl, GetUrl, Trans}; | ||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::sync::{RwLock, Arc}; | |||||
use std::sync::{Arc, RwLock}; | |||||
use tera::{to_value, Value, Function}; | |||||
use tera::{to_value, Function, Value}; | |||||
use config::{Config, Taxonomy as TaxonomyConfig}; | use config::{Config, Taxonomy as TaxonomyConfig}; | ||||
use library::{Library, Taxonomy, TaxonomyItem}; | use library::{Library, Taxonomy, TaxonomyItem}; | ||||
@@ -348,9 +338,19 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_get_taxonomy() { | fn can_get_taxonomy() { | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let taxo_config = TaxonomyConfig { name: "tags".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }; | |||||
let taxo_config = TaxonomyConfig { | |||||
name: "tags".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}; | |||||
let library = Arc::new(RwLock::new(Library::new(0, 0, false))); | let library = Arc::new(RwLock::new(Library::new(0, 0, false))); | ||||
let tag = TaxonomyItem::new("Programming", &taxo_config, &config, vec![], &library.read().unwrap()); | |||||
let tag = TaxonomyItem::new( | |||||
"Programming", | |||||
&taxo_config, | |||||
&config, | |||||
vec![], | |||||
&library.read().unwrap(), | |||||
); | |||||
let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; | let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; | ||||
let taxonomies = vec![tags.clone()]; | let taxonomies = vec![tags.clone()]; | ||||
@@ -388,7 +388,11 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn can_get_taxonomy_url() { | fn can_get_taxonomy_url() { | ||||
let config = Config::default(); | let config = Config::default(); | ||||
let taxo_config = TaxonomyConfig { name: "tags".to_string(), lang: config.default_language.clone(), ..TaxonomyConfig::default() }; | |||||
let taxo_config = TaxonomyConfig { | |||||
name: "tags".to_string(), | |||||
lang: config.default_language.clone(), | |||||
..TaxonomyConfig::default() | |||||
}; | |||||
let library = Library::new(0, 0, false); | let library = Library::new(0, 0, false); | ||||
let tag = TaxonomyItem::new("Programming", &taxo_config, &config, vec![], &library); | let tag = TaxonomyItem::new("Programming", &taxo_config, &config, vec![], &library); | ||||
let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; | let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; | ||||
@@ -25,7 +25,7 @@ pub mod global_fns; | |||||
use tera::{Context, Tera}; | use tera::{Context, Tera}; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
lazy_static! { | lazy_static! { | ||||
pub static ref ZOLA_TERA: Tera = { | pub static ref ZOLA_TERA: Tera = { | ||||
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; | |||||
use std::time::SystemTime; | use std::time::SystemTime; | ||||
use walkdir::WalkDir; | use walkdir::WalkDir; | ||||
use errors::{Result, Error}; | |||||
use errors::{Error, Result}; | |||||
pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result<bool> { | pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result<bool> { | ||||
let canonical_path = path | let canonical_path = path | ||||
@@ -19,8 +19,8 @@ pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result<bool> { | |||||
/// Create a file with the content given | /// Create a file with the content given | ||||
pub fn create_file(path: &Path, content: &str) -> Result<()> { | pub fn create_file(path: &Path, content: &str) -> Result<()> { | ||||
let mut file = File::create(&path) | |||||
.map_err(|e| Error::chain(format!("Failed to create {:?}", path), e))?; | |||||
let mut file = | |||||
File::create(&path).map_err(|e| Error::chain(format!("Failed to create {:?}", path), e))?; | |||||
file.write_all(content.as_bytes())?; | file.write_all(content.as_bytes())?; | ||||
Ok(()) | Ok(()) | ||||
} | } | ||||
@@ -37,8 +37,9 @@ pub fn ensure_directory_exists(path: &Path) -> Result<()> { | |||||
/// exists before creating it | /// exists before creating it | ||||
pub fn create_directory(path: &Path) -> Result<()> { | pub fn create_directory(path: &Path) -> Result<()> { | ||||
if !path.exists() { | if !path.exists() { | ||||
create_dir_all(path) | |||||
.map_err(|e| Error::chain(format!("Was not able to create folder {}", path.display()), e))?; | |||||
create_dir_all(path).map_err(|e| { | |||||
Error::chain(format!("Was not able to create folder {}", path.display()), e) | |||||
})?; | |||||
} | } | ||||
Ok(()) | Ok(()) | ||||
} | } | ||||
@@ -16,7 +16,7 @@ impl<T> InsertMany for Vec<T> { | |||||
for (idx, elem) in elem_to_insert.into_iter() { | for (idx, elem) in elem_to_insert.into_iter() { | ||||
let head_len = idx - last_idx; | let head_len = idx - last_idx; | ||||
inserted.extend(self.splice(0 .. head_len, std::iter::empty())); | |||||
inserted.extend(self.splice(0..head_len, std::iter::empty())); | |||||
inserted.push(elem); | inserted.push(elem); | ||||
last_idx = idx; | last_idx = idx; | ||||
} | } | ||||
@@ -41,4 +41,4 @@ mod test { | |||||
v2.insert_many(vec![(0, 0), (2, -1)]); | v2.insert_many(vec![(0, 0), (2, -1)]); | ||||
assert_eq!(v2, &[0, 1, 2, -1, 3, 4, 5]); | assert_eq!(v2, &[0, 1, 2, -1, 3, 4, 5]); | ||||
} | } | ||||
} | |||||
} |
@@ -36,7 +36,7 @@ use ctrlc; | |||||
use notify::{watcher, RecursiveMode, Watcher}; | use notify::{watcher, RecursiveMode, Watcher}; | ||||
use ws::{Message, Sender, WebSocket}; | use ws::{Message, Sender, WebSocket}; | ||||
use errors::{Result, Error as ZolaError}; | |||||
use errors::{Error as ZolaError, Result}; | |||||
use site::Site; | use site::Site; | ||||
use utils::fs::copy_file; | use utils::fs::copy_file; | ||||
@@ -296,11 +296,7 @@ pub fn serve( | |||||
}; | }; | ||||
console::info(&msg); | console::info(&msg); | ||||
// Force refresh | // Force refresh | ||||
rebuild_done_handling( | |||||
&broadcaster, | |||||
rebuild::after_template_change(site, &path), | |||||
"/x.js", | |||||
); | |||||
rebuild_done_handling(&broadcaster, rebuild::after_template_change(site, &path), "/x.js"); | |||||
}; | }; | ||||
let reload_sass = |site: &Site, path: &Path, partial_path: &Path| { | let reload_sass = |site: &Site, path: &Path, partial_path: &Path| { | ||||
@@ -7,8 +7,8 @@ use atty; | |||||
use chrono::Duration; | use chrono::Duration; | ||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; | use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; | ||||
use site::Site; | |||||
use errors::Error; | use errors::Error; | ||||
use site::Site; | |||||
lazy_static! { | lazy_static! { | ||||
/// Termcolor color choice. | /// Termcolor color choice. | ||||
@@ -64,9 +64,7 @@ pub fn warn_about_ignored_pages(site: &Site) { | |||||
let ignored_pages: Vec<_> = library | let ignored_pages: Vec<_> = library | ||||
.sections_values() | .sections_values() | ||||
.iter() | .iter() | ||||
.flat_map(|s| { | |||||
s.ignored_pages.iter().map(|k| library.get_page_by_key(*k).file.path.clone()) | |||||
}) | |||||
.flat_map(|s| s.ignored_pages.iter().map(|k| library.get_page_by_key(*k).file.path.clone())) | |||||
.collect(); | .collect(); | ||||
if !ignored_pages.is_empty() { | if !ignored_pages.is_empty() { | ||||