1use std::env;
4use std::path::{Path, PathBuf};
5
6use anyhow::{Context, Result};
7use directories::ProjectDirs;
8
9#[derive(Debug, Clone)]
11pub struct AppPaths {
12 pub root: Option<PathBuf>,
14 pub config_dir: PathBuf,
16 pub data_dir: PathBuf,
18 pub log_dir: PathBuf,
20 pub config_file: PathBuf,
22 pub database_path: PathBuf,
24}
25
26impl AppPaths {
27 pub fn discover(database_override: Option<PathBuf>) -> Result<Self> {
29 if let Ok(root) = env::var("CARGOWATCH_HOME") {
30 let root = PathBuf::from(root);
31 let config_dir = root.join("config");
32 let data_dir = root.join("data");
33 let log_dir = root.join("logs");
34 let config_file = config_dir.join("config.toml");
35 let database_path = database_override.unwrap_or_else(|| data_dir.join("cargowatch.db"));
36 return Ok(Self {
37 root: Some(root),
38 config_dir,
39 data_dir,
40 log_dir,
41 config_file,
42 database_path,
43 });
44 }
45
46 let project_dirs = ProjectDirs::from("dev", "cargowatch", "CargoWatch")
47 .context("failed to resolve application directories")?;
48 let config_dir = project_dirs.config_dir().to_path_buf();
49 let data_dir = project_dirs.data_local_dir().to_path_buf();
50 let log_dir = data_dir.join("logs");
51 let config_file = config_dir.join("config.toml");
52 let database_path = database_override.unwrap_or_else(|| data_dir.join("cargowatch.db"));
53
54 Ok(Self {
55 root: None,
56 config_dir,
57 data_dir,
58 log_dir,
59 config_file,
60 database_path,
61 })
62 }
63
64 pub fn ensure_exists(&self) -> Result<()> {
66 std::fs::create_dir_all(&self.config_dir)
67 .with_context(|| format!("failed to create {}", self.config_dir.display()))?;
68 std::fs::create_dir_all(&self.data_dir)
69 .with_context(|| format!("failed to create {}", self.data_dir.display()))?;
70 std::fs::create_dir_all(&self.log_dir)
71 .with_context(|| format!("failed to create {}", self.log_dir.display()))?;
72 Ok(())
73 }
74
75 pub fn config_file(&self) -> &Path {
77 &self.config_file
78 }
79}