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}
51
52impl fmt::Display for ErrorCode {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 Self::Encoding(code) => write!(f, "{code}"),
56 Self::ForeignReader => f.write_str("reader belongs to another parser"),
57 Self::MissingField => f.write_str("required field absent"),
58 Self::UnexpectedField => f.write_str("field not part of this version"),
59 Self::TrailingData => f.write_str("bytes left over"),
60 Self::EmptySequence => f.write_str("sequence with no members"),
61 Self::DuplicateExtension => f.write_str("extension appears more than once"),
62 Self::MalformedTime => f.write_str("malformed time"),
63 Self::TimeOutOfRange => f.write_str("time names a moment that does not exist"),
64 Self::UnsupportedVersion => f.write_str("unknown version"),
65 Self::UnsupportedNameSyntax => f.write_str("unsupported distinguished-name syntax"),
66 Self::AlgorithmMismatch => {
67 f.write_str("signed and unsigned algorithm identifiers differ")
68 }
69 Self::MalformedValue => f.write_str("value the schema does not allow"),
70 Self::DefaultWritten => f.write_str("default value explicitly encoded"),
71 Self::ValueTooLarge => f.write_str("value too large to represent"),
72 }
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct Error {
79 code: ErrorCode,
80 field: &'static str,
81 offset: u64,
82}
83
84impl Error {
85 #[must_use]
87 pub fn resource(
88 reason: structured_asn1::memory::MemoryError,
89 field: &'static str,
90 offset: u64,
91 ) -> Self {
92 Self::new(ErrorCode::Encoding(reason.into()), field, offset)
93 }
94 #[must_use]
96 pub fn new(code: ErrorCode, field: &'static str, offset: u64) -> Self {
97 Self {
98 code,
99 field,
100 offset,
101 }
102 }
103
104 #[must_use]
106 pub fn code(&self) -> ErrorCode {
107 self.code
108 }
109
110 #[must_use]
112 pub fn field(&self) -> &'static str {
113 self.field
114 }
115
116 #[must_use]
118 pub fn offset(&self) -> u64 {
119 self.offset
120 }
121
122 #[must_use]
127 pub fn is_limit(&self) -> bool {
128 matches!(
129 self.code,
130 ErrorCode::Encoding(
131 structured_asn1::ErrorCode::LimitExceeded(_)
132 | structured_asn1::ErrorCode::TagTooLarge
133 )
134 )
135 }
136
137 #[must_use]
139 pub fn is_resource(&self) -> bool {
140 self.is_limit()
141 || matches!(
142 self.code,
143 ErrorCode::Encoding(
144 structured_asn1::ErrorCode::AllocatorExhausted
145 | structured_asn1::ErrorCode::CapacityOverflow
146 )
147 )
148 }
149}
150
151impl fmt::Display for Error {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "{} in {} at {}", self.code, self.field, self.offset)
154 }
155}
156
157#[cfg(feature = "std")]
158impl std::error::Error for Error {}
159
160pub type Result<T> = core::result::Result<T, Error>;
162
163pub(crate) trait Located<T> {
165 fn field(self, name: &'static str) -> Result<T>;
167}
168
169impl<T> Located<T> for core::result::Result<T, structured_asn1::Error> {
170 fn field(self, name: &'static str) -> Result<T> {
171 self.map_err(|error| Error::new(ErrorCode::Encoding(error.code()), name, error.offset()))
172 }
173}
174
175pub(crate) fn gate<T>(result: Result<T>) -> Result<Option<T>> {
180 match result {
181 Ok(value) => Ok(Some(value)),
182 Err(error) if error.is_resource() => Err(error),
183 Err(_) => Ok(None),
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::{Error, ErrorCode, gate};
190
191 #[test]
192 fn a_tag_header_ceiling_survives_optional_field_probing() {
193 let error = Error::new(
195 ErrorCode::Encoding(structured_asn1::ErrorCode::TagTooLarge),
196 "extension",
197 42,
198 );
199 assert!(error.is_limit());
200 let result: super::Result<Option<()>> = gate(Err(error.clone()));
201 assert_eq!(result.unwrap_err(), error);
202 }
203
204 #[test]
205 fn allocator_failure_is_not_a_document_fault() {
206 let error = Error::new(
209 ErrorCode::Encoding(structured_asn1::ErrorCode::AllocatorExhausted),
210 "extension",
211 42,
212 );
213 let result: super::Result<Option<()>> = gate(Err(error.clone()));
214 assert_eq!(result.unwrap_err(), error);
215 }
216}