Browse Source

Fix clippy & use musl target on Rust compiler for static compilation (#554)

remotes/origin/add-with-shims
Dominik Nakamura 3 years ago committed by GitHub
parent
commit
74c9b37895
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      .github/workflows/rust.yml
  2. 2
      src/alias.rs
  3. 4
      src/arch.rs
  4. 2
      src/archive/zip.rs
  5. 2
      src/choose_version_for_user_input.rs
  6. 2
      src/commands/alias.rs
  7. 2
      src/commands/current.rs
  8. 4
      src/commands/default.rs
  9. 4
      src/commands/env.rs
  10. 4
      src/commands/exec.rs
  11. 8
      src/commands/install.rs
  12. 10
      src/commands/ls_local.rs
  13. 4
      src/commands/ls_remote.rs
  14. 8
      src/commands/uninstall.rs
  15. 24
      src/commands/use.rs
  16. 12
      src/config.rs
  17. 4
      src/directory_portal.rs
  18. 3
      src/installed_versions.rs
  19. 17
      src/log_level.rs
  20. 8
      src/main.rs
  21. 26
      src/remote_node_index.rs
  22. 6
      src/shell/bash.rs
  23. 6
      src/shell/fish.rs
  24. 2
      src/shell/infer/unix.rs
  25. 5
      src/shell/powershell.rs
  26. 12
      src/shell/shell.rs
  27. 6
      src/shell/windows_cmd/mod.rs
  28. 6
      src/shell/zsh.rs
  29. 36
      src/user_version.rs
  30. 13
      src/user_version_reader.rs
  31. 13
      src/version.rs
  32. 2
      src/version_files.rs
  33. 7
      tests/shellcode/call.rs
  34. 2
      tests/shellcode/line_separated_expressions.rs
  35. 2
      tests/shellcode/mod.rs

32
.github/workflows/rust.yml

@ -17,6 +17,16 @@ jobs:
- name: cargo fmt - name: cargo fmt
run: cargo fmt -- --check run: cargo fmt -- --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
- uses: actions/checkout@v2
- name: cargo clippy
run: cargo clippy -- -D warnings
unit_tests: unit_tests:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
@ -67,7 +77,7 @@ jobs:
path: target/release/fnm.exe path: target/release/fnm.exe
build_macos_release: build_macos_release:
runs-on: macOS-latest runs-on: macos-latest
name: "Release build for macOS" name: "Release build for macOS"
steps: steps:
- uses: hecrj/setup-rust-action@v1 - uses: hecrj/setup-rust-action@v1
@ -91,17 +101,23 @@ jobs:
name: "Build static Linux binary" name: "Build static Linux binary"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1
- name: Build static binary with:
rust-version: stable
targets: x86_64-unknown-linux-musl
- name: Install musl tools
run: | run: |
sudo chown -R 1000:1000 . sudo apt-get update
docker run --rm -v "$(pwd)":/home/rust/src ekidd/rust-musl-builder:stable \ sudo apt-get install -y --no-install-recommends musl-tools
cargo build --target x86_64-unknown-linux-gnu --release - uses: actions/checkout@v2
sudo chown -R $(whoami):$(whoami) . - name: Build release binary
run: cargo build --release --target x86_64-unknown-linux-musl
- name: Strip binary from debug symbols
run: strip target/x86_64-unknown-linux-musl/release/fnm
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: fnm-linux name: fnm-linux
path: target/x86_64-unknown-linux-gnu/release/fnm path: target/x86_64-unknown-linux-musl/release/fnm
build_static_arm_binary: build_static_arm_binary:
name: "Build ARM binary" name: "Build ARM binary"

2
src/alias.rs

@ -28,7 +28,7 @@ pub fn create_alias(
pub fn list_aliases(config: &FnmConfig) -> std::io::Result<Vec<StoredAlias>> { pub fn list_aliases(config: &FnmConfig) -> std::io::Result<Vec<StoredAlias>> {
let vec: Vec<_> = std::fs::read_dir(&config.aliases_dir())? let vec: Vec<_> = std::fs::read_dir(&config.aliases_dir())?
.filter_map(|item| item.ok()) .filter_map(Result::ok)
.filter_map(|x| TryInto::<StoredAlias>::try_into(x.path().as_path()).ok()) .filter_map(|x| TryInto::<StoredAlias>::try_into(x.path().as_path()).ok())
.collect(); .collect();
Ok(vec) Ok(vec)

4
src/arch.rs

@ -18,7 +18,7 @@ pub fn get_safe_arch<'a>(arch: &'a Arch, version: &Version) -> &'a Arch {
return match (platform_name(), platform_arch(), version) { return match (platform_name(), platform_arch(), version) {
("darwin", "arm64", Version::Semver(v)) if v.major < 16 => &Arch::X64, ("darwin", "arm64", Version::Semver(v)) if v.major < 16 => &Arch::X64,
_ => &arch, _ => arch,
}; };
} }
@ -83,7 +83,7 @@ impl ArchError {
} }
impl std::fmt::Display for ArchError { impl std::fmt::Display for ArchError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.details) write!(f, "{}", self.details)
} }
} }

2
src/archive/zip.rs

