Skip to main content

fea_rs/compile/
error.rs

1//! Error types related to compilation
2
3use std::fmt::Display;
4
5use smol_str::SmolStr;
6use write_fonts::{BuilderError, read::ReadError};
7
8use crate::{DiagnosticSet, parse::SourceLoadError};
9
10/// An error that occurs when extracting a glyph order from a UFO.
11#[derive(Clone, Debug, thiserror::Error)]
12#[non_exhaustive]
13pub enum UfoGlyphOrderError {
14    /// Missing 'public.glyphOrder' key
15    #[error("No public.glyphOrder key in lib.plist")]
16    KeyNotSet,
17    /// Glyph order is present, but malformed
18    #[error("public.glyphOrder exists, but is not an array of strings")]
19    Malformed,
20    /// Glyphs were present but not a valid glyph order.
21    #[error(transparent)]
22    BadGlyphOrder(#[from] GlyphOrderError),
23}
24
25/// An error that occurs when extracting a glyph order from a font file.
26#[derive(Clone, Debug, thiserror::Error)]
27#[non_exhaustive]
28pub enum FontGlyphOrderError {
29    /// Failed to read font data
30    #[error("Failed to read font data: '{0}'")]
31    ReadError(
32        #[from]
33        #[source]
34        ReadError,
35    ),
36    /// Post table is missing glyph names
37    #[error("The post table exists, but did not include all glyph names")]
38    MissingNames,
39    /// Glyphs were present but not a valid glyph order.
40    #[error(transparent)]
41    BadGlyphOrder(#[from] GlyphOrderError),
42}
43
44/// An error that occurs when loading a raw glyph order.
45#[derive(Clone, Debug, thiserror::Error)]
46#[non_exhaustive]
47pub enum GlyphOrderError {
48    /// Invalid name
49    #[error("Invalid name '{name}' in glyph order")]
50    #[allow(missing_docs)]
51    NameError { name: SmolStr },
52    /// Missing .notdef glyph
53    #[error("The first glyph must be '.notdef'")]
54    MissingNotDef,
55    #[error("Order contains too many glyphs ({found})")]
56    #[allow(missing_docs)]
57    TooManyGlyphs { found: u32 },
58}
59
60/// An error reported by the compiler
61#[derive(Debug, thiserror::Error)]
62#[allow(missing_docs)]
63#[non_exhaustive]
64pub enum CompilerError {
65    #[error(transparent)]
66    SourceLoad(#[from] SourceLoadError),
67    #[error("FEA parsing failed with {} errors", .0.messages.len())]
68    ParseFail(DiagnosticSet),
69    #[error("FEA validation failed with {} errors", .0.messages.len())]
70    ValidationFail(DiagnosticSet),
71    #[error("FEA compilation failed with {} errors", .0.messages.len())]
72    CompilationFail(DiagnosticSet),
73    #[error(transparent)]
74    WriteFail(#[from] BuilderError),
75}
76
77impl CompilerError {
78    /// Return a `Display` type that reports the location and nature of syntax errors
79    pub fn display_verbose(&self) -> impl Display + '_ {
80        struct Verbose<'a>(&'a CompilerError);
81        impl std::fmt::Display for Verbose<'_> {
82            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
83                write!(f, "{}", self.0)?;
84                if let Some(diagnostics) = self.0.diagnostics() {
85                    write!(f, "\n{}", diagnostics.display())?;
86                }
87                Ok(())
88            }
89        }
90        Verbose(self)
91    }
92
93    /// Return the `DiagnosticSet` associated, if any
94    pub fn diagnostics(&self) -> Option<&DiagnosticSet> {
95        match self {
96            CompilerError::ParseFail(x)
97            | CompilerError::ValidationFail(x)
98            | CompilerError::CompilationFail(x) => Some(x),
99            _ => None,
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    /// Some clients, notably fontc, expect this.
109    #[test]
110    fn assert_compiler_error_is_send() {
111        fn send_me_baby<T: Send>() {}
112        send_me_baby::<CompilerError>();
113    }
114}