Skip to main content

typst_bake/
error.rs

1//! Error types for typst-bake.
2
3use std::fmt;
4
5use thiserror::Error;
6
7/// A source location (file, line, column) within a Typst source file.
8///
9/// Line and column are 1-based; the column counts characters from the start of
10/// the line, matching the Typst CLI's reporting.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct SourceLocation {
13    /// Path of the source file, e.g. `reports/event_report/report.typ`.
14    pub file: String,
15    /// 1-based line number.
16    pub line: usize,
17    /// 1-based column number (character count within the line).
18    pub column: usize,
19}
20
21impl fmt::Display for SourceLocation {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        write!(f, "{}:{}:{}", self.file, self.line, self.column)
24    }
25}
26
27/// Whether a diagnostic is a fatal error or a non-fatal warning.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum Severity {
30    /// A fatal error; compilation did not produce a document.
31    Error,
32    /// A non-fatal warning; compilation still succeeded.
33    Warning,
34}
35
36impl fmt::Display for Severity {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::Error => f.write_str("error"),
40            Self::Warning => f.write_str("warning"),
41        }
42    }
43}
44
45/// A hint attached to a [`Diagnostic`].
46///
47/// Most hints are general advice and carry no location. Some instead point at a
48/// *secondary* piece of code related to the diagnostic — for those, `location` is
49/// set to where that code lives.
50#[derive(Debug, Clone, PartialEq, Eq)]
51#[non_exhaustive]
52pub struct Hint {
53    /// The hint message.
54    pub message: String,
55    /// Where the hint points, when it refers to a secondary piece of code.
56    pub location: Option<SourceLocation>,
57}
58
59impl fmt::Display for Hint {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match &self.location {
62            Some(loc) => write!(f, "{loc}: {}", self.message),
63            None => f.write_str(&self.message),
64        }
65    }
66}
67
68/// A single Typst compilation diagnostic with resolved source location.
69#[derive(Debug, Clone, PartialEq, Eq)]
70#[non_exhaustive]
71pub struct Diagnostic {
72    /// Whether this is an error or a warning.
73    pub severity: Severity,
74    /// Where the diagnostic occurred, if it points into a source file.
75    pub location: Option<SourceLocation>,
76    /// The diagnostic message.
77    pub message: String,
78    /// Additional hints the compiler provided.
79    pub hints: Vec<Hint>,
80    /// The chain of call/import sites leading to the diagnostic (may be empty).
81    pub trace: Vec<SourceLocation>,
82}
83
84impl fmt::Display for Diagnostic {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        let severity = self.severity;
87        match &self.location {
88            Some(loc) => write!(f, "{loc}: {severity}: {}", self.message)?,
89            None => write!(f, "{severity}: {}", self.message)?,
90        }
91        for hint in &self.hints {
92            match &hint.location {
93                Some(loc) => write!(f, "\n  hint at {loc}: {}", hint.message)?,
94                None => write!(f, "\n  hint: {}", hint.message)?,
95            }
96        }
97        for site in &self.trace {
98            write!(f, "\n  called from: {site}")?;
99        }
100        Ok(())
101    }
102}
103
104/// Format a list of diagnostics, one per line, for the `Compilation` error.
105fn format_diagnostics(diagnostics: &[Diagnostic]) -> String {
106    diagnostics
107        .iter()
108        .map(|d| d.to_string())
109        .collect::<Vec<_>>()
110        .join("\n")
111}
112
113/// Errors that can occur during document compilation and rendering.
114#[derive(Error, Debug)]
115pub enum Error {
116    /// Entry file was not found in the embedded templates.
117    #[error("entry file not found: {0}")]
118    EntryNotFound(&'static str),
119
120    /// Entry file content is not valid UTF-8.
121    #[error("entry file is not valid UTF-8")]
122    InvalidUtf8,
123
124    /// Typst compilation failed.
125    #[error("compilation failed:\n{}", format_diagnostics(.0))]
126    Compilation(Vec<Diagnostic>),
127
128    /// PDF generation failed.
129    #[error("PDF generation failed: {0}")]
130    PdfGeneration(String),
131
132    /// PNG encoding failed.
133    #[error("PNG encoding failed: {0}")]
134    PngEncoding(String),
135
136    /// Invalid file path provided for runtime file injection.
137    #[error("invalid file path: {0}")]
138    InvalidFilePath(String),
139
140    /// Invalid page selection (empty or out of range).
141    #[error("invalid page selection: {0}")]
142    InvalidPageSelection(String),
143
144    /// Invalid PDF configuration (e.g. a standard/tagging conflict or bad timestamp).
145    #[error("invalid PDF config: {0}")]
146    InvalidPdfConfig(String),
147
148    /// Decompression of embedded content failed.
149    #[error("decompression failed")]
150    Decompression(#[from] std::io::Error),
151}
152
153/// A specialized Result type for typst-bake operations.
154pub type Result<T> = std::result::Result<T, Error>;