-
-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement VersionReq union, intersection, and simplification #223
Conversation
/cc @Laegluin |
versions.zip is a zip file containing a file with all 12665 |
self | ||
} | ||
|
||
fn simplify(&mut self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not pub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I don't think it should be actually. I think simplify
should be performed internally when it makes sense to do so (like after a call to intersection
). It's not clear there's value in users being able to call it randomly.
I ran some smoke tests over that data set. It found too many for me to summarize. The ugly hack of code I threw together was: Cargo.toml crates-index={ git = "https://github.com/frewsxcv/rust-crates-index"}
semver = { git = "https://github.com/jonhoo/semver", branch = "merge-ranges" } main.rs fn main() {
let index = crates_index::BareIndex::new_cargo_default();
let index = index.open_or_clone().unwrap();
let mut req_set = BTreeSet::new();
let mut ver_set = BTreeSet::new();
for crt in index.crates() {
for ver in crt.versions() {
if let Ok(v) = semver::Version::parse(ver.version()) {
ver_set.insert(v);
} else {
dbg![ver.version()];
}
for dep in ver.dependencies() {
if let Ok(r) = semver::VersionReq::parse(dep.requirement()) {
req_set.insert(r);
} else {
dbg![dep.requirement()];
}
}
}
}
for (i, req1) in req_set.iter().rev().enumerate() {
dbg!((req1.to_string(), i, i as f32 / req_set.len() as f32));
// let mut simp = req1.clone();
// simp.simplify();
// for ver in &ver_set {
// if req1.matches(ver) != simp.matches(ver) {
// dbg![(req1.to_string(), simp.to_string(), ver.to_string())];
// }
// }
for req2 in req_set.range(req1..) {
let int1 = req1.clone().intersection(req2);
let int2 = req2.clone().intersection(req1);
let uni1 = req1.clone().union(req2);
let uni2 = req2.clone().union(req1);
for ver in &ver_set {
let int = req1.matches(ver) && req2.matches(ver);
if int != int1.matches(ver) {
dbg![(
req1.to_string(),
req2.to_string(),
ver.to_string(),
int1.to_string(),
int
)];
}
if int != int2.matches(ver) {
dbg![(
req1.to_string(),
req2.to_string(),
ver.to_string(),
int2.to_string(),
int
)];
}
let uni = req1.matches(ver) || req2.matches(ver);
if uni != uni1.matches(ver) {
dbg![(
req1.to_string(),
req2.to_string(),
ver.to_string(),
uni1.to_string(),
uni
)];
}
if uni != uni2.matches(ver) {
dbg![(
req1.to_string(),
req2.to_string(),
ver.to_string(),
uni2.to_string(),
uni
)];
}
}
}
}
} |
Oh, nice! Could you share some of the problem cases you came across? |
Here is the zip file of the first ~580k cases cleaned into a csv. |
@Eh2406 Hmm, the first couple of cases seem suspicious if I'm reading them right:
The intersection of For the union case: Specifically, what seems to be happening is that I think this is essentially the same as #172, and if I understand Steve correctly there, pre-release versions should only be selected if the major-minor-patch the pre-release is for is explicitly mentioned in the version requirements. That is, |
Indeed your last paragraph describes it well. I think this is going to be a tricky problem. To belabor the examples:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR!
I would prefer if this logic were provided in a separate crate for now. I am not necessarily opposed to bringing intersection and simplification into the semver
crate eventually, but as you found in #223 (comment) it's going to be a lot more complicated than this PR, and I don't feel that here is going to be the best place to iterate on it.
This PR adds
VersionReq::union
andVersionReq::intersection
, and thus fixes #170.Since the naive implementation tends to produce fairly verbose bounds, it also adds a method that "simplifies" a
VersionReq
. It does so by first reducing eachRange
to the smallest range that matches all the range's predicates, and then merging overlapping ranges. For example, theVersionReq
:which is really
is simplified to
I did not add automatic simplification after a call to
parse
, but that might be worthwhile.I would love for this to have significantly more test-cases, so if you can think of any, please propose them here.
Also, if you have an idea for how to avoid panicking when trying to combine
VersionReq
s with differentcompat
s, please speak up!