@ -33,7 +33,7 @@ impl<R: Read> Extract for Zip<R> {
for i in 0..archive.len() { for i in 0..archive.len() {
let mut file = archive.by_index(i)?; let mut file = archive.by_index(i)?;
let outpath = path.join(file.sanitized_name()); let outpath = path.join(file.mangled_name());
{ {
let comment = file.comment(); let comment = file.comment();

2
src/choose_version_for_user_input.rs

@ -50,7 +50,7 @@ pub fn choose_version_for_user_input<'a>(
version: Version::Alias(alias_name), version: Version::Alias(alias_name),
}) })
} else { } else {
let current_version = requested_version.to_version(&all_versions, &config); let current_version = requested_version.to_version(&all_versions, config);
current_version.map(|version| { current_version.map(|version| {
info!("Using Node {}", version.to_string().cyan()); info!("Using Node {}", version.to_string().cyan());
let path = config let path = config

2
src/commands/alias.rs

@ -25,7 +25,7 @@ impl Command for Alias {
version: self.to_version, version: self.to_version,
})?; })?;
create_alias(&config, &self.name, applicable_version.version()) create_alias(config, &self.name, applicable_version.version())
.context(CantCreateSymlink)?; .context(CantCreateSymlink)?;
Ok(()) Ok(())

2
src/commands/current.rs

@ -10,7 +10,7 @@ impl Command for Current {
type Error = Error; type Error = Error;
fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> {
let version_string = match current_version(&config)? { let version_string = match current_version(config)? {
Some(ver) => ver.v_str(), Some(ver) => ver.v_str(),
None => "none".into(), None => "none".into(),
}; };

4
src/commands/default.rs

@ -11,6 +11,7 @@ pub struct Default {
impl Command for Default { impl Command for Default {
type Error = super::alias::Error; type Error = super::alias::Error;
fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> {
Alias { Alias {
name: "default".into(), name: "default".into(),
@ -18,7 +19,8 @@ impl Command for Default {
} }
.apply(config) .apply(config)
} }
fn handle_error(err: Self::Error, config: &FnmConfig) { fn handle_error(err: Self::Error, config: &FnmConfig) {
Alias::handle_error(err, config) Alias::handle_error(err, config);
} }
} }

4
src/commands/env.rs

@ -54,7 +54,7 @@ impl Command for Env {
} }
let shell: Box<dyn Shell> = self.shell.or_else(&infer_shell).context(CantInferShell)?; let shell: Box<dyn Shell> = self.shell.or_else(&infer_shell).context(CantInferShell)?;
let multishell_path = make_symlink(&config); let multishell_path = make_symlink(config);
let binary_path = if cfg!(windows) { let binary_path = if cfg!(windows) {
multishell_path.clone() multishell_path.clone()
} else { } else {
@ -82,7 +82,7 @@ impl Command for Env {
shell.set_env_var("FNM_ARCH", &config.arch.to_string()) shell.set_env_var("FNM_ARCH", &config.arch.to_string())
); );
if self.use_on_cd { if self.use_on_cd {
println!("{}", shell.use_on_cd(&config)); println!("{}", shell.use_on_cd(config));
} }
Ok(()) Ok(())
} }

4
src/commands/exec.rs

@ -40,10 +40,10 @@ impl Cmd for Exec {
let current_dir = std::env::current_dir().unwrap(); let current_dir = std::env::current_dir().unwrap();
UserVersionReader::Path(current_dir) UserVersionReader::Path(current_dir)
}) })
.to_user_version() .into_user_version()
.context(CantInferVersion)?; .context(CantInferVersion)?;
let applicable_version = choose_version_for_user_input(&version, &config) let applicable_version = choose_version_for_user_input(&version, config)
.context(ApplicableVersionError)? .context(ApplicableVersionError)?
.context(VersionNotFound { version })?; .context(VersionNotFound { version })?;

8
src/commands/install.rs

