use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result};
use core::{convert::TryFrom, fmt};
const CONSTRUCTED_FLAG: u8 = 0b100000;
const CONTEXT_SPECIFIC_FLAG: u8 = 0b10000000;
pub trait Tagged {
const TAG: Tag;
}
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
#[allow(clippy::identity_op)]
#[non_exhaustive]
#[repr(u8)]
pub enum Tag {
Boolean = 0x01,
Integer = 0x02,
BitString = 0x03,
OctetString = 0x04,
Null = 0x05,
ObjectIdentifier = 0x06,
Utf8String = 0x0C,
Set = 0x11,
PrintableString = 0x13,
Ia5String = 0x16,
UtcTime = 0x17,
GeneralizedTime = 0x18,
Sequence = 0x10 | CONSTRUCTED_FLAG,
ContextSpecific0 = 0 | CONTEXT_SPECIFIC_FLAG | CONSTRUCTED_FLAG,
ContextSpecific1 = 1 | CONTEXT_SPECIFIC_FLAG | CONSTRUCTED_FLAG,
ContextSpecific2 = 2 | CONTEXT_SPECIFIC_FLAG | CONSTRUCTED_FLAG,
ContextSpecific3 = 3 | CONTEXT_SPECIFIC_FLAG | CONSTRUCTED_FLAG,
}
impl TryFrom<u8> for Tag {
type Error = Error;
fn try_from(byte: u8) -> Result<Tag> {
match byte {
0x01 => Ok(Tag::Boolean),
0x02 => Ok(Tag::Integer),
0x03 => Ok(Tag::BitString),
0x04 => Ok(Tag::OctetString),
0x05 => Ok(Tag::Null),
0x06 => Ok(Tag::ObjectIdentifier),
0x0C => Ok(Tag::Utf8String),
0x11 => Ok(Tag::Set),
0x13 => Ok(Tag::PrintableString),
0x16 => Ok(Tag::Ia5String),
0x17 => Ok(Tag::UtcTime),
0x18 => Ok(Tag::GeneralizedTime),
0x30 => Ok(Tag::Sequence),
0xA0 => Ok(Tag::ContextSpecific0),
0xA1 => Ok(Tag::ContextSpecific1),
0xA2 => Ok(Tag::ContextSpecific2),
0xA3 => Ok(Tag::ContextSpecific3),
_ => Err(ErrorKind::UnknownTag { byte }.into()),
}
}
}
impl Tag {
pub fn assert_eq(self, expected: Tag) -> Result<Tag> {
if self == expected {
Ok(self)
} else {
Err(ErrorKind::UnexpectedTag {
expected: Some(expected),
actual: self,
}
.into())
}
}
pub fn type_name(self) -> &'static str {
match self {
Self::Boolean => "BOOLEAN",
Self::Integer => "INTEGER",
Self::BitString => "BIT STRING",
Self::OctetString => "OCTET STRING",
Self::Null => "NULL",
Self::ObjectIdentifier => "OBJECT IDENTIFIER",
Self::Utf8String => "UTF8String",
Self::Set => "SET",
Self::PrintableString => "PrintableString",
Self::Ia5String => "IA5String",
Self::UtcTime => "UTCTime",
Self::GeneralizedTime => "GeneralizedTime",
Self::Sequence => "SEQUENCE",
Self::ContextSpecific0 => "Context Specific 0",
Self::ContextSpecific1 => "Context Specific 1",
Self::ContextSpecific2 => "Context Specific 2",
Self::ContextSpecific3 => "Context Specific 3",
}
}
}
impl Decodable<'_> for Tag {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
decoder.byte().and_then(Self::try_from)
}
}
impl Encodable for Tag {
fn encoded_len(&self) -> Result<Length> {
Ok(Length::one())
}
fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
encoder.byte(*self as u8)
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.type_name())
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Tag(0x{:02x}: {})", *self as u8, self.type_name())
}
}