use anyhow::{anyhow, Result};
use directories::ProjectDirs;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct Paths {
pub data_dir: PathBuf,
pub config_file: PathBuf,
}
impl Paths {
pub fn resolve(override_data: Option<&Path>, override_config: Option<&Path>) -> Result<Self> {
let data_dir = if let Some(d) = override_data {
d.to_path_buf()
} else {
default_data_dir()?
};
let config_file = override_config
.map(PathBuf::from)
.unwrap_or_else(|| data_dir.join("config.toml"));
Ok(Self {
data_dir,
config_file,
})
}
#[must_use]
pub fn data_root(&self) -> PathBuf {
self.data_dir.join("data")
}
#[must_use]
pub fn dict_core_dir(&self) -> PathBuf {
self.data_root()
}
#[must_use]
pub fn rules_dir(&self) -> PathBuf {
self.data_root()
}
#[must_use]
pub fn dict_user_dir(&self) -> PathBuf {
self.data_root().join("user")
}
#[must_use]
pub fn overrides_file(&self) -> PathBuf {
self.data_root().join("overrides.toml")
}
}
fn default_data_dir() -> Result<PathBuf> {
if let Ok(exe) = std::env::current_exe() {
if let Some(parent) = exe.parent() {
return Ok(parent.to_path_buf());
}
}
let project = ProjectDirs::from("com", "furigana", "furigana")
.ok_or_else(|| anyhow!("プロジェクトディレクトリの解決に失敗"))?;
Ok(project.data_dir().to_path_buf())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolve_uses_overrides_and_derives_subpaths() {
let base = Path::new("/tmp/furi_d");
let p = Paths::resolve(Some(base), Some(Path::new("/tmp/c.toml"))).unwrap();
assert_eq!(p.data_dir, base);
assert_eq!(p.config_file, Path::new("/tmp/c.toml"));
assert_eq!(p.data_root(), base.join("data"));
assert_eq!(p.dict_user_dir(), base.join("data").join("user"));
assert_eq!(p.overrides_file(), base.join("data").join("overrides.toml"));
}
#[test]
fn resolve_config_defaults_under_data_dir() {
let base = Path::new("/tmp/furi_d");
let p = Paths::resolve(Some(base), None).unwrap();
assert_eq!(p.config_file, base.join("config.toml"));
}
}