Skip to content

Commit

Permalink
Use anyhow (#1816)
Browse files Browse the repository at this point in the history
Also fixes #1783
  • Loading branch information
Keats authored Apr 1, 2022
1 parent 62a0e7b commit c11ae6e
Show file tree
Hide file tree
Showing 25 changed files with 147 additions and 281 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ name = "zola"
[dependencies]
atty = "0.2.11"
clap = { version = "3", features = ["derive"] }
lazy_static = "1.1"
termcolor = "1.0.4"
# Below is for the serve cmd
hyper = { version = "0.14.1", default-features = false, features = ["runtime", "server", "http2", "http1"] }
Expand Down
14 changes: 5 additions & 9 deletions components/config/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use libs::toml::Value as Toml;
use serde::{Deserialize, Serialize};

use crate::theme::Theme;
use errors::{bail, Error, Result};
use errors::{anyhow, bail, Result};
use utils::fs::read_file;

// We want a default base url for tests
Expand Down Expand Up @@ -155,13 +155,12 @@ impl Config {
/// Parses a config file from the given path
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> {
let path = path.as_ref();
let content =
read_file(path).map_err(|e| errors::Error::chain("Failed to load config", e))?;
let content = read_file(path)?;

let mut config = Config::parse(&content)?;
let config_dir = path
.parent()
.ok_or_else(|| Error::msg("Failed to find directory containing the config file."))?;
.ok_or_else(|| anyhow!("Failed to find directory containing the config file."))?;

// this is the step at which missing extra syntax and highlighting themes are raised as errors
config.markdown.init_extra_syntaxes_and_highlight_themes(config_dir)?;
Expand Down Expand Up @@ -272,10 +271,7 @@ impl Config {
.translations
.get(key)
.ok_or_else(|| {
Error::msg(format!(
"Translation key '{}' for language '{}' is missing",
key, lang
))
anyhow!("Translation key '{}' for language '{}' is missing", key, lang)
})
.map(|term| term.to_string())
} else {
Expand Down Expand Up @@ -325,7 +321,7 @@ pub fn merge(into: &mut Toml, from: &Toml) -> Result<()> {
}
_ => {
// Trying to merge a table with something else
Err(Error::msg(&format!("Cannot merge config.toml with theme.toml because the following values have incompatibles types:\n- {}\n - {}", into, from)))
Err(anyhow!("Cannot merge config.toml with theme.toml because the following values have incompatibles types:\n- {}\n - {}", into, from))
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions components/config/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::path::Path;
use libs::toml::Value as Toml;
use serde::{Deserialize, Serialize};

use errors::{bail, Result};
use errors::{bail, Context, Result};
use utils::fs::read_file;

/// Holds the data from a `theme.toml` file.
Expand Down Expand Up @@ -40,8 +40,8 @@ impl Theme {

/// Parses a theme file from the given path
pub fn from_file(path: &Path, theme_name: &str) -> Result<Theme> {
let content = read_file(path)
.map_err(|e| errors::Error::chain(format!("Failed to load theme {}", theme_name), e))?;
let content =
read_file(path).with_context(|| format!("Failed to load theme {}", theme_name))?;
Theme::parse(&content)
}
}
1 change: 1 addition & 0 deletions components/errors/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ edition = "2021"

[dependencies]
libs = { path = "../libs" }
anyhow = "1.0.56"
122 changes: 1 addition & 121 deletions components/errors/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,121 +1 @@
use std::convert::Into;
use std::error::Error as StdError;
use std::fmt;

use libs::{image, syntect, tera, toml};

#[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 + Send + Sync>>,
}

impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self.source {
Some(ref err) => Some(&**err),
None => match self.kind {
ErrorKind::Tera(ref err) => err.source(),
_ => None,
},
}
}
}

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 + Send + Sync>>) -> Self {
Self { kind: ErrorKind::Msg(value.to_string()), source: Some(source.into()) }
}

/// Create an error from a list of path collisions, formatting the output
pub fn from_collisions(collisions: Vec<(String, Vec<String>)>) -> Self {
let mut msg = String::from("Found path collisions:\n");

for (path, filepaths) in collisions {
let row = format!("- `{}` from files {:?}\n", path, filepaths);
msg.push_str(&row);
}

Self { kind: ErrorKind::Msg(msg), source: None }
}
}

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 {
($e:expr) => {
return Err($e.into());
};
($fmt:expr, $($arg:tt)+) => {
return Err(format!($fmt, $($arg)+).into());
};
}
pub use anyhow::*;
19 changes: 7 additions & 12 deletions components/front_matter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::Path;
use libs::once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

use errors::{bail, Error, Result};
use errors::{bail, Context, Result};
use libs::regex::Regex;
use libs::{serde_yaml, toml};

Expand Down Expand Up @@ -39,7 +39,7 @@ impl RawFrontMatter<'_> {
RawFrontMatter::Toml(s) => toml::from_str(s)?,
RawFrontMatter::Yaml(s) => match serde_yaml::from_str(s) {
Ok(d) => d,
Err(e) => bail!(format!("YAML deserialize error: {:?}", e)),
Err(e) => bail!("YAML deserialize error: {:?}", e),
},
};
Ok(f)
Expand Down Expand Up @@ -105,12 +105,10 @@ pub fn split_section_content<'c>(
content: &'c str,
) -> Result<(SectionFrontMatter, &'c str)> {
let (front_matter, content) = split_content(file_path, content)?;
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,
)
let meta = SectionFrontMatter::parse(&front_matter).with_context(|| {
format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy())
})?;

Ok((meta, content))
}

