diff --git a/src/commands/exec.rs b/src/commands/exec.rs index 8095476..cc4a90c 100644 --- a/src/commands/exec.rs +++ b/src/commands/exec.rs @@ -1,10 +1,12 @@ use super::command::Command as Cmd; -use crate::choose_version_for_user_input::choose_version_for_user_input; use crate::choose_version_for_user_input::Error as UserInputError; use crate::config::FnmConfig; use crate::outln; use crate::user_version::UserVersion; -use crate::version_files::get_user_version_from_file; +use crate::{ + choose_version_for_user_input::choose_version_for_user_input, + user_version_reader::UserVersionReader, +}; use colored::Colorize; use snafu::{OptionExt, ResultExt, Snafu}; use std::process::{Command, Stdio}; @@ -13,8 +15,9 @@ use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt(setting = structopt::clap::AppSettings::TrailingVarArg)] pub struct Exec { + /// Either an explicit version, or a filename with the version written in it #[structopt(long = "using")] - version: Option, + version: Option, /// Deprecated. This is the default now. #[structopt(long = "using-file", hidden = true)] using_file: bool, @@ -34,10 +37,11 @@ impl Cmd for Exec { let version = self .version - .or_else(|| { + .unwrap_or_else(|| { let current_dir = std::env::current_dir().unwrap(); - get_user_version_from_file(current_dir) + UserVersionReader::Path(current_dir) }) + .to_user_version() .context(CantInferVersion)?; let applicable_version = choose_version_for_user_input(&version, &config) diff --git a/src/commands/install.rs b/src/commands/install.rs index 754876d..1169e0a 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -6,7 +6,7 @@ use crate::outln; use crate::remote_node_index; use crate::user_version::UserVersion; use crate::version::Version; -use crate::version_files::get_user_version_from_file; +use crate::version_files::get_user_version_for_directory; use colored::Colorize; use log::debug; use snafu::{ensure, OptionExt, ResultExt, Snafu}; @@ -48,7 +48,7 @@ impl super::command::Command for Install { let current_dir = std::env::current_dir().unwrap(); let current_version = self .version()? - .or_else(|| get_user_version_from_file(current_dir)) + .or_else(|| get_user_version_for_directory(current_dir)) .context(CantInferVersion)?; let version = match current_version.clone() { diff --git a/src/commands/uninstall.rs b/src/commands/uninstall.rs index 1db3647..9a5abb7 100644 --- a/src/commands/uninstall.rs +++ b/src/commands/uninstall.rs @@ -5,7 +5,7 @@ use crate::installed_versions; use crate::outln; use crate::user_version::UserVersion; use crate::version::Version; -use crate::version_files::get_user_version_from_file; +use crate::version_files::get_user_version_for_directory; use colored::Colorize; use log::debug; use snafu::{ensure, OptionExt, ResultExt, Snafu}; @@ -26,7 +26,7 @@ impl Command for Uninstall { .version .or_else(|| { let current_dir = std::env::current_dir().unwrap(); - get_user_version_from_file(current_dir) + get_user_version_for_directory(current_dir) }) .context(CantInferVersion)?; diff --git a/src/commands/use.rs b/src/commands/use.rs index 01a4954..ea483e9 100644 --- a/src/commands/use.rs +++ b/src/commands/use.rs @@ -7,7 +7,7 @@ use crate::outln; use crate::system_version; use crate::user_version::UserVersion; use crate::version::Version; -use crate::version_files::get_user_version_from_file; +use crate::version_files::get_user_version_for_directory; use colored::Colorize; use snafu::{ensure, OptionExt, ResultExt, Snafu}; use structopt::StructOpt; @@ -33,7 +33,7 @@ impl Command for Use { .version .or_else(|| { let current_dir = std::env::current_dir().unwrap(); - get_user_version_from_file(current_dir) + get_user_version_for_directory(current_dir) }) .context(CantInferVersion)?; diff --git a/src/main.rs b/src/main.rs index db74201..c87a6e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ mod shell; mod system_info; mod system_version; mod user_version; +mod user_version_reader; mod version; mod version_files; diff --git a/src/user_version_reader.rs b/src/user_version_reader.rs new file mode 100644 index 0000000..a40f194 --- /dev/null +++ b/src/user_version_reader.rs @@ -0,0 +1,112 @@ +use crate::user_version::UserVersion; +use crate::version_files::{get_user_version_for_directory, get_user_version_for_file}; +use std::path::PathBuf; +use std::str::FromStr; + +#[derive(Debug)] +pub enum UserVersionReader { + Direct(UserVersion), + Path(PathBuf), +} + +impl UserVersionReader { + pub fn to_user_version(self) -> Option { + match self { + Self::Direct(uv) => Some(uv), + Self::Path(pathbuf) if pathbuf.is_file() => get_user_version_for_file(&pathbuf), + Self::Path(pathbuf) => get_user_version_for_directory(&pathbuf), + } + } +} + +impl FromStr for UserVersionReader { + type Err = semver::SemVerError; + + fn from_str(s: &str) -> Result { + let pathbuf = PathBuf::from_str(&s); + let user_version = UserVersion::from_str(&s); + match (user_version, pathbuf) { + (_, Ok(pathbuf)) if pathbuf.exists() => Ok(Self::Path(pathbuf)), + (Ok(user_version), _) => Ok(Self::Direct(user_version)), + (Err(user_version_err), _) => Err(user_version_err), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::version::Version; + use pretty_assertions::assert_eq; + use std::io::Write; + use tempfile::{NamedTempFile, TempDir}; + + #[test] + fn test_file_pathbuf_to_version() { + let mut file = NamedTempFile::new().unwrap(); + write!(file, "14").unwrap(); + let pathbuf = file.path().to_path_buf(); + + let user_version = UserVersionReader::Path(pathbuf).to_user_version(); + assert_eq!(user_version, Some(UserVersion::OnlyMajor(14))); + } + + #[test] + fn test_directory_pathbuf_to_version() { + let directory = TempDir::new().unwrap(); + let node_version_path = directory.path().join(".node-version"); + std::fs::write(node_version_path, "14").unwrap(); + let pathbuf = directory.path().to_path_buf(); + + let user_version = UserVersionReader::Path(pathbuf).to_user_version(); + assert_eq!(user_version, Some(UserVersion::OnlyMajor(14))); + } + + #[test] + fn test_direct_to_version() { + let user_version = UserVersionReader::Direct(UserVersion::OnlyMajor(14)).to_user_version(); + assert_eq!(user_version, Some(UserVersion::OnlyMajor(14))); + } + + #[test] + fn test_from_str_directory() { + let directory = TempDir::new().unwrap(); + let node_version_path = directory.path().join(".node-version"); + std::fs::write(node_version_path, "14").unwrap(); + let pathbuf = directory.path().to_path_buf(); + + let user_version = UserVersionReader::from_str(pathbuf.to_str().unwrap()); + assert!(matches!(user_version, Ok(UserVersionReader::Path(_)))); + } + + #[test] + fn test_from_str_file() { + let mut file = NamedTempFile::new().unwrap(); + write!(file, "14").unwrap(); + let pathbuf = file.path().to_path_buf(); + + let user_version = UserVersionReader::from_str(pathbuf.to_str().unwrap()); + assert!(matches!(user_version, Ok(UserVersionReader::Path(_)))); + } + + #[test] + fn test_non_existing_path() { + let user_version = + UserVersionReader::from_str("/tmp/some_random_text_that_probably_does_not_exist"); + assert!(matches!( + user_version, + Ok(UserVersionReader::Direct(UserVersion::Full( + Version::Alias(_) + ))) + )); + } + + #[test] + fn test_a_version_number() { + let user_version = UserVersionReader::from_str("12.0"); + assert!(matches!( + user_version, + Ok(UserVersionReader::Direct(UserVersion::MajorMinor(12, 0))) + )); + } +} diff --git a/src/version_files.rs b/src/version_files.rs index e951e6f..c5bf308 100644 --- a/src/version_files.rs +++ b/src/version_files.rs @@ -7,7 +7,7 @@ use std::str::FromStr; const PATH_PARTS: [&str; 2] = [".nvmrc", ".node-version"]; -pub fn get_user_version_from_file(path: impl AsRef) -> Option { +pub fn get_user_version_for_directory(path: impl AsRef) -> Option { let path = path.as_ref(); for path_part in PATH_PARTS.iter() { @@ -17,24 +17,30 @@ pub fn get_user_version_from_file(path: impl AsRef) -> Option new_path.display(), new_path.exists() ); - if let Ok(file) = std::fs::File::open(new_path) { - let version = { - let mut reader = DecodeReaderBytes::new(file); - let mut version = String::new(); - reader.read_to_string(&mut version).map(|_| version) - }; - - match version { - Err(err) => info!("Can't read file: {}", err), - Ok(version) => { - info!("Found string {:?} in version file", version); - if let Ok(ver) = UserVersion::from_str(version.trim()) { - return Some(ver); - } - } - } + if let Some(version) = get_user_version_for_file(&new_path) { + return Some(version); } } None } + +pub fn get_user_version_for_file(path: impl AsRef) -> Option { + let file = std::fs::File::open(path).ok()?; + let version = { + let mut reader = DecodeReaderBytes::new(file); + let mut version = String::new(); + reader.read_to_string(&mut version).map(|_| version) + }; + + match version { + Err(err) => { + info!("Can't read file: {}", err); + None + } + Ok(version) => { + info!("Found string {:?} in version file", version); + UserVersion::from_str(version.trim()).ok() + } + } +}