uv/crates/uv-resolver/src/fork_urls.rs
konsti a6dfd3953a
Handle universal vs. fork markers with ResolverMarkers (#5099)
* Use a dedicated `ResolverMarkers` check in the fork state. This is
better than the `MarkerTree::And(Vec::new())` check.
* Report the timing correct naming universal resolution instead of two
spaces around an empty string when there are no markers.
* On resolution error, show the split that we're in. I'm not sure how to
word this, since we're doing a universal resolution until we fork, so
the trace may contain information from requirements that are not part of
this fork.
2024-07-17 18:59:33 +02:00

65 lines
2.3 KiB
Rust

use std::collections::hash_map::Entry;
use rustc_hash::FxHashMap;
use distribution_types::Verbatim;
use pypi_types::VerbatimParsedUrl;
use uv_normalize::PackageName;
use crate::resolver::ResolverMarkers;
use crate::ResolveError;
/// See [`crate::resolver::SolveState`].
#[derive(Default, Debug, Clone)]
pub(crate) struct ForkUrls(FxHashMap<PackageName, VerbatimParsedUrl>);
impl ForkUrls {
/// Get the URL previously used for a package in this fork.
pub(crate) fn get(&self, package_name: &PackageName) -> Option<&VerbatimParsedUrl> {
self.0.get(package_name)
}
/// Whether we use a URL for this package.
pub(crate) fn contains_key(&self, package_name: &PackageName) -> bool {
self.0.contains_key(package_name)
}
/// Check that this is the only URL used for this package in this fork.
pub(crate) fn insert(
&mut self,
package_name: &PackageName,
url: &VerbatimParsedUrl,
fork_markers: &ResolverMarkers,
) -> Result<(), ResolveError> {
match self.0.entry(package_name.clone()) {
Entry::Occupied(previous) => {
if previous.get() != url {
let mut conflicting_url = vec![
previous.get().verbatim.verbatim().to_string(),
url.verbatim.verbatim().to_string(),
];
conflicting_url.sort();
return match fork_markers {
ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => {
Err(ResolveError::ConflictingUrlsUniversal(
package_name.clone(),
conflicting_url,
))
}
ResolverMarkers::Fork(fork_markers) => {
Err(ResolveError::ConflictingUrlsFork {
package_name: package_name.clone(),
urls: conflicting_url,
fork_markers: fork_markers.clone(),
})
}
};
}
}
Entry::Vacant(vacant) => {
vacant.insert(url.clone());
}
}
Ok(())
}
}