use crate::types::constraints::{Bounded, Size};
use num_bigint::BigInt;
use snafu::Snafu;
#[cfg(feature = "backtraces")]
use snafu::{Backtrace, GenerateImplicitData};
use alloc::{boxed::Box, string::ToString};
#[derive(Debug)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum CodecEncodeError {
Ber(BerEncodeErrorKind),
Cer(CerEncodeErrorKind),
Der(DerEncodeErrorKind),
Uper(UperEncodeErrorKind),
Aper(AperEncodeErrorKind),
Jer(JerEncodeErrorKind),
Coer(CoerEncodeErrorKind),
Xer(XerEncodeErrorKind),
}
impl core::fmt::Display for CodecEncodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CodecEncodeError::Ber(kind) => write!(f, "BER encoding error: {kind}"),
CodecEncodeError::Cer(kind) => write!(f, "CER encoding error: {kind}"),
CodecEncodeError::Der(kind) => write!(f, "DER encoding error: {kind}"),
CodecEncodeError::Uper(kind) => write!(f, "UPER encoding error: {kind}"),
CodecEncodeError::Aper(kind) => write!(f, "APER encoding error: {kind}"),
CodecEncodeError::Jer(kind) => write!(f, "JER encoding error: {kind}"),
CodecEncodeError::Coer(kind) => write!(f, "COER encoding error: {kind}"),
CodecEncodeError::Xer(kind) => write!(f, "XER encoding error: {kind}"),
}
}
}
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!(Coer, CoerEncodeErrorKind);
impl_from!(Xer, XerEncodeErrorKind);
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::error::Error for EncodeError {}
impl core::fmt::Display for EncodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} (Codec: {})", self.kind, self.codec)?;
#[cfg(feature = "backtraces")]
write!(f, "\n\nBacktrace:\n{}", self.backtrace)?;
Ok(())
}
}
impl EncodeError {
#[must_use]
pub fn alphabet_constraint_not_satisfied(
reason: super::strings::PermittedAlphabetError,
codec: crate::Codec,
) -> Self {
Self::from_kind(
EncodeErrorKind::AlphabetConstraintNotSatisfied { reason },
codec,
)
}
#[must_use]
pub fn size_constraint_not_satisfied(
size: usize,
expected: &Size,
codec: crate::Codec,
) -> Self {
Self::from_kind(
EncodeErrorKind::SizeConstraintNotSatisfied {
size,
expected: (**expected),
},
codec,
)
}
#[must_use]
pub fn value_constraint_not_satisfied(
value: BigInt,
expected: &Bounded<i128>,
codec: crate::Codec,
) -> Self {
Self::from_kind(
EncodeErrorKind::ValueConstraintNotSatisfied {
value,
expected: (*expected),
},
codec,
)
}
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 length_exceeds_platform_size(codec: crate::Codec) -> Self {
Self::from_kind(EncodeErrorKind::LengthExceedsPlatformSize, codec)
}
#[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 real_not_supported(codec: crate::Codec) -> Self {
Self::from_kind(EncodeErrorKind::RealNotSuppored, 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,
#[allow(unreachable_patterns)]
CodecEncodeError::Cer(_) => crate::Codec::Cer,
#[allow(unreachable_patterns)]
CodecEncodeError::Der(_) => crate::Codec::Der,
#[allow(unreachable_patterns)]
CodecEncodeError::Uper(_) => crate::Codec::Uper,
#[allow(unreachable_patterns)]
CodecEncodeError::Aper(_) => crate::Codec::Aper,
CodecEncodeError::Jer(_) => crate::Codec::Jer,
CodecEncodeError::Coer(_) => crate::Codec::Coer,
CodecEncodeError::Xer(_) => crate::Codec::Xer,
};
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("Invalid length, exceeds platform maximum size usize::MAX"))]
LengthExceedsPlatformSize,
#[snafu(display(
"The provided value does not fit in the reserved octets {expected}; actual: {value}"
))]
MoreBytesThanExpected {
value: usize,
expected: usize,
},
#[snafu(display("Custom error: {}", msg))]
Custom {
msg: alloc::string::String,
},
#[snafu(display("{inner}"))]
CodecSpecific {
inner: CodecEncodeError,
},
#[snafu(display("Alphabet constraint not satisfied: {reason}"))]
AlphabetConstraintNotSatisfied {
reason: super::strings::PermittedAlphabetError,
},
#[snafu(display("Size constraint not satisfied: expected: {expected}; actual: {size}"))]
SizeConstraintNotSatisfied {
size: usize,
expected: Bounded<usize>,
},
#[snafu(display("Value constraint not satisfied: expected: {expected}; actual: {value}"))]
ValueConstraintNotSatisfied {
value: BigInt,
expected: Bounded<i128>,
},
#[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 in Choice"))]
VariantNotInChoice,
#[snafu(display("Encoder doesn't support `REAL` type"))]
RealNotSuppored,
}
#[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: BigInt,
},
#[snafu(display("Exceeds supported real value range"))]
ExceedsSupportedRealRange,
#[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 {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum XerEncodeErrorKind {
XmlEncodingError {
upstream: alloc::string::String,
},
#[snafu(display("Failed to encode integer"))]
UnsupportedIntegerValue,
#[snafu(display("Missing identifier for ASN.1 type"))]
MissingIdentifier,
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum CoerEncodeErrorKind {
#[snafu(display("Provided data is too long to be encoded with COER"))]
TooLongValue {
length: u128,
},
#[snafu(display("Provided integer exceeds limits of the constrained word sizes"))]
InvalidConstrainedIntegerOctetSize,
}
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, Identifier::EMPTY);
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::{EncodeErrorKind, strings::PermittedAlphabetError};
#[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!");
}
}
}
}
}
}