diff --git a/Cargo.lock b/Cargo.lock index 64d1cd1..f400abc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,17 +63,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -199,26 +188,24 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "4.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", - "indexmap", + "is-terminal", "once_cell", "strsim", "termcolor", - "textwrap", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ "heck", "proc-macro-error", @@ -229,9 +216,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" dependencies = [ "os_str_bytes", ] @@ -386,6 +373,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "filetime" version = "0.2.15" @@ -582,6 +590,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hmac" version = "0.12.1" @@ -701,12 +718,34 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "ipnet" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +[[package]] +name = "is-terminal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "itertools" version = "0.10.5" @@ -754,9 +793,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "linux-raw-sys" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" [[package]] name = "log" @@ -916,7 +961,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -1254,6 +1299,20 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustix" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1456,12 +1515,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78fbf2dd23e79c28ccfa2472d3e6b3b189866ffef1aeb91f17c2d968b6586378" -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.30" @@ -1795,6 +1848,63 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "winreg" version = "0.10.1" diff --git a/Cargo.toml b/Cargo.toml index abd99dc..94448b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ exclude = [ "test-data/", ] +[profile.release] +strip = true +lto = true + [[bin]] name = "nvm" path = "src/main.rs" @@ -25,7 +29,7 @@ path = "src/main.rs" [dependencies] anyhow = "1.0.66" -clap = { version = "3.2.23", features = ["derive", "env", "cargo"] } +clap = { version = "4.0.29", features = ["derive", "env", "cargo"] } dialoguer = "0.10.2" dirs = "4.0.0" itertools = "0.10.5" diff --git a/src/archives.rs b/src/archives.rs index 8ceff84..1fae724 100644 --- a/src/archives.rs +++ b/src/archives.rs @@ -36,13 +36,12 @@ pub fn extract_archive(bytes: Response, path: &Path) -> Result<()> { if item.is_dir() && !new_path.exists() { create_dir_all(&new_path) - .unwrap_or_else(|_| panic!("Could not create new folder: {:?}", new_path)); + .unwrap_or_else(|_| panic!("Could not create new folder: {new_path:?}")); } if item.is_file() { let mut file = File::create(&*new_path)?; - copy(&mut item, &mut file) - .unwrap_or_else(|_| panic!("Couldn't write to {:?}", new_path)); + copy(&mut item, &mut file).unwrap_or_else(|_| panic!("Couldn't write to {new_path:?}")); } } diff --git a/src/files/mod.rs b/src/files/mod.rs index 33e3c79..c47e0ac 100644 --- a/src/files/mod.rs +++ b/src/files/mod.rs @@ -51,7 +51,7 @@ pub fn get_version_file() -> Option { let contents = fs::read_to_string(existing_file); if let Ok(contents) = contents { - let parse_result = Range::parse(&contents); + let parse_result = Range::parse(contents); if let Ok(parse_result) = parse_result { return Some(VersionFile::Nvmrc(parse_result)); @@ -75,7 +75,7 @@ pub fn get_version_file() -> Option { .and_then(|line| line.split(' ').nth(1)); if let Some(version_string) = version_string { - let parse_result = Range::parse(&version_string); + let parse_result = Range::parse(version_string); if let Ok(parse_result) = parse_result { return Some(VersionFile::Asdf(parse_result)); diff --git a/src/main.rs b/src/main.rs index 0e96c04..230e18b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use std::{ }; use anyhow::{bail, Result}; -use clap::{AppSettings, Parser, ValueHint}; +use clap::{Parser, ValueHint}; use crate::subcommand::{ install::InstallCommand, list::ListCommand, parse_version::ParseVersionCommand, @@ -28,10 +28,11 @@ enum Subcommands { } #[derive(Parser, Debug)] -#[clap( -name = "nvm(-rust)", -about = "Node Version Manager (but better, and in Rust)", -setting = AppSettings::ColoredHelp +#[command( + name = "nvm(-rust)", + author, + about, + about = "Node Version Manager (but better, and in Rust)" )] pub struct Config { /// Installation directory @@ -45,9 +46,6 @@ pub struct Config { env("NVM_SHIMS_DIR") )] shims_dir: Option, - /// Level of verbosity, can be used multiple times - #[clap(global(true), hidden(true), short, long, parse(from_occurrences))] - verbose: i32, /// Accept any prompts needed for the command to complete #[clap(global(true), short, long)] force: bool, @@ -77,7 +75,6 @@ impl Config { fn with_force(&self) -> Self { Self { force: true, - verbose: self.verbose, dir: Some(self.get_dir()), shims_dir: Some(self.get_shims_dir()), command: self.command.clone(), @@ -97,14 +94,13 @@ impl Config { fn ensure_dir_exists(path: &Path) { if !path.exists() { - fs::create_dir_all(path) - .unwrap_or_else(|err| panic!("Could not create {:?} - {}", path, err)); + fs::create_dir_all(path).unwrap_or_else(|err| panic!("Could not create {path:?} - {err}")); - println!("Created nvm dir at {:?}", path); + println!("Created nvm dir at {path:?}"); } if !path.is_dir() { - panic!("{:?} is not a directory! Please rename it.", path) + panic!("{path:?} is not a directory! Please rename it.") } } @@ -122,7 +118,7 @@ https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10"; fn ensure_symlinks_work(config: &Config) -> Result<()> { let target_path = &config.get_dir().join("test"); - if windows::fs::symlink_dir(&config.get_shims_dir(), target_path).is_err() { + if windows::fs::symlink_dir(config.get_shims_dir(), target_path).is_err() { bail!("{SYMLINK_ERROR}"); } @@ -155,3 +151,10 @@ fn main() -> Result<()> { _ => Ok(()), } } + +#[test] +fn verify_cli() { + use clap::CommandFactory; + + Config::command().debug_assert() +} diff --git a/src/node_version.rs b/src/node_version.rs index 1146928..260f334 100644 --- a/src/node_version.rs +++ b/src/node_version.rs @@ -37,7 +37,7 @@ impl Ord for dyn NodeVersion { } } -pub fn is_version_range(value: &str) -> Result { +pub fn parse_range(value: &str) -> Result { Range::parse(value).context(value.to_string()) } @@ -100,7 +100,7 @@ impl OnlineNodeVersion { let url = format!("https://nodejs.org/dist/v{}/{}", self.version, file_name); - Url::parse(&url).context(format!("Could not create a valid download url. [{}]", url)) + Url::parse(&url).context(format!("Could not create a valid download url. [{url}]")) } #[cfg(target_os = "windows")] @@ -196,7 +196,7 @@ impl InstalledNodeVersion { #[allow(dead_code)] pub fn validate(&self, config: &Config) -> Result<()> { let version_dir = - read_link(&config.get_shims_dir()).expect("Could not read installation dir"); + read_link(config.get_shims_dir()).expect("Could not read installation dir"); let mut required_files = vec![version_dir; 2]; required_files[0].set_file_name(format!("node{}", Self::exec_ext())); @@ -228,7 +228,7 @@ impl InstalledNodeVersion { .expect("Failed to read nvm dir") { if entry.is_err() { - println!("Could not read {:?}", entry); + println!("Could not read {entry:?}"); continue; } diff --git a/src/subcommand/install.rs b/src/subcommand/install.rs index 056a09f..0e68c3a 100644 --- a/src/subcommand/install.rs +++ b/src/subcommand/install.rs @@ -1,29 +1,26 @@ use std::{borrow::Borrow, path::Path}; use anyhow::{Context, Result}; -use clap::{AppSettings, Parser}; +use clap::Parser; use node_semver::Range; use crate::{ - archives, files, node_version, - node_version::{InstalledNodeVersion, NodeVersion, OnlineNodeVersion}, + archives, files, + node_version::{ + filter_version_req, parse_range, InstalledNodeVersion, NodeVersion, OnlineNodeVersion, + }, subcommand::{switch::SwitchCommand, Action}, Config, }; #[derive(Parser, Clone, Debug)] -#[clap( -about = "Install a new node version", -alias = "i", -alias = "add", -setting = AppSettings::ColoredHelp -)] +#[command(about = "Install a new node version", alias = "i", alias = "add")] pub struct InstallCommand { /// A semver range. The latest version matching this range will be installed - #[clap(validator = node_version::is_version_range)] + #[arg(value_parser = parse_range)] pub version: Option, /// Switch to the new version after installing it - #[clap(long, short, default_value("false"))] + #[arg(long, short, default_value("false"))] pub switch: bool, } @@ -40,7 +37,7 @@ impl Action for InstallCommand { let version_filter = version_filter.unwrap(); let online_versions = OnlineNodeVersion::fetch_all()?; - let filtered_versions = node_version::filter_version_req(online_versions, &version_filter); + let filtered_versions = filter_version_req(online_versions, &version_filter); let version_to_install = filtered_versions.first().context(format!( "Did not find a version matching `{}`!", @@ -86,7 +83,7 @@ impl Action for InstallCommand { fn download_and_extract_to(version: &OnlineNodeVersion, path: &Path) -> Result<()> { let url = version.get_download_url().unwrap(); - println!("Downloading from {}...", url); + println!("Downloading from {url}..."); let response = reqwest::blocking::get(url) .context(format!("Failed to download version: {}", version.version()))?; diff --git a/src/subcommand/list.rs b/src/subcommand/list.rs index e01976a..8181a29 100644 --- a/src/subcommand/list.rs +++ b/src/subcommand/list.rs @@ -1,11 +1,11 @@ use anyhow::Result; -use clap::{AppSettings, Parser}; +use clap::Parser; use itertools::Itertools; use node_semver::Range; use crate::{ node_version, - node_version::{InstalledNodeVersion, NodeVersion, OnlineNodeVersion}, + node_version::{parse_range, InstalledNodeVersion, NodeVersion, OnlineNodeVersion}, subcommand::Action, Config, }; @@ -47,19 +47,15 @@ impl<'p> VersionStatus<'p> { } #[derive(Parser, Clone, Debug)] -#[clap( -about = "List installed and released node versions", -alias = "ls", -setting = AppSettings::ColoredHelp -)] +#[command(about = "List installed and released node versions", alias = "ls")] pub struct ListCommand { /// Only display installed versions - #[clap(short, long, alias = "installed")] + #[arg(short, long, alias = "installed")] pub local: bool, /// Filter by semantic versions. /// /// `12`, `^10.9`, `>=8.10`, `>=8, <9` - #[clap(short('F'), long, validator = node_version::is_version_range)] + #[arg(short('F'), long, value_parser = parse_range)] pub filter: Option, } diff --git a/src/subcommand/parse_version.rs b/src/subcommand/parse_version.rs index 0eaa43a..231b456 100644 --- a/src/subcommand/parse_version.rs +++ b/src/subcommand/parse_version.rs @@ -1,19 +1,18 @@ use anyhow::Result; -use clap::{AppSettings, Parser}; +use clap::Parser; use node_semver::Range; -use crate::{files, node_version::is_version_range, subcommand::Action, Config}; +use crate::{files, node_version::parse_range, subcommand::Action, Config}; #[derive(Parser, Clone, Debug)] -#[clap( -about = "Echo what a version string will be parsed to", -alias = "pv", -setting = AppSettings::ColoredHelp, -setting = AppSettings::Hidden +#[command( + about = "Echo what a version string will be parsed to", + alias = "pv", + hide(true) )] pub struct ParseVersionCommand { /// The semver range to echo the parsed result of - #[clap(validator = is_version_range)] + #[arg(value_parser = parse_range)] pub version: Option, } diff --git a/src/subcommand/switch.rs b/src/subcommand/switch.rs index d9beb72..dd23984 100644 --- a/src/subcommand/switch.rs +++ b/src/subcommand/switch.rs @@ -9,26 +9,21 @@ use std::os::unix::fs::symlink; use std::os::windows::fs::symlink_dir; use anyhow::Result; -use clap::{AppSettings, Parser}; +use clap::Parser; use node_semver::{Range, Version}; use crate::{ - files, node_version, - node_version::{InstalledNodeVersion, NodeVersion}, + files, + node_version::{parse_range, InstalledNodeVersion, NodeVersion}, subcommand::Action, Config, }; #[derive(Parser, Clone, Debug)] -#[clap( -about = "Switch to an installed node version", -alias = "switch", -alias = "use", -setting = AppSettings::ColoredHelp -)] +#[command(about = "Switch to an installed node version", alias = "switch")] pub struct SwitchCommand { /// A semver range. The latest version matching this range will be switched to. - #[clap(validator = node_version::is_version_range)] + #[arg(value_parser = parse_range)] pub version: Option, } diff --git a/src/subcommand/uninstall.rs b/src/subcommand/uninstall.rs index d25784c..bc72067 100644 --- a/src/subcommand/uninstall.rs +++ b/src/subcommand/uninstall.rs @@ -1,24 +1,23 @@ use anyhow::Result; -use clap::{AppSettings, Parser}; +use clap::Parser; use node_semver::Range; use crate::{ - node_version, - node_version::{InstalledNodeVersion, NodeVersion}, + node_version::{parse_range, InstalledNodeVersion, NodeVersion}, subcommand::Action, Config, }; #[derive(Parser, Clone, Debug)] -#[clap( -about = "Uninstall a version", -alias = "r", -alias = "remove", -setting = AppSettings::ColoredHelp +#[command( + about = "Uninstall a version", + alias = "r", + alias = "rm", + alias = "remove" )] pub struct UninstallCommand { /// A semver range. The latest version matching this range will be installed - #[clap(validator = node_version::is_version_range)] + #[arg(value_parser = parse_range)] pub version: Range, } diff --git a/tests/utils.rs b/tests/utils.rs index 92a0265..90ffb80 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -30,7 +30,7 @@ pub fn setup_integration_test() -> Result<(TempDir, Command)> { let temp_dir = integration_dir(); let mut cmd = Command::cargo_bin("nvm").expect("Could not create Command"); - cmd.args(&["--dir", &temp_dir.to_string_lossy()]); + cmd.args(["--dir", &temp_dir.to_string_lossy()]); Ok((temp_dir, cmd)) } @@ -148,11 +148,11 @@ pub fn assert_version_installed( pub fn get_selected_version(temp_dir: &TempDir) -> Option { let symlink_path = temp_dir.child("shims"); - match fs::read_link(&symlink_path) { + match fs::read_link(symlink_path) { Ok(shims_dir) => { let file_path = shims_dir.join(required_files()[0]); - Some(fs::read_to_string(&file_path).unwrap()) + Some(fs::read_to_string(file_path).unwrap()) }, Err(_) => None, }