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