plausible_cli/config/
mod.rs1use std::path::{Path, PathBuf};
2
3pub mod accounts;
4
5#[derive(Debug, Clone)]
7pub struct ConfigPaths {
8 base_dir: PathBuf,
9}
10
11impl ConfigPaths {
12 pub fn with_project_dirs() -> Result<Self, ConfigError> {
14 let project_dirs = directories::ProjectDirs::from("io", "plausible", "plausible-cli")
15 .ok_or(ConfigError::UnsupportedPlatform)?;
16 Ok(Self {
17 base_dir: project_dirs.config_dir().to_path_buf(),
18 })
19 }
20
21 pub fn from_base_dir(base_dir: impl Into<PathBuf>) -> Self {
23 Self {
24 base_dir: base_dir.into(),
25 }
26 }
27
28 pub fn config_file(&self) -> PathBuf {
30 self.base_dir.join("config.toml")
31 }
32
33 pub fn usage_dir(&self) -> PathBuf {
35 self.base_dir.join("usage")
36 }
37
38 pub fn accounts_dir(&self) -> PathBuf {
40 self.base_dir.join("accounts")
41 }
42
43 pub fn accounts_file(&self) -> PathBuf {
45 self.base_dir.join("accounts.json")
46 }
47
48 pub fn ensure_exists(&self) -> Result<(), ConfigError> {
50 let base = self.base_dir.clone();
51 std::fs::create_dir_all(&base).map_err(|source| ConfigError::Io {
52 path: base.clone(),
53 source,
54 })?;
55
56 let accounts_dir = self.accounts_dir();
57 std::fs::create_dir_all(&accounts_dir).map_err(|source| ConfigError::Io {
58 path: accounts_dir.clone(),
59 source,
60 })?;
61
62 let usage_dir = self.usage_dir();
63 std::fs::create_dir_all(&usage_dir).map_err(|source| ConfigError::Io {
64 path: usage_dir.clone(),
65 source,
66 })?;
67 Ok(())
68 }
69
70 pub fn base_dir(&self) -> &Path {
72 &self.base_dir
73 }
74}
75
76#[derive(thiserror::Error, Debug)]
77pub enum ConfigError {
78 #[error("failed to determine configuration directory for this platform")]
79 UnsupportedPlatform,
80 #[error("I/O error interacting with {path:?}")]
81 Io {
82 path: PathBuf,
83 #[source]
84 source: std::io::Error,
85 },
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use std::fs;
92
93 #[test]
94 fn config_paths_resolve_expected_files() {
95 let tmp = tempfile::tempdir().expect("temporary dir");
96 let paths = ConfigPaths::from_base_dir(tmp.path());
97
98 assert_eq!(paths.config_file(), tmp.path().join("config.toml"));
99 assert_eq!(paths.usage_dir(), tmp.path().join("usage"));
100 assert_eq!(paths.accounts_dir(), tmp.path().join("accounts"));
101 assert_eq!(paths.accounts_file(), tmp.path().join("accounts.json"));
102 }
103
104 #[test]
105 fn ensure_exists_creates_directory_tree() {
106 let tmp = tempfile::tempdir().expect("temporary dir");
107 let base = tmp.path().join("nested").join("plausible");
108 let paths = ConfigPaths::from_base_dir(&base);
109
110 paths.ensure_exists().expect("create dirs");
111 assert!(fs::metadata(paths.accounts_dir())
112 .expect("metadata")
113 .is_dir());
114 assert!(fs::metadata(paths.usage_dir()).expect("metadata").is_dir());
115 assert!(fs::metadata(&base).expect("metadata").is_dir());
116 }
117}