From 934ec4f6b2aafe9d0d5eb5dcb1a863237c019018 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Wed, 25 Oct 2023 23:49:03 +0800 Subject: [PATCH] feat: add remote version sorting and filtering --- .changeset/fifty-emus-type.md | 5 ++++ src/commands/install.rs | 34 +++++++++++++++++-------- src/commands/ls_remote.rs | 47 ++++++++++++++++++++++++++++++++--- src/remote_node_index.rs | 25 ++++++++++++++++--- 4 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 .changeset/fifty-emus-type.md diff --git a/.changeset/fifty-emus-type.md b/.changeset/fifty-emus-type.md new file mode 100644 index 0000000..406a88a --- /dev/null +++ b/.changeset/fifty-emus-type.md @@ -0,0 +1,5 @@ +--- +"fnm": minor +--- + +feat: add remote version sorting and filtering diff --git a/src/commands/install.rs b/src/commands/install.rs index 2c55384..77840eb 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -67,8 +67,11 @@ impl Command for Install { return Err(Error::UninstallableVersion { version: v }); } UserVersion::Full(Version::Lts(lts_type)) => { - let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror) - .map_err(|source| Error::CantListRemoteVersions { source })?; + let available_versions: Vec<_> = remote_node_index::list( + &config.node_dist_mirror, + &remote_node_index::SortingMethod::Ascending, + ) + .map_err(|source| Error::CantListRemoteVersions { source })?; let picked_version = lts_type .pick_latest(&available_versions) .ok_or_else(|| Error::CantFindRelevantLts { @@ -84,8 +87,11 @@ impl Command for Install { picked_version } UserVersion::Full(Version::Latest) => { - let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror) - .map_err(|source| Error::CantListRemoteVersions { source })?; + let available_versions: Vec<_> = remote_node_index::list( + &config.node_dist_mirror, + &remote_node_index::SortingMethod::Ascending, + ) + .map_err(|source| Error::CantListRemoteVersions { source })?; let picked_version = available_versions .last() .ok_or(Error::CantFindLatest)? @@ -99,11 +105,14 @@ impl Command for Install { picked_version } current_version => { - let available_versions: Vec<_> = remote_node_index::list(&config.node_dist_mirror) - .map_err(|source| Error::CantListRemoteVersions { source })? - .drain(..) - .map(|x| x.version) - .collect(); + let available_versions: Vec<_> = remote_node_index::list( + &config.node_dist_mirror, + &remote_node_index::SortingMethod::Ascending, + ) + .map_err(|source| Error::CantListRemoteVersions { source })? + .drain(..) + .map(|x| x.version) + .collect(); current_version .to_version(&available_versions, config) @@ -254,8 +263,11 @@ mod tests { .apply(&config) .expect("Can't install"); - let available_versions: Vec<_> = - remote_node_index::list(&config.node_dist_mirror).expect("Can't get node version list"); + let available_versions: Vec<_> = remote_node_index::list( + &config.node_dist_mirror, + &remote_node_index::SortingMethod::Ascending, + ) + .expect("Can't get node version list"); let latest_version = available_versions.last().unwrap().version.clone(); assert!(config.installations_dir().exists()); diff --git a/src/commands/ls_remote.rs b/src/commands/ls_remote.rs index 015b3ac..6cc03e9 100644 --- a/src/commands/ls_remote.rs +++ b/src/commands/ls_remote.rs @@ -1,24 +1,63 @@ use crate::config::FnmConfig; use crate::remote_node_index; +use colored::Colorize; use thiserror::Error; #[derive(clap::Parser, Debug)] -pub struct LsRemote {} +pub struct LsRemote { + /// Filter for version prefixes + filter: Option, + + // Version sorting order + #[arg(long, default_value = "asc")] + sort: remote_node_index::SortingMethod, +} impl super::command::Command for LsRemote { type Error = Error; fn apply(self, config: &FnmConfig) -> Result<(), Self::Error> { - let all_versions = remote_node_index::list(&config.node_dist_mirror)?; + let filter = self + .filter + .and_then(|f| Some(f.strip_prefix('v').unwrap_or(&f).to_owned())); - for version in all_versions { + let mut all_versions = remote_node_index::list(&config.node_dist_mirror, &self.sort)?; + + if let Some(filter) = &filter { + all_versions = all_versions + .into_iter() + .filter(|v| { + v.version + .v_str() + .strip_prefix('v') + .and_then(|rv| Some(rv.starts_with(filter))) + .unwrap_or(false) + }) + .collect(); + } + + for version in &all_versions { print!("{}", version.version); if let Some(lts) = &version.lts { - print!(" ({lts})"); + print!("{}", format!(" ({lts})").cyan()); } println!(); } + if all_versions.is_empty() { + eprintln!( + "{}", + format!( + "No versions were found{}!", + match filter { + Some(filter) => format!(" with prefix {filter}"), + None => "".to_owned(), + } + ) + .red() + ); + } + Ok(()) } } diff --git a/src/remote_node_index.rs b/src/remote_node_index.rs index 4c9f3bd..ac5974f 100644 --- a/src/remote_node_index.rs +++ b/src/remote_node_index.rs @@ -66,16 +66,35 @@ pub struct IndexedNodeVersion { pub files: Vec, } +#[derive(clap::ValueEnum, Clone, Debug, PartialEq)] +pub enum SortingMethod { + #[clap(name = "desc")] + /// Sort versions in descending order (latest to earliest) + Descending, + #[clap(name = "asc")] + /// Sort versions in ascending order (earliest to latest) + Ascending, +} + /// Prints /// /// ```rust /// use crate::remote_node_index::list; /// ``` -pub fn list(base_url: &Url) -> Result, crate::http::Error> { +pub fn list( + base_url: &Url, + sort: &SortingMethod, +) -> Result, crate::http::Error> { let index_json_url = format!("{base_url}/index.json"); let resp = crate::http::get(&index_json_url)?; let mut value: Vec = resp.json()?; - value.sort_by(|a, b| a.version.cmp(&b.version)); + + if *sort == SortingMethod::Ascending { + value.sort_by(|a, b| a.version.cmp(&b.version)); + } else { + value.sort_by(|a, b| b.version.cmp(&a.version)); + } + Ok(value) } @@ -88,7 +107,7 @@ mod tests { fn test_list() { let base_url = Url::parse("https://nodejs.org/dist").unwrap(); let expected_version = Version::parse("12.0.0").unwrap(); - let mut versions = list(&base_url).expect("Can't get HTTP data"); + let mut versions = list(&base_url, &SortingMethod::Ascending).expect("Can't get HTTP data"); assert_eq!( versions .drain(..)