1use std::path::PathBuf;
20
21use crate::config::ConfigEnv;
22
23#[must_use]
33pub fn config_home(env: &impl ConfigEnv) -> Option<PathBuf> {
34 if let Some(xdg) = env.var("XDG_CONFIG_HOME") {
35 return Some(PathBuf::from(xdg).join("midnight-manual"));
36 }
37 if let Some(home) = env.var("HOME") {
38 return Some(PathBuf::from(home).join(".config").join("midnight-manual"));
39 }
40 None
41}
42
43#[must_use]
45pub fn auth_file_path(env: &impl ConfigEnv) -> Option<PathBuf> {
46 config_home(env).map(|p| p.join("auth.toml"))
47}
48
49#[must_use]
53pub fn keys_dir(env: &impl ConfigEnv) -> Option<PathBuf> {
54 config_home(env).map(|p| p.join("keys"))
55}
56
57#[must_use]
60pub fn private_key_path(env: &impl ConfigEnv, user_id: &str) -> Option<PathBuf> {
61 keys_dir(env).map(|p| p.join(format!("{user_id}.private")))
62}
63
64#[must_use]
71pub fn telemetry_marker_path(env: &impl ConfigEnv) -> Option<PathBuf> {
72 config_home(env).map(|p| p.join("telemetry-disabled"))
73}
74
75#[must_use]
83pub fn user_store_path(env: &impl ConfigEnv) -> Option<PathBuf> {
84 if let Some(p) = env.var("MIDNIGHT_MANUAL_USER_STORE") {
85 return Some(PathBuf::from(p));
86 }
87 config_home(env).map(|p| p.join("users.toml"))
88}
89
90#[cfg(test)]
91mod tests {
92 use std::collections::HashMap;
93
94 use super::*;
95
96 #[derive(Default)]
97 struct FakeEnv(HashMap<String, String>);
98
99 impl FakeEnv {
100 fn set(mut self, k: &str, v: &str) -> Self {
101 self.0.insert(k.into(), v.into());
102 self
103 }
104 }
105
106 impl ConfigEnv for FakeEnv {
107 fn var(&self, name: &str) -> Option<String> {
108 self.0.get(name).cloned()
109 }
110 }
111
112 #[test]
113 fn xdg_beats_home_for_config_dir() {
114 let env = FakeEnv::default()
115 .set("XDG_CONFIG_HOME", "/x")
116 .set("HOME", "/h");
117 assert_eq!(config_home(&env), Some(PathBuf::from("/x/midnight-manual")));
118 }
119
120 #[test]
121 fn home_fallback_for_config_dir() {
122 let env = FakeEnv::default().set("HOME", "/h");
123 assert_eq!(config_home(&env), Some(PathBuf::from("/h/.config/midnight-manual")),);
124 }
125
126 #[test]
127 fn none_when_no_env_at_all() {
128 let env = FakeEnv::default();
129 assert!(config_home(&env).is_none());
130 assert!(auth_file_path(&env).is_none());
131 assert!(keys_dir(&env).is_none());
132 assert!(private_key_path(&env, "aaron").is_none());
133 }
134
135 #[test]
136 fn auth_file_under_config_home() {
137 let env = FakeEnv::default().set("HOME", "/h");
138 assert_eq!(
139 auth_file_path(&env),
140 Some(PathBuf::from("/h/.config/midnight-manual/auth.toml")),
141 );
142 }
143
144 #[test]
145 fn private_key_path_uses_user_id() {
146 let env = FakeEnv::default().set("XDG_CONFIG_HOME", "/x");
147 assert_eq!(
148 private_key_path(&env, "aaron"),
149 Some(PathBuf::from("/x/midnight-manual/keys/aaron.private")),
150 );
151 }
152
153 #[test]
154 fn user_store_env_var_wins() {
155 let env = FakeEnv::default()
156 .set("MIDNIGHT_MANUAL_USER_STORE", "/etc/users.toml")
157 .set("XDG_CONFIG_HOME", "/x");
158 assert_eq!(user_store_path(&env), Some(PathBuf::from("/etc/users.toml")));
159 }
160
161 #[test]
162 fn telemetry_marker_under_config_home() {
163 let env = FakeEnv::default().set("XDG_CONFIG_HOME", "/x");
164 assert_eq!(
165 telemetry_marker_path(&env),
166 Some(PathBuf::from("/x/midnight-manual/telemetry-disabled")),
167 );
168 }
169
170 #[test]
171 fn telemetry_marker_none_without_env() {
172 let env = FakeEnv::default();
173 assert!(telemetry_marker_path(&env).is_none());
174 }
175
176 #[test]
177 fn user_store_xdg_fallback() {
178 let env = FakeEnv::default().set("XDG_CONFIG_HOME", "/x");
179 assert_eq!(user_store_path(&env), Some(PathBuf::from("/x/midnight-manual/users.toml")),);
180 }
181}