diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 60711e3..bcd6abd 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -24,7 +24,7 @@ use tokio::{ use xdg::BaseDirectories; use crate::config::{Config, MailAccountConfig}; -use crate::mail::{sync_main, MailStore}; +use crate::mail::{mail_main, MailStore}; type ExitListener = oneshot::Receiver<()>; @@ -155,7 +155,7 @@ async fn run_single_mail_account( let (tx, mut rx) = mpsc::unbounded_channel(); - let sync_fut = sync_main(&account_name, account, tx, store).fuse(); + let sync_fut = mail_main(&account_name, account, tx, store).fuse(); pin_mut!(sync_fut); debug!("Mail account loop for {}.", account_name); @@ -164,7 +164,7 @@ async fn run_single_mail_account( res = sync_fut => match res { Ok(_) => {}, Err(err) => { - error!("sync_main died with: {:?}", err); + error!("mail_main died with: {:?}", err); break; } }, diff --git a/daemon/src/mail/mod.rs b/daemon/src/mail/mod.rs index ba70404..371e320 100644 --- a/daemon/src/mail/mod.rs +++ b/daemon/src/mail/mod.rs @@ -7,6 +7,7 @@ use anyhow::{Context, Result}; use futures::stream::StreamExt; use panorama_imap::{ client::{auth::Login, ConfigBuilder}, + pool::{ImapPool, PoolConfig}, proto::{ command::FetchItems, response::{MailboxData, Response}, @@ -23,14 +24,37 @@ pub use self::store::MailStore; static MIGRATOR: Migrator = sqlx::migrate!(); /// The main function for the IMAP syncing thread -pub async fn sync_main( +pub async fn mail_main( acct_name: impl AsRef, acct: MailAccountConfig, _mail2ui_tx: UnboundedSender, _mail_store: MailStore, ) -> Result<()> { let acct_name = acct_name.as_ref(); - debug!("Starting main synchronization procedure for {}", acct_name); + debug!( + "Starting main synchronization procedure for account '{}'", + acct_name + ); + + // create a connection pool + let client_config = ConfigBuilder::default() + .hostname(acct.imap.server.clone()) + .port(acct.imap.port) + .tls(matches!(acct.imap.tls, TlsMethod::On)) + .build()?; + let pool_config = PoolConfig { + max_connections: 10, + }; + let pool = ImapPool::new(client_config, pool_config); + + // grab one connection from that pool and start running a background + // synchronization thread + let sync_conn = pool.acquire().await?; + + // let the rest of the pool respond to requests coming from the channel + // TODO: + + return Ok(()); // loop ensures that the connection is retried after it dies loop { diff --git a/imap/src/client/client.rs b/imap/src/client/client.rs index 5dbb0ac..31f3cdf 100644 --- a/imap/src/client/client.rs +++ b/imap/src/client/client.rs @@ -29,7 +29,6 @@ use super::tls::wrap_tls; /// An IMAP client that hasn't been connected yet. #[derive(Builder, Clone, Debug)] -#[builder(build_fn(private))] pub struct Config { /// The hostname of the IMAP server. If using TLS, must be an address pub hostname: String, diff --git a/imap/src/client/mod.rs b/imap/src/client/mod.rs index 7e4004c..8192863 100644 --- a/imap/src/client/mod.rs +++ b/imap/src/client/mod.rs @@ -21,14 +21,12 @@ pub mod response_stream; mod client; mod codec; mod inner; -mod pool; mod tls; pub use self::client::{ ClientAuthenticated, ClientUnauthenticated, Config, ConfigBuilder, }; pub use self::codec::{ImapCodec, TaggedCommand}; -pub use self::pool::ImapPool; #[cfg(feature = "low-level")] pub use self::inner::Inner; diff --git a/imap/src/client/pool.rs b/imap/src/client/pool.rs deleted file mode 100644 index 38f0c4a..0000000 --- a/imap/src/client/pool.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::sync::Arc; - -use crossbeam::queue::ArrayQueue; - -use super::{ClientAuthenticated, Config}; - -#[derive(Clone)] -pub struct ImapPool(Arc); - -impl ImapPool { - pub fn new(config: Config) -> Self { - let inner = InnerPool { - config, - connections: ArrayQueue::new(10), - }; - ImapPool(Arc::new(inner)) - } -} - -struct InnerPool { - config: Config, - connections: ArrayQueue, -} - -impl InnerPool { - pub fn connect() {} -} diff --git a/imap/src/interface.rs b/imap/src/interface.rs new file mode 100644 index 0000000..66708c1 --- /dev/null +++ b/imap/src/interface.rs @@ -0,0 +1,10 @@ +use anyhow::Result; + +use crate::proto::response::Envelope; + +#[async_trait] +pub trait ImapClient { + async fn list_messages(&self) -> Result>; +} + +pub struct ListMessagesOptions {} diff --git a/imap/src/lib.rs b/imap/src/lib.rs index 0d2dee1..1730ba2 100644 --- a/imap/src/lib.rs +++ b/imap/src/lib.rs @@ -20,4 +20,8 @@ extern crate panorama_proto_common; extern crate maplit; pub mod client; +pub mod interface; pub mod proto; + +#[cfg(feature = "pool")] +pub mod pool; diff --git a/imap/src/pool/mod.rs b/imap/src/pool/mod.rs new file mode 100644 index 0000000..ca81e31 --- /dev/null +++ b/imap/src/pool/mod.rs @@ -0,0 +1,67 @@ +use std::{borrow::Borrow, ops::Deref, sync::Arc}; + +use anyhow::Result; +use crossbeam::queue::ArrayQueue; +use tokio::sync::Semaphore; + +use crate::{interface::ImapClient, proto::response::Envelope}; + +use super::client::{ClientAuthenticated, Config}; + +pub struct PoolConfig { + pub max_connections: usize, +} + +/// A pool of IMAP connections. +#[derive(Clone)] +pub struct ImapPool(Arc); + +impl Deref for ImapPool { + type Target = InnerPool; + + fn deref(&self) -> &Self::Target { self.0.borrow() } +} + +#[async_trait] +impl ImapClient for ImapPool { + async fn list_messages(&self) -> Result> { + let client = self.acquire(); + + todo!() + } +} + +impl ImapPool { + pub fn new(config: Config, pool_config: PoolConfig) -> Self { + let inner = InnerPool::init(config, pool_config); + ImapPool(Arc::new(inner)) + } +} + +pub struct InnerPool { + config: Config, + semaphore: Semaphore, + connections: ArrayQueue, +} + +impl InnerPool { + pub fn init(config: Config, pool_config: PoolConfig) -> Self { + InnerPool { + config, + semaphore: Semaphore::new(pool_config.max_connections), + connections: ArrayQueue::new(pool_config.max_connections), + } + } + + pub async fn acquire(&self) -> Result { + let guard = match self.connections.pop() { + // we can reuse + Some(conn) => {} + + // no existing connection, time to make a new one + None => {} + }; + + todo!() + } +}