112 lines
2.8 KiB
Rust
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(())
|
|
}
|
|
}
|