use std::fmt::Debug;
#[derive(Clone, PartialEq, Eq, Hash)]
enum RecordData<'a> {
Borrowed(&'a [u8]),
Owned(Vec<u8>),
}
impl Debug for RecordData<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RecordData::Borrowed(data) => write!(f, "RecordData::Borrowed({} bytes)", data.len()),
RecordData::Owned(data) => write!(f, "RecordData::Owned({} bytes)", data.len()),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Record<'a>(RecordData<'a>);
impl<'a> Record<'a> {
pub fn new(data: Vec<u8>) -> Self {
Record(RecordData::Owned(data))
}
pub fn from_slice(data: &'a [u8]) -> Self {
Record(RecordData::Borrowed(data))
}
pub fn data(&self) -> &[u8] {
match &self.0 {
RecordData::Borrowed(data) => data,
RecordData::Owned(data) => data,
}
}
pub fn compressed(&self) -> bool {
self.data().len() >= 6 && self.data()[4..6].as_ref() == b"BZ"
}
pub fn decompress<'b>(&self) -> crate::result::Result<Record<'b>> {
use crate::result::Error;
use bzip2::read::BzDecoder;
use std::io::Read;
if !self.compressed() {
return Err(Error::UncompressedData);
}
let data = self.data().split_at(4).1;
let mut decompressed_data = Vec::new();
BzDecoder::new(data).read_to_end(&mut decompressed_data)?;
Ok(Record::new(decompressed_data))
}
pub fn messages(&self) -> crate::result::Result<Vec<nexrad_decode::messages::Message<'_>>> {
use crate::result::Error;
use nexrad_decode::messages::decode_messages;
if self.compressed() {
return Err(Error::CompressedData);
}
Ok(decode_messages(self.data())?)
}
#[cfg(feature = "nexrad-model")]
pub fn radials(&self) -> crate::result::Result<Vec<nexrad_model::data::Radial>> {
use nexrad_decode::messages::MessageContents;
let mut radials = Vec::new();
for message in self.messages()? {
match message.into_contents() {
MessageContents::DigitalRadarData(m) => radials.push(m.into_radial()?),
MessageContents::DigitalRadarDataLegacy(m) => radials.push(m.into_radial()?),
_ => {}
}
}
Ok(radials)
}
}
impl Debug for Record<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("Record");
debug.field("data.len()", &self.data().len());
debug.field(
"borrowed",
match &self.0 {
RecordData::Borrowed(_) => &true,
RecordData::Owned(_) => &false,
},
);
debug.field("compressed", &self.compressed());
debug.field(
"messages.len()",
&self.messages().map(|messages| messages.len()),
);
debug.finish()
}
}
pub fn split_compressed_records(data: &[u8]) -> crate::result::Result<Vec<Record<'_>>> {
if data.len() < 4 {
return split_ldm_records(data);
}
let first_four = [data[0], data[1], data[2], data[3]];
if first_four == [0, 0, 0, 0] {
return split_ctm_frames(data);
}
split_ldm_records(data)
}
fn split_ldm_records(data: &[u8]) -> crate::result::Result<Vec<Record<'_>>> {
use crate::result::Error;
let mut records = Vec::new();
let mut position = 0;
loop {
if position >= data.len() {
break;
}
if position + 4 > data.len() {
return Err(Error::TruncatedRecord {
expected: position + 4,
actual: data.len(),
});
}
let mut record_size_bytes = [0; 4];
record_size_bytes.copy_from_slice(&data[position..position + 4]);
let record_size = i32::from_be_bytes(record_size_bytes).unsigned_abs() as usize;
if record_size == 0 {
return Err(Error::InvalidRecordSize {
size: record_size,
offset: position,
});
}
let record_end = position + record_size + 4;
if record_end > data.len() {
return Err(Error::TruncatedRecord {
expected: record_end,
actual: data.len(),
});
}
records.push(Record::from_slice(&data[position..record_end]));
position = record_end;
}
Ok(records)
}
fn split_ctm_frames(data: &[u8]) -> crate::result::Result<Vec<Record<'_>>> {
if data.is_empty() {
return Ok(Vec::new());
}
Ok(vec![Record::from_slice(data)])
}