]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/summary.rs
Simplify retrieval of dependency data
[cargo.git] / src / cargo / core / summary.rs
CommitLineData
f38c53f5 1use std::collections::BTreeMap;
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>,
1ce76415
CL
29}
30
31impl Summary {
1e682848
AC
32 pub fn new(
33 pkg_id: PackageId,
34 dependencies: Vec<Dependency>,
35 features: BTreeMap<String, Vec<String>>,
36 links: Option<String>,
37 ) -> CargoResult<Summary> {
2b46d039 38 for dep in dependencies.iter() {
85f96a99 39 if features.get(&*dep.name()).is_some() {
1e682848
AC
40 bail!(
41 "Features and dependencies cannot have the \
42 same name: `{}`",
43 dep.name()
44 )
2b46d039
AC
45 }
46 if dep.is_optional() && !dep.is_transitive() {
1e682848
AC
47 bail!(
48 "Dev-dependencies are not allowed to be optional: `{}`",
49 dep.name()
50 )
2b46d039
AC
51 }
52 }
76211088 53 let feature_map = build_feature_map(features, &dependencies)?;
2b46d039 54 Ok(Summary {
0d17d6c4
AC
55 inner: Rc::new(Inner {
56 package_id: pkg_id,
78f35646 57 dependencies,
76211088 58 features: feature_map,
0d17d6c4 59 checksum: None,
9b731821 60 links: links.map(|l| InternedString::new(&l)),
0d17d6c4 61 }),
2b46d039 62 })
1ce76415
CL
63 }
64
1e682848
AC
65 pub fn package_id(&self) -> &PackageId {
66 &self.inner.package_id
67 }
68 pub fn name(&self) -> InternedString {
69 self.package_id().name()
70 }
71 pub fn version(&self) -> &Version {
72 self.package_id().version()
73 }
74 pub fn source_id(&self) -> &SourceId {
75 self.package_id().source_id()
76 }
77 pub fn dependencies(&self) -> &[Dependency] {
78 &self.inner.dependencies
79 }
116a3cd2 80 pub fn features(&self) -> &FeatureMap {
1e682848
AC
81 &self.inner.features
82 }
5430db61 83 pub fn checksum(&self) -> Option<&str> {
0d17d6c4 84 self.inner.checksum.as_ref().map(|s| &s[..])
5430db61 85 }
9b731821
E
86 pub fn links(&self) -> Option<InternedString> {
87 self.inner.links
78f35646 88 }
798b620c 89
816373d9 90 pub fn override_id(mut self, id: PackageId) -> Summary {
0d17d6c4 91 Rc::make_mut(&mut self.inner).package_id = id;
816373d9
AC
92 self
93 }
94
5430db61 95 pub fn set_checksum(mut self, cksum: String) -> Summary {
0d17d6c4 96 Rc::make_mut(&mut self.inner).checksum = Some(cksum);
5430db61
AC
97 self
98 }
99
157d639a 100 pub fn map_dependencies<F>(mut self, f: F) -> Summary
1e682848
AC
101 where
102 F: FnMut(Dependency) -> Dependency,
103 {
0d17d6c4
AC
104 {
105 let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
106 let deps = mem::replace(slot, Vec::new());
107 *slot = deps.into_iter().map(f).collect();
108 }
798b620c
AC
109 self
110 }
99ec335f 111
1e682848 112 pub fn map_source(self, to_replace: &SourceId, replace_with: &SourceId) -> Summary {
99ec335f
AC
113 let me = if self.package_id().source_id() == to_replace {
114 let new_id = self.package_id().with_source_id(replace_with);
115 self.override_id(new_id)
116 } else {
117 self
118 };
1e682848 119 me.map_dependencies(|dep| dep.map_source(to_replace, replace_with))
99ec335f 120 }
1ce76415 121}
da0ec9a3 122
58518273
AC
123impl PartialEq for Summary {
124 fn eq(&self, other: &Summary) -> bool {
0d17d6c4 125 self.inner.package_id == other.inner.package_id
58518273
AC
126 }
127}
116a3cd2 128
76211088
DO
129// Checks features for errors, bailing out a CargoResult:Err if invalid,
130// and creates FeatureValues for each feature.
131fn build_feature_map(
132 features: BTreeMap<String, Vec<String>>,
51d19d01 133 dependencies: &[Dependency],
76211088
DO
134) -> CargoResult<FeatureMap> {
135 use self::FeatureValue::*;
136 let mut map = BTreeMap::new();
137 for (feature, list) in features.iter() {
138 let mut values = vec![];
139 for dep in list {
5c9d34be 140 let val = FeatureValue::build(InternedString::new(dep), |fs| features.contains_key(fs));
76211088
DO
141 if let &Feature(_) = &val {
142 values.push(val);
143 continue;
144 }
145
146 // Find data for the referenced dependency...
147 let dep_data = {
e357d174
DO
148 match val {
149 Feature(_) => None,
150 Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
151 dependencies.iter().find(|d| d.name() == *dep_name)
152 }
153 }
76211088
DO
154 };
155
156 match (&val, dep_data) {
157 (&Crate(ref dep), Some(d)) => {
158 if !d.is_optional() {
159 bail!(
160 "Feature `{}` depends on `{}` which is not an \
161 optional dependency.\nConsider adding \
162 `optional = true` to the dependency",
163 feature,
164 dep
165 )
166 }
167 }
168 (&CrateFeature(ref dep_name, _), None) => bail!(
169 "Feature `{}` requires a feature of `{}` which is not a \
170 dependency",
171 feature,
172 dep_name
173 ),
174 (&Crate(ref dep), None) => bail!(
175 "Feature `{}` includes `{}` which is neither \
176 a dependency nor another feature",
177 feature,
178 dep
179 ),
180 (&CrateFeature(_, _), Some(_)) | (&Feature(_), _) => {}
181 }
182 values.push(val);
183 }
184 map.insert(feature.clone(), values);
185 }
186 Ok(map)
187}
188
7b542268
DO
189/// FeatureValue represents the types of dependencies a feature can have:
190///
191/// * Another feature
192/// * An optional dependency
193/// * A feature in a depedency
194///
195/// The selection between these 3 things happens as part of the construction of the FeatureValue.
540bd458 196#[derive(Clone, Debug)]
7b542268
DO
197pub enum FeatureValue {
198 Feature(InternedString),
199 Crate(InternedString),
200 CrateFeature(InternedString, InternedString),
201}
202
203impl FeatureValue {
5c9d34be 204 fn build<T>(feature: InternedString, is_feature: T) -> FeatureValue
7b542268
DO
205 where
206 T: Fn(&str) -> bool,
207 {
208 match feature.find('/') {
209 Some(pos) => {
210 let (dep, dep_feat) = feature.split_at(pos);
211 let dep_feat = &dep_feat[1..];
212 FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
213 }
5c9d34be
E
214 None if is_feature(&feature) => FeatureValue::Feature(feature),
215 None => FeatureValue::Crate(feature),
7b542268
DO
216 }
217 }
218
5c9d34be
E
219 pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
220 Self::build(feature, |fs| s.features().contains_key(fs))
7b542268
DO
221 }
222
223 pub fn to_string(&self) -> String {
224 use self::FeatureValue::*;
225 match *self {
226 Feature(ref f) => f.to_string(),
227 Crate(ref c) => c.to_string(),
228 CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
229 }
230 }
231}
232
540bd458
DO
233impl Serialize for FeatureValue {
234 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
235 where
236 S: Serializer,
237 {
238 use self::FeatureValue::*;
239 match *self {
240 Feature(ref f) => serializer.serialize_str(f),
241 Crate(ref c) => serializer.serialize_str(c),
242 CrateFeature(ref c, ref f) => {
243 serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/"))
244 }
245 }
246 }
247}
248
7b542268 249pub type FeatureMap = BTreeMap<String, Vec<FeatureValue>>;