parse date + fetch
This commit is contained in:
parent
a43b3ead2a
commit
f35ec53938
13 changed files with 216 additions and 39 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -768,6 +768,7 @@ dependencies = [
|
||||||
"format-bytes",
|
"format-bytes",
|
||||||
"log",
|
"log",
|
||||||
"nom",
|
"nom",
|
||||||
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
3
Justfile
3
Justfile
|
@ -7,6 +7,9 @@ fmt:
|
||||||
doc:
|
doc:
|
||||||
cargo doc --workspace --no-deps
|
cargo doc --workspace --no-deps
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo test --all
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
cargo watch -c
|
cargo watch -c
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl<'a> Decoder for ImapCodec {
|
||||||
Err(Err::Error(err)) | Err(Err::Failure(err)) => {
|
Err(Err::Error(err)) | Err(Err::Failure(err)) => {
|
||||||
let buf4 = buf3.clone().into();
|
let buf4 = buf3.clone().into();
|
||||||
error!("failed to parse: {:?}", buf4);
|
error!("failed to parse: {:?}", buf4);
|
||||||
error!("code: {}", convert_error(buf4, err));
|
error!("code: {}", convert_error(buf4, &err));
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("error during parsing of {:?}", buf),
|
format!("error during parsing of {:?}", buf),
|
||||||
|
|
|
@ -13,7 +13,3 @@ pub mod rfc6154;
|
||||||
|
|
||||||
#[cfg(feature = "rfc2177")]
|
#[cfg(feature = "rfc2177")]
|
||||||
pub mod rfc2177;
|
pub mod rfc2177;
|
||||||
|
|
||||||
// tests
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod test_rfc3501;
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ impl DisplayBytes for Tag {
|
||||||
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> { write_bytes!(w, b"{}", self.0) }
|
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> { write_bytes!(w, b"{}", self.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Timestamp(DateTime<FixedOffset>);
|
pub struct Timestamp(pub(crate) DateTime<FixedOffset>);
|
||||||
|
|
||||||
#[cfg(feature = "fuzzing")]
|
#[cfg(feature = "fuzzing")]
|
||||||
impl<'a> Arbitrary<'a> for Timestamp {
|
impl<'a> Arbitrary<'a> for Timestamp {
|
||||||
|
@ -145,7 +145,7 @@ pub struct Envelope {
|
||||||
pub message_id: Option<Bytes>,
|
pub message_id: Option<Bytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
|
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
pub name: Option<Bytes>,
|
pub name: Option<Bytes>,
|
||||||
|
|
|
@ -3,21 +3,28 @@
|
||||||
//!
|
//!
|
||||||
//! Grammar from <https://datatracker.ietf.org/doc/html/rfc3501#section-9>
|
//! Grammar from <https://datatracker.ietf.org/doc/html/rfc3501#section-9>
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
|
use chrono::{FixedOffset, NaiveTime, TimeZone};
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
combinator::{map, map_res, opt, verify},
|
combinator::{map, map_res, opt, verify},
|
||||||
multi::{many0, many1},
|
multi::{count, many0, many1, many_m_n},
|
||||||
sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
|
sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
|
||||||
};
|
};
|
||||||
use panorama_proto_common::{
|
use panorama_proto_common::{
|
||||||
byte, never, parse_u32, satisfy, tagi, take, take_while1, Bytes, VResult,
|
byte, never, parse_num, satisfy, tagi, take, take_while1, Bytes, VResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::response::{
|
use super::response::{
|
||||||
Address, Atom, Capability, Condition, Envelope, Flag, Mailbox, MailboxData, MailboxList,
|
Address, Atom, Capability, Condition, Envelope, Flag, Mailbox, MailboxData, MailboxList,
|
||||||
MailboxListFlag, MessageAttribute, Response, ResponseCode, ResponseText, Status, Tag,
|
MailboxListFlag, MessageAttribute, Response, ResponseCode, ResponseText, Status, Tag,
|
||||||
|
Timestamp,
|
||||||
|
};
|
||||||
|
use super::rfc2234::{
|
||||||
|
is_char, is_cr, is_ctl, is_digit, is_dquote, is_lf, is_sp, CRLF, DIGIT, DQUOTE, SP,
|
||||||
};
|
};
|
||||||
use super::rfc2234::{is_char, is_cr, is_ctl, is_digit, is_dquote, is_lf, is_sp, CRLF, DQUOTE, SP};
|
|
||||||
|
|
||||||
/// Grammar rule `T / nil` produces `Option<T>`
|
/// Grammar rule `T / nil` produces `Option<T>`
|
||||||
macro_rules! opt_nil {
|
macro_rules! opt_nil {
|
||||||
|
@ -89,6 +96,54 @@ rule!(pub continue_req : Response => delimited(pair(byte(b'+'), SP),
|
||||||
map(resp_text, Response::Continue),
|
map(resp_text, Response::Continue),
|
||||||
CRLF));
|
CRLF));
|
||||||
|
|
||||||
|
rule!(pub date_day : u32 => map(many_m_n(1, 2, DIGIT), |s| match s.as_slice() {
|
||||||
|
&[x] => (x - b'0') as u32,
|
||||||
|
&[x, y] => (x - b'0') as u32 * 10 + (y - b'0') as u32,
|
||||||
|
_ => unreachable!("only up to two digits"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
rule!(pub date_day_fixed : u32 => alt((
|
||||||
|
map(preceded(SP, DIGIT), |d| (d - b'0') as u32),
|
||||||
|
map(pair(DIGIT, DIGIT), |(x, y)| (x - b'0') as u32 * 10 + (y - b'0') as u32),
|
||||||
|
)));
|
||||||
|
|
||||||
|
rule!(pub date_month : u32 => alt((
|
||||||
|
map(tagi(b"Jan"), |_| 1),
|
||||||
|
map(tagi(b"Feb"), |_| 2),
|
||||||
|
map(tagi(b"Mar"), |_| 3),
|
||||||
|
map(tagi(b"Apr"), |_| 4),
|
||||||
|
map(tagi(b"May"), |_| 5),
|
||||||
|
map(tagi(b"Jun"), |_| 6),
|
||||||
|
map(tagi(b"Jul"), |_| 7),
|
||||||
|
map(tagi(b"Aug"), |_| 8),
|
||||||
|
map(tagi(b"Sep"), |_| 9),
|
||||||
|
map(tagi(b"Oct"), |_| 10),
|
||||||
|
map(tagi(b"Nov"), |_| 11),
|
||||||
|
map(tagi(b"Dec"), |_| 12),
|
||||||
|
)));
|
||||||
|
|
||||||
|
rule!(pub date_time : Timestamp => delimited(DQUOTE,
|
||||||
|
map_res(tuple((
|
||||||
|
date_day_fixed,
|
||||||
|
byte(b'-'),
|
||||||
|
date_month,
|
||||||
|
byte(b'-'),
|
||||||
|
date_year,
|
||||||
|
SP,
|
||||||
|
time,
|
||||||
|
SP,
|
||||||
|
zone,
|
||||||
|
)), |(d, _, m, _, y, _, time, _, zone)| {
|
||||||
|
eprintln!("{}-{}-{} {:?} {:?}", y, m, d, time, zone);
|
||||||
|
zone.ymd(y, m, d)
|
||||||
|
.and_time(time)
|
||||||
|
.map(Timestamp)
|
||||||
|
.ok_or_else(|| anyhow!("invalid time"))
|
||||||
|
}),
|
||||||
|
DQUOTE));
|
||||||
|
|
||||||
|
rule!(pub date_year : i32 => map_res(count(DIGIT, 4), parse_num::<_, i32>));
|
||||||
|
|
||||||
rule!(pub envelope : Envelope => map(paren!(tuple((
|
rule!(pub envelope : Envelope => map(paren!(tuple((
|
||||||
terminated(env_date, SP),
|
terminated(env_date, SP),
|
||||||
terminated(env_subject, SP),
|
terminated(env_subject, SP),
|
||||||
|
@ -215,8 +270,10 @@ rule!(pub msg_att_dynamic : MessageAttribute => alt((
|
||||||
)));
|
)));
|
||||||
|
|
||||||
rule!(pub msg_att_static : MessageAttribute => alt((
|
rule!(pub msg_att_static : MessageAttribute => alt((
|
||||||
|
map(preceded(pair(tagi(b"UID"), SP), uniqueid), MessageAttribute::Uid),
|
||||||
map(preceded(pair(tagi(b"ENVELOPE"), SP), envelope), MessageAttribute::Envelope),
|
map(preceded(pair(tagi(b"ENVELOPE"), SP), envelope), MessageAttribute::Envelope),
|
||||||
map(preceded(pair(tagi(b"ENVELOPE"), SP), envelope), MessageAttribute::Envelope),
|
map(preceded(pair(tagi(b"INTERNALDATE"), SP), date_time), MessageAttribute::InternalDate),
|
||||||
|
map(preceded(pair(tagi(b"RFC822.SIZE"), SP), number), MessageAttribute::Rfc822Size),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
rule!(pub nil : Bytes => tagi(b"NIL"));
|
rule!(pub nil : Bytes => tagi(b"NIL"));
|
||||||
|
@ -224,7 +281,7 @@ rule!(pub nil : Bytes => tagi(b"NIL"));
|
||||||
rule!(pub nstring : Option<Bytes> => opt_nil!(string));
|
rule!(pub nstring : Option<Bytes> => opt_nil!(string));
|
||||||
|
|
||||||
pub(crate) fn number(i: Bytes) -> VResult<Bytes, u32> {
|
pub(crate) fn number(i: Bytes) -> VResult<Bytes, u32> {
|
||||||
map_res(take_while1(is_digit), parse_u32)(i)
|
map_res(take_while1(is_digit), parse_num::<_, u32>)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
rule!(pub nz_number : u32 => verify(number, |n| *n != 0));
|
rule!(pub nz_number : u32 => verify(number, |n| *n != 0));
|
||||||
|
@ -294,3 +351,20 @@ rule!(pub text : Bytes => map(take_while1(is_text_char), Bytes::from));
|
||||||
|
|
||||||
pub(crate) fn is_text_char(c: u8) -> bool { is_char(c) && !is_cr(c) && !is_lf(c) }
|
pub(crate) fn is_text_char(c: u8) -> bool { is_char(c) && !is_cr(c) && !is_lf(c) }
|
||||||
rule!(pub TEXT_CHAR : u8 => satisfy(is_text_char));
|
rule!(pub TEXT_CHAR : u8 => satisfy(is_text_char));
|
||||||
|
|
||||||
|
rule!(pub time : NaiveTime => map(
|
||||||
|
tuple((
|
||||||
|
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
||||||
|
byte(b':'),
|
||||||
|
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
||||||
|
byte(b':'),
|
||||||
|
map_res(count(DIGIT, 2), parse_num::<_, u32>),
|
||||||
|
)),
|
||||||
|
|(h, _, m, _, s)| NaiveTime::from_hms(h, m, s)));
|
||||||
|
|
||||||
|
rule!(pub uniqueid : u32 => nz_number);
|
||||||
|
|
||||||
|
rule!(pub zone : FixedOffset => map(pair(
|
||||||
|
alt((map(byte(b'+'), |_| true), map(byte(b'-'), |_| false), )),
|
||||||
|
map_res(count(DIGIT, 4), parse_num::<_, i32>),
|
||||||
|
), |(pos, value)| if pos { FixedOffset::east(value) } else { FixedOffset::west(value) }));
|
|
@ -1,11 +1,12 @@
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
use chrono::*;
|
||||||
use nom::{multi::*, sequence::*};
|
use nom::{multi::*, sequence::*};
|
||||||
use panorama_proto_common::*;
|
use panorama_proto_common::*;
|
||||||
|
|
||||||
use super::response::*;
|
use crate::proto::response::*;
|
||||||
use super::rfc2234::*;
|
use crate::proto::rfc2234::*;
|
||||||
use super::rfc3501::*;
|
use crate::proto::rfc3501::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_literal() {
|
fn test_literal() {
|
||||||
|
@ -22,23 +23,27 @@ fn test_literal() {
|
||||||
fn afl() { let _ = response(Bytes::from(b"* 4544444444 444 ")); }
|
fn afl() { let _ = response(Bytes::from(b"* 4544444444 444 ")); }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_date() {}
|
fn test_date() {
|
||||||
|
assert_eq!(date_year(Bytes::from(b"2021")).unwrap().1, 2021);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
date_time(Bytes::from(b"\"22-Mar-2021 01:44:12 +0000\""))
|
||||||
|
.unwrap()
|
||||||
|
.1,
|
||||||
|
Timestamp(FixedOffset::east(0).ymd(2021, 3, 22).and_hms(1, 44, 12)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fetch() {
|
fn test_fetch() {
|
||||||
|
assert!(flag_list(Bytes::from(b"()")).unwrap().1.is_empty());
|
||||||
|
|
||||||
use nom::Err;
|
use nom::Err;
|
||||||
use panorama_proto_common::convert_error;
|
use panorama_proto_common::convert_error;
|
||||||
let buf = Bytes::from(b"* 8045 FETCH (UID 8225 ENVELOPE (\"Sun, 21 Mar 2021 18:44:10 -0700\" \"SUBJECT\" ((\"SENDER\" NIL \"sender\" \"example.com\")) ((\"SENDER\" NIL \"sender\" \"example.com\")) ((\"noreply\" NIL \"noreply\" \"example.com\")) ((\"NAME\" NIL \"user\" \"gmail.com\")) NIL NIL NIL \"<HASH-99c91810@example.com>\") FLAGS () INTERNALDATE \"22-Mar-2021 01:44:12 +0000\" RFC822.SIZE 13503)\r\n".to_vec());
|
let buf = Bytes::from(b"* 8045 FETCH (UID 8225 ENVELOPE (\"Sun, 21 Mar 2021 18:44:10 -0700\" \"SUBJECT\" ((\"SENDER\" NIL \"sender\" \"example.com\")) ((\"SENDER\" NIL \"sender\" \"example.com\")) ((\"noreply\" NIL \"noreply\" \"example.com\")) ((\"NAME\" NIL \"user\" \"gmail.com\")) NIL NIL NIL \"<HASH-99c91810@example.com>\") FLAGS () INTERNALDATE \"22-Mar-2021 01:44:12 +0000\" RFC822.SIZE 13503)\r\n".to_vec());
|
||||||
let res = response(buf.clone());
|
let res = response(buf.clone());
|
||||||
let res = match res {
|
println!("response: {:?}", res);
|
||||||
Ok((_, res)) => res,
|
assert!(matches!(res.unwrap().1, Response::Fetch(8045, _)));
|
||||||
Err(Err::Error(err)) | Err(Err::Failure(err)) => {
|
|
||||||
println!("code: {}", convert_error(buf, err));
|
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
assert!(matches!(res, Response::Expunge(2)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
|
@ -21,4 +21,5 @@ log = "0.4.14"
|
||||||
nom = "6.2.1"
|
nom = "6.2.1"
|
||||||
|
|
||||||
# for fuzzing
|
# for fuzzing
|
||||||
arbitrary = { version = "1", optional = true, features = ["derive"] }
|
arbitrary = { version = "1", optional = true, features = ["derive"] }
|
||||||
|
num-traits = "0.2.14"
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::ops::{Deref, RangeBounds};
|
||||||
use format_bytes::DisplayBytes;
|
use format_bytes::DisplayBytes;
|
||||||
use nom::{
|
use nom::{
|
||||||
error::{ErrorKind, ParseError},
|
error::{ErrorKind, ParseError},
|
||||||
CompareResult, Err, IResult, InputLength, Needed,
|
CompareResult, Err, HexDisplay, IResult, InputLength, Needed,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "fuzzing")]
|
#[cfg(feature = "fuzzing")]
|
||||||
|
@ -42,6 +42,50 @@ impl DisplayBytes for Bytes {
|
||||||
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> { w.write(&*self.0).map(|_| ()) }
|
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> { w.write(&*self.0).map(|_| ()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CHARS: &[u8] = b"0123456789abcdef";
|
||||||
|
impl HexDisplay for Bytes {
|
||||||
|
fn to_hex(&self, chunk_size: usize) -> String { self.to_hex_from(chunk_size, 0) }
|
||||||
|
|
||||||
|
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
|
||||||
|
let mut v = Vec::with_capacity(self.len() * 3);
|
||||||
|
let mut i = from;
|
||||||
|
for chunk in self.chunks(chunk_size) {
|
||||||
|
let s = format!("{:08x}", i);
|
||||||
|
for &ch in s.as_bytes().iter() {
|
||||||
|
v.push(ch);
|
||||||
|
}
|
||||||
|
v.push(b'\t');
|
||||||
|
|
||||||
|
i += chunk_size;
|
||||||
|
|
||||||
|
for &byte in chunk {
|
||||||
|
v.push(CHARS[(byte >> 4) as usize]);
|
||||||
|
v.push(CHARS[(byte & 0xf) as usize]);
|
||||||
|
v.push(b' ');
|
||||||
|
}
|
||||||
|
if chunk_size > chunk.len() {
|
||||||
|
for _ in 0..(chunk_size - chunk.len()) {
|
||||||
|
v.push(b' ');
|
||||||
|
v.push(b' ');
|
||||||
|
v.push(b' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.push(b'\t');
|
||||||
|
|
||||||
|
for &byte in chunk {
|
||||||
|
if (byte >= 32 && byte <= 126) || byte >= 128 {
|
||||||
|
v.push(byte);
|
||||||
|
} else {
|
||||||
|
v.push(b'.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.push(b'\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
String::from_utf8_lossy(&v[..]).into_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<bytes::Bytes> for Bytes {
|
impl From<bytes::Bytes> for Bytes {
|
||||||
fn from(b: bytes::Bytes) -> Self { Bytes(b) }
|
fn from(b: bytes::Bytes) -> Self { Bytes(b) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,33 @@ use std::ops::Deref;
|
||||||
use bstr::ByteSlice;
|
use bstr::ByteSlice;
|
||||||
use nom::{
|
use nom::{
|
||||||
error::{VerboseError, VerboseErrorKind},
|
error::{VerboseError, VerboseErrorKind},
|
||||||
Offset,
|
Err, HexDisplay, Offset,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::VResult;
|
||||||
|
|
||||||
|
/// Same as nom's dbg_dmp, except operates on Bytes
|
||||||
|
pub fn dbg_dmp<'a, T, F, O>(mut f: F, context: &'static str) -> impl FnMut(T) -> VResult<T, O>
|
||||||
|
where
|
||||||
|
F: FnMut(T) -> VResult<T, O>,
|
||||||
|
T: AsRef<[u8]> + HexDisplay + Clone + Debug + Deref<Target = [u8]>,
|
||||||
|
{
|
||||||
|
move |i: T| match f(i.clone()) {
|
||||||
|
Err(Err::Failure(e)) => {
|
||||||
|
println!(
|
||||||
|
"{}: Error({}) at:\n{}",
|
||||||
|
context,
|
||||||
|
convert_error(i.clone(), &e),
|
||||||
|
i.to_hex(16)
|
||||||
|
);
|
||||||
|
Err(Err::Failure(e))
|
||||||
|
}
|
||||||
|
a => a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Same as nom's convert_error, except operates on u8
|
/// Same as nom's convert_error, except operates on u8
|
||||||
pub fn convert_error<I: Deref<Target = [u8]> + Debug>(input: I, e: VerboseError<I>) -> String {
|
pub fn convert_error<I: Deref<Target = [u8]> + Debug>(input: I, e: &VerboseError<I>) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
debug!("e: {:?}", e);
|
debug!("e: {:?}", e);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,6 @@ mod parsers;
|
||||||
mod rule;
|
mod rule;
|
||||||
|
|
||||||
pub use crate::bytes::{Bytes, ShitCompare, ShitNeededForParsing};
|
pub use crate::bytes::{Bytes, ShitCompare, ShitNeededForParsing};
|
||||||
pub use crate::convert_error::convert_error;
|
pub use crate::convert_error::{convert_error, dbg_dmp};
|
||||||
pub use crate::formatter::quote_string;
|
pub use crate::formatter::quote_string;
|
||||||
pub use crate::parsers::{byte, never, parse_u32, satisfy, skip, tagi, take, take_while1, VResult};
|
pub use crate::parsers::{byte, never, parse_num, satisfy, skip, tagi, take, take_while1, VResult};
|
||||||
|
|
|
@ -3,6 +3,7 @@ use nom::{
|
||||||
error::{ErrorKind, ParseError, VerboseError},
|
error::{ErrorKind, ParseError, VerboseError},
|
||||||
CompareResult, Err, IResult, InputLength, Needed, Parser, ToUsize,
|
CompareResult, Err, IResult, InputLength, Needed, Parser, ToUsize,
|
||||||
};
|
};
|
||||||
|
use num_traits::{CheckedAdd, CheckedMul, FromPrimitive, Zero};
|
||||||
|
|
||||||
use super::bytes::{ShitCompare, ShitNeededForParsing};
|
use super::bytes::{ShitCompare, ShitNeededForParsing};
|
||||||
|
|
||||||
|
@ -71,23 +72,50 @@ macro_rules! paren {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse from a [u8] into a u32 without first decoding it to UTF-8.
|
/// Parse from a [u8] into a u32 without first decoding it to UTF-8.
|
||||||
pub fn parse_u32(s: impl AsRef<[u8]>) -> Result<u32> {
|
pub fn parse_num<S, T>(s: S) -> Result<T>
|
||||||
let mut total = 0u32;
|
where
|
||||||
|
S: AsRef<[u8]>,
|
||||||
|
T: CheckedMul + Zero + CheckedAdd + FromPrimitive,
|
||||||
|
{
|
||||||
|
let mut total = T::zero();
|
||||||
|
let ten = T::from_u8(10).unwrap();
|
||||||
let s = s.as_ref();
|
let s = s.as_ref();
|
||||||
for digit in s.iter() {
|
for digit in s.iter() {
|
||||||
let digit = *digit;
|
let digit = *digit;
|
||||||
total = match total.checked_mul(10) {
|
total = match total.checked_mul(&ten) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => bail!("number {:?} overflows u32", s),
|
None => bail!("number {:?} overflow", s),
|
||||||
};
|
};
|
||||||
if !(digit >= b'0' && digit <= b'9') {
|
if !(digit >= b'0' && digit <= b'9') {
|
||||||
bail!("invalid digit {}", digit)
|
bail!("invalid digit {}", digit)
|
||||||
}
|
}
|
||||||
total += (digit - b'\x30') as u32;
|
let new_digit = T::from_u8(digit - b'\x30').unwrap();
|
||||||
|
total = match total.checked_add(&new_digit) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => bail!("number {:?} overflow", s),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(total)
|
Ok(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /// Parse from a [u8] into a u32 without first decoding it to UTF-8.
|
||||||
|
// pub fn parse_u32(s: impl AsRef<[u8]>) -> Result<u32> {
|
||||||
|
// let mut total = 0u32;
|
||||||
|
// let s = s.as_ref();
|
||||||
|
// for digit in s.iter() {
|
||||||
|
// let digit = *digit;
|
||||||
|
// total = match total.checked_mul(10) {
|
||||||
|
// Some(v) => v,
|
||||||
|
// None => bail!("number {:?} overflows u32", s),
|
||||||
|
// };
|
||||||
|
// if !(digit >= b'0' && digit <= b'9') {
|
||||||
|
// bail!("invalid digit {}", digit)
|
||||||
|
// }
|
||||||
|
// total += (digit - b'\x30') as u32;
|
||||||
|
// }
|
||||||
|
// Ok(total)
|
||||||
|
// }
|
||||||
|
|
||||||
/// Always fails, used as a no-op.
|
/// Always fails, used as a no-op.
|
||||||
pub fn never<I, O, E>(i: I) -> IResult<I, O, E>
|
pub fn never<I, O, E>(i: I) -> IResult<I, O, E>
|
||||||
where
|
where
|
||||||
|
@ -167,7 +195,10 @@ where
|
||||||
{
|
{
|
||||||
move |i: I| match i.first().map(|t| (f(t), t)) {
|
move |i: I| match i.first().map(|t| (f(t), t)) {
|
||||||
Some((true, ft)) => Ok((i.slice(1..), ft)),
|
Some((true, ft)) => Ok((i.slice(1..), ft)),
|
||||||
Some((false, _)) => Err(Err::Error(E::from_error_kind(i, ErrorKind::Satisfy))),
|
Some((false, _)) => Err(Err::Error(E::from_error_kind(
|
||||||
|
i.slice(1..),
|
||||||
|
ErrorKind::Satisfy,
|
||||||
|
))),
|
||||||
None => Err(Err::Incomplete(Needed::Unknown)),
|
None => Err(Err::Incomplete(Needed::Unknown)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ macro_rules! rule {
|
||||||
$vis fn $name (
|
$vis fn $name (
|
||||||
i: panorama_proto_common::Bytes
|
i: panorama_proto_common::Bytes
|
||||||
) -> panorama_proto_common::VResult<panorama_proto_common::Bytes, $ret> {
|
) -> panorama_proto_common::VResult<panorama_proto_common::Bytes, $ret> {
|
||||||
$expr(i)
|
panorama_proto_common::dbg_dmp($expr, stringify!($name))(i)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue