]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/summary.rs
Auto merge of #8416 - est31:remove_derive, r=alexcrichton
[cargo.git] / src / cargo / core / summary.rs
CommitLineData
ea957da7 1use std::borrow::Borrow;
4966f539 2use std::collections::{BTreeMap, HashMap};
ea957da7 3use std::fmt::Display;
5ae13e32 4use std::hash::{Hash, Hasher};
798b620c 5use std::mem;
0d17d6c4 6use std::rc::Rc;
2b46d039 7
540bd458
DO
8use serde::{Serialize, Serializer};
9
04ddd4d0
DW
10use crate::core::interning::InternedString;
11use crate::core::{Dependency, PackageId, SourceId};
5c9d34be 12use semver::Version;
2b46d039 13
04ddd4d0 14use 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 21pub struct Summary {
0d17d6c4
AC
22 inner: Rc<Inner>,
23}
24
25#[derive(Debug, Clone)]
26struct 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
35impl 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
134impl 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
140impl Eq for Summary {}
141
142impl 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 150fn 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
155where
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
352pub enum FeatureValue {
353 Feature(InternedString),
354 Crate(InternedString),
355 CrateFeature(InternedString, InternedString),
356}
357
358impl 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
402impl 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 418pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>;