1use std::path::PathBuf;
9
10pub fn config_dir() -> Option<PathBuf> {
12 dirs::config_dir().map(|d| d.join("perspt"))
13}
14
15pub fn config_file() -> Option<PathBuf> {
17 config_dir().map(|d| d.join("config.toml"))
18}
19
20pub fn policy_dir() -> Option<PathBuf> {
22 config_dir().map(|d| d.join("rules"))
23}
24
25pub fn data_dir() -> Option<PathBuf> {
27 dirs::data_local_dir().map(|d| d.join("perspt"))
28}
29
30pub fn database_path() -> Option<PathBuf> {
32 data_dir().map(|d| d.join("perspt.db"))
33}
34
35pub fn legacy_dir() -> Option<PathBuf> {
37 dirs::home_dir()
38 .map(|h| h.join(".perspt"))
39 .filter(|p| p.is_dir())
40}
41
42pub fn check_legacy_migration() {
47 let Some(legacy) = legacy_dir() else {
48 return;
49 };
50
51 let legacy_config = legacy.join("config.toml");
52 let legacy_rules = legacy.join("rules");
53
54 let new_config = config_file();
55 let new_rules = policy_dir();
56
57 if legacy_config.exists() {
58 let target = new_config
59 .as_deref()
60 .map(|p| p.display().to_string())
61 .unwrap_or_else(|| "<config_dir>/perspt/config.toml".into());
62 if new_config.as_ref().is_none_or(|p| !p.exists()) {
63 log::warn!(
64 "Legacy config found at {}. Consider moving it to {}",
65 legacy_config.display(),
66 target
67 );
68 }
69 }
70
71 if legacy_rules.is_dir() {
72 let target = new_rules
73 .as_deref()
74 .map(|p| p.display().to_string())
75 .unwrap_or_else(|| "<config_dir>/perspt/rules/".into());
76 if new_rules.as_ref().is_none_or(|p| !p.is_dir()) {
77 log::warn!(
78 "Legacy rules found at {}. Consider moving them to {}",
79 legacy_rules.display(),
80 target
81 );
82 }
83 }
84}
85
86pub fn resolve_config_file() -> Option<PathBuf> {
89 if let Some(path) = config_file() {
91 if path.exists() {
92 return Some(path);
93 }
94 }
95 legacy_dir()
97 .map(|d| d.join("config.toml"))
98 .filter(|p| p.exists())
99}
100
101pub fn resolve_policy_dir() -> Option<PathBuf> {
104 if let Some(path) = policy_dir() {
106 if path.is_dir() {
107 return Some(path);
108 }
109 }
110 legacy_dir().map(|d| d.join("rules")).filter(|p| p.is_dir())
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn config_dir_ends_with_perspt() {
120 if let Some(dir) = config_dir() {
121 assert!(dir.ends_with("perspt"));
122 }
123 }
124
125 #[test]
126 fn config_file_ends_with_config_toml() {
127 if let Some(path) = config_file() {
128 assert_eq!(path.file_name().unwrap(), "config.toml");
129 }
130 }
131
132 #[test]
133 fn policy_dir_ends_with_rules() {
134 if let Some(dir) = policy_dir() {
135 assert!(dir.ends_with("rules"));
136 }
137 }
138
139 #[test]
140 fn data_dir_ends_with_perspt() {
141 if let Some(dir) = data_dir() {
142 assert!(dir.ends_with("perspt"));
143 }
144 }
145
146 #[test]
147 fn database_path_ends_with_db() {
148 if let Some(path) = database_path() {
149 assert_eq!(path.file_name().unwrap(), "perspt.db");
150 }
151 }
152}