From b3529a058f2dcae1bd072de946500a157a759ff4 Mon Sep 17 00:00:00 2001 From: Edward Rudd Date: Tue, 18 Oct 2022 21:26:25 -0400 Subject: [PATCH] fixup! adjust parse_until_done to return an Option so it is more versatile --- src/parse.rs | 82 ++++++++++++++++++++++++++++------------------ src/types/acls.rs | 9 ++--- src/types/quota.rs | 4 +-- 3 files changed, 55 insertions(+), 40 deletions(-) diff --git a/src/parse.rs b/src/parse.rs index 0c81312..5e3dbc6 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -63,37 +63,6 @@ where 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, - map: F, -) -> Result> -where - F: FnMut(Response<'input>) -> Result>, -{ - let mut temp_output = Vec::::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> { Map1(T), Map2(U), @@ -101,7 +70,6 @@ pub(crate) enum MapOrNot2<'a, T, U> { #[allow(dead_code)] MapVec2(Vec), Not(Response<'a>), - #[allow(dead_code)] Ignore, } @@ -149,6 +117,56 @@ where } } +fn parse_until_done_internal<'input, T, F>( + input: &'input [u8], + optional: bool, + unsolicited: &mut mpsc::Sender, + map: F, +) -> Result> +where + F: FnMut(Response<'input>) -> Result>, +{ + let mut temp_output = Vec::::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, + map: F, +) -> Result> +where + F: FnMut(Response<'input>) -> Result>, +{ + 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, + map: F, +) -> Result +where + F: FnMut(Response<'input>) -> Result>, +{ + parse_until_done_internal(input, false, unsolicited, map).map(|e| e.unwrap()) +} + pub fn parse_expunge( lines: Vec, unsolicited: &mut mpsc::Sender, diff --git a/src/types/acls.rs b/src/types/acls.rs index 182f780..b67076b 100644 --- a/src/types/acls.rs +++ b/src/types/acls.rs @@ -123,7 +123,7 @@ impl AclResponse { data: owned, acl_builder: |input| { // 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 { mailbox: a.mailbox, acls: a @@ -137,7 +137,6 @@ impl AclResponse { })), resp => Ok(MapOrNot::Not(resp)), }) - .map(|o| o.unwrap()) }, } .try_build() @@ -207,7 +206,7 @@ impl ListRightsResponse { data: owned, rights_builder: |input| { // 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 { mailbox: a.mailbox, identifier: a.identifier, @@ -216,7 +215,6 @@ impl ListRightsResponse { })), resp => Ok(MapOrNot::Not(resp)), }) - .map(|o| o.unwrap()) }, } .try_build() @@ -288,14 +286,13 @@ impl MyRightsResponse { data: owned, rights_builder: |input| { // 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 { mailbox: a.mailbox, rights: a.rights.into(), })), resp => Ok(MapOrNot::Not(resp)), }) - .map(|o| o.unwrap()) }, } .try_build() diff --git a/src/types/quota.rs b/src/types/quota.rs index f5632eb..d38e32e 100644 --- a/src/types/quota.rs +++ b/src/types/quota.rs @@ -1,5 +1,5 @@ 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 imap_proto::Response; use ouroboros::self_referencing; @@ -90,7 +90,7 @@ impl QuotaResponse { data: owned, quota_builder: |input| { // 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))), resp => Ok(MapOrNot::Not(resp)), })