@@ -342,6 +342,7 @@ dependencies = [ | |||
"serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"syntect 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"utils 0.1.0", | |||
] | |||
[[package]] | |||
@@ -616,7 +617,6 @@ dependencies = [ | |||
name = "errors" | |||
version = "0.1.0" | |||
dependencies = [ | |||
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"syntect 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -2229,7 +2229,6 @@ dependencies = [ | |||
"base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"config 0.1.0", | |||
"csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"errors 0.1.0", | |||
"imageproc 0.1.0", | |||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||
@@ -13,3 +13,4 @@ lazy_static = "1" | |||
syntect = "3" | |||
errors = { path = "../errors" } | |||
utils = { path = "../utils" } |
@@ -1,6 +1,4 @@ | |||
use std::collections::HashMap; | |||
use std::fs::File; | |||
use std::io::prelude::*; | |||
use std::path::{Path, PathBuf}; | |||
use chrono::Utc; | |||
@@ -9,9 +7,10 @@ use syntect::parsing::{SyntaxSet, SyntaxSetBuilder}; | |||
use toml; | |||
use toml::Value as Toml; | |||
use errors::{Result, ResultExt}; | |||
use errors::Result; | |||
use highlighting::THEME_SET; | |||
use theme::Theme; | |||
use utils::fs::read_file_with_error; | |||
// We want a default base url for tests | |||
static DEFAULT_BASE_URL: &'static str = "http://a-website.com"; | |||
@@ -66,7 +65,13 @@ impl Taxonomy { | |||
impl Default for Taxonomy { | |||
fn default() -> Taxonomy { | |||
Taxonomy { name: String::new(), paginate_by: None, paginate_path: None, rss: false, lang: None } | |||
Taxonomy { | |||
name: String::new(), | |||
paginate_by: None, | |||
paginate_path: None, | |||
rss: false, | |||
lang: None, | |||
} | |||
} | |||
} | |||
@@ -172,15 +177,12 @@ impl Config { | |||
/// Parses a config file from the given path | |||
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> { | |||
let mut content = String::new(); | |||
let path = path.as_ref(); | |||
let file_name = path.file_name().unwrap(); | |||
File::open(path) | |||
.chain_err(|| { | |||
format!("No `{:?}` file found. Are you in the right directory?", file_name) | |||
})? | |||
.read_to_string(&mut content)?; | |||
let content = read_file_with_error( | |||
path, | |||
&format!("No `{:?}` file found. Are you in the right directory?", file_name), | |||
)?; | |||
Config::parse(&content) | |||
} | |||
@@ -1,14 +1,16 @@ | |||
#[macro_use] | |||
extern crate serde_derive; | |||
extern crate toml; | |||
#[macro_use] | |||
extern crate errors; | |||
extern crate chrono; | |||
extern crate globset; | |||
extern crate toml; | |||
#[macro_use] | |||
extern crate lazy_static; | |||
extern crate syntect; | |||
#[macro_use] | |||
extern crate errors; | |||
extern crate utils; | |||
mod config; | |||
pub mod highlighting; | |||
mod theme; | |||
@@ -1,11 +1,10 @@ | |||
use std::collections::HashMap; | |||
use std::fs::File; | |||
use std::io::prelude::*; | |||
use std::path::PathBuf; | |||
use toml::Value as Toml; | |||
use errors::{Result, ResultExt}; | |||
use errors::Result; | |||
use utils::fs::read_file_with_error; | |||
/// Holds the data from a `theme.toml` file. | |||
/// There are other fields than `extra` in it but Zola | |||
@@ -40,15 +39,12 @@ impl Theme { | |||
/// Parses a theme file from the given path | |||
pub fn from_file(path: &PathBuf) -> Result<Theme> { | |||
let mut content = String::new(); | |||
File::open(path) | |||
.chain_err(|| { | |||
"No `theme.toml` file found. \ | |||
Is the `theme` defined in your `config.toml present in the `themes` directory \ | |||
and does it have a `theme.toml` inside?" | |||
})? | |||
.read_to_string(&mut content)?; | |||
let content = read_file_with_error( | |||
path, | |||
"No `theme.toml` file found. \ | |||
Is the `theme` defined in your `config.toml present in the `themes` directory \ | |||
and does it have a `theme.toml` inside?", | |||
)?; | |||
Theme::parse(&content) | |||
} | |||
} |
@@ -4,7 +4,6 @@ version = "0.1.0" | |||
authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||
[dependencies] | |||
error-chain = "0.12" | |||
tera = "0.11" | |||
toml = "0.4" | |||
image = "0.20" | |||
@@ -1,26 +1,101 @@ | |||
#![allow(unused_doc_comments)] | |||
#[macro_use] | |||
extern crate error_chain; | |||
extern crate image; | |||
extern crate syntect; | |||
extern crate tera; | |||
extern crate toml; | |||
error_chain! { | |||
errors {} | |||
use std::convert::Into; | |||
use std::error::Error as StdError; | |||
use std::fmt; | |||
#[derive(Debug)] | |||
pub enum ErrorKind { | |||
Msg(String), | |||
Tera(tera::Error), | |||
Io(::std::io::Error), | |||
Toml(toml::de::Error), | |||
Image(image::ImageError), | |||
Syntect(syntect::LoadingError), | |||
} | |||
/// The Error type | |||
#[derive(Debug)] | |||
pub struct Error { | |||
/// Kind of error | |||
pub kind: ErrorKind, | |||
pub source: Option<Box<dyn StdError>>, | |||
} | |||
unsafe impl Sync for Error {} | |||
unsafe impl Send for Error {} | |||
impl StdError for Error { | |||
fn source(&self) -> Option<&(dyn StdError + 'static)> { | |||
self.source.as_ref().map(|c| &**c) | |||
} | |||
} | |||
impl fmt::Display for Error { | |||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |||
match self.kind { | |||
ErrorKind::Msg(ref message) => write!(f, "{}", message), | |||
ErrorKind::Tera(ref e) => write!(f, "{}", e), | |||
ErrorKind::Io(ref e) => write!(f, "{}", e), | |||
ErrorKind::Toml(ref e) => write!(f, "{}", e), | |||
ErrorKind::Image(ref e) => write!(f, "{}", e), | |||
ErrorKind::Syntect(ref e) => write!(f, "{}", e), | |||
} | |||
} | |||
} | |||
links { | |||
Tera(tera::Error, tera::ErrorKind); | |||
impl Error { | |||
/// Creates generic error | |||
pub fn msg(value: impl ToString) -> Self { | |||
Self { kind: ErrorKind::Msg(value.to_string()), source: None } | |||
} | |||
foreign_links { | |||
Io(::std::io::Error); | |||
Toml(toml::de::Error); | |||
Image(image::ImageError); | |||
Syntect(syntect::LoadingError); | |||
/// Creates generic error with a cause | |||
pub fn chain(value: impl ToString, source: impl Into<Box<dyn StdError>>) -> Self { | |||
Self { kind: ErrorKind::Msg(value.to_string()), source: Some(source.into()) } | |||
} | |||
} | |||
impl From<&str> for Error { | |||
fn from(e: &str) -> Self { | |||
Self::msg(e) | |||
} | |||
} | |||
impl From<String> for Error { | |||
fn from(e: String) -> Self { | |||
Self::msg(e) | |||
} | |||
} | |||
impl From<toml::de::Error> for Error { | |||
fn from(e: toml::de::Error) -> Self { | |||
Self { kind: ErrorKind::Toml(e), source: None } | |||
} | |||
} | |||
impl From<syntect::LoadingError> for Error { | |||
fn from(e: syntect::LoadingError) -> Self { | |||
Self { kind: ErrorKind::Syntect(e), source: None } | |||
} | |||
} | |||
impl From<tera::Error> for Error { | |||
fn from(e: tera::Error) -> Self { | |||
Self { kind: ErrorKind::Tera(e), source: None } | |||
} | |||
} | |||
impl From<::std::io::Error> for Error { | |||
fn from(e: ::std::io::Error) -> Self { | |||
Self { kind: ErrorKind::Io(e), source: None } | |||
} | |||
} | |||
impl From<image::ImageError> for Error { | |||
fn from(e: image::ImageError) -> Self { | |||
Self { kind: ErrorKind::Image(e), source: None } | |||
} | |||
} | |||
/// Convenient wrapper around std::Result. | |||
pub type Result<T> = ::std::result::Result<T, Error>; | |||
// So we can use bail! in all other crates | |||
#[macro_export] | |||
@@ -12,7 +12,7 @@ extern crate toml; | |||
extern crate errors; | |||
extern crate utils; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use regex::Regex; | |||
use std::path::Path; | |||
@@ -71,8 +71,8 @@ pub fn split_section_content( | |||
content: &str, | |||
) -> Result<(SectionFrontMatter, String)> { | |||
let (front_matter, content) = split_content(file_path, content)?; | |||
let meta = SectionFrontMatter::parse(&front_matter).chain_err(|| { | |||
format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()) | |||
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) | |||
})?; | |||
Ok((meta, content)) | |||
} | |||
@@ -81,8 +81,8 @@ pub fn split_section_content( | |||
/// Returns a parsed `PageFrontMatter` and the rest of the content | |||
pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> { | |||
let (front_matter, content) = split_content(file_path, content)?; | |||
let meta = PageFrontMatter::parse(&front_matter).chain_err(|| { | |||
format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()) | |||
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) | |||
})?; | |||
Ok((meta, content)) | |||
} | |||
@@ -20,7 +20,7 @@ use image::{FilterType, GenericImageView}; | |||
use rayon::prelude::*; | |||
use regex::Regex; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use utils::fs as ufs; | |||
static RESIZED_SUBDIR: &'static str = "processed_images"; | |||
@@ -456,7 +456,7 @@ impl Processor { | |||
let target = | |||
self.resized_path.join(Self::op_filename(*hash, op.collision_id, op.format)); | |||
op.perform(&self.content_path, &target) | |||
.chain_err(|| format!("Failed to process image: {}", op.source)) | |||
.map_err(|e| Error::chain(format!("Failed to process image: {}", op.source), e)) | |||
}) | |||
.collect::<Result<()>>() | |||
} | |||
@@ -8,7 +8,7 @@ use slug::slugify; | |||
use tera::{Context as TeraContext, Tera}; | |||
use config::Config; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use front_matter::{split_page_content, InsertAnchor, PageFrontMatter}; | |||
use library::Library; | |||
use rendering::{render_content, Header, RenderContext}; | |||
@@ -234,7 +234,7 @@ impl Page { | |||
context.tera_context.insert("page", &SerializingPage::from_page_basic(self, None)); | |||
let res = render_content(&self.raw_content, &context) | |||
.chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?; | |||
.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.content = res.body; | |||
@@ -258,7 +258,7 @@ impl Page { | |||
context.insert("lang", &self.lang); | |||
render_template(&tpl_name, tera, &context, &config.theme) | |||
.chain_err(|| format!("Failed to render page '{}'", self.file.path.display())) | |||
.map_err(|e| Error::chain(format!("Failed to render page '{}'", self.file.path.display()), e)) | |||
} | |||
/// Creates a vectors of asset URLs. | |||
@@ -5,7 +5,7 @@ use slotmap::Key; | |||
use tera::{Context as TeraContext, Tera}; | |||
use config::Config; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use front_matter::{split_section_content, SectionFrontMatter}; | |||
use rendering::{render_content, Header, RenderContext}; | |||
use utils::fs::{find_related_assets, read_file}; | |||
@@ -172,7 +172,7 @@ impl Section { | |||
context.tera_context.insert("section", &SerializingSection::from_section_basic(self, None)); | |||
let res = render_content(&self.raw_content, &context) | |||
.chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?; | |||
.map_err(|e| Error::chain(format!("Failed to render content of {}", self.file.path.display()), e))?; | |||
self.content = res.body; | |||
self.toc = res.toc; | |||
Ok(()) | |||
@@ -190,7 +190,7 @@ impl Section { | |||
context.insert("lang", &self.lang); | |||
render_template(tpl_name, tera, &context, &config.theme) | |||
.chain_err(|| format!("Failed to render section '{}'", self.file.path.display())) | |||
.map_err(|e| Error::chain(format!("Failed to render section '{}'", self.file.path.display()), e)) | |||
} | |||
/// Is this the index section? | |||
@@ -4,7 +4,7 @@ use slotmap::Key; | |||
use tera::{to_value, Context, Tera, Value}; | |||
use config::Config; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use utils::templates::render_template; | |||
use content::{Section, SerializingPage, SerializingSection}; | |||
@@ -222,7 +222,7 @@ impl<'a> Paginator<'a> { | |||
context.insert("paginator", &self.build_paginator_context(pager)); | |||
render_template(&self.template, tera, &context, &config.theme) | |||
.chain_err(|| format!("Failed to render pager {}", pager.index)) | |||
.map_err(|e| Error::chain(format!("Failed to render pager {}", pager.index), e)) | |||
} | |||
} | |||
@@ -5,7 +5,7 @@ use slug::slugify; | |||
use tera::{Context, Tera}; | |||
use config::{Config, Taxonomy as TaxonomyConfig}; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use utils::templates::render_template; | |||
use content::SerializingPage; | |||
@@ -145,7 +145,7 @@ impl Taxonomy { | |||
context.insert("current_path", &format!("/{}/{}", self.kind.name, item.slug)); | |||
render_template(&format!("{}/single.html", self.kind.name), tera, &context, &config.theme) | |||
.chain_err(|| format!("Failed to render single term {} page.", self.kind.name)) | |||
.map_err(|e| Error::chain(format!("Failed to render single term {} page.", self.kind.name), e)) | |||
} | |||
pub fn render_all_terms( | |||
@@ -164,7 +164,7 @@ impl Taxonomy { | |||
context.insert("current_path", &self.kind.name); | |||
render_template(&format!("{}/list.html", self.kind.name), tera, &context, &config.theme) | |||
.chain_err(|| format!("Failed to render a list of {} page.", self.kind.name)) | |||
.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> { | |||
@@ -334,7 +334,7 @@ mod tests { | |||
let err = taxonomies.unwrap_err(); | |||
// no path as this is created by Default | |||
assert_eq!( | |||
err.description(), | |||
format!("{}", err), | |||
"Page `` has taxonomy `tags` which is not defined in config.toml" | |||
); | |||
} | |||
@@ -442,7 +442,7 @@ mod tests { | |||
let err = taxonomies.unwrap_err(); | |||
// no path as this is created by Default | |||
assert_eq!( | |||
err.description(), | |||
format!("{}", err), | |||
"Page `` has taxonomy `tags` which is not available in that language" | |||
); | |||
} | |||
@@ -4,7 +4,7 @@ use regex::Regex; | |||
use tera::{to_value, Context, Map, Value}; | |||
use context::RenderContext; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
// This include forces recompiling this source file if the grammar file changes. | |||
// Uncomment it when doing changes to the .pest file | |||
@@ -116,7 +116,7 @@ fn render_shortcode( | |||
let res = context | |||
.tera | |||
.render(&tpl_name, &tera_context) | |||
.chain_err(|| format!("Failed to render {} shortcode", name))?; | |||
.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 | |||
// A blank like will cause the markdown parser to think we're out of HTML and start looking | |||
@@ -660,7 +660,7 @@ fn can_show_error_message_for_invalid_external_links() { | |||
let res = render_content("[a link](http://google.comy)", &context); | |||
assert!(res.is_err()); | |||
let err = res.unwrap_err(); | |||
assert!(err.description().contains("Link http://google.comy is not valid")); | |||
assert!(format!("{}", err).contains("Link http://google.comy is not valid")); | |||
} | |||
#[test] | |||
@@ -30,7 +30,7 @@ use sass_rs::{compile_file, Options as SassOptions, OutputStyle}; | |||
use tera::{Context, Tera}; | |||
use config::{get_config, Config}; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
use front_matter::InsertAnchor; | |||
use library::{ | |||
find_taxonomies, sort_actual_pages_by_date, Library, Page, Paginator, Section, Taxonomy, | |||
@@ -87,7 +87,7 @@ impl Site { | |||
format!("{}/{}", path.to_string_lossy().replace("\\", "/"), "templates/**/*.*ml"); | |||
// Only parsing as we might be extending templates from themes and that would error | |||
// as we haven't loaded them yet | |||
let mut tera = Tera::parse(&tpl_glob).chain_err(|| "Error parsing templates")?; | |||
let mut tera = Tera::parse(&tpl_glob).map_err(|e| Error::chain("Error parsing templates", e))?; | |||
if let Some(theme) = config.theme.clone() { | |||
// Grab data from the extra section of the theme | |||
config.merge_with_theme(&path.join("themes").join(&theme).join("theme.toml"))?; | |||
@@ -104,9 +104,9 @@ impl Site { | |||
format!("themes/{}/templates/**/*.*ml", theme) | |||
); | |||
let mut tera_theme = | |||
Tera::parse(&theme_tpl_glob).chain_err(|| "Error parsing templates from themes")?; | |||
Tera::parse(&theme_tpl_glob).map_err(|e| Error::chain("Error parsing templates from themes", e))?; | |||
rewrite_theme_paths(&mut tera_theme, &theme); | |||
// TODO: same as below | |||
// TODO: we do that twice, make it dry? | |||
if theme_path.join("templates").join("robots.txt").exists() { | |||
tera_theme | |||
.add_template_file(theme_path.join("templates").join("robots.txt"), None)?; | |||
@@ -470,7 +470,7 @@ impl Site { | |||
pub fn clean(&self) -> Result<()> { | |||
if self.output_path.exists() { | |||
// Delete current `public` directory so we can start fresh | |||
remove_dir_all(&self.output_path).chain_err(|| "Couldn't delete output directory")?; | |||
remove_dir_all(&self.output_path).map_err(|e| Error::chain("Couldn't delete output directory", e))?; | |||
} | |||
Ok(()) | |||
@@ -11,7 +11,6 @@ pulldown-cmark = "0.2" | |||
toml = "0.4" | |||
csv = "1" | |||
serde_json = "1.0" | |||
error-chain = "0.12" | |||
reqwest = "0.9" | |||
url = "1.5" | |||
@@ -1,5 +1,3 @@ | |||
extern crate error_chain; | |||
use std::collections::HashMap; | |||
use std::sync::{Arc, Mutex}; | |||
@@ -25,7 +25,7 @@ pub mod global_fns; | |||
use tera::{Context, Tera}; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
lazy_static! { | |||
pub static ref ZOLA_TERA: Tera = { | |||
@@ -57,5 +57,5 @@ pub fn render_redirect_template(url: &str, tera: &Tera) -> Result<String> { | |||
context.insert("url", &url); | |||
tera.render("internal/alias.html", &context) | |||
.chain_err(|| format!("Failed to render alias for '{}'", url)) | |||
.map_err(|e| Error::chain(format!("Failed to render alias for '{}'", url), e)) | |||
} |
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; | |||
use std::time::SystemTime; | |||
use walkdir::WalkDir; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error}; | |||
pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result<bool> { | |||
let canonical_path = path | |||
@@ -19,7 +19,8 @@ pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result<bool> { | |||
/// Create a file with the content given | |||
pub fn create_file(path: &Path, content: &str) -> Result<()> { | |||
let mut file = File::create(&path).chain_err(|| format!("Failed to create {:?}", path))?; | |||
let mut file = File::create(&path) | |||
.map_err(|e| Error::chain(format!("Failed to create {:?}", path), e))?; | |||
file.write_all(content.as_bytes())?; | |||
Ok(()) | |||
} | |||
@@ -37,7 +38,7 @@ pub fn ensure_directory_exists(path: &Path) -> Result<()> { | |||
pub fn create_directory(path: &Path) -> Result<()> { | |||
if !path.exists() { | |||
create_dir_all(path) | |||
.chain_err(|| format!("Was not able to create folder {}", path.display()))?; | |||
.map_err(|e| Error::chain(format!("Was not able to create folder {}", path.display()), e))?; | |||
} | |||
Ok(()) | |||
} | |||
@@ -46,7 +47,7 @@ pub fn create_directory(path: &Path) -> Result<()> { | |||
pub fn read_file(path: &Path) -> Result<String> { | |||
let mut content = String::new(); | |||
File::open(path) | |||
.chain_err(|| format!("Failed to open '{:?}'", path.display()))? | |||
.map_err(|e| Error::chain(format!("Failed to open '{:?}'", path.display()), e))? | |||
.read_to_string(&mut content)?; | |||
// Remove utf-8 BOM if any. | |||
@@ -57,6 +58,19 @@ pub fn read_file(path: &Path) -> Result<String> { | |||
Ok(content) | |||
} | |||
/// Return the content of a file, with error handling added. | |||
/// The default error message is overwritten by the message given. | |||
/// That means it is allocation 2 strings, oh well | |||
pub fn read_file_with_error(path: &Path, message: &str) -> Result<String> { | |||
let res = read_file(&path); | |||
if res.is_ok() { | |||
return res; | |||
} | |||
let mut err = Error::msg(message); | |||
err.source = res.unwrap_err().source; | |||
Err(err) | |||
} | |||
/// Looks into the current folder for the path and see if there's anything that is not a .md | |||
/// file. Those will be copied next to the rendered .html file | |||
pub fn find_related_assets(path: &Path) -> Vec<PathBuf> { | |||
@@ -4,4 +4,5 @@ | |||
{% block doc_content %} | |||
<h1>{{page.title}}</h1> | |||
{{page.content | safe}} | |||
{{hey}} | |||
{% endblock doc_content %} |
@@ -36,7 +36,7 @@ use ctrlc; | |||
use notify::{watcher, RecursiveMode, Watcher}; | |||
use ws::{Message, Sender, WebSocket}; | |||
use errors::{Result, ResultExt}; | |||
use errors::{Result, Error as ZolaError}; | |||
use site::Site; | |||
use utils::fs::copy_file; | |||
@@ -179,23 +179,23 @@ pub fn serve( | |||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); | |||
watcher | |||
.watch("content/", RecursiveMode::Recursive) | |||
.chain_err(|| "Can't watch the `content` folder. Does it exist?")?; | |||
.map_err(|e| ZolaError::chain("Can't watch the `content` folder. Does it exist?", e))?; | |||
watcher | |||
.watch(config_file, RecursiveMode::Recursive) | |||
.chain_err(|| "Can't watch the `config` file. Does it exist?")?; | |||
.map_err(|e| ZolaError::chain("Can't watch the `config` file. Does it exist?", e))?; | |||
if Path::new("static").exists() { | |||
watching_static = true; | |||
watcher | |||
.watch("static/", RecursiveMode::Recursive) | |||
.chain_err(|| "Can't watch the `static` folder.")?; | |||
.map_err(|e| ZolaError::chain("Can't watch the `static` folder.", e))?; | |||
} | |||
if Path::new("templates").exists() { | |||
watching_templates = true; | |||
watcher | |||
.watch("templates/", RecursiveMode::Recursive) | |||
.chain_err(|| "Can't watch the `templates` folder.")?; | |||
.map_err(|e| ZolaError::chain("Can't watch the `templates` folder.", e))?; | |||
} | |||
// Sass support is optional so don't make it an error to no have a sass folder | |||
@@ -1,4 +1,5 @@ | |||
use std::env; | |||
use std::error::Error as StdError; | |||
use std::io::Write; | |||
use std::time::Instant; | |||
@@ -6,7 +7,6 @@ use atty; | |||
use chrono::Duration; | |||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; | |||
use errors::Error; | |||
use site::Site; | |||
lazy_static! { | |||
@@ -91,13 +91,15 @@ pub fn report_elapsed_time(instant: Instant) { | |||
} | |||
/// Display an error message and the actual error(s) | |||
pub fn unravel_errors(message: &str, error: &Error) { | |||
pub fn unravel_errors(message: &str, error: &StdError) { | |||
if !message.is_empty() { | |||
self::error(message); | |||
} | |||
self::error(&format!("Error: {}", error)); | |||
for e in error.iter().skip(1) { | |||
let mut cause = error.source(); | |||
while let Some(e) = cause { | |||
self::error(&format!("Reason: {}", e)); | |||
cause = e.source(); | |||
} | |||
} | |||