116 lines
2.6 KiB
Rust
116 lines
2.6 KiB
Rust
use std::fmt;
|
|
|
|
mod std_impls;
|
|
pub mod quoted;
|
|
|
|
pub fn skip_whitespace(data: &mut &str) {
|
|
*data = (*data).trim_left();
|
|
}
|
|
|
|
pub fn next_field<'a>(data: &mut &'a str) -> ::errors::Result<&'a str> {
|
|
*data = (*data).trim_left();
|
|
if data.is_empty() { bail!("missing field"); }
|
|
match data.find(char::is_whitespace) {
|
|
None => {
|
|
let result = *data;
|
|
*data = "";
|
|
Ok(result)
|
|
},
|
|
Some(next) => {
|
|
let result = &(*data)[..next];
|
|
*data = &(*data)[next..].trim_left();
|
|
Ok(result)
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn next_quoted_field(data: &mut &str) -> ::errors::Result<Vec<u8>> {
|
|
*data = (*data).trim_left();
|
|
if data.is_empty() { bail!("missing field"); }
|
|
|
|
let result = quoted::UnquoteIterator::new(data).collect::<Result<Vec<_>, _>>()?;
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn quote(data: &[u8]) -> String {
|
|
let mut result = String::with_capacity(data.len() + 2);
|
|
result.push('"');
|
|
for qc in quoted::EncodeIterator::new_quoted(data) {
|
|
result += &qc;
|
|
}
|
|
result.push('"');
|
|
result
|
|
}
|
|
|
|
// also escapes whitespace, but doesn't use quotes to surround it
|
|
pub fn escape(data: &[u8]) -> String {
|
|
let mut result = String::with_capacity(data.len());
|
|
for qc in quoted::EncodeIterator::new_encode_whitespace(data) {
|
|
result += &qc;
|
|
}
|
|
result
|
|
}
|
|
|
|
/// Each call to write!() makes sure a space is emitted to separate it
|
|
/// from previous fields.
|
|
pub struct DnsTextFormatter<'a> {
|
|
f: &'a mut fmt::Formatter<'a>,
|
|
need_space: bool,
|
|
}
|
|
|
|
impl<'a> DnsTextFormatter<'a> {
|
|
pub fn new(f: &'a mut fmt::Formatter<'a>) -> Self {
|
|
DnsTextFormatter {
|
|
f: f,
|
|
need_space: false,
|
|
}
|
|
}
|
|
|
|
/// make sure a field separator was emitted
|
|
pub fn next_field(&mut self) -> fmt::Result {
|
|
if self.need_space {
|
|
write!(self.f, " ")?;
|
|
self.need_space = false;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// a field was emitted through `inner`; next field needs a separator
|
|
pub fn end_field(&mut self) {
|
|
self.need_space = true;
|
|
}
|
|
|
|
/// direct access to underlying output; you'll need to call
|
|
/// `next_field` and `end_field` manually.
|
|
pub fn inner(&mut self) -> &mut fmt::Formatter<'a> {
|
|
self.f
|
|
}
|
|
|
|
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
|
self.next_field()?;
|
|
self.f.write_fmt(args)?;
|
|
self.end_field();
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub trait DnsTextData {
|
|
fn dns_parse(data: &mut &str) -> ::errors::Result<Self>
|
|
where
|
|
Self: Sized,
|
|
;
|
|
// format might fail if there is no (known) text representation.
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
|
}
|
|
|
|
pub fn parse<T>(data: &str) -> ::errors::Result<T>
|
|
where
|
|
T: DnsTextData
|
|
{
|
|
let mut data = data;
|
|
let result = T::dns_parse(&mut data)?;
|
|
let data = data.trim();
|
|
ensure!(data.is_empty(), "didn't parse complete text, remaining: {:?}", data);
|
|
Ok(result)
|
|
}
|