@ -55,7 +55,7 @@ impl super::command::Command for Install {
let version = match current_version.clone() { let version = match current_version.clone() {
UserVersion::Full(Version::Semver(actual_version)) => Version::Semver(actual_version), UserVersion::Full(Version::Semver(actual_version)) => Version::Semver(actual_version),
UserVersion::Full(v @ Version::Bypassed) | UserVersion::Full(v @ Version::Alias(_)) => { UserVersion::Full(v @ (Version::Bypassed | Version::Alias(_))) => {
ensure!(false, UninstallableVersion { version: v }); ensure!(false, UninstallableVersion { version: v });
unreachable!(); unreachable!();
} }
@ -84,7 +84,7 @@ impl super::command::Command for Install {
.collect(); .collect();
current_version current_version
.to_version(&available_versions, &config) .to_version(&available_versions, config)
.context(CantFindNodeVersion { .context(CantFindNodeVersion {
requested_version: current_version, requested_version: current_version,
})? })?
@ -117,12 +117,12 @@ impl super::command::Command for Install {
alias_name.cyan(), alias_name.cyan(),
version.v_str().cyan() version.v_str().cyan()
); );
create_alias(&config, &alias_name, &version).context(IoError)?; create_alias(config, &alias_name, &version).context(IoError)?;
} }
if !config.default_version_dir().exists() { if !config.default_version_dir().exists() {
debug!("Tagging {} as the default version", version.v_str().cyan()); debug!("Tagging {} as the default version", version.v_str().cyan());
create_alias(&config, "default", &version).context(IoError)?; create_alias(config, "default", &version).context(IoError)?;
} }
Ok(()) Ok(())

10
src/commands/ls_local.rs

@ -2,7 +2,7 @@ use crate::alias::{list_aliases, StoredAlias};
use crate::config::FnmConfig; use crate::config::FnmConfig;
use crate::current_version::current_version; use crate::current_version::current_version;
use crate::version::Version; use crate::version::Version;
use colored::*; use colored::Colorize;
use snafu::{ResultExt, Snafu}; use snafu::{ResultExt, Snafu};
use std::collections::HashMap; use std::collections::HashMap;
use structopt::StructOpt; use structopt::StructOpt;
@ -19,8 +19,8 @@ impl super::command::Command for LsLocal {
crate::installed_versions::list(base_dir).context(CantListLocallyInstalledVersion)?; crate::installed_versions::list(base_dir).context(CantListLocallyInstalledVersion)?;
versions.insert(0, Version::Bypassed); versions.insert(0, Version::Bypassed);
versions.sort(); versions.sort();
let aliases_hash = generate_aliases_hash(&config).context(CantReadAliases)?; let aliases_hash = generate_aliases_hash(config).context(CantReadAliases)?;
let curr_version = current_version(&config).ok().flatten(); let curr_version = current_version(config).ok().flatten();
for version in versions { for version in versions {
let version_aliases = match aliases_hash.get(&version.v_str()) { let version_aliases = match aliases_hash.get(&version.v_str()) {
@ -28,7 +28,7 @@ impl super::command::Command for LsLocal {
Some(versions) => { Some(versions) => {
let version_string = versions let version_string = versions
.iter() .iter()
.map(|x| x.name()) .map(StoredAlias::name)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
format!(" {}", version_string.dimmed()) format!(" {}", version_string.dimmed())
@ -48,7 +48,7 @@ impl super::command::Command for LsLocal {
} }
fn generate_aliases_hash(config: &FnmConfig) -> std::io::Result<HashMap<String, Vec<StoredAlias>>> { fn generate_aliases_hash(config: &FnmConfig) -> std::io::Result<HashMap<String, Vec<StoredAlias>>> {
let mut aliases = list_aliases(&config)?; let mut aliases = list_aliases(config)?;
let mut hashmap: HashMap<String, Vec<StoredAlias>> = HashMap::with_capacity(aliases.len()); let mut hashmap: HashMap<String, Vec<StoredAlias>> = HashMap::with_capacity(aliases.len());
for alias in aliases.drain(..) { for alias in aliases.drain(..) {
if let Some(value) = hashmap.get_mut(alias.s_ver()) { if let Some(value) = hashmap.get_mut(alias.s_ver()) {

4
src/commands/ls_remote.rs

@ -10,9 +10,7 @@ impl super::command::Command for LsRemote {
type Error = Error; type Error = Error;
fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> {
let mut all_versions = let all_versions = remote_node_index::list(&config.node_dist_mirror).context(HttpError)?;
remote_node_index::list(&config.node_dist_mirror).context(HttpError)?;
all_versions.sort();
for version in all_versions { for version in all_versions {
print!("{}", version.version); print!("{}", version.version);

8
src/commands/uninstall.rs

@ -37,7 +37,7 @@ impl Command for Uninstall {
let available_versions: Vec<&Version> = all_versions let available_versions: Vec<&Version> = all_versions
.iter() .iter()
.filter(|v| requested_version.matches(v, &config)) .filter(|v| requested_version.matches(v, config))
.collect(); .collect();
ensure!( ensure!(
@ -51,12 +51,12 @@ impl Command for Uninstall {
); );
let version = requested_version let version = requested_version
.to_version(&all_versions, &config) .to_version(&all_versions, config)
.context(CantFindVersion)?; .context(CantFindVersion)?;
let matching_aliases = version.find_aliases(&config).context(IoError)?; let matching_aliases = version.find_aliases(config).context(IoError)?;
let root_path = version let root_path = version
.root_path(&config) .root_path(config)
.with_context(|| RootPathNotFound { .with_context(|| RootPathNotFound {
version: version.clone(), version: version.clone(),
})?; })?;

24
src/commands/use.rs

@ -24,7 +24,7 @@ impl Command for Use {
fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> {
let multishell_path = config.multishell_path().context(FnmEnvWasNotSourced)?; let multishell_path = config.multishell_path().context(FnmEnvWasNotSourced)?;
warn_if_multishell_path_not_in_path_env_var(&multishell_path, &config); warn_if_multishell_path_not_in_path_env_var(multishell_path, config);
let all_versions = let all_versions =
installed_versions::list(config.installations_dir()).context(VersionListingError)?; installed_versions::list(config.installations_dir()).context(VersionListingError)?;
@ -34,7 +34,7 @@ impl Command for Use {
let current_dir = std::env::current_dir().unwrap(); let current_dir = std::env::current_dir().unwrap();
UserVersionReader::Path(current_dir) UserVersionReader::Path(current_dir)
}) })
.to_user_version() .into_user_version()
.context(CantInferVersion)?; .context(CantInferVersion)?;
let version_path = if let UserVersion::Full(Version::Bypassed) = requested_version { let version_path = if let UserVersion::Full(Version::Bypassed) = requested_version {
@ -50,23 +50,20 @@ impl Command for Use {
return Ok(()); return Ok(());
} }
} else { } else {
let current_version = requested_version.to_version(&all_versions, &config); let current_version = requested_version.to_version(&all_versions, config);
match current_version { if let Some(version) = current_version {
Some(version) => {
outln!(config#Info, "Using Node {}", version.to_string().cyan()); outln!(config#Info, "Using Node {}", version.to_string().cyan());
config config
.installations_dir() .installations_dir()
.join(version.to_string()) .join(version.to_string())
.join("installation") .join("installation")
} } else {
None => {
install_new_version(requested_version, config, self.install_if_missing)?; install_new_version(requested_version, config, self.install_if_missing)?;
return Ok(()); return Ok(());
} }
}
}; };
replace_symlink(&version_path, &multishell_path).context(SymlinkingCreationIssue)?; replace_symlink(&version_path, multishell_path).context(SymlinkingCreationIssue)?;
Ok(()) Ok(())
} }
@ -86,7 +83,7 @@ fn install_new_version(
Install { Install {
version: Some(requested_version.clone()), version: Some(requested_version.clone()),
..Default::default() ..Install::default()
} }
.apply(config) .apply(config)
.context(InstallError)?; .context(InstallError)?;
@ -97,7 +94,7 @@ fn install_new_version(
} }
.apply(config)?; .apply(config)?;
return Ok(()); Ok(())
} }
/// Tries to delete `from`, and then tries to symlink `from` to `to` anyway. /// Tries to delete `from`, and then tries to symlink `from` to `to` anyway.
@ -115,11 +112,12 @@ fn replace_symlink(from: &std::path::Path, to: &std::path::Path) -> std::io::Res
} }
fn should_install_interactively(requested_version: &UserVersion) -> bool { fn should_install_interactively(requested_version: &UserVersion) -> bool {
use std::io::Write;
if !(atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stdin)) { if !(atty::is(atty::Stream::Stdout) && atty::is(atty::Stream::Stdin)) {
return false; return false;
} }
use std::io::Write;
let error_message = format!( let error_message = format!(
"Can't find an installed Node version matching {}.", "Can't find an installed Node version matching {}.",
requested_version.to_string().italic() requested_version.to_string().italic()
@ -146,7 +144,7 @@ fn warn_if_multishell_path_not_in_path_env_var(
multishell_path.to_path_buf() multishell_path.to_path_buf()
}; };
for path in std::env::split_paths(&std::env::var("PATH").unwrap_or(String::new())) { for path in std::env::split_paths(&std::env::var("PATH").unwrap_or_default()) {
if bin_path == path { if bin_path == path {
return; return;
} }

12
src/config.rs

@ -1,4 +1,4 @@
use crate::arch; use crate::arch::Arch;
use crate::log_level::LogLevel; use crate::log_level::LogLevel;
use crate::path_ext::PathExt; use crate::path_ext::PathExt;
use dirs::home_dir; use dirs::home_dir;
@ -57,7 +57,7 @@ pub struct FnmConfig {
global = true, global = true,
hide_env_values = true hide_env_values = true
)] )]
pub arch: arch::Arch, pub arch: Arch,
} }
impl Default for FnmConfig { impl Default for FnmConfig {
@ -67,7 +67,7 @@ impl Default for FnmConfig {
base_dir: None, base_dir: None,
multishell_path: None, multishell_path: None,
log_level: LogLevel::Info, log_level: LogLevel::Info,
arch: Default::default(), arch: Arch::default(),
} }
} }
} }
@ -112,10 +112,4 @@ impl FnmConfig {
self.base_dir = base_dir; self.base_dir = base_dir;
self self
} }
pub fn multishell_base_dir(&self) -> std::path::PathBuf {
std::env::temp_dir()
.join("fnm_multishell")
.ensure_exists_silently()
}
} }

4
src/directory_portal.rs

@ -1,4 +1,4 @@
use log::*; use log::debug;
use std::path::Path; use std::path::Path;
use tempfile::TempDir; use tempfile::TempDir;
@ -19,7 +19,7 @@ impl<P: AsRef<Path>> DirectoryPortal<P> {
pub fn new_in(parent_dir: impl AsRef<Path>, target: P) -> Self { pub fn new_in(parent_dir: impl AsRef<Path>, target: P) -> Self {
let temp_dir = TempDir::new_in(parent_dir).expect("Can't generate a temp directory"); let temp_dir = TempDir::new_in(parent_dir).expect("Can't generate a temp directory");
debug!("Created a temp directory in {:?}", temp_dir.path()); debug!("Created a temp directory in {:?}", temp_dir.path());
Self { target, temp_dir } Self { temp_dir, target }
} }
pub fn teleport(self) -> std::io::Result<P> { pub fn teleport(self) -> std::io::Result<P> {

3
src/installed_versions.rs

@ -9,8 +9,7 @@ pub fn list<P: AsRef<Path>>(installations_dir: P) -> Result<Vec<Version>, Error>
if entry if entry
.file_name() .file_name()
.to_str() .to_str()
.map(|s| s.starts_with(".")) .map_or(false, |s| s.starts_with('.'))
.unwrap_or(false)
{ {
continue; continue;
} }

17
src/log_level.rs

@ -8,10 +8,7 @@ pub enum LogLevel {
impl LogLevel { impl LogLevel {
pub fn is_writable(&self, logging: &Self) -> bool { pub fn is_writable(&self, logging: &Self) -> bool {
use std::cmp::Ordering; use std::cmp::Ordering;
match self.cmp(logging) { matches!(self.cmp(logging), Ordering::Greater | Ordering::Equal)
Ordering::Greater | Ordering::Equal => true,
_ => false,
}
} }
pub fn writer_for(&self, logging: &Self) -> Box<dyn std::io::Write> { pub fn writer_for(&self, logging: &Self) -> Box<dyn std::io::Write> {
@ -30,12 +27,12 @@ impl LogLevel {
} }
} }
impl Into<&'static str> for LogLevel { impl From<LogLevel> for &'static str {
fn into(self) -> &'static str { fn from(level: LogLevel) -> Self {
match self { match level {
Self::Quiet => "quiet", LogLevel::Quiet => "quiet",
Self::Info => "info", LogLevel::Info => "info",
Self::Error => "error", LogLevel::Error => "error",
} }
} }
} }

8
src/main.rs

@ -1,3 +1,11 @@
#![warn(rust_2018_idioms, clippy::all, clippy::pedantic)]
#![allow(
clippy::enum_variant_names,
clippy::large_enum_variant,
clippy::module_name_repetitions,
clippy::similar_names
)]
mod alias; mod alias;
mod arch; mod arch;
mod archive; mod archive;

26
src/remote_node_index.rs

@ -15,11 +15,11 @@ mod lts_status {
Yes(String), Yes(String),
} }
impl Into<Option<String>> for LtsStatus { impl From<LtsStatus> for Option<String> {
fn into(self) -> Option<String> { fn from(status: LtsStatus) -> Self {
match self { match status {
Self::Nope(_) => None, LtsStatus::Nope(_) => None,
Self::Yes(x) => Some(x), LtsStatus::Yes(x) => Some(x),
} }
} }
} }
@ -60,7 +60,7 @@ mod lts_status {
} }
} }
#[derive(Deserialize, Debug, Eq, Ord)] #[derive(Deserialize, Debug)]
pub struct IndexedNodeVersion { pub struct IndexedNodeVersion {
pub version: Version, pub version: Version,
#[serde(with = "lts_status")] #[serde(with = "lts_status")]
@ -69,18 +69,6 @@ pub struct IndexedNodeVersion {
pub files: Vec<String>, pub files: Vec<String>,
} }
impl PartialEq for IndexedNodeVersion {
fn eq(&self, other: &Self) -> bool {
self.version.eq(&other.version)
}
}
impl PartialOrd for IndexedNodeVersion {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.version.partial_cmp(&other.version)
}
}
/// Prints /// Prints
/// ///
/// ```rust /// ```rust
@ -111,7 +99,7 @@ pub fn list(base_url: &Url) -> Result<Vec<IndexedNodeVersion>, ureq::Error> {
) )
})?; })?;
value.sort(); value.sort_by(|a, b| a.version.cmp(&b.version));
Ok(value) Ok(value)
} }

