Skip to content

Commit

Permalink
Support XDG convention in MacOS too (#1143)
Browse files Browse the repository at this point in the history
* feat(macos): Support XDG conventions on macOS too

* store base strategy in FnmConfig

* add changeset

---------

Co-authored-by: Utkarsh Gupta <utkarshgupta137@gmail.com>
  • Loading branch information
Schniz and utkarshgupta137 authored May 29, 2024
1 parent b527554 commit f76a001
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 80 deletions.
5 changes: 5 additions & 0 deletions .changeset/cold-drinks-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fnm": minor
---

use XDG conventions in MacOS directories by default
70 changes: 21 additions & 49 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ chrono = { version = "0.4.38", features = ["serde", "now"], default-features = f
tar = "0.4.40"
xz2 = "0.1.7"
node-semver = "2.1.0"
dirs = "5.0.1"
etcetera = "0.8.0"
colored = "2.1.0"
zip = "2.1.0"
tempfile = "3.10.1"
Expand Down
3 changes: 1 addition & 2 deletions src/commands/env.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use super::command::Command;
use crate::config::FnmConfig;
use crate::directories;
use crate::fs::symlink_dir;
use crate::outln;
use crate::path_ext::PathExt;
Expand Down Expand Up @@ -36,7 +35,7 @@ fn generate_symlink_path() -> String {
}

fn make_symlink(config: &FnmConfig) -> Result<std::path::PathBuf, Error> {
let base_dir = directories::multishell_storage().ensure_exists_silently();
let base_dir = config.multishell_storage().ensure_exists_silently();
let mut temp_dir = base_dir.join(generate_symlink_path());

while temp_dir.exists() {
Expand Down
24 changes: 10 additions & 14 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::arch::Arch;
use crate::directories::Directories;
use crate::log_level::LogLevel;
use crate::path_ext::PathExt;
use crate::version_file_strategy::VersionFileStrategy;
use dirs::{data_dir, home_dir};
use url::Url;

#[derive(clap::Parser, Debug)]
Expand Down Expand Up @@ -87,6 +87,9 @@ pub struct FnmConfig {
verbatim_doc_comment
)]
resolve_engines: bool,

#[clap(skip)]
directories: Directories,
}

impl Default for FnmConfig {
Expand All @@ -100,6 +103,7 @@ impl Default for FnmConfig {
version_file_strategy: VersionFileStrategy::default(),
corepack_enabled: false,
resolve_engines: false,
directories: Directories::default(),
}
}
}
Expand Down Expand Up @@ -134,19 +138,7 @@ impl FnmConfig {
return dir;
}

let legacy = home_dir()
.map(|dir| dir.join(".fnm"))
.filter(|dir| dir.exists());

let modern = data_dir().map(|dir| dir.join("fnm"));

if let Some(dir) = legacy {
return dir;
}

modern
.expect("Can't get data directory")
.ensure_exists_silently()
self.directories.default_base_dir()
}

pub fn installations_dir(&self) -> std::path::PathBuf {
Expand All @@ -165,6 +157,10 @@ impl FnmConfig {
.ensure_exists_silently()
}

pub fn multishell_storage(&self) -> std::path::PathBuf {
self.directories.multishell_storage()
}

#[cfg(test)]
pub fn with_base_dir(mut self, base_dir: Option<std::path::PathBuf>) -> Self {
self.base_dir = base_dir;
Expand Down
80 changes: 66 additions & 14 deletions src/directories.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,78 @@
use etcetera::BaseStrategy;
use std::path::PathBuf;

use crate::path_ext::PathExt;

fn xdg_dir(env: &str) -> Option<PathBuf> {
let env_var = std::env::var(env).ok()?;
Some(PathBuf::from(env_var))
if cfg!(windows) {
let env_var = std::env::var(env).ok()?;
Some(PathBuf::from(env_var))
} else {
// On non-Windows platforms, `etcetera` already handles XDG variables
None
}
}

fn state_dir() -> Option<PathBuf> {
xdg_dir("XDG_STATE_HOME").or_else(dirs::state_dir)
fn runtime_dir(basedirs: &impl BaseStrategy) -> Option<PathBuf> {
xdg_dir("XDG_RUNTIME_DIR").or_else(|| basedirs.runtime_dir())
}

fn cache_dir() -> Option<PathBuf> {
xdg_dir("XDG_CACHE_HOME").or_else(dirs::cache_dir)
fn state_dir(basedirs: &impl BaseStrategy) -> Option<PathBuf> {
xdg_dir("XDG_STATE_HOME").or_else(|| basedirs.state_dir())
}

fn runtime_dir() -> Option<PathBuf> {
xdg_dir("XDG_RUNTIME_DIR").or_else(dirs::runtime_dir)
fn cache_dir(basedirs: &impl BaseStrategy) -> PathBuf {
xdg_dir("XDG_CACHE_HOME").unwrap_or_else(|| basedirs.cache_dir())
}

pub fn multishell_storage() -> PathBuf {
runtime_dir()
.or_else(state_dir)
.or_else(cache_dir)
.unwrap_or_else(std::env::temp_dir)
.join("fnm_multishells")
/// A helper struct for directories in fnm that uses XDG Base Directory Specification
/// if applicable for the platform.
#[derive(Debug)]
pub struct Directories(
#[cfg(windows)] etcetera::base_strategy::Windows,
#[cfg(not(windows))] etcetera::base_strategy::Xdg,
);

impl Default for Directories {
fn default() -> Self {
Self(etcetera::choose_base_strategy().expect("choosing base strategy"))
}
}

impl Directories {
pub fn strategy(&self) -> &impl BaseStrategy {
&self.0
}

pub fn default_base_dir(&self) -> PathBuf {
let strategy = self.strategy();
let modern = strategy.data_dir().join("fnm");
if modern.exists() {
return modern;
}

let legacy = strategy.home_dir().join(".fnm");
if legacy.exists() {
return legacy;
}

#[cfg(target_os = "macos")]
{
let basedirs = etcetera::base_strategy::Apple::new().expect("Can't get home directory");
let legacy = basedirs.data_dir().join("fnm");
if legacy.exists() {
return legacy;
}
}

modern.ensure_exists_silently()
}

pub fn multishell_storage(&self) -> PathBuf {
let basedirs = self.strategy();
let dir = runtime_dir(basedirs)
.or_else(|| state_dir(basedirs))
.unwrap_or_else(|| cache_dir(basedirs));
dir.join("fnm_multishells")
}
}

0 comments on commit f76a001

Please sign in to comment.