use std::ops::Range;
use std::io;
use nom::IResult;
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Begin {
inner: String,
address: Range<usize>,
timestamp: Range<usize>,
}
impl Begin {
#[inline]
pub(crate) fn ranges<T: AsRef<[u8]>>(string: T) -> io::Result<(Range<usize>, Range<usize>)> {
let string = string.as_ref();
if let IResult::Done(_, (address, timestamp)) = parser::parse(string) {
if timestamp.len() == 24 {
let a = address.as_ptr() as usize - string.as_ptr() as usize;
let t = timestamp.as_ptr() as usize - string.as_ptr() as usize;
return Ok((
Range { start: a, end: a + address.len() },
Range { start: t, end: t + timestamp.len() },
));
}
}
Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid beginning"))
}
#[inline]
pub fn new<T: Into<Vec<u8>>>(string: T) -> io::Result<Self> {
let string = string.into();
let (address, timestamp) = try!(Begin::ranges(&string));
Ok(Begin {
inner: unsafe { String::from_utf8_unchecked(string) },
address: address,
timestamp: timestamp,
})
}
#[inline]
pub fn address(&self) -> &str {
&self.inner[Range { start: self.address.start, end: self.address.end }]
}
#[inline]
pub fn timestamp(&self) -> &str {
&self.inner[Range { start: self.timestamp.start, end: self.timestamp.end }]
}
}
mod parser {
use util::parser::{is_ws, is_printable, is_printable_or_ws};
named!(pub parse(&[u8]) -> (&[u8], &[u8]),
do_parse!(
tag!("From ") >>
take_while!(is_ws) >>
addr: address >>
take_while!(is_ws) >>
time: timestamp >>
eof!() >>
(addr, time)));
named!(address(&[u8]) -> &[u8],
take_while!(is_printable));
named!(timestamp(&[u8]) -> &[u8],
take_while_n!(24, is_printable_or_ws));
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn ok() {
let v = Begin::new("From foo@example.com Wed Nov 17 14:35:53 2010").unwrap();
assert_eq!(v.address(), "foo@example.com");
assert_eq!(v.timestamp(), "Wed Nov 17 14:35:53 2010");
}
#[test]
fn fail() {
assert!(Begin::new("From foo@example.com").is_err());
assert!(Begin::new("From foo@example.com Wed Nov 17 14:35:53 20109").is_err());
}
}