1use anstream::eprintln;
2
3use uv_cache::Refresh;
4use uv_configuration::{BuildIsolation, Reinstall, Upgrade};
5use uv_distribution_types::{ConfigSettings, PackageConfigSettings, Requirement};
6use uv_resolver::{ExcludeNewer, ExcludeNewerPackage, PrereleaseMode};
7use uv_settings::{Combine, PipOptions, ResolverInstallerOptions, ResolverOptions};
8use uv_warnings::owo_colors::OwoColorize;
9
10use crate::{
11 BuildOptionsArgs, FetchArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
12 ResolverInstallerArgs,
13};
14
15pub fn flag(yes: bool, no: bool, name: &str) -> Option<bool> {
17 match (yes, no) {
18 (true, false) => Some(true),
19 (false, true) => Some(false),
20 (false, false) => None,
21 (..) => {
22 eprintln!(
23 "{}{} `{}` and `{}` cannot be used together. \
24 Boolean flags on different levels are currently not supported \
25 (https://github.com/clap-rs/clap/issues/6049)",
26 "error".bold().red(),
27 ":".bold(),
28 format!("--{name}").green(),
29 format!("--no-{name}").green(),
30 );
31 #[allow(clippy::exit)]
33 {
34 std::process::exit(2);
35 }
36 }
37 }
38}
39
40impl From<RefreshArgs> for Refresh {
41 fn from(value: RefreshArgs) -> Self {
42 let RefreshArgs {
43 refresh,
44 no_refresh,
45 refresh_package,
46 } = value;
47
48 Self::from_args(flag(refresh, no_refresh, "no-refresh"), refresh_package)
49 }
50}
51
52impl From<ResolverArgs> for PipOptions {
53 fn from(args: ResolverArgs) -> Self {
54 let ResolverArgs {
55 index_args,
56 upgrade,
57 no_upgrade,
58 upgrade_package,
59 index_strategy,
60 keyring_provider,
61 resolution,
62 prerelease,
63 pre,
64 fork_strategy,
65 config_setting,
66 config_settings_package,
67 no_build_isolation,
68 no_build_isolation_package,
69 build_isolation,
70 exclude_newer,
71 link_mode,
72 no_sources,
73 exclude_newer_package,
74 } = args;
75
76 Self {
77 upgrade: flag(upgrade, no_upgrade, "no-upgrade"),
78 upgrade_package: Some(upgrade_package),
79 index_strategy,
80 keyring_provider,
81 resolution,
82 fork_strategy,
83 prerelease: if pre {
84 Some(PrereleaseMode::Allow)
85 } else {
86 prerelease
87 },
88 config_settings: config_setting
89 .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
90 config_settings_package: config_settings_package.map(|config_settings| {
91 config_settings
92 .into_iter()
93 .collect::<PackageConfigSettings>()
94 }),
95 no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
96 no_build_isolation_package: Some(no_build_isolation_package),
97 exclude_newer,
98 exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
99 link_mode,
100 no_sources: if no_sources { Some(true) } else { None },
101 ..Self::from(index_args)
102 }
103 }
104}
105
106impl From<InstallerArgs> for PipOptions {
107 fn from(args: InstallerArgs) -> Self {
108 let InstallerArgs {
109 index_args,
110 reinstall,
111 no_reinstall,
112 reinstall_package,
113 index_strategy,
114 keyring_provider,
115 config_setting,
116 config_settings_package,
117 no_build_isolation,
118 build_isolation,
119 exclude_newer,
120 link_mode,
121 compile_bytecode,
122 no_compile_bytecode,
123 no_sources,
124 exclude_newer_package,
125 } = args;
126
127 Self {
128 reinstall: flag(reinstall, no_reinstall, "reinstall"),
129 reinstall_package: Some(reinstall_package),
130 index_strategy,
131 keyring_provider,
132 config_settings: config_setting
133 .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
134 config_settings_package: config_settings_package.map(|config_settings| {
135 config_settings
136 .into_iter()
137 .collect::<PackageConfigSettings>()
138 }),
139 no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
140 exclude_newer,
141 exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
142 link_mode,
143 compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
144 no_sources: if no_sources { Some(true) } else { None },
145 ..Self::from(index_args)
146 }
147 }
148}
149
150impl From<ResolverInstallerArgs> for PipOptions {
151 fn from(args: ResolverInstallerArgs) -> Self {
152 let ResolverInstallerArgs {
153 index_args,
154 upgrade,
155 no_upgrade,
156 upgrade_package,
157 reinstall,
158 no_reinstall,
159 reinstall_package,
160 index_strategy,
161 keyring_provider,
162 resolution,
163 prerelease,
164 pre,
165 fork_strategy,
166 config_setting,
167 config_settings_package,
168 no_build_isolation,
169 no_build_isolation_package,
170 build_isolation,
171 exclude_newer,
172 link_mode,
173 compile_bytecode,
174 no_compile_bytecode,
175 no_sources,
176 exclude_newer_package,
177 } = args;
178
179 Self {
180 upgrade: flag(upgrade, no_upgrade, "upgrade"),
181 upgrade_package: Some(upgrade_package),
182 reinstall: flag(reinstall, no_reinstall, "reinstall"),
183 reinstall_package: Some(reinstall_package),
184 index_strategy,
185 keyring_provider,
186 resolution,
187 prerelease: if pre {
188 Some(PrereleaseMode::Allow)
189 } else {
190 prerelease
191 },
192 fork_strategy,
193 config_settings: config_setting
194 .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
195 config_settings_package: config_settings_package.map(|config_settings| {
196 config_settings
197 .into_iter()
198 .collect::<PackageConfigSettings>()
199 }),
200 no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
201 no_build_isolation_package: Some(no_build_isolation_package),
202 exclude_newer,
203 exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
204 link_mode,
205 compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
206 no_sources: if no_sources { Some(true) } else { None },
207 ..Self::from(index_args)
208 }
209 }
210}
211
212impl From<FetchArgs> for PipOptions {
213 fn from(args: FetchArgs) -> Self {
214 let FetchArgs {
215 index_args,
216 index_strategy,
217 keyring_provider,
218 exclude_newer,
219 } = args;
220
221 Self {
222 index_strategy,
223 keyring_provider,
224 exclude_newer,
225 ..Self::from(index_args)
226 }
227 }
228}
229
230impl From<IndexArgs> for PipOptions {
231 fn from(args: IndexArgs) -> Self {
232 let IndexArgs {
233 default_index,
234 index,
235 index_url,
236 extra_index_url,
237 no_index,
238 find_links,
239 } = args;
240
241 Self {
242 index: default_index
243 .and_then(Maybe::into_option)
244 .map(|default_index| vec![default_index])
245 .combine(index.map(|index| {
246 index
247 .iter()
248 .flat_map(std::clone::Clone::clone)
249 .filter_map(Maybe::into_option)
250 .collect()
251 })),
252 index_url: index_url.and_then(Maybe::into_option),
253 extra_index_url: extra_index_url.map(|extra_index_urls| {
254 extra_index_urls
255 .into_iter()
256 .filter_map(Maybe::into_option)
257 .collect()
258 }),
259 no_index: if no_index { Some(true) } else { None },
260 find_links: find_links.map(|find_links| {
261 find_links
262 .into_iter()
263 .filter_map(Maybe::into_option)
264 .collect()
265 }),
266 ..Self::default()
267 }
268 }
269}
270
271pub fn resolver_options(
273 resolver_args: ResolverArgs,
274 build_args: BuildOptionsArgs,
275) -> ResolverOptions {
276 let ResolverArgs {
277 index_args,
278 upgrade,
279 no_upgrade,
280 upgrade_package,
281 index_strategy,
282 keyring_provider,
283 resolution,
284 prerelease,
285 pre,
286 fork_strategy,
287 config_setting,
288 config_settings_package,
289 no_build_isolation,
290 no_build_isolation_package,
291 build_isolation,
292 exclude_newer,
293 link_mode,
294 no_sources,
295 exclude_newer_package,
296 } = resolver_args;
297
298 let BuildOptionsArgs {
299 no_build,
300 build,
301 no_build_package,
302 no_binary,
303 binary,
304 no_binary_package,
305 } = build_args;
306
307 ResolverOptions {
308 index: index_args
309 .default_index
310 .and_then(Maybe::into_option)
311 .map(|default_index| vec![default_index])
312 .combine(index_args.index.map(|index| {
313 index
314 .into_iter()
315 .flat_map(|v| v.clone())
316 .filter_map(Maybe::into_option)
317 .collect()
318 })),
319 index_url: index_args.index_url.and_then(Maybe::into_option),
320 extra_index_url: index_args.extra_index_url.map(|extra_index_url| {
321 extra_index_url
322 .into_iter()
323 .filter_map(Maybe::into_option)
324 .collect()
325 }),
326 no_index: if index_args.no_index {
327 Some(true)
328 } else {
329 None
330 },
331 find_links: index_args.find_links.map(|find_links| {
332 find_links
333 .into_iter()
334 .filter_map(Maybe::into_option)
335 .collect()
336 }),
337 upgrade: Upgrade::from_args(
338 flag(upgrade, no_upgrade, "no-upgrade"),
339 upgrade_package.into_iter().map(Requirement::from).collect(),
340 ),
341 index_strategy,
342 keyring_provider,
343 resolution,
344 prerelease: if pre {
345 Some(PrereleaseMode::Allow)
346 } else {
347 prerelease
348 },
349 fork_strategy,
350 dependency_metadata: None,
351 config_settings: config_setting
352 .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
353 config_settings_package: config_settings_package.map(|config_settings| {
354 config_settings
355 .into_iter()
356 .collect::<PackageConfigSettings>()
357 }),
358 build_isolation: BuildIsolation::from_args(
359 flag(no_build_isolation, build_isolation, "build-isolation"),
360 no_build_isolation_package,
361 ),
362 extra_build_dependencies: None,
363 extra_build_variables: None,
364 exclude_newer: ExcludeNewer::from_args(
365 exclude_newer,
366 exclude_newer_package.unwrap_or_default(),
367 ),
368 link_mode,
369 no_build: flag(no_build, build, "build"),
370 no_build_package: Some(no_build_package),
371 no_binary: flag(no_binary, binary, "binary"),
372 no_binary_package: Some(no_binary_package),
373 no_sources: if no_sources { Some(true) } else { None },
374 }
375}
376
377pub fn resolver_installer_options(
379 resolver_installer_args: ResolverInstallerArgs,
380 build_args: BuildOptionsArgs,
381) -> ResolverInstallerOptions {
382 let ResolverInstallerArgs {
383 index_args,
384 upgrade,
385 no_upgrade,
386 upgrade_package,
387 reinstall,
388 no_reinstall,
389 reinstall_package,
390 index_strategy,
391 keyring_provider,
392 resolution,
393 prerelease,
394 pre,
395 fork_strategy,
396 config_setting,
397 config_settings_package,
398 no_build_isolation,
399 no_build_isolation_package,
400 build_isolation,
401 exclude_newer,
402 exclude_newer_package,
403 link_mode,
404 compile_bytecode,
405 no_compile_bytecode,
406 no_sources,
407 } = resolver_installer_args;
408
409 let BuildOptionsArgs {
410 no_build,
411 build,
412 no_build_package,
413 no_binary,
414 binary,
415 no_binary_package,
416 } = build_args;
417
418 let default_index = index_args
419 .default_index
420 .and_then(Maybe::into_option)
421 .map(|default_index| vec![default_index]);
422 let index = index_args.index.map(|index| {
423 index
424 .into_iter()
425 .flat_map(|v| v.clone())
426 .filter_map(Maybe::into_option)
427 .collect()
428 });
429
430 ResolverInstallerOptions {
431 index: default_index.combine(index),
432 index_url: index_args.index_url.and_then(Maybe::into_option),
433 extra_index_url: index_args.extra_index_url.map(|extra_index_url| {
434 extra_index_url
435 .into_iter()
436 .filter_map(Maybe::into_option)
437 .collect()
438 }),
439 no_index: if index_args.no_index {
440 Some(true)
441 } else {
442 None
443 },
444 find_links: index_args.find_links.map(|find_links| {
445 find_links
446 .into_iter()
447 .filter_map(Maybe::into_option)
448 .collect()
449 }),
450 upgrade: Upgrade::from_args(
451 flag(upgrade, no_upgrade, "upgrade"),
452 upgrade_package.into_iter().map(Requirement::from).collect(),
453 ),
454 reinstall: Reinstall::from_args(
455 flag(reinstall, no_reinstall, "reinstall"),
456 reinstall_package,
457 ),
458 index_strategy,
459 keyring_provider,
460 resolution,
461 prerelease: if pre {
462 Some(PrereleaseMode::Allow)
463 } else {
464 prerelease
465 },
466 fork_strategy,
467 dependency_metadata: None,
468 config_settings: config_setting
469 .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
470 config_settings_package: config_settings_package.map(|config_settings| {
471 config_settings
472 .into_iter()
473 .collect::<PackageConfigSettings>()
474 }),
475 build_isolation: BuildIsolation::from_args(
476 flag(no_build_isolation, build_isolation, "build-isolation"),
477 no_build_isolation_package,
478 ),
479 extra_build_dependencies: None,
480 extra_build_variables: None,
481 exclude_newer,
482 exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
483 link_mode,
484 compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
485 no_build: flag(no_build, build, "build"),
486 no_build_package: if no_build_package.is_empty() {
487 None
488 } else {
489 Some(no_build_package)
490 },
491 no_binary: flag(no_binary, binary, "binary"),
492 no_binary_package: if no_binary_package.is_empty() {
493 None
494 } else {
495 Some(no_binary_package)
496 },
497 no_sources: if no_sources { Some(true) } else { None },
498 }
499}