blob: dec547a84080c30472de1a6350d047f9013f5698 [file] [log] [blame]
use failure::{Error, Fail};
use rayon::iter::{FromParallelIterator, IntoParallelIterator};
use reqwest::StatusCode;
use std::fmt::{self, Display, Formatter};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use url::Url;
/// The error which were generated while checking links.
#[derive(Debug, Fail)]
#[fail(display = "there are broken links")]
pub struct BrokenLinks(Vec<Box<BrokenLink>>);
impl BrokenLinks {
pub fn links(&self) -> &[Box<BrokenLink>] {
&self.0
}
}
impl FromParallelIterator<Box<BrokenLink>> for BrokenLinks {
fn from_par_iter<I>(par_iter: I) -> Self
where
I: IntoParallelIterator<Item = Box<BrokenLink>>,
{
BrokenLinks(Vec::from_par_iter(par_iter))
}
}
impl FromIterator<Box<BrokenLink>> for BrokenLinks {
fn from_iter<I: IntoIterator<Item = Box<BrokenLink>>>(it: I) -> BrokenLinks {
BrokenLinks(it.into_iter().collect())
}
}
impl IntoIterator for BrokenLinks {
type Item = Box<BrokenLink>;
type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
/// An iterator over all the links in [`BrokenLinks`].
///
/// [`BrokenLinks`]: struct.BrokenLinks.html
pub struct Links<'a> {
parent: &'a BrokenLinks,
cursor: usize,
}
impl<'a> Iterator for Links<'a> {
type Item = &'a BrokenLink;
fn next(&mut self) -> Option<Self::Item> {
let item = self.parent.0.get(self.cursor).map(|b| &**b);
self.cursor += 1;
item
}
}
impl<'a> IntoIterator for &'a BrokenLinks {
type Item = &'a BrokenLink;
type IntoIter = Links<'a>;
fn into_iter(self) -> Self::IntoIter {
Links {
parent: self,
cursor: 0,
}
}
}
/// Some arbitrary broken link which occurs at a specific line in a chapter. The
/// `Display` impl should state why the link is "broken".
pub trait BrokenLink: Fail {
/// Which chapter it was in.
fn chapter(&self) -> &Path;
/// The line this error occurred on.
fn line(&self) -> usize;
fn as_fail(&self) -> &Fail;
}
macro_rules! impl_broken_link {
($name:ty) => {
impl BrokenLink for $name {
fn line(&self) -> usize {
self.line
}
fn chapter(&self) -> &Path {
&self.chapter
}
fn as_fail(&self) -> &Fail {
self
}
}
};
}
impl_broken_link!(EmptyLink);
impl_broken_link!(FileNotFound);
impl_broken_link!(HttpError);
impl_broken_link!(UnsuccessfulStatus);
impl_broken_link!(ForbiddenPath);
/// The user specified a file which doesn't exist.
#[derive(Debug, Clone, PartialEq, Fail)]
pub struct EmptyLink {
pub chapter: PathBuf,
pub line: usize,
}
impl EmptyLink {
pub(crate) fn new<P>(chapter: P, line: usize) -> EmptyLink
where
P: Into<PathBuf>,
{
let chapter = chapter.into();
EmptyLink { chapter, line }
}
}
impl Display for EmptyLink {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "The link is empty")
}
}
/// Received an unsuccessful status code when fetching a resource from the
/// internet.
#[derive(Debug, Clone, PartialEq, Fail)]
pub struct UnsuccessfulStatus {
pub url: Url,
pub code: StatusCode,
pub chapter: PathBuf,
pub line: usize,
}
impl UnsuccessfulStatus {
pub(crate) fn new<P>(url: Url, code: StatusCode, chapter: P, line: usize) -> UnsuccessfulStatus
where
P: Into<PathBuf>,
{
let chapter = chapter.into();
UnsuccessfulStatus {
url,
code,
chapter,
line,
}
}
}
impl Display for UnsuccessfulStatus {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "\"{}\" returned {}", self.url, self.code,)
}
}
/// The user specified a file which doesn't exist.
#[derive(Debug, Clone, PartialEq, Fail)]
pub struct FileNotFound {
pub path: PathBuf,
pub chapter: PathBuf,
pub line: usize,
}
impl FileNotFound {
pub(crate) fn new<P, Q>(path: P, chapter: Q, line: usize) -> FileNotFound
where
P: Into<PathBuf>,
Q: Into<PathBuf>,
{
let path = path.into();
let chapter = chapter.into();
FileNotFound {
path,
chapter,
line,
}
}
}
impl Display for FileNotFound {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "\"{}\" doesn't exist", self.path.display(),)
}
}
/// An error occurred while trying to fetch the link from the internet.
#[derive(Debug, Fail)]
pub struct HttpError {
pub url: Url,
pub chapter: PathBuf,
pub line: usize,
pub error: Error,
}
impl HttpError {
pub(crate) fn new<P, E>(url: Url, chapter: P, line: usize, error: E) -> HttpError
where
P: Into<PathBuf>,
E: Into<Error>,
{
let chapter = chapter.into();
let error = error.into();
HttpError {
url,
chapter,
line,
error,
}
}
}
impl Display for HttpError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"There was an error while fetching \"{}\", {}",
self.url, self.error,
)
}
}
#[derive(Debug, Clone, PartialEq, Fail)]
pub struct ForbiddenPath {
pub path: PathBuf,
pub chapter: PathBuf,
pub line: usize,
}
impl ForbiddenPath {
pub(crate) fn new<P, Q>(path: P, chapter: Q, line: usize) -> ForbiddenPath
where
P: Into<PathBuf>,
Q: Into<PathBuf>,
{
let path = path.into();
let chapter = chapter.into();
ForbiddenPath {
path,
chapter,
line,
}
}
}
impl Display for ForbiddenPath {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"\"{}\" goes outside the book's source directory",
self.path.display(),
)
}
}