1use serde::{Deserialize, Serialize};
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5#[serde(default)]
6pub struct ServerSettings {
7 pub notmuch_config: Option<PathBuf>,
8 pub mbsync_config: Option<PathBuf>,
9 pub msmtp_config: Option<PathBuf>,
10 pub maildir_root: Option<PathBuf>,
11 pub notmuch_bin: Option<PathBuf>,
12 pub mbsync_bin: Option<PathBuf>,
13 pub msmtp_bin: Option<PathBuf>,
14 pub index: Option<bool>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Binaries {
21 pub notmuch: PathBuf,
22 pub mbsync: PathBuf,
23 pub msmtp: PathBuf,
24}
25
26impl Default for Binaries {
27 fn default() -> Self {
28 Self {
29 notmuch: PathBuf::from(crate::tools::NOTMUCH),
30 mbsync: PathBuf::from(crate::tools::MBSYNC),
31 msmtp: PathBuf::from(crate::tools::MSMTP),
32 }
33 }
34}
35
36impl Binaries {
37 pub fn from_settings(settings: &ServerSettings) -> Self {
38 let defaults = Self::default();
39 Self {
40 notmuch: settings.notmuch_bin.clone().unwrap_or(defaults.notmuch),
41 mbsync: settings.mbsync_bin.clone().unwrap_or(defaults.mbsync),
42 msmtp: settings.msmtp_bin.clone().unwrap_or(defaults.msmtp),
43 }
44 }
45}
46
47impl ServerSettings {
48 pub fn default_path() -> Option<PathBuf> {
49 dirs::config_dir().map(|d| d.join("ecr").join("server.toml"))
50 }
51
52 pub fn load() -> Self {
53 match Self::default_path() {
54 Some(path) => Self::load_from(&path),
55 None => Self::default(),
56 }
57 }
58
59 pub fn load_from(path: &Path) -> Self {
60 let Ok(text) = std::fs::read_to_string(path) else {
61 return Self::default();
62 };
63 match toml::from_str::<ServerSettings>(&text) {
64 Ok(settings) => settings,
65 Err(err) => {
66 tracing::warn!(path = %path.display(), %err, "ignoring unreadable server settings");
67 Self::default()
68 }
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use std::io::Write;
77
78 #[test]
79 fn missing_file_yields_defaults() {
80 let settings = ServerSettings::load_from(Path::new("/nonexistent/ecr/server.toml"));
81 assert!(settings.notmuch_config.is_none());
82 }
83
84 #[test]
85 fn explicit_paths_are_read() {
86 let dir = tempfile::tempdir().unwrap();
87 let path = dir.path().join("server.toml");
88 let mut f = std::fs::File::create(&path).unwrap();
89 writeln!(f, r#"notmuch_config = "/etc/notmuch/config""#).unwrap();
90
91 let settings = ServerSettings::load_from(&path);
92 assert_eq!(
93 settings.notmuch_config,
94 Some(PathBuf::from("/etc/notmuch/config"))
95 );
96 assert!(settings.mbsync_config.is_none());
97 }
98
99 #[test]
100 fn malformed_file_degrades_to_defaults() {
101 let dir = tempfile::tempdir().unwrap();
102 let path = dir.path().join("server.toml");
103 std::fs::write(&path, "this is not toml {{{").unwrap();
104 assert!(ServerSettings::load_from(&path).notmuch_config.is_none());
105 }
106}