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 pub fn no_binary_requirement(&self, package_name: Option<&PackageName>) -> bool {
93 match package_name {
94 Some(name) => self.no_binary_package(name),
95 None => self.no_binary_all(),
96 }
97 }
98
99 pub fn no_build_all(&self) -> bool {
100 matches!(self.no_build, NoBuild::All)
101 }
102
103 pub fn no_binary_all(&self) -> bool {
104 matches!(self.no_binary, NoBinary::All)
105 }
106
107 pub fn no_build(&self) -> &NoBuild {
109 &self.no_build
110 }
111
112 pub fn no_binary(&self) -> &NoBinary {
114 &self.no_binary
115 }
116}
117
118#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
119#[serde(rename_all = "kebab-case", deny_unknown_fields)]
120pub enum NoBinary {
121 #[default]
123 None,
124
125 All,
127
128 Packages(Vec<PackageName>),
130}
131
132impl NoBinary {
133 pub fn from_args(no_binary: Option<bool>, no_binary_package: Vec<PackageName>) -> Self {
135 match no_binary {
136 Some(true) => Self::All,
137 Some(false) => Self::None,
138 None => {
139 if no_binary_package.is_empty() {
140 Self::None
141 } else {
142 Self::Packages(no_binary_package)
143 }
144 }
145 }
146 }
147
148 pub fn from_pip_args(no_binary: Vec<PackageNameSpecifier>) -> Self {
150 let combined = PackageNameSpecifiers::from_iter(no_binary.into_iter());
151 match combined {
152 PackageNameSpecifiers::All => Self::All,
153 PackageNameSpecifiers::None => Self::None,
154 PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
155 }
156 }
157
158 pub fn from_pip_arg(no_binary: PackageNameSpecifier) -> Self {
160 Self::from_pip_args(vec![no_binary])
161 }
162
163 #[must_use]
165 pub fn combine(self, other: Self) -> Self {
166 match (self, other) {
167 (Self::None, Self::None) => Self::None,
169 (Self::All, _) | (_, Self::All) => Self::All,
171 (Self::Packages(a), Self::None) => Self::Packages(a),
173 (Self::None, Self::Packages(b)) => Self::Packages(b),
174 (Self::Packages(mut a), Self::Packages(b)) => {
176 a.extend(b);
177 Self::Packages(a)
178 }
179 }
180 }
181
182 pub fn extend(&mut self, other: Self) {
184 match (&mut *self, other) {
185 (Self::All, _) | (_, Self::All) => *self = Self::All,
187 (Self::None, Self::None) => {
189 }
191 (Self::Packages(_), Self::None) => {
193 }
195 (Self::None, Self::Packages(b)) => {
196 *self = Self::Packages(b);
198 }
199 (Self::Packages(a), Self::Packages(b)) => {
201 a.extend(b);
202 }
203 }
204 }
205}
206
207impl NoBinary {
208 pub fn is_none(&self) -> bool {
210 matches!(self, Self::None)
211 }
212}
213
214#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
215#[serde(rename_all = "kebab-case", deny_unknown_fields)]
216pub enum NoBuild {
217 #[default]
219 None,
220
221 All,
223
224 Packages(Vec<PackageName>),
226}
227
228impl NoBuild {
229 pub fn from_args(no_build: Option<bool>, no_build_package: Vec<PackageName>) -> Self {
231 match no_build {
232 Some(true) => Self::All,
233 Some(false) => Self::None,
234 None => {
235 if no_build_package.is_empty() {
236 Self::None
237 } else {
238 Self::Packages(no_build_package)
239 }
240 }
241 }
242 }
243
244 pub fn from_pip_args(only_binary: Vec<PackageNameSpecifier>, no_build: bool) -> Self {
246 if no_build {
247 Self::All
248 } else {
249 let combined = PackageNameSpecifiers::from_iter(only_binary.into_iter());
250 match combined {
251 PackageNameSpecifiers::All => Self::All,
252 PackageNameSpecifiers::None => Self::None,
253 PackageNameSpecifiers::Packages(packages) => Self::Packages(packages),
254 }
255 }
256 }
257
258 pub fn from_pip_arg(no_build: PackageNameSpecifier) -> Self {
260 Self::from_pip_args(vec![no_build], false)
261 }
262
263 #[must_use]
265 pub fn combine(self, other: Self) -> Self {
266 match (self, other) {
267 (Self::None, Self::None) => Self::None,
269 (Self::All, _) | (_, Self::All) => Self::All,
271 (Self::Packages(a), Self::None) => Self::Packages(a),
273 (Self::None, Self::Packages(b)) => Self::Packages(b),
274 (Self::Packages(mut a), Self::Packages(b)) => {
276 a.extend(b);
277 Self::Packages(a)
278 }
279 }
280 }
281
282 pub fn extend(&mut self, other: Self) {
284 match (&mut *self, other) {
285 (Self::All, _) | (_, Self::All) => *self = Self::All,
287 (Self::None, Self::None) => {
289 }
291 (Self::Packages(_), Self::None) => {
293 }
295 (Self::None, Self::Packages(b)) => {
296 *self = Self::Packages(b);
298 }
299 (Self::Packages(a), Self::Packages(b)) => {
301 a.extend(b);
302 }
303 }
304 }
305}
306
307impl NoBuild {
308 pub fn is_none(&self) -> bool {
310 matches!(self, Self::None)
311 }
312}
313
314#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
315#[serde(deny_unknown_fields, rename_all = "kebab-case")]
316#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
317#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
318pub enum IndexStrategy {
319 #[default]
324 #[cfg_attr(feature = "clap", clap(alias = "first-match"))]
325 FirstIndex,
326 #[cfg_attr(feature = "clap", clap(alias = "unsafe-any-match"))]
338 #[serde(alias = "unsafe-any-match")]
339 UnsafeFirstMatch,
340 UnsafeBestMatch,
354}
355
356#[cfg(test)]
357mod tests {
358 use std::str::FromStr;
359
360 use anyhow::Error;
361
362 use super::*;
363
364 #[test]
365 fn no_build_from_args() -> Result<(), Error> {
366 assert_eq!(
367 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":all:")?], false),
368 NoBuild::All,
369 );
370 assert_eq!(
371 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":all:")?], true),
372 NoBuild::All,
373 );
374 assert_eq!(
375 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":none:")?], true),
376 NoBuild::All,
377 );
378 assert_eq!(
379 NoBuild::from_pip_args(vec![PackageNameSpecifier::from_str(":none:")?], false),
380 NoBuild::None,
381 );
382 assert_eq!(
383 NoBuild::from_pip_args(
384 vec![
385 PackageNameSpecifier::from_str("foo")?,
386 PackageNameSpecifier::from_str("bar")?
387 ],
388 false
389 ),
390 NoBuild::Packages(vec![
391 PackageName::from_str("foo")?,
392 PackageName::from_str("bar")?
393 ]),
394 );
395 assert_eq!(
396 NoBuild::from_pip_args(
397 vec![
398 PackageNameSpecifier::from_str("test")?,
399 PackageNameSpecifier::All
400 ],
401 false
402 ),
403 NoBuild::All,
404 );
405 assert_eq!(
406 NoBuild::from_pip_args(
407 vec![
408 PackageNameSpecifier::from_str("foo")?,
409 PackageNameSpecifier::from_str(":none:")?,
410 PackageNameSpecifier::from_str("bar")?
411 ],
412 false
413 ),
414 NoBuild::Packages(vec![PackageName::from_str("bar")?]),
415 );
416
417 Ok(())
418 }
419}