Remove error-chain

Closes #576
This commit is contained in:
Vincent Prouillet 2019-01-11 20:29:46 +01:00
parent 69fb399726
commit 5caf24f06c
23 changed files with 176 additions and 88 deletions

3
Cargo.lock generated
View file

@ -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)",

View file

@ -13,3 +13,4 @@ lazy_static = "1"
syntect = "3"
errors = { path = "../errors" }
utils = { path = "../utils" }

View file

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

View file

@ -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;

View file

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

View file

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

View file

@ -1,27 +1,102 @@
#![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;
links {
Tera(tera::Error, tera::ErrorKind);
}
#[derive(Debug)]
pub enum ErrorKind {
Msg(String),
Tera(tera::Error),
Io(::std::io::Error),
Toml(toml::de::Error),
Image(image::ImageError),
Syntect(syntect::LoadingError),
}
foreign_links {
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),
}
}
}
impl Error {
/// Creates generic error
pub fn msg(value: impl ToString) -> Self {
Self { kind: ErrorKind::Msg(value.to_string()), source: None }
}
/// 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]
macro_rules! bail {

View file

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

View file

@ -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<()>>()
}

View file

@ -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.

View file

@ -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?

View file

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

View file

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

View file

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

View file

@ -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]

View file

@ -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(())

View file

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

View file

@ -1,5 +1,3 @@
extern crate error_chain;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

View file

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

View file

@ -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> {

View file

@ -4,4 +4,5 @@
{% block doc_content %}
<h1>{{page.title}}</h1>
{{page.content | safe}}
{{hey}}
{% endblock doc_content %}

View file

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

View file

@ -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();
}
}