Skip to content

Commit 6fe22ed

Browse files
authored
Replace mpsc::{Sender,Receiver} by VecDeque (#260)
1 parent a7d22f0 commit 6fe22ed

12 files changed

+178
-190
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ bufstream = "0.1.3"
2929
imap-proto = "0.16.1"
3030
nom = { version = "7.1.0", default-features = false }
3131
base64 = "0.22"
32-
chrono = { version = "0.4", default-features = false, features = ["std"]}
32+
chrono = { version = "0.4.37", default-features = false, features = ["std"]}
3333
lazy_static = "1.4"
3434
ouroboros = "0.18.0"
3535

src/client.rs

+37-38
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use bufstream::BufStream;
33
use chrono::{DateTime, FixedOffset};
44
use imap_proto::Response;
55
use std::collections::HashSet;
6+
use std::collections::VecDeque;
67
use std::io::{Read, Write};
78
use std::ops::{Deref, DerefMut};
89
use std::str;
9-
use std::sync::mpsc;
1010

1111
use super::authenticator::Authenticator;
1212
use super::error::{Bad, Bye, Error, No, ParseError, Result, TagMismatch, ValidateError};
@@ -141,11 +141,9 @@ fn validate_sequence_set(
141141
#[derive(Debug)]
142142
pub struct Session<T: Read + Write> {
143143
conn: Connection<T>,
144-
pub(crate) unsolicited_responses_tx: mpsc::Sender<UnsolicitedResponse>,
145-
146144
/// Server responses that are not related to the current command. See also the note on
147145
/// [unilateral server responses in RFC 3501](https://tools.ietf.org/html/rfc3501#section-7).
148-
pub unsolicited_responses: mpsc::Receiver<UnsolicitedResponse>,
146+
pub(crate) unsolicited_responses: VecDeque<UnsolicitedResponse>,
149147
}
150148

151149
/// 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> {
262260
self.session.stream.flush()?;
263261
self.session
264262
.read_response()
265-
.and_then(|(lines, _)| parse_append(&lines, &mut self.session.unsolicited_responses_tx))
263+
.and_then(|(lines, _)| parse_append(&lines, &mut self.session.unsolicited_responses))
266264
}
267265
}
268266

@@ -370,10 +368,10 @@ impl<T: Read + Write> Client<T> {
370368
///
371369
/// This allows reading capabilities before authentication.
372370
pub fn capabilities(&mut self) -> Result<Capabilities> {
373-
// Create a temporary channel as we do not care about out of band responses before login
374-
let (mut tx, _rx) = mpsc::channel();
371+
// Create a temporary vec deque as we do not care about out of band responses before login
372+
let mut unsolicited_responses = VecDeque::new();
375373
self.run_command_and_read_response("CAPABILITY")
376-
.and_then(|lines| Capabilities::parse(lines, &mut tx))
374+
.and_then(|lines| Capabilities::parse(lines, &mut unsolicited_responses))
377375
}
378376

379377
/// Log in to the IMAP server. Upon success a [`Session`](struct.Session.html) instance is
@@ -530,14 +528,17 @@ impl<T: Read + Write> Client<T> {
530528
impl<T: Read + Write> Session<T> {
531529
// not public, just to avoid duplicating the channel creation code
532530
fn new(conn: Connection<T>) -> Self {
533-
let (tx, rx) = mpsc::channel();
534531
Session {
535532
conn,
536-
unsolicited_responses: rx,
537-
unsolicited_responses_tx: tx,
533+
unsolicited_responses: VecDeque::new(),
538534
}
539535
}
540536

537+
/// Takes all the unsolicited responses received thus far.
538+
pub fn take_all_unsolicited(&mut self) -> impl ExactSizeIterator<Item = UnsolicitedResponse> {
539+
std::mem::take(&mut self.unsolicited_responses).into_iter()
540+
}
541+
541542
/// Selects a mailbox
542543
///
543544
/// The `SELECT` command selects a mailbox so that messages in the mailbox can be accessed.
@@ -561,7 +562,7 @@ impl<T: Read + Write> Session<T> {
561562
"SELECT {}",
562563
validate_str("SELECT", "mailbox", mailbox_name.as_ref())?
563564
))
564-
.and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx))
565+
.and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses))
565566
}
566567

