fixup! adjust parse_until_done to return an Option so it is more versatile

This commit is contained in:
Edward Rudd 2022-10-18 21:26:25 -04:00
parent 6146e0c399
commit b3529a058f
3 changed files with 55 additions and 40 deletions

View file

@ -63,37 +63,6 @@ where
Ok(()) Ok(())
} }
/// Parse and return an expected single `T` Response with `F`.
/// Responses other than `T` go into the `unsolicited` channel.
///
/// If more than one `T` are found then [`Error::Parse`] is returned
/// If zero `T` are found and optional is false then [`Error::Parse`] is returned, otherwise None is
pub(crate) fn parse_until_done<'input, T, F>(
input: &'input [u8],
optional: bool,
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
map: F,
) -> Result<Option<T>>
where
F: FnMut(Response<'input>) -> Result<MapOrNot<'input, T>>,
{
let mut temp_output = Vec::<T>::new();
parse_many_into(input, &mut temp_output, unsolicited, map)?;
match temp_output.len() {
1 => Ok(Some(temp_output.remove(0))),
0 => {
if optional {
Ok(None)
} else {
Err(Error::Parse(ParseError::Invalid(input.to_vec())))
}
}
_ => Err(Error::Parse(ParseError::Invalid(input.to_vec()))),
}
}
pub(crate) enum MapOrNot2<'a, T, U> { pub(crate) enum MapOrNot2<'a, T, U> {
Map1(T), Map1(T),
Map2(U), Map2(U),
@ -101,7 +70,6 @@ pub(crate) enum MapOrNot2<'a, T, U> {
#[allow(dead_code)] #[allow(dead_code)]
MapVec2(Vec<U>), MapVec2(Vec<U>),
Not(Response<'a>), Not(Response<'a>),
#[allow(dead_code)]
Ignore, Ignore,
} }
@ -149,6 +117,56 @@ where
} }
} }
fn parse_until_done_internal<'input, T, F>(
input: &'input [u8],
optional: bool,
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
map: F,
) -> Result<Option<T>>
where
F: FnMut(Response<'input>) -> Result<MapOrNot<'input, T>>,
{
let mut temp_output = Vec::<T>::new();
parse_many_into(input, &mut temp_output, unsolicited, map)?;
match temp_output.len() {
1 => Ok(Some(temp_output.remove(0))),
0 if optional => Ok(None),
_ => Err(Error::Parse(ParseError::Invalid(input.to_vec()))),
}
}
/// Parse and return an optional single `T` Response with `F`.
/// Responses other than `T` go into the `unsolicited` channel.
///
/// If more than one `T` are found then [`Error::Parse`] is returned
pub(crate) fn parse_until_done_optional<'input, T, F>(
input: &'input [u8],
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
map: F,
) -> Result<Option<T>>
where
F: FnMut(Response<'input>) -> Result<MapOrNot<'input, T>>,
{
parse_until_done_internal(input, true, unsolicited, map)
}
/// Parse and return an expected single `T` Response with `F`.
/// Responses other than `T` go into the `unsolicited` channel.
///
/// If zero or more than one `T` are found then [`Error::Parse`] is returned.
pub(crate) fn parse_until_done<'input, T, F>(
input: &'input [u8],
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
map: F,
) -> Result<T>
where
F: FnMut(Response<'input>) -> Result<MapOrNot<'input, T>>,
{
parse_until_done_internal(input, false, unsolicited, map).map(|e| e.unwrap())
}
pub fn parse_expunge( pub fn parse_expunge(
lines: Vec<u8>, lines: Vec<u8>,
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>, unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,

View file

@ -123,7 +123,7 @@ impl AclResponse {
data: owned, data: owned,
acl_builder: |input| { acl_builder: |input| {
// There should only be ONE single ACL response // There should only be ONE single ACL response
parse_until_done(input, false, unsolicited, |response| match response { parse_until_done(input, unsolicited, |response| match response {
Response::Acl(a) => Ok(MapOrNot::Map(Acl { Response::Acl(a) => Ok(MapOrNot::Map(Acl {
mailbox: a.mailbox, mailbox: a.mailbox,
acls: a acls: a
@ -137,7 +137,6 @@ impl AclResponse {
})), })),
resp => Ok(MapOrNot::Not(resp)), resp => Ok(MapOrNot::Not(resp)),
}) })
.map(|o| o.unwrap())
}, },
} }
.try_build() .try_build()
@ -207,7 +206,7 @@ impl ListRightsResponse {
data: owned, data: owned,
rights_builder: |input| { rights_builder: |input| {
// There should only be ONE single LISTRIGHTS response // There should only be ONE single LISTRIGHTS response
parse_until_done(input, false, unsolicited, |response| match response { parse_until_done(input, unsolicited, |response| match response {
Response::ListRights(a) => Ok(MapOrNot::Map(ListRights { Response::ListRights(a) => Ok(MapOrNot::Map(ListRights {
mailbox: a.mailbox, mailbox: a.mailbox,
identifier: a.identifier, identifier: a.identifier,
@ -216,7 +215,6 @@ impl ListRightsResponse {
})), })),
resp => Ok(MapOrNot::Not(resp)), resp => Ok(MapOrNot::Not(resp)),
}) })
.map(|o| o.unwrap())
}, },
} }
.try_build() .try_build()
@ -288,14 +286,13 @@ impl MyRightsResponse {
data: owned, data: owned,
rights_builder: |input| { rights_builder: |input| {
// There should only be ONE single MYRIGHTS response // There should only be ONE single MYRIGHTS response
parse_until_done(input, false, unsolicited, |response| match response { parse_until_done(input, unsolicited, |response| match response {
Response::MyRights(a) => Ok(MapOrNot::Map(MyRights { Response::MyRights(a) => Ok(MapOrNot::Map(MyRights {
mailbox: a.mailbox, mailbox: a.mailbox,
rights: a.rights.into(), rights: a.rights.into(),
})), })),
resp => Ok(MapOrNot::Not(resp)), resp => Ok(MapOrNot::Not(resp)),
}) })
.map(|o| o.unwrap())
}, },
} }
.try_build() .try_build()

View file

@ -1,5 +1,5 @@
use crate::error::{Error, ParseError}; use crate::error::{Error, ParseError};
use crate::parse::{parse_many_into2, parse_until_done, MapOrNot, MapOrNot2}; use crate::parse::{parse_many_into2, parse_until_done_optional, MapOrNot, MapOrNot2};
use crate::types::UnsolicitedResponse; use crate::types::UnsolicitedResponse;
use imap_proto::Response; use imap_proto::Response;
use ouroboros::self_referencing; use ouroboros::self_referencing;
@ -90,7 +90,7 @@ impl QuotaResponse {
data: owned, data: owned,
quota_builder: |input| { quota_builder: |input| {
// There should zero or one QUOTA response // There should zero or one QUOTA response
parse_until_done(input, true, unsolicited, |response| match response { parse_until_done_optional(input, unsolicited, |response| match response {
Response::Quota(q) => Ok(MapOrNot::Map(Quota::from_imap_proto(q))), Response::Quota(q) => Ok(MapOrNot::Map(Quota::from_imap_proto(q))),
resp => Ok(MapOrNot::Not(resp)), resp => Ok(MapOrNot::Not(resp)),
}) })