i3status_rs/blocks/
maildir.rs1use super::prelude::*;
40use maildir::Maildir;
41use std::path::PathBuf;
42
43#[derive(Deserialize, Debug, SmartDefault)]
44#[serde(deny_unknown_fields, default)]
45pub struct Config {
46 pub format: FormatConfig,
47 #[default(5.into())]
48 pub interval: Seconds,
49 pub inboxes: Vec<String>,
50 #[default(1)]
51 pub threshold_warning: usize,
52 #[default(10)]
53 pub threshold_critical: usize,
54 #[default(MailType::New)]
55 pub display_type: MailType,
56}
57
58fn expand_inbox(inbox: &str) -> Result<impl Iterator<Item = PathBuf>> {
59 let expanded = shellexpand::full(inbox).error("Failed to expand inbox")?;
60 let paths = glob::glob(&expanded).error("Glob expansion failed")?;
61 Ok(paths.filter_map(|p| p.ok()))
62}
63
64pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
65 let format = config.format.with_default(" $icon $status ")?;
66
67 let mut inboxes = Vec::with_capacity(config.inboxes.len());
68 for inbox in &config.inboxes {
69 inboxes.extend(expand_inbox(inbox)?.map(Maildir::from));
70 }
71
72 loop {
73 let mut newmails = 0;
74 for inbox in &inboxes {
75 newmails += match config.display_type {
77 MailType::New => inbox.count_new(),
78 MailType::Cur => inbox.count_cur(),
79 MailType::All => inbox.count_new() + inbox.count_cur(),
80 };
81 }
82
83 let mut widget = Widget::new().with_format(format.clone());
84 widget.state = if newmails >= config.threshold_critical {
85 State::Critical
86 } else if newmails >= config.threshold_warning {
87 State::Warning
88 } else {
89 State::Idle
90 };
91 widget.set_values(map!(
92 "icon" => Value::icon("mail"),
93 "status" => Value::number(newmails)
94 ));
95 api.set_widget(widget)?;
96
97 select! {
98 _ = sleep(config.interval.0) => (),
99 _ = api.wait_for_update_request() => (),
100 }
101 }
102}
103
104#[derive(Clone, Debug, Deserialize)]
105#[serde(rename_all = "lowercase")]
106pub enum MailType {
107 New,
108 Cur,
109 All,
110}