use anyhow::{Context, Result};
use directories::ProjectDirs;
use std::path::PathBuf;
pub trait AppContext: Send + Sync + std::fmt::Debug {
fn get_data_dir(&self) -> Result<PathBuf>;
fn get_config_dir(&self) -> Result<PathBuf>;
fn get_cache_dir(&self) -> Result<PathBuf>;
fn get_config_file_path(&self) -> Result<PathBuf> {
Ok(self.get_config_dir()?.join("config.toml"))
}
fn get_journal_path(&self) -> Option<PathBuf> {
self.get_data_dir().ok().map(|p| p.join("journal.json"))
}
fn get_local_task_path(&self) -> Option<PathBuf> {
self.get_data_dir().ok().map(|p| p.join("local.json"))
}
fn get_alarm_index_path(&self) -> Option<PathBuf> {
self.get_data_dir().ok().map(|p| p.join("alarm_index.json"))
}
}
#[derive(Clone, Debug)]
pub struct StandardContext {
override_root: Option<PathBuf>,
}
impl StandardContext {
pub fn new(override_root: Option<PathBuf>) -> Self {
Self { override_root }
}
fn ensure_exists(path: PathBuf) -> Result<PathBuf> {
if !path.exists() {
std::fs::create_dir_all(&path)
.with_context(|| format!("Failed to create directory: {:?}", path))?;
}
Ok(path)
}
fn get_proj_dirs() -> Option<ProjectDirs> {
ProjectDirs::from("com", "cfait", "cfait")
.or_else(|| ProjectDirs::from("com", "trougnouf", "cfait"))
}
}
impl AppContext for StandardContext {
fn get_data_dir(&self) -> Result<PathBuf> {
if let Some(root) = &self.override_root {
return Self::ensure_exists(root.join("data"));
}
let proj = Self::get_proj_dirs().ok_or_else(|| anyhow::anyhow!("No home directory"))?;
Self::ensure_exists(proj.data_dir().to_path_buf())
}
fn get_config_dir(&self) -> Result<PathBuf> {
if let Some(root) = &self.override_root {
return Self::ensure_exists(root.join("config"));
}
let proj = Self::get_proj_dirs().ok_or_else(|| anyhow::anyhow!("No home directory"))?;
Self::ensure_exists(proj.config_dir().to_path_buf())
}
fn get_cache_dir(&self) -> Result<PathBuf> {
if let Some(root) = &self.override_root {
return Self::ensure_exists(root.join("cache"));
}
let proj = Self::get_proj_dirs().ok_or_else(|| anyhow::anyhow!("No home directory"))?;
Self::ensure_exists(proj.cache_dir().to_path_buf())
}
}
#[derive(Clone, Debug)]
pub struct TestContext {
pub root: PathBuf,
}
impl TestContext {
pub fn new() -> Self {
let uuid = uuid::Uuid::new_v4();
let root = std::env::temp_dir().join(format!("cfait_test_{}", uuid));
std::fs::create_dir_all(&root).expect("failed to create TestContext temp dir");
Self { root }
}
}
impl Default for TestContext {
fn default() -> Self {
Self::new()
}
}
impl AppContext for TestContext {
fn get_data_dir(&self) -> Result<PathBuf> {
let p = self.root.join("data");
std::fs::create_dir_all(&p)?;
Ok(p)
}
fn get_config_dir(&self) -> Result<PathBuf> {
let p = self.root.join("config");
std::fs::create_dir_all(&p)?;
Ok(p)
}
fn get_cache_dir(&self) -> Result<PathBuf> {
let p = self.root.join("cache");
std::fs::create_dir_all(&p)?;
Ok(p)
}
}
impl Drop for TestContext {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}
pub type SharedContext = std::sync::Arc<dyn AppContext>;