Skip to main content

cargo/util/toml/
mod.rs

1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
2use std::fmt;
3use std::fs;
4use std::path::{Path, PathBuf};
5use std::rc::Rc;
6use std::str;
7
8use anyhow::{anyhow, bail};
9use cargo_platform::Platform;
10use log::{debug, trace};
11use semver::{self, VersionReq};
12use serde::de;
13use serde::ser;
14use serde::{Deserialize, Serialize};
15use url::Url;
16
17use crate::core::dependency::DepKind;
18use crate::core::manifest::{LibKind, ManifestMetadata, TargetSourcePath, Warnings};
19use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};
20use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest};
21use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
22use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
23use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
24use crate::util::{self, paths, validate_package_name, Config, IntoUrl};
25
26mod targets;
27use self::targets::targets;
28
29pub fn read_manifest(
30    path: &Path,
31    source_id: SourceId,
32    config: &Config,
33) -> Result<(EitherManifest, Vec<PathBuf>), ManifestError> {
34    trace!(
35        "read_manifest; path={}; source-id={}",
36        path.display(),
37        source_id
38    );
39    let contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
40
41    do_read_manifest(&contents, path, source_id, config)
42        .chain_err(|| format!("failed to parse manifest at `{}`", path.display()))
43        .map_err(|err| ManifestError::new(err, path.into()))
44}
45
46fn do_read_manifest(
47    contents: &str,
48    manifest_file: &Path,
49    source_id: SourceId,
50    config: &Config,
51) -> CargoResult<(EitherManifest, Vec<PathBuf>)> {
52    let package_root = manifest_file.parent().unwrap();
53
54    let toml = {
55        let pretty_filename = manifest_file
56            .strip_prefix(config.cwd())
57            .unwrap_or(manifest_file);
58        parse(contents, pretty_filename, config)?
59    };
60
61    let mut unused = BTreeSet::new();
62    let manifest: TomlManifest = serde_ignored::deserialize(toml, |path| {
63        let mut key = String::new();
64        stringify(&mut key, &path);
65        unused.insert(key);
66    })?;
67    let add_unused = |warnings: &mut Warnings| {
68        for key in unused {
69            warnings.add_warning(format!("unused manifest key: {}", key));
70            if key == "profiles.debug" {
71                warnings.add_warning("use `[profile.dev]` to configure debug builds".to_string());
72            }
73        }
74    };
75
76    let manifest = Rc::new(manifest);
77    return if manifest.project.is_some() || manifest.package.is_some() {
78        let (mut manifest, paths) =
79            TomlManifest::to_real_manifest(&manifest, source_id, package_root, config)?;
80        add_unused(manifest.warnings_mut());
81        if !manifest.targets().iter().any(|t| !t.is_custom_build()) {
82            bail!(
83                "no targets specified in the manifest\n  \
84                 either src/lib.rs, src/main.rs, a [lib] section, or \
85                 [[bin]] section must be present"
86            )
87        }
88        Ok((EitherManifest::Real(manifest), paths))
89    } else {
90        let (mut m, paths) =
91            TomlManifest::to_virtual_manifest(&manifest, source_id, package_root, config)?;
92        add_unused(m.warnings_mut());
93        Ok((EitherManifest::Virtual(m), paths))
94    };
95
96    fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {
97        use serde_ignored::Path;
98
99        match *path {
100            Path::Root => {}
101            Path::Seq { parent, index } => {
102                stringify(dst, parent);
103                if !dst.is_empty() {
104                    dst.push('.');
105                }
106                dst.push_str(&index.to_string());
107            }
108            Path::Map { parent, ref key } => {
109                stringify(dst, parent);
110                if !dst.is_empty() {
111                    dst.push('.');
112                }
113                dst.push_str(key);
114            }
115            Path::Some { parent }
116            | Path::NewtypeVariant { parent }
117            | Path::NewtypeStruct { parent } => stringify(dst, parent),
118        }
119    }
120}
121
122pub fn parse(toml: &str, file: &Path, config: &Config) -> CargoResult<toml::Value> {
123    let first_error = match toml.parse() {
124        Ok(ret) => return Ok(ret),
125        Err(e) => e,
126    };
127
128    let mut second_parser = toml::de::Deserializer::new(toml);
129    second_parser.set_require_newline_after_table(false);
130    if let Ok(ret) = toml::Value::deserialize(&mut second_parser) {
131        let msg = format!(
132            "\
133TOML file found which contains invalid syntax and will soon not parse
134at `{}`.
135
136The TOML spec requires newlines after table definitions (e.g., `[a] b = 1` is
137invalid), but this file has a table header which does not have a newline after
138it. A newline needs to be added and this warning will soon become a hard error
139in the future.",
140            file.display()
141        );
142        config.shell().warn(&msg)?;
143        return Ok(ret);
144    }
145
146    let mut third_parser = toml::de::Deserializer::new(toml);
147    third_parser.set_allow_duplicate_after_longer_table(true);
148    if let Ok(ret) = toml::Value::deserialize(&mut third_parser) {
149        let msg = format!(
150            "\
151TOML file found which contains invalid syntax and will soon not parse
152at `{}`.
153
154The TOML spec requires that each table header is defined at most once, but
155historical versions of Cargo have erroneously accepted this file. The table
156definitions will need to be merged together with one table header to proceed,
157and this will become a hard error in the future.",
158            file.display()
159        );
160        config.shell().warn(&msg)?;
161        return Ok(ret);
162    }
163
164    let first_error = anyhow::Error::from(first_error);
165    Err(first_error.context("could not parse input as TOML").into())
166}
167
168type TomlLibTarget = TomlTarget;
169type TomlBinTarget = TomlTarget;
170type TomlExampleTarget = TomlTarget;
171type TomlTestTarget = TomlTarget;
172type TomlBenchTarget = TomlTarget;
173
174#[derive(Clone, Debug, Serialize)]
175#[serde(untagged)]
176pub enum TomlDependency {
177    Simple(String),
178    Detailed(DetailedTomlDependency),
179}
180
181impl<'de> de::Deserialize<'de> for TomlDependency {
182    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183    where
184        D: de::Deserializer<'de>,
185    {
186        struct TomlDependencyVisitor;
187
188        impl<'de> de::Visitor<'de> for TomlDependencyVisitor {
189            type Value = TomlDependency;
190
191            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
192                formatter.write_str(
193                    "a version string like \"0.9.8\" or a \
194                     detailed dependency like { version = \"0.9.8\" }",
195                )
196            }
197
198            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
199            where
200                E: de::Error,
201            {
202                Ok(TomlDependency::Simple(s.to_owned()))
203            }
204
205            fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
206            where
207                V: de::MapAccess<'de>,
208            {
209                let mvd = de::value::MapAccessDeserializer::new(map);
210                DetailedTomlDependency::deserialize(mvd).map(TomlDependency::Detailed)
211            }
212        }
213
214        deserializer.deserialize_any(TomlDependencyVisitor)
215    }
216}
217
218#[derive(Deserialize, Serialize, Clone, Debug, Default)]
219#[serde(rename_all = "kebab-case")]
220pub struct DetailedTomlDependency {
221    version: Option<String>,
222    registry: Option<String>,
223    /// The URL of the `registry` field.
224    /// This is an internal implementation detail. When Cargo creates a
225    /// package, it replaces `registry` with `registry-index` so that the
226    /// manifest contains the correct URL. All users won't have the same
227    /// registry names configured, so Cargo can't rely on just the name for
228    /// crates published by other users.
229    registry_index: Option<String>,
230    path: Option<String>,
231    git: Option<String>,
232    branch: Option<String>,
233    tag: Option<String>,
234    rev: Option<String>,
235    features: Option<Vec<String>>,
236    optional: Option<bool>,
237    default_features: Option<bool>,
238    #[serde(rename = "default_features")]
239    default_features2: Option<bool>,
240    package: Option<String>,
241    public: Option<bool>,
242}
243
244#[derive(Debug, Deserialize, Serialize)]
245#[serde(rename_all = "kebab-case")]
246pub struct TomlManifest {
247    cargo_features: Option<Vec<String>>,
248    package: Option<Box<TomlProject>>,
249    project: Option<Box<TomlProject>>,
250    profile: Option<TomlProfiles>,
251    lib: Option<TomlLibTarget>,
252    bin: Option<Vec<TomlBinTarget>>,
253    example: Option<Vec<TomlExampleTarget>>,
254    test: Option<Vec<TomlTestTarget>>,
255    bench: Option<Vec<TomlTestTarget>>,
256    dependencies: Option<BTreeMap<String, TomlDependency>>,
257    dev_dependencies: Option<BTreeMap<String, TomlDependency>>,
258    #[serde(rename = "dev_dependencies")]
259    dev_dependencies2: Option<BTreeMap<String, TomlDependency>>,
260    build_dependencies: Option<BTreeMap<String, TomlDependency>>,
261    #[serde(rename = "build_dependencies")]
262    build_dependencies2: Option<BTreeMap<String, TomlDependency>>,
263    features: Option<BTreeMap<String, Vec<String>>>,
264    target: Option<BTreeMap<String, TomlPlatform>>,
265    replace: Option<BTreeMap<String, TomlDependency>>,
266    patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
267    workspace: Option<TomlWorkspace>,
268    badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
269}
270
271#[derive(Deserialize, Serialize, Clone, Debug, Default)]
272pub struct TomlProfiles(BTreeMap<InternedString, TomlProfile>);
273
274impl TomlProfiles {
275    pub fn get_all(&self) -> &BTreeMap<InternedString, TomlProfile> {
276        &self.0
277    }
278
279    pub fn get(&self, name: &str) -> Option<&TomlProfile> {
280        self.0.get(name)
281    }
282
283    pub fn validate(&self, features: &Features, warnings: &mut Vec<String>) -> CargoResult<()> {
284        for (name, profile) in &self.0 {
285            profile.validate(name, features, warnings)?;
286        }
287        Ok(())
288    }
289}
290
291#[derive(Clone, Debug, Eq, PartialEq)]
292pub struct TomlOptLevel(pub String);
293
294impl<'de> de::Deserialize<'de> for TomlOptLevel {
295    fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
296    where
297        D: de::Deserializer<'de>,
298    {
299        struct Visitor;
300
301        impl<'de> de::Visitor<'de> for Visitor {
302            type Value = TomlOptLevel;
303
304            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
305                formatter.write_str("an optimization level")
306            }
307
308            fn visit_i64<E>(self, value: i64) -> Result<TomlOptLevel, E>
309            where
310                E: de::Error,
311            {
312                Ok(TomlOptLevel(value.to_string()))
313            }
314
315            fn visit_str<E>(self, value: &str) -> Result<TomlOptLevel, E>
316            where
317                E: de::Error,
318            {
319                if value == "s" || value == "z" {
320                    Ok(TomlOptLevel(value.to_string()))
321                } else {
322                    Err(E::custom(format!(
323                        "must be an integer, `z`, or `s`, \
324                         but found: {}",
325                        value
326                    )))
327                }
328            }
329        }
330
331        d.deserialize_any(Visitor)
332    }
333}
334
335impl ser::Serialize for TomlOptLevel {
336    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
337    where
338        S: ser::Serializer,
339    {
340        match self.0.parse::<u32>() {
341            Ok(n) => n.serialize(serializer),
342            Err(_) => self.0.serialize(serializer),
343        }
344    }
345}
346
347#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
348#[serde(untagged)]
349pub enum U32OrBool {
350    U32(u32),
351    Bool(bool),
352}
353
354impl<'de> de::Deserialize<'de> for U32OrBool {
355    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
356    where
357        D: de::Deserializer<'de>,
358    {
359        struct Visitor;
360
361        impl<'de> de::Visitor<'de> for Visitor {
362            type Value = U32OrBool;
363
364            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
365                formatter.write_str("a boolean or an integer")
366            }
367
368            fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
369            where
370                E: de::Error,
371            {
372                Ok(U32OrBool::Bool(b))
373            }
374
375            fn visit_i64<E>(self, u: i64) -> Result<Self::Value, E>
376            where
377                E: de::Error,
378            {
379                Ok(U32OrBool::U32(u as u32))
380            }
381
382            fn visit_u64<E>(self, u: u64) -> Result<Self::Value, E>
383            where
384                E: de::Error,
385            {
386                Ok(U32OrBool::U32(u as u32))
387            }
388        }
389
390        deserializer.deserialize_any(Visitor)
391    }
392}
393
394#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
395#[serde(rename_all = "kebab-case")]
396pub struct TomlProfile {
397    pub opt_level: Option<TomlOptLevel>,
398    pub lto: Option<StringOrBool>,
399    pub codegen_units: Option<u32>,
400    pub debug: Option<U32OrBool>,
401    pub debug_assertions: Option<bool>,
402    pub rpath: Option<bool>,
403    pub panic: Option<String>,
404    pub overflow_checks: Option<bool>,
405    pub incremental: Option<bool>,
406    pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
407    pub build_override: Option<Box<TomlProfile>>,
408    pub dir_name: Option<InternedString>,
409    pub inherits: Option<InternedString>,
410}
411
412#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
413pub enum ProfilePackageSpec {
414    Spec(PackageIdSpec),
415    All,
416}
417
418impl ser::Serialize for ProfilePackageSpec {
419    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
420    where
421        S: ser::Serializer,
422    {
423        match *self {
424            ProfilePackageSpec::Spec(ref spec) => spec.serialize(s),
425            ProfilePackageSpec::All => "*".serialize(s),
426        }
427    }
428}
429
430impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
431    fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
432    where
433        D: de::Deserializer<'de>,
434    {
435        let string = String::deserialize(d)?;
436        if string == "*" {
437            Ok(ProfilePackageSpec::All)
438        } else {
439            PackageIdSpec::parse(&string)
440                .map_err(de::Error::custom)
441                .map(ProfilePackageSpec::Spec)
442        }
443    }
444}
445
446impl TomlProfile {
447    pub fn validate(
448        &self,
449        name: &str,
450        features: &Features,
451        warnings: &mut Vec<String>,
452    ) -> CargoResult<()> {
453        if name == "debug" {
454            warnings.push("use `[profile.dev]` to configure debug builds".to_string());
455        }
456
457        if let Some(ref profile) = self.build_override {
458            features.require(Feature::profile_overrides())?;
459            profile.validate_override("build-override")?;
460        }
461        if let Some(ref packages) = self.package {
462            features.require(Feature::profile_overrides())?;
463            for profile in packages.values() {
464                profile.validate_override("package")?;
465            }
466        }
467
468        // Feature gate definition of named profiles
469        match name {
470            "dev" | "release" | "bench" | "test" | "doc" => {}
471            _ => {
472                features.require(Feature::named_profiles())?;
473            }
474        }
475
476        // Profile name validation
477        Self::validate_name(name, "profile name")?;
478
479        // Feature gate on uses of keys related to named profiles
480        if self.inherits.is_some() {
481            features.require(Feature::named_profiles())?;
482        }
483
484        if self.dir_name.is_some() {
485            features.require(Feature::named_profiles())?;
486        }
487
488        // `dir-name` validation
489        match &self.dir_name {
490            None => {}
491            Some(dir_name) => {
492                Self::validate_name(dir_name, "dir-name")?;
493            }
494        }
495
496        // `inherits` validation
497        match &self.inherits {
498            None => {}
499            Some(inherits) => {
500                Self::validate_name(inherits, "inherits")?;
501            }
502        }
503
504        match name {
505            "doc" => {
506                warnings.push("profile `doc` is deprecated and has no effect".to_string());
507            }
508            "test" | "bench" => {
509                if self.panic.is_some() {
510                    warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
511                }
512            }
513            _ => {}
514        }
515
516        if let Some(panic) = &self.panic {
517            if panic != "unwind" && panic != "abort" {
518                bail!(
519                    "`panic` setting of `{}` is not a valid setting,\
520                     must be `unwind` or `abort`",
521                    panic
522                );
523            }
524        }
525        Ok(())
526    }
527
528    /// Validate dir-names and profile names according to RFC 2678.
529    pub fn validate_name(name: &str, what: &str) -> CargoResult<()> {
530        if let Some(ch) = name
531            .chars()
532            .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
533        {
534            bail!("Invalid character `{}` in {}: `{}`", ch, what, name);
535        }
536
537        match name {
538            "package" | "build" => {
539                bail!("Invalid {}: `{}`", what, name);
540            }
541            "debug" if what == "profile" => {
542                if what == "profile name" {
543                    // Allowed, but will emit warnings
544                } else {
545                    bail!("Invalid {}: `{}`", what, name);
546                }
547            }
548            "doc" if what == "dir-name" => {
549                bail!("Invalid {}: `{}`", what, name);
550            }
551            _ => {}
552        }
553
554        Ok(())
555    }
556
557    fn validate_override(&self, which: &str) -> CargoResult<()> {
558        if self.package.is_some() {
559            bail!("package-specific profiles cannot be nested");
560        }
561        if self.build_override.is_some() {
562            bail!("build-override profiles cannot be nested");
563        }
564        if self.panic.is_some() {
565            bail!("`panic` may not be specified in a `{}` profile", which)
566        }
567        if self.lto.is_some() {
568            bail!("`lto` may not be specified in a `{}` profile", which)
569        }
570        if self.rpath.is_some() {
571            bail!("`rpath` may not be specified in a `{}` profile", which)
572        }
573        Ok(())
574    }
575
576    /// Overwrite self's values with the given profile.
577    pub fn merge(&mut self, profile: &TomlProfile) {
578        if let Some(v) = &profile.opt_level {
579            self.opt_level = Some(v.clone());
580        }
581
582        if let Some(v) = &profile.lto {
583            self.lto = Some(v.clone());
584        }
585
586        if let Some(v) = profile.codegen_units {
587            self.codegen_units = Some(v);
588        }
589
590        if let Some(v) = &profile.debug {
591            self.debug = Some(v.clone());
592        }
593
594        if let Some(v) = profile.debug_assertions {
595            self.debug_assertions = Some(v);
596        }
597
598        if let Some(v) = profile.rpath {
599            self.rpath = Some(v);
600        }
601
602        if let Some(v) = &profile.panic {
603            self.panic = Some(v.clone());
604        }
605
606        if let Some(v) = profile.overflow_checks {
607            self.overflow_checks = Some(v);
608        }
609
610        if let Some(v) = profile.incremental {
611            self.incremental = Some(v);
612        }
613
614        if let Some(other_package) = &profile.package {
615            match &mut self.package {
616                Some(self_package) => {
617                    for (spec, other_pkg_profile) in other_package {
618                        match self_package.get_mut(spec) {
619                            Some(p) => p.merge(other_pkg_profile),
620                            None => {
621                                self_package.insert(spec.clone(), other_pkg_profile.clone());
622                            }
623                        }
624                    }
625                }
626                None => self.package = Some(other_package.clone()),
627            }
628        }
629
630        if let Some(other_bo) = &profile.build_override {
631            match &mut self.build_override {
632                Some(self_bo) => self_bo.merge(other_bo),
633                None => self.build_override = Some(other_bo.clone()),
634            }
635        }
636
637        if let Some(v) = &profile.inherits {
638            self.inherits = Some(*v);
639        }
640
641        if let Some(v) = &profile.dir_name {
642            self.dir_name = Some(*v);
643        }
644    }
645}
646
647#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
648pub struct StringOrVec(Vec<String>);
649
650impl<'de> de::Deserialize<'de> for StringOrVec {
651    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
652    where
653        D: de::Deserializer<'de>,
654    {
655        struct Visitor;
656
657        impl<'de> de::Visitor<'de> for Visitor {
658            type Value = StringOrVec;
659
660            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
661                formatter.write_str("string or list of strings")
662            }
663
664            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
665            where
666                E: de::Error,
667            {
668                Ok(StringOrVec(vec![s.to_string()]))
669            }
670
671            fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
672            where
673                V: de::SeqAccess<'de>,
674            {
675                let seq = de::value::SeqAccessDeserializer::new(v);
676                Vec::deserialize(seq).map(StringOrVec)
677            }
678        }
679
680        deserializer.deserialize_any(Visitor)
681    }
682}
683
684#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
685#[serde(untagged)]
686pub enum StringOrBool {
687    String(String),
688    Bool(bool),
689}
690
691impl<'de> de::Deserialize<'de> for StringOrBool {
692    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
693    where
694        D: de::Deserializer<'de>,
695    {
696        struct Visitor;
697
698        impl<'de> de::Visitor<'de> for Visitor {
699            type Value = StringOrBool;
700
701            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
702                formatter.write_str("a boolean or a string")
703            }
704
705            fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
706            where
707                E: de::Error,
708            {
709                Ok(StringOrBool::Bool(b))
710            }
711
712            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
713            where
714                E: de::Error,
715            {
716                Ok(StringOrBool::String(s.to_string()))
717            }
718        }
719
720        deserializer.deserialize_any(Visitor)
721    }
722}
723
724#[derive(PartialEq, Clone, Debug, Serialize)]
725#[serde(untagged)]
726pub enum VecStringOrBool {
727    VecString(Vec<String>),
728    Bool(bool),
729}
730
731impl<'de> de::Deserialize<'de> for VecStringOrBool {
732    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
733    where
734        D: de::Deserializer<'de>,
735    {
736        struct Visitor;
737
738        impl<'de> de::Visitor<'de> for Visitor {
739            type Value = VecStringOrBool;
740
741            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
742                formatter.write_str("a boolean or vector of strings")
743            }
744
745            fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
746            where
747                V: de::SeqAccess<'de>,
748            {
749                let seq = de::value::SeqAccessDeserializer::new(v);
750                Vec::deserialize(seq).map(VecStringOrBool::VecString)
751            }
752
753            fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
754            where
755                E: de::Error,
756            {
757                Ok(VecStringOrBool::Bool(b))
758            }
759        }
760
761        deserializer.deserialize_any(Visitor)
762    }
763}
764
765/// Represents the `package`/`project` sections of a `Cargo.toml`.
766///
767/// Note that the order of the fields matters, since this is the order they
768/// are serialized to a TOML file. For example, you cannot have values after
769/// the field `metadata`, since it is a table and values cannot appear after
770/// tables.
771#[derive(Deserialize, Serialize, Clone, Debug)]
772pub struct TomlProject {
773    edition: Option<String>,
774    name: InternedString,
775    version: semver::Version,
776    authors: Option<Vec<String>>,
777    build: Option<StringOrBool>,
778    metabuild: Option<StringOrVec>,
779    links: Option<String>,
780    exclude: Option<Vec<String>>,
781    include: Option<Vec<String>>,
782    publish: Option<VecStringOrBool>,
783    #[serde(rename = "publish-lockfile")]
784    publish_lockfile: Option<bool>,
785    workspace: Option<String>,
786    #[serde(rename = "im-a-teapot")]
787    im_a_teapot: Option<bool>,
788    autobins: Option<bool>,
789    autoexamples: Option<bool>,
790    autotests: Option<bool>,
791    autobenches: Option<bool>,
792    #[serde(rename = "namespaced-features")]
793    namespaced_features: Option<bool>,
794    #[serde(rename = "default-run")]
795    default_run: Option<String>,
796
797    // Package metadata.
798    description: Option<String>,
799    homepage: Option<String>,
800    documentation: Option<String>,
801    readme: Option<String>,
802    keywords: Option<Vec<String>>,
803    categories: Option<Vec<String>>,
804    license: Option<String>,
805    #[serde(rename = "license-file")]
806    license_file: Option<String>,
807    repository: Option<String>,
808    metadata: Option<toml::Value>,
809}
810
811#[derive(Debug, Deserialize, Serialize)]
812pub struct TomlWorkspace {
813    members: Option<Vec<String>>,
814    #[serde(rename = "default-members")]
815    default_members: Option<Vec<String>>,
816    exclude: Option<Vec<String>>,
817}
818
819impl TomlProject {
820    pub fn to_package_id(&self, source_id: SourceId) -> CargoResult<PackageId> {
821        PackageId::new(self.name, self.version.clone(), source_id)
822    }
823}
824
825struct Context<'a, 'b> {
826    pkgid: Option<PackageId>,
827    deps: &'a mut Vec<Dependency>,
828    source_id: SourceId,
829    nested_paths: &'a mut Vec<PathBuf>,
830    config: &'b Config,
831    warnings: &'a mut Vec<String>,
832    platform: Option<Platform>,
833    root: &'a Path,
834    features: &'a Features,
835}
836
837impl TomlManifest {
838    pub fn prepare_for_publish(
839        &self,
840        config: &Config,
841        package_root: &Path,
842    ) -> CargoResult<TomlManifest> {
843        let mut package = self
844            .package
845            .as_ref()
846            .or_else(|| self.project.as_ref())
847            .unwrap()
848            .clone();
849        package.workspace = None;
850        if let Some(license_file) = &package.license_file {
851            let license_path = Path::new(&license_file);
852            let abs_license_path = paths::normalize_path(&package_root.join(license_path));
853            if abs_license_path.strip_prefix(package_root).is_err() {
854                // This path points outside of the package root. `cargo package`
855                // will copy it into the root, so adjust the path to this location.
856                package.license_file = Some(
857                    license_path
858                        .file_name()
859                        .unwrap()
860                        .to_str()
861                        .unwrap()
862                        .to_string(),
863                );
864            }
865        }
866        let all = |_d: &TomlDependency| true;
867        return Ok(TomlManifest {
868            package: Some(package),
869            project: None,
870            profile: self.profile.clone(),
871            lib: self.lib.clone(),
872            bin: self.bin.clone(),
873            example: self.example.clone(),
874            test: self.test.clone(),
875            bench: self.bench.clone(),
876            dependencies: map_deps(config, self.dependencies.as_ref(), all)?,
877            dev_dependencies: map_deps(
878                config,
879                self.dev_dependencies
880                    .as_ref()
881                    .or_else(|| self.dev_dependencies2.as_ref()),
882                TomlDependency::is_version_specified,
883            )?,
884            dev_dependencies2: None,
885            build_dependencies: map_deps(
886                config,
887                self.build_dependencies
888                    .as_ref()
889                    .or_else(|| self.build_dependencies2.as_ref()),
890                all,
891            )?,
892            build_dependencies2: None,
893            features: self.features.clone(),
894            target: match self.target.as_ref().map(|target_map| {
895                target_map
896                    .iter()
897                    .map(|(k, v)| {
898                        Ok((
899                            k.clone(),
900                            TomlPlatform {
901                                dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
902                                dev_dependencies: map_deps(
903                                    config,
904                                    v.dev_dependencies
905                                        .as_ref()
906                                        .or_else(|| v.dev_dependencies2.as_ref()),
907                                    TomlDependency::is_version_specified,
908                                )?,
909                                dev_dependencies2: None,
910                                build_dependencies: map_deps(
911                                    config,
912                                    v.build_dependencies
913                                        .as_ref()
914                                        .or_else(|| v.build_dependencies2.as_ref()),
915                                    all,
916                                )?,
917                                build_dependencies2: None,
918                            },
919                        ))
920                    })
921                    .collect()
922            }) {
923                Some(Ok(v)) => Some(v),
924                Some(Err(e)) => return Err(e),
925                None => None,
926            },
927            replace: None,
928            patch: None,
929            workspace: None,
930            badges: self.badges.clone(),
931            cargo_features: self.cargo_features.clone(),
932        });
933
934        fn map_deps(
935            config: &Config,
936            deps: Option<&BTreeMap<String, TomlDependency>>,
937            filter: impl Fn(&TomlDependency) -> bool,
938        ) -> CargoResult<Option<BTreeMap<String, TomlDependency>>> {
939            let deps = match deps {
940                Some(deps) => deps,
941                None => return Ok(None),
942            };
943            let deps = deps
944                .iter()
945                .filter(|(_k, v)| filter(v))
946                .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?)))
947                .collect::<CargoResult<BTreeMap<_, _>>>()?;
948            Ok(Some(deps))
949        }
950
951        fn map_dependency(config: &Config, dep: &TomlDependency) -> CargoResult<TomlDependency> {
952            match dep {
953                TomlDependency::Detailed(d) => {
954                    let mut d = d.clone();
955                    // Path dependencies become crates.io deps.
956                    d.path.take();
957                    // Same with git dependencies.
958                    d.git.take();
959                    d.branch.take();
960                    d.tag.take();
961                    d.rev.take();
962                    // registry specifications are elaborated to the index URL
963                    if let Some(registry) = d.registry.take() {
964                        let src = SourceId::alt_registry(config, &registry)?;
965                        d.registry_index = Some(src.url().to_string());
966                    }
967                    Ok(TomlDependency::Detailed(d))
968                }
969                TomlDependency::Simple(s) => Ok(TomlDependency::Detailed(DetailedTomlDependency {
970                    version: Some(s.clone()),
971                    ..Default::default()
972                })),
973            }
974        }
975    }
976
977    pub fn to_real_manifest(
978        me: &Rc<TomlManifest>,
979        source_id: SourceId,
980        package_root: &Path,
981        config: &Config,
982    ) -> CargoResult<(Manifest, Vec<PathBuf>)> {
983        let mut nested_paths = vec![];
984        let mut warnings = vec![];
985        let mut errors = vec![];
986
987        // Parse features first so they will be available when parsing other parts of the TOML.
988        let empty = Vec::new();
989        let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
990        let features = Features::new(cargo_features, &mut warnings)?;
991
992        let project = me.project.as_ref().or_else(|| me.package.as_ref());
993        let project = project.ok_or_else(|| anyhow!("no `package` section found"))?;
994
995        let package_name = project.name.trim();
996        if package_name.is_empty() {
997            bail!("package name cannot be an empty string")
998        }
999
1000        validate_package_name(package_name, "package name", "")?;
1001
1002        let pkgid = project.to_package_id(source_id)?;
1003
1004        let edition = if let Some(ref edition) = project.edition {
1005            features
1006                .require(Feature::edition())
1007                .chain_err(|| "editions are unstable")?;
1008            edition
1009                .parse()
1010                .chain_err(|| "failed to parse the `edition` key")?
1011        } else {
1012            Edition::Edition2015
1013        };
1014
1015        if project.metabuild.is_some() {
1016            features.require(Feature::metabuild())?;
1017        }
1018
1019        // If we have no lib at all, use the inferred lib, if available.
1020        // If we have a lib with a path, we're done.
1021        // If we have a lib with no path, use the inferred lib or else the package name.
1022        let targets = targets(
1023            &features,
1024            me,
1025            package_name,
1026            package_root,
1027            edition,
1028            &project.build,
1029            &project.metabuild,
1030            &mut warnings,
1031            &mut errors,
1032        )?;
1033
1034        if targets.is_empty() {
1035            debug!("manifest has no build targets");
1036        }
1037
1038        if let Err(e) = unique_build_targets(&targets, package_root) {
1039            warnings.push(format!(
1040                "file found to be present in multiple \
1041                 build targets: {}",
1042                e
1043            ));
1044        }
1045
1046        if let Some(links) = &project.links {
1047            if !targets.iter().any(|t| t.is_custom_build()) {
1048                bail!(
1049                    "package `{}` specifies that it links to `{}` but does not \
1050                     have a custom build script",
1051                    pkgid,
1052                    links
1053                )
1054            }
1055        }
1056
1057        let mut deps = Vec::new();
1058        let replace;
1059        let patch;
1060
1061        {
1062            let mut cx = Context {
1063                pkgid: Some(pkgid),
1064                deps: &mut deps,
1065                source_id,
1066                nested_paths: &mut nested_paths,
1067                config,
1068                warnings: &mut warnings,
1069                features: &features,
1070                platform: None,
1071                root: package_root,
1072            };
1073
1074            fn process_dependencies(
1075                cx: &mut Context<'_, '_>,
1076                new_deps: Option<&BTreeMap<String, TomlDependency>>,
1077                kind: Option<DepKind>,
1078            ) -> CargoResult<()> {
1079                let dependencies = match new_deps {
1080                    Some(dependencies) => dependencies,
1081                    None => return Ok(()),
1082                };
1083                for (n, v) in dependencies.iter() {
1084                    let dep = v.to_dependency(n, cx, kind)?;
1085                    cx.deps.push(dep);
1086                }
1087
1088                Ok(())
1089            }
1090
1091            // Collect the dependencies.
1092            process_dependencies(&mut cx, me.dependencies.as_ref(), None)?;
1093            let dev_deps = me
1094                .dev_dependencies
1095                .as_ref()
1096                .or_else(|| me.dev_dependencies2.as_ref());
1097            process_dependencies(&mut cx, dev_deps, Some(DepKind::Development))?;
1098            let build_deps = me
1099                .build_dependencies
1100                .as_ref()
1101                .or_else(|| me.build_dependencies2.as_ref());
1102            process_dependencies(&mut cx, build_deps, Some(DepKind::Build))?;
1103
1104            for (name, platform) in me.target.iter().flatten() {
1105                cx.platform = {
1106                    let platform: Platform = name.parse()?;
1107                    platform.check_cfg_attributes(&mut cx.warnings);
1108                    Some(platform)
1109                };
1110                process_dependencies(&mut cx, platform.dependencies.as_ref(), None)?;
1111                let build_deps = platform
1112                    .build_dependencies
1113                    .as_ref()
1114                    .or_else(|| platform.build_dependencies2.as_ref());
1115                process_dependencies(&mut cx, build_deps, Some(DepKind::Build))?;
1116                let dev_deps = platform
1117                    .dev_dependencies
1118                    .as_ref()
1119                    .or_else(|| platform.dev_dependencies2.as_ref());
1120                process_dependencies(&mut cx, dev_deps, Some(DepKind::Development))?;
1121            }
1122
1123            replace = me.replace(&mut cx)?;
1124            patch = me.patch(&mut cx)?;
1125        }
1126
1127        {
1128            let mut names_sources = BTreeMap::new();
1129            for dep in &deps {
1130                let name = dep.name_in_toml();
1131                let prev = names_sources.insert(name.to_string(), dep.source_id());
1132                if prev.is_some() && prev != Some(dep.source_id()) {
1133                    bail!(
1134                        "Dependency '{}' has different source paths depending on the build \
1135                         target. Each dependency must have a single canonical source path \
1136                         irrespective of build target.",
1137                        name
1138                    );
1139                }
1140            }
1141        }
1142
1143        let exclude = project.exclude.clone().unwrap_or_default();
1144        let include = project.include.clone().unwrap_or_default();
1145        if project.namespaced_features.is_some() {
1146            features.require(Feature::namespaced_features())?;
1147        }
1148
1149        let summary_features = me
1150            .features
1151            .as_ref()
1152            .map(|x| {
1153                x.iter()
1154                    .map(|(k, v)| (k.as_str(), v.iter().collect()))
1155                    .collect()
1156            })
1157            .unwrap_or_else(BTreeMap::new);
1158        let summary = Summary::new(
1159            pkgid,
1160            deps,
1161            &summary_features,
1162            project.links.as_deref(),
1163            project.namespaced_features.unwrap_or(false),
1164        )?;
1165        let metadata = ManifestMetadata {
1166            description: project.description.clone(),
1167            homepage: project.homepage.clone(),
1168            documentation: project.documentation.clone(),
1169            readme: project.readme.clone(),
1170            authors: project.authors.clone().unwrap_or_default(),
1171            license: project.license.clone(),
1172            license_file: project.license_file.clone(),
1173            repository: project.repository.clone(),
1174            keywords: project.keywords.clone().unwrap_or_default(),
1175            categories: project.categories.clone().unwrap_or_default(),
1176            badges: me.badges.clone().unwrap_or_default(),
1177            links: project.links.clone(),
1178        };
1179
1180        let workspace_config = match (me.workspace.as_ref(), project.workspace.as_ref()) {
1181            (Some(config), None) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
1182                package_root,
1183                &config.members,
1184                &config.default_members,
1185                &config.exclude,
1186            )),
1187            (None, root) => WorkspaceConfig::Member {
1188                root: root.cloned(),
1189            },
1190            (Some(..), Some(..)) => bail!(
1191                "cannot configure both `package.workspace` and \
1192                 `[workspace]`, only one can be specified"
1193            ),
1194        };
1195        let profiles = me.profile.clone();
1196        if let Some(profiles) = &profiles {
1197            profiles.validate(&features, &mut warnings)?;
1198        }
1199        let publish = match project.publish {
1200            Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
1201            Some(VecStringOrBool::Bool(false)) => Some(vec![]),
1202            None | Some(VecStringOrBool::Bool(true)) => None,
1203        };
1204
1205        let publish_lockfile = match project.publish_lockfile {
1206            Some(b) => {
1207                features.require(Feature::publish_lockfile())?;
1208                warnings.push(
1209                    "The `publish-lockfile` feature is deprecated and currently \
1210                     has no effect. It may be removed in a future version."
1211                        .to_string(),
1212                );
1213                b
1214            }
1215            None => features.is_enabled(Feature::publish_lockfile()),
1216        };
1217
1218        if summary.features().contains_key("default-features") {
1219            warnings.push(
1220                "`default-features = [\"..\"]` was found in [features]. \
1221                 Did you mean to use `default = [\"..\"]`?"
1222                    .to_string(),
1223            )
1224        }
1225
1226        if let Some(run) = &project.default_run {
1227            if !targets
1228                .iter()
1229                .filter(|t| t.is_bin())
1230                .any(|t| t.name() == run)
1231            {
1232                let suggestion =
1233                    util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name());
1234                bail!("default-run target `{}` not found{}", run, suggestion);
1235            }
1236        }
1237
1238        let custom_metadata = project.metadata.clone();
1239        let mut manifest = Manifest::new(
1240            summary,
1241            targets,
1242            exclude,
1243            include,
1244            project.links.clone(),
1245            metadata,
1246            custom_metadata,
1247            profiles,
1248            publish,
1249            publish_lockfile,
1250            replace,
1251            patch,
1252            workspace_config,
1253            features,
1254            edition,
1255            project.im_a_teapot,
1256            project.default_run.clone(),
1257            Rc::clone(me),
1258            project.metabuild.clone().map(|sov| sov.0),
1259        );
1260        if project.license_file.is_some() && project.license.is_some() {
1261            manifest.warnings_mut().add_warning(
1262                "only one of `license` or \
1263                 `license-file` is necessary"
1264                    .to_string(),
1265            );
1266        }
1267        for warning in warnings {
1268            manifest.warnings_mut().add_warning(warning);
1269        }
1270        for error in errors {
1271            manifest.warnings_mut().add_critical_warning(error);
1272        }
1273
1274        manifest.feature_gate()?;
1275
1276        Ok((manifest, nested_paths))
1277    }
1278
1279    fn to_virtual_manifest(
1280        me: &Rc<TomlManifest>,
1281        source_id: SourceId,
1282        root: &Path,
1283        config: &Config,
1284    ) -> CargoResult<(VirtualManifest, Vec<PathBuf>)> {
1285        if me.project.is_some() {
1286            bail!("virtual manifests do not define [project]");
1287        }
1288        if me.package.is_some() {
1289            bail!("virtual manifests do not define [package]");
1290        }
1291        if me.lib.is_some() {
1292            bail!("virtual manifests do not specify [lib]");
1293        }
1294        if me.bin.is_some() {
1295            bail!("virtual manifests do not specify [[bin]]");
1296        }
1297        if me.example.is_some() {
1298            bail!("virtual manifests do not specify [[example]]");
1299        }
1300        if me.test.is_some() {
1301            bail!("virtual manifests do not specify [[test]]");
1302        }
1303        if me.bench.is_some() {
1304            bail!("virtual manifests do not specify [[bench]]");
1305        }
1306        if me.dependencies.is_some() {
1307            bail!("virtual manifests do not specify [dependencies]");
1308        }
1309        if me.dev_dependencies.is_some() || me.dev_dependencies2.is_some() {
1310            bail!("virtual manifests do not specify [dev-dependencies]");
1311        }
1312        if me.build_dependencies.is_some() || me.build_dependencies2.is_some() {
1313            bail!("virtual manifests do not specify [build-dependencies]");
1314        }
1315        if me.features.is_some() {
1316            bail!("virtual manifests do not specify [features]");
1317        }
1318        if me.target.is_some() {
1319            bail!("virtual manifests do not specify [target]");
1320        }
1321        if me.badges.is_some() {
1322            bail!("virtual manifests do not specify [badges]");
1323        }
1324
1325        let mut nested_paths = Vec::new();
1326        let mut warnings = Vec::new();
1327        let mut deps = Vec::new();
1328        let empty = Vec::new();
1329        let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
1330        let features = Features::new(cargo_features, &mut warnings)?;
1331
1332        let (replace, patch) = {
1333            let mut cx = Context {
1334                pkgid: None,
1335                deps: &mut deps,
1336                source_id,
1337                nested_paths: &mut nested_paths,
1338                config,
1339                warnings: &mut warnings,
1340                platform: None,
1341                features: &features,
1342                root,
1343            };
1344            (me.replace(&mut cx)?, me.patch(&mut cx)?)
1345        };
1346        let profiles = me.profile.clone();
1347        if let Some(profiles) = &profiles {
1348            profiles.validate(&features, &mut warnings)?;
1349        }
1350        let workspace_config = match me.workspace {
1351            Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
1352                root,
1353                &config.members,
1354                &config.default_members,
1355                &config.exclude,
1356            )),
1357            None => {
1358                bail!("virtual manifests must be configured with [workspace]");
1359            }
1360        };
1361        Ok((
1362            VirtualManifest::new(replace, patch, workspace_config, profiles, features),
1363            nested_paths,
1364        ))
1365    }
1366
1367    fn replace(&self, cx: &mut Context<'_, '_>) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
1368        if self.patch.is_some() && self.replace.is_some() {
1369            bail!("cannot specify both [replace] and [patch]");
1370        }
1371        let mut replace = Vec::new();
1372        for (spec, replacement) in self.replace.iter().flatten() {
1373            let mut spec = PackageIdSpec::parse(spec).chain_err(|| {
1374                format!(
1375                    "replacements must specify a valid semver \
1376                     version to replace, but `{}` does not",
1377                    spec
1378                )
1379            })?;
1380            if spec.url().is_none() {
1381                spec.set_url(CRATES_IO_INDEX.parse().unwrap());
1382            }
1383
1384            if replacement.is_version_specified() {
1385                bail!(
1386                    "replacements cannot specify a version \
1387                     requirement, but found one for `{}`",
1388                    spec
1389                );
1390            }
1391
1392            let mut dep = replacement.to_dependency(spec.name().as_str(), cx, None)?;
1393            {
1394                let version = spec.version().ok_or_else(|| {
1395                    anyhow!(
1396                        "replacements must specify a version \
1397                         to replace, but `{}` does not",
1398                        spec
1399                    )
1400                })?;
1401                dep.set_version_req(VersionReq::exact(version));
1402            }
1403            replace.push((spec, dep));
1404        }
1405        Ok(replace)
1406    }
1407
1408    fn patch(&self, cx: &mut Context<'_, '_>) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
1409        let mut patch = HashMap::new();
1410        for (url, deps) in self.patch.iter().flatten() {
1411            let url = match &url[..] {
1412                CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
1413                _ => cx
1414                    .config
1415                    .get_registry_index(url)
1416                    .or_else(|_| url.into_url())
1417                    .chain_err(|| {
1418                        format!("[patch] entry `{}` should be a URL or registry name", url)
1419                    })?,
1420            };
1421            patch.insert(
1422                url,
1423                deps.iter()
1424                    .map(|(name, dep)| dep.to_dependency(name, cx, None))
1425                    .collect::<CargoResult<Vec<_>>>()?,
1426            );
1427        }
1428        Ok(patch)
1429    }
1430
1431    fn maybe_custom_build(
1432        &self,
1433        build: &Option<StringOrBool>,
1434        package_root: &Path,
1435    ) -> Option<PathBuf> {
1436        let build_rs = package_root.join("build.rs");
1437        match *build {
1438            // Explicitly no build script.
1439            Some(StringOrBool::Bool(false)) => None,
1440            Some(StringOrBool::Bool(true)) => Some(build_rs),
1441            Some(StringOrBool::String(ref s)) => Some(PathBuf::from(s)),
1442            None => {
1443                match fs::metadata(&build_rs) {
1444                    // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1445                    // a build script.
1446                    Ok(ref e) if e.is_file() => Some(build_rs),
1447                    Ok(_) | Err(_) => None,
1448                }
1449            }
1450        }
1451    }
1452
1453    pub fn has_profiles(&self) -> bool {
1454        self.profile.is_some()
1455    }
1456}
1457
1458/// Checks a list of build targets, and ensures the target names are unique within a vector.
1459/// If not, the name of the offending build target is returned.
1460fn unique_build_targets(targets: &[Target], package_root: &Path) -> Result<(), String> {
1461    let mut seen = HashSet::new();
1462    for target in targets {
1463        if let TargetSourcePath::Path(path) = target.src_path() {
1464            let full = package_root.join(path);
1465            if !seen.insert(full.clone()) {
1466                return Err(full.display().to_string());
1467            }
1468        }
1469    }
1470    Ok(())
1471}
1472
1473impl TomlDependency {
1474    fn to_dependency(
1475        &self,
1476        name: &str,
1477        cx: &mut Context<'_, '_>,
1478        kind: Option<DepKind>,
1479    ) -> CargoResult<Dependency> {
1480        match *self {
1481            TomlDependency::Simple(ref version) => DetailedTomlDependency {
1482                version: Some(version.clone()),
1483                ..Default::default()
1484            }
1485            .to_dependency(name, cx, kind),
1486            TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
1487        }
1488    }
1489
1490    fn is_version_specified(&self) -> bool {
1491        match self {
1492            TomlDependency::Detailed(d) => d.version.is_some(),
1493            TomlDependency::Simple(..) => true,
1494        }
1495    }
1496}
1497
1498impl DetailedTomlDependency {
1499    fn to_dependency(
1500        &self,
1501        name_in_toml: &str,
1502        cx: &mut Context<'_, '_>,
1503        kind: Option<DepKind>,
1504    ) -> CargoResult<Dependency> {
1505        if self.version.is_none() && self.path.is_none() && self.git.is_none() {
1506            let msg = format!(
1507                "dependency ({}) specified without \
1508                 providing a local path, Git repository, or \
1509                 version to use. This will be considered an \
1510                 error in future versions",
1511                name_in_toml
1512            );
1513            cx.warnings.push(msg);
1514        }
1515
1516        if let Some(version) = &self.version {
1517            if version.contains('+') {
1518                cx.warnings.push(format!(
1519                    "version requirement `{}` for dependency `{}` \
1520                     includes semver metadata which will be ignored, removing the \
1521                     metadata is recommended to avoid confusion",
1522                    version, name_in_toml
1523                ));
1524            }
1525        }
1526
1527        if self.git.is_none() {
1528            let git_only_keys = [
1529                (&self.branch, "branch"),
1530                (&self.tag, "tag"),
1531                (&self.rev, "rev"),
1532            ];
1533
1534            for &(key, key_name) in &git_only_keys {
1535                if key.is_some() {
1536                    let msg = format!(
1537                        "key `{}` is ignored for dependency ({}). \
1538                         This will be considered an error in future versions",
1539                        key_name, name_in_toml
1540                    );
1541                    cx.warnings.push(msg)
1542                }
1543            }
1544        }
1545
1546        let new_source_id = match (
1547            self.git.as_ref(),
1548            self.path.as_ref(),
1549            self.registry.as_ref(),
1550            self.registry_index.as_ref(),
1551        ) {
1552            (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!(
1553                "dependency ({}) specification is ambiguous. \
1554                 Only one of `git` or `registry` is allowed.",
1555                name_in_toml
1556            ),
1557            (_, _, Some(_), Some(_)) => bail!(
1558                "dependency ({}) specification is ambiguous. \
1559                 Only one of `registry` or `registry-index` is allowed.",
1560                name_in_toml
1561            ),
1562            (Some(git), maybe_path, _, _) => {
1563                if maybe_path.is_some() {
1564                    let msg = format!(
1565                        "dependency ({}) specification is ambiguous. \
1566                         Only one of `git` or `path` is allowed. \
1567                         This will be considered an error in future versions",
1568                        name_in_toml
1569                    );
1570                    cx.warnings.push(msg)
1571                }
1572
1573                let n_details = [&self.branch, &self.tag, &self.rev]
1574                    .iter()
1575                    .filter(|d| d.is_some())
1576                    .count();
1577
1578                if n_details > 1 {
1579                    let msg = format!(
1580                        "dependency ({}) specification is ambiguous. \
1581                         Only one of `branch`, `tag` or `rev` is allowed. \
1582                         This will be considered an error in future versions",
1583                        name_in_toml
1584                    );
1585                    cx.warnings.push(msg)
1586                }
1587
1588                let reference = self
1589                    .branch
1590                    .clone()
1591                    .map(GitReference::Branch)
1592                    .or_else(|| self.tag.clone().map(GitReference::Tag))
1593                    .or_else(|| self.rev.clone().map(GitReference::Rev))
1594                    .unwrap_or_else(|| GitReference::Branch("master".to_string()));
1595                let loc = git.into_url()?;
1596                SourceId::for_git(&loc, reference)?
1597            }
1598            (None, Some(path), _, _) => {
1599                cx.nested_paths.push(PathBuf::from(path));
1600                // If the source ID for the package we're parsing is a path
1601                // source, then we normalize the path here to get rid of
1602                // components like `..`.
1603                //
1604                // The purpose of this is to get a canonical ID for the package
1605                // that we're depending on to ensure that builds of this package
1606                // always end up hashing to the same value no matter where it's
1607                // built from.
1608                if cx.source_id.is_path() {
1609                    let path = cx.root.join(path);
1610                    let path = util::normalize_path(&path);
1611                    SourceId::for_path(&path)?
1612                } else {
1613                    cx.source_id
1614                }
1615            }
1616            (None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?,
1617            (None, None, None, Some(registry_index)) => {
1618                let url = registry_index.into_url()?;
1619                SourceId::for_registry(&url)?
1620            }
1621            (None, None, None, None) => SourceId::crates_io(cx.config)?,
1622        };
1623
1624        let (pkg_name, explicit_name_in_toml) = match self.package {
1625            Some(ref s) => (&s[..], Some(name_in_toml)),
1626            None => (name_in_toml, None),
1627        };
1628
1629        let version = self.version.as_deref();
1630        let mut dep = match cx.pkgid {
1631            Some(id) => Dependency::parse(pkg_name, version, new_source_id, id, cx.config)?,
1632            None => Dependency::parse_no_deprecated(pkg_name, version, new_source_id)?,
1633        };
1634        dep.set_features(self.features.iter().flatten())
1635            .set_default_features(
1636                self.default_features
1637                    .or(self.default_features2)
1638                    .unwrap_or(true),
1639            )
1640            .set_optional(self.optional.unwrap_or(false))
1641            .set_platform(cx.platform.clone());
1642        if let Some(registry) = &self.registry {
1643            let registry_id = SourceId::alt_registry(cx.config, registry)?;
1644            dep.set_registry_id(registry_id);
1645        }
1646        if let Some(registry_index) = &self.registry_index {
1647            let url = registry_index.into_url()?;
1648            let registry_id = SourceId::for_registry(&url)?;
1649            dep.set_registry_id(registry_id);
1650        }
1651
1652        if let Some(kind) = kind {
1653            dep.set_kind(kind);
1654        }
1655        if let Some(name_in_toml) = explicit_name_in_toml {
1656            cx.features.require(Feature::rename_dependency())?;
1657            dep.set_explicit_name_in_toml(name_in_toml);
1658        }
1659
1660        if let Some(p) = self.public {
1661            cx.features.require(Feature::public_dependency())?;
1662
1663            if dep.kind() != DepKind::Normal {
1664                bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
1665            }
1666
1667            dep.set_public(p);
1668        }
1669        Ok(dep)
1670    }
1671}
1672
1673#[derive(Default, Serialize, Deserialize, Debug, Clone)]
1674struct TomlTarget {
1675    name: Option<String>,
1676
1677    // The intention was to only accept `crate-type` here but historical
1678    // versions of Cargo also accepted `crate_type`, so look for both.
1679    #[serde(rename = "crate-type")]
1680    crate_type: Option<Vec<String>>,
1681    #[serde(rename = "crate_type")]
1682    crate_type2: Option<Vec<String>>,
1683
1684    path: Option<PathValue>,
1685    test: Option<bool>,
1686    doctest: Option<bool>,
1687    bench: Option<bool>,
1688    doc: Option<bool>,
1689    plugin: Option<bool>,
1690    #[serde(rename = "proc-macro")]
1691    proc_macro: Option<bool>,
1692    #[serde(rename = "proc_macro")]
1693    proc_macro2: Option<bool>,
1694    harness: Option<bool>,
1695    #[serde(rename = "required-features")]
1696    required_features: Option<Vec<String>>,
1697    edition: Option<String>,
1698}
1699
1700#[derive(Clone)]
1701struct PathValue(PathBuf);
1702
1703impl<'de> de::Deserialize<'de> for PathValue {
1704    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1705    where
1706        D: de::Deserializer<'de>,
1707    {
1708        Ok(PathValue(String::deserialize(deserializer)?.into()))
1709    }
1710}
1711
1712impl ser::Serialize for PathValue {
1713    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1714    where
1715        S: ser::Serializer,
1716    {
1717        self.0.serialize(serializer)
1718    }
1719}
1720
1721/// Corresponds to a `target` entry, but `TomlTarget` is already used.
1722#[derive(Serialize, Deserialize, Debug)]
1723struct TomlPlatform {
1724    dependencies: Option<BTreeMap<String, TomlDependency>>,
1725    #[serde(rename = "build-dependencies")]
1726    build_dependencies: Option<BTreeMap<String, TomlDependency>>,
1727    #[serde(rename = "build_dependencies")]
1728    build_dependencies2: Option<BTreeMap<String, TomlDependency>>,
1729    #[serde(rename = "dev-dependencies")]
1730    dev_dependencies: Option<BTreeMap<String, TomlDependency>>,
1731    #[serde(rename = "dev_dependencies")]
1732    dev_dependencies2: Option<BTreeMap<String, TomlDependency>>,
1733}
1734
1735impl TomlTarget {
1736    fn new() -> TomlTarget {
1737        TomlTarget::default()
1738    }
1739
1740    fn name(&self) -> String {
1741        match self.name {
1742            Some(ref name) => name.clone(),
1743            None => panic!("target name is required"),
1744        }
1745    }
1746
1747    fn proc_macro(&self) -> Option<bool> {
1748        self.proc_macro.or(self.proc_macro2).or_else(|| {
1749            if let Some(types) = self.crate_types() {
1750                if types.contains(&"proc-macro".to_string()) {
1751                    return Some(true);
1752                }
1753            }
1754            None
1755        })
1756    }
1757
1758    fn crate_types(&self) -> Option<&Vec<String>> {
1759        self.crate_type
1760            .as_ref()
1761            .or_else(|| self.crate_type2.as_ref())
1762    }
1763}
1764
1765impl fmt::Debug for PathValue {
1766    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1767        self.0.fmt(f)
1768    }
1769}