use crate::core::AppType;
use crate::env::StdEnv;
use crate::platform::{DirectoryFinder, DirectoryInfo};
use crate::{ConfigTier, Fs, StdFs};
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct PathsBuilder {
app_name: String,
app_type: AppType,
#[cfg(windows)]
company_name: Option<String>,
#[cfg(all(target_os = "macos", feature = "macos-gui"))]
bundle_id: Option<String>,
legacy_rc: bool,
}
impl PathsBuilder {
pub fn new(app_name: impl Into<String>) -> Self {
Self {
app_name: app_name.into(),
app_type: AppType::default(),
#[cfg(windows)]
company_name: None,
#[cfg(all(target_os = "macos", feature = "macos-gui"))]
bundle_id: None,
legacy_rc: true,
}
}
#[must_use]
pub const fn app_type(mut self, app_type: AppType) -> Self {
self.app_type = app_type;
self
}
#[must_use]
pub const fn legacy_rc(mut self, enabled: bool) -> Self {
self.legacy_rc = enabled;
self
}
#[cfg(windows)]
pub fn company_name(mut self, name: impl Into<String>) -> Self {
self.company_name = Some(name.into());
self
}
#[cfg(all(target_os = "macos", feature = "macos-gui"))]
#[must_use]
pub fn bundle_id(mut self, id: impl Into<String>) -> Self {
self.bundle_id = Some(id.into());
self
}
#[must_use]
pub fn build(self) -> PathFinder {
let dir_finder = self.build_directory_finder();
PathFinder {
dir_finder,
fs: Arc::new(StdFs),
}
}
fn build_directory_finder(self) -> Box<dyn DirectoryFinder> {
cfg_if::cfg_if! {
if #[cfg(all(target_os = "macos", feature = "macos-gui"))] {
use crate::platform::MacOSGuiDirectoryFinder;
use crate::platform::UnixDirectoryFinder;
if self.app_type == AppType::Gui {
let bundle_id = self.bundle_id.unwrap_or_else(|| {
format!("com.example.{}", self.app_name)
});
Box::new(MacOSGuiDirectoryFinder::new(bundle_id))
} else {
Box::new(UnixDirectoryFinder::new(self.app_name).legacy_rc(self.legacy_rc))
}
} else if #[cfg(windows)] {
use crate::platform::WindowsDirectoryFinder;
let company_name = self.company_name.unwrap_or_else(|| {
self.app_name.clone()
});
Box::new(WindowsDirectoryFinder::new(self.app_name, company_name))
} else {
use crate::platform::UnixDirectoryFinder;
Box::new(UnixDirectoryFinder::new(self.app_name).legacy_rc(self.legacy_rc))
}
}
}
}
pub struct PathFinder {
dir_finder: Box<dyn DirectoryFinder>,
fs: Arc<dyn Fs>,
}
impl PathFinder {
#[must_use]
pub fn user_dirs(&self) -> Vec<PathBuf> {
self.dir_finder.user_dirs(&StdEnv)
}
#[must_use]
pub fn local_dirs(&self) -> Vec<PathBuf> {
self.dir_finder.local_dirs(&StdEnv)
}
#[must_use]
pub fn system_dirs(&self) -> Vec<PathBuf> {
self.dir_finder.system_dirs(&StdEnv)
}
#[must_use]
pub fn all_dirs(&self) -> Vec<DirectoryInfo> {
let mut dirs = Vec::new();
dirs.extend(self.dirs_with_tier(self.user_dirs(), ConfigTier::User));
dirs.extend(self.dirs_with_tier(self.local_dirs(), ConfigTier::Local));
dirs.extend(self.dirs_with_tier(self.system_dirs(), ConfigTier::System));
dirs
}
#[must_use]
pub fn user_config_dir(&self) -> Option<PathBuf> {
self.user_dirs().into_iter().next()
}
pub fn ensure_user_config_dir(&self) -> std::io::Result<PathBuf> {
let path = self.user_config_dir().ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"No user config directory found",
)
})?;
self.fs.create_dir_all(&path)?;
Ok(path)
}
fn dirs_with_tier(&self, paths: Vec<PathBuf>, tier: ConfigTier) -> Vec<DirectoryInfo> {
paths
.into_iter()
.map(|path| DirectoryInfo {
exists: self.fs.is_dir(&path),
path,
tier,
})
.collect()
}
}
impl std::fmt::Debug for PathFinder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PathFinder")
.field("user_dirs", &self.user_dirs())
.field("local_dirs", &self.local_dirs())
.field("system_dirs", &self.system_dirs())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_default() {
let builder = PathsBuilder::new("myapp");
assert_eq!(builder.app_name, "myapp");
assert_eq!(builder.app_type, AppType::Cli);
}
#[test]
fn test_builder_app_type() {
let builder = PathsBuilder::new("myapp").app_type(AppType::Gui);
assert_eq!(builder.app_type, AppType::Gui);
}
#[test]
fn test_finder_creation() {
let finder = PathsBuilder::new("myapp").build();
let dirs = finder.user_dirs();
assert!(!dirs.is_empty());
}
}