diff --git a/src/bin/delete-version.rs b/src/bin/delete-version.rs index 49acea495ca..c3711175146 100644 --- a/src/bin/delete-version.rs +++ b/src/bin/delete-version.rs @@ -10,6 +10,7 @@ extern crate cargo_registry; extern crate postgres; extern crate time; +extern crate semver; use std::env; use std::io; @@ -37,6 +38,7 @@ fn delete(tx: &postgres::transaction::Transaction) { None => { println!("needs a version argument"); return } Some(s) => s, }; + let version = semver::Version::parse(&version).unwrap(); let krate = Crate::find_by_name(tx, &name).unwrap(); let v = Version::find_by_num(tx, krate.id, &version).unwrap().unwrap(); diff --git a/src/bin/transfer-crates.rs b/src/bin/transfer-crates.rs index b5bd8ffbc3f..bf6cf2da6bb 100644 --- a/src/bin/transfer-crates.rs +++ b/src/bin/transfer-crates.rs @@ -8,6 +8,7 @@ extern crate cargo_registry; extern crate postgres; extern crate time; +extern crate semver; use std::env; use std::io; diff --git a/src/git.rs b/src/git.rs index 5a5652ea6f7..37b20982ee4 100644 --- a/src/git.rs +++ b/src/git.rs @@ -4,6 +4,7 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; +use semver; use git2; use rustc_serialize::json; @@ -68,7 +69,7 @@ pub fn add_crate(app: &App, krate: &Crate) -> CargoResult<()> { }) } -pub fn yank(app: &App, krate: &str, version: &str, +pub fn yank(app: &App, krate: &str, version: &semver::Version, yanked: bool) -> CargoResult<()> { let repo = app.git_repo.lock().unwrap(); let repo = &*repo; diff --git a/src/krate.rs b/src/krate.rs index 391b37abef0..191ee69c3df 100644 --- a/src/krate.rs +++ b/src/krate.rs @@ -5,7 +5,6 @@ use std::io::prelude::*; use std::io; use std::mem; use std::sync::Arc; -use std::borrow::Cow; use conduit::{Request, Response}; use conduit_router::RequestParams; @@ -65,7 +64,7 @@ pub struct EncodableCrate { pub badges: Option>, pub created_at: String, pub downloads: i32, - pub max_version: Cow<'static, str>, + pub max_version: String, pub description: Option, pub homepage: Option, pub documentation: Option, @@ -233,13 +232,13 @@ impl Crate { } pub fn minimal_encodable(self, - max_version: Cow<'static, str>, + max_version: semver::Version, badges: Option>) -> EncodableCrate { self.encodable(max_version, None, None, None, badges) } pub fn encodable(self, - max_version: Cow<'static, str>, + max_version: semver::Version, versions: Option>, keywords: Option<&[Keyword]>, categories: Option<&[Category]>, @@ -269,7 +268,7 @@ impl Crate { keywords: keyword_ids, categories: category_ids, badges: badges, - max_version: max_version, + max_version: max_version.to_string(), documentation: documentation, homepage: homepage, description: description, @@ -284,14 +283,14 @@ impl Crate { } } - pub fn max_version(&self, conn: &GenericConnection) -> CargoResult> { + pub fn max_version(&self, conn: &GenericConnection) -> CargoResult { let stmt = conn.prepare("SELECT num FROM versions WHERE crate_id = $1 AND yanked = 'f'")?; let rows = stmt.query(&[&self.id])?; Ok(rows.iter() - .map(|r| Cow::Owned(r.get("num"))) - .max_by_key(|v| semver::Version::parse(&v).ok()) - .unwrap_or_else(|| Cow::Borrowed("0.0.0"))) + .map(|r| semver::Version::parse(&r.get::<_, String>("num")).unwrap()) + .max() + .unwrap_or_else(|| semver::Version::parse("0.0.0").unwrap())) } pub fn versions(&self, conn: &GenericConnection) -> CargoResult> { @@ -389,7 +388,7 @@ impl Crate { features: &HashMap>, authors: &[String]) -> CargoResult { - match Version::find_by_num(conn, self.id, &ver.to_string())? { + match Version::find_by_num(conn, self.id, ver)? { Some(..) => { return Err(human(format!("crate version `{}` is already uploaded", ver))) diff --git a/src/tests/all.rs b/src/tests/all.rs index c5b60fc6696..61e618c8262 100755 --- a/src/tests/all.rs +++ b/src/tests/all.rs @@ -219,10 +219,10 @@ fn sign_in_as(req: &mut Request, user: &User) { } fn mock_crate(req: &mut Request, krate: Crate) -> (Crate, Version) { - mock_crate_vers(req, krate, "1.0.0") + mock_crate_vers(req, krate, &semver::Version::parse("1.0.0").unwrap()) } -fn mock_crate_vers(req: &mut Request, krate: Crate, v: &str) +fn mock_crate_vers(req: &mut Request, krate: Crate, v: &semver::Version) -> (Crate, Version) { let user = req.extensions().find::().unwrap(); let mut krate = Crate::find_or_insert(req.tx().unwrap(), &krate.name, @@ -234,8 +234,7 @@ fn mock_crate_vers(req: &mut Request, krate: Crate, v: &str) &krate.license, &None, krate.max_upload_size).unwrap(); - let v = semver::Version::parse(v).unwrap(); - let v = krate.add_version(req.tx().unwrap(), &v, &HashMap::new(), &[]); + let v = krate.add_version(req.tx().unwrap(), v, &HashMap::new(), &[]); (krate, v.unwrap()) } diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 184262502aa..f2f73239bbd 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -1156,12 +1156,14 @@ fn ignored_badges() { fn reverse_dependencies() { let (_b, app, middle) = ::app(); + let v100 = semver::Version::parse("1.0.0").unwrap(); + let v110 = semver::Version::parse("1.1.0").unwrap(); let mut req = ::req(app, Method::Get, "/api/v1/crates/c1/reverse_dependencies"); ::mock_user(&mut req, ::user("foo")); - let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), "1.0.0"); - let (_, c2v1) = ::mock_crate_vers(&mut req, ::krate("c2"), "1.0.0"); - let (_, c2v2) = ::mock_crate_vers(&mut req, ::krate("c2"), "1.1.0"); + let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), &v100); + let (_, c2v1) = ::mock_crate_vers(&mut req, ::krate("c2"), &v100); + let (_, c2v2) = ::mock_crate_vers(&mut req, ::krate("c2"), &v110); ::mock_dep(&mut req, &c2v1, &c1, None); ::mock_dep(&mut req, &c2v2, &c1, None); @@ -1185,12 +1187,15 @@ fn reverse_dependencies() { fn reverse_dependencies_when_old_version_doesnt_depend_but_new_does() { let (_b, app, middle) = ::app(); + let v100 = semver::Version::parse("1.0.0").unwrap(); + let v110 = semver::Version::parse("1.1.0").unwrap(); + let v200 = semver::Version::parse("2.0.0").unwrap(); let mut req = ::req(app, Method::Get, "/api/v1/crates/c1/reverse_dependencies"); ::mock_user(&mut req, ::user("foo")); - let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), "1.1.0"); - let _ = ::mock_crate_vers(&mut req, ::krate("c2"), "1.0.0"); - let (_, c2v2) = ::mock_crate_vers(&mut req, ::krate("c2"), "2.0.0"); + let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), &v110); + let _ = ::mock_crate_vers(&mut req, ::krate("c2"), &v100); + let (_, c2v2) = ::mock_crate_vers(&mut req, ::krate("c2"), &v200); ::mock_dep(&mut req, &c2v2, &c1, None); @@ -1205,12 +1210,14 @@ fn reverse_dependencies_when_old_version_doesnt_depend_but_new_does() { fn reverse_dependencies_when_old_version_depended_but_new_doesnt() { let (_b, app, middle) = ::app(); + let v100 = semver::Version::parse("1.0.0").unwrap(); + let v200 = semver::Version::parse("2.0.0").unwrap(); let mut req = ::req(app, Method::Get, "/api/v1/crates/c1/reverse_dependencies"); ::mock_user(&mut req, ::user("foo")); - let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), "1.0.0"); - let (_, c2v1) = ::mock_crate_vers(&mut req, ::krate("c2"), "1.0.0"); - let _ = ::mock_crate_vers(&mut req, ::krate("c2"), "2.0.0"); + let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), &v100); + let (_, c2v1) = ::mock_crate_vers(&mut req, ::krate("c2"), &v100); + let _ = ::mock_crate_vers(&mut req, ::krate("c2"), &v200); ::mock_dep(&mut req, &c2v1, &c1, None); @@ -1224,13 +1231,15 @@ fn reverse_dependencies_when_old_version_depended_but_new_doesnt() { fn prerelease_versions_not_included_in_reverse_dependencies() { let (_b, app, middle) = ::app(); + let v100 = semver::Version::parse("1.0.0").unwrap(); + let v110_pre = semver::Version::parse("1.1.0-pre").unwrap(); let mut req = ::req(app, Method::Get, "/api/v1/crates/c1/reverse_dependencies"); ::mock_user(&mut req, ::user("foo")); - let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), "1.0.0"); - let _ = ::mock_crate_vers(&mut req, ::krate("c2"), "1.1.0-pre"); - let (_, c3v1) = ::mock_crate_vers(&mut req, ::krate("c3"), "1.0.0"); - let _ = ::mock_crate_vers(&mut req, ::krate("c3"), "1.1.0-pre"); + let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), &v100); + let _ = ::mock_crate_vers(&mut req, ::krate("c2"), &v110_pre); + let (_, c3v1) = ::mock_crate_vers(&mut req, ::krate("c3"), &v100); + let _ = ::mock_crate_vers(&mut req, ::krate("c3"), &v110_pre); ::mock_dep(&mut req, &c3v1, &c1, None); @@ -1245,12 +1254,14 @@ fn prerelease_versions_not_included_in_reverse_dependencies() { fn yanked_versions_not_included_in_reverse_dependencies() { let (_b, app, middle) = ::app(); + let v100 = semver::Version::parse("1.0.0").unwrap(); + let v200 = semver::Version::parse("2.0.0").unwrap(); let mut req = ::req(app, Method::Get, "/api/v1/crates/c1/reverse_dependencies"); ::mock_user(&mut req, ::user("foo")); - let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), "1.0.0"); - let _ = ::mock_crate_vers(&mut req, ::krate("c2"), "1.0.0"); - let (_, c2v2) = ::mock_crate_vers(&mut req, ::krate("c2"), "2.0.0"); + let (c1, _) = ::mock_crate_vers(&mut req, ::krate("c1"), &v100); + let _ = ::mock_crate_vers(&mut req, ::krate("c2"), &v100); + let (_, c2v2) = ::mock_crate_vers(&mut req, ::krate("c2"), &v200); ::mock_dep(&mut req, &c2v2, &c1, None); diff --git a/src/version.rs b/src/version.rs index 1e1e72a31b3..6815074db85 100644 --- a/src/version.rs +++ b/src/version.rs @@ -25,7 +25,7 @@ use util::{RequestUtils, CargoResult, ChainError, internal, human}; pub struct Version { pub id: i32, pub crate_id: i32, - pub num: String, + pub num: semver::Version, pub updated_at: Timespec, pub created_at: Timespec, pub downloads: i32, @@ -61,8 +61,9 @@ pub struct VersionLinks { impl Version { pub fn find_by_num(conn: &GenericConnection, crate_id: i32, - num: &str) + num: &semver::Version) -> CargoResult> { + let num = num.to_string(); let stmt = conn.prepare("SELECT * FROM versions \ WHERE crate_id = $1 AND num = $2")?; let rows = stmt.query(&[&crate_id, &num])?; @@ -191,6 +192,7 @@ impl Version { impl Model for Version { fn from_row(row: &Row) -> Version { + let num: String = row.get("num"); let features: Option = row.get("features"); let features = features.map(|s| { json::decode(&s).unwrap() @@ -198,7 +200,7 @@ impl Model for Version { Version { id: row.get("id"), crate_id: row.get("crate_id"), - num: row.get("num"), + num: semver::Version::parse(&num).unwrap(), updated_at: row.get("updated_at"), created_at: row.get("created_at"), downloads: row.get("downloads"), @@ -269,6 +271,9 @@ pub fn show(req: &mut Request) -> CargoResult { fn version_and_crate(req: &mut Request) -> CargoResult<(Version, Crate)> { let crate_name = &req.params()["crate_id"]; let semver = &req.params()["version"]; + let semver = semver::Version::parse(semver).map_err(|_| { + human(format!("invalid semver: {}", semver)) + })?; let tx = req.tx()?; let krate = Crate::find_by_name(tx, crate_name)?; let version = Version::find_by_num(tx, krate.id, &semver)?;