1use std::path::PathBuf;
2
3pub fn data_dir() -> PathBuf {
4 let base = dirs::data_dir().unwrap_or_else(|| PathBuf::from("."));
5 base.join("huddle")
6}
7
8pub fn config_path() -> PathBuf {
15 let base = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
16 base.join("huddle").join("config.toml")
17}
18
19pub fn load_relays() -> Option<Vec<String>> {
25 let path = config_path();
26 let body = std::fs::read_to_string(&path).ok()?;
27 let mut in_network = false;
28 let mut out: Vec<String> = Vec::new();
29 for line in body.lines() {
30 let line = line.trim();
31 if line.is_empty() || line.starts_with('#') {
32 continue;
33 }
34 if line.starts_with('[') {
35 in_network = line == "[network]";
36 continue;
37 }
38 if !in_network {
39 continue;
40 }
41 if let Some(rest) = line.strip_prefix("relays") {
42 let rest = rest.trim_start().trim_start_matches('=').trim();
43 let payload = rest.trim_start_matches('[').trim_end_matches(']');
47 for item in payload.split(',') {
48 let item = item.trim().trim_matches('"').trim_matches('\'');
49 if !item.is_empty() {
50 out.push(item.to_string());
51 }
52 }
53 }
54 }
55 Some(out)
56}
57
58pub fn db_path() -> PathBuf {
59 data_dir().join("huddle.db")
60}
61
62pub fn identity_key_path() -> PathBuf {
63 data_dir().join("identity.key")
64}
65
66pub fn log_path() -> PathBuf {
67 data_dir().join("huddle.log")
68}
69
70pub fn ensure_data_dir() -> std::io::Result<()> {
71 std::fs::create_dir_all(data_dir())
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn data_dir_is_inside_huddle_directory() {
80 let dir = data_dir();
81 assert!(dir.ends_with("huddle") || dir.to_string_lossy().contains("huddle"));
82 }
83
84 #[test]
85 fn db_path_ends_with_huddle_db() {
86 let path = db_path();
87 assert_eq!(path.file_name().unwrap(), "huddle.db");
88 }
89
90 #[test]
91 fn identity_path_ends_with_identity_key() {
92 let path = identity_key_path();
93 assert_eq!(path.file_name().unwrap(), "identity.key");
94 }
95}