From 30d34ce1718a8f7929612af48a5b475f38fbcfaf Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Sat, 22 Jun 2024 07:52:08 +0100 Subject: [PATCH] feat: move over to using the "mbwatch" group for watchers Now mbwatch will look for a group called "mbwatch" to find what channels and mailboxes to watch. This allows the user to be move specific in what they are watching. You don't always need to watch the "INBOX" nor do you have to watch all of the channels you have setup in with mbsync. The config will need to be defined in the `Channels` prop on the group. It needs to be a comma separated of channels and mailboxes. This is an example of the config. ``` Group mbwatch Channels work:INBOX, work:Archive, personal:INBOX ``` --- src/config.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 38 +++++++++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/config.rs b/src/config.rs index e34d3e5..1e31f10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -40,17 +40,24 @@ impl ImapStoreConfig { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct ChannelConfig { pub name: String, pub near: String, pub far: String, } +#[derive(Debug, Default, Clone)] +pub struct GroupConfig { + pub name: String, + pub channels: Vec<(String, String)>, +} + #[derive(Debug, Default)] pub struct Config { pub channels: Vec, pub imap_stores: Vec, + pub groups: Vec, } impl Config { @@ -60,6 +67,20 @@ impl Config { .find(|store| format!(":{}:", store.name) == name) .map(|store| store.clone()) } + + pub fn find_channel(&self, name: &str) -> Option { + self.channels + .iter() + .find(|channel| channel.name == name) + .map(|channel| channel.clone()) + } + + pub fn find_group(&self, name: &str) -> Option { + self.groups + .iter() + .find(|group| group.name == name) + .map(|group| group.clone()) + } } enum ConfigLine { @@ -75,6 +96,9 @@ enum ConfigLine { Near(String), Far(String), + Group(String), + Channels(String), + End, } @@ -97,6 +121,9 @@ impl TryFrom<&str> for ConfigLine { "Near" => Ok(ConfigLine::Near(value)), "Far" => Ok(ConfigLine::Far(value)), + "Group" => Ok(ConfigLine::Group(value)), + "Channels" => Ok(ConfigLine::Channels(value)), + _ => { if value == "" { return Ok(ConfigLine::End); @@ -143,7 +170,7 @@ pub fn from_file(config_path: &str) -> Config { ConfigLine::ImapStore(name) => { let mut imap_store_config = ImapStoreConfig::default(); imap_store_config.name = name; - config.imap_stores.push(imap_store_config) + config.imap_stores.push(imap_store_config); } ConfigLine::Host(host) => { let store = config.imap_stores.last_mut().unwrap(); @@ -184,6 +211,27 @@ pub fn from_file(config_path: &str) -> Config { channel.near = near; } + ConfigLine::Group(name) => { + let mut group = GroupConfig::default(); + group.name = name; + + config.groups.push(group); + } + ConfigLine::Channels(channels) => { + let group = config.groups.last_mut().unwrap(); + group.channels = channels + .split(',') + .map(|channel| { + let mut iter = channel.trim().split(':'); + + ( + iter.next().unwrap_or("").to_string(), + iter.next().unwrap_or("").to_string(), + ) + }) + .collect(); + } + ConfigLine::End => {} } } diff --git a/src/main.rs b/src/main.rs index fc52d0f..4a80cbe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use std::thread; use config::ImapStoreConfig; use imap::{ImapConnection, Session}; -fn connect(config: &ImapStoreConfig) -> Option>> { +fn connect(config: &ImapStoreConfig, mailbox: &String) -> Option>> { let mut client_builder = imap::ClientBuilder::new(config.host.clone(), config.port()) .mode(imap::ConnectionMode::AutoTls) .tls_kind(imap::TlsKind::Rust); @@ -38,7 +38,7 @@ fn connect(config: &ImapStoreConfig) -> Option>> return None; } - session.select("INBOX").expect("Unable to select folder"); + session.select(mailbox).expect("Unable to select folder"); Some(session) } @@ -56,18 +56,32 @@ fn main() { }; let config = config::from_file(&format!("{home}/.mbsyncrc")); + let mbwatch_group = match config.find_group("mbwatch") { + Some(group) => group, + None => panic!("Unable to find mbwatch group in your mbsync config"), + }; let mut watchers = Vec::new(); - for channel in &config.channels { + for (channel_name, mailbox) in &mbwatch_group.channels { + let channel = match config.find_channel(channel_name) { + Some(channel) => channel, + None => panic!("Unable to find channel {}", channel_name), + }; + + if mailbox.is_empty() { + panic!("No mailbox defined for channel {}", channel_name); + } + let imap_store = match config.find_imap_store(&channel.far) { Some(store) => store, None => panic!("Unable to find store {}", &channel.far), }; let channel_name = channel.name.clone(); + let mailbox = mailbox.clone(); watchers.push(thread::spawn(move || { - let mut session = match connect(&imap_store) { + let mut session = match connect(&imap_store, &mailbox) { Some(session) => session, None => { log::error!("Unable to connect to channel {}", channel_name); @@ -75,7 +89,11 @@ fn main() { } }; - log::info!("Watching for messages on channel {}", channel_name); + log::info!( + "Watching for messages on channel {} in mailbox {}", + channel_name, + mailbox + ); loop { let result = session @@ -86,15 +104,19 @@ fn main() { log::error!("Error while idling: {:?}", result); thread::sleep(std::time::Duration::from_secs(10)); - session = connect(&imap_store).unwrap(); + session = connect(&imap_store, &mailbox).unwrap(); continue; } - log::info!("Syncing changes for {}", channel_name); + log::info!( + "Syncing changes for {} in mailbox {}", + channel_name, + mailbox + ); Command::new("mbsync") - .args(["--all", &format!("{}:INBOX", channel_name)]) + .args(["--all", &format!("{}:{}", channel_name, mailbox)]) .output() .expect("Unable to sync mail"); }