uv_distribution_types/
build_requires.rs1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use uv_cache_key::{CacheKey, CacheKeyHasher};
6use uv_normalize::PackageName;
7
8use crate::{Name, Requirement, RequirementSource, Resolution};
9
10#[derive(Debug, thiserror::Error)]
11pub enum ExtraBuildRequiresError {
12 #[error(
13 "`{0}` was declared as an extra build dependency with `match-runtime = true`, but was not found in the resolution"
14 )]
15 NotFound(PackageName),
16 #[error(
17 "Dependencies marked with `match-runtime = true` cannot include version specifiers, but found: `{0}{1}`"
18 )]
19 VersionSpecifiersNotAllowed(PackageName, Box<RequirementSource>),
20 #[error(
21 "Dependencies marked with `match-runtime = true` cannot include URL constraints, but found: `{0}{1}`"
22 )]
23 UrlNotAllowed(PackageName, Box<RequirementSource>),
24}
25
26#[derive(Debug, Clone, Default)]
28pub struct ExtraBuildRequires(BTreeMap<PackageName, Vec<ExtraBuildRequirement>>);
29
30impl std::ops::Deref for ExtraBuildRequires {
31 type Target = BTreeMap<PackageName, Vec<ExtraBuildRequirement>>;
32
33 fn deref(&self) -> &Self::Target {
34 &self.0
35 }
36}
37
38impl std::ops::DerefMut for ExtraBuildRequires {
39 fn deref_mut(&mut self) -> &mut Self::Target {
40 &mut self.0
41 }
42}
43
44impl IntoIterator for ExtraBuildRequires {
45 type Item = (PackageName, Vec<ExtraBuildRequirement>);
46 type IntoIter = std::collections::btree_map::IntoIter<PackageName, Vec<ExtraBuildRequirement>>;
47
48 fn into_iter(self) -> Self::IntoIter {
49 self.0.into_iter()
50 }
51}
52
53impl FromIterator<(PackageName, Vec<ExtraBuildRequirement>)> for ExtraBuildRequires {
54 fn from_iter<T: IntoIterator<Item = (PackageName, Vec<ExtraBuildRequirement>)>>(
55 iter: T,
56 ) -> Self {
57 Self(iter.into_iter().collect())
58 }
59}
60
61#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
62pub struct ExtraBuildRequirement {
63 pub requirement: Requirement,
65 pub match_runtime: bool,
67}
68
69impl From<ExtraBuildRequirement> for Requirement {
70 fn from(value: ExtraBuildRequirement) -> Self {
71 value.requirement
72 }
73}
74
75impl CacheKey for ExtraBuildRequirement {
76 fn cache_key(&self, state: &mut CacheKeyHasher) {
77 self.requirement.cache_key(state);
78 self.match_runtime.cache_key(state);
79 }
80}
81
82impl ExtraBuildRequires {
83 pub fn match_runtime(self, resolution: &Resolution) -> Result<Self, ExtraBuildRequiresError> {
85 self.into_iter()
86 .filter(|(_, requirements)| !requirements.is_empty())
87 .filter(|(name, _)| resolution.distributions().any(|dist| dist.name() == name))
88 .map(|(name, requirements)| {
89 let requirements = requirements
90 .into_iter()
91 .map(|requirement| match requirement {
92 ExtraBuildRequirement {
93 requirement,
94 match_runtime: true,
95 } => {
96 if let RequirementSource::Registry { specifier, .. } =
99 &requirement.source
100 {
101 if !specifier.is_empty() {
102 return Err(
103 ExtraBuildRequiresError::VersionSpecifiersNotAllowed(
104 requirement.name.clone(),
105 Box::new(requirement.source.clone()),
106 ),
107 );
108 }
109 } else {
110 return Err(ExtraBuildRequiresError::VersionSpecifiersNotAllowed(
111 requirement.name.clone(),
112 Box::new(requirement.source.clone()),
113 ));
114 }
115
116 let dist = resolution
117 .distributions()
118 .find(|dist| dist.name() == &requirement.name)
119 .ok_or_else(|| {
120 ExtraBuildRequiresError::NotFound(requirement.name.clone())
121 })?;
122 let requirement = Requirement {
123 source: RequirementSource::from(dist),
124 ..requirement
125 };
126 Ok::<_, ExtraBuildRequiresError>(ExtraBuildRequirement {
127 requirement,
128 match_runtime: true,
129 })
130 }
131 requirement => Ok(requirement),
132 })
133 .collect::<Result<Vec<_>, _>>()?;
134 Ok::<_, ExtraBuildRequiresError>((name, requirements))
135 })
136 .collect::<Result<Self, _>>()
137 }
138}
139
140pub type BuildVariables = BTreeMap<String, String>;
142
143#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
145#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
146pub struct ExtraBuildVariables(BTreeMap<PackageName, BuildVariables>);
147
148impl std::ops::Deref for ExtraBuildVariables {
149 type Target = BTreeMap<PackageName, BuildVariables>;
150
151 fn deref(&self) -> &Self::Target {
152 &self.0
153 }
154}
155
156impl std::ops::DerefMut for ExtraBuildVariables {
157 fn deref_mut(&mut self) -> &mut Self::Target {
158 &mut self.0
159 }
160}
161
162impl IntoIterator for ExtraBuildVariables {
163 type Item = (PackageName, BuildVariables);
164 type IntoIter = std::collections::btree_map::IntoIter<PackageName, BuildVariables>;
165
166 fn into_iter(self) -> Self::IntoIter {
167 self.0.into_iter()
168 }
169}
170
171impl FromIterator<(PackageName, BuildVariables)> for ExtraBuildVariables {
172 fn from_iter<T: IntoIterator<Item = (PackageName, BuildVariables)>>(iter: T) -> Self {
173 Self(iter.into_iter().collect())
174 }
175}
176
177impl CacheKey for ExtraBuildVariables {
178 fn cache_key(&self, state: &mut CacheKeyHasher) {
179 for (package, vars) in &self.0 {
180 package.as_str().cache_key(state);
181 for (key, value) in vars {
182 key.cache_key(state);
183 value.cache_key(state);
184 }
185 }
186 }
187}