Add a `PubGrubRequirement` struct (#3553)

## Summary

Formalize some of the patterns in here. No behavior changes, just moving
this method onto a struct.
This commit is contained in:
Charlie Marsh 2024-05-13 12:59:10 -04:00 committed by GitHub
parent eb8e733790
commit 10ec48299e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 132 additions and 99 deletions

View File

@ -91,14 +91,16 @@ fn add_requirements(
} }
// Add the package, plus any extra variants. // Add the package, plus any extra variants.
for result in std::iter::once(to_pubgrub(requirement, None, urls, locals)).chain( for result in std::iter::once(PubGrubRequirement::from_requirement(
requirement requirement,
.extras None,
.clone() urls,
.into_iter() locals,
.map(|extra| to_pubgrub(requirement, Some(extra), urls, locals)), ))
) { .chain(requirement.extras.clone().into_iter().map(|extra| {
let (package, version) = result?; PubGrubRequirement::from_requirement(requirement, Some(extra), urls, locals)
})) {
let PubGrubRequirement { package, version } = result?;
match &package { match &package {
PubGrubPackage::Package(name, ..) => { PubGrubPackage::Package(name, ..) => {
@ -152,7 +154,8 @@ fn add_requirements(
} }
// Add the package. // Add the package.
let (package, version) = to_pubgrub(constraint, None, urls, locals)?; let PubGrubRequirement { package, version } =
PubGrubRequirement::from_constraint(constraint, urls, locals)?;
// Ignore self-dependencies. // Ignore self-dependencies.
if let PubGrubPackage::Package(name, ..) = &package { if let PubGrubPackage::Package(name, ..) = &package {
@ -178,106 +181,136 @@ impl From<PubGrubDependencies> for Vec<(PubGrubPackage, Range<Version>)> {
} }
} }
/// Convert a [`Requirement`] to a PubGrub-compatible package and range. /// A PubGrub-compatible package and version range.
fn to_pubgrub( #[derive(Debug, Clone)]
requirement: &Requirement, struct PubGrubRequirement {
extra: Option<ExtraName>, package: PubGrubPackage,
urls: &Urls, version: Range<Version>,
locals: &Locals, }
) -> Result<(PubGrubPackage, Range<Version>), ResolveError> {
match &requirement.source {
RequirementSource::Registry { specifier, .. } => {
// TODO(konsti): We're currently losing the index information here, but we need
// either pass it to `PubGrubPackage` or the `ResolverProvider` beforehand.
// If the specifier is an exact version, and the user requested a local version that's
// more precise than the specifier, use the local version instead.
let version = if let Some(expected) = locals.get(&requirement.name) {
specifier
.iter()
.map(|specifier| {
Locals::map(expected, specifier)
.map_err(ResolveError::InvalidVersion)
.and_then(|specifier| PubGrubSpecifier::try_from(&specifier))
})
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?
} else {
specifier
.iter()
.map(PubGrubSpecifier::try_from)
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?
};
Ok(( impl PubGrubRequirement {
PubGrubPackage::from_package(requirement.name.clone(), extra, urls), /// Convert a [`Requirement`] to a PubGrub-compatible package and range.
version, pub(crate) fn from_requirement(
)) requirement: &Requirement,
} extra: Option<ExtraName>,
RequirementSource::Url { url, .. } => { urls: &Urls,
let Some(expected) = urls.get(&requirement.name) else { locals: &Locals,
return Err(ResolveError::DisallowedUrl( ) -> Result<Self, ResolveError> {
requirement.name.clone(), match &requirement.source {
url.to_string(), RequirementSource::Registry { specifier, .. } => {
)); // TODO(konsti): We're currently losing the index information here, but we need
}; // either pass it to `PubGrubPackage` or the `ResolverProvider` beforehand.
// If the specifier is an exact version, and the user requested a local version that's
// more precise than the specifier, use the local version instead.
let version = if let Some(expected) = locals.get(&requirement.name) {
specifier
.iter()
.map(|specifier| {
Locals::map(expected, specifier)
.map_err(ResolveError::InvalidVersion)
.and_then(|specifier| PubGrubSpecifier::try_from(&specifier))
})
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?
} else {
specifier
.iter()
.map(PubGrubSpecifier::try_from)
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?
};
if !Urls::is_allowed(expected, url) { Ok(Self {
return Err(ResolveError::ConflictingUrlsTransitive( package: PubGrubPackage::from_package(requirement.name.clone(), extra, urls),
requirement.name.clone(), version,
expected.verbatim().to_string(), })
url.verbatim().to_string(),
));
} }
RequirementSource::Url { url, .. } => {
let Some(expected) = urls.get(&requirement.name) else {
return Err(ResolveError::DisallowedUrl(
requirement.name.clone(),
url.to_string(),
));
};
Ok(( if !Urls::is_allowed(expected, url) {
PubGrubPackage::Package(requirement.name.clone(), extra, Some(expected.clone())), return Err(ResolveError::ConflictingUrlsTransitive(
Range::full(), requirement.name.clone(),
)) expected.verbatim().to_string(),
} url.verbatim().to_string(),
RequirementSource::Git { url, .. } => { ));
let Some(expected) = urls.get(&requirement.name) else { }
return Err(ResolveError::DisallowedUrl(
requirement.name.clone(),
url.to_string(),
));
};
if !Urls::is_allowed(expected, url) { Ok(Self {
return Err(ResolveError::ConflictingUrlsTransitive( package: PubGrubPackage::Package(
requirement.name.clone(), requirement.name.clone(),
expected.verbatim().to_string(), extra,
url.verbatim().to_string(), Some(expected.clone()),
)); ),
version: Range::full(),
})
} }
RequirementSource::Git { url, .. } => {
let Some(expected) = urls.get(&requirement.name) else {
return Err(ResolveError::DisallowedUrl(
requirement.name.clone(),
url.to_string(),
));
};
Ok(( if !Urls::is_allowed(expected, url) {
PubGrubPackage::Package(requirement.name.clone(), extra, Some(expected.clone())), return Err(ResolveError::ConflictingUrlsTransitive(
Range::full(), requirement.name.clone(),
)) expected.verbatim().to_string(),
} url.verbatim().to_string(),
RequirementSource::Path { url, .. } => { ));
let Some(expected) = urls.get(&requirement.name) else { }
return Err(ResolveError::DisallowedUrl(
requirement.name.clone(),
url.to_string(),
));
};
if !Urls::is_allowed(expected, url) { Ok(Self {
return Err(ResolveError::ConflictingUrlsTransitive( package: PubGrubPackage::Package(
requirement.name.clone(), requirement.name.clone(),
expected.verbatim().to_string(), extra,
url.verbatim().to_string(), Some(expected.clone()),
)); ),
version: Range::full(),
})
} }
RequirementSource::Path { url, .. } => {
let Some(expected) = urls.get(&requirement.name) else {
return Err(ResolveError::DisallowedUrl(
requirement.name.clone(),
url.to_string(),
));
};
Ok(( if !Urls::is_allowed(expected, url) {
PubGrubPackage::Package(requirement.name.clone(), extra, Some(expected.clone())), return Err(ResolveError::ConflictingUrlsTransitive(
Range::full(), requirement.name.clone(),
)) expected.verbatim().to_string(),
url.verbatim().to_string(),
));
}
Ok(Self {
package: PubGrubPackage::Package(
requirement.name.clone(),
extra,
Some(expected.clone()),
),
version: Range::full(),
})
}
} }
} }
/// Convert a constraint to a PubGrub-compatible package and range.
pub(crate) fn from_constraint(
constraint: &Requirement,
urls: &Urls,
locals: &Locals,
) -> Result<Self, ResolveError> {
Self::from_requirement(constraint, None, urls, locals)
}
} }