use std::ops::Deref;
use {
packet::Tag,
Error,
Result
};
use packet::BodyLength;
#[derive(Clone, Debug)]
pub struct CTBCommon {
pub tag: Tag,
}
#[derive(Clone, Debug)]
pub struct CTBNew {
pub common: CTBCommon,
}
impl CTBNew {
pub fn new(tag: Tag) -> Self {
CTBNew {
common: CTBCommon {
tag: tag,
},
}
}
}
impl Deref for CTBNew {
type Target = CTBCommon;
fn deref(&self) -> &Self::Target {
&self.common
}
}
#[derive(Debug)]
#[derive(Clone, Copy, PartialEq)]
pub enum PacketLengthType {
OneOctet,
TwoOctets,
FourOctets,
Indeterminate,
}
impl PacketLengthType {
pub fn try_from(u: u8) -> Result<Self> {
match u {
0 => Ok(PacketLengthType::OneOctet),
1 => Ok(PacketLengthType::TwoOctets),
2 => Ok(PacketLengthType::FourOctets),
3 => Ok(PacketLengthType::Indeterminate),
_ => Err(Error::InvalidArgument(
format!("Invalid packet length: {}", u)).into()),
}
}
}
impl From<PacketLengthType> for u8 {
fn from(l: PacketLengthType) -> Self {
match l {
PacketLengthType::OneOctet => 0,
PacketLengthType::TwoOctets => 1,
PacketLengthType::FourOctets => 2,
PacketLengthType::Indeterminate => 3,
}
}
}
#[derive(Clone, Debug)]
pub struct CTBOld {
pub common: CTBCommon,
pub length_type: PacketLengthType,
}
impl CTBOld {
pub fn new(tag: Tag, length: BodyLength) -> Result<Self> {
let n: u8 = tag.into();
if n > 15 {
return Err(Error::InvalidArgument(
format!("Only tags 0-15 are supported, got: {:?} ({})",
tag, n)).into());
}
let length_type = match length {
BodyLength::Full(l) => {
match l {
0 ... 0xFF => PacketLengthType::OneOctet,
0x1_00 ... 0xFF_FF => PacketLengthType::TwoOctets,
_ => PacketLengthType::FourOctets,
}
},
BodyLength::Partial(_) =>
return Err(Error::InvalidArgument(
"Partial body lengths are not support for old format packets".
into()).into()),
BodyLength::Indeterminate =>
PacketLengthType::Indeterminate,
};
Ok(CTBOld {
common: CTBCommon {
tag: tag,
},
length_type: length_type,
})
}
}
impl Deref for CTBOld {
type Target = CTBCommon;
fn deref(&self) -> &Self::Target {
&self.common
}
}
#[derive(Clone, Debug)]
pub enum CTB {
New(CTBNew),
Old(CTBOld),
}
impl CTB {
pub fn new(tag: Tag) -> Self {
CTB::New(CTBNew::new(tag))
}
}
impl Deref for CTB {
type Target = CTBCommon;
fn deref(&self) -> &Self::Target {
match self {
&CTB::New(ref ctb) => return &ctb.common,
&CTB::Old(ref ctb) => return &ctb.common,
}
}
}
impl CTB {
pub fn from_ptag(ptag: u8) -> Result<CTB> {
if ptag & 0b1000_0000 == 0 {
return Err(
Error::MalformedPacket(
format!("Malformed CTB: MSB of ptag ({:#010b}) not set{}.",
ptag,
if ptag == '-' as u8 {
" (ptag is a dash, perhaps this is an \
ASCII-armor encoded message)"
} else {
""
})).into());
}
let new_format = ptag & 0b0100_0000 != 0;
let ctb = if new_format {
let tag = ptag & 0b0011_1111;
CTB::New(CTBNew {
common: CTBCommon {
tag: tag.into()
}})
} else {
let tag = (ptag & 0b0011_1100) >> 2;
let length_type = ptag & 0b0000_0011;
CTB::Old(CTBOld {
common: CTBCommon {
tag: tag.into(),
},
length_type: PacketLengthType::try_from(length_type)?,
})
};
Ok(ctb)
}
}
#[test]
fn ctb() {
if let CTB::Old(ctb) = CTB::from_ptag(0x99).unwrap() {
assert_eq!(ctb.tag, Tag::PublicKey);
assert_eq!(ctb.length_type, PacketLengthType::TwoOctets);
} else {
panic!("Expected an old format packet.");
}
if let CTB::Old(ctb) = CTB::from_ptag(0xa3).unwrap() {
assert_eq!(ctb.tag, Tag::CompressedData);
assert_eq!(ctb.length_type, PacketLengthType::Indeterminate);
} else {
panic!("Expected an old format packet.");
}
if let CTB::New(ctb) = CTB::from_ptag(0xcb).unwrap() {
assert_eq!(ctb.tag, Tag::Literal);
} else {
panic!("Expected a new format packet.");
}
}