1use std::fmt::{Display, Formatter};
2
3use uv_normalize::PackageName;
4
5use crate::{PackageNameSpecifier, PackageNameSpecifiers};
6
7#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
8pub enum BuildKind {
9 #[default]
11 Wheel,
12 Sdist,
14 Editable,
16}
17
18impl Display for BuildKind {
19 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Self::Wheel => f.write_str("wheel"),
22 Self::Sdist => f.write_str("sdist"),
23 Self::Editable => f.write_str("editable"),
24 }
25 }
26}
27
28#[derive(Debug, Copy, Clone, PartialEq, Eq)]
29pub enum BuildOutput {
30 Stderr,
32 Debug,
34 Quiet,
36}
37
38#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
39#[serde(rename_all = "kebab-case", deny_unknown_fields)]
40pub struct BuildOptions {
41 no_binary: NoBinary,
42 no_build: NoBuild,
43}
44
45impl BuildOptions {
46 pub fn new(no_binary: NoBinary, no_build: NoBuild) -> Self {
47 Self {
48 no_binary,
49 no_build,
50 }
51 }
52
53 #[must_use]
54 pub fn combine(self, no_binary: NoBinary, no_build: NoBuild) -> Self {
55 Self {
56 no_binary: self.no_binary.combine(no_binary),
57 no_build: self.no_build.combine(no_build),
58 }
59 }
60
61 pub fn no_binary_package(&self, package_name: &PackageName) -> bool {
62 match &self.no_binary {
63 NoBinary::None => false,
64 NoBinary::All => match &self.no_build {
65 NoBuild::Packages(packages) => !packages.contains(package_name),
67 _ => true,
68 },
69 NoBinary::Packages(packages) => packages.contains(package_name),
70 }
71 }
72
73 pub fn no_build_package(&self, package_name: &PackageName) -> bool {
74 match &self.no_build {
75 NoBuild::All => match &self.no_binary {
76 NoBinary::Packages(packages) => !packages.contains(package_name),
78 _ => true,
79 },
80 NoBuild::None => false,
81 NoBuild::Packages(packages) => packages.contains(package_name),
82 }
83 }
84
85 pub fn no_build_requirement(&self, package_name: Option<&PackageName>) -> bool {
86 match package_name {
87 Some(name) => self.no_build_package(name),
88 None => self.no_build_all(),
89 }
90 }
91
92 fn no_build_all(&self) -> bool {
93 matches!(self.no_build, NoBuild::All)
94 }
95
96 pub fn no_build(&self) -> &NoBuild {
98 &self.no_build
99 }
100
101 pub fn no_binary(&self) -> &NoBinary {
103 &self.no_binary
104 }
105}
106
107#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
108#[serde(rename_all = "kebab-case", deny_unknown_fields)]
109pub enum NoBinary {
110 #[default]
112 None,
113
114 All,
116
117 Packages(Vec<PackageName>),
119}
120
121impl NoBinary {
122 pub fn from_args(no_binary: Option<bool>, no_binary_package: Vec<PackageName>) -> Self {
124 match no_binary {
125 Some(true) => Self::All,
126 Some(false) => Self::None,
127 None => {
128 if no_binary_package.is_empty() {
129 Self::None
130 } else {
131 Self::Packages(no_binary_package)
132 }
133 }
134 }
135 }
136
137 pub fn from_pip_args(no_binary: Vec<PackageNameSpecifier>) -> Self {
139 let combined = PackageNameSpecifiers::from_iter(no_binary.into_iter());
140 match combined {
141 PackageNameSpecifiers::All => Self::All,
142 PackageNameSpecifiers::None => Self::None,
143 PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
144 }
145 }
146
147 pub fn from_pip_arg(no_binary: PackageNameSpecifier) -> Self {
149 Self::from_pip_args(vec![no_binary])
150 }
151
152 #[must_use]
154 pub fn combine(self, other: Self) -> Self {
155 match (self, other) {
156 (Self::None, Self::None) => Self::None,
158 (Self::All, _) | (_, Self::All) => Self::All,
160 (Self::Packages(a), Self::None) => Self::Packages(a),
162 (Self::None, Self::Packages(b)) => Self::Packages(b),
163 (Self::Packages(mut a), Self::Packages(b)) => {
165 a.extend(b);
166 Self::Packages(a)
167 }
168 }
169 }
170
171 pub fn extend(&mut self, other: Self) {
173 match (&mut *self, other) {
174 (Self::All, _) | (_, Self::All) => *self = Self::All,
176 (Self::None, Self::None) => {
178 }
180 (Self::Packages(_), Self::None) => {
182 }
184 (Self::None, Self::Packages(b)) => {
185 *self = Self::Packages(b);
187 }
188 (Self::Packages(a), Self::Packages(b)) => {
190 a.extend(b);
191 }
192 }
193 }
194}
195
196impl NoBinary {
197 pub fn is_none(&self) -> bool {
199 matches!(self, Self::None)
200 }
201}
202
203#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
204#[serde(rename_all = "kebab-case", deny_unknown_fields)]
205pub enum NoBuild {
206 #[default]
208 None,
209
210 All,
212
213 Packages(Vec<PackageName>),
215}
216
217impl NoBuild {
218 pub fn from_args(no_build: Option<bool>, no_build_package: Vec<PackageName>) -> Self {
220 match no_build {
221 Some(true) => Self::All,
222 Some(false) => Self::None,
223 None => {
224 if no_build_package.is_empty() {
225 Self::None
226 } else {
227 Self::Packages(no_build_package)
228 }
229 }
230 }
231 }
232
233 pub fn from_pip_args(only_binary: Vec<PackageNameSpecifier>, no_build: bool) -> Self {
235 if no_build {
236 Self::All
237 } else {
238 let combined = PackageNameSpecifiers::from_iter(only_binary.into_iter());
239 match combined {
240 PackageNameSpecifiers::All => Self::All,
241 PackageNameSpecifiers::None => Self::None,
242 PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
243 }
244 }
245 }
246
247 pub fn from_pip_arg(no_build: PackageNameSpecifier) -> Self {
249 Self::from_pip_args(vec![no_build], false)
250 }
251
252 #[must_use]
254 pub fn combine(self, other: Self) -> Self {
255 match (self, other) {
256 (Self::None, Self::None) => Self::None,
258 (Self::All, _) | (_, Self::All) => Self::All,
260 (Self::Packages(a), Self::None) => Self::Packages(a),
262 (Self::None, Self::Packages(b)) => Self::Packages(b),
263 (Self::Packages(mut a), Self::Packages(b)) => {
265 a.extend(b);
266 Self::Packages(a)
267 }
268 }
269 }
270
271 pub fn extend(&mut self, other: Self) {
273 match (&mut *self, other) {
274 (Self::All, _) | (_, Self::All) => *self = Self::All,
276 (Self::None, Self::None) => {
278 }
280 (Self::Packages(_), Self::None) => {
282 }
284 (Self::None, Self::Packages(b)) => {
285 *self = Self::Packages(b);
287 }
288 (Self::Packages(a), Self::Packages(b)) => {
290 a.extend(b);
291 }
292 }
293 }
294}
295
296impl NoBuild {
297 pub fn is_none(&self) -> bool {
299 matches!(self, Self::None)
300 }
301}
302
303#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
304#[serde(deny_unknown_fields, rename_all = "kebab-case")]
305#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
306#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
307pub enum IndexStrategy {
308 #[default]
313 #[cfg_attr(feature = "clap", clap(alias = "first-match"))]
314 FirstIndex,
315 #[cfg_attr(feature = "clap", clap(alias = "unsafe-any-match"))]
327 #[serde(alias = "unsafe-any-match")]
328 UnsafeFirstMatch,
329 UnsafeBestMatch,
343}
344
345#[cfg(test)]
346mod tests {
347 use std::str::FromStr;
348
349 use anyhow::Error;
350
351 use super::*;
352
353 #[test]
354 fn no_build_from_args() -> Result<(), Error> {
355 assert_eq!(
356 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":all:")?], false),
357 NoBuild::All,
358 );
359 assert_eq!(
360 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":all:")?], true),
361 NoBuild::All,
362 );
363 assert_eq!(
364 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":none:")?], true),
365 NoBuild::All,
366 );
367 assert_eq!(
368 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":none:")?], false),
369 NoBuild::None,
370 );
371 assert_eq!(
372 NoBuild::from_pip_args(
373 vec![
374 PackageNameSpecifier::from_str("foo")?,
375 PackageNameSpecifier::from_str("bar")?
376 ],
377 false
378 ),
379 NoBuild::Packages(vec![
380 PackageName::from_str("foo")?,
381 PackageName::from_str("bar")?
382 ]),
383 );
384 assert_eq!(
385 NoBuild::from_pip_args(
386 vec![
387 PackageNameSpecifier::from_str("test")?,
388 PackageNameSpecifier::All
389 ],
390 false
391 ),
392 NoBuild::All,
393 );
394 assert_eq!(
395 NoBuild::from_pip_args(
396 vec![
397 PackageNameSpecifier::from_str("foo")?,
398 PackageNameSpecifier::from_str(":none:")?,
399 PackageNameSpecifier::from_str("bar")?
400 ],
401 false
402 ),
403 NoBuild::Packages(vec![PackageName::from_str("bar")?]),
404 );
405
406 Ok(())
407 }
408}