diff --git a/Cargo.toml b/Cargo.toml index a57fc1a..d8dbc4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ bufstream = "0.1.3" imap-proto = "0.16.1" nom = { version = "7.1.0", default-features = false } base64 = "0.22" -chrono = { version = "0.4", default-features = false, features = ["std"]} +chrono = { version = "0.4.37", default-features = false, features = ["std"]} lazy_static = "1.4" ouroboros = "0.18.0" diff --git a/src/client.rs b/src/client.rs index bac0d1d..881eb98 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,10 +3,10 @@ use bufstream::BufStream; use chrono::{DateTime, FixedOffset}; use imap_proto::Response; use std::collections::HashSet; +use std::collections::VecDeque; use std::io::{Read, Write}; use std::ops::{Deref, DerefMut}; use std::str; -use std::sync::mpsc; use super::authenticator::Authenticator; use super::error::{Bad, Bye, Error, No, ParseError, Result, TagMismatch, ValidateError}; @@ -141,11 +141,9 @@ fn validate_sequence_set( #[derive(Debug)] pub struct Session { conn: Connection, - pub(crate) unsolicited_responses_tx: mpsc::Sender, - /// Server responses that are not related to the current command. See also the note on /// [unilateral server responses in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7). - pub unsolicited_responses: mpsc::Receiver, + pub(crate) unsolicited_responses: VecDeque, } /// An (unauthenticated) handle to talk to an IMAP server. This is what you get when first @@ -262,7 +260,7 @@ impl<'a, T: Read + Write> AppendCmd<'a, T> { self.session.stream.flush()?; self.session .read_response() - .and_then(|(lines, _)| parse_append(&lines, &mut self.session.unsolicited_responses_tx)) + .and_then(|(lines, _)| parse_append(&lines, &mut self.session.unsolicited_responses)) } } @@ -370,10 +368,10 @@ impl Client { /// /// This allows reading capabilities before authentication. pub fn capabilities(&mut self) -> Result { - // Create a temporary channel as we do not care about out of band responses before login - let (mut tx, _rx) = mpsc::channel(); + // Create a temporary vec deque as we do not care about out of band responses before login + let mut unsolicited_responses = VecDeque::new(); self.run_command_and_read_response("CAPABILITY") - .and_then(|lines| Capabilities::parse(lines, &mut tx)) + .and_then(|lines| Capabilities::parse(lines, &mut unsolicited_responses)) } /// Log in to the IMAP server. Upon success a [`Session`](struct.Session.html) instance is @@ -530,14 +528,17 @@ impl Client { impl Session { // not public, just to avoid duplicating the channel creation code fn new(conn: Connection) -> Self { - let (tx, rx) = mpsc::channel(); Session { conn, - unsolicited_responses: rx, - unsolicited_responses_tx: tx, + unsolicited_responses: VecDeque::new(), } } + /// Takes all the unsolicited responses received thus far. + pub fn take_all_unsolicited(&mut self) -> impl ExactSizeIterator { + std::mem::take(&mut self.unsolicited_responses).into_iter() + } + /// Selects a mailbox /// /// The `SELECT` command selects a mailbox so that messages in the mailbox can be accessed. @@ -561,7 +562,7 @@ impl Session { "SELECT {}", validate_str("SELECT", "mailbox", mailbox_name.as_ref())? )) - .and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) + .and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses)) } /// The `EXAMINE` command is identical to [`Session::select`] and returns the same output; @@ -573,7 +574,7 @@ impl Session { "EXAMINE {}", validate_str("EXAMINE", "mailbox", mailbox_name.as_ref())? )) - .and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx)) + .and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses)) } /// Fetch retrieves data associated with a set of messages in the mailbox. @@ -640,7 +641,7 @@ impl Session { query: impl AsRef, ) -> Result { if sequence_set.as_ref().is_empty() { - Fetches::parse(vec![], &mut self.unsolicited_responses_tx) + Fetches::parse(vec![], &mut self.unsolicited_responses) } else { let synopsis = "FETCH"; self.run_command_and_read_response(&format!( @@ -648,7 +649,7 @@ impl Session { validate_sequence_set(synopsis, "seq", sequence_set.as_ref())?, validate_str_noquote(synopsis, "query", query.as_ref())? )) - .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses)) } } @@ -660,7 +661,7 @@ impl Session { query: impl AsRef, ) -> Result { if uid_set.as_ref().is_empty() { - Fetches::parse(vec![], &mut self.unsolicited_responses_tx) + Fetches::parse(vec![], &mut self.unsolicited_responses) } else { let synopsis = "UID FETCH"; self.run_command_and_read_response(&format!( @@ -668,14 +669,14 @@ impl Session { validate_sequence_set(synopsis, "seq", uid_set.as_ref())?, validate_str_noquote(synopsis, "query", query.as_ref())? )) - .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses)) } } /// Noop always succeeds, and it does nothing. pub fn noop(&mut self) -> Result<()> { self.run_command_and_read_response("NOOP") - .and_then(|lines| parse_noop(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| parse_noop(lines, &mut self.unsolicited_responses)) } /// Logout informs the server that the client is done with the connection. @@ -807,7 +808,7 @@ impl Session { /// one of the listed capabilities. See [`Capabilities`] for further details. pub fn capabilities(&mut self) -> Result { self.run_command_and_read_response("CAPABILITY") - .and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses)) } /// The [`EXPUNGE` command](https://tools.ietf.org/html/rfc3501#section-6.4.3) permanently @@ -816,7 +817,7 @@ impl Session { pub fn expunge(&mut self) -> Result { self.run_command("EXPUNGE")?; self.read_response() - .and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses_tx)) + .and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses)) } /// The [`UID EXPUNGE` command](https://tools.ietf.org/html/rfc4315#section-2.1) permanently @@ -844,7 +845,7 @@ impl Session { pub fn uid_expunge(&mut self, uid_set: impl AsRef) -> Result { self.run_command(&format!("UID EXPUNGE {}", uid_set.as_ref()))?; self.read_response() - .and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses_tx)) + .and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses)) } /// The [`CHECK` command](https://tools.ietf.org/html/rfc3501#section-6.4.1) requests a @@ -934,7 +935,7 @@ impl Session { sequence_set.as_ref(), query.as_ref() )) - .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses)) } /// Equivalent to [`Session::store`], except that all identifiers in `sequence_set` are @@ -949,7 +950,7 @@ impl Session { uid_set.as_ref(), query.as_ref() )) - .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses)) } /// The [`COPY` command](https://tools.ietf.org/html/rfc3501#section-6.4.7) copies the @@ -1084,7 +1085,7 @@ impl Session { quote!(reference_name.unwrap_or("")), mailbox_pattern.unwrap_or("\"\"") )) - .and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses)) } /// The [`LSUB` command](https://tools.ietf.org/html/rfc3501#section-6.3.9) returns a subset of @@ -1112,7 +1113,7 @@ impl Session { quote!(reference_name.unwrap_or("")), mailbox_pattern.unwrap_or("") )) - .and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses)) } /// The [`STATUS` command](https://tools.ietf.org/html/rfc3501#section-6.3.10) requests the @@ -1161,9 +1162,7 @@ impl Session { validate_str("STATUS", "mailbox", mailbox_name)?, data_items.as_ref() )) - .and_then(|lines| { - parse_status(&lines[..], mailbox_name, &mut self.unsolicited_responses_tx) - }) + .and_then(|lines| parse_status(&lines[..], mailbox_name, &mut self.unsolicited_responses)) } /// This method returns a handle that lets you use the [`IDLE` @@ -1264,7 +1263,7 @@ impl Session { /// - `SINCE `: Messages whose internal date (disregarding time and timezone) is within or later than the specified date. pub fn search(&mut self, query: impl AsRef) -> Result> { self.run_command_and_read_response(&format!("SEARCH {}", query.as_ref())) - .and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses)) } /// Equivalent to [`Session::search`], except that the returned identifiers @@ -1272,7 +1271,7 @@ impl Session { /// command](https://tools.ietf.org/html/rfc3501#section-6.4.8). pub fn uid_search(&mut self, query: impl AsRef) -> Result> { self.run_command_and_read_response(&format!("UID SEARCH {}", query.as_ref())) - .and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses)) } /// This issues the [SORT command](https://tools.ietf.org/html/rfc5256#section-3), @@ -1292,7 +1291,7 @@ impl Session { charset, query.as_ref() )) - .and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses)) } /// Equivalent to [`Session::sort`], except that it returns [`Uid`]s. @@ -1310,7 +1309,7 @@ impl Session { charset, query.as_ref() )) - .and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses)) } /// The [`SETACL` command](https://datatracker.ietf.org/doc/html/rfc4314#section-3.1) @@ -1373,7 +1372,7 @@ impl Session { "GETACL {}", validate_str("GETACL", "mailbox", mailbox_name.as_ref())? )) - .and_then(|lines| AclResponse::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| AclResponse::parse(lines, &mut self.unsolicited_responses)) } /// The [`LISTRIGHTS` command](https://datatracker.ietf.org/doc/html/rfc4314#section-3.4) @@ -1394,7 +1393,7 @@ impl Session { validate_str("LISTRIGHTS", "mailbox", mailbox_name.as_ref())?, validate_str("LISTRIGHTS", "identifier", identifier.as_ref())? )) - .and_then(|lines| ListRightsResponse::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| ListRightsResponse::parse(lines, &mut self.unsolicited_responses)) } /// The [`MYRIGHTS` command](https://datatracker.ietf.org/doc/html/rfc4314#section-3.5) @@ -1408,7 +1407,7 @@ impl Session { "MYRIGHTS {}", validate_str("MYRIGHTS", "mailbox", mailbox_name.as_ref())?, )) - .and_then(|lines| MyRightsResponse::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| MyRightsResponse::parse(lines, &mut self.unsolicited_responses)) } /// The [`SETQUOTA` command](https://datatracker.ietf.org/doc/html/rfc2087#section-4.1) @@ -1428,7 +1427,7 @@ impl Session { validate_str("SETQUOTA", "quota_root", quota_root.as_ref())?, limits, )) - .and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses)) } /// The [`GETQUOTA` command](https://datatracker.ietf.org/doc/html/rfc2087#section-4.2) @@ -1439,7 +1438,7 @@ impl Session { "GETQUOTA {}", validate_str("GETQUOTA", "quota_root", quota_root.as_ref())? )) - .and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses)) } /// The [`GETQUOTAROOT` command](https://datatracker.ietf.org/doc/html/rfc2087#section-4.3) @@ -1450,7 +1449,7 @@ impl Session { "GETQUOTAROOT {}", validate_str("GETQUOTAROOT", "mailbox", mailbox_name.as_ref())? )) - .and_then(|lines| QuotaRootResponse::parse(lines, &mut self.unsolicited_responses_tx)) + .and_then(|lines| QuotaRootResponse::parse(lines, &mut self.unsolicited_responses)) } // these are only here because they are public interface, the rest is in `Connection` diff --git a/src/extensions/list_status.rs b/src/extensions/list_status.rs index cefa591..1cbaca2 100644 --- a/src/extensions/list_status.rs +++ b/src/extensions/list_status.rs @@ -7,9 +7,9 @@ use crate::parse::try_handle_unilateral; use crate::types::{Mailbox, Name, UnsolicitedResponse}; use imap_proto::types::{MailboxDatum, Response, StatusAttribute}; use ouroboros::self_referencing; +use std::collections::VecDeque; use std::io::{Read, Write}; use std::slice::Iter; -use std::sync::mpsc; /// A wrapper for one or more [`Name`] responses paired with optional [`Mailbox`] responses. /// @@ -27,7 +27,7 @@ impl ExtendedNames { /// Parse one or more LIST-STATUS responses from a response buffer pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> core::result::Result { ExtendedNamesTryBuilder { data: owned, @@ -145,13 +145,13 @@ impl Session { data_items: &str, ) -> Result { let reference = validate_str("LIST-STATUS", "reference", reference_name.unwrap_or(""))?; - self.run_command_and_read_response(&format!( + let lines = self.run_command_and_read_response(format!( "LIST {} {} RETURN (STATUS {})", &reference, mailbox_pattern.unwrap_or("\"\""), data_items - )) - .and_then(|lines| ExtendedNames::parse(lines, &mut self.unsolicited_responses_tx)) + ))?; + ExtendedNames::parse(lines, &mut self.unsolicited_responses) } } @@ -171,9 +171,9 @@ mod tests { * LIST (\\UnMarked) \".\" feeds\r\n\ * LIST () \".\" feeds.test\r\n\ * STATUS feeds.test (HIGHESTMODSEQ 757)\r\n"; - let (mut send, recv) = mpsc::channel(); - let fetches = ExtendedNames::parse(lines.to_vec(), &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let fetches = ExtendedNames::parse(lines.to_vec(), &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); assert!(!fetches.is_empty()); assert_eq!(fetches.len(), 4); let (name, status) = fetches.get(0).unwrap(); diff --git a/src/extensions/metadata.rs b/src/extensions/metadata.rs index 514ce52..da8864e 100644 --- a/src/extensions/metadata.rs +++ b/src/extensions/metadata.rs @@ -15,8 +15,8 @@ use crate::error::{Error, ParseError, Result}; use crate::parse::try_handle_unilateral; use crate::types::*; use imap_proto::types::{MailboxDatum, Metadata, Response, ResponseCode}; +use std::collections::VecDeque; use std::io::{Read, Write}; -use std::sync::mpsc; // for intra-doc links #[allow(unused_imports)] @@ -83,7 +83,7 @@ impl MetadataDepth { fn parse_metadata<'a>( mut lines: &'a [u8], - unsolicited: &'a mut mpsc::Sender, + unsolicited: &'a mut VecDeque, ) -> Result> { let mut res: Vec = Vec::new(); loop { @@ -197,7 +197,7 @@ impl Session { .as_str(), ); let (lines, ok) = self.run(command)?; - let meta = parse_metadata(&lines[..ok], &mut self.unsolicited_responses_tx)?; + let meta = parse_metadata(&lines[..ok], &mut self.unsolicited_responses)?; let missed = if maxsize.is_some() { if let Ok((_, Response::Done { code, .. })) = imap_proto::parser::parse_response(&lines[ok..]) diff --git a/src/parse.rs b/src/parse.rs index 942132f..34e2cac 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -2,7 +2,9 @@ use imap_proto::{MailboxDatum, Response, ResponseCode, StatusAttribute}; use lazy_static::lazy_static; use regex::Regex; use std::collections::HashSet; -use std::sync::mpsc; +use std::collections::VecDeque; +use std::convert::TryFrom; +use std::iter::Extend; use super::error::{Error, ParseError, Result}; use super::types::*; @@ -35,7 +37,7 @@ pub(crate) enum MapOrNot<'a, T> { pub(crate) fn parse_many_into<'input, T, F>( input: &'input [u8], into: &mut impl Extend, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, mut map: F, ) -> Result<()> where @@ -77,7 +79,7 @@ pub(crate) fn parse_many_into2<'input, T, U, F, IU, IT>( input: &'input [u8], into1: &mut IT, into2: &mut IU, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, mut map: F, ) -> Result<()> where @@ -118,7 +120,7 @@ where fn parse_until_done_internal<'input, T, F>( input: &'input [u8], optional: bool, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, map: F, ) -> Result> where @@ -141,7 +143,7 @@ where /// 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, + unsolicited: &mut VecDeque, map: F, ) -> Result> where @@ -156,7 +158,7 @@ where /// 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, + unsolicited: &mut VecDeque, map: F, ) -> Result where @@ -169,7 +171,7 @@ where pub fn parse_expunge( lines: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { let mut lines: &[u8] = &lines; let mut expunged = Vec::new(); @@ -222,7 +224,7 @@ pub fn parse_expunge( pub fn parse_append( mut lines: &[u8], - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { let mut appended = Appended::default(); @@ -254,10 +256,7 @@ pub fn parse_append( } } -pub fn parse_noop( - lines: Vec, - unsolicited: &mut mpsc::Sender, -) -> Result<()> { +pub fn parse_noop(lines: Vec, unsolicited: &mut VecDeque) -> Result<()> { let mut lines: &[u8] = &lines; loop { @@ -281,7 +280,7 @@ pub fn parse_noop( pub fn parse_mailbox( mut lines: &[u8], - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { let mut mailbox = Mailbox::default(); @@ -331,12 +330,10 @@ pub fn parse_mailbox( match m { MailboxDatum::Status { mailbox, status } => { - unsolicited - .send(UnsolicitedResponse::Status { - mailbox: mailbox.into(), - attributes: status, - }) - .unwrap(); + unsolicited.push_back(UnsolicitedResponse::Status { + mailbox: mailbox.into(), + attributes: status, + }) } MailboxDatum::Exists(e) => { mailbox.exists = e; @@ -352,7 +349,7 @@ pub fn parse_mailbox( } Ok((rest, Response::Expunge(n))) => { lines = rest; - unsolicited.send(UnsolicitedResponse::Expunge(n)).unwrap(); + unsolicited.push_back(UnsolicitedResponse::Expunge(n)) } Ok((_, resp)) => { break Err(resp.into()); @@ -371,7 +368,7 @@ pub fn parse_mailbox( pub fn parse_status( mut lines: &[u8], mailbox_name: &str, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { let mut mailbox = Mailbox::default(); let mut got_anything = false; @@ -417,7 +414,7 @@ pub fn parse_status( fn parse_ids_with>( lines: &[u8], - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, mut collection: T, ) -> Result { let mut lines = lines; @@ -450,14 +447,14 @@ fn parse_ids_with>( pub fn parse_id_set( lines: &[u8], - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result> { parse_ids_with(lines, unsolicited, HashSet::new()) } pub fn parse_id_seq( lines: &[u8], - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result> { parse_ids_with(lines, unsolicited, Vec::new()) } @@ -482,11 +479,11 @@ pub fn parse_idle(lines: &[u8]) -> (&[u8], Option>) // Returns `None` if the response was handled, `Some(res)` if not. pub(crate) fn try_handle_unilateral<'a>( res: Response<'a>, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Option> { match UnsolicitedResponse::try_from(res) { Ok(response) => { - unsolicited.send(response).ok(); + unsolicited.push_back(response); None } Err(unhandled) => Some(unhandled), @@ -508,10 +505,10 @@ mod tests { Capability::Atom(Cow::Borrowed("LOGINDISABLED")), ]; let lines = b"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n"; - let (mut send, recv) = mpsc::channel(); - let capabilities = Capabilities::parse(lines.to_vec(), &mut send).unwrap(); + let mut queue = VecDeque::new(); + let capabilities = Capabilities::parse(lines.to_vec(), &mut queue).unwrap(); // shouldn't be any unexpected responses parsed - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); assert_eq!(capabilities.len(), 4); for e in expected_capabilities { assert!(capabilities.has(&e)); @@ -526,10 +523,10 @@ mod tests { Capability::Atom(Cow::Borrowed("STARTTLS")), ]; let lines = b"* CAPABILITY IMAP4REV1 STARTTLS\r\n"; - let (mut send, recv) = mpsc::channel(); - let capabilities = Capabilities::parse(lines.to_vec(), &mut send).unwrap(); + let mut queue = VecDeque::new(); + let capabilities = Capabilities::parse(lines.to_vec(), &mut queue).unwrap(); // shouldn't be any unexpected responses parsed - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); assert_eq!(capabilities.len(), 2); for e in expected_capabilities { assert!(capabilities.has(&e)); @@ -539,18 +536,18 @@ mod tests { #[test] #[should_panic] fn parse_capability_invalid_test() { - let (mut send, recv) = mpsc::channel(); + let mut queue = VecDeque::new(); let lines = b"* JUNK IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n"; - Capabilities::parse(lines.to_vec(), &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + Capabilities::parse(lines.to_vec(), &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); } #[test] fn parse_names_test() { let lines = b"* LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n"; - let (mut send, recv) = mpsc::channel(); - let names = Names::parse(lines.to_vec(), &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let names = Names::parse(lines.to_vec(), &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); assert_eq!(names.len(), 1); let first = names.get(0).unwrap(); assert_eq!( @@ -564,9 +561,9 @@ mod tests { #[test] fn parse_fetches_empty() { let lines = b""; - let (mut send, recv) = mpsc::channel(); - let fetches = Fetches::parse(lines.to_vec(), &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let fetches = Fetches::parse(lines.to_vec(), &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); assert!(fetches.is_empty()); } @@ -575,9 +572,9 @@ mod tests { let lines = b"\ * 24 FETCH (FLAGS (\\Seen) UID 4827943)\r\n\ * 25 FETCH (FLAGS (\\Seen))\r\n"; - let (mut send, recv) = mpsc::channel(); - let fetches = Fetches::parse(lines.to_vec(), &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let fetches = Fetches::parse(lines.to_vec(), &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); assert_eq!(fetches.len(), 2); let first = fetches.get(0).unwrap(); assert_eq!(first.message, 24); @@ -599,9 +596,9 @@ mod tests { let lines = b"\ * 37 FETCH (UID 74)\r\n\ * 1 RECENT\r\n"; - let (mut send, recv) = mpsc::channel(); - let fetches = Fetches::parse(lines.to_vec(), &mut send).unwrap(); - assert_eq!(recv.try_recv(), Ok(UnsolicitedResponse::Recent(1))); + let mut queue = VecDeque::new(); + let fetches = Fetches::parse(lines.to_vec(), &mut queue).unwrap(); + assert_eq!(queue.pop_front(), Some(UnsolicitedResponse::Recent(1))); assert_eq!(fetches.len(), 1); let first = fetches.get(0).unwrap(); assert_eq!(first.message, 37); @@ -616,11 +613,11 @@ mod tests { let lines = b"\ * OK Searched 91% of the mailbox, ETA 0:01\r\n\ * 37 FETCH (UID 74)\r\n"; - let (mut send, recv) = mpsc::channel(); - let fetches = Fetches::parse(lines.to_vec(), &mut send).unwrap(); + let mut queue = VecDeque::new(); + let fetches = Fetches::parse(lines.to_vec(), &mut queue).unwrap(); assert_eq!( - recv.try_recv(), - Ok(UnsolicitedResponse::Ok { + queue.pop_front(), + Some(UnsolicitedResponse::Ok { code: None, information: Some(String::from("Searched 91% of the mailbox, ETA 0:01")), }) @@ -636,10 +633,10 @@ mod tests { let lines = b"\ * LIST (\\HasNoChildren) \".\" \"INBOX\"\r\n\ * 4 EXPUNGE\r\n"; - let (mut send, recv) = mpsc::channel(); - let names = Names::parse(lines.to_vec(), &mut send).unwrap(); + let mut queue = VecDeque::new(); + let names = Names::parse(lines.to_vec(), &mut queue).unwrap(); - assert_eq!(recv.try_recv().unwrap(), UnsolicitedResponse::Expunge(4)); + assert_eq!(queue.pop_front(), Some(UnsolicitedResponse::Expunge(4))); assert_eq!(names.len(), 1); let first = names.get(0).unwrap(); @@ -663,8 +660,8 @@ mod tests { * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n\ * STATUS dev.github (MESSAGES 10 UIDNEXT 11 UIDVALIDITY 1408806928 UNSEEN 0)\r\n\ * 4 EXISTS\r\n"; - let (mut send, recv) = mpsc::channel(); - let capabilities = Capabilities::parse(lines.to_vec(), &mut send).unwrap(); + let mut queue = VecDeque::new(); + let capabilities = Capabilities::parse(lines.to_vec(), &mut queue).unwrap(); assert_eq!(capabilities.len(), 4); for e in expected_capabilities { @@ -672,8 +669,8 @@ mod tests { } assert_eq!( - recv.try_recv().unwrap(), - UnsolicitedResponse::Status { + queue.pop_front(), + Some(UnsolicitedResponse::Status { mailbox: "dev.github".to_string(), attributes: vec![ StatusAttribute::Messages(10), @@ -681,9 +678,9 @@ mod tests { StatusAttribute::UidValidity(1408806928), StatusAttribute::Unseen(0) ] - } + }) ); - assert_eq!(recv.try_recv().unwrap(), UnsolicitedResponse::Exists(4)); + assert_eq!(queue.pop_front(), Some(UnsolicitedResponse::Exists(4))); } #[test] @@ -692,14 +689,14 @@ mod tests { * SEARCH 23 42 4711\r\n\ * 1 RECENT\r\n\ * STATUS INBOX (MESSAGES 10 UIDNEXT 11 UIDVALIDITY 1408806928 UNSEEN 0)\r\n"; - let (mut send, recv) = mpsc::channel(); - let ids = parse_id_set(lines, &mut send).unwrap(); + let mut queue = VecDeque::new(); + let ids = parse_id_set(lines, &mut queue).unwrap(); assert_eq!(ids, [23, 42, 4711].iter().cloned().collect()); - assert_eq!(recv.try_recv().unwrap(), UnsolicitedResponse::Recent(1)); + assert_eq!(queue.pop_front().unwrap(), UnsolicitedResponse::Recent(1)); assert_eq!( - recv.try_recv().unwrap(), + queue.pop_front().unwrap(), UnsolicitedResponse::Status { mailbox: "INBOX".to_string(), attributes: vec![ @@ -716,9 +713,9 @@ mod tests { fn parse_ids_test() { let lines = b"* SEARCH 1600 1698 1739 1781 1795 1885 1891 1892 1893 1898 1899 1901 1911 1926 1932 1933 1993 1994 2007 2032 2033 2041 2053 2062 2063 2065 2066 2072 2078 2079 2082 2084 2095 2100 2101 2102 2103 2104 2107 2116 2120 2135 2138 2154 2163 2168 2172 2189 2193 2198 2199 2205 2212 2213 2221 2227 2267 2275 2276 2295 2300 2328 2330 2332 2333 2334\r\n\ * SEARCH 2335 2336 2337 2338 2339 2341 2342 2347 2349 2350 2358 2359 2362 2369 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2390 2392 2397 2400 2401 2403 2405 2409 2411 2414 2417 2419 2420 2424 2426 2428 2439 2454 2456 2467 2468 2469 2490 2515 2519 2520 2521\r\n"; - let (mut send, recv) = mpsc::channel(); - let ids = parse_id_set(lines, &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let ids = parse_id_set(lines, &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); let ids: HashSet = ids.iter().cloned().collect(); assert_eq!( ids, @@ -739,16 +736,16 @@ mod tests { ); let lines = b"* SEARCH\r\n"; - let (mut send, recv) = mpsc::channel(); - let ids = parse_id_set(lines, &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let ids = parse_id_set(lines, &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); let ids: HashSet = ids.iter().cloned().collect(); assert_eq!(ids, HashSet::::new()); let lines = b"* SORT\r\n"; - let (mut send, recv) = mpsc::channel(); - let ids = parse_id_seq(lines, &mut send).unwrap(); - assert!(recv.try_recv().is_err()); + let mut queue = VecDeque::new(); + let ids = parse_id_seq(lines, &mut queue).unwrap(); + assert_eq!(queue.pop_front(), None); let ids: Vec = ids.iter().cloned().collect(); assert_eq!(ids, Vec::::new()); } @@ -760,8 +757,8 @@ mod tests { // two cases the VANISHED response will be a different type than expected // and so goes into the unsolicited responses channel. let lines = b"* VANISHED 3\r\n"; - let (mut send, recv) = mpsc::channel(); - let resp = parse_expunge(lines.to_vec(), &mut send).unwrap(); + let mut queue = VecDeque::new(); + let resp = parse_expunge(lines.to_vec(), &mut queue).unwrap(); // Should be not empty, and have no seqs assert!(!resp.is_empty()); @@ -773,14 +770,14 @@ mod tests { assert_eq!(None, uids.next()); // Should be nothing in the unsolicited responses channel - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); // Test VANISHED mixed with FETCH let lines = b"* VANISHED (EARLIER) 3:8,12,50:60\r\n\ * 49 FETCH (UID 117 FLAGS (\\Seen \\Answered) MODSEQ (90060115194045001))\r\n"; - let fetches = Fetches::parse(lines.to_vec(), &mut send).unwrap(); - match recv.try_recv().unwrap() { + let fetches = Fetches::parse(lines.to_vec(), &mut queue).unwrap(); + match queue.pop_front().unwrap() { UnsolicitedResponse::Vanished { earlier, uids } => { assert!(earlier); assert_eq!(uids.len(), 3); @@ -793,7 +790,7 @@ mod tests { } what => panic!("Unexpected response in unsolicited responses: {:?}", what), } - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); assert_eq!(fetches.len(), 1); let first = fetches.get(0).unwrap(); assert_eq!(first.message, 49); @@ -809,16 +806,16 @@ mod tests { // SELECT/EXAMINE (QRESYNC); UID FETCH (VANISHED); or EXPUNGE commands. In the latter // case, the VANISHED responses will be parsed with the response and the list of // expunged message is included in the returned struct. - let (mut send, recv) = mpsc::channel(); + let mut queue = VecDeque::new(); // Test VANISHED mixed with FETCH let lines = b"* VANISHED 3:5,12\r\n\ B202 OK [HIGHESTMODSEQ 20010715194045319] expunged\r\n"; - let deleted = parse_expunge(lines.to_vec(), &mut send).unwrap(); + let deleted = parse_expunge(lines.to_vec(), &mut queue).unwrap(); // No unsolicited responses, they are aggregated in the returned type - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); assert_eq!(deleted.mod_seq, Some(20010715194045319)); let mut del = deleted.uids(); @@ -836,10 +833,10 @@ mod tests { // UID assigned to the appended message in the destination mailbox. // If the MULTIAPPEND extension is also used, there can be multiple UIDs. let lines = b"A003 OK [APPENDUID 38505 3955] APPEND completed\r\n"; - let (mut send, recv) = mpsc::channel(); - let resp = parse_append(lines, &mut send).unwrap(); + let mut queue = VecDeque::new(); + let resp = parse_append(lines, &mut queue).unwrap(); - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); assert_eq!(resp.uid_validity, Some(38505)); match resp.uids { Some(uid_list) => { @@ -858,10 +855,10 @@ mod tests { // UID assigned to the appended message in the destination mailbox. // If the MULTIAPPEND extension is also used, there can be multiple UIDs. let lines = b"A003 OK [APPENDUID 38505 3955:3957] APPEND completed\r\n"; - let (mut send, recv) = mpsc::channel(); - let resp = parse_append(lines, &mut send).unwrap(); + let mut queue = VecDeque::new(); + let resp = parse_append(lines, &mut queue).unwrap(); - assert!(recv.try_recv().is_err()); + assert_eq!(queue.pop_front(), None); assert_eq!(resp.uid_validity, Some(38505)); match resp.uids { Some(uid_list) => { diff --git a/src/testing.rs b/src/testing.rs index 89460f3..4ab9ca5 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -19,7 +19,7 @@ use crate::{extensions::list_status::ExtendedNames, types::*}; /// Methods to build a [`Capabilities`] response object pub mod capabilities { use crate::types::Capabilities; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`Capabilities`] based on the provided input /// @@ -30,16 +30,16 @@ pub mod capabilities { /// let response = imap::testing::capabilities::parse(input); /// ``` pub fn parse(input: impl Into>) -> Capabilities { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - Capabilities::parse(input.into(), &mut tx).unwrap() + Capabilities::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`Fetches`] response object pub mod fetches { use crate::types::Fetches; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`Fetches`] based on the provided input /// @@ -53,16 +53,16 @@ pub mod fetches { /// let response = imap::testing::fetches::parse(input); /// ``` pub fn parse(input: impl Into>) -> Fetches { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - Fetches::parse(input.into(), &mut tx).unwrap() + Fetches::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`Names`] response object pub mod names { use crate::types::Names; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`Names`] based on the provided input /// Example input. @@ -74,16 +74,16 @@ pub mod names { /// let response = imap::testing::names::parse(input); ///``` pub fn parse(input: impl Into>) -> Names { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - Names::parse(input.into(), &mut tx).unwrap() + Names::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`ExtendedNames`] response object pub mod extended_names { use crate::extensions::list_status::ExtendedNames; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`ExtendedNames`] based on the provided input /// @@ -102,16 +102,16 @@ pub mod extended_names { /// let response = imap::testing::extended_names::parse(input); /// ``` pub fn parse(input: impl Into>) -> ExtendedNames { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - ExtendedNames::parse(input.into(), &mut tx).unwrap() + ExtendedNames::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`AclResponse`] response object pub mod acl_response { use crate::types::AclResponse; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`AclResponse`] based on the provided input /// @@ -122,16 +122,16 @@ pub mod acl_response { /// let response = imap::testing::acl_response::parse(input); /// ``` pub fn parse(input: impl Into>) -> AclResponse { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - AclResponse::parse(input.into(), &mut tx).unwrap() + AclResponse::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`ListRightsResponse`] response object pub mod list_rights_response { use crate::types::ListRightsResponse; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`ListRightsResponse`] based on the provided input /// @@ -142,16 +142,16 @@ pub mod list_rights_response { /// let response = imap::testing::list_rights_response::parse(input); ///``` pub fn parse(input: impl Into>) -> ListRightsResponse { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - ListRightsResponse::parse(input.into(), &mut tx).unwrap() + ListRightsResponse::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`MyRightsResponse`] response object pub mod my_rights_response { use crate::types::MyRightsResponse; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`MyRightsResponse`] based on the provided input /// @@ -162,16 +162,16 @@ pub mod my_rights_response { /// let response = imap::testing::my_rights_response::parse(input); /// ``` pub fn parse(input: impl Into>) -> MyRightsResponse { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - MyRightsResponse::parse(input.into(), &mut tx).unwrap() + MyRightsResponse::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`QuotaResponse`] response object pub mod quota_response { use crate::types::QuotaResponse; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`QuotaResponse`] based on the provided input /// @@ -182,16 +182,16 @@ pub mod quota_response { /// let response = imap::testing::quota_response::parse(input); /// ``` pub fn parse(input: impl Into>) -> QuotaResponse { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - QuotaResponse::parse(input.into(), &mut tx).unwrap() + QuotaResponse::parse(input.into(), &mut unsolicited_responses).unwrap() } } /// Methods to build a [`QuotaRootResponse`] response object pub mod quota_root_response { use crate::types::QuotaRootResponse; - use std::sync::mpsc; + use std::collections::VecDeque; /// Builds an [`QuotaRootResponse`] based on the provided input /// @@ -205,8 +205,8 @@ pub mod quota_root_response { /// let response = imap::testing::quota_root_response::parse(input); /// ``` pub fn parse(input: impl Into>) -> QuotaRootResponse { - let (mut tx, _rx) = mpsc::channel(); + let mut unsolicited_responses = VecDeque::new(); - QuotaRootResponse::parse(input.into(), &mut tx).unwrap() + QuotaRootResponse::parse(input.into(), &mut unsolicited_responses).unwrap() } } diff --git a/src/types/acls.rs b/src/types/acls.rs index cf89fef..a504aaf 100644 --- a/src/types/acls.rs +++ b/src/types/acls.rs @@ -8,8 +8,8 @@ use imap_proto::Response; use ouroboros::self_referencing; use std::borrow::Cow; use std::collections::HashSet; +use std::collections::VecDeque; use std::fmt::{Display, Formatter}; -use std::sync::mpsc; /// Specifies how [`Session::set_acl`] should modify an existing permission set. #[derive(Debug, Clone, Copy)] @@ -117,7 +117,7 @@ impl AclResponse { /// Parse the given input into a [`Acl`] response. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { AclResponseTryBuilder { data: owned, @@ -200,7 +200,7 @@ impl ListRightsResponse { /// Parse the given input into a [`ListRights`] response. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { ListRightsResponseTryBuilder { data: owned, @@ -280,7 +280,7 @@ impl MyRightsResponse { /// Parse the given input into a [`MyRights`] response. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { MyRightsResponseTryBuilder { data: owned, diff --git a/src/types/capabilities.rs b/src/types/capabilities.rs index d79cc37..a43892e 100644 --- a/src/types/capabilities.rs +++ b/src/types/capabilities.rs @@ -5,7 +5,7 @@ use imap_proto::{Capability, Response}; use ouroboros::self_referencing; use std::collections::hash_set::Iter; use std::collections::HashSet; -use std::sync::mpsc; +use std::collections::VecDeque; const IMAP4REV1_CAPABILITY: &str = "IMAP4rev1"; const AUTH_CAPABILITY_PREFIX: &str = "AUTH="; @@ -47,7 +47,7 @@ impl Capabilities { /// Parse the given input into one or more [`Capabilitity`] responses. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { CapabilitiesTryBuilder { data: owned, diff --git a/src/types/fetch.rs b/src/types/fetch.rs index 1b417cb..57e914f 100644 --- a/src/types/fetch.rs +++ b/src/types/fetch.rs @@ -7,8 +7,8 @@ use imap_proto::types::{ AttributeValue, BodyStructure, Envelope, MessageSection, Response, SectionPath, }; use ouroboros::self_referencing; +use std::collections::VecDeque; use std::slice::Iter; -use std::sync::mpsc; /// Format of Date and Time as defined RFC3501. /// See `date-time` element in [Formal Syntax](https://tools.ietf.org/html/rfc3501#section-9) @@ -28,7 +28,7 @@ impl Fetches { /// Parse one or more [`Fetch`] responses from a response buffer. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { FetchesTryBuilder { data: owned, diff --git a/src/types/name.rs b/src/types/name.rs index 9a2c955..0c15b84 100644 --- a/src/types/name.rs +++ b/src/types/name.rs @@ -4,8 +4,8 @@ use crate::types::UnsolicitedResponse; use imap_proto::{MailboxDatum, NameAttribute, Response}; use ouroboros::self_referencing; use std::borrow::Cow; +use std::collections::VecDeque; use std::slice::Iter; -use std::sync::mpsc; /// A wrapper for one or more [`Name`] responses. #[self_referencing] @@ -20,7 +20,7 @@ impl Names { /// Parse one or more [`Name`] from a response buffer pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { NamesTryBuilder { data: owned, diff --git a/src/types/quota.rs b/src/types/quota.rs index 8cbda63..9b1f015 100644 --- a/src/types/quota.rs +++ b/src/types/quota.rs @@ -4,8 +4,8 @@ use crate::types::UnsolicitedResponse; use imap_proto::Response; use ouroboros::self_referencing; use std::borrow::Cow; +use std::collections::VecDeque; use std::fmt::{Debug, Display, Formatter}; -use std::sync::mpsc; /// From [SETQUOTA Resource limit](https://datatracker.ietf.org/doc/html/rfc2087#section-4.1) /// @@ -105,7 +105,7 @@ impl QuotaResponse { /// Parse the [`Quota`] response from a response buffer. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { QuotaResponseTryBuilder { data: owned, @@ -200,7 +200,7 @@ impl QuotaRootResponse { /// Parse the [`QuotaRoot`] response from a response buffer. pub(crate) fn parse( owned: Vec, - unsolicited: &mut mpsc::Sender, + unsolicited: &mut VecDeque, ) -> Result { QuotaRootResponseTryBuilder { data: owned, diff --git a/tests/imap_integration.rs b/tests/imap_integration.rs index 713e6c8..e2d2a49 100644 --- a/tests/imap_integration.rs +++ b/tests/imap_integration.rs @@ -5,6 +5,7 @@ extern crate native_tls; use chrono::{FixedOffset, TimeZone}; use lettre::Transport; +use std::collections::VecDeque; use std::net::TcpStream; use crate::imap::extensions::sort::{SortCharset, SortCriterion}; @@ -206,10 +207,7 @@ fn inbox() { // we should also get two unsolicited responses: Exists and Recent c.noop().unwrap(); - let mut unsolicited = Vec::new(); - while let Ok(m) = c.unsolicited_responses.try_recv() { - unsolicited.push(m); - } + let unsolicited: VecDeque<_> = c.take_all_unsolicited().collect(); assert_eq!(unsolicited.len(), 2); assert!(unsolicited .iter() @@ -324,10 +322,7 @@ fn inbox_uid() { // we should also get two unsolicited responses: Exists and Recent c.noop().unwrap(); - let mut unsolicited = Vec::new(); - while let Ok(m) = c.unsolicited_responses.try_recv() { - unsolicited.push(m); - } + let unsolicited: VecDeque<_> = c.take_all_unsolicited().collect(); assert_eq!(unsolicited.len(), 2); assert!(unsolicited .iter() @@ -502,12 +497,9 @@ fn append_with_flags_and_date() { let mbox = "INBOX"; c.select(mbox).unwrap(); // append - #[allow(deprecated)] - // ymd_opt is deprecated in chrono 0.4.23 and replace with new with_ymd_and_hms let date = FixedOffset::east_opt(8 * 3600) .unwrap() - .ymd_opt(2020, 12, 13) - .and_hms_opt(13, 36, 36) + .with_ymd_and_hms(2020, 12, 13, 13, 36, 36) .unwrap(); c.append(mbox, &e.formatted()) .flag(Flag::Seen)