add date_time parser
This commit is contained in:
parent
880ef94db5
commit
0162d39deb
11 changed files with 172 additions and 110 deletions
72
Cargo.lock
generated
72
Cargo.lock
generated
|
@ -70,13 +70,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.42"
|
||||
version = "0.1.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
|
||||
checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -527,7 +527,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"strsim 0.9.3",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -538,7 +538,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -549,7 +549,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -562,7 +562,7 @@ dependencies = [
|
|||
"derive_builder_core",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -574,7 +574,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -615,7 +615,7 @@ checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -708,9 +708,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "format-bytes"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc35f5e45d6b31053cea13078ffc6fa52fa8617aa54b7ac2011720d9c009e04f"
|
||||
checksum = "1c4e89040c7fd7b4e6ba2820ac705a45def8a0c098ec78d170ae88f1ef1d5762"
|
||||
dependencies = [
|
||||
"format-bytes-macros",
|
||||
"proc-macro-hack",
|
||||
|
@ -725,7 +725,7 @@ dependencies = [
|
|||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -800,7 +800,7 @@ dependencies = [
|
|||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1086,9 +1086,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.86"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
|
@ -1336,9 +1336,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.7.0"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10acf907b94fc1b1a152d08ef97e7759650268cf986bf127f387e602b02c7e5a"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
@ -1426,6 +1426,7 @@ dependencies = [
|
|||
"assert_matches",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"derive_builder",
|
||||
"futures",
|
||||
"log",
|
||||
|
@ -1434,7 +1435,6 @@ dependencies = [
|
|||
"pest_derive",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
@ -1507,7 +1507,7 @@ dependencies = [
|
|||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1523,9 +1523,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.4"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
|
||||
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -1576,7 +1576,7 @@ dependencies = [
|
|||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -1858,22 +1858,22 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.123"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
|
||||
checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.123"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
||||
checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1904,7 +1904,7 @@ checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2027,7 +2027,7 @@ dependencies = [
|
|||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2059,9 +2059,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.60"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||
checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
|
@ -2182,7 +2182,7 @@ checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2413,7 +2413,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -2435,7 +2435,7 @@ checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -2584,7 +2584,7 @@ dependencies = [
|
|||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2608,5 +2608,5 @@ dependencies = [
|
|||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.60",
|
||||
"syn 1.0.62",
|
||||
]
|
||||
|
|
|
@ -14,18 +14,18 @@ members = ["imap", "smtp"]
|
|||
[dependencies]
|
||||
crossterm = "0.19.0"
|
||||
anyhow = "1.0.38"
|
||||
async-trait = "0.1.42"
|
||||
async-trait = "0.1.48"
|
||||
cfg-if = "1.0.0"
|
||||
chrono = "0.4.19"
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
format-bytes = "0.2.0"
|
||||
format-bytes = "0.2.2"
|
||||
futures = "0.3.13"
|
||||
inotify = { version = "0.9.2", features = ["stream"] }
|
||||
log = "0.4.14"
|
||||
notify-rust = { version = "4.2.2", default-features = false, features = ["z"] }
|
||||
notify-rust = { version = "4.3.0", default-features = false, features = ["z"] }
|
||||
parking_lot = "0.11.1"
|
||||
# pgp = "0.7.1"
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde = { version = "1.0.124", features = ["derive"] }
|
||||
structopt = "0.3.21"
|
||||
tokio = { version = "1.2.0", features = ["full"] }
|
||||
tokio-rustls = "0.22.0"
|
||||
|
|
|
@ -8,13 +8,11 @@ categories = ["email", "network-programming", "parser-implementations"]
|
|||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.38"
|
||||
async-trait = "0.1.42"
|
||||
bytes = { version = "1.0.1" }
|
||||
chrono = "0.4.19"
|
||||
derive_builder = "0.9.0"
|
||||
futures = "0.3.12"
|
||||
log = "0.4.14"
|
||||
|
@ -25,7 +23,6 @@ pest = { git = "https://github.com/iptq/pest", rev = "6a4d3a3d10e42a3ee605ca979d
|
|||
pest_derive = { git = "https://github.com/iptq/pest", rev = "6a4d3a3d10e42a3ee605ca979d0fcdac97a83a99" }
|
||||
tokio = { version = "1.1.1", features = ["full"] }
|
||||
tokio-rustls = "0.22.0"
|
||||
tokio-stream = "0.1.3"
|
||||
tokio-util = { version = "0.6.3" }
|
||||
webpki-roots = "0.21.0"
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ async fn listen<C>(
|
|||
conn: ReadHalf<C>,
|
||||
mut cmd_rx: mpsc::UnboundedReceiver<Command2>,
|
||||
greeting_tx: oneshot::Sender<()>,
|
||||
mut exit_rx: oneshot::Receiver<()>,
|
||||
exit_rx: oneshot::Receiver<()>,
|
||||
) -> Result<ReadHalf<C>>
|
||||
where
|
||||
C: AsyncRead + Unpin,
|
||||
|
@ -197,7 +197,7 @@ where
|
|||
let mut exit_rx = exit_rx.map_err(|_| ()).shared();
|
||||
// let mut exit_fut = Some(exit_rx.fuse());
|
||||
// let mut fut1 = None;
|
||||
let mut cache = String::new();
|
||||
let cache = String::new();
|
||||
|
||||
loop {
|
||||
// let mut next_line = String::new();
|
||||
|
@ -227,19 +227,6 @@ where
|
|||
}
|
||||
|
||||
resp = read_fut => {
|
||||
// trace!("read line {:?}", next_line);
|
||||
// res should not be None here
|
||||
// cache += &next_line;
|
||||
// let resp = match parse_response(&cache) {
|
||||
// Ok(v) => {
|
||||
// cache.clear();
|
||||
// v
|
||||
// }
|
||||
// Err(e) => {
|
||||
// error!("parse error: {}", e);
|
||||
// continue;
|
||||
// }
|
||||
// };
|
||||
let resp = match resp {
|
||||
Some(Ok(v)) => v,
|
||||
a => { error!("failed: {:?}", a); bail!("fuck"); },
|
||||
|
|
|
@ -13,16 +13,12 @@ impl Decoder for ImapCodec {
|
|||
|
||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
let s = std::str::from_utf8(src)?;
|
||||
debug!("-------------------------");
|
||||
debug!("s: {:?}", s);
|
||||
match parse_streamed_response(s) {
|
||||
Ok((resp, len)) => {
|
||||
src.advance(len);
|
||||
return Ok(Some(resp));
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("failed to parse: {:?} {}", e, e);
|
||||
}
|
||||
Err(e) => {}
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
|
|
|
@ -5,8 +5,7 @@ use super::Rule;
|
|||
type PSR<'a> = Box<ParserState<'a, Rule>>;
|
||||
|
||||
/// This is a hack around the literal syntax to allow us to parse characters statefully.
|
||||
pub(crate) fn literal_internal(mut state: PSR) -> PestResult<PSR> {
|
||||
// debug!("STATE: {:?}", state);
|
||||
pub(crate) fn literal_internal(state: PSR) -> PestResult<PSR> {
|
||||
use pest::Atomicity;
|
||||
|
||||
// yoinked from the generated code
|
||||
|
@ -19,10 +18,7 @@ pub(crate) fn literal_internal(mut state: PSR) -> PestResult<PSR> {
|
|||
#[allow(non_snake_case, unused_variables)]
|
||||
pub fn number(state: PSR) -> PestResult<PSR> {
|
||||
state.rule(Rule::number, |state| {
|
||||
state.sequence(|state| {
|
||||
debug!("number::atomic::sequence");
|
||||
digit(state).and_then(|state| state.repeat(digit))
|
||||
})
|
||||
state.sequence(|state| digit(state).and_then(|state| state.repeat(digit)))
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
|
@ -37,10 +33,6 @@ pub(crate) fn literal_internal(mut state: PSR) -> PestResult<PSR> {
|
|||
#[inline]
|
||||
#[allow(non_snake_case, unused_variables)]
|
||||
pub fn crlf(state: PSR) -> PestResult<PSR> {
|
||||
debug!(
|
||||
"running rule 'crlf' {:?}",
|
||||
state.queue().iter().rev().take(10).collect::<Vec<_>>()
|
||||
);
|
||||
state.sequence(|state| state.match_string("\r")?.match_string("\n"))
|
||||
}
|
||||
|
||||
|
@ -63,15 +55,11 @@ pub(crate) fn literal_internal(mut state: PSR) -> PestResult<PSR> {
|
|||
QueueableToken::Start { input_pos: pos, .. } => pos,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
debug!("start_pos: {}, end_pos: {}", start_pos, end_pos);
|
||||
|
||||
let inp = state.position().get_str();
|
||||
let seg = &inp[start_pos..end_pos];
|
||||
match seg.parse::<usize>() {
|
||||
Ok(v) => {
|
||||
debug!("got length: {}", v);
|
||||
v
|
||||
}
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"failed to parse int from {}..{} {:?}: {}",
|
||||
|
@ -95,7 +83,6 @@ pub(crate) fn literal_internal(mut state: PSR) -> PestResult<PSR> {
|
|||
state
|
||||
})
|
||||
})
|
||||
// todo!("hit internal state: {:?}", state,);
|
||||
}
|
||||
|
||||
pub(crate) fn noop(state: PSR) -> PestResult<PSR> {
|
||||
|
|
|
@ -8,6 +8,7 @@ mod tests;
|
|||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{DateTime, FixedOffset, TimeZone};
|
||||
use pest::{error::Error, iterators::Pair, Parser};
|
||||
|
||||
use crate::response::*;
|
||||
|
@ -153,7 +154,7 @@ fn build_msg_att_static(pair: Pair<Rule>) -> AttributeValue {
|
|||
|
||||
match pair.as_rule() {
|
||||
Rule::msg_att_static_internaldate => {
|
||||
AttributeValue::InternalDate(build_string(unwrap1(unwrap1(pair))))
|
||||
AttributeValue::InternalDate(build_date_time(unwrap1(pair)))
|
||||
}
|
||||
Rule::msg_att_static_rfc822_size => AttributeValue::Rfc822Size(build_number(unwrap1(pair))),
|
||||
Rule::msg_att_static_envelope => AttributeValue::Envelope(build_envelope(unwrap1(pair))),
|
||||
|
@ -272,7 +273,6 @@ fn build_capabilities(pair: Pair<Rule>) -> Vec<Capability> {
|
|||
if !matches!(pair.as_rule(), Rule::capability_data) {
|
||||
unreachable!("{:#?}", pair);
|
||||
}
|
||||
|
||||
pair.into_inner().map(build_capability).collect()
|
||||
}
|
||||
|
||||
|
@ -323,9 +323,7 @@ fn build_flag(mut pair: Pair<Rule>) -> MailboxFlag {
|
|||
}
|
||||
|
||||
fn build_mailbox_data(pair: Pair<Rule>) -> MailboxData {
|
||||
if !matches!(pair.as_rule(), Rule::mailbox_data) {
|
||||
unreachable!("{:#?}", pair);
|
||||
}
|
||||
assert!(matches!(pair.as_rule(), Rule::mailbox_data));
|
||||
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
|
@ -357,9 +355,7 @@ fn build_mailbox_data(pair: Pair<Rule>) -> MailboxData {
|
|||
}
|
||||
|
||||
fn build_mailbox_list(pair: Pair<Rule>) -> (Vec<String>, Option<String>, String) {
|
||||
if !matches!(pair.as_rule(), Rule::mailbox_list) {
|
||||
unreachable!("{:#?}", pair);
|
||||
}
|
||||
assert!(matches!(pair.as_rule(), Rule::mailbox_list));
|
||||
|
||||
let mut pairs = pair.into_inner();
|
||||
let mut pair = pairs.next().unwrap();
|
||||
|
@ -411,9 +407,7 @@ where
|
|||
T: FromStr,
|
||||
T::Err: Debug,
|
||||
{
|
||||
if !matches!(pair.as_rule(), Rule::nz_number | Rule::number) {
|
||||
unreachable!("not a number {:#?}", pair);
|
||||
}
|
||||
assert!(matches!(pair.as_rule(), Rule::nz_number | Rule::number));
|
||||
pair.as_str().parse::<T>().unwrap()
|
||||
}
|
||||
|
||||
|
@ -430,9 +424,6 @@ fn build_astring(pair: Pair<Rule>) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrapper around [build_string][1], except return None for the `nil` case
|
||||
///
|
||||
/// [1]: self::build_string
|
||||
fn build_nstring(pair: Pair<Rule>) -> Option<String> {
|
||||
assert!(matches!(pair.as_rule(), Rule::nstring));
|
||||
let pair = unwrap1(pair);
|
||||
|
@ -475,3 +466,60 @@ fn build_literal(pair: Pair<Rule>) -> String {
|
|||
let literal_str = pairs.next().unwrap();
|
||||
literal_str.as_str().to_owned()
|
||||
}
|
||||
|
||||
fn parse_zone(s: impl AsRef<str>) -> ParseResult<FixedOffset> {
|
||||
let mut pairs = Rfc3501::parse(Rule::zone, s.as_ref())?;
|
||||
let pair = pairs.next().unwrap();
|
||||
Ok(build_zone(pair))
|
||||
}
|
||||
|
||||
fn build_zone(pair: Pair<Rule>) -> FixedOffset {
|
||||
assert!(matches!(pair.as_rule(), Rule::zone));
|
||||
let n = pair.as_str().parse::<i32>().unwrap();
|
||||
let sign = if n != 0 { n / n.abs() } else { 1 };
|
||||
let h = n.abs() / 100;
|
||||
let m = n.abs() % 100;
|
||||
FixedOffset::east(sign * (h * 60 + m) * 60)
|
||||
}
|
||||
|
||||
fn build_date_time(pair: Pair<Rule>) -> DateTime<FixedOffset> {
|
||||
let mut pairs = pair.into_inner();
|
||||
let pair = pairs.next().unwrap();
|
||||
assert!(matches!(pair.as_rule(), Rule::date_day_fixed));
|
||||
let day = pair.as_str().trim().parse::<u32>().unwrap();
|
||||
|
||||
let pair = pairs.next().unwrap();
|
||||
assert!(matches!(pair.as_rule(), Rule::date_month));
|
||||
let month = match pair.as_str() {
|
||||
"Jan" => 1,
|
||||
"Feb" => 2,
|
||||
"Mar" => 3,
|
||||
"Apr" => 4,
|
||||
"May" => 5,
|
||||
"Jun" => 6,
|
||||
"Jul" => 7,
|
||||
"Aug" => 8,
|
||||
"Sep" => 9,
|
||||
"Oct" => 10,
|
||||
"Nov" => 11,
|
||||
"Dec" => 12,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let pair = pairs.next().unwrap();
|
||||
assert!(matches!(pair.as_rule(), Rule::date_year));
|
||||
let year = pair.as_str().trim().parse::<i32>().unwrap();
|
||||
|
||||
let pair = pairs.next().unwrap();
|
||||
assert!(matches!(pair.as_rule(), Rule::time));
|
||||
let mut parts = pair.as_str().split(':');
|
||||
let hour = parts.next().unwrap().parse::<u32>().unwrap();
|
||||
let minute = parts.next().unwrap().parse::<u32>().unwrap();
|
||||
let second = parts.next().unwrap().parse::<u32>().unwrap();
|
||||
|
||||
let pair = pairs.next().unwrap();
|
||||
assert!(matches!(pair.as_rule(), Rule::zone));
|
||||
let zone = build_zone(pair);
|
||||
|
||||
zone.ymd(year, month, day).and_hms(hour, minute, second)
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ continue_req = { "+" ~ sp ~ (resp_text | base64) ~ crlf }
|
|||
date_day_fixed = { (sp ~ digit) | digit{2} }
|
||||
date_month = { "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" }
|
||||
// TODO: date_time is a real date time
|
||||
// date_time = { dquote ~ date_day_fixed ~ "-" ~ date_month ~ "-" ~ date_year ~ sp ~ time ~ sp ~ zone ~ dquote }
|
||||
date_time = ${ string }
|
||||
// date_time = ${ string }
|
||||
date_time = { dquote_ ~ date_day_fixed ~ "-" ~ date_month ~ "-" ~ date_year ~ sp ~ time ~ sp ~ zone ~ dquote_ }
|
||||
date_year = @{ digit{4} }
|
||||
digit_nz = @{ '\x31'..'\x39' }
|
||||
env_bcc = { "(" ~ address{1,} ~ ")" | nil }
|
||||
|
@ -151,6 +151,7 @@ crlf = _{ cr ~ lf }
|
|||
ctl = @{ '\x00'..'\x1f' | "\x7f" }
|
||||
digit = @{ '\x30'..'\x39' }
|
||||
dquote = @{ "\"" }
|
||||
dquote_ = _{ "\"" }
|
||||
lf = _{ "\x0a" }
|
||||
sp = _{ " " }
|
||||
|
||||
|
|
|
@ -1,22 +1,56 @@
|
|||
use anyhow::Result;
|
||||
use chrono::FixedOffset;
|
||||
use pest::Parser;
|
||||
|
||||
use super::*;
|
||||
use crate::response::*;
|
||||
use pest::Parser;
|
||||
|
||||
fn parse<F, R>(r: Rule, f: F) -> impl Fn(&str) -> ParseResult<R>
|
||||
where
|
||||
F: Fn(Pair<Rule>) -> R,
|
||||
{
|
||||
move |s: &str| {
|
||||
let mut pairs = Rfc3501::parse(r, s.as_ref())?;
|
||||
let pair = pairs.next().unwrap();
|
||||
Ok(f(pair))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_literal() {
|
||||
assert_eq!(parse_literal("{7}\r\nhellosu"), Ok("hellosu".to_owned()));
|
||||
let p = parse(Rule::literal, build_literal);
|
||||
assert_eq!(p("{7}\r\nhellosu"), Ok("hellosu".to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zone() {
|
||||
let p = parse(Rule::zone, build_zone);
|
||||
assert_eq!(p("+0000"), Ok(FixedOffset::east(0)));
|
||||
assert_eq!(p("-0200"), Ok(FixedOffset::west(7200)));
|
||||
assert_eq!(p("+0330"), Ok(FixedOffset::east(12600)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_time() -> Result<()> {
|
||||
let p = parse(Rule::date_time, build_date_time);
|
||||
assert_eq!(
|
||||
p("\"17-Jul-1996 02:44:25 -0700\"")?,
|
||||
DateTime::parse_from_rfc3339("1996-07-17T02:44:25-07:00")?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn test_capability() {
|
||||
assert_eq!(parse_capability("IMAP4rev1"), Ok(Capability::Imap4rev1));
|
||||
assert_eq!(parse_capability("LOGINDISABLED"), Ok(Capability::Atom("LOGINDISABLED".to_owned())));
|
||||
assert_eq!(parse_capability("AUTH=PLAIN"), Ok(Capability::Auth("PLAIN".to_owned())));
|
||||
assert_eq!(parse_capability("auth=plain"), Ok(Capability::Auth("PLAIN".to_owned())));
|
||||
let p = parse(Rule::capability, build_capability);
|
||||
assert_eq!(p("IMAP4rev1"), Ok(Capability::Imap4rev1));
|
||||
assert_eq!(p("LOGINDISABLED"), Ok(Capability::Atom("LOGINDISABLED".to_owned())));
|
||||
assert_eq!(p("AUTH=PLAIN"), Ok(Capability::Auth("PLAIN".to_owned())));
|
||||
assert_eq!(p("auth=plain"), Ok(Capability::Auth("PLAIN".to_owned())));
|
||||
|
||||
assert!(parse_capability("(OSU)").is_err());
|
||||
assert!(parse_capability("\x01HELLO").is_err());
|
||||
assert!(p("(OSU)").is_err());
|
||||
assert!(p("\x01HELLO").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -108,7 +142,9 @@ fn test_section_8() {
|
|||
12,
|
||||
vec![
|
||||
AttributeValue::Flags(vec![MailboxFlag::Seen]),
|
||||
AttributeValue::InternalDate("17-Jul-1996 02:44:25 -0700".to_owned()),
|
||||
AttributeValue::InternalDate(
|
||||
DateTime::parse_from_rfc3339("1996-07-17T02:44:25-07:00").unwrap()
|
||||
),
|
||||
AttributeValue::Rfc822Size(4286),
|
||||
AttributeValue::Envelope(Envelope {
|
||||
date: Some("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)".to_owned()),
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Response {
|
||||
Capabilities(Vec<Capability>),
|
||||
|
@ -78,7 +80,7 @@ pub enum AttributeValue {
|
|||
BodyStructure(BodyStructure),
|
||||
Envelope(Envelope),
|
||||
Flags(Vec<MailboxFlag>),
|
||||
InternalDate(String),
|
||||
InternalDate(DateTime<FixedOffset>),
|
||||
ModSeq(u64), // RFC 4551, section 3.3.2
|
||||
Rfc822(Option<String>),
|
||||
Rfc822Header(Option<String>),
|
||||
|
|
|
@ -50,16 +50,22 @@ pub async fn run_ui(
|
|||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.margin(0)
|
||||
.constraints([Constraint::Length(1), Constraint::Max(5000)])
|
||||
.constraints([
|
||||
Constraint::Length(1),
|
||||
Constraint::Max(5000),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.split(f.size());
|
||||
|
||||
// this is the title bar
|
||||
let titles = vec!["hellosu"].into_iter().map(Spans::from).collect();
|
||||
let titles = vec!["OSU mail"].into_iter().map(Spans::from).collect();
|
||||
let tabs = Tabs::new(titles);
|
||||
f.render_widget(tabs, chunks[0]);
|
||||
|
||||
mail_tab.render(f, chunks[1]);
|
||||
// render_mail_tab(f, chunks[1], &folders, &messages);
|
||||
|
||||
let status = Paragraph::new("hellosu");
|
||||
f.render_widget(status, chunks[2]);
|
||||
})?;
|
||||
|
||||
let event = if event::poll(FRAME_DURATION)? {
|
||||
|
@ -69,7 +75,9 @@ pub async fn run_ui(
|
|||
if let Event::Key(KeyEvent { code, .. }) = event {
|
||||
let selected = mail_tab.message_list.selected();
|
||||
let len = mail_tab.messages.len();
|
||||
let seln = selected.map(|x| if x < len - 1 { x + 1 } else { x }).unwrap_or(0);
|
||||
let seln = selected
|
||||
.map(|x| if x < len - 1 { x + 1 } else { x })
|
||||
.unwrap_or(0);
|
||||
let selp = selected.map(|x| if x > 0 { x - 1 } else { 0 }).unwrap_or(0);
|
||||
match code {
|
||||
KeyCode::Char('q') => break,
|
||||
|
|
Loading…
Reference in a new issue