1use core::fmt;
9
10mod callback;
11pub use callback::{CallbackError, CallbackResult};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum ErrorCode {
17 Encoding(structured_asn1::ErrorCode),
19 ForeignReader,
21 MissingField,
23 UnexpectedField,
25 TrailingData,
27 EmptySequence,
29 DuplicateExtension,
32 MalformedTime,
35 TimeOutOfRange,
37 UnsupportedVersion,
39 UnsupportedNameSyntax,
41 AlgorithmMismatch,
44 MalformedValue,
46 DefaultWritten,
48 ValueTooLarge,
50 DigestOutput,
53}
54
55impl fmt::Display for ErrorCode {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 Self::Encoding(code) => write!(f, "{code}"),
59 Self::ForeignReader => f.write_str("reader belongs to another parser"),
60 Self::MissingField => f.write_str("required field absent"),
61 Self::UnexpectedField => f.write_str("field not part of this version"),
62 Self::TrailingData => f.write_str("bytes left over"),
63 Self::EmptySequence => f.write_str("sequence with no members"),
64 Self::DuplicateExtension => f.write_str("extension appears more than once"),
65 Self::MalformedTime => f.write_str("malformed time"),
66 Self::TimeOutOfRange => f.write_str("time names a moment that does not exist"),
67 Self::UnsupportedVersion => f.write_str("unknown version"),
68 Self::UnsupportedNameSyntax => f.write_str("unsupported distinguished-name syntax"),
69 Self::AlgorithmMismatch => {
70 f.write_str("signed and unsigned algorithm identifiers differ")
71 }
72 Self::MalformedValue => f.write_str("value the schema does not allow"),
73 Self::DefaultWritten => f.write_str("default value explicitly encoded"),
74 Self::ValueTooLarge => f.write_str("value too large to represent"),
75 Self::DigestOutput => f.write_str("digest output contradicts its algorithm's width"),
76 }
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct Error {
83 code: ErrorCode,
84 field: &'static str,
85 offset: u64,
86}
87
88impl Error {
89 #[must_use]
91 pub fn resource(
92 reason: structured_asn1::memory::MemoryError,
93 field: &'static str,
94 offset: u64,
95 ) -> Self {
96 Self::new(ErrorCode::Encoding(reason.into()), field, offset)
97 }
98 #[must_use]
100 pub fn new(code: ErrorCode, field: &'static str, offset: u64) -> Self {
101 Self {
102 code,
103 field,
104 offset,
105 }
106 }
107
108 #[must_use]
110 pub fn code(&self) -> ErrorCode {
111 self.code
112 }
113
114 #[must_use]
116 pub fn field(&self) -> &'static str {
117 self.field
118 }
119
120 #[must_use]
122 pub fn offset(&self) -> u64 {
123 self.offset
124 }
125
126 #[must_use]
131 pub fn is_limit(&self) -> bool {
132 matches!(
133 self.code,
134 ErrorCode::Encoding(
135 structured_asn1::ErrorCode::LimitExceeded(_)
136 | structured_asn1::ErrorCode::TagTooLarge
137 )
138 )
139 }
140
141 #[must_use]
143 pub fn is_resource(&self) -> bool {
144 self.is_limit()
145 || matches!(
146 self.code,
147 ErrorCode::Encoding(
148 structured_asn1::ErrorCode::AllocatorExhausted
149 | structured_asn1::ErrorCode::CapacityOverflow
150 )
151 )
152 }
153}
154
155impl fmt::Display for Error {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(f, "{} in {} at {}", self.code, self.field, self.offset)
158 }
159}
160
161#[cfg(feature = "std")]
162impl std::error::Error for Error {}
163
164pub type Result<T> = core::result::Result<T, Error>;
166
167pub(crate) trait Located<T> {
169 fn field(self, name: &'static str) -> Result<T>;
171}
172
173impl<T> Located<T> for core::result::Result<T, structured_asn1::Error> {
174 fn field(self, name: &'static str) -> Result<T> {
175 self.map_err(|error| Error::new(ErrorCode::Encoding(error.code()), name, error.offset()))
176 }
177}
178
179pub(crate) fn gate<T>(result: Result<T>) -> Result<Option<T>> {
184 match result {
185 Ok(value) => Ok(Some(value)),
186 Err(error) if error.is_resource() => Err(error),
187 Err(_) => Ok(None),
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::{Error, ErrorCode, gate};
194
195 #[test]
196 fn a_tag_header_ceiling_survives_optional_field_probing() {
197 let error = Error::new(
199 ErrorCode::Encoding(structured_asn1::ErrorCode::TagTooLarge),
200 "extension",
201 42,
202 );
203 assert!(error.is_limit());
204 let result: super::Result<Option<()>> = gate(Err(error.clone()));
205 assert_eq!(result.unwrap_err(), error);
206 }
207
208 #[test]
209 fn allocator_failure_is_not_a_document_fault() {
210 let error = Error::new(
213 ErrorCode::Encoding(structured_asn1::ErrorCode::AllocatorExhausted),
214 "extension",
215 42,
216 );
217 let result: super::Result<Option<()>> = gate(Err(error.clone()));
218 assert_eq!(result.unwrap_err(), error);
219 }
220}