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}