diff --git a/src/main.rs b/src/main.rs index 5607b06965..69d78db15c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,16 +111,24 @@ fn actual_main() -> Result<(), i32> { (!lhs.needs_update(lhstv, lhsip), &lhs.name).cmp(&(!rhs.needs_update(rhstv, rhsip), &rhs.name)) }) { write!(out, "{}\t", package.name).unwrap(); + if let Some(ref v) = package.version { write!(out, "v{}", v).unwrap(); + } else { + write!(out, "No").unwrap(); } + if let Some(tv) = package_target_version { write!(out, "\t{}", tv).unwrap(); } else if let Some(upd_v) = package.update_to_version() { write!(out, "\tv{}", upd_v).unwrap(); + if let Some(alt_v) = package.alternative_version.as_ref() { + write!(out, " (v{} available)", alt_v).unwrap(); + } } else { write!(out, "\tN/A").unwrap(); } + writeln!(out, "\t{}", if package.needs_update(package_target_version, package_install_prereleases) { diff --git a/src/ops/mod.rs b/src/ops/mod.rs index c15a56aadd..38796e2add 100644 --- a/src/ops/mod.rs +++ b/src/ops/mod.rs @@ -12,8 +12,8 @@ use std::fs::{self, DirEntry, File}; use std::path::{PathBuf, Path}; use std::io::{Write, Read}; use std::time::SystemTime; +use std::{cmp, env, mem}; use std::borrow::Cow; -use std::{cmp, env}; use regex::Regex; use url::Url; use toml; @@ -51,6 +51,7 @@ lazy_static! { /// name: "racer".to_string(), /// version: Some(Semver::parse("1.2.10").unwrap()), /// newest_version: None, +/// alternative_version: None, /// max_version: None, /// }); /// @@ -73,6 +74,8 @@ pub struct MainRepoPackage { /// /// `None` by default, acquire via `MainRepoPackage::pull_version()`. pub newest_version: Option, + /// If present, the alternative newest version not chosen because of unfulfilled requirements like (not) being a prerelease. + pub alternative_version: Option, /// User-bounded maximum version to update up to. pub max_version: Option, } @@ -151,6 +154,7 @@ impl MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("1.2.10").unwrap()), /// newest_version: None, + /// alternative_version: None, /// max_version: None, /// }); /// @@ -160,6 +164,7 @@ impl MainRepoPackage { /// name: "cargo-outdated".to_string(), /// version: Some(Semver::parse("0.2.0").unwrap()), /// newest_version: None, + /// alternative_version: None, /// max_version: None, /// }); /// # } @@ -178,19 +183,33 @@ impl MainRepoPackage { name: c.get(1).unwrap().as_str().to_string(), version: Some(Semver::parse(c.get(2).unwrap().as_str()).unwrap()), newest_version: None, + alternative_version: None, max_version: None, } }) } - /// Download the version list for this crate off the specified repository tree. + /// Download the version list for this crate off the specified repository tree and set the latest and alternative versions. pub fn pull_version<'t>(&mut self, registry: &Tree<'t>, registry_parent: &'t Repository, install_prereleases: Option) { - let vers = crate_versions(&mut &find_package_data(&self.name, registry, registry_parent) - .ok_or_else(|| format!("package {} not found", self.name)) - .unwrap() - [..], - install_prereleases); - self.newest_version = vers.into_iter().max(); + let mut vers = + crate_versions(&mut &find_package_data(&self.name, registry, registry_parent).ok_or_else(|| format!("package {} not found", self.name)).unwrap() + [..]); + vers.sort(); + + self.newest_version = None; + self.alternative_version = None; + + let mut vers = vers.into_iter().rev(); + if let Some(newest) = vers.next() { + self.newest_version = Some(newest); + + if self.newest_version.as_ref().unwrap().is_prerelease() && !install_prereleases.unwrap_or(false) { + if let Some(newest_nonpre) = vers.find(|v| !v.is_prerelease()) { + mem::swap(&mut self.alternative_version, &mut self.newest_version); + self.newest_version = Some(newest_nonpre); + } + } + } } /// Check whether this package needs to be installed @@ -208,24 +227,28 @@ impl MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("1.7.2").unwrap()), /// newest_version: Some(Semver::parse("2.0.6").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(None, None)); /// assert!(MainRepoPackage { /// name: "racer".to_string(), /// version: None, /// newest_version: Some(Semver::parse("2.0.6").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(None, None)); /// assert!(!MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("2.0.6").unwrap()), /// newest_version: Some(Semver::parse("2.0.6").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(None, None)); /// assert!(!MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("2.0.6").unwrap()), /// newest_version: None, + /// alternative_version: None, /// max_version: None, /// }.needs_update(None, None)); /// @@ -234,18 +257,21 @@ impl MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("1.7.2").unwrap()), /// newest_version: Some(Semver::parse("1.7.3").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(Some(&req), None)); /// assert!(MainRepoPackage { /// name: "racer".to_string(), /// version: None, /// newest_version: Some(Semver::parse("2.0.6").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(Some(&req), None)); /// assert!(!MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("1.7.2").unwrap()), /// newest_version: Some(Semver::parse("2.0.6").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(Some(&req), None)); /// @@ -253,21 +279,24 @@ impl MainRepoPackage { /// name: "cargo-audit".to_string(), /// version: None, /// newest_version: Some(Semver::parse("0.9.0-beta2").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(Some(&req), None)); /// assert!(MainRepoPackage { /// name: "cargo-audit".to_string(), /// version: None, /// newest_version: Some(Semver::parse("0.9.0-beta2").unwrap()), + /// alternative_version: None, /// max_version: None, /// }.needs_update(Some(&req), Some(true))); /// # } /// ``` pub fn needs_update(&self, req: Option<&SemverReq>, install_prereleases: Option) -> bool { + let update_to_version = self.update_to_version(); + (req.into_iter().zip(self.version.as_ref()).map(|(sr, cv)| !sr.matches(cv)).next().unwrap_or(true) || - req.into_iter().zip(self.update_to_version()).map(|(sr, uv)| sr.matches(uv)).next().unwrap_or(true)) && - self.update_to_version() - .map(|upd_v| { + req.into_iter().zip(update_to_version).map(|(sr, uv)| sr.matches(uv)).next().unwrap_or(true)) && + update_to_version.map(|upd_v| { (!upd_v.is_prerelease() || install_prereleases.unwrap_or(false)) && (self.version.is_none() || (*self.version.as_ref().unwrap() < *upd_v)) }) .unwrap_or(false) @@ -287,6 +316,7 @@ impl MainRepoPackage { /// name: "racer".to_string(), /// version: Some(Semver::parse("1.7.2").unwrap()), /// newest_version: Some(Semver::parse("2.0.6").unwrap()), + /// alternative_version: None, /// max_version: Some(Semver::parse("2.0.5").unwrap()), /// }.update_to_version(), /// Some(&Semver::parse("2.0.5").unwrap())); @@ -294,6 +324,7 @@ impl MainRepoPackage { /// name: "gutenberg".to_string(), /// version: Some(Semver::parse("0.0.7").unwrap()), /// newest_version: None, + /// alternative_version: None, /// max_version: None, /// }.update_to_version(), /// None); @@ -645,6 +676,7 @@ pub fn intersect_packages(installed: &[MainRepoPackage], to_update: &[(String, O name: p.0.clone(), version: None, newest_version: None, + alternative_version: None, max_version: p.1.clone(), } })) @@ -659,27 +691,24 @@ pub fn intersect_packages(installed: &[MainRepoPackage], to_update: &[(String, O /// # use cargo_update::ops::crate_versions; /// # use std::fs::File; /// # let desc_path = "test-data/checksums-versions.json"; -/// let versions = crate_versions(&mut File::open(desc_path).unwrap(), None); +/// let versions = crate_versions(&mut File::open(desc_path).unwrap()); /// /// println!("Released versions of checksums:"); /// for ver in &versions { /// println!(" {}", ver); /// } /// ``` -pub fn crate_versions(package_desc: &mut R, install_prereleases: Option) -> Vec { +pub fn crate_versions(package_desc: &mut R) -> Vec { let mut buf = String::new(); package_desc.read_to_string(&mut buf).unwrap(); - crate_versions_impl(buf, install_prereleases) + crate_versions_impl(buf) } -fn crate_versions_impl(buf: String, install_prereleases: Option) -> Vec { - let install_prereleases = install_prereleases.unwrap_or(false); - +fn crate_versions_impl(buf: String) -> Vec { buf.lines() .map(|p| json::parse(p).unwrap()) .filter(|j| !j["yanked"].as_bool().unwrap()) .map(|j| Semver::parse(j["vers"].as_str().unwrap()).unwrap()) - .filter(|v| !v.is_prerelease() || install_prereleases) .collect() } diff --git a/tests/ops/installed_main_repo_packages.rs b/tests/ops/installed_main_repo_packages.rs index db6d6331fe..b06ca5b43c 100644 --- a/tests/ops/installed_main_repo_packages.rs +++ b/tests/ops/installed_main_repo_packages.rs @@ -25,18 +25,21 @@ fn existant() { name: "cargo-outdated".to_string(), version: Some(Semver::parse("0.2.0").unwrap()), newest_version: None, + alternative_version: None, max_version: None, }, MainRepoPackage { name: "racer".to_string(), version: Some(Semver::parse("1.2.10").unwrap()), newest_version: None, + alternative_version: None, max_version: None, }, MainRepoPackage { name: "rustfmt".to_string(), version: Some(Semver::parse("0.6.2").unwrap()), newest_version: None, + alternative_version: None, max_version: None, }]); } diff --git a/tests/ops/main_repo_package/parse.rs b/tests/ops/main_repo_package/parse.rs index 561bba28d4..230733461e 100644 --- a/tests/ops/main_repo_package/parse.rs +++ b/tests/ops/main_repo_package/parse.rs @@ -9,6 +9,7 @@ fn main_repository() { name: "cargo-count".to_string(), version: Some(Semver::parse("0.2.2").unwrap()), newest_version: None, + alternative_version: None, max_version: None, })); } diff --git a/tests/ops/mod.rs b/tests/ops/mod.rs index 237ebf00df..f30bb24872 100644 --- a/tests/ops/mod.rs +++ b/tests/ops/mod.rs @@ -22,7 +22,7 @@ fn intersect_packages() { #[test] fn crate_versions() { - assert_eq!(ops::crate_versions(&mut File::open("test-data/checksums-versions.json").unwrap(), None), + assert_eq!(ops::crate_versions(&mut File::open("test-data/checksums-versions.json").unwrap()), vec![Semver::parse("0.2.0").unwrap(), Semver::parse("0.2.1").unwrap(), Semver::parse("0.3.0").unwrap(),