567568
/// The `EXAMINE` command is identical to [`Session::select`] and returns the same output;
@@ -573,7 +574,7 @@ impl<T: Read + Write> Session<T> {
573574
"EXAMINE {}",
574575
validate_str("EXAMINE", "mailbox", mailbox_name.as_ref())?
575576
))
576-
.and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses_tx))
577+
.and_then(|(lines, _)| parse_mailbox(&lines[..], &mut self.unsolicited_responses))
577578
}
578579

579580
/// Fetch retrieves data associated with a set of messages in the mailbox.
@@ -640,15 +641,15 @@ impl<T: Read + Write> Session<T> {
640641
query: impl AsRef<str>,
641642
) -> Result<Fetches> {
642643
if sequence_set.as_ref().is_empty() {
643-
Fetches::parse(vec![], &mut self.unsolicited_responses_tx)
644+
Fetches::parse(vec![], &mut self.unsolicited_responses)
644645
} else {
645646
let synopsis = "FETCH";
646647
self.run_command_and_read_response(&format!(
647648
"FETCH {} {}",
648649
validate_sequence_set(synopsis, "seq", sequence_set.as_ref())?,
649650
validate_str_noquote(synopsis, "query", query.as_ref())?
650651
))
651-
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx))
652+
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses))
652653
}
653654
}
654655

@@ -660,22 +661,22 @@ impl<T: Read + Write> Session<T> {
660661
query: impl AsRef<str>,
661662
) -> Result<Fetches> {
662663
if uid_set.as_ref().is_empty() {
663-
Fetches::parse(vec![], &mut self.unsolicited_responses_tx)
664+
Fetches::parse(vec![], &mut self.unsolicited_responses)
664665
} else {
665666
let synopsis = "UID FETCH";
666667
self.run_command_and_read_response(&format!(
667668
"UID FETCH {} {}",
668669
validate_sequence_set(synopsis, "seq", uid_set.as_ref())?,
669670
validate_str_noquote(synopsis, "query", query.as_ref())?
670671
))
671-
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx))
672+
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses))
672673
}
673674
}
674675

675676
/// Noop always succeeds, and it does nothing.
676677
pub fn noop(&mut self) -> Result<()> {
677678
self.run_command_and_read_response("NOOP")
678-
.and_then(|lines| parse_noop(lines, &mut self.unsolicited_responses_tx))
679+
.and_then(|lines| parse_noop(lines, &mut self.unsolicited_responses))
679680
}
680681

681682
/// Logout informs the server that the client is done with the connection.
@@ -807,7 +808,7 @@ impl<T: Read + Write> Session<T> {
807808
/// one of the listed capabilities. See [`Capabilities`] for further details.
808809
pub fn capabilities(&mut self) -> Result<Capabilities> {
809810
self.run_command_and_read_response("CAPABILITY")
810-
.and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses_tx))
811+
.and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses))
811812
}
812813

813814
/// The [`EXPUNGE` command](https://tools.ietf.org/html/rfc3501#section-6.4.3) permanently
@@ -816,7 +817,7 @@ impl<T: Read + Write> Session<T> {
816817
pub fn expunge(&mut self) -> Result<Deleted> {
817818
self.run_command("EXPUNGE")?;
818819
self.read_response()
819-
.and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses_tx))
820+
.and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses))
820821
}
821822

822823
/// The [`UID EXPUNGE` command](https://tools.ietf.org/html/rfc4315#section-2.1) permanently
@@ -844,7 +845,7 @@ impl<T: Read + Write> Session<T> {
844845
pub fn uid_expunge(&mut self, uid_set: impl AsRef<str>) -> Result<Deleted> {
845846
self.run_command(&format!("UID EXPUNGE {}", uid_set.as_ref()))?;
846847
self.read_response()
847-
.and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses_tx))
848+
.and_then(|(lines, _)| parse_expunge(lines, &mut self.unsolicited_responses))
848849
}
849850

