1use std::path::Path;
2
3use rustc_hash::{FxHashMap, FxHashSet};
4
5use uv_cache::Refresh;
6use uv_cache_info::Timestamp;
7use uv_distribution_types::{Requirement, RequirementSource};
8use uv_normalize::{GroupName, PackageName};
9
10#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
12#[serde(rename_all = "kebab-case", deny_unknown_fields)]
13pub enum Reinstall {
14 #[default]
16 None,
17
18 All,
20
21 Packages(Vec<PackageName>, Vec<Box<Path>>),
23}
24
25impl Reinstall {
26 pub fn from_args(reinstall: Option<bool>, reinstall_package: Vec<PackageName>) -> Option<Self> {
28 match reinstall {
29 Some(true) => Some(Self::All),
30 Some(false) => Some(Self::None),
31 None if reinstall_package.is_empty() => None,
32 None => Some(Self::Packages(reinstall_package, Vec::new())),
33 }
34 }
35
36 pub fn is_none(&self) -> bool {
38 matches!(self, Self::None)
39 }
40
41 pub fn is_all(&self) -> bool {
43 matches!(self, Self::All)
44 }
45
46 pub fn contains_package(&self, package_name: &PackageName) -> bool {
48 match self {
49 Self::None => false,
50 Self::All => true,
51 Self::Packages(packages, ..) => packages.contains(package_name),
52 }
53 }
54
55 pub fn contains_path(&self, path: &Path) -> bool {
57 match self {
58 Self::None => false,
59 Self::All => true,
60 Self::Packages(.., paths) => paths
61 .iter()
62 .any(|target| same_file::is_same_file(path, target).unwrap_or(false)),
63 }
64 }
65
66 #[must_use]
68 pub fn combine(self, other: Self) -> Self {
69 match self {
70 Self::All | Self::None => self,
72 Self::Packages(self_packages, self_paths) => match other {
73 Self::All => other,
75 Self::None => Self::Packages(self_packages, self_paths),
77 Self::Packages(other_packages, other_paths) => {
79 let mut combined_packages = self_packages;
80 combined_packages.extend(other_packages);
81 let mut combined_paths = self_paths;
82 combined_paths.extend(other_paths);
83 Self::Packages(combined_packages, combined_paths)
84 }
85 },
86 }
87 }
88
89 #[must_use]
91 pub fn with_path(self, path: Box<Path>) -> Self {
92 match self {
93 Self::None => Self::Packages(vec![], vec![path]),
94 Self::All => Self::All,
95 Self::Packages(packages, mut paths) => {
96 paths.push(path);
97 Self::Packages(packages, paths)
98 }
99 }
100 }
101
102 #[must_use]
104 pub fn with_package(self, package_name: PackageName) -> Self {
105 match self {
106 Self::None => Self::Packages(vec![package_name], vec![]),
107 Self::All => Self::All,
108 Self::Packages(mut packages, paths) => {
109 packages.push(package_name);
110 Self::Packages(packages, paths)
111 }
112 }
113 }
114
115 pub fn package(package_name: PackageName) -> Self {
117 Self::Packages(vec![package_name], vec![])
118 }
119}
120
121impl From<Reinstall> for Refresh {
123 fn from(value: Reinstall) -> Self {
124 match value {
125 Reinstall::None => Self::None(Timestamp::now()),
126 Reinstall::All => Self::All(Timestamp::now()),
127 Reinstall::Packages(packages, paths) => {
128 Self::Packages(packages, paths, Timestamp::now())
129 }
130 }
131 }
132}
133
134#[derive(Debug, Default, Clone)]
136pub enum UpgradeStrategy {
137 #[default]
139 None,
140
141 All,
143
144 Some(FxHashSet<PackageName>, FxHashSet<GroupName>),
146}
147
148#[derive(Debug, Default, Clone)]
150pub struct Upgrade {
151 strategy: UpgradeStrategy,
153
154 constraints: FxHashMap<PackageName, Vec<Requirement>>,
156}
157
158impl Upgrade {
159 pub fn none() -> Self {
161 Self {
162 strategy: UpgradeStrategy::None,
163 constraints: FxHashMap::default(),
164 }
165 }
166
167 pub fn all() -> Self {
169 Self {
170 strategy: UpgradeStrategy::All,
171 constraints: FxHashMap::default(),
172 }
173 }
174
175 pub fn from_args(
177 upgrade: Option<bool>,
178 upgrade_package: Vec<Requirement>,
179 upgrade_group: Vec<GroupName>,
180 ) -> Option<Self> {
181 let groups: FxHashSet<GroupName> = upgrade_group.into_iter().collect();
182
183 let strategy = match upgrade {
184 Some(true) => UpgradeStrategy::All,
185 Some(false) => {
186 if upgrade_package.is_empty() && groups.is_empty() {
187 return Some(Self::none());
188 }
189 let packages = upgrade_package.iter().map(|req| req.name.clone()).collect();
192 UpgradeStrategy::Some(packages, groups)
193 }
194 None => {
195 if upgrade_package.is_empty() && groups.is_empty() {
196 return None;
197 }
198 let packages = upgrade_package.iter().map(|req| req.name.clone()).collect();
199 UpgradeStrategy::Some(packages, groups)
200 }
201 };
202
203 let mut constraints: FxHashMap<PackageName, Vec<Requirement>> = FxHashMap::default();
204 for requirement in upgrade_package {
205 if let RequirementSource::Registry { specifier, .. } = &requirement.source {
207 if specifier.is_empty() {
208 continue;
209 }
210 }
211 constraints
212 .entry(requirement.name.clone())
213 .or_default()
214 .push(requirement);
215 }
216
217 Some(Self {
218 strategy,
219 constraints,
220 })
221 }
222
223 pub fn package(package_name: PackageName) -> Self {
225 let mut packages = FxHashSet::default();
226 packages.insert(package_name);
227 Self {
228 strategy: UpgradeStrategy::Some(packages, FxHashSet::default()),
229 constraints: FxHashMap::default(),
230 }
231 }
232
233 pub fn is_none(&self) -> bool {
235 matches!(self.strategy, UpgradeStrategy::None)
236 }
237
238 pub fn is_all(&self) -> bool {
240 matches!(self.strategy, UpgradeStrategy::All)
241 }
242
243 pub fn constraints(&self) -> impl Iterator<Item = &Requirement> {
247 self.constraints
248 .values()
249 .flat_map(|requirements| requirements.iter())
250 }
251
252 pub fn packages(&self) -> Option<&FxHashSet<PackageName>> {
254 match &self.strategy {
255 UpgradeStrategy::Some(packages, _) => Some(packages),
256 _ => None,
257 }
258 }
259
260 pub fn groups(&self) -> Option<&FxHashSet<GroupName>> {
262 match &self.strategy {
263 UpgradeStrategy::Some(_, groups) if !groups.is_empty() => Some(groups),
264 _ => None,
265 }
266 }
267
268 #[must_use]
270 pub fn combine(self, other: Self) -> Self {
271 let strategy = match (self.strategy, other.strategy) {
274 (_, UpgradeStrategy::All) => UpgradeStrategy::All,
275 (_, UpgradeStrategy::None) => UpgradeStrategy::None,
276 (
277 UpgradeStrategy::Some(mut self_packages, mut self_groups),
278 UpgradeStrategy::Some(other_packages, other_groups),
279 ) => {
280 self_packages.extend(other_packages);
281 self_groups.extend(other_groups);
282 UpgradeStrategy::Some(self_packages, self_groups)
283 }
284 (_, UpgradeStrategy::Some(packages, groups)) => UpgradeStrategy::Some(packages, groups),
285 };
286
287 let mut combined_constraints = self.constraints.clone();
289 for (package, requirements) in other.constraints {
290 combined_constraints
291 .entry(package)
292 .or_default()
293 .extend(requirements);
294 }
295
296 Self {
297 strategy,
298 constraints: combined_constraints,
299 }
300 }
301}
302
303impl From<Upgrade> for Refresh {
305 fn from(value: Upgrade) -> Self {
306 match value.strategy {
307 UpgradeStrategy::None => Self::None(Timestamp::now()),
308 UpgradeStrategy::All => Self::All(Timestamp::now()),
309 UpgradeStrategy::Some(packages, _) => Self::Packages(
310 packages.into_iter().collect::<Vec<_>>(),
311 Vec::new(),
312 Timestamp::now(),
313 ),
314 }
315 }
316}
317
318#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
320#[serde(rename_all = "kebab-case", deny_unknown_fields)]
321#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
322pub enum BuildIsolation {
323 #[default]
325 Isolate,
326
327 Shared,
329
330 SharedPackage(Vec<PackageName>),
332}
333
334impl BuildIsolation {
335 pub fn from_args(
337 no_build_isolation: Option<bool>,
338 no_build_isolation_package: Vec<PackageName>,
339 ) -> Option<Self> {
340 match no_build_isolation {
341 Some(true) => Some(Self::Shared),
342 Some(false) => Some(Self::Isolate),
343 None if no_build_isolation_package.is_empty() => None,
344 None => Some(Self::SharedPackage(no_build_isolation_package)),
345 }
346 }
347
348 #[must_use]
350 pub fn combine(self, other: Self) -> Self {
351 match self {
352 Self::Isolate | Self::Shared => self,
354 Self::SharedPackage(self_packages) => match other {
355 Self::Shared => other,
357 Self::Isolate => Self::SharedPackage(self_packages),
359 Self::SharedPackage(other_packages) => {
361 let mut combined = self_packages;
362 combined.extend(other_packages);
363 Self::SharedPackage(combined)
364 }
365 },
366 }
367 }
368}