Skip to content

Commit 53a29c6

Browse files
authored
feat: sort extras by name and urls by filename (#540)
Sorts the extras according to name for consistency. Also sorts the urls slightly different. Instead of sorting by the complete url, now its first sorted by filename followed by the entire url. This causes the urls to be sorted by package name most of the time which reduces the diff if versions change. Fixes prefix-dev/pixi#791
1 parent 517fbbc commit 53a29c6

12 files changed

+1649
-1600
lines changed

crates/rattler_lock/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
use fxhash::FxHashMap;
7171
use pep508_rs::Requirement;
7272
use rattler_conda_types::{MatchSpec, PackageRecord, Platform, RepoDataRecord};
73-
use std::collections::{HashMap, HashSet};
73+
use std::collections::{BTreeSet, HashMap};
7474
use std::sync::Arc;
7575
use std::{borrow::Cow, io::Read, path::Path, str::FromStr};
7676
use url::Url;
@@ -550,7 +550,7 @@ impl PypiPackage {
550550
}
551551

552552
/// Returns the extras enabled for this package
553-
pub fn extras(&self) -> &HashSet<String> {
553+
pub fn extras(&self) -> &BTreeSet<String> {
554554
&self.environment_data().extras
555555
}
556556

crates/rattler_lock/src/parse/serialize.rs

+60-11
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ use crate::{Channel, EnvironmentPackageData, LockFile, PypiPackageData};
44
use itertools::Itertools;
55
use rattler_conda_types::Platform;
66
use serde::{Serialize, Serializer};
7-
use std::{
8-
cmp::Ordering,
9-
collections::{BTreeMap, HashSet},
10-
};
7+
use std::collections::{BTreeSet, HashSet};
8+
use std::{cmp::Ordering, collections::BTreeMap};
119
use url::Url;
1210

1311
#[derive(Serialize)]
@@ -31,16 +29,16 @@ enum SerializablePackageData<'a> {
3129
Pypi(&'a PypiPackageData),
3230
}
3331

34-
#[derive(Serialize)]
32+
#[derive(Serialize, Eq, PartialEq)]
3533
#[serde(untagged, rename_all = "snake_case")]
3634
enum SerializablePackageSelector<'a> {
3735
Conda {
3836
conda: &'a Url,
3937
},
4038
Pypi {
4139
pypi: &'a Url,
42-
#[serde(skip_serializing_if = "HashSet::is_empty")]
43-
extras: &'a HashSet<String>,
40+
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
41+
extras: &'a BTreeSet<String>,
4442
},
4543
}
4644

@@ -53,6 +51,60 @@ impl<'a> SerializablePackageSelector<'a> {
5351
}
5452
}
5553

54+
impl<'a> PartialOrd for SerializablePackageSelector<'a> {
55+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
56+
Some(self.cmp(other))
57+
}
58+
}
59+
60+
impl<'a> Ord for SerializablePackageSelector<'a> {
61+
fn cmp(&self, other: &Self) -> Ordering {
62+
match (self, other) {
63+
(
64+
SerializablePackageSelector::Conda { .. },
65+
SerializablePackageSelector::Pypi { .. },
66+
) => {
67+
// Sort conda packages before pypi packages
68+
Ordering::Less
69+
}
70+
(
71+
SerializablePackageSelector::Pypi { .. },
72+
SerializablePackageSelector::Conda { .. },
73+
) => {
74+
// Sort Pypi packages after conda packages
75+
Ordering::Greater
76+
}
77+
(
78+
SerializablePackageSelector::Conda { conda: a },
79+
SerializablePackageSelector::Conda { conda: b },
80+
)
81+
| (
82+
SerializablePackageSelector::Pypi { pypi: a, .. },
83+
SerializablePackageSelector::Pypi { pypi: b, .. },
84+
) => {
85+
// First sort packages just by their filename. Since most of the time the urls end
86+
// in the packages filename this causes the urls to be sorted by package name.
87+
if let (Some(a), Some(b)) = (
88+
a.path_segments()
89+
.and_then(Iterator::last)
90+
.map(str::to_lowercase),
91+
b.path_segments()
92+
.and_then(Iterator::last)
93+
.map(str::to_lowercase),
94+
) {
95+
match a.cmp(&b) {
96+
Ordering::Equal => {}
97+
ordering => return ordering,
98+
}
99+
}
100+
101+
// Otherwise just sort by their full URL
102+
a.cmp(b)
103+
}
104+
}
105+
}
106+
}
107+
56108
impl<'a> SerializablePackageData<'a> {
57109
fn name(&self) -> &str {
58110
match self {
@@ -149,10 +201,7 @@ impl Serialize for LockFile {
149201
}
150202
}
151203
})
152-
.sorted_by_key(|p| match p {
153-
SerializablePackageSelector::Conda { conda } => *conda,
154-
SerializablePackageSelector::Pypi { pypi, .. } => *pypi,
155-
})
204+
.sorted()
156205
.collect(),
157206
)
158207
})

crates/rattler_lock/src/pypi.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use pep508_rs::Requirement;
44
use serde::{Deserialize, Serialize};
55
use serde_with::{serde_as, skip_serializing_none};
66
use std::cmp::Ordering;
7-
use std::collections::HashSet;
7+
use std::collections::BTreeSet;
88
use url::Url;
99

1010
/// A pinned Pypi package
@@ -37,8 +37,8 @@ pub struct PypiPackageData {
3737
/// the same pypi package but with different extras enabled.
3838
#[derive(Clone, Debug, Default)]
3939
pub struct PypiPackageEnvironmentData {
40-
/// The extras enabled for the package. Note that the order doesn't matter.
41-
pub extras: HashSet<String>,
40+
/// The extras enabled for the package. Note that the order doesn't matter here but it does matter for serialization.
41+
pub extras: BTreeSet<String>,
4242
}
4343

4444
impl PartialOrd for PypiPackageData {

0 commit comments

Comments
 (0)