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