fea_rs/compile/
error.rs

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