diff --git a/Cargo.lock b/Cargo.lock index 61dd9c401..d81e12cac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -747,20 +747,11 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "directories" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys 0.3.7", -] - [[package]] name = "dirs" version = "0.1.54" dependencies = [ - "directories", + "etcetera", ] [[package]] @@ -769,18 +760,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", + "dirs-sys", ] [[package]] @@ -860,6 +840,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "extracted_fzy" version = "0.1.54" diff --git a/Cargo.toml b/Cargo.toml index 81cda03d5..5e36ee89a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ chrono-humanize = "0.2.3" clap = { version = "4.2", features = ["derive"] } colors-transform = "0.2.11" criterion = "0.5" -directories = "4.0" +etcetera = "0.8.0" futures = "0.3" fuzzy-matcher = "0.3" grep-matcher = "0.1" diff --git a/crates/cli/src/command/cache.rs b/crates/cli/src/command/cache.rs index 987c18880..ff844176d 100644 --- a/crates/cli/src/command/cache.rs +++ b/crates/cli/src/command/cache.rs @@ -47,7 +47,7 @@ impl List { if self.all { writeln!(lock, "Cached entries:")?; - let mut entries = read_dir(&cache_dir)? + let mut entries = read_dir(cache_dir)? .map(|res| { res.map(|e| { e.path() @@ -81,7 +81,7 @@ impl Purge { fn run(&self) -> Result<()> { let cache_dir = Dirs::clap_cache_dir()?; - if let Ok(cache_size) = dir_size(&cache_dir) { + if let Ok(cache_size) = dir_size(cache_dir) { let readable_size = if cache_size > 1024 * 1024 { format!("{}MB", cache_size / 1024 / 1024) } else if cache_size > 1024 { @@ -99,7 +99,7 @@ impl Purge { } } - remove_dir_contents(&cache_dir)?; + remove_dir_contents(cache_dir)?; println!( "Current cache directory {} has been purged sucessfully", diff --git a/crates/dirs/Cargo.toml b/crates/dirs/Cargo.toml index c358d10e3..b08ebf575 100644 --- a/crates/dirs/Cargo.toml +++ b/crates/dirs/Cargo.toml @@ -4,4 +4,4 @@ version.workspace = true edition.workspace = true [dependencies] -directories = { workspace = true } +etcetera = { workspace = true } diff --git a/crates/dirs/src/lib.rs b/crates/dirs/src/lib.rs index 0862b232d..947cdfb40 100644 --- a/crates/dirs/src/lib.rs +++ b/crates/dirs/src/lib.rs @@ -1,33 +1,81 @@ -use directories::{BaseDirs, ProjectDirs}; -use std::path::PathBuf; +use etcetera::app_strategy::choose_native_strategy; +use etcetera::{choose_app_strategy, AppStrategy, AppStrategyArgs}; +use std::path::{Path, PathBuf}; use std::sync::OnceLock; pub struct Dirs; +pub struct DirsProject { + home_dir: PathBuf, + config_dir: PathBuf, + cache_dir: PathBuf, + data_dir: PathBuf, +} + +impl DirsProject { + fn new(app: impl AppStrategy) -> Self { + DirsProject { + home_dir: app.home_dir().to_path_buf(), + config_dir: app.config_dir(), + cache_dir: app.cache_dir(), + data_dir: app.data_dir(), + } + } +} + impl Dirs { /// Project directory specifically for Vim Clap. /// /// All the files created by vim-clap are stored there. - pub fn project() -> &'static ProjectDirs { - static CELL: OnceLock = OnceLock::new(); + fn project() -> &'static DirsProject { + static CELL: OnceLock = OnceLock::new(); CELL.get_or_init(|| { - ProjectDirs::from("org", "vim", "Vim Clap") - .expect("Couldn't create project directory for vim-clap") + let app = AppStrategyArgs { + top_level_domain: "org".to_string(), + author: "vim".to_owned(), + app_name: "Vim Clap".to_owned(), + }; + + if cfg!(target_os = "macos") { + // SAFETY it does never fail + let apple = + choose_native_strategy(app.clone()).expect("Cannot find the home directory"); + + if apple.in_config_dir("config.toml").exists() { + return DirsProject::new(apple); + } + } + let xdg = choose_app_strategy(app).expect("Cannot find the home directory"); + + DirsProject::new(xdg) }) } - /// Provides access to the standard directories that the operating system uses. - pub fn base() -> &'static BaseDirs { - static CELL: OnceLock = OnceLock::new(); + /// Get the home directory + pub fn home_dir() -> &'static Path { + Self::project().home_dir.as_path() + } + + /// Get the config directory + pub fn config_dir() -> &'static Path { + Self::project().config_dir.as_path() + } + + /// Get the cache directory + pub fn cache_dir() -> &'static Path { + Self::project().cache_dir.as_path() + } - CELL.get_or_init(|| BaseDirs::new().expect("Failed to construct BaseDirs")) + /// Get the data directory + pub fn data_dir() -> &'static Path { + Self::project().data_dir.as_path() } /// Cache directory for Vim Clap project. - pub fn clap_cache_dir() -> std::io::Result { - let cache_dir = Self::project().cache_dir(); + pub fn clap_cache_dir() -> std::io::Result<&'static Path> { + let cache_dir = Self::cache_dir(); std::fs::create_dir_all(cache_dir)?; - Ok(cache_dir.to_path_buf()) + Ok(cache_dir) } } diff --git a/crates/maple_config/src/lib.rs b/crates/maple_config/src/lib.rs index e0dfeee14..303cc4b41 100644 --- a/crates/maple_config/src/lib.rs +++ b/crates/maple_config/src/lib.rs @@ -14,10 +14,9 @@ fn load_config( specified_config_file: Option, ) -> (Config, PathBuf, Option) { let config_file = specified_config_file.unwrap_or_else(|| { - // Linux: ~/.config/vimclap/config.toml - // macOS: ~/Library/Application\ Support/org.vim.Vim-Clap/config.toml + // Linux & macOS: ~/.config/vimclap/config.toml // Windows: ~\AppData\Roaming\Vim\Vim Clap\config\config.toml - let config_file_path = Dirs::project().config_dir().join("config.toml"); + let config_file_path = Dirs::config_dir().join("config.toml"); if !config_file_path.exists() { std::fs::create_dir_all(&config_file_path).ok(); diff --git a/crates/maple_core/src/datastore/mod.rs b/crates/maple_core/src/datastore/mod.rs index 2ec7d1266..f97b1f953 100644 --- a/crates/maple_core/src/datastore/mod.rs +++ b/crates/maple_core/src/datastore/mod.rs @@ -69,14 +69,14 @@ pub fn cache_metadata_path() -> Option<&'static PathBuf> { /// Returns a `PathBuf` using given file name under the project data directory. pub fn generate_data_file_path(filename: &str) -> std::io::Result { - let data_dir = Dirs::project().data_dir(); + let data_dir = Dirs::data_dir(); std::fs::create_dir_all(data_dir)?; Ok(data_dir.join(filename)) } /// Returns a `PathBuf` using given file name under the project cache directory. pub fn generate_cache_file_path(filename: impl AsRef) -> std::io::Result { - let cache_dir = Dirs::project().cache_dir(); + let cache_dir = Dirs::cache_dir(); std::fs::create_dir_all(cache_dir)?; Ok(cache_dir.join(filename)) } diff --git a/crates/maple_core/src/searcher/tagfiles.rs b/crates/maple_core/src/searcher/tagfiles.rs index 907a0cb24..178cb1593 100644 --- a/crates/maple_core/src/searcher/tagfiles.rs +++ b/crates/maple_core/src/searcher/tagfiles.rs @@ -34,7 +34,7 @@ impl TagItem { let mut home_path = PathBuf::new(); let path = Path::new(&self.path); let path = path.strip_prefix(cwd).unwrap_or({ - path.strip_prefix(Dirs::base().home_dir()) + path.strip_prefix(Dirs::home_dir()) .map(|path| { home_path.push("~"); home_path = home_path.join(path); diff --git a/crates/maple_core/src/stdio_server/winbar.rs b/crates/maple_core/src/stdio_server/winbar.rs index 05757bff0..addd273d2 100644 --- a/crates/maple_core/src/stdio_server/winbar.rs +++ b/crates/maple_core/src/stdio_server/winbar.rs @@ -68,7 +68,7 @@ pub async fn update_winbar( } None => winwidth, }; - let path = if let Some(home) = dirs::Dirs::base().home_dir().to_str() { + let path = if let Some(home) = dirs::Dirs::home_dir().to_str() { path.replacen(home, "~", 1) } else { path diff --git a/crates/maple_core/src/tools/ctags/mod.rs b/crates/maple_core/src/tools/ctags/mod.rs index cb8cb6fc1..33d7b7fc3 100644 --- a/crates/maple_core/src/tools/ctags/mod.rs +++ b/crates/maple_core/src/tools/ctags/mod.rs @@ -33,7 +33,7 @@ pub static DEFAULT_EXCLUDE_OPT: Lazy = Lazy::new(|| { /// Directory for the `tags` files. pub static CTAGS_TAGS_DIR: Lazy = Lazy::new(|| { - let tags_dir = Dirs::project().data_dir().join("tags"); + let tags_dir = Dirs::data_dir().join("tags"); std::fs::create_dir_all(&tags_dir).expect("Couldn't create tags directory for vim-clap"); diff --git a/crates/maple_core/src/tools/gtags/mod.rs b/crates/maple_core/src/tools/gtags/mod.rs index 726fcaf27..ca203624a 100644 --- a/crates/maple_core/src/tools/gtags/mod.rs +++ b/crates/maple_core/src/tools/gtags/mod.rs @@ -6,7 +6,7 @@ pub static GTAGS_EXISTS: Lazy = Lazy::new(|| gtags_executable_exists().unw /// Directory for `GTAGS`/`GRTAGS`. pub static GTAGS_DIR: Lazy = Lazy::new(|| { - let gtags_dir = Dirs::project().data_dir().join("gtags"); + let gtags_dir = Dirs::data_dir().join("gtags"); std::fs::create_dir_all(>ags_dir).expect("Couldn't create gtags directory for vim-clap"); diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 4c6b31ebd..ff5e24f95 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -21,7 +21,7 @@ impl<'de> Deserialize<'de> for AbsPathBuf { if path.is_absolute() { Ok(Self(path)) } else if let Ok(stripped) = path.strip_prefix("~") { - let path = Dirs::base().home_dir().join(stripped); + let path = Dirs::home_dir().join(stripped); // Resolve the symlink. let path = canonicalize(path).map_err(|err| DeserializeError::custom(err.to_string()))?; @@ -124,7 +124,7 @@ pub fn expand_tilde(path: impl AsRef) -> PathBuf { .as_ref() .strip_prefix(HOME_PREFIX.get_or_init(|| format!("~{MAIN_SEPARATOR}"))) { - Dirs::base().home_dir().join(stripped) + Dirs::home_dir().join(stripped) } else { path.as_ref().into() } @@ -135,7 +135,7 @@ pub fn truncate_absolute_path(abs_path: &str, max_len: usize) -> Cow<'_, str> { if abs_path.len() > max_len { let gap = abs_path.len() - max_len; - if let Some(home_dir) = Dirs::base().home_dir().to_str() { + if let Some(home_dir) = Dirs::home_dir().to_str() { if abs_path.starts_with(home_dir) { // ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/string.rs if home_dir.len() > gap { @@ -302,10 +302,7 @@ mod tests { let p = ".rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/string.rs"; #[cfg(target_os = "windows")] let p = r#".rustup\toolchains\stable-x86_64-unknown-linux-gnu\lib\rustlib\src\rust\library\alloc\src\string.rs"#; - let abs_path = format!( - "{}{MAIN_SEPARATOR}{p}", - Dirs::base().home_dir().to_str().unwrap(), - ); + let abs_path = format!("{}{MAIN_SEPARATOR}{p}", Dirs::home_dir().to_str().unwrap(),); let max_len = 60; #[cfg(not(target_os = "windows"))] let expected = "~/.rustup/.../src/rust/library/alloc/src/string.rs";