850851
/// The [`CHECK` command](https://tools.ietf.org/html/rfc3501#section-6.4.1) requests a
@@ -934,7 +935,7 @@ impl<T: Read + Write> Session<T> {
934935
sequence_set.as_ref(),
935936
query.as_ref()
936937
))
937-
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx))
938+
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses))
938939
}
939940

940941
/// Equivalent to [`Session::store`], except that all identifiers in `sequence_set` are
@@ -949,7 +950,7 @@ impl<T: Read + Write> Session<T> {
949950
uid_set.as_ref(),
950951
query.as_ref()
951952
))
952-
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses_tx))
953+
.and_then(|lines| Fetches::parse(lines, &mut self.unsolicited_responses))
953954
}
954955

955956
/// The [`COPY` command](https://tools.ietf.org/html/rfc3501#section-6.4.7) copies the
@@ -1084,7 +1085,7 @@ impl<T: Read + Write> Session<T> {
10841085
quote!(reference_name.unwrap_or("")),
10851086
mailbox_pattern.unwrap_or("\"\"")
10861087
))
1087-
.and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses_tx))
1088+
.and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses))
10881089
}
10891090

10901091
/// The [`LSUB` command](https://tools.ietf.org/html/rfc3501#section-6.3.9) returns a subset of
@@ -1112,7 +1113,7 @@ impl<T: Read + Write> Session<T> {
11121113
quote!(reference_name.unwrap_or("")),
11131114
mailbox_pattern.unwrap_or("")
11141115
))
1115-
.and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses_tx))
1116+
.and_then(|lines| Names::parse(lines, &mut self.unsolicited_responses))
11161117
}
11171118

11181119
/// The [`STATUS` command](https://tools.ietf.org/html/rfc3501#section-6.3.10) requests the
@@ -1161,9 +1162,7 @@ impl<T: Read + Write> Session<T> {
11611162
validate_str("STATUS", "mailbox", mailbox_name)?,
11621163
data_items.as_ref()
11631164
))
1164-
.and_then(|lines| {
1165-
parse_status(&lines[..], mailbox_name, &mut self.unsolicited_responses_tx)
1166-
})
1165+
.and_then(|lines| parse_status(&lines[..], mailbox_name, &mut self.unsolicited_responses))
11671166
}
11681167

11691168
/// This method returns a handle that lets you use the [`IDLE`
@@ -1264,15 +1263,15 @@ impl<T: Read + Write> Session<T> {
12641263
/// - `SINCE <date>`: Messages whose internal date (disregarding time and timezone) is within or later than the specified date.
12651264
pub fn search(&mut self, query: impl AsRef<str>) -> Result<HashSet<Seq>> {
12661265
self.run_command_and_read_response(&format!("SEARCH {}", query.as_ref()))
1267-
.and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses_tx))
1266+
.and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses))
12681267
}
12691268

12701269
/// Equivalent to [`Session::search`], except that the returned identifiers
12711270
/// are [`Uid`] instead of [`Seq`]. See also the [`UID`
12721271
/// command](https://tools.ietf.org/html/rfc3501#section-6.4.8).
12731272
pub fn uid_search(&mut self, query: impl AsRef<str>) -> Result<HashSet<Uid>> {
12741273
self.run_command_and_read_response(&format!("UID SEARCH {}", query.as_ref()))
1275-
.and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses_tx))
1274+
.and_then(|lines| parse_id_set(&lines, &mut self.unsolicited_responses))
12761275
}
12771276

12781277
/// This issues the [SORT command](https://tools.ietf.org/html/rfc5256#section-3),
@@ -1292,7 +1291,7 @@ impl<T: Read + Write> Session<T> {
12921291
charset,
12931292
query.as_ref()
12941293
))
1295-
.and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses_tx))
1294+
.and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses))
12961295
}
12971296

