Skip to main content

uv_configuration/
install_options.rs

1use std::collections::BTreeSet;
2
3use tracing::debug;
4
5use uv_normalize::PackageName;
6
7/// Minimal view of a package used to apply install filters.
8#[derive(Debug, Clone, Copy)]
9pub struct InstallTarget<'a> {
10    /// The package name.
11    pub name: &'a PackageName,
12    /// Whether the package refers to a local source (path, directory, editable, etc.).
13    pub is_local: bool,
14}
15
16#[derive(Debug, Clone, Default)]
17pub struct InstallOptions {
18    /// Omit the project itself from the resolution.
19    no_install_project: bool,
20    /// Include only the project itself in the resolution.
21    only_install_project: bool,
22    /// Omit all workspace members (including the project itself) from the resolution.
23    no_install_workspace: bool,
24    /// Include only workspace members (including the project itself) in the resolution.
25    only_install_workspace: bool,
26    /// Omit all local packages from the resolution.
27    no_install_local: bool,
28    /// Include only local packages in the resolution.
29    only_install_local: bool,
30    /// Omit the specified packages from the resolution.
31    no_install_package: Vec<PackageName>,
32    /// Include only the specified packages in the resolution.
33    only_install_package: Vec<PackageName>,
34}
35
36impl InstallOptions {
37    #[expect(clippy::fn_params_excessive_bools)]
38    pub fn new(
39        no_install_project: bool,
40        only_install_project: bool,
41        no_install_workspace: bool,
42        only_install_workspace: bool,
43        no_install_local: bool,
44        only_install_local: bool,
45        no_install_package: Vec<PackageName>,
46        only_install_package: Vec<PackageName>,
47    ) -> Self {
48        Self {
49            no_install_project,
50            only_install_project,
51            no_install_workspace,
52            only_install_workspace,
53            no_install_local,
54            only_install_local,
55            no_install_package,
56            only_install_package,
57        }
58    }
59
60    /// Returns `true` if a package passes the install filters.
61    pub fn include_package(
62        &self,
63        target: InstallTarget<'_>,
64        project_name: Option<&PackageName>,
65        members: &BTreeSet<PackageName>,
66    ) -> bool {
67        let package_name = target.name;
68
69        // If `--only-install-package` is set, only include specified packages.
70        if !self.only_install_package.is_empty() {
71            if self.only_install_package.contains(package_name) {
72                return true;
73            }
74            debug!("Omitting `{package_name}` from resolution due to `--only-install-package`");
75            return false;
76        }
77
78        // If `--only-install-local` is set, only include local packages.
79        if self.only_install_local {
80            if target.is_local {
81                return true;
82            }
83            debug!("Omitting `{package_name}` from resolution due to `--only-install-local`");
84            return false;
85        }
86
87        // If `--only-install-workspace` is set, only include the project and workspace members.
88        if self.only_install_workspace {
89            // Check if it's the project itself
90            if let Some(project_name) = project_name
91                && package_name == project_name
92            {
93                return true;
94            }
95
96            // Check if it's a workspace member
97            if members.contains(package_name) {
98                return true;
99            }
100
101            // Otherwise, exclude it
102            debug!("Omitting `{package_name}` from resolution due to `--only-install-workspace`");
103            return false;
104        }
105
106        // If `--only-install-project` is set, only include the project itself.
107        if self.only_install_project {
108            if let Some(project_name) = project_name
109                && package_name == project_name
110            {
111                return true;
112            }
113            debug!("Omitting `{package_name}` from resolution due to `--only-install-project`");
114            return false;
115        }
116
117        // If `--no-install-project` is set, remove the project itself.
118        if self.no_install_project
119            && let Some(project_name) = project_name
120            && package_name == project_name
121        {
122            debug!("Omitting `{package_name}` from resolution due to `--no-install-project`");
123            return false;
124        }
125
126        // If `--no-install-workspace` is set, remove the project and any workspace members.
127        if self.no_install_workspace {
128            // In some cases, the project root might be omitted from the list of workspace members
129            // encoded in the lockfile. (But we already checked this above if `--no-install-project`
130            // is set.)
131            if !self.no_install_project
132                && let Some(project_name) = project_name
133                && package_name == project_name
134            {
135                debug!("Omitting `{package_name}` from resolution due to `--no-install-workspace`");
136                return false;
137            }
138
139            if members.contains(package_name) {
140                debug!("Omitting `{package_name}` from resolution due to `--no-install-workspace`");
141                return false;
142            }
143        }
144
145        // If `--no-install-local` is set, remove local packages.
146        if self.no_install_local {
147            if target.is_local {
148                debug!("Omitting `{package_name}` from resolution due to `--no-install-local`");
149                return false;
150            }
151        }
152
153        // If `--no-install-package` is provided, remove the requested packages.
154        if self.no_install_package.contains(package_name) {
155            debug!("Omitting `{package_name}` from resolution due to `--no-install-package`");
156            return false;
157        }
158
159        true
160    }
161}