]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/summary.rs
one more small change
[cargo.git] / src / cargo / core / summary.rs
CommitLineData
4966f539 1use std::collections::{BTreeMap, HashMap};
798b620c 2use std::mem;
0d17d6c4 3use std::rc::Rc;
2b46d039 4
540bd458
DO
5use serde::{Serialize, Serializer};
6
9b731821 7use core::interning::InternedString;
5c9d34be
E
8use core::{Dependency, PackageId, SourceId};
9use semver::Version;
2b46d039 10
7ab18e3a 11use 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 18pub struct Summary {
0d17d6c4
AC
19 inner: Rc<Inner>,
20}
21
22#[derive(Debug, Clone)]
23struct 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
32impl 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
129impl 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.
137fn 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
332pub enum FeatureValue {
333 Feature(InternedString),
334 Crate(InternedString),
335 CrateFeature(InternedString, InternedString),
336}
337
338impl 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
380impl 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 396pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>;