Skip to main content

uv_cli/
options.rs

1use std::error::Error;
2use std::fmt;
3
4use anyhow::bail;
5
6use uv_cache::Refresh;
7use uv_configuration::{BuildIsolation, Reinstall, Upgrade};
8use uv_distribution_types::{ConfigSettings, Index, PackageConfigSettings, Requirement};
9use uv_resolver::{ExcludeNewerPackage, PrereleaseMode};
10use uv_settings::{Combine, EnvFlag, PipOptions, ResolverInstallerOptions, ResolverOptions};
11use uv_warnings::owo_colors::OwoColorize;
12
13use crate::{
14    BuildIsolationArgs, BuildOptionsArgs, CompileBytecodeArgs, ExcludeNewerArgs, FetchArgs,
15    IndexArgs, InstallerArgs, Maybe, PackageBuildIsolationArgs, PackageExcludeNewerArgs,
16    RefreshArgs, RegistryClientArgs, ReinstallArgs, ResolverArgs, ResolverInstallerArgs,
17    SourcesArgs, VersionSelectionArgs,
18};
19
20/// An error caused by an invalid combination of command-line arguments.
21#[derive(Debug)]
22pub struct ArgumentError(String);
23
24impl fmt::Display for ArgumentError {
25    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
26        self.0.fmt(formatter)
27    }
28}
29
30impl Error for ArgumentError {}
31
32/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag.
33pub fn flag(yes: bool, no: bool, name: &str) -> anyhow::Result<Option<bool>> {
34    match (yes, no) {
35        (true, false) => Ok(Some(true)),
36        (false, true) => Ok(Some(false)),
37        (false, false) => Ok(None),
38        (..) => {
39            bail!(ArgumentError(format!(
40                "`{}` and `{}` cannot be used together. \
41                Boolean flags on different levels are currently not supported \
42                (https://github.com/clap-rs/clap/issues/6049)",
43                format!("--{name}").green(),
44                format!("--no-{name}").green(),
45            )));
46        }
47    }
48}
49
50/// The source of a boolean flag value.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum FlagSource {
53    /// The flag was set via command-line argument.
54    Cli,
55    /// The flag was set via environment variable.
56    Env(&'static str),
57    /// The flag was set via workspace/project configuration.
58    Config,
59}
60
61impl fmt::Display for FlagSource {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        match self {
64            Self::Cli => write!(f, "command-line argument"),
65            Self::Env(name) => write!(f, "environment variable `{name}`"),
66            Self::Config => write!(f, "workspace configuration"),
67        }
68    }
69}
70
71/// A boolean flag value with its source.
72#[derive(Debug, Clone, Copy)]
73pub enum Flag {
74    /// The flag is not set.
75    Disabled,
76    /// The flag is enabled with a known source.
77    Enabled {
78        source: FlagSource,
79        /// The CLI flag name (e.g., "locked" for `--locked`).
80        name: &'static str,
81    },
82}
83
84impl Flag {
85    /// Create a flag that is explicitly disabled.
86    pub const fn disabled() -> Self {
87        Self::Disabled
88    }
89
90    /// Create an enabled flag from a CLI argument.
91    pub const fn from_cli(name: &'static str) -> Self {
92        Self::Enabled {
93            source: FlagSource::Cli,
94            name,
95        }
96    }
97
98    /// Create an enabled flag from workspace/project configuration.
99    pub const fn from_config(name: &'static str) -> Self {
100        Self::Enabled {
101            source: FlagSource::Config,
102            name,
103        }
104    }
105
106    /// Returns `true` if the flag is set.
107    pub fn is_enabled(self) -> bool {
108        matches!(self, Self::Enabled { .. })
109    }
110
111    /// Returns the source of the flag, if it is set.
112    pub fn source(self) -> Option<FlagSource> {
113        match self {
114            Self::Disabled => None,
115            Self::Enabled { source, .. } => Some(source),
116        }
117    }
118}
119
120impl From<Flag> for bool {
121    fn from(flag: Flag) -> Self {
122        flag.is_enabled()
123    }
124}
125
126/// Resolve a boolean flag from CLI arguments and an environment variable.
127///
128/// The CLI argument takes precedence over the environment variable. Returns a [`Flag`] with the
129/// resolved value and source.
130pub fn resolve_flag(cli_flag: bool, name: &'static str, env_flag: EnvFlag) -> Flag {
131    if cli_flag {
132        Flag::Enabled {
133            source: FlagSource::Cli,
134            name,
135        }
136    } else if env_flag.value == Some(true) {
137        Flag::Enabled {
138            source: FlagSource::Env(env_flag.env_var),
139            name,
140        }
141    } else {
142        Flag::Disabled
143    }
144}
145
146/// Resolve a pair of mutually exclusive boolean flags from the CLI and environment variables.
147///
148/// If either flag is set on the command line, both environment variables are ignored so the CLI
149/// retains precedence over the full pair.
150pub fn resolve_flag_pair(
151    cli_flag: bool,
152    cli_no_flag: bool,
153    name: &'static str,
154    no_name: &'static str,
155    env_flag: Option<EnvFlag>,
156    env_no_flag: Option<EnvFlag>,
157) -> (Flag, Flag) {
158    if cli_flag || cli_no_flag {
159        (
160            if cli_flag {
161                Flag::from_cli(name)
162            } else {
163                Flag::disabled()
164            },
165            if cli_no_flag {
166                Flag::from_cli(no_name)
167            } else {
168                Flag::disabled()
169            },
170        )
171    } else {
172        (
173            env_flag.map_or_else(Flag::disabled, |env_flag| {
174                resolve_flag(false, name, env_flag)
175            }),
176            env_no_flag.map_or_else(Flag::disabled, |env_no_flag| {
177                resolve_flag(false, no_name, env_no_flag)
178            }),
179        )
180    }
181}
182
183/// Check if two flags conflict and return an error if they do.
184///
185/// This function checks if both flags are enabled (truthy) and reports an error if so, including
186/// the source of each flag (CLI or environment variable) in the error message.
187pub fn check_conflicts(flag_a: Flag, flag_b: Flag) -> anyhow::Result<()> {
188    if let (
189        Flag::Enabled {
190            source: source_a,
191            name: name_a,
192        },
193        Flag::Enabled {
194            source: source_b,
195            name: name_b,
196        },
197    ) = (flag_a, flag_b)
198    {
199        let display_a = match source_a {
200            FlagSource::Cli => format!("`--{name_a}`"),
201            FlagSource::Env(env) => format!("`{env}` (environment variable)"),
202            FlagSource::Config => format!("`{name_a}` (workspace configuration)"),
203        };
204        let display_b = match source_b {
205            FlagSource::Cli => format!("`--{name_b}`"),
206            FlagSource::Env(env) => format!("`{env}` (environment variable)"),
207            FlagSource::Config => format!("`{name_b}` (workspace configuration)"),
208        };
209        bail!(ArgumentError(format!(
210            "the argument {} cannot be used with {}",
211            display_a.green(),
212            display_b.green()
213        )));
214    }
215    Ok(())
216}
217
218impl TryFrom<RefreshArgs> for Refresh {
219    type Error = anyhow::Error;
220
221    fn try_from(value: RefreshArgs) -> anyhow::Result<Self> {
222        let RefreshArgs {
223            refresh,
224            no_refresh,
225            refresh_package,
226        } = value;
227
228        Ok(Self::from_args(
229            flag(refresh, no_refresh, "no-refresh")?,
230            refresh_package,
231        ))
232    }
233}
234
235/// Extract the `--index` and `--default-index` values from [`IndexArgs`].
236pub fn indexes_from_args(
237    default_index: Option<&Maybe<Index>>,
238    index: Option<&[Vec<Maybe<Index>>]>,
239) -> Option<Vec<Index>> {
240    let default_index = default_index
241        .cloned()
242        .and_then(Maybe::into_option)
243        .map(|default_index| vec![default_index]);
244    let index = index.map(|index| {
245        index
246            .iter()
247            .flatten()
248            .cloned()
249            .filter_map(Maybe::into_option)
250            .collect()
251    });
252
253    default_index.combine(index)
254}
255
256impl TryFrom<ResolverArgs> for PipOptions {
257    type Error = anyhow::Error;
258
259    fn try_from(args: ResolverArgs) -> anyhow::Result<Self> {
260        let ResolverArgs {
261            index_args,
262            upgrade,
263            no_upgrade,
264            upgrade_package,
265            upgrade_group,
266            registry_client:
267                RegistryClientArgs {
268                    index_strategy,
269                    keyring_provider,
270                },
271            version_selection:
272                VersionSelectionArgs {
273                    resolution,
274                    prerelease,
275                    pre,
276                    fork_strategy,
277                },
278            config_setting,
279            config_settings_package,
280            build_isolation:
281                PackageBuildIsolationArgs {
282                    build_isolation:
283                        BuildIsolationArgs {
284                            no_build_isolation,
285                            build_isolation,
286                        },
287                    no_build_isolation_package,
288                },
289            exclude_newer:
290                PackageExcludeNewerArgs {
291                    exclude_newer: ExcludeNewerArgs { exclude_newer },
292                    exclude_newer_package,
293                },
294            link_mode,
295            sources:
296                SourcesArgs {
297                    no_sources,
298                    no_sources_package,
299                },
300        } = args;
301
302        if !upgrade_group.is_empty() {
303            bail!(ArgumentError(format!(
304                "`{}` is not supported in `uv pip` commands",
305                "--upgrade-group".green()
306            )));
307        }
308
309        Ok(Self {
310            upgrade: flag(upgrade, no_upgrade, "no-upgrade")?,
311            upgrade_package: Some(upgrade_package),
312            index_strategy,
313            keyring_provider,
314            resolution,
315            fork_strategy,
316            prerelease: if pre {
317                Some(PrereleaseMode::Allow)
318            } else {
319                prerelease
320            },
321            config_settings: config_setting
322                .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
323            config_settings_package: config_settings_package.map(|config_settings| {
324                config_settings
325                    .into_iter()
326                    .collect::<PackageConfigSettings>()
327            }),
328            no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation")?,
329            no_build_isolation_package: Some(no_build_isolation_package),
330            exclude_newer,
331            exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
332            link_mode,
333            no_sources: if no_sources { Some(true) } else { None },
334            no_sources_package: if no_sources_package.is_empty() {
335                None
336            } else {
337                Some(no_sources_package)
338            },
339            ..Self::from(index_args)
340        })
341    }
342}
343
344impl TryFrom<InstallerArgs> for PipOptions {
345    type Error = anyhow::Error;
346
347    fn try_from(args: InstallerArgs) -> anyhow::Result<Self> {
348        let InstallerArgs {
349            index_args,
350            reinstall:
351                ReinstallArgs {
352                    reinstall,
353                    no_reinstall,
354                    reinstall_package,
355                },
356            registry_client:
357                RegistryClientArgs {
358                    index_strategy,
359                    keyring_provider,
360                },
361            config_setting,
362            config_settings_package,
363            build_isolation:
364                BuildIsolationArgs {
365                    no_build_isolation,
366                    build_isolation,
367                },
368            exclude_newer:
369                PackageExcludeNewerArgs {
370                    exclude_newer: ExcludeNewerArgs { exclude_newer },
371                    exclude_newer_package,
372                },
373            link_mode,
374            compile_bytecode:
375                CompileBytecodeArgs {
376                    compile_bytecode,
377                    no_compile_bytecode,
378                },
379            sources:
380                SourcesArgs {
381                    no_sources,
382                    no_sources_package,
383                },
384        } = args;
385
386        Ok(Self {
387            reinstall: flag(reinstall, no_reinstall, "reinstall")?,
388            reinstall_package: Some(reinstall_package),
389            index_strategy,
390            keyring_provider,
391            config_settings: config_setting
392                .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
393            config_settings_package: config_settings_package.map(|config_settings| {
394                config_settings
395                    .into_iter()
396                    .collect::<PackageConfigSettings>()
397            }),
398            no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation")?,
399            exclude_newer,
400            exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
401            link_mode,
402            compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode")?,
403            no_sources: if no_sources { Some(true) } else { None },
404            no_sources_package: if no_sources_package.is_empty() {
405                None
406            } else {
407                Some(no_sources_package)
408            },
409            ..Self::from(index_args)
410        })
411    }
412}
413
414impl TryFrom<ResolverInstallerArgs> for PipOptions {
415    type Error = anyhow::Error;
416
417    fn try_from(args: ResolverInstallerArgs) -> anyhow::Result<Self> {
418        let ResolverInstallerArgs {
419            index_args,
420            upgrade,
421            no_upgrade,
422            upgrade_package,
423            upgrade_group,
424            reinstall:
425                ReinstallArgs {
426                    reinstall,
427                    no_reinstall,
428                    reinstall_package,
429                },
430            registry_client:
431                RegistryClientArgs {
432                    index_strategy,
433                    keyring_provider,
434                },
435            version_selection:
436                VersionSelectionArgs {
437                    resolution,
438                    prerelease,
439                    pre,
440                    fork_strategy,
441                },
442            config_setting,
443            config_settings_package,
444            build_isolation:
445                PackageBuildIsolationArgs {
446                    build_isolation:
447                        BuildIsolationArgs {
448                            no_build_isolation,
449                            build_isolation,
450                        },
451                    no_build_isolation_package,
452                },
453            exclude_newer:
454                PackageExcludeNewerArgs {
455                    exclude_newer: ExcludeNewerArgs { exclude_newer },
456                    exclude_newer_package,
457                },
458            link_mode,
459            compile_bytecode:
460                CompileBytecodeArgs {
461                    compile_bytecode,
462                    no_compile_bytecode,
463                },
464            sources:
465                SourcesArgs {
466                    no_sources,
467                    no_sources_package,
468                },
469        } = args;
470
471        if !upgrade_group.is_empty() {
472            bail!(ArgumentError(format!(
473                "`{}` is not supported in `uv pip` commands",
474                "--upgrade-group".green()
475            )));
476        }
477
478        Ok(Self {
479            upgrade: flag(upgrade, no_upgrade, "upgrade")?,
480            upgrade_package: Some(upgrade_package),
481            reinstall: flag(reinstall, no_reinstall, "reinstall")?,
482            reinstall_package: Some(reinstall_package),
483            index_strategy,
484            keyring_provider,
485            resolution,
486            prerelease: if pre {
487                Some(PrereleaseMode::Allow)
488            } else {
489                prerelease
490            },
491            fork_strategy,
492            config_settings: config_setting
493                .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
494            config_settings_package: config_settings_package.map(|config_settings| {
495                config_settings
496                    .into_iter()
497                    .collect::<PackageConfigSettings>()
498            }),
499            no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation")?,
500            no_build_isolation_package: Some(no_build_isolation_package),
501            exclude_newer,
502            exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
503            link_mode,
504            compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode")?,
505            no_sources: if no_sources { Some(true) } else { None },
506            no_sources_package: if no_sources_package.is_empty() {
507                None
508            } else {
509                Some(no_sources_package)
510            },
511            ..Self::from(index_args)
512        })
513    }
514}
515
516impl From<FetchArgs> for PipOptions {
517    fn from(args: FetchArgs) -> Self {
518        let FetchArgs {
519            index_args,
520            registry_client:
521                RegistryClientArgs {
522                    index_strategy,
523                    keyring_provider,
524                },
525            exclude_newer:
526                PackageExcludeNewerArgs {
527                    exclude_newer: ExcludeNewerArgs { exclude_newer },
528                    exclude_newer_package,
529                },
530        } = args;
531
532        Self {
533            index_strategy,
534            keyring_provider,
535            exclude_newer,
536            exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
537            ..Self::from(index_args)
538        }
539    }
540}
541
542impl From<IndexArgs> for PipOptions {
543    fn from(args: IndexArgs) -> Self {
544        let IndexArgs {
545            default_index,
546            index,
547            index_url,
548            extra_index_url,
549            no_index,
550            find_links,
551        } = args;
552
553        Self {
554            index: indexes_from_args(default_index.as_ref(), index.as_deref()),
555            index_url: index_url.and_then(Maybe::into_option),
556            extra_index_url: extra_index_url.map(|extra_index_urls| {
557                extra_index_urls
558                    .into_iter()
559                    .filter_map(Maybe::into_option)
560                    .collect()
561            }),
562            no_index: if no_index { Some(true) } else { None },
563            find_links: find_links.map(|find_links| {
564                find_links
565                    .into_iter()
566                    .filter_map(Maybe::into_option)
567                    .collect()
568            }),
569            ..Self::default()
570        }
571    }
572}
573
574/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildOptionsArgs`].
575pub fn resolver_options(
576    resolver_args: ResolverArgs,
577    build_args: BuildOptionsArgs,
578) -> anyhow::Result<ResolverOptions> {
579    let ResolverArgs {
580        index_args,
581        upgrade,
582        no_upgrade,
583        upgrade_package,
584        upgrade_group,
585        registry_client:
586            RegistryClientArgs {
587                index_strategy,
588                keyring_provider,
589            },
590        version_selection:
591            VersionSelectionArgs {
592                resolution,
593                prerelease,
594                pre,
595                fork_strategy,
596            },
597        config_setting,
598        config_settings_package,
599        build_isolation:
600            PackageBuildIsolationArgs {
601                build_isolation:
602                    BuildIsolationArgs {
603                        no_build_isolation,
604                        build_isolation,
605                    },
606                no_build_isolation_package,
607            },
608        exclude_newer:
609            PackageExcludeNewerArgs {
610                exclude_newer: ExcludeNewerArgs { exclude_newer },
611                exclude_newer_package,
612            },
613        link_mode,
614        sources: SourcesArgs {
615            no_sources,
616            no_sources_package,
617        },
618    } = resolver_args;
619
620    let BuildOptionsArgs {
621        no_build,
622        build,
623        no_build_package,
624        no_binary,
625        binary,
626        no_binary_package,
627    } = build_args;
628
629    Ok(ResolverOptions {
630        index: indexes_from_args(
631            index_args.default_index.as_ref(),
632            index_args.index.as_deref(),
633        ),
634        index_url: index_args.index_url.and_then(Maybe::into_option),
635        extra_index_url: index_args.extra_index_url.map(|extra_index_url| {
636            extra_index_url
637                .into_iter()
638                .filter_map(Maybe::into_option)
639                .collect()
640        }),
641        no_index: if index_args.no_index {
642            Some(true)
643        } else {
644            None
645        },
646        find_links: index_args.find_links.map(|find_links| {
647            find_links
648                .into_iter()
649                .filter_map(Maybe::into_option)
650                .collect()
651        }),
652        upgrade: Upgrade::from_args(
653            flag(upgrade, no_upgrade, "no-upgrade")?,
654            upgrade_package.into_iter().map(Requirement::from).collect(),
655            upgrade_group,
656        ),
657        index_strategy,
658        keyring_provider,
659        resolution,
660        prerelease: if pre {
661            Some(PrereleaseMode::Allow)
662        } else {
663            prerelease
664        },
665        fork_strategy,
666        dependency_metadata: None,
667        config_settings: config_setting
668            .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
669        config_settings_package: config_settings_package.map(|config_settings| {
670            config_settings
671                .into_iter()
672                .collect::<PackageConfigSettings>()
673        }),
674        build_isolation: BuildIsolation::from_args(
675            flag(no_build_isolation, build_isolation, "build-isolation")?,
676            no_build_isolation_package,
677        ),
678        extra_build_dependencies: None,
679        extra_build_variables: None,
680        exclude_newer,
681        exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
682        link_mode,
683        torch_backend: None,
684        no_build: flag(no_build, build, "build")?,
685        no_build_package: if no_build_package.is_empty() {
686            None
687        } else {
688            Some(no_build_package)
689        },
690        no_binary: flag(no_binary, binary, "binary")?,
691        no_binary_package: if no_binary_package.is_empty() {
692            None
693        } else {
694            Some(no_binary_package)
695        },
696        no_sources: if no_sources { Some(true) } else { None },
697        no_sources_package: if no_sources_package.is_empty() {
698            None
699        } else {
700            Some(no_sources_package)
701        },
702    })
703}
704
705/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildOptionsArgs`].
706pub fn resolver_installer_options(
707    resolver_installer_args: ResolverInstallerArgs,
708    build_args: BuildOptionsArgs,
709) -> anyhow::Result<ResolverInstallerOptions> {
710    let index = indexes_from_args(
711        resolver_installer_args.index_args.default_index.as_ref(),
712        resolver_installer_args.index_args.index.as_deref(),
713    );
714    resolver_installer_options_with_indexes(resolver_installer_args, build_args, index)
715}
716
717/// Construct the [`ResolverInstallerOptions`] with a precomputed list of indexes.
718pub fn resolver_installer_options_with_indexes(
719    resolver_installer_args: ResolverInstallerArgs,
720    build_args: BuildOptionsArgs,
721    index: Option<Vec<Index>>,
722) -> anyhow::Result<ResolverInstallerOptions> {
723    let ResolverInstallerArgs {
724        index_args,
725        upgrade,
726        no_upgrade,
727        upgrade_package,
728        upgrade_group,
729        reinstall:
730            ReinstallArgs {
731                reinstall,
732                no_reinstall,
733                reinstall_package,
734            },
735        registry_client:
736            RegistryClientArgs {
737                index_strategy,
738                keyring_provider,
739            },
740        version_selection:
741            VersionSelectionArgs {
742                resolution,
743                prerelease,
744                pre,
745                fork_strategy,
746            },
747        config_setting,
748        config_settings_package,
749        build_isolation:
750            PackageBuildIsolationArgs {
751                build_isolation:
752                    BuildIsolationArgs {
753                        no_build_isolation,
754                        build_isolation,
755                    },
756                no_build_isolation_package,
757            },
758        exclude_newer:
759            PackageExcludeNewerArgs {
760                exclude_newer: ExcludeNewerArgs { exclude_newer },
761                exclude_newer_package,
762            },
763        link_mode,
764        compile_bytecode:
765            CompileBytecodeArgs {
766                compile_bytecode,
767                no_compile_bytecode,
768            },
769        sources: SourcesArgs {
770            no_sources,
771            no_sources_package,
772        },
773    } = resolver_installer_args;
774
775    let BuildOptionsArgs {
776        no_build,
777        build,
778        no_build_package,
779        no_binary,
780        binary,
781        no_binary_package,
782    } = build_args;
783
784    Ok(ResolverInstallerOptions {
785        index,
786        index_url: index_args.index_url.and_then(Maybe::into_option),
787        extra_index_url: index_args.extra_index_url.map(|extra_index_url| {
788            extra_index_url
789                .into_iter()
790                .filter_map(Maybe::into_option)
791                .collect()
792        }),
793        no_index: if index_args.no_index {
794            Some(true)
795        } else {
796            None
797        },
798        find_links: index_args.find_links.map(|find_links| {
799            find_links
800                .into_iter()
801                .filter_map(Maybe::into_option)
802                .collect()
803        }),
804        upgrade: Upgrade::from_args(
805            flag(upgrade, no_upgrade, "upgrade")?,
806            upgrade_package.into_iter().map(Requirement::from).collect(),
807            upgrade_group,
808        ),
809        reinstall: Reinstall::from_args(
810            flag(reinstall, no_reinstall, "reinstall")?,
811            reinstall_package,
812        ),
813        index_strategy,
814        keyring_provider,
815        resolution,
816        prerelease: if pre {
817            Some(PrereleaseMode::Allow)
818        } else {
819            prerelease
820        },
821        fork_strategy,
822        dependency_metadata: None,
823        config_settings: config_setting
824            .map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
825        config_settings_package: config_settings_package.map(|config_settings| {
826            config_settings
827                .into_iter()
828                .collect::<PackageConfigSettings>()
829        }),
830        build_isolation: BuildIsolation::from_args(
831            flag(no_build_isolation, build_isolation, "build-isolation")?,
832            no_build_isolation_package,
833        ),
834        extra_build_dependencies: None,
835        extra_build_variables: None,
836        exclude_newer,
837        exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
838        link_mode,
839        compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode")?,
840        no_build: flag(no_build, build, "build")?,
841        no_build_package: if no_build_package.is_empty() {
842            None
843        } else {
844            Some(no_build_package)
845        },
846        no_binary: flag(no_binary, binary, "binary")?,
847        no_binary_package: if no_binary_package.is_empty() {
848            None
849        } else {
850            Some(no_binary_package)
851        },
852        no_sources: if no_sources { Some(true) } else { None },
853        no_sources_package: if no_sources_package.is_empty() {
854            None
855        } else {
856            Some(no_sources_package)
857        },
858        torch_backend: None,
859    })
860}