Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How would you make an idiomatic idle loop? #89

Open
bamthomas opened this issue Sep 7, 2023 · 4 comments
Open

How would you make an idiomatic idle loop? #89

bamthomas opened this issue Sep 7, 2023 · 4 comments

Comments

@bamthomas
Copy link

bamthomas commented Sep 7, 2023

An email client will most of the time use an IDLE loop to get the new messages/deleted messages events in real time.
What would be the best way to do this with async-imap as the session is borrowed by idle?

I have stored a session in a struct:

pub struct ImapClient {
    session: async_imap::Session<async_native_tls::TlsStream<TcpStream>>
}

That session is initialized with a try_new method.

Then I have a method that makes an idle wait. It returns the number of fetched messages.

   pub async fn wait_for_new_messages(&mut self) -> Result<usize> {
        self.session.select("INBOX").await.unwrap();
        let mut idle = self.session.idle(); // boom: self.session moved due to this method call
        idle.init().await?
       // code for idle/fetch EXISTS
    }

cf this file

The caller is responsible to loop like:

loop {
    match imap_client.wait_for_new_messages().await {
        Ok(nb) => debug!("received {} messages", nb),
        Err(err) => error!("{:?}", err)
    }
}

I understand that the session moves to the idle Handler, to return it when idle is done.

How could I use the session instance with idle?
(@hpk42 I will push a doc PR with my findings, I just want to reach a working example)
Thank you for your answers.

What I tried:

  • encapsulate idle handler in the struct ImapClient but when creating the session, same issue is happening with self
  • encapsulate the Client and recreating a session each time but that means re-authenticate at each idle loop step thus having an unnecessary network overhead
  • encapsulate the Client and making the idle loop inside the ImapClient. But same issue happens with the borrow in the loop
@bamthomas
Copy link
Author

For now I made a workaround by overriding the session variable in a loop with the idle returned instance. I'm going to try to refactor to make the code cleaner and see if I can override the struct instance with the idle.done() instance.

https://github.com/iroco-co/mailtempest/blob/4bbf011bf388cb589aaa578d5aee7406f07e48b3/src/mail_reader.rs#L42-L56

@felinira
Copy link

I'd really prefer if the IDLE handle would only take a mutable borrow and have a lifetime associated that mutably borrows the session for that long. I store my sessions in a mutex for easy reuse and also box them with a trait for connection type agnosticity and this makes this it nearly impossible to use IDLE.

@link2xt
Copy link
Contributor

link2xt commented Sep 18, 2023

In Delta Chat session is stored inside an Option:
https://github.com/deltachat/deltachat-core-rust/blob/061d091c971ac8cb860f92e6e81c298dcffa8f26/src/imap.rs#L93

This allows to .take() it instead of borrowing while you are using it, then return it back.

@felinira
Copy link

I suppose that works :) I guess not using mutex is for the better anyways. Just mostly didn't expect to suddenly need an owned version of it, as everything else works fine without.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants