rsbag/src/parse/header.rs
2021-11-18 15:11:56 -08:00

112 lines
2.8 KiB
Rust

use core::fmt::{self, Write as _};
use nom::{
bytes::complete::{tag, take_until},
combinator::rest,
error::context,
multi::{length_value, many0},
number::{complete::le_u32 as le_u32_complete, streaming::le_u32 as le_u32_streaming},
sequence::separated_pair,
Parser,
};
use smallvec::SmallVec;
use super::{fields, IResult, Input, Op};
use crate::{Error, Result};
#[derive(Clone, Debug)]
pub struct HeaderField {
name: SmallVec<[u8; 16]>,
value: SmallVec<[u8; 16]>,
}
impl HeaderField {
pub fn name(&self) -> &[u8] {
&self.name
}
pub fn value(&self) -> &[u8] {
&self.value
}
pub fn parse(input: Input) -> IResult<Self> {
// Uses complete combinators because Header has length
context(
"Header Entry",
length_value(
le_u32_complete,
separated_pair(take_until("="), tag("="), rest),
),
)
.map(|(name, value)| HeaderField {
name: SmallVec::from_slice(name),
value: SmallVec::from_slice(value),
})
.parse(input)
}
}
impl fmt::Display for HeaderField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = String::from_utf8_lossy(self.name.as_ref());
write!(f, "{}=", name)?;
for byte in self.value.iter().copied() {
write!(f, "{:02x} ", byte)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Header(pub Vec<HeaderField>);
impl Header {
pub fn parse(input: Input) -> IResult<Self> {
context(
"Header",
length_value(le_u32_streaming, many0(HeaderField::parse)),
)
.map(Header)
.parse(input)
}
pub fn find_field(&self, name: &[u8]) -> Result<&[u8]> {
self.0
.iter()
.find(|field| field.name() == name)
.map(|field| field.value())
.ok_or_else(|| Error::MissingField(String::from_utf8_lossy(name).into_owned()))
}
pub fn read_op(&self) -> Result<Op> {
self.find_field(b"op").and_then(fields::Op::parse)
}
pub fn read_u64(&self, field: &[u8]) -> Result<u64> {
self.find_field(field).and_then(fields::parse_u64)
}
pub fn read_u32(&self, field: &[u8]) -> Result<u32> {
self.find_field(field).and_then(fields::parse_u32)
}
pub fn read_string(&self, field: &[u8]) -> Result<String> {
self.find_field(field).and_then(fields::parse_string)
}
}
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for header in &self.0 {
if first {
first = false;
} else {
f.write_char('\n')?;
}
header.fmt(f)?;
}
Ok(())
}
}