use crate::errors::DecodingError;
use bytes::Bytes;
use core::fmt;
use core::str::FromStr;
use der::{asn1::Utf8StringRef, Decode, Encode, Reader, Writer};
use hifitime::Epoch;
use super::{dataset::DataSetType, semver::Semver, ANISE_VERSION};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Metadata {
pub anise_version: Semver,
pub dataset_type: DataSetType,
pub creation_date: Epoch,
pub originator: String,
}
impl Metadata {
pub fn decode_header(bytes: &[u8]) -> Result<Self, DecodingError> {
let anise_version =
Semver::from_der(bytes.get(..5).ok_or(DecodingError::InaccessibleBytes {
start: 0,
end: 5,
size: bytes.len(),
})?)
.map_err(|err| DecodingError::DecodingDer { err })?;
let dataset_type = DataSetType::from_der(bytes.get(5..8).ok_or({
DecodingError::InaccessibleBytes {
start: 5,
end: 8,
size: bytes.len(),
}
})?)
.map_err(|err| DecodingError::DecodingDer { err })?;
let me = Self {
anise_version,
dataset_type,
..Default::default()
};
Ok(me)
}
pub fn from_bytes(buf: Bytes) -> Self {
Self::from_der(&buf).unwrap()
}
}
impl Default for Metadata {
fn default() -> Self {
Self {
anise_version: ANISE_VERSION,
dataset_type: DataSetType::NotApplicable,
creation_date: Epoch::now().unwrap(),
originator: Default::default(),
}
}
}
impl Encode for Metadata {
fn encoded_len(&self) -> der::Result<der::Length> {
self.anise_version.encoded_len()?
+ self.dataset_type.encoded_len()?
+ Utf8StringRef::new(&format!("{}", self.creation_date))?.encoded_len()?
+ Utf8StringRef::new(&self.originator)?.encoded_len()?
}
fn encode(&self, encoder: &mut impl Writer) -> der::Result<()> {
self.anise_version.encode(encoder)?;
self.dataset_type.encode(encoder)?;
Utf8StringRef::new(&format!("{}", self.creation_date))?.encode(encoder)?;
Utf8StringRef::new(&self.originator)?.encode(encoder)
}
}
impl<'a> Decode<'a> for Metadata {
fn decode<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
let anise_version = decoder.decode()?;
let dataset_type = decoder.decode()?;
let creation_date = Epoch::from_str(decoder.decode::<Utf8StringRef<'a>>()?.as_str())
.map_err(|_| {
der::Error::new(
der::ErrorKind::Value {
tag: der::Tag::Utf8String,
},
der::Length::ONE,
)
})?;
let originator = decoder.decode::<Utf8StringRef<'a>>()?.to_string();
Ok(Self {
anise_version,
dataset_type,
creation_date,
originator,
})
}
}
impl fmt::Display for Metadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ANISE version {}", self.anise_version)?;
writeln!(
f,
"Originator: {}",
if self.originator.is_empty() {
"(not set)"
} else {
&self.originator
}
)?;
writeln!(f, "Creation date: {}", self.creation_date)
}
}
#[cfg(test)]
mod metadata_ut {
use super::Metadata;
use der::{Decode, Encode};
#[test]
fn meta_encdec_min_repr() {
let repr = Metadata::default();
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = Metadata::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
assert_eq!(
format!("{repr}"),
format!(
r#"ANISE version ANISE version 0.7.0
Originator: (not set)
Creation date: {}
"#,
repr_dec.creation_date
)
);
}
#[test]
fn meta_invalid() {
let repr = Metadata::default();
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
assert!(Metadata::decode_header(&buf).is_ok());
assert!(
Metadata::decode_header(&buf[..7]).is_err(),
"should not have enough for dataset"
);
assert!(
Metadata::decode_header(&buf[..4]).is_err(),
"should not have enough for version"
);
}
#[test]
fn meta_with_orig() {
let repr = Metadata {
originator: "Nyx Space Origin".to_string(),
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = Metadata::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
}