Skip to main content

structured_pades/
error.rs

1//! Located syntax, ambiguity, unsupported capability and processing refusals.
2
3use crate::source::Location;
4use core::fmt;
5use structured_asn1::memory::MemoryError;
6
7/// A concrete PDF grammar or document-structure violation.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Syntax {
10    /// Missing or malformed file header.
11    Header,
12    /// Unexpected token for the selected grammar.
13    Token,
14    /// Value or delimiter extends beyond its original source.
15    End,
16    /// Invalid integer or real spelling.
17    Number,
18    /// Invalid name escape or forbidden zero byte.
19    Name,
20    /// Invalid literal or hexadecimal string.
21    String,
22    /// Invalid dictionary entry or conflicting duplicate key.
23    Dictionary,
24    /// Invalid stream extent, encoding or decoding parameters.
25    Stream,
26    /// Invalid indirect object definition or reference identity.
27    Object,
28    /// Invalid cross-reference section or object location.
29    CrossReference,
30    /// Invalid terminal startxref record.
31    StartXref,
32    /// Invalid document trailer.
33    Trailer,
34    /// The carried signature container has the wrong format or extent.
35    Container,
36    /// Reference or cross-reference chain returns to itself.
37    Cycle,
38}
39
40/// Caller-selected mechanism limit, separate from malformed syntax.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum Limit {
43    /// Container or resolution nesting limit.
44    Depth,
45    /// Cumulative original and decoded object count.
46    Objects,
47    /// Cumulative revision count.
48    Revisions,
49    /// Cumulative decoded output allowance.
50    Decoded,
51}
52
53/// An explicitly unavailable processing capability.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum Unsupported {
56    /// Requested stream filter has no selected structural decoder.
57    Filter,
58    /// External stream bytes were not supplied by a caller.
59    ExternalStream,
60    /// Required encrypted source bytes are unavailable.
61    Encryption,
62    /// A number cannot be represented for this particular query.
63    NumericRange,
64}
65
66/// Independent failure classes; processing causes cannot become syntax faults.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum ErrorKind {
69    /// Unreadable syntax with its actual domain and range.
70    Syntax(Syntax),
71    /// Two incompatible readings; selecting the first would lose meaning.
72    Ambiguous(Syntax),
73    /// Caller-selected resource count exhausted before the operation.
74    Limit(Limit),
75    /// Actual shared allocator, capacity, or current/source work refusal.
76    Resource(MemoryError),
77    /// Required capability is explicitly unavailable.
78    Unsupported(Unsupported),
79    /// A caller mixed source/session/document identities.
80    Foreign,
81}
82
83/// Failure preserving both logical coordinates and honest physical enclosure.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub struct Error {
86    /// Original failure classification and cause.
87    pub kind: ErrorKind,
88    /// Original range, with its source discriminator.
89    pub location: Location,
90}
91impl Error {
92    /// Processing and source misuse cannot become a recoverable syntax slot.
93    #[must_use]
94    pub fn is_terminal(&self) -> bool {
95        matches!(
96            self.kind,
97            ErrorKind::Limit(_) | ErrorKind::Resource(_) | ErrorKind::Foreign
98        )
99    }
100}
101
102impl fmt::Display for Error {
103    fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(output, "{:?} at {:?}", self.kind, self.location)
105    }
106}
107
108#[cfg(feature = "std")]
109impl std::error::Error for Error {}
110
111/// Recoverable original format fault, never an allocator/work/processing cause.
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub struct Fault {
114    /// Specific readable format violation.
115    pub code: Syntax,
116    /// Original source location.
117    pub location: Location,
118}
119
120/// Result of a PDF mechanism operation.
121pub type Result<T> = core::result::Result<T, Error>;
122
123/// A format operation may fail independently in PDF processing or in the
124/// caller's asynchronous source. Opaque causes are never syntax diagnostics.
125#[derive(Debug)]
126pub enum ReadFailure<E> {
127    /// Original located PDF syntax, ambiguity, resource or capability cause.
128    Format(Error),
129    /// Original source snapshot/acquisition/resource cause and range.
130    Input(crate::range::InputError<E>),
131}
132impl<E> ReadFailure<E> {
133    /// Opaque acquisition, resource and source-identity refusals are terminal.
134    #[must_use]
135    pub fn is_terminal(&self) -> bool {
136        match self {
137            Self::Format(cause) => cause.is_terminal(),
138            Self::Input(_) => true,
139        }
140    }
141}
142
143impl<E> From<Error> for ReadFailure<E> {
144    fn from(error: Error) -> Self {
145        Self::Format(error)
146    }
147}
148
149impl<E> From<crate::range::InputError<E>> for ReadFailure<E> {
150    fn from(error: crate::range::InputError<E>) -> Self {
151        Self::Input(error)
152    }
153}