uv_resolver/
prerelease.rs1use uv_distribution_types::RequirementSource;
2use uv_normalize::PackageName;
3use uv_pep440::Operator;
4
5use crate::resolver::ForkSet;
6use crate::{DependencyMode, Manifest, ResolverEnvironment};
7
8#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
9#[serde(deny_unknown_fields, rename_all = "kebab-case")]
10#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
11#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
12pub enum PrereleaseMode {
13 Disallow,
15
16 Allow,
18
19 IfNecessary,
21
22 Explicit,
25
26 #[default]
29 IfNecessaryOrExplicit,
30}
31
32impl std::fmt::Display for PrereleaseMode {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::Disallow => write!(f, "disallow"),
36 Self::Allow => write!(f, "allow"),
37 Self::IfNecessary => write!(f, "if-necessary"),
38 Self::Explicit => write!(f, "explicit"),
39 Self::IfNecessaryOrExplicit => write!(f, "if-necessary-or-explicit"),
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
47pub(crate) enum PrereleaseStrategy {
48 Disallow,
50
51 Allow,
53
54 IfNecessary,
56
57 Explicit(ForkSet),
60
61 IfNecessaryOrExplicit(ForkSet),
64}
65
66impl PrereleaseStrategy {
67 pub(crate) fn from_mode(
68 mode: PrereleaseMode,
69 manifest: &Manifest,
70 env: &ResolverEnvironment,
71 dependencies: DependencyMode,
72 ) -> Self {
73 let mut packages = ForkSet::default();
74
75 match mode {
76 PrereleaseMode::Disallow => Self::Disallow,
77 PrereleaseMode::Allow => Self::Allow,
78 PrereleaseMode::IfNecessary => Self::IfNecessary,
79 _ => {
80 for requirement in manifest.requirements(env, dependencies) {
81 let RequirementSource::Registry { specifier, .. } = &requirement.source else {
82 continue;
83 };
84
85 if specifier
86 .iter()
87 .filter(|spec| {
88 !matches!(spec.operator(), Operator::NotEqual | Operator::NotEqualStar)
89 })
90 .any(uv_pep440::VersionSpecifier::any_prerelease)
91 {
92 packages.add(&requirement, ());
93 }
94 }
95
96 match mode {
97 PrereleaseMode::Explicit => Self::Explicit(packages),
98 PrereleaseMode::IfNecessaryOrExplicit => Self::IfNecessaryOrExplicit(packages),
99 _ => unreachable!(),
100 }
101 }
102 }
103 }
104
105 pub(crate) fn allows(
107 &self,
108 package_name: &PackageName,
109 env: &ResolverEnvironment,
110 ) -> AllowPrerelease {
111 match self {
112 Self::Disallow => AllowPrerelease::No,
113 Self::Allow => AllowPrerelease::Yes,
114 Self::IfNecessary => AllowPrerelease::IfNecessary,
115 Self::Explicit(packages) => {
116 if packages.contains(package_name, env) {
117 AllowPrerelease::Yes
118 } else {
119 AllowPrerelease::No
120 }
121 }
122 Self::IfNecessaryOrExplicit(packages) => {
123 if packages.contains(package_name, env) {
124 AllowPrerelease::Yes
125 } else {
126 AllowPrerelease::IfNecessary
127 }
128 }
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
135pub(crate) enum AllowPrerelease {
136 Yes,
138
139 No,
141
142 IfNecessary,
144}