12981297
/// Equivalent to [`Session::sort`], except that it returns [`Uid`]s.
@@ -1310,7 +1309,7 @@ impl<T: Read + Write> Session<T> {
13101309
charset,
13111310
query.as_ref()
13121311
))
1313-
.and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses_tx))
1312+
.and_then(|lines| parse_id_seq(&lines, &mut self.unsolicited_responses))
13141313
}
13151314

13161315
/// The [`SETACL` command](https://datatracker.ietf.org/doc/html/rfc4314#section-3.1)
@@ -1373,7 +1372,7 @@ impl<T: Read + Write> Session<T> {
13731372
"GETACL {}",
13741373
validate_str("GETACL", "mailbox", mailbox_name.as_ref())?
13751374
))
1376-
.and_then(|lines| AclResponse::parse(lines, &mut self.unsolicited_responses_tx))
1375+
.and_then(|lines| AclResponse::parse(lines, &mut self.unsolicited_responses))
13771376
}
13781377

13791378
/// The [`LISTRIGHTS` command](https://datatracker.ietf.org/doc/html/rfc4314#section-3.4)
@@ -1394,7 +1393,7 @@ impl<T: Read + Write> Session<T> {
13941393
validate_str("LISTRIGHTS", "mailbox", mailbox_name.as_ref())?,
13951394
validate_str("LISTRIGHTS", "identifier", identifier.as_ref())?
13961395
))
1397-
.and_then(|lines| ListRightsResponse::parse(lines, &mut self.unsolicited_responses_tx))
1396+
.and_then(|lines| ListRightsResponse::parse(lines, &mut self.unsolicited_responses))
13981397
}
13991398

14001399
/// The [`MYRIGHTS` command](https://datatracker.ietf.org/doc/html/rfc4314#section-3.5)
@@ -1408,7 +1407,7 @@ impl<T: Read + Write> Session<T> {
14081407
"MYRIGHTS {}",
14091408
validate_str("MYRIGHTS", "mailbox", mailbox_name.as_ref())?,
14101409
))
1411-
.and_then(|lines| MyRightsResponse::parse(lines, &mut self.unsolicited_responses_tx))
1410+
.and_then(|lines| MyRightsResponse::parse(lines, &mut self.unsolicited_responses))
14121411
}
14131412

14141413
/// The [`SETQUOTA` command](https://datatracker.ietf.org/doc/html/rfc2087#section-4.1)
@@ -1428,7 +1427,7 @@ impl<T: Read + Write> Session<T> {
14281427
validate_str("SETQUOTA", "quota_root", quota_root.as_ref())?,
14291428
limits,
14301429
))
1431-
.and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses_tx))
1430+
.and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses))
14321431
}
14331432

14341433
/// The [`GETQUOTA` command](https://datatracker.ietf.org/doc/html/rfc2087#section-4.2)
@@ -1439,7 +1438,7 @@ impl<T: Read + Write> Session<T> {
14391438
"GETQUOTA {}",
14401439
validate_str("GETQUOTA", "quota_root", quota_root.as_ref())?
14411440
))
1442-
.and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses_tx))
1441+
.and_then(|lines| QuotaResponse::parse(lines, &mut self.unsolicited_responses))
14431442
}
14441443

14451444
/// The [`GETQUOTAROOT` command](https://datatracker.ietf.org/doc/html/rfc2087#section-4.3)
@@ -1450,7 +1449,7 @@ impl<T: Read + Write> Session<T> {
14501449
"GETQUOTAROOT {}",
14511450
validate_str("GETQUOTAROOT", "mailbox", mailbox_name.as_ref())?
14521451
))
1453-
.and_then(|lines| QuotaRootResponse::parse(lines, &mut self.unsolicited_responses_tx))
1452+
.and_then(|lines| QuotaRootResponse::parse(lines, &mut self.unsolicited_responses))
14541453
}
14551454

14561455
// these are only here because they are public interface, the rest is in `Connection`

