Merge pull request #361 from Michael-F-Bryan/error-chain

Add error-chain throughout the codebase
This commit is contained in:
Mathieu David 2017-06-26 17:07:51 +02:00 committed by GitHub
commit c9a117cc4e
11 changed files with 94 additions and 61 deletions

View File

@ -28,6 +28,13 @@ matrix:
- nodejs
- os: linux
env: TARGET=x86_64-unknown-linux-musl CHANNEL=stable
dist: trusty
addons:
apt:
packages: &musl_packages
- musl
- musl-dev
- musl-tools
# Beta channel
- os: osx
env: TARGET=i686-apple-darwin CHANNEL=beta
@ -42,6 +49,13 @@ matrix:
env: TARGET=x86_64-unknown-linux-gnu CHANNEL=beta
- os: linux
env: TARGET=x86_64-unknown-linux-musl CHANNEL=beta
dist: trusty
addons:
apt:
packages: &musl_packages
- musl
- musl-dev
- musl-tools
# Nightly channel
- os: osx
env: TARGET=i686-apple-darwin CHANNEL=nightly
@ -56,6 +70,13 @@ matrix:
env: TARGET=x86_64-unknown-linux-gnu CHANNEL=nightly
- os: linux
env: TARGET=x86_64-unknown-linux-musl CHANNEL=nightly
dist: trusty
addons:
apt:
packages: &musl_packages
- musl
- musl-dev
- musl-tools
install:
- export PATH="$PATH:$HOME/.cargo/bin"

View File

@ -19,6 +19,7 @@ clap = "2.24"
handlebars = "0.27"
serde = "1.0"
serde_derive = "1.0"
error-chain = "0.10.0"
serde_json = "1.0"
pulldown-cmark = "0.0.14"
log = "0.3"

View File

