]>
Commit | Line | Data |
---|---|---|
ea957da7 | 1 | use std::borrow::Borrow; |
4966f539 | 2 | use std::collections::{BTreeMap, HashMap}; |
ea957da7 | 3 | use std::fmt::Display; |
5ae13e32 | 4 | use std::hash::{Hash, Hasher}; |
798b620c | 5 | use std::mem; |
0d17d6c4 | 6 | use std::rc::Rc; |
2b46d039 | 7 | |
540bd458 DO |
8 | use serde::{Serialize, Serializer}; |
9 | ||
04ddd4d0 DW |
10 | use crate::core::interning::InternedString; |
11 | use crate::core::{Dependency, PackageId, SourceId}; | |
5c9d34be | 12 | use semver::Version; |
2b46d039 | 13 | |
04ddd4d0 | 14 | use crate::util::CargoResult; |
1ce76415 | 15 | |
670a3df4 | 16 | /// Subset of a `Manifest`. Contains only the most important information about |
798b620c | 17 | /// a package. |
c7641c24 | 18 | /// |
64ff29ff | 19 | /// Summaries are cloned, and should not be mutated after creation |
0d17d6c4 | 20 | #[derive(Debug, Clone)] |
1ce76415 | 21 | pub struct Summary { |
0d17d6c4 AC |
22 | inner: Rc<Inner>, |
23 | } | |
24 | ||
25 | #[derive(Debug, Clone)] | |
26 | struct Inner { | |
61c95b75 | 27 | package_id: PackageId, |
2b46d039 | 28 | dependencies: Vec<Dependency>, |
880337ac | 29 | features: Rc<FeatureMap>, |
5430db61 | 30 | checksum: Option<String>, |
9b731821 | 31 | links: Option<InternedString>, |
a9f163e3 | 32 | namespaced_features: bool, |
1ce76415 CL |
33 | } |
34 | ||
35 | impl Summary { | |
ea957da7 | 36 | pub fn new<K>( |
1e682848 AC |
37 | pkg_id: PackageId, |
38 | dependencies: Vec<Dependency>, | |
689f412c | 39 | features: &BTreeMap<K, Vec<impl AsRef<str>>>, |
c14bb6e0 | 40 | links: Option<impl Into<InternedString>>, |
a9f163e3 | 41 | namespaced_features: bool, |
ea957da7 | 42 | ) -> CargoResult<Summary> |
e5a11190 E |
43 | where |
44 | K: Borrow<str> + Ord + Display, | |
45 | { | |
2b46d039 | 46 | for dep in dependencies.iter() { |
5295cadd AC |
47 | let feature = dep.name_in_toml(); |
48 | if !namespaced_features && features.get(&*feature).is_some() { | |
3a18c89a | 49 | anyhow::bail!( |
1e682848 AC |
50 | "Features and dependencies cannot have the \ |
51 | same name: `{}`", | |
5295cadd | 52 | feature |
1e682848 | 53 | ) |
2b46d039 AC |
54 | } |
55 | if dep.is_optional() && !dep.is_transitive() { | |
3a18c89a | 56 | anyhow::bail!( |
1e682848 | 57 | "Dev-dependencies are not allowed to be optional: `{}`", |
5295cadd | 58 | feature |
1e682848 | 59 | ) |
2b46d039 AC |
60 | } |
61 | } | |
ef0b4776 | 62 | let feature_map = build_feature_map(features, &dependencies, namespaced_features)?; |
2b46d039 | 63 | Ok(Summary { |
0d17d6c4 AC |
64 | inner: Rc::new(Inner { |
65 | package_id: pkg_id, | |
78f35646 | 66 | dependencies, |
880337ac | 67 | features: Rc::new(feature_map), |
0d17d6c4 | 68 | checksum: None, |
c14bb6e0 | 69 | links: links.map(|l| l.into()), |
a9f163e3 | 70 | namespaced_features, |
0d17d6c4 | 71 | }), |
2b46d039 | 72 | }) |
1ce76415 CL |
73 | } |
74 | ||
dae87a26 E |
75 | pub fn package_id(&self) -> PackageId { |
76 | self.inner.package_id | |
1e682848 AC |
77 | } |
78 | pub fn name(&self) -> InternedString { | |
79 | self.package_id().name() | |
80 | } | |
81 | pub fn version(&self) -> &Version { | |
82 | self.package_id().version() | |
83 | } | |
e5a11190 | 84 | pub fn source_id(&self) -> SourceId { |
1e682848 AC |
85 | self.package_id().source_id() |
86 | } | |
87 | pub fn dependencies(&self) -> &[Dependency] { | |
88 | &self.inner.dependencies | |
89 | } | |
116a3cd2 | 90 | pub fn features(&self) -> &FeatureMap { |
1e682848 AC |
91 | &self.inner.features |
92 | } | |
5430db61 | 93 | pub fn checksum(&self) -> Option<&str> { |
d47a9545 | 94 | self.inner.checksum.as_deref() |
5430db61 | 95 | } |
9b731821 E |
96 | pub fn links(&self) -> Option<InternedString> { |
97 | self.inner.links | |
78f35646 | 98 | } |
a9f163e3 DO |
99 | pub fn namespaced_features(&self) -> bool { |
100 | self.inner.namespaced_features | |
101 | } | |
798b620c | 102 | |
816373d9 | 103 | pub fn override_id(mut self, id: PackageId) -> Summary { |
0d17d6c4 | 104 | Rc::make_mut(&mut self.inner).package_id = id; |
816373d9 AC |
105 | self |
106 | } | |
107 | ||
014ef2e4 | 108 | pub fn set_checksum(&mut self, cksum: String) { |
0d17d6c4 | 109 | Rc::make_mut(&mut self.inner).checksum = Some(cksum); |
5430db61 AC |
110 | } |
111 | ||
157d639a | 112 | pub fn map_dependencies<F>(mut self, f: F) -> Summary |
1e682848 AC |
113 | where |
114 | F: FnMut(Dependency) -> Dependency, | |
115 | { | |
0d17d6c4 AC |
116 | { |
117 | let slot = &mut Rc::make_mut(&mut self.inner).dependencies; | |
6a6fa368 | 118 | *slot = mem::take(slot).into_iter().map(f).collect(); |
0d17d6c4 | 119 | } |
798b620c AC |
120 | self |
121 | } | |
99ec335f | 122 | |
e5a11190 | 123 | pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Summary { |
99ec335f AC |
124 | let me = if self.package_id().source_id() == to_replace { |
125 | let new_id = self.package_id().with_source_id(replace_with); | |
126 | self.override_id(new_id) | |
127 | } else { | |
128 | self | |
129 | }; | |
1e682848 | 130 | me.map_dependencies(|dep| dep.map_source(to_replace, replace_with)) |
99ec335f | 131 | } |
1ce76415 | 132 | } |
da0ec9a3 | 133 | |
58518273 AC |
134 | impl PartialEq for Summary { |
135 | fn eq(&self, other: &Summary) -> bool { | |
0d17d6c4 | 136 | self.inner.package_id == other.inner.package_id |
58518273 AC |
137 | } |
138 | } | |
116a3cd2 | 139 | |
5ae13e32 E |
140 | impl Eq for Summary {} |
141 | ||
142 | impl Hash for Summary { | |
143 | fn hash<H: Hasher>(&self, state: &mut H) { | |
144 | self.inner.package_id.hash(state); | |
145 | } | |
146 | } | |
147 | ||
76211088 DO |
148 | // Checks features for errors, bailing out a CargoResult:Err if invalid, |
149 | // and creates FeatureValues for each feature. | |
ea957da7 | 150 | fn build_feature_map<K>( |
137d5cd1 | 151 | features: &BTreeMap<K, Vec<impl AsRef<str>>>, |
51d19d01 | 152 | dependencies: &[Dependency], |
cb533ae1 | 153 | namespaced: bool, |
ea957da7 | 154 | ) -> CargoResult<FeatureMap> |
e5a11190 E |
155 | where |
156 | K: Borrow<str> + Ord + Display, | |
157 | { | |
76211088 | 158 | use self::FeatureValue::*; |
02b0dba3 AC |
159 | let mut dep_map = HashMap::new(); |
160 | for dep in dependencies.iter() { | |
399319eb | 161 | dep_map |
5295cadd | 162 | .entry(dep.name_in_toml()) |
385b54b3 | 163 | .or_insert_with(Vec::new) |
02b0dba3 AC |
164 | .push(dep); |
165 | } | |
4966f539 | 166 | |
76211088 | 167 | let mut map = BTreeMap::new(); |
ea957da7 | 168 | for (feature, list) in features.iter() { |
cb533ae1 DO |
169 | // If namespaced features is active and the key is the same as that of an |
170 | // optional dependency, that dependency must be included in the values. | |
171 | // Thus, if a `feature` is found that has the same name as a dependency, we | |
172 | // (a) bail out if the dependency is non-optional, and (b) we track if the | |
173 | // feature requirements include the dependency `crate:feature` in the list. | |
174 | // This is done with the `dependency_found` variable, which can only be | |
175 | // false if features are namespaced and the current feature key is the same | |
176 | // as the name of an optional dependency. If so, it gets set to true during | |
177 | // iteration over the list if the dependency is found in the list. | |
178 | let mut dependency_found = if namespaced { | |
ea957da7 | 179 | match dep_map.get(feature.borrow()) { |
ef0b4776 | 180 | Some(dep_data) => { |
02b0dba3 | 181 | if !dep_data.iter().any(|d| d.is_optional()) { |
3a18c89a | 182 | anyhow::bail!( |
cb533ae1 DO |
183 | "Feature `{}` includes the dependency of the same name, but this is \ |
184 | left implicit in the features included by this feature.\n\ | |
185 | Additionally, the dependency must be marked as optional to be \ | |
186 | included in the feature definition.\n\ | |
187 | Consider adding `crate:{}` to this feature's requirements \ | |
188 | and marking the dependency as `optional = true`", | |
189 | feature, | |
190 | feature | |
191 | ) | |
192 | } else { | |
193 | false | |
194 | } | |
195 | } | |
196 | None => true, | |
197 | } | |
198 | } else { | |
199 | true | |
200 | }; | |
201 | ||
76211088 DO |
202 | let mut values = vec![]; |
203 | for dep in list { | |
cb533ae1 | 204 | let val = FeatureValue::build( |
ea957da7 | 205 | InternedString::new(dep.as_ref()), |
52635fe3 | 206 | |fs| features.contains_key(fs.as_str()), |
cb533ae1 DO |
207 | namespaced, |
208 | ); | |
76211088 DO |
209 | |
210 | // Find data for the referenced dependency... | |
211 | let dep_data = { | |
e357d174 | 212 | match val { |
cb533ae1 | 213 | Feature(ref dep_name) | Crate(ref dep_name) | CrateFeature(ref dep_name, _) => { |
4966f539 | 214 | dep_map.get(dep_name.as_str()) |
e357d174 DO |
215 | } |
216 | } | |
76211088 | 217 | }; |
399319eb E |
218 | let is_optional_dep = dep_data |
219 | .iter() | |
02b0dba3 AC |
220 | .flat_map(|d| d.iter()) |
221 | .any(|d| d.is_optional()); | |
cb533ae1 DO |
222 | if let FeatureValue::Crate(ref dep_name) = val { |
223 | // If we have a dependency value, check if this is the dependency named | |
224 | // the same as the feature that we were looking for. | |
ea957da7 | 225 | if !dependency_found && feature.borrow() == dep_name.as_str() { |
cb533ae1 DO |
226 | dependency_found = true; |
227 | } | |
228 | } | |
76211088 | 229 | |
cb533ae1 DO |
230 | match (&val, dep_data.is_some(), is_optional_dep) { |
231 | // The value is a feature. If features are namespaced, this just means | |
232 | // it's not prefixed with `crate:`, so we have to check whether the | |
233 | // feature actually exist. If the feature is not defined *and* an optional | |
234 | // dependency of the same name exists, the feature is defined implicitly | |
235 | // here by adding it to the feature map, pointing to the dependency. | |
236 | // If features are not namespaced, it's been validated as a feature already | |
237 | // while instantiating the `FeatureValue` in `FeatureValue::build()`, so | |
238 | // we don't have to do so here. | |
239 | (&Feature(feat), _, true) => { | |
240 | if namespaced && !features.contains_key(&*feat) { | |
52635fe3 | 241 | map.insert(feat, vec![FeatureValue::Crate(feat)]); |
76211088 DO |
242 | } |
243 | } | |
cb533ae1 DO |
244 | // If features are namespaced and the value is not defined as a feature |
245 | // and there is no optional dependency of the same name, error out. | |
246 | // If features are not namespaced, there must be an existing feature | |
247 | // here (checked by `FeatureValue::build()`), so it will always be defined. | |
248 | (&Feature(feat), dep_exists, false) => { | |
249 | if namespaced && !features.contains_key(&*feat) { | |
250 | if dep_exists { | |
3a18c89a | 251 | anyhow::bail!( |
cb533ae1 DO |
252 | "Feature `{}` includes `{}` which is not defined as a feature.\n\ |
253 | A non-optional dependency of the same name is defined; consider \ | |
254 | adding `optional = true` to its definition", | |
255 | feature, | |
256 | feat | |
257 | ) | |
258 | } else { | |
3a18c89a | 259 | anyhow::bail!( |
cb533ae1 DO |
260 | "Feature `{}` includes `{}` which is not defined as a feature", |
261 | feature, | |
262 | feat | |
263 | ) | |
264 | } | |
265 | } | |
266 | } | |
267 | // The value is a dependency. If features are namespaced, it is explicitly | |
268 | // tagged as such (`crate:value`). If features are not namespaced, any value | |
269 | // not recognized as a feature is pegged as a `Crate`. Here we handle the case | |
270 | // where the dependency exists but is non-optional. It branches on namespaced | |
271 | // just to provide the correct string for the crate dependency in the error. | |
e5a11190 E |
272 | (&Crate(ref dep), true, false) => { |
273 | if namespaced { | |
3a18c89a | 274 | anyhow::bail!( |
e5a11190 E |
275 | "Feature `{}` includes `crate:{}` which is not an \ |
276 | optional dependency.\nConsider adding \ | |
277 | `optional = true` to the dependency", | |
278 | feature, | |
279 | dep | |
280 | ) | |
281 | } else { | |
3a18c89a | 282 | anyhow::bail!( |
e5a11190 E |
283 | "Feature `{}` depends on `{}` which is not an \ |
284 | optional dependency.\nConsider adding \ | |
285 | `optional = true` to the dependency", | |
286 | feature, | |
287 | dep | |
288 | ) | |
289 | } | |
290 | } | |
cb533ae1 DO |
291 | // If namespaced, the value was tagged as a dependency; if not namespaced, |
292 | // this could be anything not defined as a feature. This handles the case | |
293 | // where no such dependency is actually defined; again, the branch on | |
294 | // namespaced here is just to provide the correct string in the error. | |
e5a11190 E |
295 | (&Crate(ref dep), false, _) => { |
296 | if namespaced { | |
3a18c89a | 297 | anyhow::bail!( |
e5a11190 E |
298 | "Feature `{}` includes `crate:{}` which is not a known \ |
299 | dependency", | |
300 | feature, | |
301 | dep | |
302 | ) | |
303 | } else { | |
3a18c89a | 304 | anyhow::bail!( |
e5a11190 E |
305 | "Feature `{}` includes `{}` which is neither a dependency nor \ |
306 | another feature", | |
307 | feature, | |
308 | dep | |
309 | ) | |
310 | } | |
311 | } | |
cb533ae1 DO |
312 | (&Crate(_), true, true) => {} |
313 | // If the value is a feature for one of the dependencies, bail out if no such | |
314 | // dependency is actually defined in the manifest. | |
3a18c89a | 315 | (&CrateFeature(ref dep, _), false, _) => anyhow::bail!( |
76211088 DO |
316 | "Feature `{}` requires a feature of `{}` which is not a \ |
317 | dependency", | |
318 | feature, | |
76211088 DO |
319 | dep |
320 | ), | |
cb533ae1 | 321 | (&CrateFeature(_, _), true, _) => {} |
76211088 DO |
322 | } |
323 | values.push(val); | |
324 | } | |
cb533ae1 DO |
325 | |
326 | if !dependency_found { | |
327 | // If we have not found the dependency of the same-named feature, we should | |
328 | // bail here. | |
3a18c89a | 329 | anyhow::bail!( |
cb533ae1 DO |
330 | "Feature `{}` includes the optional dependency of the \ |
331 | same name, but this is left implicit in the features \ | |
332 | included by this feature.\nConsider adding \ | |
333 | `crate:{}` to this feature's requirements.", | |
334 | feature, | |
335 | feature | |
336 | ) | |
337 | } | |
338 | ||
ea957da7 | 339 | map.insert(InternedString::new(feature.borrow()), values); |
76211088 DO |
340 | } |
341 | Ok(map) | |
342 | } | |
343 | ||
7b542268 DO |
344 | /// FeatureValue represents the types of dependencies a feature can have: |
345 | /// | |
346 | /// * Another feature | |
347 | /// * An optional dependency | |
4a64d05e | 348 | /// * A feature in a dependency |
7b542268 DO |
349 | /// |
350 | /// The selection between these 3 things happens as part of the construction of the FeatureValue. | |
540bd458 | 351 | #[derive(Clone, Debug)] |
7b542268 DO |
352 | pub enum FeatureValue { |
353 | Feature(InternedString), | |
354 | Crate(InternedString), | |
355 | CrateFeature(InternedString, InternedString), | |
356 | } | |
357 | ||
358 | impl FeatureValue { | |
cb533ae1 | 359 | fn build<T>(feature: InternedString, is_feature: T, namespaced: bool) -> FeatureValue |
7b542268 | 360 | where |
52635fe3 | 361 | T: Fn(InternedString) -> bool, |
7b542268 | 362 | { |
cb533ae1 DO |
363 | match (feature.find('/'), namespaced) { |
364 | (Some(pos), _) => { | |
7b542268 DO |
365 | let (dep, dep_feat) = feature.split_at(pos); |
366 | let dep_feat = &dep_feat[1..]; | |
367 | FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat)) | |
368 | } | |
cb533ae1 DO |
369 | (None, true) if feature.starts_with("crate:") => { |
370 | FeatureValue::Crate(InternedString::new(&feature[6..])) | |
371 | } | |
372 | (None, true) => FeatureValue::Feature(feature), | |
52635fe3 | 373 | (None, false) if is_feature(feature) => FeatureValue::Feature(feature), |
cb533ae1 | 374 | (None, false) => FeatureValue::Crate(feature), |
7b542268 DO |
375 | } |
376 | } | |
377 | ||
5c9d34be | 378 | pub fn new(feature: InternedString, s: &Summary) -> FeatureValue { |
cb533ae1 DO |
379 | Self::build( |
380 | feature, | |
52635fe3 | 381 | |fs| s.features().contains_key(&fs), |
cb533ae1 DO |
382 | s.namespaced_features(), |
383 | ) | |
7b542268 DO |
384 | } |
385 | ||
cb533ae1 | 386 | pub fn to_string(&self, s: &Summary) -> String { |
7b542268 DO |
387 | use self::FeatureValue::*; |
388 | match *self { | |
389 | Feature(ref f) => f.to_string(), | |
e5a11190 E |
390 | Crate(ref c) => { |
391 | if s.namespaced_features() { | |
392 | format!("crate:{}", &c) | |
393 | } else { | |
394 | c.to_string() | |
395 | } | |
396 | } | |
7b542268 DO |
397 | CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"), |
398 | } | |
399 | } | |
400 | } | |
401 | ||
540bd458 DO |
402 | impl Serialize for FeatureValue { |
403 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
404 | where | |
405 | S: Serializer, | |
406 | { | |
407 | use self::FeatureValue::*; | |
408 | match *self { | |
409 | Feature(ref f) => serializer.serialize_str(f), | |
410 | Crate(ref c) => serializer.serialize_str(c), | |
411 | CrateFeature(ref c, ref f) => { | |
412 | serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/")) | |
413 | } | |
414 | } | |
415 | } | |
416 | } | |
417 | ||
52635fe3 | 418 | pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>; |