Skip to main content

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}