uv_configuration/
prerelease.rs1use std::ops::{Deref, DerefMut};
2use std::str::FromStr;
3
4use rustc_hash::FxHashMap;
5use uv_normalize::PackageName;
6
7#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
8#[serde(deny_unknown_fields, rename_all = "kebab-case")]
9#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
10#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
11pub enum PrereleaseMode {
12 Disallow,
14
15 Allow,
17
18 #[default]
20 IfNecessary,
21
22 Explicit,
26
27 #[deprecated(note = "use `if-necessary` instead")]
29 IfNecessaryOrExplicit,
30}
31
32#[allow(deprecated)]
33impl std::fmt::Display for PrereleaseMode {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 Self::Disallow => write!(f, "disallow"),
37 Self::Allow => write!(f, "allow"),
38 Self::IfNecessary => write!(f, "if-necessary"),
39 Self::Explicit => write!(f, "explicit"),
40 Self::IfNecessaryOrExplicit => write!(f, "if-necessary-or-explicit"),
41 }
42 }
43}
44
45impl FromStr for PrereleaseMode {
46 type Err = String;
47
48 fn from_str(value: &str) -> Result<Self, Self::Err> {
49 match value {
50 "disallow" => Ok(Self::Disallow),
51 "allow" => Ok(Self::Allow),
52 "if-necessary" => Ok(Self::IfNecessary),
53 "explicit" => Ok(Self::Explicit),
54 #[allow(deprecated)]
55 "if-necessary-or-explicit" => Ok(Self::IfNecessaryOrExplicit),
56 _ => Err(format!(
57 "expected one of `disallow`, `allow`, `if-necessary`, `explicit`, or `if-necessary-or-explicit`, found `{value}`"
58 )),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
66pub struct PrereleasePackageEntry {
67 package: PackageName,
68 mode: PrereleaseMode,
69}
70
71impl FromStr for PrereleasePackageEntry {
72 type Err = String;
73
74 fn from_str(value: &str) -> Result<Self, Self::Err> {
75 let Some((package, mode)) = value.split_once('=') else {
76 return Err(format!(
77 "Invalid `prerelease-package` value `{value}`: expected format `PACKAGE=MODE`"
78 ));
79 };
80
81 let package = PackageName::from_str(package).map_err(|err| {
82 format!("Invalid `prerelease-package` package name `{package}`: {err}")
83 })?;
84 let mode = PrereleaseMode::from_str(mode)
85 .map_err(|err| format!("Invalid `prerelease-package` mode: {err}"))?;
86
87 Ok(Self { package, mode })
88 }
89}
90
91#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
93#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
94pub struct PrereleasePackage(FxHashMap<PackageName, PrereleaseMode>);
95
96impl Deref for PrereleasePackage {
97 type Target = FxHashMap<PackageName, PrereleaseMode>;
98
99 fn deref(&self) -> &Self::Target {
100 &self.0
101 }
102}
103
104impl DerefMut for PrereleasePackage {
105 fn deref_mut(&mut self) -> &mut Self::Target {
106 &mut self.0
107 }
108}
109
110impl FromIterator<PrereleasePackageEntry> for PrereleasePackage {
111 fn from_iter<T: IntoIterator<Item = PrereleasePackageEntry>>(iter: T) -> Self {
112 Self(
113 iter.into_iter()
114 .map(|entry| (entry.package, entry.mode))
115 .collect(),
116 )
117 }
118}
119
120impl IntoIterator for PrereleasePackage {
121 type Item = (PackageName, PrereleaseMode);
122 type IntoIter = std::collections::hash_map::IntoIter<PackageName, PrereleaseMode>;
123
124 fn into_iter(self) -> Self::IntoIter {
125 self.0.into_iter()
126 }
127}
128
129impl<'a> IntoIterator for &'a PrereleasePackage {
130 type Item = (&'a PackageName, &'a PrereleaseMode);
131 type IntoIter = std::collections::hash_map::Iter<'a, PackageName, PrereleaseMode>;
132
133 fn into_iter(self) -> Self::IntoIter {
134 self.0.iter()
135 }
136}
137
138impl PrereleasePackage {
139 pub fn is_empty(&self) -> bool {
141 self.0.is_empty()
142 }
143}
144
145#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
147#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
148pub struct Prerelease {
149 pub global: PrereleaseMode,
151 pub package: PrereleasePackage,
153}
154
155impl Prerelease {
156 pub fn mode(&self, package: &PackageName) -> PrereleaseMode {
158 self.package.get(package).copied().unwrap_or(self.global)
159 }
160}