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