rasn/error/
encode.rs

1use crate::prelude::Integer;
2use crate::types::constraints::{Bounded, Size};
3use snafu::Snafu;
4#[cfg(feature = "backtraces")]
5use snafu::{Backtrace, GenerateImplicitData};
6
7use alloc::{boxed::Box, string::ToString};
8
9/// Variants for every codec-specific `EncodeError` kind.
10#[derive(Debug)]
11#[non_exhaustive]
12pub enum CodecEncodeError {
13    Ber(BerEncodeErrorKind),
14    Cer(CerEncodeErrorKind),
15    Der(DerEncodeErrorKind),
16    Uper(UperEncodeErrorKind),
17    Aper(AperEncodeErrorKind),
18    Jer(JerEncodeErrorKind),
19    Coer(CoerEncodeErrorKind),
20}
21macro_rules! impl_from {
22    ($variant:ident, $error_kind:ty) => {
23        impl From<$error_kind> for EncodeError {
24            fn from(error: $error_kind) -> Self {
25                Self::from_codec_kind(CodecEncodeError::$variant(error))
26            }
27        }
28    };
29}
30
31// implement From for each variant of CodecEncodeError into EncodeError
32impl_from!(Ber, BerEncodeErrorKind);
33impl_from!(Cer, CerEncodeErrorKind);
34impl_from!(Der, DerEncodeErrorKind);
35impl_from!(Uper, UperEncodeErrorKind);
36impl_from!(Aper, AperEncodeErrorKind);
37impl_from!(Jer, JerEncodeErrorKind);
38impl_from!(Coer, CoerEncodeErrorKind);
39
40impl From<CodecEncodeError> for EncodeError {
41    fn from(error: CodecEncodeError) -> Self {
42        Self::from_codec_kind(error)
43    }
44}
45
46/// An error type for failed encoding for every encoder.
47/// Abstracts over the different generic and codec-specific errors.
48///
49/// `kind` field is used to determine the kind of error that occurred.
50/// `codec` field is used to determine the codec that failed.
51/// `backtrace` field is used to determine the backtrace of the error.
52///
53/// There is `Kind::CodecSpecific` variant which wraps the codec-specific
54/// errors as `CodecEncodeError` type.
55///
56/// # Example
57/// ```
58///
59/// use rasn::prelude::*;
60/// use rasn::error::{EncodeErrorKind, strings::PermittedAlphabetError};
61/// use rasn::codec::Codec;
62///
63/// #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
64/// #[rasn(delegate, from("a..z"))]
65/// struct MyConstrainedString (
66///     VisibleString,
67/// );
68///
69/// // Below sample requires that `backtraces` feature is enabled
70///
71/// let constrained_str = MyConstrainedString(VisibleString::try_from("abcD").unwrap());
72/// let encoded = Codec::Uper.encode_to_binary(&constrained_str);
73/// match encoded {
74///     Ok(succ) => {
75///         println!("Successful encoding!");
76///         dbg!(succ);
77///     }
78///     Err(e) => {
79///         // e is EncodeError, Kind is boxed
80///         match *e.kind {
81///             EncodeErrorKind::AlphabetConstraintNotSatisfied {
82///                reason: PermittedAlphabetError::CharacterNotFound {character },
83///             } => {
84///                 println!("Codec: {}", e.codec);
85///                 println!("Character {} not found from the permitted list.",
86///                          char::from_u32(character).unwrap());
87///                 #[cfg(feature = "backtraces")]
88///                 println!("Backtrace:\n{}", e.backtrace);
89///             }
90///             _ => {
91///                 panic!("Unexpected error!");
92///             }
93///         }
94///     }
95/// }
96/// // Should print ->
97/// //
98/// // Codec: UPER
99/// // Character D not found from the permitted list.
100/// // Backtrace: ...
101/// ```
102///
103#[derive(Debug)]
104#[allow(clippy::module_name_repetitions)]
105pub struct EncodeError {
106    pub kind: Box<EncodeErrorKind>,
107    pub codec: crate::Codec,
108    #[cfg(feature = "backtraces")]
109    pub backtrace: Backtrace,
110}
111impl core::fmt::Display for EncodeError {
112    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113        writeln!(f, "Error Kind: {}", self.kind)?;
114        writeln!(f, "Codec: {}", self.kind)?;
115        #[cfg(feature = "backtraces")]
116        write!(f, "\nBacktrace:\n{}", self.backtrace)?;
117
118        Ok(())
119    }
120}
121impl EncodeError {
122    #[must_use]
123    pub fn alphabet_constraint_not_satisfied(
124        reason: super::strings::PermittedAlphabetError,
125        codec: crate::Codec,
126    ) -> Self {
127        Self::from_kind(
128            EncodeErrorKind::AlphabetConstraintNotSatisfied { reason },
129            codec,
130        )
131    }
132    #[must_use]
133    pub fn size_constraint_not_satisfied(
134        size: usize,
135        expected: &Size,
136        codec: crate::Codec,
137    ) -> Self {
138        Self::from_kind(
139            EncodeErrorKind::SizeConstraintNotSatisfied {
140                size,
141                expected: (**expected),
142            },
143            codec,
144        )
145    }
146    #[must_use]
147    pub fn value_constraint_not_satisfied(
148        value: Integer,
149        expected: &Bounded<i128>,
150        codec: crate::Codec,
151    ) -> Self {
152        Self::from_kind(
153            EncodeErrorKind::ValueConstraintNotSatisfied {
154                value,
155                expected: (*expected),
156            },
157            codec,
158        )
159    }
160    pub fn check_length(length: usize, expected: &Size, codec: crate::Codec) -> Result<(), Self> {
161        expected.contains_or_else(&length, || Self {
162            kind: Box::new(EncodeErrorKind::InvalidLength {
163                length,
164                expected: **expected,
165            }),
166            codec,
167            #[cfg(feature = "backtraces")]
168            backtrace: Backtrace::generate(),
169        })
170    }
171    #[must_use]
172    pub fn length_exceeds_platform_size(codec: crate::Codec) -> Self {
173        Self::from_kind(EncodeErrorKind::LengthExceedsPlatformSize, codec)
174    }
175    /// An error for failed conversion from `BitInt` or `BigUint` to primitive integer types
176    #[must_use]
177    pub fn integer_type_conversion_failed(msg: alloc::string::String, codec: crate::Codec) -> Self {
178        Self::from_kind(EncodeErrorKind::IntegerTypeConversionFailed { msg }, codec)
179    }
180    #[must_use]
181    pub fn invalid_length(length: usize, expected: Bounded<usize>, codec: crate::Codec) -> Self {
182        Self::from_kind(EncodeErrorKind::InvalidLength { length, expected }, codec)
183    }
184    #[must_use]
185    pub fn opaque_conversion_failed(msg: alloc::string::String, codec: crate::Codec) -> Self {
186        Self::from_kind(EncodeErrorKind::OpaqueConversionFailed { msg }, codec)
187    }
188    #[must_use]
189    pub fn variant_not_in_choice(codec: crate::Codec) -> Self {
190        Self::from_kind(EncodeErrorKind::VariantNotInChoice, codec)
191    }
192    #[must_use]
193    pub fn from_kind(kind: EncodeErrorKind, codec: crate::Codec) -> Self {
194        Self {
195            kind: Box::new(kind),
196            codec,
197            #[cfg(feature = "backtraces")]
198            backtrace: Backtrace::generate(),
199        }
200    }
201    #[must_use]
202    fn from_codec_kind(inner: CodecEncodeError) -> Self {
203        let codec = match inner {
204            CodecEncodeError::Ber(_) => crate::Codec::Ber,
205            CodecEncodeError::Cer(_) => crate::Codec::Cer,
206            CodecEncodeError::Der(_) => crate::Codec::Der,
207            CodecEncodeError::Uper(_) => crate::Codec::Uper,
208            CodecEncodeError::Aper(_) => crate::Codec::Aper,
209            CodecEncodeError::Jer(_) => crate::Codec::Jer,
210            CodecEncodeError::Coer(_) => crate::Codec::Coer,
211        };
212        Self {
213            kind: Box::new(EncodeErrorKind::CodecSpecific { inner }),
214            codec,
215            #[cfg(feature = "backtraces")]
216            backtrace: Backtrace::generate(),
217        }
218    }
219}
220
221/// `EncodeError` kinds which are common for all codecs.
222#[derive(Snafu, Debug)]
223#[snafu(visibility(pub))]
224#[non_exhaustive]
225pub enum EncodeErrorKind {
226    #[snafu(display("Failed to convert BIT STRING unused bits to u8: {err}"))]
227    FailedBitStringUnusedBitsToU8 { err: core::num::TryFromIntError },
228    #[snafu(display("invalid length, expected: {expected}; actual: {length}"))]
229    InvalidLength {
230        /// Actual length of the data
231        length: usize,
232        /// Expected length of the data
233        expected: Bounded<usize>,
234    },
235    #[snafu(display("invalid length, exceeds platform maximum size usize::MAX"))]
236    LengthExceedsPlatformSize,
237    #[snafu(display("Integer does not fit to the reserved octets {expected}; actual: {value}"))]
238    MoreBytesThanExpected { value: usize, expected: usize },
239    #[snafu(display("custom error:\n{}", msg))]
240    Custom { msg: alloc::string::String },
241    #[snafu(display("Wrapped codec-specific encode error"))]
242    CodecSpecific { inner: CodecEncodeError },
243    #[snafu(display("Alphabet constraint not satisfied: {reason}"))]
244    AlphabetConstraintNotSatisfied {
245        /// Inner error from mapping realized characters to allowed characters
246        reason: super::strings::PermittedAlphabetError,
247    },
248    #[snafu(display("Size constraint not satisfied: expected: {expected}; actual: {size}"))]
249    SizeConstraintNotSatisfied {
250        /// Actual sie of the data
251        size: usize,
252        /// Expected size by the constraint
253        expected: Bounded<usize>,
254    },
255    #[snafu(display("Value constraint not satisfied: expected: {expected}; actual: {value}"))]
256    ValueConstraintNotSatisfied {
257        /// Actual value of the data
258        value: Integer,
259        /// Expected value by the constraint
260        expected: Bounded<i128>,
261    },
262    #[snafu(display("Failed to cast integer to another integer type: {msg} "))]
263    IntegerTypeConversionFailed { msg: alloc::string::String },
264    #[snafu(display("Conversion to Opaque type failed: {msg}"))]
265    OpaqueConversionFailed { msg: alloc::string::String },
266    #[snafu(display("Selected Variant not found from Choice"))]
267    VariantNotInChoice,
268}
269/// `EncodeError` kinds of `Kind::CodecSpecific` which are specific for BER.
270#[derive(Snafu, Debug)]
271#[snafu(visibility(pub))]
272#[non_exhaustive]
273pub enum BerEncodeErrorKind {
274    #[snafu(display("Cannot encode `ANY` types in `SET` fields"))]
275    AnyInSet,
276    /// `OBJECT IDENTIFIER` must have at least two components.
277    #[snafu(display(
278    "Invalid Object Identifier: must have at least two components and first octet must be 0, 1 or 2. Provided: {:?}", oid
279    ))]
280    InvalidObjectIdentifier { oid: alloc::vec::Vec<u32> },
281}
282impl BerEncodeErrorKind {
283    #[must_use]
284    pub fn invalid_object_identifier(oid: alloc::vec::Vec<u32>) -> Self {
285        Self::InvalidObjectIdentifier { oid }
286    }
287}
288
289// TODO are there CER/DER/APER/UPER specific errors?
290/// `EncodeError` kinds of `Kind::CodecSpecific` which are specific for CER.
291#[derive(Snafu, Debug)]
292#[snafu(visibility(pub))]
293#[non_exhaustive]
294pub enum CerEncodeErrorKind {}
295
296/// `EncodeError` kinds of `Kind::CodecSpecific` which are specific for DER.
297#[derive(Snafu, Debug)]
298#[snafu(visibility(pub))]
299#[non_exhaustive]
300pub enum DerEncodeErrorKind {}
301
302/// `EncodeError` kinds of `Kind::CodecSpecific` which are specific for UPER.
303#[derive(Snafu, Debug)]
304#[snafu(visibility(pub))]
305#[non_exhaustive]
306pub enum JerEncodeErrorKind {
307    /// Upstream `serde` error
308    JsonEncodingError { upstream: alloc::string::String },
309    /// Error to be thrown when the JER encoder contains no encoded root value
310    #[snafu(display("No encoded JSON root value found!"))]
311    NoRootValueFound,
312    /// Internal JSON encoder error
313    #[snafu(display("Error in JSON encoder: {}", msg))]
314    JsonEncoder {
315        /// The error's message.
316        msg: alloc::string::String,
317    },
318    #[snafu(display("Exceeds supported integer range -2^63..2^63 ({:?}).", value))]
319    ExceedsSupportedIntSize {
320        /// value failed to encode
321        value: num_bigint::BigInt,
322    },
323    #[snafu(display("Invalid character: {:?}", error))]
324    InvalidCharacter {
325        /// value failed to encode
326        error: alloc::string::FromUtf8Error,
327    },
328}
329
330/// `EncodeError` kinds of `Kind::CodecSpecific` which are specific for UPER.
331#[derive(Snafu, Debug)]
332#[snafu(visibility(pub))]
333#[non_exhaustive]
334pub enum UperEncodeErrorKind {}
335
336/// `EncodeError` kinds of `Kind::CodecSpecific` which are specific for APER.
337#[derive(Snafu, Debug)]
338#[snafu(visibility(pub))]
339#[non_exhaustive]
340pub enum AperEncodeErrorKind {}
341
342#[derive(Snafu, Debug)]
343#[snafu(visibility(pub))]
344#[non_exhaustive]
345pub enum CoerEncodeErrorKind {
346    #[snafu(display("Provided data is too long to be encoded with COER."))]
347    TooLongValue { length: u128 },
348    #[snafu(display(
349    "Provided length in not correct format. Should be bits as multiple of 8. {remainder}; actual: {length}"
350    ))]
351    LengthNotAsBitLength { length: usize, remainder: usize },
352    #[snafu(display("Provided integer exceeds limits of the constrained word sizes."))]
353    InvalidConstrainedIntegerOctetSize,
354}
355
356impl crate::enc::Error for EncodeError {
357    fn custom<D: core::fmt::Display>(msg: D, codec: crate::Codec) -> Self {
358        Self {
359            kind: Box::new(EncodeErrorKind::Custom {
360                msg: msg.to_string(),
361            }),
362            codec,
363            #[cfg(feature = "backtraces")]
364            backtrace: Backtrace::generate(),
365        }
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use super::*;
372    use crate::prelude::*;
373
374    #[test]
375    fn test_ber_error() {
376        use crate::ber::enc;
377        use crate::enc::Encoder;
378
379        let oid = ObjectIdentifier::new(vec![2, 5, 4, 3]);
380        assert!(oid.is_some());
381        // Higher level abstraction does not allow us to provide OID errors because we provide only valid types
382        let oid_encoded = crate::Codec::Ber.encode_to_binary(&oid);
383        assert!(oid_encoded.is_ok());
384
385        let oid = vec![3, 5, 4, 3];
386
387        let mut enc = enc::Encoder::new(enc::EncoderOptions::ber());
388        let result = enc.encode_object_identifier(Tag::OBJECT_IDENTIFIER, &oid);
389        assert!(result.is_err());
390        match result {
391            Err(e) => match *e.kind {
392                EncodeErrorKind::CodecSpecific {
393                    inner: CodecEncodeError::Ber(BerEncodeErrorKind::InvalidObjectIdentifier { .. }),
394                } => {}
395                _ => {
396                    panic!("Expected invalid object identifier error of specific type!");
397                }
398            },
399            _ => panic!("Unexpected OK!"),
400        }
401        // Debug output should look something like this:
402        // dbg!(result.err());
403        // EncodeError {
404        //     kind: CodecSpecific {
405        //         inner: Ber(
406        //             InvalidObjectIdentifier {
407        //                 oid: [
408        //                     3,
409        //                     5,
410        //                     4,
411        //                     3,
412        //                 ],
413        //             },
414        //         ),
415        //     },
416        //     codec: Ber,
417        //     backtrace: Backtrace( .... ),
418        // },
419    }
420    #[test]
421    fn test_uper_constrained_string_error() {
422        use crate as rasn;
423        use rasn::codec::Codec;
424        use rasn::error::{strings::PermittedAlphabetError, EncodeErrorKind};
425        #[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
426        #[rasn(delegate, from("a..z"))]
427        struct MyConstrainedString(VisibleString);
428
429        let constrained_str = MyConstrainedString(VisibleString::try_from("abcD").unwrap());
430        let encoded = Codec::Uper.encode_to_binary(&constrained_str);
431        match encoded {
432            Ok(_) => {}
433            Err(e) => {
434                // EncodeError
435                match *e.kind {
436                    EncodeErrorKind::AlphabetConstraintNotSatisfied {
437                        reason: PermittedAlphabetError::CharacterNotFound { .. },
438                    } => {}
439                    _ => {
440                        panic!("Unexpected error!");
441                    }
442                }
443            }
444        }
445    }
446}