Skip to main content

latex_rust/
error.rs

1//! Public error types. Unsupported input is never rendered.
2
3use core::fmt;
4
5/// Crate-level error.
6///
7/// Unsupported input is never rendered. [`Error::Parse`] wraps tokenizer/parser
8/// failures; [`Error::Font`] wraps face/glyph failures; [`Error::Unsupported`]
9/// is a construct or feature out of scope; [`Error::Malformed`] is a bad value
10/// that parsed as the wrong shape; [`Error::InvalidOption`] is a render option
11/// out of range.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum Error {
14    /// Tokenizer or parser rejected the input.
15    Parse(ParseError),
16    /// Font bytes or a requested glyph could not be used.
17    Font(FontError),
18    /// A feature listed as out of scope, or not yet implemented.
19    ///
20    /// Callers must treat this as failure. The renderer does not invent output.
21    Unsupported {
22        /// Human-readable name of the missing feature or construct.
23        what: String,
24    },
25    /// Syntactically invalid value (for example a color spec with the wrong
26    /// number of components). Distinct from [`Self::Unsupported`].
27    Malformed {
28        /// Human-readable description of what was malformed.
29        what: String,
30    },
31    /// A render option is out of range (for example PNG DPI of 0 or above 2400).
32    InvalidOption {
33        /// Human-readable name of the invalid option.
34        what: String,
35    },
36}
37
38/// Tokenizer / parser failure.
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub enum ParseError {
41    /// Input ended with a stray `\` and no command character.
42    TrailingBackslash,
43    /// Command is known but this crate will not invent a rendering for it.
44    Unsupported(String),
45    /// Command is not in the catalog and is not a known math structure.
46    Unknown(String),
47    /// Syntactically invalid input. Names the construct or position.
48    Malformed(String),
49    /// `\left` without `\right`, or `\right` without `\left`.
50    UnmatchedDelimiter,
51}
52
53/// Font loader or metric lookup failure.
54#[derive(Clone, Debug, PartialEq, Eq)]
55pub enum FontError {
56    /// Embedded or supplied bytes are not a usable OpenType face.
57    InvalidFace,
58    /// Character has no glyph in this face.
59    MissingGlyph {
60        /// Requested character.
61        ch: char,
62    },
63}
64
65impl fmt::Display for Error {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            Self::Parse(e) => write!(f, "{e}"),
69            Self::Font(e) => write!(f, "{e}"),
70            Self::Unsupported { what } => write!(f, "unsupported: {what}"),
71            Self::Malformed { what } => write!(f, "malformed: {what}"),
72            Self::InvalidOption { what } => write!(f, "invalid option: {what}"),
73        }
74    }
75}
76
77impl fmt::Display for ParseError {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            Self::TrailingBackslash => f.write_str("trailing backslash"),
81            Self::Unsupported(s) => write!(f, "unsupported: {s}"),
82            Self::Unknown(s) => write!(f, "unknown command: {s}"),
83            Self::Malformed(s) => write!(f, "malformed: {s}"),
84            Self::UnmatchedDelimiter => f.write_str("unmatched delimiter"),
85        }
86    }
87}
88
89impl fmt::Display for FontError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match self {
92            Self::InvalidFace => f.write_str("invalid OpenType face"),
93            Self::MissingGlyph { ch } => write!(f, "missing glyph for {ch:?}"),
94        }
95    }
96}
97
98impl std::error::Error for Error {}
99
100impl From<ParseError> for Error {
101    fn from(e: ParseError) -> Self {
102        Self::Parse(e)
103    }
104}
105
106impl From<FontError> for Error {
107    fn from(e: FontError) -> Self {
108        Self::Font(e)
109    }
110}