diff options
author | Thomas Letan <contact@thomasletan.fr> | 2019-08-04 23:06:18 +0200 |
---|---|---|
committer | Thomas Letan <contact@thomasletan.fr> | 2019-08-04 23:10:04 +0200 |
commit | 56dbe3dc6024a39d5c588a38efc02f4e2933c5a0 (patch) | |
tree | 84767776cfd08c355d60af1090dafe42e28ae2f9 | |
parent | fix: Address several errors returned by EPUB Validator (diff) |
refactor: Provide a less cumbersome error handling
-rw-r--r-- | src/epub.rs | 39 | ||||
-rw-r--r-- | src/error.rs | 30 | ||||
-rw-r--r-- | src/main.rs | 22 | ||||
-rw-r--r-- | src/project.rs | 28 |
4 files changed, 71 insertions, 48 deletions
diff --git a/src/epub.rs b/src/epub.rs index ebd68b4..db910e0 100644 --- a/src/epub.rs +++ b/src/epub.rs @@ -1,9 +1,13 @@ -use crate::project::{Error, Project, Chapter}; +use std::fs; use std::fs::{create_dir_all}; use std::path::{Path, PathBuf}; + use tera::{Tera, Context}; use serde_json::json; +use crate::error::{Raise, Error}; +use crate::project::{Project, Chapter}; + const EPUB_MIMETYPE: &'static str = "application/epub+zip"; fn write_template_to(tera : &Tera, template : &str, ctx : &Context, path : &PathBuf) -> Result<(), Error> { @@ -11,21 +15,19 @@ fn write_template_to(tera : &Tera, template : &str, ctx : &Context, path : &Path if !directory.exists() { create_dir_all(directory) - .map_err(|_| Error(format!("cannot create directory {:?}", directory)))?; + .or_raise(&format!("cannot create directory {:?}", directory))?; } let content = tera.render(template, ctx) - .map_err(|e| Error(format!("cannot render {}: {}", template, e)))?; + .or_raise(&format!("cannot render {}", template))?; - std::fs::write(path, content) - .map_err(|_| Error(format!("cannot create {:?}", path)))?; + fs::write(path, content).or_raise(&format!("cannot create {:?}", path))?; Ok(()) } fn create_mimetype() -> Result<(), Error> { - std::fs::write("mimetype", EPUB_MIMETYPE) - .map_err(|_| Error(String::from("cannot create mimetype")))?; + fs::write("mimetype", EPUB_MIMETYPE).or_raise("cannot create mimetype")?; Ok(()) } @@ -85,18 +87,13 @@ fn fonts_dir(assets : &PathBuf) -> Result<PathBuf, Error> { } fn install_fonts(assets : &PathBuf, fonts : &Vec<&str>) -> Result<(), Error> { - create_dir_all("OEBPS/Fonts/") - .map_err(|_| Error(String::from("cannot create directory OEBPS/Fonts/")))?; + create_dir_all("OEBPS/Fonts/").or_raise("cannot create directory OEBPS/Fonts/")?; for f in fonts { - let mut src = fonts_dir(assets)?; - src.push(f); - let mut dst = PathBuf::from("OEBPS/Fonts"); - dst.push(f); - - std::fs::copy(src, dst) - .map_err(|_| Error(format!("cannot copy {}", f)))?; + let src = fonts_dir(assets)?.join(f); + let dst = PathBuf::from("OEBPS/Fonts").join(f); + fs::copy(src, dst).or_raise(&format!("cannot copy {}", f))?; } Ok(()) @@ -104,15 +101,13 @@ fn install_fonts(assets : &PathBuf, fonts : &Vec<&str>) -> Result<(), Error> { fn install_cover(cover : &PathBuf) -> Result<String, Error> { let extension = cover.extension() - .ok_or(Error(String::from("cover lacks an extension")))? + .or_raise("cover lacks an extension")? .to_str() - .ok_or(Error(String::from("cover extension is not valid utf-8")))?; + .or_raise("cover extension is not valid utf-8")?; - let mut dst = PathBuf::from("OEBPS"); - dst.push(format!("cover.{}", extension)); + let dst = PathBuf::from("OEBPS").join(format!("cover.{}", extension)); - std::fs::copy(cover, dst) - .map_err(|_| Error(format!("cannot copy {:?}", cover)))?; + fs::copy(cover, dst).or_raise(&format!("cannot copy {:?}", cover))?; Ok(extension.into()) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..d92077e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,30 @@ +#[derive(Debug)] +pub struct Error(pub String); + +impl Error { + pub fn new(str : &str) -> Error { + Error(String::from(str)) + } +} + +pub trait Raise { + type Out; + + fn or_raise(self, msg: &str) -> Self::Out; +} + +impl<T> Raise for Option<T> { + type Out = Result<T, Error>; + + fn or_raise(self, msg: &str) -> Result<T, Error> { + self.ok_or(Error(String::from(msg))) + } +} + +impl<T, E> Raise for Result<T, E> { + type Out = Result<T, Error>; + + fn or_raise(self, msg: &str) -> Result<T, Error> { + self.map_err(|_| Error(String::from(msg))) + } +} diff --git a/src/main.rs b/src/main.rs index b3d0251..bcccad1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,27 +8,28 @@ extern crate tera; use clap::{App, SubCommand}; +pub mod error; pub mod render; pub mod project; pub mod epub; -use project::{Project, Error}; +use std::env::{current_dir, set_current_dir}; +use std::fs::{create_dir, remove_dir_all}; +use std::path::PathBuf; + use ogmarkup::typography::FRENCH; -use std::path::PathBuf; -use std::fs::{create_dir, remove_dir_all}; +use error::{Raise, Error}; +use project::Project; const BUILD_DIR : &'static str = "_build"; fn cd_clean_build_dir() -> Result<(), Error> { - remove_dir_all(BUILD_DIR) - .map_err(|_| Error(String::from("cannot clean up _build/")))?; + remove_dir_all(BUILD_DIR).or_raise("cannot clean up _build/")?; - create_dir(BUILD_DIR) - .map_err(|_| Error(String::from("cannot create _build/")))?; + create_dir(BUILD_DIR).or_raise("cannot create _build/")?; - std::env::set_current_dir(BUILD_DIR) - .map_err(|_| Error(String::from("cannot set current directory to _build")))?; + set_current_dir(BUILD_DIR).or_raise("cannot set current directory to _build/")?; Ok(()) } @@ -60,8 +61,7 @@ fn main() -> Result<(), Error> { let (subcommand, _args) = matches.subcommand(); // TODO: in release mode, look for /usr/share/celtchar/assets - let assets: PathBuf = std::env::current_dir() - .map_err(|_| Error(String::from("cannot get current directory")))?; + let assets: PathBuf = current_dir().or_raise("cannot get current directory")?; match subcommand { "build" => build(&assets)?, diff --git a/src/project.rs b/src/project.rs index 05d0f33..c411587 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,18 +1,16 @@ use std::fs; use std::path::PathBuf; -use std::env::current_dir; +use std::env::{current_dir, set_current_dir}; use serde_derive::{Deserialize, Serialize}; use ogmarkup::typography::Typography; use crate::render::Html; +use crate::error::{Raise, Error}; const PROJECT_FILE: &'static str = "Book.toml"; -#[derive(Debug)] -pub struct Error(pub String); - #[derive(Debug, Serialize, Deserialize)] pub struct Chapter<A> { pub title: Option<String>, @@ -26,11 +24,12 @@ fn compile_file <'input, T> ( where T : Typography, { - let input = fs::read_to_string(path.as_path()).map_err(|_| Error(format!("cannot open {:?}", path)))?; + let input = fs::read_to_string(path.as_path()) + .or_raise(&format!("cannot open {:?}", path))?; ogmarkup::compile(input.as_str(), typo) .map(|x: Html| x.to_string()) - .map_err(|_| Error(format!("cannot render {:?}", path))) + .or_raise(&format!("cannot render {:?}", path)) } impl Chapter<Vec<PathBuf>> { @@ -62,8 +61,7 @@ pub struct Project<A> { impl Project<Vec<PathBuf>> { pub fn cd_root() -> Result<(), Error> { - let mut cwd: PathBuf = current_dir() - .map_err(|_| Error(String::from("cannot get current directory")))?; + let mut cwd: PathBuf = current_dir().or_raise("cannot get current directory")?; loop { cwd.push(PROJECT_FILE); // (*) @@ -71,8 +69,8 @@ impl Project<Vec<PathBuf>> { if cwd.exists() { cwd.pop(); - std::env::set_current_dir(cwd.as_path()) - .map_err(|_| Error(format!("cannot set current directory to {:?}", cwd)))?; + set_current_dir(cwd.as_path()) + .or_raise(&format!("cannot set current directory to {:?}", cwd))?; return Ok(()); } else { @@ -84,7 +82,7 @@ impl Project<Vec<PathBuf>> { // `pop` returns false, we are at the root of the current FS, and // there is no project file to find. if !cwd.pop() { - return Err(Error(String::from("could not find Book.toml"))) + return Err(Error::new("could not find Book.toml")) } } } @@ -94,10 +92,10 @@ impl Project<Vec<PathBuf>> { /// read it as a TOML file. pub fn find_project() -> Result<Self, Error> { let input = fs::read_to_string(PROJECT_FILE) - .map_err(|_| Error(String::from("found Book.toml, but cannot read it")))?; + .or_raise("found Book.toml, but cannot read it")?; - return toml::from_str(input.as_str()) - .map_err(|e| Error(String::from(format!("toml error: {:?}", e)))); + toml::from_str(input.as_str()) + .or_raise(&format!("could not parse Book.toml")) } pub fn load_and_render<'input, T> ( @@ -111,7 +109,7 @@ impl Project<Vec<PathBuf>> { let title = self.title; let cover = self.cover .map(|x| fs::canonicalize(&x) - .map_err(|_| Error(String::from("cannot compute a canonical path for the cover")))) + .or_raise("cannot compute a canonical path for the cover")) // from Option<Result<_, E>> to Result<Option<_>, E> .map_or(Ok(None), |r| r.map(Some))?; |