diff --git a/NEWS.md b/NEWS.md index d768cbdc5..e4a61522d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,14 @@ +# i3status-rust 0.35.0 [unreleased] + +### Bug Fixes and Improvements + +- The `notmuch` block can now automatically locate the notmuch database (the same way the `notmuch` command does) without being explicitly configured. Furthermore, the block has gained a `profile` option that can be used to specify a notmuch profile other than "default". + +### Breaking Changes + +- The `interval` option has been removed from the `notmuch` block in favor of watching the notmuch database for changes. +- The `maildir` option in the `notmuch` block has been renamed to `database` to better reflect its purpose, and has been made optional. + # i3status-rust 0.34.0 ### New Blocks and Features diff --git a/cspell.yaml b/cspell.yaml index 80a7b3bb2..69dfbc719 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -13,6 +13,7 @@ ignoreRegExpList: # Ignore unicode characters - /(\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8})/g words: + - xapian - aarch - alacritty - alphanum diff --git a/src/blocks/notmuch.rs b/src/blocks/notmuch.rs index f7ce2ddc2..1c3077979 100644 --- a/src/blocks/notmuch.rs +++ b/src/blocks/notmuch.rs @@ -43,16 +43,23 @@ //! # Icons Used //! - `mail` +use inotify::{Inotify, WatchMask}; + use super::prelude::*; #[derive(Deserialize, Debug, SmartDefault)] #[serde(deny_unknown_fields, default)] pub struct Config { pub format: FormatConfig, - #[default(10.into())] - pub interval: Seconds, - #[default("~/.mail".into())] - pub maildir: ShellString, + /// Path to the notmuch database. + /// + /// Defaults to the database used by the notmuch CLI tool. + pub database: Option, + /// Database profile. Cannot be specified at the same time as `database`. + /// + /// Defaults to the profile used by the notmuch CLI tool. + pub profile: Option, + /// The notmuch query to count. pub query: String, #[default(u32::MAX)] pub threshold_warning: u32, @@ -67,12 +74,36 @@ pub struct Config { pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { let format = config.format.with_default(" $icon $count ")?; - let db = config.maildir.expand()?; - let mut timer = config.interval.timer(); + if config.database.is_some() && config.profile.is_some() { + return Err(Error::new( + "cannot specify both a notmuch database and a notmuch profile", + )); + } + + let profile = config.profile.as_deref(); + + let db_path = config.database.as_ref().map(|p| p.expand()).transpose()?; + let db_path: Option<&str> = db_path.as_deref(); + let notify = Inotify::init().error("Failed to start inotify")?; + + { + let lock_path = open_database(db_path, profile) + .error("failed to open the notmuch database")? + .path() + .join("xapian/flintlock"); + notify + .watches() + .add(lock_path, WatchMask::CLOSE_WRITE) + .error("failed to watch the notmuch database lock")?; + } + + let mut updates = notify + .into_event_stream([0; 1024]) + .error("Failed to create event stream")?; loop { // TODO: spawn_blocking? - let count = run_query(&db, &config.query).error("Failed to get count")?; + let count = run_query(db_path, profile, &config.query).error("Failed to get count")?; let mut widget = Widget::new().with_format(format.clone()); @@ -96,18 +127,34 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { api.set_widget(widget)?; tokio::select! { - _ = timer.tick() => (), + _ = updates.next_debounced() => (), _ = api.wait_for_update_request() => (), } } } -fn run_query(db_path: &str, query_string: &str) -> std::result::Result { +fn open_database( + db_path: Option<&str>, + profile: Option<&str>, +) -> std::result::Result { + notmuch::Database::open_with_config( + db_path, + notmuch::DatabaseMode::ReadOnly, + None::<&str>, + profile, + ) +} + +fn run_query( + db_path: Option<&str>, + profile: Option<&str>, + query_string: &str, +) -> std::result::Result { let db = notmuch::Database::open_with_config( - Some(db_path), + db_path, notmuch::DatabaseMode::ReadOnly, None::<&str>, - None, + profile, )?; let query = db.create_query(query_string)?; query.count_messages()