Expand All @@ -121,11 +119,8 @@ pub fn split_page_content<'c>(
content: &'c str,
) -> Result<(PageFrontMatter, &'c str)> {
let (front_matter, content) = split_content(file_path, content)?;
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,
)
let meta = PageFrontMatter::parse(&front_matter).with_context(|| {
format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy())
})?;
Ok((meta, content))
}
Expand Down
40 changes: 18 additions & 22 deletions components/imageproc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::hash_map::Entry as HEntry;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::ffi::OsStr;
use std::fs::{self, File};
use std::hash::{Hash, Hasher};
Expand All @@ -19,7 +18,7 @@ use serde::{Deserialize, Serialize};
use svg_metadata::Metadata as SvgMetadata;

use config::Config;
use errors::{Error, Result};
use errors::{anyhow, Context, Error, Result};
use utils::fs as ufs;

static RESIZED_SUBDIR: &str = "processed_images";
Expand Down Expand Up @@ -83,22 +82,20 @@ impl ResizeArgs {
match op {
"fit_width" => {
if width.is_none() {
return Err("op=\"fit_width\" requires a `width` argument".into());
return Err(anyhow!("op=\"fit_width\" requires a `width` argument"));
}
}
"fit_height" => {
if height.is_none() {
return Err("op=\"fit_height\" requires a `height` argument".into());
return Err(anyhow!("op=\"fit_height\" requires a `height` argument"));
}
}
"scale" | "fit" | "fill" => {
if width.is_none() || height.is_none() {
return Err(
format!("op={} requires a `width` and `height` argument", op).into()
);
return Err(anyhow!("op={} requires a `width` and `height` argument", op));
}
}
_ => return Err(format!("Invalid image resize operation: {}", op).into()),
_ => return Err(anyhow!("Invalid image resize operation: {}", op)),
};

Ok(match op {
Expand Down Expand Up @@ -224,7 +221,7 @@ impl Format {
"jpeg" | "jpg" => Ok(Jpeg(jpg_quality)),
"png" => Ok(Png),
"webp" => Ok(WebP(quality)),
_ => Err(format!("Invalid image format: {}", format).into()),
_ => Err(anyhow!("Invalid image format: {}", format)),
}
}

Expand Down Expand Up @@ -332,7 +329,8 @@ impl ImageOp {
img.write_to(&mut f, ImageOutputFormat::Jpeg(q))?;
}
Format::WebP(q) => {
let encoder = webp::Encoder::from_image(&img)?;
let encoder = webp::Encoder::from_image(&img)
.map_err(|_| anyhow!("Unable to load this kind of image with webp"))?;
let memory = match q {
Some(q) => encoder.encode(q as f32),
None => encoder.encode_lossless(),
Expand Down Expand Up @@ -415,9 +413,8 @@ impl Processor {
format: &str,
quality: Option<u8>,
) -> Result<EnqueueResponse> {
let meta = ImageMeta::read(&input_path).map_err(|e| {
Error::chain(format!("Failed to read image: {}", input_path.display()), e)
})?;
let meta = ImageMeta::read(&input_path)
.with_context(|| format!("Failed to read image: {}", input_path.display()))?;

let args = ResizeArgs::from_args(op, width, height)?;
let op = ResizeOp::new(args, meta.size);
Expand Down Expand Up @@ -529,8 +526,9 @@ impl Processor {
.map(|(hash, op)| {
let target =
self.output_dir.join(Self::op_filename(*hash, op.collision_id, op.format));
op.perform(&target).map_err(|e| {
Error::chain(format!("Failed to process image: {}", op.input_path.display()), e)

op.perform(&target).with_context(|| {
format!("Failed to process image: {}", op.input_path.display())
})
})
.collect::<Result<()>>()
Expand Down Expand Up @@ -571,29 +569,27 @@ pub fn read_image_metadata<P: AsRef<Path>>(path: P) -> Result<ImageMetaResponse>
let path = path.as_ref();
let ext = path.extension().and_then(OsStr::to_str).unwrap_or("").to_lowercase();

let error = |e: Box<dyn StdError + Send + Sync>| {
Error::chain(format!("Failed to read image: {}", path.display()), e)
};
let err_context = || format!("Failed to read image: {}", path.display());

match ext.as_str() {
"svg" => {
let img = SvgMetadata::parse_file(&path).map_err(|e| error(e.into()))?;
let img = SvgMetadata::parse_file(&path).with_context(err_context)?;
match (img.height(), img.width(), img.view_box()) {
(Some(h), Some(w), _) => Ok((h, w)),
(_, _, Some(view_box)) => Ok((view_box.height, view_box.width)),
_ => Err("Invalid dimensions: SVG width/height and viewbox not set.".into()),
_ => Err(anyhow!("Invalid dimensions: SVG width/height and viewbox not set.")),
}
.map(|(h, w)| ImageMetaResponse::new_svg(h as u32, w as u32))
}
"webp" => {
// Unfortunatelly we have to load the entire image here, unlike with the others :|
let data = fs::read(path).map_err(|e| error(e.into()))?;
let data = fs::read(path).with_context(err_context)?;
let decoder = webp::Decoder::new(&data[..]);
decoder.decode().map(ImageMetaResponse::from).ok_or_else(|| {
Error::msg(format!("Failed to decode WebP image: {}", path.display()))
})
}
_ => ImageMeta::read(path).map(ImageMetaResponse::from).map_err(|e| error(e.into())),
_ => ImageMeta::read(path).map(ImageMetaResponse::from).with_context(err_context),
}
}

Expand Down
Loading

0 comments on commit c11ae6e

Please sign in to comment.