Skip to main content

otf_pdf_syntax/
error.rs

1//! The syntax layer's error type.
2
3use std::fmt;
4
5use crate::limits::LimitKind;
6use crate::object::ObjId;
7use crate::source::SourceError;
8
9/// Everything that can go wrong in the syntax layer.
10#[derive(Debug, Clone, PartialEq)]
11pub enum Error {
12    /// The source failed to serve a read.
13    Source(SourceError),
14    /// Malformed syntax that could not be (or, in strict mode, was not
15    /// allowed to be) repaired.
16    Syntax {
17        /// Byte offset in the file or in the buffer being parsed.
18        offset: u64,
19        /// What was expected or found.
20        message: &'static str,
21    },
22    /// The input ended inside an object.
23    UnexpectedEof,
24    /// A resource limit was exceeded.
25    Limit(LimitKind),
26    /// No xref and no objects could be found.
27    NoXref,
28    /// The trailer has no usable `/Root`.
29    NoRoot,
30    /// An object is not in the xref, or cannot be found where it points.
31    MissingObject(ObjId),
32    /// An object had a different type than required.
33    WrongType {
34        /// What was required.
35        expected: &'static str,
36        /// What was found.
37        found: &'static str,
38    },
39    /// A stream's filters failed.
40    Filter(&'static str),
41    /// The file uses an encryption scheme this library cannot read.
42    UnsupportedEncryption(String),
43    /// Neither the user nor the owner password matched.
44    InvalidPassword,
45}
46
47impl Error {
48    /// A syntax error at `offset`.
49    pub fn syntax(offset: u64, message: &'static str) -> Self {
50        Self::Syntax { offset, message }
51    }
52}
53
54impl fmt::Display for Error {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Self::Source(e) => e.fmt(f),
58            Self::Syntax { offset, message } => {
59                write!(f, "syntax error at byte {offset}: {message}")
60            }
61            Self::UnexpectedEof => f.write_str("unexpected end of input"),
62            Self::Limit(kind) => write!(f, "{kind} limit exceeded"),
63            Self::NoXref => f.write_str("no cross-reference data or objects found"),
64            Self::NoRoot => f.write_str("trailer has no document catalog (/Root)"),
65            Self::MissingObject(id) => write!(f, "object {} {} not found", id.num, id.generation),
66            Self::WrongType { expected, found } => {
67                write!(f, "expected {expected}, found {found}")
68            }
69            Self::Filter(msg) => write!(f, "stream decoding failed: {msg}"),
70            Self::UnsupportedEncryption(msg) => write!(f, "unsupported encryption: {msg}"),
71            Self::InvalidPassword => f.write_str("incorrect password"),
72        }
73    }
74}
75
76impl std::error::Error for Error {
77    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
78        match self {
79            Self::Source(e) => Some(e),
80            _ => None,
81        }
82    }
83}
84
85impl From<SourceError> for Error {
86    fn from(e: SourceError) -> Self {
87        Self::Source(e)
88    }
89}
90
91/// The syntax layer's result type.
92pub type Result<T> = std::result::Result<T, Error>;
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn display() {
100        assert_eq!(
101            Error::syntax(5, "expected dictionary").to_string(),
102            "syntax error at byte 5: expected dictionary"
103        );
104        assert_eq!(
105            Error::Limit(LimitKind::Depth).to_string(),
106            "nesting depth limit exceeded"
107        );
108        assert_eq!(
109            Error::MissingObject(ObjId::new(3, 0)).to_string(),
110            "object 3 0 not found"
111        );
112        assert!(std::error::Error::source(&Error::Source(SourceError::new("x"))).is_some());
113        assert!(std::error::Error::source(&Error::NoRoot).is_none());
114    }
115}