use std::sync::Arc;
use bitvec::field::BitField;
use bitvec::order::Msb0;
use bitvec::view::BitView;
#[cfg(feature = "tracing")]
use tracing::warn;
use strum_macros::EnumDiscriminants;
use crate::ErrorKind;
use crate::klv_packet::KlvPacket;
use crate::tag::Tag;
#[derive(Clone, Debug, EnumDiscriminants)]
#[strum_discriminants(vis(pub))]
#[strum_discriminants(name(KlvValueType))]
pub enum KlvValue {
Unknown,
Deprecated,
Unimplemented,
Int(i64),
Int8(i8),
Int16(i16),
Int32(i32),
Uint(u64),
Uint8(u8),
Uint16(u16),
Uint32(u32),
Uint64(u64),
IMAPB(f64),
Byte(Box<[u8]>),
DLP,
VLP,
FLP,
Set(Vec<KlvPacket>),
Utf8(Arc<str>),
}
impl KlvValue {
pub fn from_bytes(tag: Tag, bytes: &[u8]) -> Result<KlvValue, ErrorKind> {
let t = tag.tag_type();
let value = match t {
KlvValueType::Int => Self::int(bytes),
KlvValueType::Int8 => Self::int8(bytes),
KlvValueType::Int16 => Self::int16(bytes),
KlvValueType::Int32 => Self::int32(bytes),
KlvValueType::Uint => Self::uint(bytes),
KlvValueType::Uint8 => Self::uint8(bytes),
KlvValueType::Uint16 => Self::uint16(bytes),
KlvValueType::Uint32 => Self::uint32(bytes),
KlvValueType::Uint64 => Self::uint64(bytes),
KlvValueType::Utf8 => Self::utf8(bytes),
KlvValueType::IMAPB => Self::imapb(bytes),
KlvValueType::Set => Self::set(tag, bytes),
KlvValueType::Byte => Self::byte(bytes),
KlvValueType::DLP => Self::dlp(bytes),
KlvValueType::VLP => Self::vlp(bytes),
KlvValueType::FLP => Self::flp(bytes),
_ => return Err(ErrorKind::UnsupportedTag(tag.into())),
};
Ok(value)
}
fn int(bytes: &[u8]) -> KlvValue {
KlvValue::Int(bytes.view_bits::<Msb0>().load_be())
}
fn int8(bytes: &[u8]) -> KlvValue {
KlvValue::Int8(bytes.view_bits::<Msb0>().load_be())
}
fn int16(bytes: &[u8]) -> KlvValue {
KlvValue::Int16(bytes.view_bits::<Msb0>().load_be())
}
fn int32(bytes: &[u8]) -> KlvValue {
KlvValue::Int32(bytes.view_bits::<Msb0>().load_be())
}
fn uint(bytes: &[u8]) -> KlvValue {
KlvValue::Uint(bytes.view_bits::<Msb0>().load_be())
}
fn uint8(bytes: &[u8]) -> KlvValue {
KlvValue::Uint8(bytes.view_bits::<Msb0>().load_be())
}
fn uint16(bytes: &[u8]) -> KlvValue {
KlvValue::Uint16(bytes.view_bits::<Msb0>().load_be())
}
fn uint32(bytes: &[u8]) -> KlvValue {
KlvValue::Uint32(bytes.view_bits::<Msb0>().load_be())
}
fn uint64(bytes: &[u8]) -> KlvValue {
KlvValue::Uint64(bytes.view_bits::<Msb0>().load_be())
}
fn utf8(bytes: &[u8]) -> KlvValue {
KlvValue::Utf8(
std::str::from_utf8(bytes)
.unwrap_or_else(|_| {
panic!(
"Cannot create UTF8 string from bytes {:02X?}",
bytes
)
})
.into(),
)
}
fn imapb(bytes: &[u8]) -> KlvValue {
Self::klv_unimplemented("IMAPB")
}
fn set(tag: Tag, bytes: &[u8]) -> KlvValue {
Self::klv_unimplemented("Set")
}
fn byte(bytes: &[u8]) -> KlvValue {
Self::klv_unimplemented("Byte")
}
fn dlp(bytes: &[u8]) -> KlvValue {
Self::klv_unimplemented("DLP")
}
fn vlp(bytes: &[u8]) -> KlvValue {
Self::klv_unimplemented("VLP")
}
fn flp(bytes: &[u8]) -> KlvValue {
Self::klv_unimplemented("FLP")
}
fn klv_unimplemented(tag_type: &str) -> KlvValue {
#[cfg(not(feature = "ignore_incomplete"))]
{
todo!("Implement converting KLV bytes to {}", tag_type);
}
#[cfg(feature = "ignore_incomplete")]
{
#[cfg(feature = "tracing")]
warn!("Converting KLV bytes to {} is not yet supported", tag_type);
KlvValue::Unimplemented
}
}
}