6
src/shell/bash.rs

@ -1,16 +1,16 @@
use super::shell::Shell; use super::shell::Shell;
use indoc::indoc; use indoc::indoc;
use std::path::PathBuf; use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
pub struct Bash; pub struct Bash;
impl Shell for Bash { impl Shell for Bash {
fn into_structopt_shell(&self) -> structopt::clap::Shell { fn to_structopt_shell(&self) -> structopt::clap::Shell {
structopt::clap::Shell::Bash structopt::clap::Shell::Bash
} }
fn path(&self, path: &PathBuf) -> String { fn path(&self, path: &Path) -> String {
format!("export PATH={:?}:$PATH", path.to_str().unwrap()) format!("export PATH={:?}:$PATH", path.to_str().unwrap())
} }

6
src/shell/fish.rs

@ -1,16 +1,16 @@
use super::shell::Shell; use super::shell::Shell;
use indoc::indoc; use indoc::indoc;
use std::path::PathBuf; use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
pub struct Fish; pub struct Fish;
impl Shell for Fish { impl Shell for Fish {
fn into_structopt_shell(&self) -> structopt::clap::Shell { fn to_structopt_shell(&self) -> structopt::clap::Shell {
structopt::clap::Shell::Fish structopt::clap::Shell::Fish
} }
fn path(&self, path: &PathBuf) -> String { fn path(&self, path: &Path) -> String {
format!("set -gx PATH {:?} $PATH;", path.to_str().unwrap()) format!("set -gx PATH {:?} $PATH;", path.to_str().unwrap())
} }

2
src/shell/infer/unix.rs

@ -73,7 +73,7 @@ fn get_process_info(pid: u32) -> std::io::Result<ProcessInfo> {
.expect("Can't read the command from ps, should be the second item in the table"); .expect("Can't read the command from ps, should be the second item in the table");
Ok(ProcessInfo { Ok(ProcessInfo {
parent_pid: u32::from_str_radix(ppid, 10).ok(), parent_pid: ppid.parse().ok(),
command: command.into(), command: command.into(),
}) })
} }

5
src/shell/powershell.rs

@ -1,11 +1,12 @@
use super::Shell; use super::Shell;
use indoc::indoc; use indoc::indoc;
use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
pub struct PowerShell; pub struct PowerShell;
impl Shell for PowerShell { impl Shell for PowerShell {
fn path(&self, path: &std::path::PathBuf) -> String { fn path(&self, path: &Path) -> String {
let current_path = std::env::var_os("PATH").expect("Can't read PATH env var"); let current_path = std::env::var_os("PATH").expect("Can't read PATH env var");
let mut split_paths: Vec<_> = std::env::split_paths(&current_path).collect(); let mut split_paths: Vec<_> = std::env::split_paths(&current_path).collect();
split_paths.insert(0, path.to_path_buf()); split_paths.insert(0, path.to_path_buf());
@ -27,7 +28,7 @@ impl Shell for PowerShell {
Set-FnmOnLoad Set-FnmOnLoad
"#).into() "#).into()
} }
fn into_structopt_shell(&self) -> clap::Shell { fn to_structopt_shell(&self) -> clap::Shell {
clap::Shell::PowerShell clap::Shell::PowerShell
} }
} }

12
src/shell/shell.rs

@ -1,11 +1,11 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::path::PathBuf; use std::path::Path;
pub trait Shell: Debug { pub trait Shell: Debug {
fn path(&self, path: &PathBuf) -> String; fn path(&self, path: &Path) -> String;
fn set_env_var(&self, name: &str, value: &str) -> String; fn set_env_var(&self, name: &str, value: &str) -> String;
fn use_on_cd(&self, config: &crate::config::FnmConfig) -> String; fn use_on_cd(&self, config: &crate::config::FnmConfig) -> String;
fn into_structopt_shell(&self) -> structopt::clap::Shell; fn to_structopt_shell(&self) -> structopt::clap::Shell;
} }
#[cfg(windows)] #[cfg(windows)]
@ -29,8 +29,8 @@ impl std::str::FromStr for Box<dyn Shell> {
} }
} }
impl Into<structopt::clap::Shell> for Box<dyn Shell> { impl From<Box<dyn Shell>> for structopt::clap::Shell {
fn into(self) -> structopt::clap::Shell { fn from(shell: Box<dyn Shell>) -> Self {
self.into_structopt_shell() shell.to_structopt_shell()
} }
} }

6
src/shell/windows_cmd/mod.rs

@ -1,15 +1,15 @@
use super::shell::Shell; use super::shell::Shell;
use std::path::PathBuf; use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
pub struct WindowsCmd; pub struct WindowsCmd;
impl Shell for WindowsCmd { impl Shell for WindowsCmd {
fn into_structopt_shell(&self) -> structopt::clap::Shell { fn to_structopt_shell(&self) -> structopt::clap::Shell {
panic!("Shell completion is not supported for Windows Command Prompt. Maybe try using PowerShell for a better experience?"); panic!("Shell completion is not supported for Windows Command Prompt. Maybe try using PowerShell for a better experience?");
} }
fn path(&self, path: &PathBuf) -> String { fn path(&self, path: &Path) -> String {
let current_path = std::env::var_os("path").expect("Can't read PATH env var"); let current_path = std::env::var_os("path").expect("Can't read PATH env var");
let mut split_paths: Vec<_> = std::env::split_paths(&current_path).collect(); let mut split_paths: Vec<_> = std::env::split_paths(&current_path).collect();
split_paths.insert(0, path.to_path_buf()); split_paths.insert(0, path.to_path_buf());

6
src/shell/zsh.rs

@ -1,16 +1,16 @@
use super::shell::Shell; use super::shell::Shell;
use indoc::indoc; use indoc::indoc;
use std::path::PathBuf; use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
pub struct Zsh; pub struct Zsh;
impl Shell for Zsh { impl Shell for Zsh {
fn into_structopt_shell(&self) -> structopt::clap::Shell { fn to_structopt_shell(&self) -> structopt::clap::Shell {
structopt::clap::Shell::Zsh structopt::clap::Shell::Zsh
} }
fn path(&self, path: &PathBuf) -> String { fn path(&self, path: &Path) -> String {
format!("export PATH={:?}:$PATH", path.to_str().unwrap()) format!("export PATH={:?}:$PATH", path.to_str().unwrap())
} }

36
src/user_version.rs

@ -34,17 +34,14 @@ impl UserVersion {
match (self, version) { match (self, version) {
(Self::Full(a), b) if a == b => true, (Self::Full(a), b) if a == b => true,
(Self::Full(user_version), maybe_alias) => { (Self::Full(user_version), maybe_alias) => {
match (user_version.alias_name(), maybe_alias.find_aliases(&config)) { match (user_version.alias_name(), maybe_alias.find_aliases(config)) {
(None, _) | (_, Err(_)) => false, (None, _) | (_, Err(_)) => false,
(Some(user_alias), Ok(aliases)) => aliases (Some(user_alias), Ok(aliases)) => {
.iter() aliases.iter().any(|alias| alias.name() == user_alias)
.find(|alias| alias.name() == user_alias)
.is_some(),
} }
} }
(_, Version::Bypassed) => false, }
(_, Version::Lts(_)) => false, (_, Version::Bypassed | Version::Lts(_) | Version::Alias(_)) => false,
(_, Version::Alias(_)) => false,
(Self::OnlyMajor(major), Version::Semver(other)) => *major == other.major, (Self::OnlyMajor(major), Version::Semver(other)) => *major == other.major,
(Self::MajorMinor(major, minor), Version::Semver(other)) => { (Self::MajorMinor(major, minor), Version::Semver(other)) => {
*major == other.major && *minor == other.minor *major == other.major && *minor == other.minor
@ -69,11 +66,7 @@ impl std::fmt::Display for UserVersion {
} }
fn skip_first_v(str: &str) -> &str { fn skip_first_v(str: &str) -> &str {
if str.starts_with('v') { str.strip_prefix('v').unwrap_or(str)
&str[1..]
} else {
str
}
} }
impl FromStr for UserVersion { impl FromStr for UserVersion {
@ -96,12 +89,10 @@ impl FromStr for UserVersion {
#[cfg(test)] #[cfg(test)]
impl PartialEq for UserVersion { impl PartialEq for UserVersion {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use UserVersion::*;
match (self, other) { match (self, other) {
(OnlyMajor(a), OnlyMajor(b)) if a == b => true, (Self::OnlyMajor(a), Self::OnlyMajor(b)) if a == b => true,
(MajorMinor(a1, a2), MajorMinor(b1, b2)) if (a1, a2) == (b1, b2) => true, (Self::MajorMinor(a1, a2), Self::MajorMinor(b1, b2)) if (a1, a2) == (b1, b2) => true,
(Full(v1), Full(v2)) if v1 == v2 => true, (Self::Full(v1), Self::Full(v2)) if v1 == v2 => true,
(_, _) => false, (_, _) => false,
} }
} }
@ -109,6 +100,8 @@ impl PartialEq for UserVersion {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::config::FnmConfig;
use super::*; use super::*;
#[test] #[test]
@ -138,7 +131,7 @@ mod tests {
expected.clone(), expected.clone(),
Version::parse("7.0.1").unwrap(), Version::parse("7.0.1").unwrap(),
]; ];
let result = UserVersion::OnlyMajor(6).to_version(&versions, &Default::default()); let result = UserVersion::OnlyMajor(6).to_version(&versions, &FnmConfig::default());
assert_eq!(result, Some(&expected)); assert_eq!(result, Some(&expected));
} }
@ -152,7 +145,7 @@ mod tests {
expected.clone(), expected.clone(),
Version::parse("7.0.1").unwrap(), Version::parse("7.0.1").unwrap(),
]; ];
let result = UserVersion::MajorMinor(6, 0).to_version(&versions, &Default::default()); let result = UserVersion::MajorMinor(6, 0).to_version(&versions, &FnmConfig::default());
assert_eq!(result, Some(&expected)); assert_eq!(result, Some(&expected));
} }
@ -166,7 +159,8 @@ mod tests {
Version::parse("6.0.1").unwrap(), Version::parse("6.0.1").unwrap(),
Version::parse("7.0.1").unwrap(), Version::parse("7.0.1").unwrap(),
]; ];
let result = UserVersion::Full(expected.clone()).to_version(&versions, &Default::default()); let result =
UserVersion::Full(expected.clone()).to_version(&versions, &FnmConfig::default());
assert_eq!(result, Some(&expected)); assert_eq!(result, Some(&expected));
} }

13
src/user_version_reader.rs

@ -10,7 +10,7 @@ pub enum UserVersionReader {
} }
impl UserVersionReader { impl UserVersionReader {
pub fn to_user_version(self) -> Option<UserVersion> { pub fn into_user_version(self) -> Option<UserVersion> {
match self { match self {
Self::Direct(uv) => Some(uv), Self::Direct(uv) => Some(uv),
Self::Path(pathbuf) if pathbuf.is_file() => get_user_version_for_file(&pathbuf), Self::Path(pathbuf) if pathbuf.is_file() => get_user_version_for_file(&pathbuf),
@ -23,8 +23,8 @@ impl FromStr for UserVersionReader {
type Err = semver::Error; type Err = semver::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let pathbuf = PathBuf::from_str(&s); let pathbuf = PathBuf::from_str(s);
let user_version = UserVersion::from_str(&s); let user_version = UserVersion::from_str(s);
match (user_version, pathbuf) { match (user_version, pathbuf) {
(_, Ok(pathbuf)) if pathbuf.exists() => Ok(Self::Path(pathbuf)), (_, Ok(pathbuf)) if pathbuf.exists() => Ok(Self::Path(pathbuf)),
(Ok(user_version), _) => Ok(Self::Direct(user_version)), (Ok(user_version), _) => Ok(Self::Direct(user_version)),
@ -47,7 +47,7 @@ mod tests {
write!(file, "14").unwrap(); write!(file, "14").unwrap();
let pathbuf = file.path().to_path_buf(); let pathbuf = file.path().to_path_buf();
let user_version = UserVersionReader::Path(pathbuf).to_user_version(); let user_version = UserVersionReader::Path(pathbuf).into_user_version();
assert_eq!(user_version, Some(UserVersion::OnlyMajor(14))); assert_eq!(user_version, Some(UserVersion::OnlyMajor(14)));
} }
@ -58,13 +58,14 @@ mod tests {
std::fs::write(node_version_path, "14").unwrap(); std::fs::write(node_version_path, "14").unwrap();
let pathbuf = directory.path().to_path_buf(); let pathbuf = directory.path().to_path_buf();
let user_version = UserVersionReader::Path(pathbuf).to_user_version(); let user_version = UserVersionReader::Path(pathbuf).into_user_version();
assert_eq!(user_version, Some(UserVersion::OnlyMajor(14))); assert_eq!(user_version, Some(UserVersion::OnlyMajor(14)));
} }
#[test] #[test]
fn test_direct_to_version() { fn test_direct_to_version() {
let user_version = UserVersionReader::Direct(UserVersion::OnlyMajor(14)).to_user_version(); let user_version =
UserVersionReader::Direct(UserVersion::OnlyMajor(14)).into_user_version();
assert_eq!(user_version, Some(UserVersion::OnlyMajor(14))); assert_eq!(user_version, Some(UserVersion::OnlyMajor(14)));
} }

13
src/version.rs

@ -12,7 +12,7 @@ pub enum Version {
} }
fn first_letter_is_number(s: &str) -> bool { fn first_letter_is_number(s: &str) -> bool {
s.chars().next().map(|x| x.is_digit(10)).unwrap_or(false) s.chars().next().map_or(false, |x| x.is_digit(10))
} }
impl Version { impl Version {
@ -25,7 +25,7 @@ impl Version {
Ok(Self::Lts(lts_type)) Ok(Self::Lts(lts_type))
} else if first_letter_is_number(lowercased.trim_start_matches('v')) { } else if first_letter_is_number(lowercased.trim_start_matches('v')) {
let version_plain = lowercased.trim_start_matches('v'); let version_plain = lowercased.trim_start_matches('v');
let sver = semver::Version::parse(&version_plain)?; let sver = semver::Version::parse(version_plain)?;
Ok(Self::Semver(sver)) Ok(Self::Semver(sver))
} else { } else {
Ok(Self::Alias(lowercased)) Ok(Self::Alias(lowercased))
@ -34,8 +34,7 @@ impl Version {
pub fn alias_name(&self) -> Option<String> { pub fn alias_name(&self) -> Option<String> {
match self { match self {
l @ Self::Lts(_) => Some(l.v_str()), l @ (Self::Lts(_) | Self::Alias(_)) => Some(l.v_str()),
l @ Self::Alias(_) => Some(l.v_str()),
_ => None, _ => None,
} }
} }
@ -44,7 +43,7 @@ impl Version {
&self, &self,
config: &config::FnmConfig, config: &config::FnmConfig,
) -> std::io::Result<Vec<alias::StoredAlias>> { ) -> std::io::Result<Vec<alias::StoredAlias>> {
let aliases = alias::list_aliases(&config)? let aliases = alias::list_aliases(config)?
.drain(..) .drain(..)
.filter(|alias| alias.s_ver() == self.v_str()) .filter(|alias| alias.s_ver() == self.v_str())
.collect(); .collect();
@ -58,7 +57,7 @@ impl Version {
pub fn installation_path(&self, config: &config::FnmConfig) -> Option<std::path::PathBuf> { pub fn installation_path(&self, config: &config::FnmConfig) -> Option<std::path::PathBuf> {
match self { match self {
Self::Bypassed => None, Self::Bypassed => None,
v @ Self::Lts(_) | v @ Self::Alias(_) => { v @ (Self::Lts(_) | Self::Alias(_)) => {
Some(config.aliases_dir().join(v.alias_name().unwrap())) Some(config.aliases_dir().join(v.alias_name().unwrap()))
} }
v @ Self::Semver(_) => Some( v @ Self::Semver(_) => Some(
@ -71,7 +70,7 @@ impl Version {
} }
pub fn root_path(&self, config: &config::FnmConfig) -> Option<std::path::PathBuf> { pub fn root_path(&self, config: &config::FnmConfig) -> Option<std::path::PathBuf> {
match self.installation_path(&config) { match self.installation_path(config) {
None => None, None => None,
Some(path) => { Some(path) => {
let mut canon_path = path.canonicalize().ok()?; let mut canon_path = path.canonicalize().ok()?;

2
src/version_files.rs

@ -10,7 +10,7 @@ const PATH_PARTS: [&str; 2] = [".nvmrc", ".node-version"];
pub fn get_user_version_for_directory(path: impl AsRef<Path>) -> Option<UserVersion> { pub fn get_user_version_for_directory(path: impl AsRef<Path>) -> Option<UserVersion> {
let path = path.as_ref(); let path = path.as_ref();
for path_part in PATH_PARTS.iter() { for path_part in &PATH_PARTS {
let new_path = path.join(path_part); let new_path = path.join(path_part);
info!( info!(
"Looking for version file in {}. exists? {}", "Looking for version file in {}. exists? {}",

7
tests/shellcode/call.rs

@ -4,16 +4,13 @@ use std::fmt::Write;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Call { pub(crate) struct Call {
binary: Box<&'static str>, binary: &'static str,
args: Vec<&'static str>, args: Vec<&'static str>,
} }
impl Call { impl Call {
pub(crate) fn new(binary: &'static str, args: Vec<&'static str>) -> Self { pub(crate) fn new(binary: &'static str, args: Vec<&'static str>) -> Self {
Self { Self { binary, args }
binary: Box::from(binary),
args,
}
} }
} }

2
tests/shellcode/line_separated_expressions.rs

@ -38,7 +38,7 @@ impl<S: Shell, A: Expression<S>, B: Expression<S>> Expression<S>
{ {
fn write_shell(&self, writer: &mut impl Write) -> std::fmt::Result { fn write_shell(&self, writer: &mut impl Write) -> std::fmt::Result {
self.a.write_shell(writer)?; self.a.write_shell(writer)?;
write!(writer, "\n")?; writeln!(writer)?;
self.b.write_shell(writer) self.b.write_shell(writer)
} }
} }

2
tests/shellcode/mod.rs

@ -81,7 +81,7 @@ macro_rules! test_shell {
$( $(
#[test] #[test]
#[serial_test::serial] #[serial_test::serial]
#[allow(non_snake_case)] #[allow(non_snake_case, clippy::redundant_closure_call)]
fn $shell() { fn $shell() {
use super::*; use super::*;
#[allow(unused)] #[allow(unused)]

Loading…
Cancel
Save