1use uv_normalize::PackageName;
2pub use uv_pypi_types::BuildKind;
3
4use crate::{PackageNameSpecifier, PackageNameSpecifiers};
5
6#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7pub enum BuildOutput {
8 Stderr,
10 Debug,
12 Quiet,
14}
15
16#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[serde(rename_all = "kebab-case", deny_unknown_fields)]
18pub struct BuildOptions {
19 no_binary: NoBinary,
20 no_build: NoBuild,
21}
22
23impl BuildOptions {
24 pub fn new(no_binary: NoBinary, no_build: NoBuild) -> Self {
25 Self {
26 no_binary,
27 no_build,
28 }
29 }
30
31 #[must_use]
32 pub fn combine(self, no_binary: NoBinary, no_build: NoBuild) -> Self {
33 Self {
34 no_binary: self.no_binary.combine(no_binary),
35 no_build: self.no_build.combine(no_build),
36 }
37 }
38
39 pub fn no_binary_package(&self, package_name: &PackageName) -> bool {
40 match &self.no_binary {
41 NoBinary::None => false,
42 NoBinary::All => match &self.no_build {
43 NoBuild::Packages(packages) => !packages.contains(package_name),
45 _ => true,
46 },
47 NoBinary::Packages(packages) => packages.contains(package_name),
48 }
49 }
50
51 pub fn no_build_package(&self, package_name: &PackageName) -> bool {
52 match &self.no_build {
53 NoBuild::All => match &self.no_binary {
54 NoBinary::Packages(packages) => !packages.contains(package_name),
56 _ => true,
57 },
58 NoBuild::None => false,
59 NoBuild::Packages(packages) => packages.contains(package_name),
60 }
61 }
62
63 pub fn no_build_requirement(&self, package_name: Option<&PackageName>) -> bool {
64 match package_name {
65 Some(name) => self.no_build_package(name),
66 None => self.no_build_all(),
67 }
68 }
69
70 fn no_build_all(&self) -> bool {
71 matches!(self.no_build, NoBuild::All)
72 }
73
74 pub fn no_build(&self) -> &NoBuild {
76 &self.no_build
77 }
78
79 pub fn no_binary(&self) -> &NoBinary {
81 &self.no_binary
82 }
83}
84
85#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
86#[serde(rename_all = "kebab-case", deny_unknown_fields)]
87pub enum NoBinary {
88 #[default]
90 None,
91
92 All,
94
95 Packages(Vec<PackageName>),
97}
98
99impl NoBinary {
100 pub fn from_args(no_binary: Option<bool>, no_binary_package: Vec<PackageName>) -> Self {
102 match no_binary {
103 Some(true) => Self::All,
104 Some(false) => Self::None,
105 None => {
106 if no_binary_package.is_empty() {
107 Self::None
108 } else {
109 Self::Packages(no_binary_package)
110 }
111 }
112 }
113 }
114
115 pub fn from_pip_args(no_binary: Vec<PackageNameSpecifier>) -> Self {
117 let combined = PackageNameSpecifiers::from_iter(no_binary.into_iter());
118 match combined {
119 PackageNameSpecifiers::All => Self::All,
120 PackageNameSpecifiers::None => Self::None,
121 PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
122 }
123 }
124
125 pub fn from_pip_arg(no_binary: PackageNameSpecifier) -> Self {
127 Self::from_pip_args(vec![no_binary])
128 }
129
130 #[must_use]
132 pub fn combine(self, other: Self) -> Self {
133 match (self, other) {
134 (Self::None, Self::None) => Self::None,
136 (Self::All, _) | (_, Self::All) => Self::All,
138 (Self::Packages(a), Self::None) => Self::Packages(a),
140 (Self::None, Self::Packages(b)) => Self::Packages(b),
141 (Self::Packages(mut a), Self::Packages(b)) => {
143 a.extend(b);
144 Self::Packages(a)
145 }
146 }
147 }
148
149 pub fn extend(&mut self, other: Self) {
151 match (&mut *self, other) {
152 (Self::All, _) | (_, Self::All) => *self = Self::All,
154 (Self::None, Self::None) => {
156 }
158 (Self::Packages(_), Self::None) => {
160 }
162 (Self::None, Self::Packages(b)) => {
163 *self = Self::Packages(b);
165 }
166 (Self::Packages(a), Self::Packages(b)) => {
168 a.extend(b);
169 }
170 }
171 }
172}
173
174impl NoBinary {
175 pub fn is_none(&self) -> bool {
177 matches!(self, Self::None)
178 }
179}
180
181#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
182#[serde(rename_all = "kebab-case", deny_unknown_fields)]
183pub enum NoBuild {
184 #[default]
186 None,
187
188 All,
190
191 Packages(Vec<PackageName>),
193}
194
195impl NoBuild {
196 pub fn from_args(no_build: Option<bool>, no_build_package: Vec<PackageName>) -> Self {
198 match no_build {
199 Some(true) => Self::All,
200 Some(false) => Self::None,
201 None => {
202 if no_build_package.is_empty() {
203 Self::None
204 } else {
205 Self::Packages(no_build_package)
206 }
207 }
208 }
209 }
210
211 pub fn from_pip_args(only_binary: Vec<PackageNameSpecifier>, no_build: bool) -> Self {
213 if no_build {
214 Self::All
215 } else {
216 let combined = PackageNameSpecifiers::from_iter(only_binary.into_iter());
217 match combined {
218 PackageNameSpecifiers::All => Self::All,
219 PackageNameSpecifiers::None => Self::None,
220 PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
221 }
222 }
223 }
224
225 pub fn from_pip_arg(no_build: PackageNameSpecifier) -> Self {
227 Self::from_pip_args(vec![no_build], false)
228 }
229
230 #[must_use]
232 pub fn combine(self, other: Self) -> Self {
233 match (self, other) {
234 (Self::None, Self::None) => Self::None,
236 (Self::All, _) | (_, Self::All) => Self::All,
238 (Self::Packages(a), Self::None) => Self::Packages(a),
240 (Self::None, Self::Packages(b)) => Self::Packages(b),
241 (Self::Packages(mut a), Self::Packages(b)) => {
243 a.extend(b);
244 Self::Packages(a)
245 }
246 }
247 }
248
249 pub fn extend(&mut self, other: Self) {
251 match (&mut *self, other) {
252 (Self::All, _) | (_, Self::All) => *self = Self::All,
254 (Self::None, Self::None) => {
256 }
258 (Self::Packages(_), Self::None) => {
260 }
262 (Self::None, Self::Packages(b)) => {
263 *self = Self::Packages(b);
265 }
266 (Self::Packages(a), Self::Packages(b)) => {
268 a.extend(b);
269 }
270 }
271 }
272}
273
274impl NoBuild {
275 pub fn is_none(&self) -> bool {
277 matches!(self, Self::None)
278 }
279}
280
281#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
282#[serde(deny_unknown_fields, rename_all = "kebab-case")]
283#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
284#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
285pub enum IndexStrategy {
286 #[default]
291 #[cfg_attr(feature = "clap", clap(alias = "first-match"))]
292 FirstIndex,
293 #[cfg_attr(feature = "clap", clap(alias = "unsafe-any-match"))]
305 #[serde(alias = "unsafe-any-match")]
306 UnsafeFirstMatch,
307 UnsafeBestMatch,
321}
322
323#[cfg(test)]
324mod tests {
325 use std::str::FromStr;
326
327 use anyhow::Error;
328
329 use super::*;
330
331 #[test]
332 fn no_build_from_args() -> Result<(), Error> {
333 assert_eq!(
334 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":all:")?], false),
335 NoBuild::All,
336 );
337 assert_eq!(
338 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":all:")?], true),
339 NoBuild::All,
340 );
341 assert_eq!(
342 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":none:")?], true),
343 NoBuild::All,
344 );
345 assert_eq!(
346 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":none:")?], false),
347 NoBuild::None,
348 );
349 assert_eq!(
350 NoBuild::from_pip_args(
351 vec![
352 PackageNameSpecifier::from_str("foo")?,
353 PackageNameSpecifier::from_str("bar")?
354 ],
355 false
356 ),
357 NoBuild::Packages(vec![
358 PackageName::from_str("foo")?,
359 PackageName::from_str("bar")?
360 ]),
361 );
362 assert_eq!(
363 NoBuild::from_pip_args(
364 vec![
365 PackageNameSpecifier::from_str("test")?,
366 PackageNameSpecifier::All
367 ],
368 false
369 ),
370 NoBuild::All,
371 );
372 assert_eq!(
373 NoBuild::from_pip_args(
374 vec![
375 PackageNameSpecifier::from_str("foo")?,
376 PackageNameSpecifier::from_str(":none:")?,
377 PackageNameSpecifier::from_str("bar")?
378 ],
379 false
380 ),
381 NoBuild::Packages(vec![PackageName::from_str("bar")?]),
382 );
383
384 Ok(())
385 }
386}