1use core::{fmt, ops};
2
3use crate::{
4 alloc::{format, vec, String, Vec},
5 TableTag,
6};
7
8#[derive(Debug)]
10#[non_exhaustive]
11pub enum ParseErrorKind {
12 UnexpectedEof,
14 UnexpectedValue {
16 name: &'static str,
18 expected: String,
20 actual: u32,
22 },
23 InvalidCharCode(u32),
25 MissingTable,
27 UnalignedTable,
29 NoSupportedCmap,
31 OffsetOutOfBounds(usize),
33 RangeOutOfBounds {
35 range: ops::Range<usize>,
37 len: usize,
39 },
40 UnexpectedTableLen {
42 expected: usize,
44 actual: usize,
46 },
47 Checksum {
49 expected: u32,
51 actual: u32,
53 },
54 Utf16,
56 UintBase128,
58 UnsupportedWoff2Table {
60 tag: TableTag,
62 transform_bits: u8,
64 },
65 TooLargeFont(usize),
67 BrotliDecompression,
69}
70
71impl fmt::Display for ParseErrorKind {
72 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 Self::UnexpectedEof => formatter.write_str("unexpected end of the font data"),
75 Self::UnexpectedValue {
76 name,
77 expected,
78 actual,
79 } => {
80 write!(
81 formatter,
82 "unexpected value of `{name}`: expected {expected}, got {actual}"
83 )
84 }
85 Self::InvalidCharCode(val) => {
86 write!(formatter, "invalid Unicode char code: {val}")
87 }
88 Self::MissingTable => formatter.write_str("missing required font table"),
89 Self::UnalignedTable => {
90 formatter.write_str("font table is not aligned to a 4-byte boundary")
91 }
92 Self::NoSupportedCmap => {
93 formatter.write_str("no supported subtable in the `cmap` table")
94 }
95 Self::OffsetOutOfBounds(val) => {
96 write!(
97 formatter,
98 "offset ({val}) inferred from the table data is out of bounds"
99 )
100 }
101 Self::RangeOutOfBounds { range, len } => {
102 write!(
103 formatter,
104 "range ({range:?}) inferred from the table data is out of bounds (..{len})"
105 )
106 }
107 Self::UnexpectedTableLen { expected, actual } => {
108 write!(
109 formatter,
110 "unexpected table length: expected {expected}, got {actual}"
111 )
112 }
113 Self::Checksum { expected, actual } => {
114 write!(
115 formatter,
116 "unexpected checksum: expected {expected}, got {actual}"
117 )
118 }
119 Self::Utf16 => formatter.write_str("failed decoding UTF-16 string"),
120 Self::UintBase128 => formatter.write_str("failed decoding uint128 value"),
121 Self::UnsupportedWoff2Table {
122 tag,
123 transform_bits,
124 } => {
125 write!(
126 formatter,
127 "unsupported WOFF2 table tag ({tag}) with transform {transform_bits}"
128 )
129 }
130 Self::TooLargeFont(len) => {
131 write!(formatter, "font size ({len}) is too large to be processed")
132 }
133 Self::BrotliDecompression => formatter.write_str("Brotli decompression error"),
134 }
135 }
136}
137
138#[cfg(feature = "std")]
139impl std::error::Error for ParseErrorKind {}
140
141macro_rules! check_exact {
142 ($val:ident, $expected:expr) => {
143 if $val == $expected {
144 Ok(())
145 } else {
146 Err($crate::ParseErrorKind::UnexpectedValue {
147 name: ::core::stringify!($val),
148 expected: $crate::alloc::ToString::to_string(&$expected),
149 actual: u32::from($val),
150 })
151 }
152 };
153}
154
155#[derive(Debug)]
157pub struct ParseError {
158 pub(crate) kind: ParseErrorKind,
159 pub(crate) offset: usize,
160 pub(crate) table: Option<TableTag>,
161}
162
163impl fmt::Display for ParseError {
164 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
165 if let Some(table) = self.table {
166 write!(formatter, "[{table}] ")?;
167 }
168 if self.offset > 0 {
169 write!(formatter, "{}: ", self.offset)?;
170 }
171 fmt::Display::fmt(&self.kind, formatter)
172 }
173}
174
175#[cfg(feature = "std")]
176impl std::error::Error for ParseError {}
177
178impl ParseError {
179 pub(crate) fn missing_table(tag: TableTag) -> Self {
180 Self {
181 kind: ParseErrorKind::MissingTable,
182 offset: 0,
183 table: Some(tag),
184 }
185 }
186
187 pub fn kind(&self) -> &ParseErrorKind {
189 &self.kind
190 }
191
192 pub fn table(&self) -> Option<TableTag> {
194 self.table
195 }
196
197 pub fn offset(&self) -> usize {
199 self.offset
200 }
201}
202
203#[derive(Debug)]
205#[non_exhaustive]
206pub enum WarningKind {
207 ValueMismatch {
209 name: &'static str,
211 computed: String,
213 recorded: String,
215 },
216}
217
218impl fmt::Display for WarningKind {
219 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
220 match self {
221 Self::ValueMismatch {
222 name,
223 computed,
224 recorded,
225 } => {
226 write!(
227 formatter,
228 "mismatch between computed ({computed}) and recorded ({recorded}) values of `{name}`"
229 )
230 }
231 }
232 }
233}
234
235#[derive(Debug)]
237pub struct Warning {
238 kind: WarningKind,
239 table: Option<TableTag>,
240}
241
242impl fmt::Display for Warning {
243 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
244 if let Some(table) = self.table {
245 write!(formatter, "[{table}] ")?;
246 }
247 fmt::Display::fmt(&self.kind, formatter)
248 }
249}
250
251#[cfg(feature = "std")]
252impl std::error::Error for Warning {}
253
254impl Warning {
255 pub fn kind(&self) -> &WarningKind {
257 &self.kind
258 }
259
260 pub fn table(&self) -> Option<TableTag> {
262 self.table
263 }
264}
265
266#[derive(Debug)]
268pub struct Warnings(Vec<Warning>);
269
270impl fmt::Display for Warnings {
271 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
272 for warn in &self.0 {
273 writeln!(formatter, "- {warn}")?;
274 }
275 Ok(())
276 }
277}
278
279impl IntoIterator for Warnings {
280 type Item = Warning;
281 type IntoIter = vec::IntoIter<Warning>;
282
283 fn into_iter(self) -> Self::IntoIter {
284 self.0.into_iter()
285 }
286}
287
288#[cfg(feature = "std")]
289impl std::error::Error for Warnings {}
290
291impl Warnings {
292 pub(crate) fn empty() -> Self {
293 Self(vec![])
294 }
295
296 pub fn is_empty(&self) -> bool {
298 self.0.is_empty()
299 }
300
301 pub fn len(&self) -> usize {
303 self.0.len()
304 }
305
306 pub fn iter(&self) -> impl ExactSizeIterator<Item = &Warning> + '_ {
308 self.0.iter()
309 }
310
311 pub(crate) fn for_table(&mut self, table: TableTag) -> TableWarnings<'_> {
312 TableWarnings { inner: self, table }
313 }
314
315 pub fn into_result(self) -> Result<(), Self> {
321 if self.0.is_empty() {
322 Ok(())
323 } else {
324 Err(self)
325 }
326 }
327}
328
329#[derive(Debug)]
330pub(crate) struct TableWarnings<'a> {
331 inner: &'a mut Warnings,
332 table: TableTag,
333}
334
335impl TableWarnings<'_> {
336 pub(crate) fn check_match<T: Copy + PartialEq + fmt::Debug>(
337 &mut self,
338 name: &'static str,
339 computed: T,
340 recorded: T,
341 ) {
342 if computed != recorded {
343 self.inner.0.push(Warning {
344 kind: WarningKind::ValueMismatch {
345 name,
346 computed: format!("{computed:?}"),
347 recorded: format!("{recorded:?}"),
348 },
349 table: Some(self.table),
350 });
351 }
352 }
353}