cargo/core/
dependency.rs

1use cargo_platform::Platform;
2use log::trace;
3use semver::ReqParseError;
4use semver::VersionReq;
5use serde::ser;
6use serde::Serialize;
7use std::rc::Rc;
8
9use crate::core::interning::InternedString;
10use crate::core::{PackageId, SourceId, Summary};
11use crate::util::errors::{CargoResult, CargoResultExt};
12use crate::util::Config;
13
14/// Information about a dependency requested by a Cargo manifest.
15/// Cheap to copy.
16#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
17pub struct Dependency {
18    inner: Rc<Inner>,
19}
20
21/// The data underlying a `Dependency`.
22#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
23struct Inner {
24    name: InternedString,
25    source_id: SourceId,
26    /// Source ID for the registry as specified in the manifest.
27    ///
28    /// This will be None if it is not specified (crates.io dependency).
29    /// This is different from `source_id` for example when both a `path` and
30    /// `registry` is specified. Or in the case of a crates.io dependency,
31    /// `source_id` will be crates.io and this will be None.
32    registry_id: Option<SourceId>,
33    req: VersionReq,
34    specified_req: bool,
35    kind: DepKind,
36    only_match_name: bool,
37    explicit_name_in_toml: Option<InternedString>,
38
39    optional: bool,
40    public: bool,
41    default_features: bool,
42    features: Vec<InternedString>,
43
44    // This dependency should be used only for this platform.
45    // `None` means *all platforms*.
46    platform: Option<Platform>,
47}
48
49#[derive(Serialize)]
50struct SerializedDependency<'a> {
51    name: &'a str,
52    source: SourceId,
53    req: String,
54    kind: DepKind,
55    rename: Option<&'a str>,
56
57    optional: bool,
58    uses_default_features: bool,
59    features: &'a [InternedString],
60    target: Option<&'a Platform>,
61    /// The registry URL this dependency is from.
62    /// If None, then it comes from the default registry (crates.io).
63    registry: Option<&'a str>,
64}
65
66impl ser::Serialize for Dependency {
67    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
68    where
69        S: ser::Serializer,
70    {
71        let registry_id = self.registry_id();
72        SerializedDependency {
73            name: &*self.package_name(),
74            source: self.source_id(),
75            req: self.version_req().to_string(),
76            kind: self.kind(),
77            optional: self.is_optional(),
78            uses_default_features: self.uses_default_features(),
79            features: self.features(),
80            target: self.platform(),
81            rename: self.explicit_name_in_toml().map(|s| s.as_str()),
82            registry: registry_id.as_ref().map(|sid| sid.url().as_str()),
83        }
84        .serialize(s)
85    }
86}
87
88#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
89pub enum DepKind {
90    Normal,
91    Development,
92    Build,
93}
94
95fn parse_req_with_deprecated(
96    name: InternedString,
97    req: &str,
98    extra: Option<(PackageId, &Config)>,
99) -> CargoResult<VersionReq> {
100    match VersionReq::parse(req) {
101        Err(ReqParseError::DeprecatedVersionRequirement(requirement)) => {
102            let (inside, config) = match extra {
103                Some(pair) => pair,
104                None => return Err(ReqParseError::DeprecatedVersionRequirement(requirement).into()),
105            };
106            let msg = format!(
107                "\
108parsed version requirement `{}` is no longer valid
109
110Previous versions of Cargo accepted this malformed requirement,
111but it is being deprecated. This was found when parsing the manifest
112of {} {}, and the correct version requirement is `{}`.
113
114This will soon become a hard error, so it's either recommended to
115update to a fixed version or contact the upstream maintainer about
116this warning.
117",
118                req,
119                inside.name(),
120                inside.version(),
121                requirement
122            );
123            config.shell().warn(&msg)?;
124
125            Ok(requirement)
126        }
127        Err(e) => {
128            let err: CargoResult<VersionReq> = Err(e.into());
129            let v: VersionReq = err.chain_err(|| {
130                format!(
131                    "failed to parse the version requirement `{}` for dependency `{}`",
132                    req, name
133                )
134            })?;
135            Ok(v)
136        }
137        Ok(v) => Ok(v),
138    }
139}
140
141impl ser::Serialize for DepKind {
142    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
143    where
144        S: ser::Serializer,
145    {
146        match *self {
147            DepKind::Normal => None,
148            DepKind::Development => Some("dev"),
149            DepKind::Build => Some("build"),
150        }
151        .serialize(s)
152    }
153}
154
155impl Dependency {
156    /// Attempt to create a `Dependency` from an entry in the manifest.
157    pub fn parse(
158        name: impl Into<InternedString>,
159        version: Option<&str>,
160        source_id: SourceId,
161        inside: PackageId,
162        config: &Config,
163    ) -> CargoResult<Dependency> {
164        let name = name.into();
165        let arg = Some((inside, config));
166        let (specified_req, version_req) = match version {
167            Some(v) => (true, parse_req_with_deprecated(name, v, arg)?),
168            None => (false, VersionReq::any()),
169        };
170
171        let mut ret = Dependency::new_override(name, source_id);
172        {
173            let ptr = Rc::make_mut(&mut ret.inner);
174            ptr.only_match_name = false;
175            ptr.req = version_req;
176            ptr.specified_req = specified_req;
177        }
178        Ok(ret)
179    }
180
181    /// Attempt to create a `Dependency` from an entry in the manifest.
182    pub fn parse_no_deprecated(
183        name: impl Into<InternedString>,
184        version: Option<&str>,
185        source_id: SourceId,
186    ) -> CargoResult<Dependency> {
187        let name = name.into();
188        let (specified_req, version_req) = match version {
189            Some(v) => (true, parse_req_with_deprecated(name, v, None)?),
190            None => (false, VersionReq::any()),
191        };
192
193        let mut ret = Dependency::new_override(name, source_id);
194        {
195            let ptr = Rc::make_mut(&mut ret.inner);
196            ptr.only_match_name = false;
197            ptr.req = version_req;
198            ptr.specified_req = specified_req;
199        }
200        Ok(ret)
201    }
202
203    pub fn new_override(name: InternedString, source_id: SourceId) -> Dependency {
204        assert!(!name.is_empty());
205        Dependency {
206            inner: Rc::new(Inner {
207                name,
208                source_id,
209                registry_id: None,
210                req: VersionReq::any(),
211                kind: DepKind::Normal,
212                only_match_name: true,
213                optional: false,
214                public: false,
215                features: Vec::new(),
216                default_features: true,
217                specified_req: false,
218                platform: None,
219                explicit_name_in_toml: None,
220            }),
221        }
222    }
223
224    pub fn version_req(&self) -> &VersionReq {
225        &self.inner.req
226    }
227
228    /// This is the name of this `Dependency` as listed in `Cargo.toml`.
229    ///
230    /// Or in other words, this is what shows up in the `[dependencies]` section
231    /// on the left hand side. This is *not* the name of the package that's
232    /// being depended on as the dependency can be renamed. For that use
233    /// `package_name` below.
234    ///
235    /// Both of the dependencies below return `foo` for `name_in_toml`:
236    ///
237    /// ```toml
238    /// [dependencies]
239    /// foo = "0.1"
240    /// ```
241    ///
242    /// and ...
243    ///
244    /// ```toml
245    /// [dependencies]
246    /// foo = { version = "0.1", package = 'bar' }
247    /// ```
248    pub fn name_in_toml(&self) -> InternedString {
249        self.explicit_name_in_toml().unwrap_or(self.inner.name)
250    }
251
252    /// The name of the package that this `Dependency` depends on.
253    ///
254    /// Usually this is what's written on the left hand side of a dependencies
255    /// section, but it can also be renamed via the `package` key.
256    ///
257    /// Both of the dependencies below return `foo` for `package_name`:
258    ///
259    /// ```toml
260    /// [dependencies]
261    /// foo = "0.1"
262    /// ```
263    ///
264    /// and ...
265    ///
266    /// ```toml
267    /// [dependencies]
268    /// bar = { version = "0.1", package = 'foo' }
269    /// ```
270    pub fn package_name(&self) -> InternedString {
271        self.inner.name
272    }
273
274    pub fn source_id(&self) -> SourceId {
275        self.inner.source_id
276    }
277
278    pub fn registry_id(&self) -> Option<SourceId> {
279        self.inner.registry_id
280    }
281
282    pub fn set_registry_id(&mut self, registry_id: SourceId) -> &mut Dependency {
283        Rc::make_mut(&mut self.inner).registry_id = Some(registry_id);
284        self
285    }
286
287    pub fn kind(&self) -> DepKind {
288        self.inner.kind
289    }
290
291    pub fn is_public(&self) -> bool {
292        self.inner.public
293    }
294
295    /// Sets whether the dependency is public.
296    pub fn set_public(&mut self, public: bool) -> &mut Dependency {
297        if public {
298            // Setting 'public' only makes sense for normal dependencies
299            assert_eq!(self.kind(), DepKind::Normal);
300        }
301        Rc::make_mut(&mut self.inner).public = public;
302        self
303    }
304
305    pub fn specified_req(&self) -> bool {
306        self.inner.specified_req
307    }
308
309    /// If none, this dependencies must be built for all platforms.
310    /// If some, it must only be built for the specified platform.
311    pub fn platform(&self) -> Option<&Platform> {
312        self.inner.platform.as_ref()
313    }
314
315    /// The renamed name of this dependency, if any.
316    ///
317    /// If the `package` key is used in `Cargo.toml` then this returns the same
318    /// value as `name_in_toml`.
319    pub fn explicit_name_in_toml(&self) -> Option<InternedString> {
320        self.inner.explicit_name_in_toml
321    }
322
323    pub fn set_kind(&mut self, kind: DepKind) -> &mut Dependency {
324        if self.is_public() {
325            // Setting 'public' only makes sense for normal dependencies
326            assert_eq!(kind, DepKind::Normal);
327        }
328        Rc::make_mut(&mut self.inner).kind = kind;
329        self
330    }
331
332    /// Sets the list of features requested for the package.
333    pub fn set_features(
334        &mut self,
335        features: impl IntoIterator<Item = impl Into<InternedString>>,
336    ) -> &mut Dependency {
337        Rc::make_mut(&mut self.inner).features = features.into_iter().map(|s| s.into()).collect();
338        self
339    }
340
341    /// Sets whether the dependency requests default features of the package.
342    pub fn set_default_features(&mut self, default_features: bool) -> &mut Dependency {
343        Rc::make_mut(&mut self.inner).default_features = default_features;
344        self
345    }
346
347    /// Sets whether the dependency is optional.
348    pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
349        Rc::make_mut(&mut self.inner).optional = optional;
350        self
351    }
352
353    /// Sets the source ID for this dependency.
354    pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
355        Rc::make_mut(&mut self.inner).source_id = id;
356        self
357    }
358
359    /// Sets the version requirement for this dependency.
360    pub fn set_version_req(&mut self, req: VersionReq) -> &mut Dependency {
361        Rc::make_mut(&mut self.inner).req = req;
362        self
363    }
364
365    pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
366        Rc::make_mut(&mut self.inner).platform = platform;
367        self
368    }
369
370    pub fn set_explicit_name_in_toml(
371        &mut self,
372        name: impl Into<InternedString>,
373    ) -> &mut Dependency {
374        Rc::make_mut(&mut self.inner).explicit_name_in_toml = Some(name.into());
375        self
376    }
377
378    /// Locks this dependency to depending on the specified package ID.
379    pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
380        assert_eq!(self.inner.source_id, id.source_id());
381        assert!(self.inner.req.matches(id.version()));
382        trace!(
383            "locking dep from `{}` with `{}` at {} to {}",
384            self.package_name(),
385            self.version_req(),
386            self.source_id(),
387            id
388        );
389        self.set_version_req(VersionReq::exact(id.version()))
390            .set_source_id(id.source_id())
391    }
392
393    /// Returns `true` if this is a "locked" dependency, basically whether it has
394    /// an exact version req.
395    pub fn is_locked(&self) -> bool {
396        // Kind of a hack to figure this out, but it works!
397        self.inner.req.to_string().starts_with('=')
398    }
399
400    /// Returns `false` if the dependency is only used to build the local package.
401    pub fn is_transitive(&self) -> bool {
402        match self.inner.kind {
403            DepKind::Normal | DepKind::Build => true,
404            DepKind::Development => false,
405        }
406    }
407
408    pub fn is_build(&self) -> bool {
409        match self.inner.kind {
410            DepKind::Build => true,
411            _ => false,
412        }
413    }
414
415    pub fn is_optional(&self) -> bool {
416        self.inner.optional
417    }
418
419    /// Returns `true` if the default features of the dependency are requested.
420    pub fn uses_default_features(&self) -> bool {
421        self.inner.default_features
422    }
423    /// Returns the list of features that are requested by the dependency.
424    pub fn features(&self) -> &[InternedString] {
425        &self.inner.features
426    }
427
428    /// Returns `true` if the package (`sum`) can fulfill this dependency request.
429    pub fn matches(&self, sum: &Summary) -> bool {
430        self.matches_id(sum.package_id())
431    }
432
433    /// Returns `true` if the package (`id`) can fulfill this dependency request.
434    pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
435        self.package_name() == id.name() && self.version_req().matches(id.version())
436    }
437
438    /// Returns `true` if the package (`id`) can fulfill this dependency request.
439    pub fn matches_id(&self, id: PackageId) -> bool {
440        self.inner.name == id.name()
441            && (self.inner.only_match_name
442                || (self.inner.req.matches(id.version()) && self.inner.source_id == id.source_id()))
443    }
444
445    pub fn map_source(mut self, to_replace: SourceId, replace_with: SourceId) -> Dependency {
446        if self.source_id() != to_replace {
447            self
448        } else {
449            self.set_source_id(replace_with);
450            self
451        }
452    }
453}