1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use thiserror::Error;
/// Errors that can occur during document conversion.
#[derive(Debug, Error)]
pub enum ConvertError {
#[error("unsupported file format: {0}")]
UnsupportedFormat(String),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("parse error: {0}")]
Parse(String),
#[error("render error: {0}")]
Render(String),
#[error("file is encrypted/password-protected and cannot be converted")]
UnsupportedEncryption,
}
/// A non-fatal warning emitted when an element cannot be fully processed.
///
/// Warnings are structured so that callers can programmatically inspect
/// what was degraded during conversion.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub enum ConvertWarning {
/// An element type is not supported and was completely omitted.
UnsupportedElement {
/// Document format (e.g. "DOCX", "PPTX", "XLSX").
format: String,
/// Name or description of the unsupported element.
element: String,
},
/// An element was partially rendered (some features degraded).
PartialElement {
/// Document format (e.g. "DOCX", "PPTX", "XLSX").
format: String,
/// Name or description of the element.
element: String,
/// Detail about what was degraded.
detail: String,
},
/// A fallback representation was used instead of full rendering.
FallbackUsed {
/// Document format (e.g. "DOCX", "PPTX", "XLSX").
format: String,
/// Original element type.
from: String,
/// Fallback representation used.
to: String,
},
/// An element was skipped during parsing.
ParseSkipped {
/// Document format (e.g. "DOCX", "PPTX", "XLSX").
format: String,
/// Reason the element was skipped.
reason: String,
},
}
impl ConvertWarning {
/// Returns the document format associated with this warning.
pub fn format(&self) -> &str {
match self {
Self::UnsupportedElement { format, .. }
| Self::PartialElement { format, .. }
| Self::FallbackUsed { format, .. }
| Self::ParseSkipped { format, .. } => format,
}
}
}
impl std::fmt::Display for ConvertWarning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnsupportedElement { format, element } => {
write!(f, "[{format}] unsupported element: {element}")
}
Self::PartialElement {
format,
element,
detail,
} => {
write!(f, "[{format}] partial rendering of {element}: {detail}")
}
Self::FallbackUsed { format, from, to } => {
write!(f, "[{format}] fallback: {from} rendered as {to}")
}
Self::ParseSkipped { format, reason } => {
write!(f, "[{format}] skipped: {reason}")
}
}
}
}
/// Per-stage timing and size metrics from a conversion.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct ConvertMetrics {
/// Time spent parsing the input document (DOCX/PPTX/XLSX → IR).
#[cfg_attr(feature = "typescript", ts(type = "number"))]
pub parse_duration: std::time::Duration,
/// Time spent generating Typst source code (IR → Typst).
#[cfg_attr(feature = "typescript", ts(type = "number"))]
pub codegen_duration: std::time::Duration,
/// Time spent compiling Typst to PDF (Typst → PDF).
#[cfg_attr(feature = "typescript", ts(type = "number"))]
pub compile_duration: std::time::Duration,
/// Total end-to-end conversion time.
#[cfg_attr(feature = "typescript", ts(type = "number"))]
pub total_duration: std::time::Duration,
/// Size of the input file in bytes.
pub input_size_bytes: u64,
/// Size of the output PDF in bytes.
pub output_size_bytes: u64,
/// Number of pages in the output PDF.
pub page_count: u32,
}
/// Result of a successful conversion, containing PDF bytes and any warnings.
#[derive(Debug)]
pub struct ConvertResult {
/// The generated PDF bytes.
pub pdf: Vec<u8>,
/// Warnings collected during conversion (non-fatal issues).
pub warnings: Vec<ConvertWarning>,
/// Per-stage timing metrics, populated when instrumentation is enabled.
pub metrics: Option<ConvertMetrics>,
}
#[cfg(test)]
#[path = "error_tests.rs"]
mod tests;
#[cfg(all(test, feature = "typescript"))]
#[path = "error_ts_tests.rs"]
mod ts_tests;