diff --git a/src/commands/use.rs b/src/commands/use.rs index 3c44b17..5f91eed 100644 --- a/src/commands/use.rs +++ b/src/commands/use.rs @@ -3,6 +3,7 @@ use super::install::Install; use crate::fs; use crate::installed_versions; use crate::outln; +use crate::symlink_path::DefaultMultishellPathExt; use crate::system_version; use crate::user_version::UserVersion; use crate::version::Version; @@ -23,8 +24,10 @@ impl Command for Use { type Error = Error; fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { - let multishell_path = config.multishell_path().context(FnmEnvWasNotSourced)?; - warn_if_multishell_path_not_in_path_env_var(multishell_path, config); + let multishell_path = config + .multishell_path_or_default() + .context(FnmEnvWasNotSourced)?; + warn_if_multishell_path_not_in_path_env_var(&multishell_path, config); let all_versions = installed_versions::list(config.installations_dir()).context(VersionListingError)?; @@ -78,7 +81,7 @@ impl Command for Use { } }; - replace_symlink(&version_path, multishell_path).context(SymlinkingCreationIssue)?; + replace_symlink(&version_path, &multishell_path).context(SymlinkingCreationIssue)?; Ok(()) } @@ -197,5 +200,5 @@ pub enum Error { "You should setup your shell profile to evaluate `fnm env`, see https://github.com/Schniz/fnm#shell-setup on how to do this", "Check out our documentation for more information: https://fnm.vercel.app" ))] - FnmEnvWasNotSourced, + FnmEnvWasNotSourced { source: Box }, } diff --git a/src/current_version.rs b/src/current_version.rs index 6f7fab3..c2be3e8 100644 --- a/src/current_version.rs +++ b/src/current_version.rs @@ -1,10 +1,12 @@ -use crate::config::FnmConfig; use crate::system_version; use crate::version::Version; -use snafu::{OptionExt, ResultExt, Snafu}; +use crate::{config::FnmConfig, symlink_path::DefaultMultishellPathExt}; +use snafu::{ResultExt, Snafu}; pub fn current_version(config: &FnmConfig) -> Result, Error> { - let multishell_path = config.multishell_path().context(EnvNotApplied)?; + let multishell_path = config + .multishell_path_or_default() + .context(CantCreateMultishellPath)?; if multishell_path.read_link().ok() == Some(system_version::path()) { return Ok(Some(Version::Bypassed)); @@ -31,7 +33,7 @@ pub enum Error { #[snafu(display( "`fnm env` was not applied in this context.\nCan't find fnm's environment variables" ))] - EnvNotApplied, + CantCreateMultishellPath { source: Box }, #[snafu(display("Can't read the version as a valid semver"))] VersionError { source: semver::Error, diff --git a/src/main.rs b/src/main.rs index 53003e3..8ba1f8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ mod version_files; #[macro_use] mod log_level; +mod symlink_path; fn main() { env_logger::init(); diff --git a/src/symlink_path.rs b/src/symlink_path.rs new file mode 100644 index 0000000..a6d45c3 --- /dev/null +++ b/src/symlink_path.rs @@ -0,0 +1,47 @@ +use std::{fs::create_dir_all, path::PathBuf}; + +use log::info; +use sysinfo::{get_current_pid, ProcessExt, System, SystemExt}; + +use crate::config::FnmConfig; + +pub fn generate_symlink_token_based_on_pid() -> Result { + let mut system = System::new(); + let current_pid = get_current_pid()?; + + system.refresh_process(current_pid); + let current_process = system + .process(current_pid) + .ok_or("Couldn't find the current process in the tree")?; + + let parent_pid = current_process + .parent() + .ok_or("Couldn't find the parent pid in the process tree")?; + + Ok(format!("pid_{}", parent_pid)) +} + +pub(crate) trait DefaultMultishellPathExt { + fn multishell_path_or_default(&self) -> Result>; +} + +impl DefaultMultishellPathExt for FnmConfig { + fn multishell_path_or_default(&self) -> Result> { + if let Some(given_path) = self.multishell_path() { + return Ok(given_path.to_path_buf()); + } + + let symlink_token = generate_symlink_token_based_on_pid()?; + let multishells_path = std::env::temp_dir().join("fnm_multishells"); + let new_path = multishells_path.join(symlink_token); + + info!( + "No multishell path given, generating a new one at {}", + new_path.display() + ); + + create_dir_all(&multishells_path)?; + + Ok(new_path) + } +}