Skip to content

Commit e460912

Browse files
committed
models/default_version: Implement Ord for Version
This allows us to simplify the `find_default_version()` version fn, which now only needs to iterate the slice once instead of up to three times, and it makes it possible to directly compare two `Version` instances too.
1 parent 82c5403 commit e460912

File tree

1 file changed

+65
-8
lines changed

1 file changed

+65
-8
lines changed

src/models/default_versions.rs

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use diesel::prelude::*;
88
/// This struct is used to load all versions of a crate from the database,
99
/// without loading all the additional data unnecessary for default version
1010
/// resolution.
11-
#[derive(Clone, Debug, Queryable, Selectable)]
11+
#[derive(Clone, Debug, PartialEq, Eq, Queryable, Selectable)]
1212
#[diesel(table_name = versions)]
1313
#[diesel(check_for_backend(diesel::pg::Pg))]
1414
struct Version {
@@ -23,6 +23,22 @@ impl Version {
2323
fn is_prerelease(&self) -> bool {
2424
!self.num.pre.is_empty()
2525
}
26+
27+
fn ord_tuple(&self) -> (bool, bool, &semver::Version, i32) {
28+
(!self.yanked, !self.is_prerelease(), &self.num, self.id)
29+
}
30+
}
31+
32+
impl PartialOrd for Version {
33+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
34+
Some(self.cmp(other))
35+
}
36+
}
37+
38+
impl Ord for Version {
39+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
40+
self.ord_tuple().cmp(&other.ord_tuple())
41+
}
2642
}
2743

2844
/// Updates the `default_versions` table entry for the specified crate.
@@ -102,20 +118,16 @@ fn calculate_default_version(crate_id: i32, conn: &mut impl Conn) -> QueryResult
102118
}
103119

104120
fn find_default_version(versions: &[Version]) -> Option<&Version> {
105-
highest(versions, |v| !v.is_prerelease() && !v.yanked)
106-
.or_else(|| highest(versions, |v| !v.yanked))
107-
.or_else(|| highest(versions, |_| true))
108-
}
109-
110-
fn highest(versions: &[Version], filter: impl FnMut(&&Version) -> bool) -> Option<&Version> {
111-
versions.iter().filter(filter).max_by_key(|v| &v.num)
121+
versions.iter().max()
112122
}
113123

114124
#[cfg(test)]
115125
mod tests {
116126
use super::*;
117127
use crate::schema::crates;
118128
use crate::test_util::test_db_connection;
129+
use insta::assert_snapshot;
130+
use std::fmt::Write;
119131

120132
fn v(num: &str, yanked: bool) -> Version {
121133
let num = semver::Version::parse(num).unwrap();
@@ -179,6 +191,51 @@ mod tests {
179191
check(&versions, "1.0.0-beta.3");
180192
}
181193

194+
#[test]
195+
fn test_ord() {
196+
let mut versions = vec![
197+
v("1.0.0", false),
198+
v("1.0.0-beta.1", false),
199+
v("1.0.0-beta.2", false),
200+
v("1.0.0-beta.3", false),
201+
v("1.0.1", true),
202+
v("1.0.2", false),
203+
v("1.1.0", false),
204+
v("1.1.1-beta.1", true),
205+
v("1.1.1", true),
206+
v("1.0.3", false),
207+
v("2.0.0-beta.1", false),
208+
];
209+
210+
versions.sort();
211+
212+
assert_snapshot!(format_versions(&versions), @r#"
213+
1.1.1-beta.1 (yanked)
214+
1.0.1 (yanked)
215+
1.1.1 (yanked)
216+
1.0.0-beta.1
217+
1.0.0-beta.2
218+
1.0.0-beta.3
219+
2.0.0-beta.1
220+
1.0.0
221+
1.0.2
222+
1.0.3
223+
1.1.0
224+
"#);
225+
}
226+
227+
fn format_versions(versions: &[Version]) -> String {
228+
let mut buf = String::with_capacity(versions.len() * 20);
229+
for v in versions {
230+
write!(buf, "{}", v.num).unwrap();
231+
if v.yanked {
232+
buf.push_str(" (yanked)");
233+
}
234+
buf.push('\n');
235+
}
236+
buf
237+
}
238+
182239
fn create_crate(name: &str, conn: &mut impl Conn) -> i32 {
183240
diesel::insert_into(crates::table)
184241
.values(crates::name.eq(name))

0 commit comments

Comments
 (0)