uv_requirements/
extras.rs1use std::sync::Arc;
2
3use futures::{TryStreamExt, stream::FuturesOrdered};
4
5use uv_distribution::{DistributionDatabase, Reporter};
6use uv_distribution_types::DistributionMetadata;
7use uv_distribution_types::Requirement;
8use uv_resolver::{InMemoryIndex, MetadataResponse};
9use uv_types::{BuildContext, HashStrategy};
10
11use crate::{Error, required_dist};
12
13pub struct ExtrasResolver<'a, Context: BuildContext> {
16 hasher: &'a HashStrategy,
18 index: &'a InMemoryIndex,
20 database: DistributionDatabase<'a, Context>,
22}
23
24impl<'a, Context: BuildContext> ExtrasResolver<'a, Context> {
25 pub fn new(
27 hasher: &'a HashStrategy,
28 index: &'a InMemoryIndex,
29 database: DistributionDatabase<'a, Context>,
30 ) -> Self {
31 Self {
32 hasher,
33 index,
34 database,
35 }
36 }
37
38 #[must_use]
40 pub fn with_reporter(self, reporter: Arc<dyn Reporter>) -> Self {
41 Self {
42 database: self.database.with_reporter(reporter),
43 ..self
44 }
45 }
46
47 pub async fn resolve(
49 self,
50 requirements: impl Iterator<Item = Requirement>,
51 ) -> Result<Vec<Requirement>, Error> {
52 let Self {
53 hasher,
54 index,
55 database,
56 } = self;
57 requirements
58 .map(async |requirement| {
59 Self::resolve_requirement(requirement, hasher, index, &database).await
60 })
61 .collect::<FuturesOrdered<_>>()
62 .try_collect()
63 .await
64 }
65
66 async fn resolve_requirement(
68 requirement: Requirement,
69 hasher: &HashStrategy,
70 index: &InMemoryIndex,
71 database: &DistributionDatabase<'a, Context>,
72 ) -> Result<Requirement, Error> {
73 let Some(dist) = required_dist(&requirement)? else {
76 return Ok(requirement);
77 };
78
79 let metadata = {
81 let id = dist.version_id();
82 if let Some(archive) = index
83 .distributions()
84 .get(&id)
85 .as_deref()
86 .and_then(|response| {
87 if let MetadataResponse::Found(archive, ..) = response {
88 Some(archive)
89 } else {
90 None
91 }
92 })
93 {
94 archive.metadata.clone()
96 } else {
97 let archive = database
99 .get_or_build_wheel_metadata(&dist, hasher.get(&dist))
100 .await
101 .map_err(|err| Error::from_dist(dist, err))?;
102
103 let metadata = archive.metadata.clone();
104
105 index
107 .distributions()
108 .done(id, Arc::new(MetadataResponse::Found(archive)));
109
110 metadata
111 }
112 };
113
114 let extras = {
116 let mut extras = metadata.provides_extra.to_vec();
117 extras.sort_unstable();
118 extras
119 };
120
121 Ok(Requirement {
122 extras: extras.into_boxed_slice(),
123 ..requirement
124 })
125 }
126}