Skip to content

Commit d8d69a3

Browse files
authored
Idle builder (#202)
1 parent 0712115 commit d8d69a3

File tree

4 files changed

+42
-74
lines changed

4 files changed

+42
-74
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
### Changed
1414
- MSRV increased to 1.43 for nom6 and bitvec
1515
- `expunge` and `uid_expunge` return `Result<Deleted>` instead of `Result<Vec<u32>>`.
16-
- Idle `wait_keepalive_while` replaces `wait_keepalive` and takes a callback with an `UnsolicitedResponse` in parameter.
16+
- IDLE capability now provides a builder interface. All `wait_*` functions are merged into `wait_while` which takes a callback with an `UnsolicitedResponse` in parameter.
1717
- All `Session.append_with_*` methods are obsoleted by `append` which returns now an `AppendCmd` builder.
1818
- Envelope `&'a [u8]` attributes are replaced by `Cow<'a, [u8]>`.
1919
- `Flag`, `Mailbox`, `UnsolicitedResponse` and `Error` are now declared as non exhaustive.

examples/idle.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,14 @@ fn main() {
5454

5555
imap.select(opt.mailbox).expect("Could not select mailbox");
5656

57-
let idle = imap.idle().expect("Could not IDLE");
58-
5957
// Implement a trivial counter that causes the IDLE callback to end the IDLE
6058
// after a fixed number of responses.
6159
//
6260
// A threaded client could use channels or shared data to interact with the
6361
// rest of the program and update mailbox state, decide to exit the IDLE, etc.
6462
let mut num_responses = 0;
6563
let max_responses = opt.max_responses;
66-
let idle_result = idle.wait_keepalive_while(|response| {
64+
let idle_result = imap.idle().wait_while(|response| {
6765
num_responses += 1;
6866
println!("IDLE response #{}: {:?}", num_responses, response);
6967
if num_responses >= max_responses {
@@ -76,7 +74,7 @@ fn main() {
7674
});
7775

7876
match idle_result {
79-
Ok(()) => println!("IDLE finished normally"),
77+
Ok(reason) => println!("IDLE finished normally {:?}", reason),
8078
Err(e) => println!("IDLE finished with error {:?}", e),
8179
}
8280

src/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ impl<T: Read + Write> Session<T> {
10931093
/// command, as specified in the base IMAP specification.
10941094
///
10951095
/// See [`extensions::idle::Handle`] for details.
1096-
pub fn idle(&mut self) -> Result<extensions::idle::Handle<'_, T>> {
1096+
pub fn idle(&mut self) -> extensions::idle::Handle<'_, T> {
10971097
extensions::idle::Handle::make(self)
10981098
}
10991099

src/extensions/idle.rs

Lines changed: 38 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ use std::time::Duration;
1919
/// specificed in [RFC 2177](https://tools.ietf.org/html/rfc2177) until the underlying server state
2020
/// changes in some way.
2121
///
22-
/// Each of the `wait` functions takes a callback function which receives any responses
22+
/// The `wait_while` function takes a callback function which receives any responses
2323
/// that arrive on the channel while IDLE. The callback function implements whatever
2424
/// logic is needed to handle the IDLE response, and then returns a boolean
2525
/// to continue idling (`true`) or stop (`false`).
26+
///
2627
/// For users that want the IDLE to exit on any change (the behavior proior to version 3.0),
2728
/// a convenience callback function [`stop_on_any`] is provided.
2829
///
@@ -37,25 +38,24 @@ use std::time::Duration;
3738
/// imap.select("INBOX")
3839
/// .expect("Could not select mailbox");
3940
///
40-
/// let idle = imap.idle().expect("Could not IDLE");
41-
///
42-
/// // Exit on any mailbox change
43-
/// let result = idle.wait_keepalive_while(idle::stop_on_any);
41+
/// // Exit on any mailbox change. By default, connections will be periodically
42+
/// // refreshed in the background.
43+
/// let result = imap.idle().wait_while(idle::stop_on_any);
4444
/// # }
4545
/// ```
4646
///
4747
/// Note that the server MAY consider a client inactive if it has an IDLE command running, and if
4848
/// such a server has an inactivity timeout it MAY log the client off implicitly at the end of its
49-
/// timeout period. Because of that, clients using IDLE are advised to terminate the IDLE and
50-
/// re-issue it at least every 29 minutes to avoid being logged off. [`Handle::wait_keepalive_while`]
51-
/// does this. This still allows a client to receive immediate mailbox updates even though it need
52-
/// only "poll" at half hour intervals.
49+
/// timeout period. Because of that, clients using IDLE are advised to terminate the IDLE and
50+
/// re-issue it at least every 29 minutes to avoid being logged off. This is done by default, but
51+
/// can be disabled by calling [`Handle::keepalive`]
5352
///
5453
/// As long as a [`Handle`] is active, the mailbox cannot be otherwise accessed.
5554
#[derive(Debug)]
5655
pub struct Handle<'a, T: Read + Write> {
5756
session: &'a mut Session<T>,
58-
keepalive: Duration,
57+
timeout: Duration,
58+
keepalive: bool,
5959
done: bool,
6060
}
6161

@@ -73,11 +73,7 @@ pub fn stop_on_any(_response: UnsolicitedResponse) -> bool {
7373
false
7474
}
7575

76-
/// Must be implemented for a transport in order for a `Session` using that transport to support
77-
/// operations with timeouts.
78-
///
79-
/// Examples of where this is useful is for `Handle::wait_keepalive_while` and
80-
/// `Handle::wait_timeout_while`.
76+
/// Must be implemented for a transport in order for a `Session` to use IDLE.
8177
pub trait SetReadTimeout {
8278
/// Set the timeout for subsequent reads to the given one.
8379
///
@@ -88,14 +84,13 @@ pub trait SetReadTimeout {
8884
}
8985

9086
impl<'a, T: Read + Write + 'a> Handle<'a, T> {
91-
pub(crate) fn make(session: &'a mut Session<T>) -> Result<Self> {
92-
let mut h = Handle {
87+
pub(crate) fn make(session: &'a mut Session<T>) -> Self {
88+
Handle {
9389
session,
94-
keepalive: Duration::from_secs(29 * 60),
90+
timeout: Duration::from_secs(29 * 60),
91+
keepalive: true,
9592
done: false,
96-
};
97-
h.init()?;
98-
Ok(h)
93+
}
9994
}
10095

10196
fn init(&mut self) -> Result<()> {
@@ -132,7 +127,7 @@ impl<'a, T: Read + Write + 'a> Handle<'a, T> {
132127

133128
/// Internal helper that doesn't consume self.
134129
///
135-
/// This is necessary so that we can keep using the inner `Session` in `wait_keepalive_while`.
130+
/// This is necessary so that we can keep using the inner `Session` in `wait_while`.
136131
fn wait_inner<F>(&mut self, reconnect: bool, mut callback: F) -> Result<WaitOutcome>
137132
where
138133
F: FnMut(UnsolicitedResponse) -> bool,
@@ -196,73 +191,48 @@ impl<'a, T: Read + Write + 'a> Handle<'a, T> {
196191
(_, result) => result,
197192
}
198193
}
199-
200-
/// Block until the given callback returns `false`, or until a response
201-
/// arrives that is not explicitly handled by [`UnsolicitedResponse`].
202-
pub fn wait_while<F>(mut self, callback: F) -> Result<()>
203-
where
204-
F: FnMut(UnsolicitedResponse) -> bool,
205-
{
206-
self.wait_inner(true, callback).map(|_| ())
207-
}
208194
}
209195

210196
impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> {
211-
/// Set the keep-alive interval to use when `wait_keepalive_while` is called.
197+
/// Set the timeout duration on the connection. This will also set the frequency
198+
/// at which the connection is refreshed.
212199
///
213-
/// The interval defaults to 29 minutes as dictated by RFC 2177.
214-
pub fn set_keepalive(&mut self, interval: Duration) {
215-
self.keepalive = interval;
200+
/// The interval defaults to 29 minutes as given in RFC 2177.
201+
pub fn timeout(&mut self, interval: Duration) -> &mut Self {
202+
self.timeout = interval;
203+
self
204+
}
205+
206+
/// Do not continuously refresh the IDLE connection in the background.
207+
///
208+
/// By default, connections will periodically be refreshed in the background using the
209+
/// timeout duration set by [`Handle::timeout`]. If you do not want this behaviour, call
210+
/// this function and the connection will simply IDLE until `wait_while` returns or
211+
/// the timeout expires.
212+
pub fn keepalive(&mut self, keepalive: bool) -> &mut Self {
213+
self.keepalive = keepalive;
214+
self
216215
}
217216

218217
/// Block until the given callback returns `false`, or until a response
219218
/// arrives that is not explicitly handled by [`UnsolicitedResponse`].
220-
///
221-
/// This method differs from [`Handle::wait_while`] in that it will periodically refresh the IDLE
222-
/// connection, to prevent the server from timing out our connection. The keepalive interval is
223-
/// set to 29 minutes by default, as dictated by RFC 2177, but can be changed using
224-
/// [`Handle::set_keepalive`].
225-
///
226-
/// This is the recommended method to use for waiting.
227-
pub fn wait_keepalive_while<F>(self, callback: F) -> Result<()>
219+
pub fn wait_while<F>(&mut self, callback: F) -> Result<WaitOutcome>
228220
where
229221
F: FnMut(UnsolicitedResponse) -> bool,
230222
{
223+
self.init()?;
231224
// The server MAY consider a client inactive if it has an IDLE command
232225
// running, and if such a server has an inactivity timeout it MAY log
233226
// the client off implicitly at the end of its timeout period. Because
234227
// of that, clients using IDLE are advised to terminate the IDLE and
235228
// re-issue it at least every 29 minutes to avoid being logged off.
236229
// This still allows a client to receive immediate mailbox updates even
237230
// though it need only "poll" at half hour intervals.
238-
let keepalive = self.keepalive;
239-
self.timed_wait(keepalive, true, callback).map(|_| ())
240-
}
241-
242-
/// Block until the given given amount of time has elapsed, the given callback
243-
/// returns `false`, or until a response arrives that is not explicitly handled
244-
/// by [`UnsolicitedResponse`].
245-
pub fn wait_with_timeout_while<F>(self, timeout: Duration, callback: F) -> Result<WaitOutcome>
246-
where
247-
F: FnMut(UnsolicitedResponse) -> bool,
248-
{
249-
self.timed_wait(timeout, false, callback)
250-
}
251-
252-
fn timed_wait<F>(
253-
mut self,
254-
timeout: Duration,
255-
reconnect: bool,
256-
callback: F,
257-
) -> Result<WaitOutcome>
258-
where
259-
F: FnMut(UnsolicitedResponse) -> bool,
260-
{
261231
self.session
262232
.stream
263233
.get_mut()
264-
.set_read_timeout(Some(timeout))?;
265-
let res = self.wait_inner(reconnect, callback);
234+
.set_read_timeout(Some(self.timeout))?;
235+
let res = self.wait_inner(self.keepalive, callback);
266236
let _ = self.session.stream.get_mut().set_read_timeout(None).is_ok();
267237
res
268238
}

0 commit comments

Comments
 (0)