Skip to main content

uv_configuration/
dependency_modifier.rs

1use std::borrow::Cow;
2
3use uv_distribution_types::{Requirement, ResolutionRecorder};
4use uv_normalize::PackageName;
5use uv_pep440::Version;
6
7use crate::{Excludes, Overrides};
8
9/// Overrides and exclusions to apply to dependency requirements.
10#[derive(Debug, Default, Clone)]
11pub struct DependencyModifiers {
12    overrides: Overrides,
13    excludes: Excludes,
14}
15
16/// The package scope in which to apply dependency modifiers.
17#[derive(Debug, Clone, Copy)]
18pub enum DependencyModifierScope<'a> {
19    /// Apply global overrides and exclusions.
20    Global,
21    /// Apply global and package-scoped overrides and exclusions to regular package metadata.
22    Package(&'a PackageName, &'a Version),
23    /// Apply global modifiers and package-scoped exclusions to a dependency group.
24    DependencyGroup(&'a PackageName, &'a Version),
25}
26
27impl DependencyModifiers {
28    /// Record which settings are consulted while resolving runtime dependencies.
29    #[must_use]
30    pub fn with_recorder(mut self, recorder: Option<ResolutionRecorder>) -> Self {
31        self.overrides = self.overrides.with_recorder(recorder.clone());
32        self.excludes = self.excludes.with_recorder(recorder);
33        self
34    }
35
36    pub fn new(overrides: Overrides, excludes: Excludes) -> Self {
37        Self {
38            overrides,
39            excludes,
40        }
41    }
42
43    /// Return whether any modifiers are scoped to this package.
44    pub fn has_scoped_package(&self, package: &PackageName) -> bool {
45        self.overrides.has_scoped_package(package) || self.excludes.has_scoped_package(package)
46    }
47
48    /// Return all global override requirements that are not excluded.
49    pub fn global_overrides(&self) -> impl Iterator<Item = &Requirement> {
50        self.overrides
51            .global_requirements()
52            .filter(|requirement| !self.excludes.contains(&requirement.name))
53    }
54
55    /// Return all scoped override requirements that are not excluded in their scope.
56    pub fn scoped_overrides(&self) -> impl Iterator<Item = &Requirement> {
57        self.overrides
58            .scoped_requirements()
59            .filter(|(package, version, requirement)| {
60                !self.excludes.contains_for_scope(
61                    &self.overrides,
62                    package,
63                    *version,
64                    &requirement.name,
65                )
66            })
67            .map(|(_, _, requirement)| requirement)
68    }
69
70    /// Return the scoped override requirements that apply to this package version.
71    pub fn scoped_overrides_for(
72        &self,
73        package: &PackageName,
74        version: &Version,
75    ) -> impl Iterator<Item = &Requirement> {
76        self.overrides
77            .scoped_requirements_for(package, version)
78            .filter(|requirement| {
79                !self
80                    .excludes
81                    .contains_for(package, version, &requirement.name)
82            })
83    }
84
85    /// Return whether a dependency is globally excluded.
86    pub fn is_excluded(&self, dependency: &PackageName) -> bool {
87        self.excludes.contains(dependency)
88    }
89
90    /// Return whether a dependency is excluded from a specific package version.
91    pub fn is_excluded_for(
92        &self,
93        package: &PackageName,
94        version: &Version,
95        dependency: &PackageName,
96    ) -> bool {
97        self.excludes.contains_for(package, version, dependency)
98    }
99
100    /// Apply dependency overrides and exclusions in the given scope.
101    pub fn apply<'a, 'scope, I>(
102        &'a self,
103        scope: DependencyModifierScope<'scope>,
104        requirements: I,
105    ) -> impl Iterator<Item = Cow<'a, Requirement>> + use<'a, 'scope, I>
106    where
107        I: IntoIterator<Item = &'a Requirement>,
108    {
109        let (overrides, excludes) = match scope {
110            DependencyModifierScope::Global => (None, None),
111            DependencyModifierScope::Package(package, version) => {
112                (Some((package, version)), Some((package, version)))
113            }
114            DependencyModifierScope::DependencyGroup(package, version) => {
115                (None, Some((package, version)))
116            }
117        };
118        self.overrides
119            .apply_for_package(overrides, requirements)
120            .filter(move |requirement| {
121                !self
122                    .excludes
123                    .contains_for_package(excludes, &requirement.name)
124            })
125    }
126}