oxirush_aper/error.rs
1//! The error of the crate and of the bindings built on it: a kind that a caller matches
2//! on, and a message for the one who reads it.
3
4use alloc::boxed::Box;
5use alloc::format;
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8use core::fmt;
9
10/// What went wrong: the part of an [`Error`] that a caller matches on.
11///
12/// The codec of this crate gives `Decode` and `Encode`. The other kinds are those of
13/// the bindings that are built on it, which have one error type for their messages,
14/// their IEs and their JSON forms.
15///
16/// More kinds may come with a later version: match the ones that matter, and leave the
17/// others to `_`.
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum ErrorKind {
21 /// Octets are not the value that they are decoded as, in aligned PER: they end
22 /// before the value does, go on after it, or hold a value outside its constraint.
23 Decode,
24 /// A value does not encode in aligned PER, as an integer outside its constraint.
25 Encode,
26 /// A PDU that holds another message than the one that it is decoded as, and the
27 /// protocol IEs of a PDU whose message has none.
28 WrongMessage,
29 /// A required IE that the message does not have.
30 MissingIe,
31 /// An IE, or a transfer, that is there and does not decode as its type.
32 UndecodableIe,
33 /// An argument that is not what a function takes, as an MCC of two digits.
34 Argument,
35 /// A name of a message, or a direction and a procedure code, that the protocol
36 /// does not have.
37 UnknownMessage,
38 /// A name or an identifier of an IE that the protocol does not have, or has no
39 /// type for where it is.
40 UnknownIe,
41 /// A path that is not well formed.
42 Path,
43 /// A name that the type at that place cannot have: a member of a value, an IE of
44 /// a message, or a name where a list goes by position.
45 NoSuchMember,
46 /// An edit whose path selects nothing.
47 NothingSelected,
48 /// A path through the type that octets carry, before they were opened.
49 NotOpened,
50 /// A JSON value that is not a value of its ASN.1 type: of another form, with a
51 /// name that an ENUMERATED does not have, or outside a constraint.
52 Value,
53 /// JSON that is not written as a tree or as a view.
54 Form,
55 /// An edit that is refused: a member that its type always has is not taken out,
56 /// and the octets of a value that was edited since are not read.
57 Edit,
58 /// Nesting or a number of occurrences over the limits of the inspection, or
59 /// what it does not represent.
60 Limit,
61}
62
63/// The criticality of an IE or of a procedure in the application protocols of 3GPP, as
64/// their ASN.1 has it (`Criticality ::= ENUMERATED { reject, ignore, notify }`): what a
65/// receiver does with what it does not comprehend. The bindings of a protocol have a
66/// type of their own for it, which converts to and from this one.
67#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
68pub enum Criticality {
69 /// The receiver rejects the procedure.
70 Reject,
71 /// The receiver ignores the IE.
72 Ignore,
73 /// The receiver ignores the IE, and notifies the sender.
74 Notify,
75}
76
77/// The IE of a message that an error is about: [`Error::ie`] of an error of the kind
78/// `MissingIe` or `UndecodableIe` that the bindings of a protocol made.
79///
80/// It is what the Criticality Diagnostics of an answer say of an IE (3GPP TS 38.413
81/// clauses 10.3.4 and 10.3.5, TS 36.413 clause 10.3): its identifier, and its
82/// criticality.
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
84#[non_exhaustive]
85pub struct ErrorIe {
86 /// The identifier of the IE.
87 pub id: u16,
88 /// Its criticality: the one that it was received with, for an IE that is there,
89 /// and the one that the object set of the message assigns it, for an IE that is
90 /// missing.
91 pub criticality: Criticality,
92 /// Its position among the IEs of the message, which starts at 0; `None` for an IE
93 /// that is missing.
94 pub position: Option<usize>,
95}
96
97impl ErrorIe {
98 /// The IE of an identifier and a criticality, at a position when it is there.
99 pub fn new(id: u16, criticality: Criticality, position: Option<usize>) -> Self {
100 Self {
101 id,
102 criticality,
103 position,
104 }
105 }
106}
107
108/// The error of every function of the crate that can fail, and of the bindings that
109/// are built on it.
110///
111/// [`kind`](Self::kind) is what a caller matches on. `Display` says what is wrong, in
112/// the words of the specification where it has them, then why, and for octets that do
113/// not decode the field of the bindings where the decoding stopped:
114///
115/// ```
116/// use oxirush_aper::{ErrorKind, Reader};
117///
118/// // A number of the constraint (0..65535) is two octets, and one is there
119/// let error = Reader::new(&[0x12]).unsigned(0, 65535).unwrap_err();
120/// assert_eq!(error.kind(), ErrorKind::Decode);
121/// assert_eq!(
122/// error.to_string(),
123/// "the octets do not decode: the octets end before the value does"
124/// );
125/// ```
126///
127/// Two errors are equal when they have the same kind and the same message. An error is
128/// one pointer, so that a `Result` of a small value stays small.
129#[derive(Clone)]
130pub struct Error(Box<Inner>);
131
132#[derive(Clone)]
133struct Inner {
134 kind: ErrorKind,
135 /// What is wrong, and after it the reason, where the message has one apart: what
136 /// the codec says of octets or of a value.
137 what: String,
138 why: Option<String>,
139 /// The fields of the bindings on the way to where a decoding stopped, the
140 /// innermost first.
141 fields: Vec<&'static str>,
142 /// The IE of a message that the error is about.
143 ie: Option<ErrorIe>,
144}
145
146impl Error {
147 /// An error of a kind, which says `what` is wrong.
148 pub fn new(kind: ErrorKind, what: impl Into<String>) -> Self {
149 Self(Box::new(Inner {
150 kind,
151 what: what.into(),
152 why: None,
153 fields: Vec::new(),
154 ie: None,
155 }))
156 }
157
158 /// What went wrong, as a caller matches on it.
159 pub fn kind(&self) -> ErrorKind {
160 self.0.kind
161 }
162
163 /// The same error, with the reason of what is wrong, which the message has after
164 /// it.
165 #[must_use]
166 pub fn because(mut self, why: impl Into<String>) -> Self {
167 self.0.why = Some(why.into());
168 self
169 }
170
171 /// The same error, told of `what`: what it said is wrong becomes the reason, where
172 /// it gave none apart.
173 #[must_use]
174 pub fn of(mut self, what: impl Into<String>) -> Self {
175 let was = core::mem::replace(&mut self.0.what, what.into());
176 if self.0.why.is_none() {
177 self.0.why = Some(was);
178 }
179 self
180 }
181
182 /// The same error, of another kind.
183 #[must_use]
184 pub fn with_kind(mut self, kind: ErrorKind) -> Self {
185 self.0.kind = kind;
186 self
187 }
188
189 /// The IE of a message that the error is about, when it is about one: an error of
190 /// the kind `MissingIe` or `UndecodableIe` that the bindings of a protocol made.
191 ///
192 /// ```
193 /// use oxirush_aper::{Criticality, Error, ErrorIe};
194 ///
195 /// // The IE 21 of an NG SETUP REQUEST, received with the criticality `ignore`
196 /// let ie = ErrorIe::new(21, Criticality::Ignore, Some(3));
197 /// let error = Error::undecodable("the DefaultPagingDRX does not decode").about_ie(ie);
198 /// assert_eq!(error.ie(), Some(ie));
199 /// assert_eq!(Error::argument("an MCC has three digits").ie(), None);
200 /// ```
201 pub fn ie(&self) -> Option<ErrorIe> {
202 self.0.ie
203 }
204
205 /// The same error, about an IE of a message.
206 #[must_use]
207 pub fn about_ie(mut self, ie: ErrorIe) -> Self {
208 self.0.ie = Some(ie);
209 self
210 }
211
212 /// The same error, with the message that `reword` makes of the one that it has.
213 #[must_use]
214 pub fn reworded(self, reword: impl FnOnce(&str) -> String) -> Self {
215 let mut reworded = Self::new(self.0.kind, reword(&self.to_string()));
216 reworded.0.ie = self.0.ie;
217 reworded
218 }
219
220 /// The reason of what is wrong, where the message has one apart, and what is wrong
221 /// otherwise, with the field of the bindings where a decoding stopped.
222 pub fn reason(&self) -> String {
223 let mut reason = self.0.why.clone().unwrap_or_else(|| self.0.what.clone());
224 self.write_fields(&mut reason);
225 reason
226 }
227
228 /// The fields of the bindings on the way to where a decoding stopped, the outermost
229 /// first: none for an error that is no decoding error, or that stopped in no field.
230 pub fn fields(&self) -> impl Iterator<Item = &'static str> + '_ {
231 self.0.fields.iter().rev().copied()
232 }
233
234 /// The same error, which was met in the field `name` of the bindings. A decoder of a
235 /// SEQUENCE names the member that did not decode.
236 #[cold]
237 #[must_use]
238 pub fn in_field(mut self, name: &'static str) -> Self {
239 self.0.fields.push(name);
240 self
241 }
242
243 /// Octets that are not a value of the type that ASN.1 names `name`: this error is
244 /// why.
245 #[cold]
246 #[must_use]
247 pub fn decoding(mut self, name: &str) -> Self {
248 let what = format!("these octets do not decode as {name}");
249 let was = core::mem::replace(&mut self.0.what, what);
250 if self.0.why.is_none() {
251 self.0.why = Some(was);
252 }
253 self
254 }
255
256 /// A value of the type that ASN.1 names `name` that does not encode: this error is
257 /// why.
258 #[cold]
259 #[must_use]
260 pub fn encoding(mut self, name: &str) -> Self {
261 let what = format!("a value of {name} does not encode");
262 let was = core::mem::replace(&mut self.0.what, what);
263 if self.0.why.is_none() {
264 self.0.why = Some(was);
265 }
266 self
267 }
268
269 /// Octets that do not decode, and why.
270 #[cold]
271 pub(crate) fn decode(why: impl Into<String>) -> Self {
272 Self::new(ErrorKind::Decode, "the octets do not decode").because(why)
273 }
274
275 /// A value that does not encode, and why.
276 #[cold]
277 pub(crate) fn encode(why: impl Into<String>) -> Self {
278 Self::new(ErrorKind::Encode, "the value does not encode").because(why)
279 }
280
281 /// The octets end before the value does.
282 #[cold]
283 pub(crate) fn ended() -> Self {
284 Self::decode("the octets end before the value does")
285 }
286
287 /// A number that is read outside its constraint.
288 #[cold]
289 pub(crate) fn number_read_outside(value: i128, lower: i128, upper: i128) -> Self {
290 Self::decode(outside(value, lower, upper))
291 }
292
293 /// A number that is written outside its constraint.
294 #[cold]
295 pub(crate) fn number_written_outside(value: i128, lower: i128, upper: i128) -> Self {
296 Self::encode(outside(value, lower, upper))
297 }
298
299 /// A size that is read outside its constraint.
300 #[cold]
301 pub(crate) fn number_written_outside_root(value: i128, lower: i128, upper: i128) -> Self {
302 Self::encode(format!(
303 "{value} is outside the root of its constraint ({lower}..{upper}, ...)"
304 ))
305 }
306
307 #[cold]
308 pub(crate) fn size_written_outside_root(size: usize, lower: usize, upper: usize) -> Self {
309 Self::encode(match lower == upper {
310 true => {
311 format!("a size of {size} is outside the root of its constraint ({lower}, ...)")
312 }
313 false => format!(
314 "a size of {size} is outside the root of its constraint ({lower}..{upper}, ...)"
315 ),
316 })
317 }
318
319 #[cold]
320 pub(crate) fn size_read_outside(size: usize, lower: usize, upper: Option<usize>) -> Self {
321 Self::decode(size_outside(size, lower, upper))
322 }
323
324 /// A size that is written outside its constraint.
325 #[cold]
326 pub(crate) fn size_written_outside(size: usize, lower: usize, upper: Option<usize>) -> Self {
327 Self::encode(size_outside(size, lower, upper))
328 }
329
330 /// A number that is read and that the type of the bindings does not hold.
331 #[cold]
332 pub(crate) fn does_not_fit(bits: u32) -> Self {
333 Self::decode(format!("the number does not fit in {bits} bits"))
334 }
335
336 fn write_fields(&self, text: &mut String) {
337 if self.0.fields.is_empty() {
338 return;
339 }
340 text.push_str(", in its field `");
341 for (at, field) in self.fields().enumerate() {
342 if at > 0 {
343 text.push('.');
344 }
345 text.push_str(field);
346 }
347 text.push('`');
348 }
349}
350
351/// The errors that the bindings of an application protocol make of a PDU, of its message
352/// and of its IEs, in one wording for every protocol. A program that uses bindings
353/// matches on the [`kind`](Error::kind) of their errors, and makes none of these.
354impl Error {
355 /// A PDU of another message than the one that ASN.1 names `expected`: `held` is the
356 /// name of the message of its `direction` and of the procedure `code`, when the
357 /// specification has one.
358 pub fn wrong_message(expected: &str, held: Option<&str>, direction: &str, code: u8) -> Self {
359 let message = match held {
360 Some(held) => format!(
361 "the message of this PDU is {held}, not {expected}: kind() says which \
362 message a PDU holds"
363 ),
364 None => format!(
365 "the message of this PDU is not {expected}: the specification has no \
366 {direction} of procedure {code}"
367 ),
368 };
369 Self::new(ErrorKind::WrongMessage, message)
370 }
371
372 /// A PDU of the message that ASN.1 names `name`, which has no container of
373 /// protocol IEs.
374 pub fn no_protocol_ies(name: &str) -> Self {
375 let what =
376 format!("the message of this PDU is {name}, which has no container of protocol IEs");
377 Self::new(ErrorKind::WrongMessage, what)
378 }
379
380 /// A PDU whose direction and procedure code the specification has no message for,
381 /// and so no object set.
382 pub fn no_message_of(direction: &str, code: u8) -> Self {
383 let what = format!(
384 "the specification has no {direction} of procedure {code}, and so no object set \
385 for its IEs"
386 );
387 Self::new(ErrorKind::UnknownMessage, what)
388 }
389
390 /// This error is why the message of a PDU does not decode as a container of
391 /// protocol IEs: the message that ASN.1 names `name`, when the specification has
392 /// one for the PDU.
393 #[must_use]
394 pub fn of_message(self, name: Option<&str>) -> Self {
395 const UNNAMED: &str =
396 "the message of this PDU does not decode as a container of protocol IEs";
397 self.of(match name {
398 Some(name) => format!("the {name} of this PDU does not decode"),
399 None => UNNAMED.to_string(),
400 })
401 }
402
403 /// This error is why `count` protocol IEs do not encode as a message.
404 #[must_use]
405 pub fn of_protocol_ies(self, count: usize) -> Self {
406 self.of(format!(
407 "these {count} protocol IEs do not encode as a message"
408 ))
409 }
410
411 /// The IE that a message names `ie`, which the field `field` requires, is not in a
412 /// message of the type `message`.
413 ///
414 /// ```
415 /// use oxirush_aper::{Error, ErrorKind};
416 ///
417 /// let error = Error::missing_ie("NGSetupRequest", "SupportedTAList", "areas");
418 /// assert_eq!(error.kind(), ErrorKind::MissingIe);
419 /// assert_eq!(
420 /// error.to_string(),
421 /// "this NGSetupRequest has no SupportedTAList, which the field `areas` requires"
422 /// );
423 /// ```
424 pub fn missing_ie(message: &str, ie: &str, field: &str) -> Self {
425 let what = format!("this {message} has no {ie}, which the field `{field}` requires");
426 Self::new(ErrorKind::MissingIe, what)
427 }
428
429 /// This error is why the IE that a message names `ie` does not decode, in a message
430 /// of the type `message`.
431 ///
432 /// ```
433 /// use oxirush_aper::{ErrorKind, Reader};
434 ///
435 /// // An ENUMERATED of four values and an extension marker, read from no octet
436 /// let codec = Reader::new(&[]).enumerated(4, 0, true).unwrap_err();
437 /// let error = codec.undecodable_ie("NGSetupRequest", "DefaultPagingDRX");
438 /// assert_eq!(error.kind(), ErrorKind::UndecodableIe);
439 /// assert_eq!(
440 /// error.to_string(),
441 /// "the DefaultPagingDRX of this NGSetupRequest does not decode: the octets end before \
442 /// the value does"
443 /// );
444 /// ```
445 #[must_use]
446 pub fn undecodable_ie(self, message: &str, ie: &str) -> Self {
447 // What the codec said of the octets, without the type that it took them for.
448 self.of(format!("the {ie} of this {message} does not decode"))
449 .with_kind(ErrorKind::UndecodableIe)
450 }
451
452 /// A value of the type that ASN.1 names `name` that is written in JSON and does
453 /// not encode: it is outside a constraint of its type, and so no value of that
454 /// type. This error is what the codec said of it.
455 #[must_use]
456 pub fn outside_its_type(self, name: &str) -> Self {
457 self.of(format!("this is not a value of {name}"))
458 .with_kind(ErrorKind::Value)
459 }
460}
461
462/// Why a number is outside its constraint.
463fn outside(value: i128, lower: i128, upper: i128) -> String {
464 format!("{value} is outside its constraint ({lower}..{upper})")
465}
466
467/// Why a size is outside its constraint.
468fn size_outside(size: usize, lower: usize, upper: Option<usize>) -> String {
469 match upper {
470 Some(upper) if upper == lower => {
471 format!("a size of {size} is outside its constraint ({lower})")
472 }
473 Some(upper) => format!("a size of {size} is outside its constraint ({lower}..{upper})"),
474 None => format!("a size of {size} is outside its constraint ({lower}..MAX)"),
475 }
476}
477
478/// An error of each kind, with what is wrong.
479macro_rules! kinds {
480 ($($(#[$doc:meta])* $name:ident $kind:ident;)*) => {
481 impl Error {
482 $(
483 $(#[$doc])*
484 pub fn $name(what: impl Into<String>) -> Self {
485 Self::new(ErrorKind::$kind, what)
486 }
487 )*
488 }
489 };
490}
491
492kinds! {
493 /// An argument that a function does not take, and what it takes.
494 argument Argument;
495 /// A name of a message, or a direction and a procedure code, that the protocol
496 /// does not have.
497 unknown_message UnknownMessage;
498 /// A name or an identifier of an IE that the protocol does not have, or has no
499 /// type for where it is.
500 unknown_ie UnknownIe;
501 /// A path that is not well formed.
502 path Path;
503 /// A name that the type at that place cannot have.
504 no_such_member NoSuchMember;
505 /// An edit whose path selects nothing.
506 nothing_selected NothingSelected;
507 /// A path through the type that octets carry, before they are opened.
508 not_opened NotOpened;
509 /// A path into an IE or a transfer that is there and did not decode.
510 undecodable UndecodableIe;
511 /// A JSON value that is not a value of its ASN.1 type.
512 value Value;
513 /// JSON that is not written as a tree or as a view.
514 form Form;
515 /// An edit that is refused.
516 edit Edit;
517 /// Nesting or a number of occurrences over the limits of the inspection.
518 limit Limit;
519}
520
521impl fmt::Display for Error {
522 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
523 let mut text = self.0.what.to_string();
524 if let Some(why) = &self.0.why {
525 text.push_str(": ");
526 text.push_str(why);
527 }
528 self.write_fields(&mut text);
529 formatter.write_str(&text)
530 }
531}
532
533impl fmt::Debug for Error {
534 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
535 let mut error = formatter.debug_struct("Error");
536 error.field("kind", &self.0.kind);
537 error.field("message", &self.to_string());
538 error.finish()
539 }
540}
541
542impl core::error::Error for Error {}
543
544impl PartialEq for Error {
545 fn eq(&self, other: &Self) -> bool {
546 self.0.kind == other.0.kind && self.to_string() == other.to_string()
547 }
548}
549
550impl Eq for Error {}
551
552#[cfg(test)]
553mod tests {
554 use super::*;
555
556 #[test]
557 fn an_error_is_cloned_sent_and_compared_by_its_kind_and_its_message() {
558 fn sent<T: Clone + Send + Sync + core::error::Error + 'static>() {}
559 sent::<Error>();
560 let error = Error::new(ErrorKind::Argument, "an MCC has three digits");
561 assert_eq!(error.kind(), ErrorKind::Argument);
562 assert_eq!(error.to_string(), "an MCC has three digits");
563 assert_eq!(error, error.clone());
564 assert_ne!(
565 error,
566 Error::new(ErrorKind::Value, "an MCC has three digits")
567 );
568 assert_ne!(error, Error::new(ErrorKind::Argument, "an MNC has two"));
569 let shown = format!("{error:?}");
570 assert_eq!(
571 shown,
572 "Error { kind: Argument, message: \"an MCC has three digits\" }"
573 );
574 // One pointer, and a result has a number beside it: 16 octets on 64 bits, 12 on
575 // the 32 bits of x86, where a number of 64 bits is aligned on 4 octets.
576 assert_eq!(size_of::<Error>(), size_of::<usize>());
577 let beside = size_of::<u64>() + size_of::<usize>();
578 assert_eq!(
579 size_of::<Result<u64, Error>>(),
580 beside.next_multiple_of(align_of::<u64>())
581 );
582 }
583
584 #[test]
585 fn a_decoding_error_names_its_type_and_the_field_where_it_stopped() {
586 let error = Error::ended().in_field("id").in_field("protocol_ies");
587 assert_eq!(error.kind(), ErrorKind::Decode);
588 assert_eq!(
589 error.to_string(),
590 "the octets do not decode: the octets end before the value does, in its field \
591 `protocol_ies.id`"
592 );
593 let named = error.clone().decoding("NGAP-PDU");
594 assert_eq!(
595 named.to_string(),
596 "these octets do not decode as NGAP-PDU: the octets end before the value does, in \
597 its field `protocol_ies.id`"
598 );
599 assert_eq!(named.fields().collect::<Vec<_>>(), ["protocol_ies", "id"]);
600 // Told of something else, what the codec said is the reason.
601 let told = named.of("the Cause of this message does not decode");
602 assert_eq!(
603 told.to_string(),
604 "the Cause of this message does not decode: the octets end before the value does, \
605 in its field `protocol_ies.id`"
606 );
607 assert_eq!(
608 told.reason(),
609 "the octets end before the value does, in its field `protocol_ies.id`"
610 );
611 let of_a_kind = told.with_kind(ErrorKind::UndecodableIe);
612 assert_eq!(of_a_kind.kind(), ErrorKind::UndecodableIe);
613 }
614
615 #[test]
616 fn a_constraint_is_told_as_asn1_writes_it() {
617 assert_eq!(
618 Error::number_read_outside(4096, 0, 4095).reason(),
619 "4096 is outside its constraint (0..4095)"
620 );
621 assert_eq!(
622 Error::number_written_outside(-9, -8, 7).to_string(),
623 "the value does not encode: -9 is outside its constraint (-8..7)"
624 );
625 assert_eq!(
626 Error::size_read_outside(4, 3, Some(3)).reason(),
627 "a size of 4 is outside its constraint (3)"
628 );
629 assert_eq!(
630 Error::size_written_outside(0, 1, Some(150)).reason(),
631 "a size of 0 is outside its constraint (1..150)"
632 );
633 assert_eq!(
634 Error::size_read_outside(0, 1, None).reason(),
635 "a size of 0 is outside its constraint (1..MAX)"
636 );
637 }
638}