use enum_iterator::Sequence;
use std::fmt::{Display, Formatter};
use crate::error::Error;
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy, Sequence)]
pub enum Tag {
SIG,
VER,
SRV,
NONC,
DELE,
PATH,
RADI,
PUBK,
MIDP,
SREP,
VERS,
MINT,
ROOT,
CERT,
MAXT,
INDX,
ZZZZ,
PAD,
}
struct TagData {
wire: &'static [u8],
display: &'static str,
}
impl Tag {
pub(crate) const HASH_PREFIX_SRV: &'static [u8] = &[0xff];
const fn data(&self) -> TagData {
match self {
Tag::SIG => TagData { wire: b"SIG\x00", display: "SIG" },
Tag::VER => TagData { wire: b"VER\x00", display: "VER" },
Tag::SRV => TagData { wire: b"SRV\x00", display: "SRV" },
Tag::NONC => TagData { wire: b"NONC", display: "NONC" },
Tag::DELE => TagData { wire: b"DELE", display: "DELE" },
Tag::PATH => TagData { wire: b"PATH", display: "PATH" },
Tag::RADI => TagData { wire: b"RADI", display: "RADI" },
Tag::PUBK => TagData { wire: b"PUBK", display: "PUBK" },
Tag::MIDP => TagData { wire: b"MIDP", display: "MIDP" },
Tag::SREP => TagData { wire: b"SREP", display: "SREP" },
Tag::VERS => TagData { wire: b"VERS", display: "VERS" },
Tag::MINT => TagData { wire: b"MINT", display: "MINT" },
Tag::ROOT => TagData { wire: b"ROOT", display: "ROOT" },
Tag::CERT => TagData { wire: b"CERT", display: "CERT" },
Tag::MAXT => TagData { wire: b"MAXT", display: "MAXT" },
Tag::INDX => TagData { wire: b"INDX", display: "INDX" },
Tag::ZZZZ => TagData { wire: b"ZZZZ", display: "ZZZZ" },
Tag::PAD => TagData { wire: b"PAD\xff", display: "PAD" },
}
}
pub const fn wire_value(&self) -> &'static [u8] {
self.data().wire
}
pub const fn from_wire(bytes: &[u8]) -> Result<Self, Error> {
match bytes {
b"SIG\x00" => Ok(Tag::SIG),
b"VER\x00" => Ok(Tag::VER),
b"SRV\x00" => Ok(Tag::SRV),
b"NONC" => Ok(Tag::NONC),
b"DELE" => Ok(Tag::DELE),
b"PATH" => Ok(Tag::PATH),
b"RADI" => Ok(Tag::RADI),
b"PUBK" => Ok(Tag::PUBK),
b"MIDP" => Ok(Tag::MIDP),
b"SREP" => Ok(Tag::SREP),
b"VERS" => Ok(Tag::VERS),
b"MINT" => Ok(Tag::MINT),
b"ROOT" => Ok(Tag::ROOT),
b"CERT" => Ok(Tag::CERT),
b"MAXT" => Ok(Tag::MAXT),
b"INDX" => Ok(Tag::INDX),
b"ZZZZ" => Ok(Tag::ZZZZ),
b"PAD\xff" => Ok(Tag::PAD),
_ => Err(Error::InvalidTag),
}
}
pub const fn is_nested(&self) -> bool {
match self {
Tag::CERT | Tag::DELE | Tag::SREP => true,
_ => false,
}
}
pub const fn as_string(&self) -> &'static str {
self.data().display
}
}
impl Display for Tag {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_string())
}
}
#[cfg(test)]
mod test {
use super::*;
use enum_iterator;
#[test]
fn tags_in_increasing_order() {
let values = enum_iterator::all::<Tag>().collect::<Vec<_>>();
for i in 1..values.len() {
assert!(
values[i-1] < values[i],
"Tags not in ascending order: {:?} >= {:?}", values[i-1], values[i]
);
}
}
#[test]
fn test_wire_value_and_from_wire() {
for tag in enum_iterator::all::<Tag>() {
let wire = tag.wire_value();
let roundtrip = Tag::from_wire(wire).unwrap();
assert_eq!(tag, roundtrip);
}
}
}