use std::borrow::Cow;
use std::error;
use std::fmt;
use std::result;
use std::string::FromUtf8Error;
use rust_i18n::t;
#[derive(Debug, PartialEq, Clone)]
pub struct Source {
#[doc(hidden)]
pub file: Option<String>,
#[doc(hidden)]
pub line: Option<u32>,
}
impl Source {
pub fn empty() -> Source {
Source {
file: None,
line: None,
}
}
pub fn new<S: Into<String>>(s: S) -> Source {
Source {
file: Some(s.into()),
line: None,
}
}
pub fn set_line(&mut self, line: u32) -> &mut Self {
self.line = Some(line);
self
}
#[doc(hidden)]
pub fn unset_line(&mut self) -> &mut Self {
self.line = None;
self
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref file) = self.file {
write!(f, "{file}")?;
if let Some(line) = self.line {
write!(f, ":{line}")?;
}
} else {
write!(f, "<UNKNOWN FILE>")?;
}
Ok(())
}
}
impl<'a> From<&'a Source> for Source {
fn from(s: &'a Source) -> Source {
s.clone()
}
}
#[derive(Debug, PartialEq)]
pub struct Error {
source: Source,
inner: Inner,
}
impl Error {
pub fn default<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::Default(msg.into()),
}
}
pub fn parser<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::Parser(msg.into()),
}
}
pub fn syntect<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::Syntect(msg.into()),
}
}
pub fn config_parser<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::ConfigParser(msg.into()),
}
}
pub fn file_not_found<
S1: Into<Cow<'static, str>>,
S2: Into<Cow<'static, str>>,
O: Into<Source>,
>(
source: O,
msg: S1,
file: S2,
) -> Error {
Error {
source: source.into(),
inner: Inner::FileNotFound(msg.into(), file.into()),
}
}
pub fn render<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::Render(msg.into()),
}
}
pub fn template<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::Template(msg.into()),
}
}
pub fn invalid_option<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::InvalidOption(msg.into()),
}
}
pub fn zipper<S: Into<Cow<'static, str>>>(msg: S) -> Error {
Error {
source: Source::empty(),
inner: Inner::Zipper(msg.into()),
}
}
pub fn book_option<S: Into<Cow<'static, str>>, O: Into<Source>>(source: O, msg: S) -> Error {
Error {
source: source.into(),
inner: Inner::BookOption(msg.into()),
}
}
pub fn with_source<O: Into<Source>>(mut self, source: O) -> Error {
self.source = source.into();
self
}
pub fn is_default(&self) -> bool {
matches!(self.inner, Inner::Default(..))
}
pub fn is_parser(&self) -> bool {
matches!(self.inner, Inner::Parser(..))
}
pub fn is_config_parser(&self) -> bool {
matches!(self.inner, Inner::ConfigParser(..))
}
pub fn is_file_not_found(&self) -> bool {
matches!(self.inner, Inner::FileNotFound(..))
}
pub fn is_render(&self) -> bool {
matches!(self.inner, Inner::Render(..))
}
pub fn is_zipper(&self) -> bool {
matches!(self.inner, Inner::Zipper(..))
}
pub fn is_book_option(&self) -> bool {
matches!(self.inner, Inner::BookOption(..))
}
pub fn is_invalid_option(&self) -> bool {
matches!(self.inner, Inner::InvalidOption(..))
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match self.inner {
Inner::Default(ref s)
| Inner::Parser(ref s)
| Inner::Zipper(ref s)
| Inner::BookOption(ref s)
| Inner::ConfigParser(ref s)
| Inner::InvalidOption(ref s)
| Inner::Render(ref s)
| Inner::Template(ref s)
| Inner::Syntect(ref s) => s.as_ref(),
Inner::FileNotFound(..) => "File not found",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let source = &self.source;
if let Some(ref file) = source.file {
write!(f, "{file}")?;
if let Some(line) = source.line {
write!(f, ":{line}")?;
}
write!(f, ": ")?;
}
match self.inner {
Inner::Default(ref s) => write!(f, "{s}"),
Inner::Parser(ref s) => {
write!(
f,
"{}",
t!("error.markdown", error = s)
)
}
Inner::ConfigParser(ref s) => {
f.write_str(&t!("error.config"))?;
f.write_str(s)
}
Inner::FileNotFound(ref description, ref file) => {
write!(
f,
"{}",
t!(
"error.file_not_found",
file = file,
description = description
)
)
}
Inner::Template(ref s) => {
write!(
f,
"{}",
t!("error.template", template = s)
)
}
Inner::Render(ref s) => {
f.write_str(&t!("error.render_error"))?;
f.write_str(s)
}
Inner::Zipper(ref s) => {
f.write_str(&t!("error.zipper"))?;
f.write_str(s)
}
Inner::BookOption(ref s) => {
f.write_str(&t!("error.bookoption"))?;
f.write_str(s)
}
Inner::InvalidOption(ref s) => {
f.write_str(&t!("error.invalid_option"))?;
f.write_str(s)
}
Inner::Syntect(ref s) => {
f.write_str(&t!("error.syntect"))?;
f.write_str(s)
}
}?;
Ok(())
}
}
pub type Result<T> = result::Result<T, Error>;
impl From<upon::Error> for Error {
fn from(err: upon::Error) -> Error {
Error::template(Source::empty(), format!("{:#}", err))
}
}
impl From<FromUtf8Error> for Error {
fn from(err: FromUtf8Error) -> Error {
Error::render(
Source::empty(),
t!("error.utf8_error", error = err),
)
}
}
impl From<std::str::Utf8Error> for Error {
fn from(err: std::str::Utf8Error) -> Error {
Error::render(
Source::empty(),
t!("error.utf8_error", error = err),
)
}
}
impl From<fmt::Error> for Error {
fn from(err: fmt::Error) -> Error {
Error::default(
Source::empty(),
t!("error.format", error = err),
)
}
}
impl From<syntect::Error> for Error {
fn from(err: syntect::Error) -> Error {
Error::syntect(
Source::empty(),
format!("{msg}: {error}",
msg = t!("error.syntect"),
error = err),
)
}
}
#[derive(Debug, PartialEq)]
enum Inner {
Default(Cow<'static, str>),
Parser(Cow<'static, str>),
ConfigParser(Cow<'static, str>),
FileNotFound(Cow<'static, str>, Cow<'static, str>), Render(Cow<'static, str>),
Zipper(Cow<'static, str>),
BookOption(Cow<'static, str>),
InvalidOption(Cow<'static, str>),
Template(Cow<'static, str>),
Syntect(Cow<'static, str>),
}