1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::prelude::*;

/// A set of supported ASN.1 codecs. Can be used to dynamically encode types
/// into different codecs at runtime.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum Codec {
    /// X.691 — Packed Encoding Rules (Aligned)
    Aper,
    /// X.690 — Basic Encoding Rules
    Ber,
    /// X.690 — Canonical Encoding Rules
    Cer,
    /// X.690 — Distinguished Encoding Rules
    Der,
    /// X.691 — Packed Encoding Rules (Unaligned)
    Uper,
    /// [JSON Encoding Rules](https://obj-sys.com/docs/JSONEncodingRules.pdf)
    Jer,
}

impl core::fmt::Display for Codec {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Aper => write!(f, "APER"),
            Self::Ber => write!(f, "BER"),
            Self::Cer => write!(f, "CER"),
            Self::Der => write!(f, "DER"),
            Self::Uper => write!(f, "UPER"),
            Self::Jer => write!(f, "JER"),
        }
    }
}

impl Codec {
    /// Encodes a given value based on the value of `Codec`.
    /// This method shall be used when using binary-based encoding rules.
    ///
    /// # Errors
    /// - If the value fails to be encoded returns `EncodeError` struct.
    pub fn encode_to_binary<T: Encode>(
        self,
        value: &T,
    ) -> Result<alloc::vec::Vec<u8>, crate::error::EncodeError> {
        match self {
            Self::Aper => crate::aper::encode(value),
            Self::Ber => crate::ber::encode(value),
            Self::Cer => crate::cer::encode(value),
            Self::Der => crate::der::encode(value),
            Self::Uper => crate::uper::encode(value),
            Self::Jer => crate::jer::encode(value).map(alloc::string::String::into_bytes),
        }
    }

    /// Decodes `input` to `D` based on the value of `Codec`.
    /// This method shall be used when using binary-based encoding rules.
    ///
    /// # Errors
    /// - If `D` cannot be decoded from `input` returns `DecodeError` struct.
    pub fn decode_from_binary<D: Decode>(
        &self,
        input: &[u8],
    ) -> Result<D, crate::error::DecodeError> {
        match self {
            Self::Aper => crate::aper::decode(input),
            Self::Ber => crate::ber::decode(input),
            Self::Cer => crate::cer::decode(input),
            Self::Der => crate::der::decode(input),
            Self::Uper => crate::uper::decode(input),
            Self::Jer => alloc::string::String::from_utf8(input.to_vec()).map_or_else(
                |e| {
                    Err(crate::error::DecodeError::from_kind(
                        crate::error::DecodeErrorKind::Custom {
                            msg: alloc::format!("Failed to decode JER from UTF8 bytes: {e:?}"),
                        },
                        self.clone(),
                    ))
                },
                |s| crate::jer::decode(&s),
            ),
        }
    }

    /// Encodes a given value based on the value of `Codec`.
    /// This method shall be used when using text-based encoding rules.
    ///
    /// # Errors
    /// - If the value fails to be encoded, or if trying to encode using
    ///   binary-based encoding rules, returns `EncodeError` struct.
    pub fn encode_to_string<T: Encode>(
        self,
        value: &T,
    ) -> Result<alloc::string::String, crate::error::EncodeError> {
        match self {
            Self::Jer => crate::jer::encode(value),
            codec => Err(crate::error::EncodeError::from_kind(
                crate::error::EncodeErrorKind::Custom {
                    msg: alloc::format!("{codec} is a binary-based encoding. Call `Codec::encode_to_binary` instead."),
                },
                codec,
            )),
        }
    }

    /// Decodes `input` to `D` based on the value of `Codec`.
    /// This method shall be used when using text-based encoding rules.
    ///
    /// # Errors
    /// - If `D` cannot be decoded from `input`, or if trying to decode using
    ///   binary-based encoding rules, returns `DecodeError` struct.
    pub fn decode_from_str<D: Decode>(&self, input: &str) -> Result<D, crate::error::DecodeError> {
        match self {
            Self::Jer => crate::jer::decode(input),
            codec => Err(crate::error::DecodeError::from_kind(
                crate::error::DecodeErrorKind::Custom {
                    msg: alloc::format!("{codec} is a text-based encoding. Call `Codec::decode_from_binary` instead."),
                },
                codec.clone(),
            )),
        }
    }
}