]>
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>, |
116a3cd2 | 29 | features: 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>>>, |
ea957da7 | 40 | links: Option<impl AsRef<str>>, |
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() { | |
54c42142 | 49 | failure::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() { | |
54c42142 | 56 | failure::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, |
76211088 | 67 | features: feature_map, |
0d17d6c4 | 68 | checksum: None, |
ea957da7 | 69 | links: links.map(|l| InternedString::new(l.as_ref())), |
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> { |
0d17d6c4 | 94 | self.inner.checksum.as_ref().map(|s| &s[..]) |
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; | |
118 | let deps = mem::replace(slot, Vec::new()); | |
119 | *slot = deps.into_iter().map(f).collect(); | |
120 | } | |
798b620c AC |
121 | self |
122 | } | |
99ec335f | 123 | |
e5a11190 | 124 | pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Summary { |
99ec335f AC |
125 | let me = if self.package_id().source_id() == to_replace { |
126 | let new_id = self.package_id().with_source_id(replace_with); | |
127 | self.override_id(new_id) | |
128 | } else { | |
129 | self | |
130 | }; | |
1e682848 | 131 | me.map_dependencies(|dep| dep.map_source(to_replace, replace_with)) |
99ec335f | 132 | } |
1ce76415 | 133 | } |
da0ec9a3 | 134 | |
58518273 AC |
135 | impl PartialEq for Summary { |
136 | fn eq(&self, other: &Summary) -> bool { | |
0d17d6c4 | 137 | self.inner.package_id == other.inner.package_id |
58518273 AC |
138 | } |
139 | } | |
116a3cd2 | 140 | |
5ae13e32 E |
141 | impl Eq for Summary {} |
142 | ||
143 | impl Hash for Summary { | |
144 | fn hash<H: Hasher>(&self, state: &mut H) { | |
145 | self.inner.package_id.hash(state); | |
146 | } | |
147 | } | |
148 | ||
76211088 DO |
149 | // Checks features for errors, bailing out a CargoResult:Err if invalid, |
150 | // and creates FeatureValues for each feature. | |
ea957da7 | 151 | fn build_feature_map<K>( |
137d5cd1 | 152 | features: &BTreeMap<K, Vec<impl AsRef<str>>>, |
51d19d01 | 153 | dependencies: &[Dependency], |
cb533ae1 | 154 | namespaced: bool, |
ea957da7 | 155 | ) -> CargoResult<FeatureMap> |
e5a11190 E |
156 | where |
157 | K: Borrow<str> + Ord + Display, | |
158 | { | |
76211088 | 159 | use self::FeatureValue::*; |
02b0dba3 AC |
160 | let mut dep_map = HashMap::new(); |
161 | for dep in dependencies.iter() { | |
399319eb | 162 | dep_map |
5295cadd | 163 | .entry(dep.name_in_toml()) |
385b54b3 | 164 | .or_insert_with(Vec::new) |
02b0dba3 AC |
165 | .push(dep); |
166 | } | |
4966f539 | 167 | |
76211088 | 168 | let mut map = BTreeMap::new(); |
ea957da7 | 169 | for (feature, list) in features.iter() { |
cb533ae1 DO |
170 | // If namespaced features is active and the key is the same as that of an |
171 | // optional dependency, that dependency must be included in the values. | |
172 | // Thus, if a `feature` is found that has the same name as a dependency, we | |
173 | // (a) bail out if the dependency is non-optional, and (b) we track if the | |
174 | // feature requirements include the dependency `crate:feature` in the list. | |
175 | // This is done with the `dependency_found` variable, which can only be | |
176 | // false if features are namespaced and the current feature key is the same | |
177 | // as the name of an optional dependency. If so, it gets set to true during | |
178 | // iteration over the list if the dependency is found in the list. | |
179 | let mut dependency_found = if namespaced { | |
ea957da7 | 180 | match dep_map.get(feature.borrow()) { |
ef0b4776 | 181 | Some(dep_data) => { |
02b0dba3 | 182 | if !dep_data.iter().any(|d| d.is_optional()) { |
54c42142 | 183 | failure::bail!( |
cb533ae1 DO |
184 | "Feature `{}` includes the dependency of the same name, but this is \ |
185 | left implicit in the features included by this feature.\n\ | |
186 | Additionally, the dependency must be marked as optional to be \ | |
187 | included in the feature definition.\n\ | |
188 | Consider adding `crate:{}` to this feature's requirements \ | |
189 | and marking the dependency as `optional = true`", | |
190 | feature, | |
191 | feature | |
192 | ) | |
193 | } else { | |
194 | false | |
195 | } | |
196 | } | |
197 | None => true, | |
198 | } | |
199 | } else { | |
200 | true | |
201 | }; | |
202 | ||
76211088 DO |
203 | let mut values = vec![]; |
204 | for dep in list { | |
cb533ae1 | 205 | let val = FeatureValue::build( |
ea957da7 | 206 | InternedString::new(dep.as_ref()), |
52635fe3 | 207 | |fs| features.contains_key(fs.as_str()), |
cb533ae1 DO |
208 | namespaced, |
209 | ); | |
76211088 DO |
210 | |
211 | // Find data for the referenced dependency... | |
212 | let dep_data = { | |
e357d174 | 213 | match val { |
cb533ae1 | 214 | Feature(ref dep_name) | Crate(ref dep_name) | CrateFeature(ref dep_name, _) => { |
4966f539 | 215 | dep_map.get(dep_name.as_str()) |
e357d174 DO |
216 | } |
217 | } | |
76211088 | 218 | }; |
399319eb E |
219 | let is_optional_dep = dep_data |
220 | .iter() | |
02b0dba3 AC |
221 | .flat_map(|d| d.iter()) |
222 | .any(|d| d.is_optional()); | |
cb533ae1 DO |
223 | if let FeatureValue::Crate(ref dep_name) = val { |
224 | // If we have a dependency value, check if this is the dependency named | |
225 | // the same as the feature that we were looking for. | |
ea957da7 | 226 | if !dependency_found && feature.borrow() == dep_name.as_str() { |
cb533ae1 DO |
227 | dependency_found = true; |
228 | } | |
229 | } | |
76211088 | 230 | |
cb533ae1 DO |
231 | match (&val, dep_data.is_some(), is_optional_dep) { |
232 | // The value is a feature. If features are namespaced, this just means | |
233 | // it's not prefixed with `crate:`, so we have to check whether the | |
234 | // feature actually exist. If the feature is not defined *and* an optional | |
235 | // dependency of the same name exists, the feature is defined implicitly | |
236 | // here by adding it to the feature map, pointing to the dependency. | |
237 | // If features are not namespaced, it's been validated as a feature already | |
238 | // while instantiating the `FeatureValue` in `FeatureValue::build()`, so | |
239 | // we don't have to do so here. | |
240 | (&Feature(feat), _, true) => { | |
241 | if namespaced && !features.contains_key(&*feat) { | |
52635fe3 | 242 | map.insert(feat, vec![FeatureValue::Crate(feat)]); |
76211088 DO |
243 | } |
244 | } | |
cb533ae1 DO |
245 | // If features are namespaced and the value is not defined as a feature |
246 | // and there is no optional dependency of the same name, error out. | |
247 | // If features are not namespaced, there must be an existing feature | |
248 | // here (checked by `FeatureValue::build()`), so it will always be defined. | |
249 | (&Feature(feat), dep_exists, false) => { | |
250 | if namespaced && !features.contains_key(&*feat) { | |
251 | if dep_exists { | |
54c42142 | 252 | failure::bail!( |
cb533ae1 DO |
253 | "Feature `{}` includes `{}` which is not defined as a feature.\n\ |
254 | A non-optional dependency of the same name is defined; consider \ | |
255 | adding `optional = true` to its definition", | |
256 | feature, | |
257 | feat | |
258 | ) | |
259 | } else { | |
54c42142 | 260 | failure::bail!( |
cb533ae1 DO |
261 | "Feature `{}` includes `{}` which is not defined as a feature", |
262 | feature, | |
263 | feat | |
264 | ) | |
265 | } | |
266 | } | |
267 | } | |
268 | // The value is a dependency. If features are namespaced, it is explicitly | |
269 | // tagged as such (`crate:value`). If features are not namespaced, any value | |
270 | // not recognized as a feature is pegged as a `Crate`. Here we handle the case | |
271 | // where the dependency exists but is non-optional. It branches on namespaced | |
272 | // just to provide the correct string for the crate dependency in the error. | |
e5a11190 E |
273 | (&Crate(ref dep), true, false) => { |
274 | if namespaced { | |
54c42142 | 275 | failure::bail!( |
e5a11190 E |
276 | "Feature `{}` includes `crate:{}` which is not an \ |
277 | optional dependency.\nConsider adding \ | |
278 | `optional = true` to the dependency", | |
279 | feature, | |
280 | dep | |
281 | ) | |
282 | } else { | |
54c42142 | 283 | failure::bail!( |
e5a11190 E |
284 | "Feature `{}` depends on `{}` which is not an \ |
285 | optional dependency.\nConsider adding \ | |
286 | `optional = true` to the dependency", | |
287 | feature, | |
288 | dep | |
289 | ) | |
290 | } | |
291 | } | |
cb533ae1 DO |
292 | // If namespaced, the value was tagged as a dependency; if not namespaced, |
293 | // this could be anything not defined as a feature. This handles the case | |
294 | // where no such dependency is actually defined; again, the branch on | |
295 | // namespaced here is just to provide the correct string in the error. | |
e5a11190 E |
296 | (&Crate(ref dep), false, _) => { |
297 | if namespaced { | |
54c42142 | 298 | failure::bail!( |
e5a11190 E |
299 | "Feature `{}` includes `crate:{}` which is not a known \ |
300 | dependency", | |
301 | feature, | |
302 | dep | |
303 | ) | |
304 | } else { | |
54c42142 | 305 | failure::bail!( |
e5a11190 E |
306 | "Feature `{}` includes `{}` which is neither a dependency nor \ |
307 | another feature", | |
308 | feature, | |
309 | dep | |
310 | ) | |
311 | } | |
312 | } | |
cb533ae1 DO |
313 | (&Crate(_), true, true) => {} |
314 | // If the value is a feature for one of the dependencies, bail out if no such | |
315 | // dependency is actually defined in the manifest. | |
54c42142 | 316 | (&CrateFeature(ref dep, _), false, _) => failure::bail!( |
76211088 DO |
317 | "Feature `{}` requires a feature of `{}` which is not a \ |
318 | dependency", | |
319 | feature, | |
76211088 DO |
320 | dep |
321 | ), | |
cb533ae1 | 322 | (&CrateFeature(_, _), true, _) => {} |
76211088 DO |
323 | } |
324 | values.push(val); | |
325 | } | |
cb533ae1 DO |
326 | |
327 | if !dependency_found { | |
328 | // If we have not found the dependency of the same-named feature, we should | |
329 | // bail here. | |
54c42142 | 330 | failure::bail!( |
cb533ae1 DO |
331 | "Feature `{}` includes the optional dependency of the \ |
332 | same name, but this is left implicit in the features \ | |
333 | included by this feature.\nConsider adding \ | |
334 | `crate:{}` to this feature's requirements.", | |
335 | feature, | |
336 | feature | |
337 | ) | |
338 | } | |
339 | ||
ea957da7 | 340 | map.insert(InternedString::new(feature.borrow()), values); |
76211088 DO |
341 | } |
342 | Ok(map) | |
343 | } | |
344 | ||
7b542268 DO |
345 | /// FeatureValue represents the types of dependencies a feature can have: |
346 | /// | |
347 | /// * Another feature | |
348 | /// * An optional dependency | |
4a64d05e | 349 | /// * A feature in a dependency |
7b542268 DO |
350 | /// |
351 | /// The selection between these 3 things happens as part of the construction of the FeatureValue. | |
540bd458 | 352 | #[derive(Clone, Debug)] |
7b542268 DO |
353 | pub enum FeatureValue { |
354 | Feature(InternedString), | |
355 | Crate(InternedString), | |
356 | CrateFeature(InternedString, InternedString), | |
357 | } | |
358 | ||
359 | impl FeatureValue { | |
cb533ae1 | 360 | fn build<T>(feature: InternedString, is_feature: T, namespaced: bool) -> FeatureValue |
7b542268 | 361 | where |
52635fe3 | 362 | T: Fn(InternedString) -> bool, |
7b542268 | 363 | { |
cb533ae1 DO |
364 | match (feature.find('/'), namespaced) { |
365 | (Some(pos), _) => { | |
7b542268 DO |
366 | let (dep, dep_feat) = feature.split_at(pos); |
367 | let dep_feat = &dep_feat[1..]; | |
368 | FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat)) | |
369 | } | |
cb533ae1 DO |
370 | (None, true) if feature.starts_with("crate:") => { |
371 | FeatureValue::Crate(InternedString::new(&feature[6..])) | |
372 | } | |
373 | (None, true) => FeatureValue::Feature(feature), | |
52635fe3 | 374 | (None, false) if is_feature(feature) => FeatureValue::Feature(feature), |
cb533ae1 | 375 | (None, false) => FeatureValue::Crate(feature), |
7b542268 DO |
376 | } |
377 | } | |
378 | ||
5c9d34be | 379 | pub fn new(feature: InternedString, s: &Summary) -> FeatureValue { |
cb533ae1 DO |
380 | Self::build( |
381 | feature, | |
52635fe3 | 382 | |fs| s.features().contains_key(&fs), |
cb533ae1 DO |
383 | s.namespaced_features(), |
384 | ) | |
7b542268 DO |
385 | } |
386 | ||
cb533ae1 | 387 | pub fn to_string(&self, s: &Summary) -> String { |
7b542268 DO |
388 | use self::FeatureValue::*; |
389 | match *self { | |
390 | Feature(ref f) => f.to_string(), | |
e5a11190 E |
391 | Crate(ref c) => { |
392 | if s.namespaced_features() { | |
393 | format!("crate:{}", &c) | |
394 | } else { | |
395 | c.to_string() | |
396 | } | |
397 | } | |
7b542268 DO |
398 | CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"), |
399 | } | |
400 | } | |
401 | } | |
402 | ||
540bd458 DO |
403 | impl Serialize for FeatureValue { |
404 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
405 | where | |
406 | S: Serializer, | |
407 | { | |
408 | use self::FeatureValue::*; | |
409 | match *self { | |
410 | Feature(ref f) => serializer.serialize_str(f), | |
411 | Crate(ref c) => serializer.serialize_str(c), | |
412 | CrateFeature(ref c, ref f) => { | |
413 | serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/")) | |
414 | } | |
415 | } | |
416 | } | |
417 | } | |
418 | ||
52635fe3 | 419 | pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>; |