@ -14,10 +14,10 @@ extern crate time;
extern crate crossbeam;
use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use mdbook::errors::*;
use clap::{App, ArgMatches, SubCommand, AppSettings};
@ -110,7 +110,7 @@ fn confirm() -> bool {
// Init command implementation
fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
fn init(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let mut book = MDBook::new(&book_dir);
@ -163,7 +163,7 @@ fn init(args: &ArgMatches) -> Result<(), Box<Error>> {
// Build command implementation
fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
fn build(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir).read_config()?;
@ -194,7 +194,7 @@ fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
// Watch command implementation
#[cfg(feature = "watch")]
fn watch(args: &ArgMatches) -> Result<(), Box<Error>> {
fn watch(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let book = MDBook::new(&book_dir).read_config()?;
@ -233,7 +233,7 @@ mod serve {
use std;
use std::path::Path;
use std::error::Error;
use mdbook::errors::*;
use self::iron::{Iron, AfterMiddleware, IronResult, IronError, Request, Response, status, Set, Chain};
use clap::ArgMatches;
use mdbook::MDBook;
@ -253,7 +253,7 @@ mod serve {
}
// Watch command implementation
pub fn serve(args: &ArgMatches) -> Result<(), Box<Error>> {
pub fn serve(args: &ArgMatches) -> Result<()> {
const RELOAD_COMMAND: &'static str = "reload";
let book_dir = get_book_dir(args);
@ -334,7 +334,7 @@ mod serve {
}
}
fn test(args: &ArgMatches) -> Result<(), Box<Error>> {
fn test(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let mut book = MDBook::new(&book_dir).read_config()?;

View File

@ -2,6 +2,7 @@ use serde::{Serialize, Serializer};
use serde::ser::SerializeStruct;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub enum BookItem {
Chapter(String, Chapter), // String = section
@ -37,7 +38,7 @@ impl Chapter {
impl Serialize for Chapter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where S: Serializer
{
let mut struct_ = serializer.serialize_struct("Chapter", 2)?;

View File

@ -4,14 +4,12 @@ pub use self::bookitem::{BookItem, BookItems};
use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io;
use std::io::{Read, Write};
use std::io::ErrorKind;
use std::process::Command;
use {theme, parse, utils};
use renderer::{Renderer, HtmlHandlebars};
use errors::*;
use config::BookConfig;
use config::tomlconfig::TomlConfig;
@ -129,7 +127,7 @@ impl MDBook {
/// and adds a `SUMMARY.md` and a
/// `chapter_1.md` to the source directory.
pub fn init(&mut self) -> Result<(), Box<Error>> {
pub fn init(&mut self) -> Result<()> {
debug!("[fn]: init");
@ -239,7 +237,7 @@ impl MDBook {
/// method of the current renderer.
///
/// It is the renderer who generates all the output files.
pub fn build(&mut self) -> Result<(), Box<Error>> {
pub fn build(&mut self) -> Result<()> {
debug!("[fn]: build");
self.init()?;
@ -249,9 +247,7 @@ impl MDBook {
utils::fs::remove_dir_content(htmlconfig.get_destination())?;
}
self.renderer.render(&self)?;
Ok(())
self.renderer.render(&self)
}
@ -259,7 +255,7 @@ impl MDBook {
self.config.get_root().join(".gitignore")
}
pub fn copy_theme(&self) -> Result<(), Box<Error>> {
pub fn copy_theme(&self) -> Result<()> {
debug!("[fn]: copy_theme");
if let Some(htmlconfig) = self.config.get_html_config() {
@ -298,16 +294,14 @@ impl MDBook {
Ok(())
}
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<()> {
let path = self.get_destination()
.ok_or(String::from("HtmlConfig not set, could not find a destination"))?
.join(filename);
utils::fs::create_file(&path)
.and_then(|mut file| file.write_all(content))
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e)))?;
Ok(())
utils::fs::create_file(&path)?
.write_all(content)
.map_err(|e| e.into())
}
/// Parses the `book.json` file (if it exists) to extract
@ -315,7 +309,7 @@ impl MDBook {
/// The `book.json` file should be in the root directory of the book.
/// The root directory is the one specified when creating a new `MDBook`
pub fn read_config(mut self) -> Result<Self, Box<Error>> {
pub fn read_config(mut self) -> Result<Self> {
let toml = self.get_root().join("book.toml");
let json = self.get_root().join("book.json");
@ -369,9 +363,9 @@ impl MDBook {
self
}
pub fn test(&mut self) -> Result<(), Box<Error>> {
pub fn test(&mut self) -> Result<()> {
// read in the chapters
self.parse_summary()?;
self.parse_summary().chain_err(|| "Couldn't parse summary")?;
for item in self.iter() {
if let BookItem::Chapter(_, ref ch) = *item {
@ -381,15 +375,10 @@ impl MDBook {
println!("[*]: Testing file: {:?}", path);
let output_result = Command::new("rustdoc").arg(&path).arg("--test").output();
let output = output_result?;
let output = Command::new("rustdoc").arg(&path).arg("--test").output()?;
if !output.status.success() {
return Err(Box::new(io::Error::new(ErrorKind::Other,
format!("{}\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)))) as
Box<Error>);
bail!(ErrorKind::Subprocess("Rustdoc returned an error".to_string(), output));
}
}
}
@ -556,7 +545,7 @@ impl MDBook {
}
// Construct book
fn parse_summary(&mut self) -> Result<(), Box<Error>> {
fn parse_summary(&mut self) -> Result<()> {
// When append becomes stable, use self.content.append() ...
self.content = parse::construct_bookitems(&self.get_source().join("SUMMARY.md"))?;
Ok(())

View File

@ -1,5 +1,6 @@
extern crate serde_json;
use std::path::PathBuf;
use errors::*;
/// The JSON configuration is **deprecated** and will be removed in the near future.
/// Please migrate to the TOML configuration.
@ -32,9 +33,9 @@ pub struct JsonConfig {
/// assert_eq!(config.dest, Some(PathBuf::from("htmlbook")));
/// ```
impl JsonConfig {
pub fn from_json(input: &str) -> Result<Self, String> {
pub fn from_json(input: &str) -> Result<Self> {
let config: JsonConfig = serde_json::from_str(input)
.map_err(|e| format!("Could not parse JSON: {}", e))?;
.chain_err(|| format!("Could not parse JSON"))?;
return Ok(config);
}

View File

@ -1,5 +1,6 @@
extern crate toml;
use std::path::PathBuf;
use errors::*;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct TomlConfig {
@ -44,9 +45,9 @@ pub struct TomlHtmlConfig {
/// assert_eq!(config.output.unwrap().html.unwrap().destination, Some(PathBuf::from("htmlbook")));
/// ```
impl TomlConfig {
pub fn from_toml(input: &str) -> Result<Self, String> {
pub fn from_toml(input: &str) -> Result<Self> {
let config: TomlConfig = toml::from_str(input)
.map_err(|e| format!("Could not parse TOML: {}", e))?;
.chain_err(|| "Could not parse TOML")?;
return Ok(config);
}

View File

@ -69,10 +69,13 @@
//!
//! Make sure to take a look at it.
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate serde;
#[macro_use] extern crate serde_json;
#[macro_use]
extern crate serde_json;
extern crate handlebars;
extern crate pulldown_cmark;
@ -90,3 +93,21 @@ pub mod utils;
pub use book::MDBook;
pub use book::BookItem;
pub use renderer::Renderer;
/// The error types used through out this crate.
pub mod errors {
error_chain!{
foreign_links {
Io(::std::io::Error);
HandlebarsRender(::handlebars::RenderError);
HandlebarsTemplate(::handlebars::TemplateError);
Utf8(::std::string::FromUtf8Error);
}
errors {
Subprocess(message: String, output: ::std::process::Output) {
description("A subprocess failed")
}
}
}
}

View File

@ -4,12 +4,12 @@ use book::MDBook;
use book::bookitem::{BookItem, Chapter};
use utils;
use theme::{self, Theme};
use errors::*;
use regex::{Regex, Captures};
use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io::{self, Read};
use std::collections::BTreeMap;
use std::collections::HashMap;
@ -28,7 +28,7 @@ impl HtmlHandlebars {
}
fn render_item(&self, item: &BookItem, mut ctx: RenderItemContext, print_content: &mut String)
-> Result<(), Box<Error>> {
-> Result<()> {
// FIXME: This should be made DRY-er and rely less on mutable state
match *item {
BookItem::Chapter(_, ref ch) |
@ -88,7 +88,7 @@ impl HtmlHandlebars {
}
/// Create an index.html from the first element in SUMMARY.md
fn render_index(&self, book: &MDBook, ch: &Chapter, destination: &Path) -> Result<(), Box<Error>> {
fn render_index(&self, book: &MDBook, ch: &Chapter, destination: &Path) -> Result<()> {
debug!("[*]: index.html");
let mut content = String::new();
@ -129,7 +129,7 @@ impl HtmlHandlebars {
rendered
}
fn copy_static_files(&self, book: &MDBook, theme: &Theme) -> Result<(), Box<Error>> {
fn copy_static_files(&self, book: &MDBook, theme: &Theme) -> Result<()> {
book.write_file("book.js", &theme.js)?;
book.write_file("book.css", &theme.css)?;
book.write_file("favicon.png", &theme.favicon)?;
@ -180,7 +180,7 @@ impl HtmlHandlebars {
/// Helper function to write a file to the build directory, normalizing
/// the path to be relative to the book root.
fn write_custom_file(&self, custom_file: &Path, book: &MDBook) -> Result<(), Box<Error>> {
fn write_custom_file(&self, custom_file: &Path, book: &MDBook) -> Result<()> {
let mut data = Vec::new();
let mut f = File::open(custom_file)?;
f.read_to_end(&mut data)?;
@ -216,7 +216,7 @@ impl HtmlHandlebars {
/// Copy across any additional CSS and JavaScript files which the book
/// has been configured to use.
fn copy_additional_css_and_js(&self, book: &MDBook) -> Result<(), Box<Error>> {
fn copy_additional_css_and_js(&self, book: &MDBook) -> Result<()> {
let custom_files = book.get_additional_css().iter().chain(
book.get_additional_js()
.iter(),
@ -232,7 +232,7 @@ impl HtmlHandlebars {
impl Renderer for HtmlHandlebars {
fn render(&self, book: &MDBook) -> Result<(), Box<Error>> {
fn render(&self, book: &MDBook) -> Result<()> {
debug!("[fn]: render");
let mut handlebars = Handlebars::new();
@ -258,9 +258,7 @@ impl Renderer for HtmlHandlebars {
debug!("[*]: Check if destination directory exists");
if fs::create_dir_all(&destination).is_err() {
return Err(Box::new(
io::Error::new(io::ErrorKind::Other, "Unexpected error when constructing destination path"),
));
bail!("Unexpected error when constructing destination path");
}
for (i, item) in book.iter().enumerate() {
@ -301,7 +299,7 @@ impl Renderer for HtmlHandlebars {
}
}
fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>> {
debug!("[fn]: make_data");
let mut data = serde_json::Map::new();

View File

@ -2,8 +2,8 @@ pub use self::html_handlebars::HtmlHandlebars;
mod html_handlebars;
use std::error::Error;
use errors::*;
pub trait Renderer {
fn render(&self, book: &::book::MDBook) -> Result<(), Box<Error>>;
fn render(&self, book: &::book::MDBook) -> Result<()>;
}

View File

@ -1,16 +1,16 @@
use std::path::{Path, PathBuf, Component};
use std::error::Error;
use std::io::{self, Read};
use errors::*;
use std::io::Read;
use std::fs::{self, File};
/// Takes a path to a file and try to read the file into a String
pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
pub fn file_to_string(path: &Path) -> Result<String> {
let mut file = match File::open(path) {
Ok(f) => f,
Err(e) => {
debug!("[*]: Failed to open {:?}", path);
return Err(Box::new(e));
bail!(e);
},
};
@ -18,7 +18,7 @@ pub fn file_to_string(path: &Path) -> Result<String, Box<Error>> {
if let Err(e) = file.read_to_string(&mut content) {
debug!("[*]: Failed to read {:?}", path);
return Err(Box::new(e));
bail!(e);
}
Ok(content)
@ -72,7 +72,7 @@ pub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {
/// it checks every directory in the path to see if it exists,
/// and if it does not it will be created.
pub fn create_file(path: &Path) -> io::Result<File> {
pub fn create_file(path: &Path) -> Result<File> {
debug!("[fn]: create_file");
// Construct path
@ -83,12 +83,12 @@ pub fn create_file(path: &Path) -> io::Result<File> {
}
debug!("[*]: Create file: {:?}", path);
File::create(path)
File::create(path).map_err(|e| e.into())
}
/// Removes all the content of a directory but not the directory itself
pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
pub fn remove_dir_content(dir: &Path) -> Result<()> {
for item in fs::read_dir(dir)? {
if let Ok(item) = item {
let item = item.path();
@ -108,7 +108,7 @@ pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
/// with the extensions given in the `ext_blacklist` array
pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str])
-> Result<(), Box<Error>> {
-> Result<()> {
debug!("[fn] copy_files_except_ext");
// Check that from and to are different
if from == to {