Better error handling 🌍

This commit is contained in:
Laurenz 2019-03-14 16:26:06 +01:00
parent aae8a3a77e
commit 22ea09d9c1
5 changed files with 110 additions and 82 deletions

View File

@ -132,15 +132,20 @@ impl<'s> Engine<'s> {
type TypeResult<T> = std::result::Result<T, TypesetError>; type TypeResult<T> = std::result::Result<T, TypesetError>;
/// The error type for typesetting. /// The error type for typesetting.
#[derive(Debug, Clone, Eq, PartialEq)] pub enum TypesetError {}
pub struct TypesetError {
message: String,
}
impl error::Error for TypesetError {} impl error::Error for TypesetError {}
impl fmt::Display for TypesetError { impl fmt::Display for TypesetError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[inline]
f.write_str(&self.message) fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
impl fmt::Debug for TypesetError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
} }
} }

View File

@ -5,9 +5,9 @@ use std::error;
use std::fmt; use std::fmt;
use std::io::{self, Cursor, Seek, SeekFrom}; use std::io::{self, Cursor, Seek, SeekFrom};
use byteorder::{BE, ReadBytesExt, WriteBytesExt}; use byteorder::{BE, ReadBytesExt, WriteBytesExt};
use opentype::{OpenTypeReader, Outlines, TableRecord, Tag}; use opentype::{Error as OpentypeError, OpenTypeReader, Outlines, TableRecord, Tag};
use opentype::tables::{Header, MacStyleFlags, Name, NameEntry, CharMap, use opentype::tables::{Header, Name, CharMap, MaximumProfile, HorizontalMetrics, Post, OS2};
MaximumProfile, HorizontalMetrics, Post, OS2}; use opentype::tables::{MacStyleFlags, NameEntry};
use crate::doc::Size; use crate::doc::Size;
@ -588,8 +588,7 @@ impl<T> TakeInvalid<T> for Option<T> {
type FontResult<T> = Result<T, FontError>; type FontResult<T> = Result<T, FontError>;
/// The error type for font subsetting. /// The error type for font operations.
#[derive(Debug)]
pub enum FontError { pub enum FontError {
/// The font file is incorrect. /// The font file is incorrect.
InvalidFont(String), InvalidFont(String),
@ -601,9 +600,6 @@ pub enum FontError {
MissingCharacter(char), MissingCharacter(char),
/// An I/O Error occured while reading the font program. /// An I/O Error occured while reading the font program.
Io(io::Error), Io(io::Error),
#[doc(hidden)]
__Extensible,
} }
impl error::Error for FontError { impl error::Error for FontError {
@ -619,18 +615,23 @@ impl error::Error for FontError {
impl fmt::Display for FontError { impl fmt::Display for FontError {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FontError::*;
match self { match self {
InvalidFont(message) => write!(f, "invalid font: {}", message), FontError::InvalidFont(message) => write!(f, "invalid font: {}", message),
MissingTable(table) => write!(f, "missing table: {}", table), FontError::MissingTable(table) => write!(f, "missing table: {}", table),
UnsupportedTable(table) => write!(f, "unsupported table: {}", table), FontError::UnsupportedTable(table) => write!(f, "unsupported table: {}", table),
MissingCharacter(c) => write!(f, "missing character: '{}'", c), FontError::MissingCharacter(c) => write!(f, "missing character: '{}'", c),
Io(err) => fmt::Display::fmt(err, f), FontError::Io(err) => write!(f, "io error: {}", err),
__Extensible => panic!("tried to display extensible variant"),
} }
} }
} }
impl fmt::Debug for FontError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl From<io::Error> for FontError { impl From<io::Error> for FontError {
#[inline] #[inline]
fn from(err: io::Error) -> FontError { fn from(err: io::Error) -> FontError {
@ -638,13 +639,12 @@ impl From<io::Error> for FontError {
} }
} }
impl From<opentype::Error> for FontError { impl From<OpentypeError> for FontError {
fn from(err: opentype::Error) -> FontError { fn from(err: OpentypeError) -> FontError {
use opentype::Error::*;
match err { match err {
InvalidFont(message) => FontError::InvalidFont(message), OpentypeError::InvalidFont(message) => FontError::InvalidFont(message),
MissingTable(tag) => FontError::MissingTable(tag.to_string()), OpentypeError::MissingTable(tag) => FontError::MissingTable(tag.to_string()),
Io(err) => FontError::Io(err), OpentypeError::Io(err) => FontError::Io(err),
_ => panic!("unexpected extensible variant"), _ => panic!("unexpected extensible variant"),
} }
} }

View File

@ -7,17 +7,17 @@
//! ``` //! ```
//! use typeset::Compiler; //! use typeset::Compiler;
//! //!
//! // Minimal source code for our document.
//! let src = "Hello World from Typeset!";
//!
//! // Create an output file. //! // Create an output file.
//! # /* //! # /*
//! let mut file = std::fs::File::create("hello-typeset.pdf").unwrap(); //! let mut file = std::fs::File::create("hello-typeset.pdf").unwrap();
//! # */ //! # */
//! # let mut file = std::fs::File::create("../target/typeset-hello.pdf").unwrap(); //! # let mut file = std::fs::File::create("../target/typeset-hello.pdf").unwrap();
//! //!
//! // Create a compiler and export a PDF. //! // Create a compiler and write the document into a file as a PDF.
//! let src = "Hello World from Typeset!";
//! let compiler = Compiler::new(); //! let compiler = Compiler::new();
//!
//! // Write the document into a file as a PDF.
//! compiler.write_pdf(src, &mut file).unwrap(); //! compiler.write_pdf(src, &mut file).unwrap();
//! ``` //! ```
@ -31,7 +31,7 @@ mod utility;
pub use crate::parsing::{Tokens, ParseError}; pub use crate::parsing::{Tokens, ParseError};
pub use crate::engine::TypesetError; pub use crate::engine::TypesetError;
pub use crate::pdf::PdfWritingError; pub use crate::pdf::PdfError;
use std::error; use std::error;
use std::fmt; use std::fmt;
@ -74,8 +74,8 @@ impl Compiler {
/// Return the abstract syntax tree representation of the document. /// Return the abstract syntax tree representation of the document.
#[inline] #[inline]
pub fn parse<'s>(&self, source: &'s str) -> Result<SyntaxTree<'s>, Error> { pub fn parse<'s>(&self, source: &'s str) -> Result<SyntaxTree<'s>, ParseError> {
Parser::new(self.tokenize(source)).parse().map_err(Into::into) Parser::new(self.tokenize(source)).parse()
} }
/// Return the abstract typesetted representation of the document. /// Return the abstract typesetted representation of the document.
@ -92,8 +92,7 @@ impl Compiler {
} }
} }
/// The error type for compilation. /// The general error type for compilation.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Error { pub enum Error {
/// An error that occured while transforming source code into /// An error that occured while transforming source code into
/// an abstract syntax tree. /// an abstract syntax tree.
@ -101,7 +100,7 @@ pub enum Error {
/// An error that occured while typesetting into an abstract document. /// An error that occured while typesetting into an abstract document.
Typeset(TypesetError), Typeset(TypesetError),
/// An error that occured while writing the document as a _PDF_. /// An error that occured while writing the document as a _PDF_.
PdfWrite(PdfWritingError) Pdf(PdfError),
} }
impl error::Error for Error { impl error::Error for Error {
@ -110,7 +109,7 @@ impl error::Error for Error {
match self { match self {
Error::Parse(err) => Some(err), Error::Parse(err) => Some(err),
Error::Typeset(err) => Some(err), Error::Typeset(err) => Some(err),
Error::PdfWrite(err) => Some(err), Error::Pdf(err) => Some(err),
} }
} }
} }
@ -121,11 +120,18 @@ impl fmt::Display for Error {
match self { match self {
Error::Parse(err) => write!(f, "parse error: {}", err), Error::Parse(err) => write!(f, "parse error: {}", err),
Error::Typeset(err) => write!(f, "typeset error: {}", err), Error::Typeset(err) => write!(f, "typeset error: {}", err),
Error::PdfWrite(err) => write!(f, "typeset error: {}", err), Error::Pdf(err) => write!(f, "pdf error: {}", err),
} }
} }
} }
impl fmt::Debug for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl From<ParseError> for Error { impl From<ParseError> for Error {
#[inline] #[inline]
fn from(err: ParseError) -> Error { fn from(err: ParseError) -> Error {
@ -140,10 +146,10 @@ impl From<TypesetError> for Error {
} }
} }
impl From<PdfWritingError> for Error { impl From<PdfError> for Error {
#[inline] #[inline]
fn from(err: PdfWritingError) -> Error { fn from(err: PdfError) -> Error {
Error::PdfWrite(err) Error::Pdf(err)
} }
} }

View File

@ -359,7 +359,6 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item = Token<'s>> {
type ParseResult<T> = std::result::Result<T, ParseError>; type ParseResult<T> = std::result::Result<T, ParseError>;
/// The error type for parsing. /// The error type for parsing.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ParseError { pub struct ParseError {
/// A message describing the error. /// A message describing the error.
message: String, message: String,
@ -368,11 +367,18 @@ pub struct ParseError {
impl error::Error for ParseError {} impl error::Error for ParseError {}
impl fmt::Display for ParseError { impl fmt::Display for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.message) f.write_str(&self.message)
} }
} }
impl fmt::Debug for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[cfg(test)] #[cfg(test)]
mod token_tests { mod token_tests {
@ -491,12 +497,12 @@ mod parse_tests {
/// Test if the source code parses into the syntax tree. /// Test if the source code parses into the syntax tree.
fn test(src: &str, tree: SyntaxTree) { fn test(src: &str, tree: SyntaxTree) {
assert_eq!(Parser::new(Tokens::new(src)).parse(), Ok(tree)); assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap(), tree);
} }
/// Test if the source parses into the error. /// Test if the source parses into the error.
fn test_err(src: &str, err: ParseError) { fn test_err(src: &str, err: &str) {
assert_eq!(Parser::new(Tokens::new(src)).parse(), Err(err)); assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap_err().message, err);
} }
/// Short cut macro to create a syntax tree. /// Short cut macro to create a syntax tree.
@ -577,17 +583,9 @@ mod parse_tests {
/// Tests whether errors get reported correctly. /// Tests whether errors get reported correctly.
#[test] #[test]
fn parse_errors() { fn parse_errors() {
test_err("No functions here]", ParseError { test_err("No functions here]", "unexpected closing bracket");
message: "unexpected closing bracket".to_owned(), test_err("[hello][world", "expected closing bracket");
}); test_err("[hello world", "expected closing bracket");
test_err("[hello][world", ParseError { test_err("[ no-name][Why?]", "expected identifier");
message: "expected closing bracket".to_owned(),
});
test_err("[hello world", ParseError {
message: "expected closing bracket".to_owned(),
});
test_err("[ no-name][Why?]", ParseError {
message: "expected identifier".to_owned(),
});
} }
} }

