kanade_shared/
default_paths.rs1use std::path::{Path, PathBuf};
31
32pub fn config_dir() -> PathBuf {
34 #[cfg(target_os = "windows")]
35 {
36 program_data().join("Kanade").join("config")
37 }
38 #[cfg(not(target_os = "windows"))]
39 {
40 PathBuf::from("/etc/kanade")
41 }
42}
43
44pub fn data_dir() -> PathBuf {
46 #[cfg(target_os = "windows")]
47 {
48 program_data().join("Kanade").join("data")
49 }
50 #[cfg(not(target_os = "windows"))]
51 {
52 PathBuf::from("/var/lib/kanade")
53 }
54}
55
56pub fn log_dir() -> PathBuf {
58 #[cfg(target_os = "windows")]
59 {
60 program_data().join("Kanade").join("logs")
61 }
62 #[cfg(not(target_os = "windows"))]
63 {
64 PathBuf::from("/var/log/kanade")
65 }
66}
67
68#[cfg(target_os = "windows")]
69fn program_data() -> PathBuf {
70 std::env::var_os("ProgramData")
71 .map(PathBuf::from)
72 .unwrap_or_else(|| PathBuf::from(r"C:\ProgramData"))
73}
74
75pub fn find_config(flag: Option<&Path>, env_var: &str, basename: &str) -> anyhow::Result<PathBuf> {
87 if let Some(p) = flag {
88 return Ok(p.to_path_buf());
89 }
90 if let Ok(raw) = std::env::var(env_var)
91 && !raw.is_empty()
92 {
93 return Ok(PathBuf::from(raw));
94 }
95 let std_path = config_dir().join(basename);
96 if std_path.exists() {
97 return Ok(std_path);
98 }
99 Err(anyhow::anyhow!(
100 "config not found — pass `--config <path>`, set `{env_var}`, or place `{basename}` at `{}`",
101 std_path.display(),
102 ))
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use std::path::PathBuf;
109
110 fn unique_env(test_name: &str) -> String {
114 format!("KANADE_TEST_CFG_{test_name}_{}", std::process::id())
115 }
116
117 #[test]
118 fn find_config_prefers_flag_over_env_and_default() {
119 let env = unique_env("flag_wins");
120 unsafe {
122 std::env::set_var(&env, "env-path.toml");
123 }
124 let flag = PathBuf::from("flag-path.toml");
125 let got = find_config(Some(&flag), &env, "agent.toml").expect("ok");
126 assert_eq!(got, flag);
127 unsafe { std::env::remove_var(&env) };
128 }
129
130 #[test]
131 fn find_config_uses_env_when_flag_missing() {
132 let env = unique_env("env_wins");
133 unsafe {
134 std::env::set_var(&env, "env-path.toml");
135 }
136 let got = find_config(None, &env, "agent.toml").expect("ok");
137 assert_eq!(got, PathBuf::from("env-path.toml"));
138 unsafe { std::env::remove_var(&env) };
139 }
140
141 #[test]
142 fn find_config_skips_empty_env() {
143 let env = unique_env("empty_env");
144 unsafe {
145 std::env::set_var(&env, "");
146 }
147 let r = find_config(None, &env, "non-existent-kanade-test.toml");
150 assert!(r.is_err(), "expected error, got {:?}", r);
151 unsafe { std::env::remove_var(&env) };
152 }
153
154 #[test]
155 fn find_config_errors_with_helpful_message() {
156 let env = unique_env("missing");
157 let r = find_config(None, &env, "definitely-not-here-kanade-test.toml");
158 let err = r.expect_err("should error");
159 let msg = format!("{err}");
160 assert!(msg.contains("--config"), "msg: {msg}");
161 assert!(msg.contains(&env), "msg: {msg}");
162 assert!(
163 msg.contains("definitely-not-here-kanade-test.toml"),
164 "msg: {msg}"
165 );
166 }
167
168 #[test]
169 fn dirs_are_os_appropriate() {
170 let cfg = config_dir();
171 let data = data_dir();
172 let logs = log_dir();
173 assert!(cfg.is_absolute(), "config_dir = {cfg:?}");
175 assert!(data.is_absolute(), "data_dir = {data:?}");
176 assert!(logs.is_absolute(), "log_dir = {logs:?}");
177 assert_ne!(cfg, data);
179 assert_ne!(data, logs);
180 assert_ne!(cfg, logs);
181 }
182
183 #[cfg(target_os = "windows")]
184 #[test]
185 fn windows_dirs_root_at_program_data_kanade() {
186 let cfg = config_dir();
187 let data = data_dir();
188 let logs = log_dir();
189 assert!(cfg.ends_with("Kanade\\config"), "{cfg:?}");
190 assert!(data.ends_with("Kanade\\data"), "{data:?}");
191 assert!(logs.ends_with("Kanade\\logs"), "{logs:?}");
192 }
193
194 #[cfg(not(target_os = "windows"))]
195 #[test]
196 fn unix_dirs_match_fhs_conventions() {
197 assert_eq!(config_dir(), PathBuf::from("/etc/kanade"));
198 assert_eq!(data_dir(), PathBuf::from("/var/lib/kanade"));
199 assert_eq!(log_dir(), PathBuf::from("/var/log/kanade"));
200 }
201}