src/extensions/list_status.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use crate::parse::try_handle_unilateral;
77
use crate::types::{Mailbox, Name, UnsolicitedResponse};
88
use imap_proto::types::{MailboxDatum, Response, StatusAttribute};
99
use ouroboros::self_referencing;
10+
use std::collections::VecDeque;
1011
use std::io::{Read, Write};
1112
use std::slice::Iter;
12-
use std::sync::mpsc;
1313

1414
/// A wrapper for one or more [`Name`] responses paired with optional [`Mailbox`] responses.
1515
///
@@ -27,7 +27,7 @@ impl ExtendedNames {
2727
/// Parse one or more LIST-STATUS responses from a response buffer
2828
pub(crate) fn parse(
2929
owned: Vec<u8>,
30-
unsolicited: &mut mpsc::Sender<UnsolicitedResponse>,
30+
unsolicited: &mut VecDeque<UnsolicitedResponse>,
3131
) -> core::result::Result<Self, Error> {
3232
ExtendedNamesTryBuilder {
3333
data: owned,
@@ -145,13 +145,13 @@ impl<T: Read + Write> Session<T> {
145145
data_items: &str,
146146
) -> Result<ExtendedNames> {
147147
let reference = validate_str("LIST-STATUS", "reference", reference_name.unwrap_or(""))?;
148-
self.run_command_and_read_response(&format!(
148+
let lines = self.run_command_and_read_response(format!(
149149
"LIST {} {} RETURN (STATUS {})",
150150
&reference,
151151
mailbox_pattern.unwrap_or("\"\""),
152152
data_items
153-
))
154-
.and_then(|lines| ExtendedNames::parse(lines, &mut self.unsolicited_responses_tx))
153+
))?;
154+
ExtendedNames::parse(lines, &mut self.unsolicited_responses)
155155
}
156156
}
157157

@@ -171,9 +171,9 @@ mod tests {
171171
* LIST (\\UnMarked) \".\" feeds\r\n\
172172
* LIST () \".\" feeds.test\r\n\
173173
* STATUS feeds.test (HIGHESTMODSEQ 757)\r\n";
174-
let (mut send, recv) = mpsc::channel();
175-
let fetches = ExtendedNames::parse(lines.to_vec(), &mut send).unwrap();
176-
assert!(recv.try_recv().is_err());
174+
let mut queue = VecDeque::new();
175+
let fetches = ExtendedNames::parse(lines.to_vec(), &mut queue).unwrap();
176+
assert_eq!(queue.pop_front(), None);
177177
assert!(!fetches.is_empty());
178178
assert_eq!(fetches.len(), 4);
179179
let (name, status) = fetches.get(0).unwrap();

src/extensions/metadata.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use crate::error::{Error, ParseError, Result};
1515
use crate::parse::try_handle_unilateral;
1616
use crate::types::*;
1717
use imap_proto::types::{MailboxDatum, Metadata, Response, ResponseCode};
18+
use std::collections::VecDeque;
1819
use std::io::{Read, Write};
19-
use std::sync::mpsc;
2020

2121
// for intra-doc links
2222
#[allow(unused_imports)]
@@ -83,7 +83,7 @@ impl MetadataDepth {
8383

8484
fn parse_metadata<'a>(
8585
mut lines: &'a [u8],
86-
unsolicited: &'a mut mpsc::Sender<UnsolicitedResponse>,
86+
unsolicited: &'a mut VecDeque<UnsolicitedResponse>,
8787
) -> Result<Vec<Metadata>> {
8888
let mut res: Vec<Metadata> = Vec::new();
8989
loop {
@@ -197,7 +197,7 @@ impl<T: Read + Write> Session<T> {
197197
.as_str(),
198198
);
199199
let (lines, ok) = self.run(command)?;
200-
let meta = parse_metadata(&lines[..ok], &mut self.unsolicited_responses_tx)?;
200+
let meta = parse_metadata(&lines[..ok], &mut self.unsolicited_responses)?;
201201
let missed = if maxsize.is_some() {
202202
if let Ok((_, Response::Done { code, .. })) =
203203
imap_proto::parser::parse_response(&lines[ok..])

0 commit comments

Comments
 (0)