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