use std::borrow::Cow; use either::Either; use rustc_hash::FxHashMap; use pep508_rs::MarkerTree; use pypi_types::{Requirement, RequirementSource}; use uv_normalize::PackageName; /// A set of constraints for a set of requirements. #[derive(Debug, Default, Clone)] pub struct Constraints(FxHashMap>); impl Constraints { /// Create a new set of constraints from a set of requirements. pub fn from_requirements(requirements: impl Iterator) -> Self { let mut constraints: FxHashMap> = FxHashMap::default(); for requirement in requirements { // Skip empty constraints. if let RequirementSource::Registry { specifier, .. } = &requirement.source { if specifier.is_empty() { continue; } } constraints .entry(requirement.name.clone()) .or_default() .push(Requirement { // We add and apply constraints independent of their extras. extras: vec![], ..requirement }); } Self(constraints) } /// Return an iterator over all [`Requirement`]s in the constraint set. pub fn requirements(&self) -> impl Iterator { self.0.values().flat_map(|requirements| requirements.iter()) } /// Get the constraints for a package. pub fn get(&self, name: &PackageName) -> Option<&Vec> { self.0.get(name) } /// Apply the constraints to a set of requirements. /// /// NB: Change this method together with [`Overrides::apply`]. pub fn apply<'a>( &'a self, requirements: impl IntoIterator>, ) -> impl Iterator> { requirements.into_iter().flat_map(|requirement| { let Some(constraints) = self.get(&requirement.name) else { // Case 1: No constraint(s). return Either::Left(std::iter::once(requirement)); }; // ASSUMPTION: There is one `extra = "..."`, and it's either the only marker or part // of the main conjunction. let Some(extra_expression) = requirement.marker.top_level_extra() else { // Case 2: A non-optional dependency with constraint(s). return Either::Right(Either::Right( std::iter::once(requirement).chain(constraints.iter().map(Cow::Borrowed)), )); }; // Case 3: An optional dependency with constraint(s). // // When the original requirement is an optional dependency, the constraint(s) need to // be optional for the same extra, otherwise we activate extras that should be inactive. Either::Right(Either::Left(std::iter::once(requirement).chain( constraints.iter().cloned().map(move |constraint| { // Add the extra to the override marker. let mut joint_marker = MarkerTree::expression(extra_expression.clone()); joint_marker.and(constraint.marker.clone()); Cow::Owned(Requirement { marker: joint_marker, ..constraint }) }), ))) }) } }