View File

@ -4,12 +4,12 @@ use std::collections::HashSet;
use std::error; use std::error;
use std::fmt; use std::fmt;
use std::io::{self, Write}; use std::io::{self, Write};
use pdf::{PdfWriter, Reference, Rect, Version, Trailer}; use pdf::{PdfWriter, Reference, Rect, Version, Trailer, DocumentCatalog};
use pdf::{DocumentCatalog, PageTree, Page, Resource, Text, Content}; use pdf::{PageTree, Page, Resource, Text, Content};
use pdf::font::{Type0Font, CMapEncoding, CIDFont, CIDFontType, CIDSystemInfo, use pdf::font::{Type0Font, CMapEncoding, CIDFont, CIDFontType, CIDSystemInfo};
WidthRecord, FontDescriptor, FontFlags, EmbeddedFont, GlyphUnit}; use pdf::font::{WidthRecord, FontDescriptor, FontFlags, EmbeddedFont, GlyphUnit};
use crate::doc::{Document, Size, Text as DocText, TextCommand as DocTextCommand}; use crate::doc::{Document, Size, Text as DocText, TextCommand as DocTextCommand};
use crate::font::Font; use crate::font::{Font, FontError};
/// Writes documents in the _PDF_ format. /// Writes documents in the _PDF_ format.
@ -279,34 +279,53 @@ impl std::ops::Deref for PdfFont {
} }
/// Result type used for parsing. /// Result type used for parsing.
type PdfResult<T> = std::result::Result<T, PdfWritingError>; type PdfResult<T> = std::result::Result<T, PdfError>;
/// The error type for _PDF_ creation. /// The error type for _PDF_ creation.
#[derive(Debug, Clone, Eq, PartialEq)] pub enum PdfError {
pub struct PdfWritingError { /// An error occured while subsetting the font for the _PDF_.
/// A message describing the error. Font(FontError),
message: String, /// An I/O Error on the underlying writable occured.
Io(io::Error),
} }
impl error::Error for PdfWritingError {} impl error::Error for PdfError {
impl From<io::Error> for PdfWritingError {
#[inline] #[inline]
fn from(err: io::Error) -> PdfWritingError { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
PdfWritingError { message: format!("{}", err) } match self {
PdfError::Font(err) => Some(err),
PdfError::Io(err) => Some(err),
}
} }
} }
impl From<crate::font::FontError> for PdfWritingError { impl fmt::Display for PdfError {
#[inline]
fn from(err: crate::font::FontError) -> PdfWritingError {
PdfWritingError { message: format!("{}", err) }
}
}
impl fmt::Display for PdfWritingError {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.message) match self {
PdfError::Font(err) => write!(f, "font error: {}", err),
PdfError::Io(err) => write!(f, "io error: {}", err),
}
}
}
impl fmt::Debug for PdfError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl From<io::Error> for PdfError {
#[inline]
fn from(err: io::Error) -> PdfError {
PdfError::Io(err)
}
}
impl From<FontError> for PdfError {
#[inline]
fn from(err: FontError) -> PdfError {
PdfError::Font(err)
} }
} }