use crate::types::constraints::{Bounded, Size};
use snafu::Snafu;
#[cfg(feature = "backtraces")]
use snafu::{Backtrace, GenerateImplicitData};
use alloc::{boxed::Box, string::ToString};
#[derive(Debug)]
#[non_exhaustive]
pub enum CodecEncodeError {
Ber(BerEncodeErrorKind),
Cer(CerEncodeErrorKind),
Der(DerEncodeErrorKind),
Uper(UperEncodeErrorKind),
Aper(AperEncodeErrorKind),
Jer(JerEncodeErrorKind),
}
macro_rules! impl_from {
($variant:ident, $error_kind:ty) => {
impl From<$error_kind> for EncodeError {
fn from(error: $error_kind) -> Self {
Self::from_codec_kind(CodecEncodeError::$variant(error))
}
}
};
}
impl_from!(Ber, BerEncodeErrorKind);
impl_from!(Cer, CerEncodeErrorKind);
impl_from!(Der, DerEncodeErrorKind);
impl_from!(Uper, UperEncodeErrorKind);
impl_from!(Aper, AperEncodeErrorKind);
impl_from!(Jer, JerEncodeErrorKind);
impl From<CodecEncodeError> for EncodeError {
fn from(error: CodecEncodeError) -> Self {
Self::from_codec_kind(error)
}
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct EncodeError {
pub kind: Box<EncodeErrorKind>,
pub codec: crate::Codec,
#[cfg(feature = "backtraces")]
pub backtrace: Backtrace,
}
impl core::fmt::Display for EncodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "Error Kind: {}", self.kind)?;
writeln!(f, "Codec: {}", self.kind)?;
#[cfg(feature = "backtraces")]
write!(f, "\nBacktrace:\n{}", self.backtrace)?;
Ok(())
}
}
impl EncodeError {
#[must_use]
pub fn alphabet_constraint_not_satisfied(
reason: super::strings::PermittedAlphabetError,
codec: crate::Codec,
) -> Self {
Self {
kind: Box::new(EncodeErrorKind::AlphabetConstraintNotSatisfied { reason }),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
}
}
pub fn check_length(length: usize, expected: &Size, codec: crate::Codec) -> Result<(), Self> {
expected.contains_or_else(&length, || Self {
kind: Box::new(EncodeErrorKind::InvalidLength {
length,
expected: (**expected),
}),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
})
}
#[must_use]
pub fn integer_type_conversion_failed(msg: alloc::string::String, codec: crate::Codec) -> Self {
Self::from_kind(EncodeErrorKind::IntegerTypeConversionFailed { msg }, codec)
}
#[must_use]
pub fn opaque_conversion_failed(msg: alloc::string::String, codec: crate::Codec) -> Self {
Self::from_kind(EncodeErrorKind::OpaqueConversionFailed { msg }, codec)
}
#[must_use]
pub fn variant_not_in_choice(codec: crate::Codec) -> Self {
Self::from_kind(EncodeErrorKind::VariantNotInChoice, codec)
}
#[must_use]
pub fn from_kind(kind: EncodeErrorKind, codec: crate::Codec) -> Self {
Self {
kind: Box::new(kind),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
}
}
#[must_use]
fn from_codec_kind(inner: CodecEncodeError) -> Self {
let codec = match inner {
CodecEncodeError::Ber(_) => crate::Codec::Ber,
CodecEncodeError::Cer(_) => crate::Codec::Cer,
CodecEncodeError::Der(_) => crate::Codec::Der,
CodecEncodeError::Uper(_) => crate::Codec::Uper,
CodecEncodeError::Aper(_) => crate::Codec::Aper,
CodecEncodeError::Jer(_) => crate::Codec::Jer,
};
Self {
kind: Box::new(EncodeErrorKind::CodecSpecific { inner }),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
}
}
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum EncodeErrorKind {
#[snafu(display("Failed to convert BIT STRING unused bits to u8: {err}"))]
FailedBitStringUnusedBitsToU8 { err: core::num::TryFromIntError },
#[snafu(display("invalid length, expected: {expected}; actual: {length}"))]
InvalidLength {
length: usize,
expected: Bounded<usize>,
},
#[snafu(display("custom error:\n{}", msg))]
Custom { msg: alloc::string::String },
#[snafu(display("Wrapped codec-specific encode error"))]
CodecSpecific { inner: CodecEncodeError },
#[snafu(display("Constraint not satisfied: {reason}"))]
AlphabetConstraintNotSatisfied {
reason: super::strings::PermittedAlphabetError,
},
#[snafu(display("Failed to cast integer to another integer type: {msg} "))]
IntegerTypeConversionFailed { msg: alloc::string::String },
#[snafu(display("Conversion to Opaque type failed: {msg}"))]
OpaqueConversionFailed { msg: alloc::string::String },
#[snafu(display("Selected Variant not found from Choice"))]
VariantNotInChoice,
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum BerEncodeErrorKind {
#[snafu(display("Cannot encode `ANY` types in `SET` fields"))]
AnyInSet,
#[snafu(display(
"Invalid Object Identifier: must have at least two components and first octet must be 0, 1 or 2. Provided: {:?}", oid
))]
InvalidObjectIdentifier { oid: alloc::vec::Vec<u32> },
}
impl BerEncodeErrorKind {
#[must_use]
pub fn invalid_object_identifier(oid: alloc::vec::Vec<u32>) -> Self {
Self::InvalidObjectIdentifier { oid }
}
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum CerEncodeErrorKind {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum DerEncodeErrorKind {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum JerEncodeErrorKind {
JsonEncodingError { upstream: alloc::string::String },
#[snafu(display("No encoded JSON root value found!"))]
NoRootValueFound,
#[snafu(display("Error in JSON encoder: {}", msg))]
JsonEncoder {
msg: alloc::string::String,
},
#[snafu(display("Exceeds supported integer range -2^63..2^63 ({:?}).", value))]
ExceedsSupportedIntSize {
value: num_bigint::BigInt,
},
#[snafu(display("Invalid character: {:?}", error))]
InvalidCharacter {
error: alloc::string::FromUtf8Error,
},
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum UperEncodeErrorKind {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum AperEncodeErrorKind {}
impl crate::enc::Error for EncodeError {
fn custom<D: core::fmt::Display>(msg: D, codec: crate::Codec) -> Self {
Self {
kind: Box::new(EncodeErrorKind::Custom {
msg: msg.to_string(),
}),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn test_ber_error() {
use crate::ber::enc;
use crate::enc::Encoder;
let oid = ObjectIdentifier::new(vec![2, 5, 4, 3]);
assert!(oid.is_some());
let oid_encoded = crate::Codec::Ber.encode_to_binary(&oid);
assert!(oid_encoded.is_ok());
let oid = vec![3, 5, 4, 3];
let mut enc = enc::Encoder::new(enc::EncoderOptions::ber());
let result = enc.encode_object_identifier(Tag::OBJECT_IDENTIFIER, &oid);
assert!(result.is_err());
match result {
Err(e) => match *e.kind {
EncodeErrorKind::CodecSpecific {
inner: CodecEncodeError::Ber(BerEncodeErrorKind::InvalidObjectIdentifier { .. }),
} => {}
_ => {
panic!("Expected invalid object identifier error of specific type!");
}
},
_ => panic!("Unexpected OK!"),
}
}
#[test]
fn test_uper_constrained_string_error() {
use crate as rasn;
use rasn::codec::Codec;
use rasn::error::{strings::PermittedAlphabetError, EncodeErrorKind};
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq)]
#[rasn(delegate, from("a..z"))]
struct MyConstrainedString(VisibleString);
let constrained_str = MyConstrainedString(VisibleString::try_from("abcD").unwrap());
let encoded = Codec::Uper.encode_to_binary(&constrained_str);
match encoded {
Ok(_) => {}
Err(e) => {
match *e.kind {
EncodeErrorKind::AlphabetConstraintNotSatisfied {
reason: PermittedAlphabetError::CharacterNotFound { .. },
} => {}
_ => {
panic!("Unexpected error!");
}
}
}
}
}
}