#
cargo_util_schemas/manifest/
mod.rs

    1//! `Cargo.toml` / Manifest schema definition
    2//!
    3//! ## Style
    4//!
    5//! - Fields duplicated for an alias will have an accessor with the primary field's name
    6//! - Keys that exist for bookkeeping but don't correspond to the schema have a `_` prefix
    7
    8use std::collections::BTreeMap;
    9use std::collections::BTreeSet;
    10#[cfg(feature = "unstable-schema")]
    11use std::collections::HashMap;
    12use std::fmt::{self, Display, Write};
    13use std::path::PathBuf;
    14use std::str;
    15
    16use serde::de::{self, IntoDeserializer as _, Unexpected};
    17use serde::ser;
    18use serde::{Deserialize, Serialize};
    19use serde_untagged::UntaggedEnumVisitor;
    20
    21use crate::core::PackageIdSpec;
    22use crate::restricted_names;
    23
    24mod rust_version;
    25
    26pub use crate::restricted_names::NameValidationError;
    27pub use rust_version::RustVersion;
    28pub use rust_version::RustVersionError;
    29
    30#[cfg(feature = "unstable-schema")]
    31use crate::schema::TomlValueWrapper;
    32
    33/// This type is used to deserialize `Cargo.toml` files.
    34#[derive(Default, Clone, Debug, Deserialize, Serialize)]
    35#[serde(rename_all = "kebab-case")]
    36#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    37pub struct TomlManifest {
    38    pub cargo_features: Option<Vec<String>>,
    39
    40    // Update `requires_package` when adding new package-specific fields
    41    pub package: Option<Box<TomlPackage>>,
    42    pub project: Option<Box<TomlPackage>>,
    43    pub badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
    44    pub features: Option<BTreeMap<FeatureName, Vec<String>>>,
    45    pub lib: Option<TomlLibTarget>,
    46    pub bin: Option<Vec<TomlBinTarget>>,
    47    pub example: Option<Vec<TomlExampleTarget>>,
    48    pub test: Option<Vec<TomlTestTarget>>,
    49    pub bench: Option<Vec<TomlTestTarget>>,
    50    pub dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
    51    pub dev_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
    52    #[serde(rename = "dev_dependencies")]
    53    pub dev_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
    54    pub build_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
    55    #[serde(rename = "build_dependencies")]
    56    pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
    57    pub target: Option<BTreeMap<String, TomlPlatform>>,
    58    pub lints: Option<InheritableLints>,
    59    pub hints: Option<Hints>,
    60
    61    pub workspace: Option<TomlWorkspace>,
    62    pub profile: Option<TomlProfiles>,
    63    pub patch: Option<BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>,
    64    pub replace: Option<BTreeMap<String, TomlDependency>>,
    65
    66    /// Report unused keys (see also nested `_unused_keys`)
    67    /// Note: this is populated by the caller, rather than automatically
    68    #[serde(skip)]
    69    pub _unused_keys: BTreeSet<String>,
    70}
    71
    72impl TomlManifest {
    73    pub fn requires_package(&self) -> impl Iterator<Item = &'static str> {
    74        [
    75            self.badges.as_ref().map(|_| "badges"),
    76            self.features.as_ref().map(|_| "features"),
    77            self.lib.as_ref().map(|_| "lib"),
    78            self.bin.as_ref().map(|_| "bin"),
    79            self.example.as_ref().map(|_| "example"),
    80            self.test.as_ref().map(|_| "test"),
    81            self.bench.as_ref().map(|_| "bench"),
    82            self.dependencies.as_ref().map(|_| "dependencies"),
    83            self.dev_dependencies().as_ref().map(|_| "dev-dependencies"),
    84            self.build_dependencies()
    85                .as_ref()
    86                .map(|_| "build-dependencies"),
    87            self.target.as_ref().map(|_| "target"),
    88            self.lints.as_ref().map(|_| "lints"),
    89            self.hints.as_ref().map(|_| "hints"),
    90        ]
    91        .into_iter()
    92        .flatten()
    93    }
    94
    95    pub fn has_profiles(&self) -> bool {
    96        self.profile.is_some()
    97    }
    98
    99    pub fn package(&self) -> Option<&Box<TomlPackage>> {
    100        self.package.as_ref().or(self.project.as_ref())
    101    }
    102
    103    pub fn dev_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
    104        self.dev_dependencies
    105            .as_ref()
    106            .or(self.dev_dependencies2.as_ref())
    107    }
    108
    109    pub fn build_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
    110        self.build_dependencies
    111            .as_ref()
    112            .or(self.build_dependencies2.as_ref())
    113    }
    114
    115    pub fn features(&self) -> Option<&BTreeMap<FeatureName, Vec<String>>> {
    116        self.features.as_ref()
    117    }
    118
    119    pub fn normalized_lints(&self) -> Result<Option<&TomlLints>, UnresolvedError> {
    120        self.lints.as_ref().map(|l| l.normalized()).transpose()
    121    }
    122}
    123
    124#[derive(Debug, Default, Deserialize, Serialize, Clone)]
    125#[serde(rename_all = "kebab-case")]
    126#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    127pub struct TomlWorkspace {
    128    pub members: Option<Vec<String>>,
    129    pub exclude: Option<Vec<String>>,
    130    pub default_members: Option<Vec<String>>,
    131    pub resolver: Option<String>,
    132
    133    #[cfg_attr(
    134        feature = "unstable-schema",
    135        schemars(with = "Option<TomlValueWrapper>")
    136    )]
    137    pub metadata: Option<toml::Value>,
    138
    139    // Properties that can be inherited by members.
    140    pub package: Option<InheritablePackage>,
    141    pub dependencies: Option<BTreeMap<PackageName, TomlDependency>>,
    142    pub lints: Option<TomlLints>,
    143}
    144
    145/// A group of fields that are inheritable by members of the workspace
    146#[derive(Clone, Debug, Default, Deserialize, Serialize)]
    147#[serde(rename_all = "kebab-case")]
    148#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    149pub struct InheritablePackage {
    150    pub version: Option<semver::Version>,
    151    pub authors: Option<Vec<String>>,
    152    pub description: Option<String>,
    153    pub homepage: Option<String>,
    154    pub documentation: Option<String>,
    155    pub readme: Option<StringOrBool>,
    156    pub keywords: Option<Vec<String>>,
    157    pub categories: Option<Vec<String>>,
    158    pub license: Option<String>,
    159    pub license_file: Option<String>,
    160    pub repository: Option<String>,
    161    pub publish: Option<VecStringOrBool>,
    162    pub edition: Option<String>,
    163    pub badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
    164    pub exclude: Option<Vec<String>>,
    165    pub include: Option<Vec<String>>,
    166    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    167    pub rust_version: Option<RustVersion>,
    168}
    169
    170/// Represents the `package`/`project` sections of a `Cargo.toml`.
    171///
    172/// Note that the order of the fields matters, since this is the order they
    173/// are serialized to a TOML file. For example, you cannot have values after
    174/// the field `metadata`, since it is a table and values cannot appear after
    175/// tables.
    176#[derive(Deserialize, Serialize, Clone, Debug, Default)]
    177#[serde(rename_all = "kebab-case")]
    178#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    179pub struct TomlPackage {
    180    pub edition: Option<InheritableString>,
    181    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    182    pub rust_version: Option<InheritableRustVersion>,
    183    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    184    pub name: Option<PackageName>,
    185    pub version: Option<InheritableSemverVersion>,
    186    pub authors: Option<InheritableVecString>,
    187    pub build: Option<TomlPackageBuild>,
    188    pub metabuild: Option<StringOrVec>,
    189    pub default_target: Option<String>,
    190    pub forced_target: Option<String>,
    191    pub links: Option<String>,
    192    pub exclude: Option<InheritableVecString>,
    193    pub include: Option<InheritableVecString>,
    194    pub publish: Option<InheritableVecStringOrBool>,
    195    pub workspace: Option<String>,
    196    pub im_a_teapot: Option<bool>,
    197    pub autolib: Option<bool>,
    198    pub autobins: Option<bool>,
    199    pub autoexamples: Option<bool>,
    200    pub autotests: Option<bool>,
    201    pub autobenches: Option<bool>,
    202    pub default_run: Option<String>,
    203
    204    // Package metadata.
    205    pub description: Option<InheritableString>,
    206    pub homepage: Option<InheritableString>,
    207    pub documentation: Option<InheritableString>,
    208    pub readme: Option<InheritableStringOrBool>,
    209    pub keywords: Option<InheritableVecString>,
    210    pub categories: Option<InheritableVecString>,
    211    pub license: Option<InheritableString>,
    212    pub license_file: Option<InheritableString>,
    213    pub repository: Option<InheritableString>,
    214    pub resolver: Option<String>,
    215
    216    #[cfg_attr(
    217        feature = "unstable-schema",
    218        schemars(with = "Option<TomlValueWrapper>")
    219    )]
    220    pub metadata: Option<toml::Value>,
    221
    222    /// Provide a helpful error message for a common user error.
    223    #[serde(rename = "cargo-features", skip_serializing)]
    224    #[cfg_attr(feature = "unstable-schema", schemars(skip))]
    225    pub _invalid_cargo_features: Option<InvalidCargoFeatures>,
    226}
    227
    228impl TomlPackage {
    229    pub fn new(name: PackageName) -> Self {
    230        Self {
    231            name: Some(name),
    232            ..Default::default()
    233        }
    234    }
    235
    236    pub fn normalized_name(&self) -> Result<&PackageName, UnresolvedError> {
    237        self.name.as_ref().ok_or(UnresolvedError)
    238    }
    239
    240    pub fn normalized_edition(&self) -> Result<Option<&String>, UnresolvedError> {
    241        self.edition.as_ref().map(|v| v.normalized()).transpose()
    242    }
    243
    244    pub fn normalized_rust_version(&self) -> Result<Option<&RustVersion>, UnresolvedError> {
    245        self.rust_version
    246            .as_ref()
    247            .map(|v| v.normalized())
    248            .transpose()
    249    }
    250
    251    pub fn normalized_version(&self) -> Result<Option<&semver::Version>, UnresolvedError> {
    252        self.version.as_ref().map(|v| v.normalized()).transpose()
    253    }
    254
    255    pub fn normalized_authors(&self) -> Result<Option<&Vec<String>>, UnresolvedError> {
    256        self.authors.as_ref().map(|v| v.normalized()).transpose()
    257    }
    258
    259    pub fn normalized_build(&self) -> Result<Option<&[String]>, UnresolvedError> {
    260        let build = self.build.as_ref().ok_or(UnresolvedError)?;
    261        match build {
    262            TomlPackageBuild::Auto(false) => Ok(None),
    263            TomlPackageBuild::Auto(true) => Err(UnresolvedError),
    264            TomlPackageBuild::SingleScript(value) => Ok(Some(std::slice::from_ref(value))),
    265            TomlPackageBuild::MultipleScript(scripts) => Ok(Some(scripts)),
    266        }
    267    }
    268
    269    pub fn normalized_exclude(&self) -> Result<Option<&Vec<String>>, UnresolvedError> {
    270        self.exclude.as_ref().map(|v| v.normalized()).transpose()
    271    }
    272
    273    pub fn normalized_include(&self) -> Result<Option<&Vec<String>>, UnresolvedError> {
    274        self.include.as_ref().map(|v| v.normalized()).transpose()
    275    }
    276
    277    pub fn normalized_publish(&self) -> Result<Option<&VecStringOrBool>, UnresolvedError> {
    278        self.publish.as_ref().map(|v| v.normalized()).transpose()
    279    }
    280
    281    pub fn normalized_description(&self) -> Result<Option<&String>, UnresolvedError> {
    282        self.description
    283            .as_ref()
    284            .map(|v| v.normalized())
    285            .transpose()
    286    }
    287
    288    pub fn normalized_homepage(&self) -> Result<Option<&String>, UnresolvedError> {
    289        self.homepage.as_ref().map(|v| v.normalized()).transpose()
    290    }
    291
    292    pub fn normalized_documentation(&self) -> Result<Option<&String>, UnresolvedError> {
    293        self.documentation
    294            .as_ref()
    295            .map(|v| v.normalized())
    296            .transpose()
    297    }
    298
    299    pub fn normalized_readme(&self) -> Result<Option<&String>, UnresolvedError> {
    300        let readme = self.readme.as_ref().ok_or(UnresolvedError)?;
    301        readme.normalized().and_then(|sb| match sb {
    302            StringOrBool::Bool(false) => Ok(None),
    303            StringOrBool::Bool(true) => Err(UnresolvedError),
    304            StringOrBool::String(value) => Ok(Some(value)),
    305        })
    306    }
    307
    308    pub fn normalized_keywords(&self) -> Result<Option<&Vec<String>>, UnresolvedError> {
    309        self.keywords.as_ref().map(|v| v.normalized()).transpose()
    310    }
    311
    312    pub fn normalized_categories(&self) -> Result<Option<&Vec<String>>, UnresolvedError> {
    313        self.categories.as_ref().map(|v| v.normalized()).transpose()
    314    }
    315
    316    pub fn normalized_license(&self) -> Result<Option<&String>, UnresolvedError> {
    317        self.license.as_ref().map(|v| v.normalized()).transpose()
    318    }
    319
    320    pub fn normalized_license_file(&self) -> Result<Option<&String>, UnresolvedError> {
    321        self.license_file
    322            .as_ref()
    323            .map(|v| v.normalized())
    324            .transpose()
    325    }
    326
    327    pub fn normalized_repository(&self) -> Result<Option<&String>, UnresolvedError> {
    328        self.repository.as_ref().map(|v| v.normalized()).transpose()
    329    }
    330}
    331
    332/// An enum that allows for inheriting keys from a workspace in a Cargo.toml.
    333#[derive(Serialize, Copy, Clone, Debug)]
    334#[serde(untagged)]
    335#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    336pub enum InheritableField<T> {
    337    /// The type that is used when not inheriting from a workspace.
    338    Value(T),
    339    /// The type when inheriting from a workspace.
    340    Inherit(TomlInheritedField),
    341}
    342
    343impl<T> InheritableField<T> {
    344    pub fn normalized(&self) -> Result<&T, UnresolvedError> {
    345        self.as_value().ok_or(UnresolvedError)
    346    }
    347
    348    pub fn as_value(&self) -> Option<&T> {
    349        match self {
    350            InheritableField::Inherit(_) => None,
    351            InheritableField::Value(defined) => Some(defined),
    352        }
    353    }
    354
    355    pub fn into_value(self) -> Option<T> {
    356        match self {
    357            Self::Inherit(_) => None,
    358            Self::Value(defined) => Some(defined),
    359        }
    360    }
    361
    362    pub fn is_inherited(&self) -> bool {
    363        matches!(self, Self::Inherit(_))
    364    }
    365}
    366
    367//. This already has a `Deserialize` impl from version_trim_whitespace
    368pub type InheritableSemverVersion = InheritableField<semver::Version>;
    369impl<'de> de::Deserialize<'de> for InheritableSemverVersion {
    370    fn deserialize<D>(d: D) -> Result<Self, D::Error>
    371    where
    372        D: de::Deserializer<'de>,
    373    {
    374        UntaggedEnumVisitor::new()
    375            .expecting("SemVer version")
    376            .string(
    377                |value| match value.trim().parse().map_err(de::Error::custom) {
    378                    Ok(parsed) => Ok(InheritableField::Value(parsed)),
    379                    Err(e) => Err(e),
    380                },
    381            )
    382            .map(|value| value.deserialize().map(InheritableField::Inherit))
    383            .deserialize(d)
    384    }
    385}
    386
    387pub type InheritableString = InheritableField<String>;
    388impl<'de> de::Deserialize<'de> for InheritableString {
    389    fn deserialize<D>(d: D) -> Result<Self, D::Error>
    390    where
    391        D: de::Deserializer<'de>,
    392    {
    393        struct Visitor;
    394
    395        impl<'de> de::Visitor<'de> for Visitor {
    396            type Value = InheritableString;
    397
    398            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
    399                f.write_str("a string or workspace")
    400            }
    401
    402            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
    403            where
    404                E: de::Error,
    405            {
    406                Ok(InheritableString::Value(value))
    407            }
    408
    409            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    410            where
    411                E: de::Error,
    412            {
    413                self.visit_string(value.to_owned())
    414            }
    415
    416            fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
    417            where
    418                V: de::MapAccess<'de>,
    419            {
    420                let mvd = de::value::MapAccessDeserializer::new(map);
    421                TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
    422            }
    423        }
    424
    425        d.deserialize_any(Visitor)
    426    }
    427}
    428
    429pub type InheritableRustVersion = InheritableField<RustVersion>;
    430impl<'de> de::Deserialize<'de> for InheritableRustVersion {
    431    fn deserialize<D>(d: D) -> Result<Self, D::Error>
    432    where
    433        D: de::Deserializer<'de>,
    434    {
    435        struct Visitor;
    436
    437        impl<'de> de::Visitor<'de> for Visitor {
    438            type Value = InheritableRustVersion;
    439
    440            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
    441                f.write_str("a semver or workspace")
    442            }
    443
    444            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
    445            where
    446                E: de::Error,
    447            {
    448                let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
    449                Ok(InheritableRustVersion::Value(value))
    450            }
    451
    452            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    453            where
    454                E: de::Error,
    455            {
    456                self.visit_string(value.to_owned())
    457            }
    458
    459            fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
    460            where
    461                V: de::MapAccess<'de>,
    462            {
    463                let mvd = de::value::MapAccessDeserializer::new(map);
    464                TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
    465            }
    466        }
    467
    468        d.deserialize_any(Visitor)
    469    }
    470}
    471
    472pub type InheritableVecString = InheritableField<Vec<String>>;
    473impl<'de> de::Deserialize<'de> for InheritableVecString {
    474    fn deserialize<D>(d: D) -> Result<Self, D::Error>
    475    where
    476        D: de::Deserializer<'de>,
    477    {
    478        struct Visitor;
    479
    480        impl<'de> de::Visitor<'de> for Visitor {
    481            type Value = InheritableVecString;
    482
    483            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
    484                f.write_str("a vector of strings or workspace")
    485            }
    486            fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
    487            where
    488                A: de::SeqAccess<'de>,
    489            {
    490                let seq = de::value::SeqAccessDeserializer::new(v);
    491                Vec::deserialize(seq).map(InheritableField::Value)
    492            }
    493
    494            fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
    495            where
    496                V: de::MapAccess<'de>,
    497            {
    498                let mvd = de::value::MapAccessDeserializer::new(map);
    499                TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
    500            }
    501        }
    502
    503        d.deserialize_any(Visitor)
    504    }
    505}
    506
    507pub type InheritableStringOrBool = InheritableField<StringOrBool>;
    508impl<'de> de::Deserialize<'de> for InheritableStringOrBool {
    509    fn deserialize<D>(d: D) -> Result<Self, D::Error>
    510    where
    511        D: de::Deserializer<'de>,
    512    {
    513        struct Visitor;
    514
    515        impl<'de> de::Visitor<'de> for Visitor {
    516            type Value = InheritableStringOrBool;
    517
    518            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
    519                f.write_str("a string, a bool, or workspace")
    520            }
    521
    522            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
    523            where
    524                E: de::Error,
    525            {
    526                let b = de::value::BoolDeserializer::new(v);
    527                StringOrBool::deserialize(b).map(InheritableField::Value)
    528            }
    529
    530            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
    531            where
    532                E: de::Error,
    533            {
    534                let string = de::value::StringDeserializer::new(v);
    535                StringOrBool::deserialize(string).map(InheritableField::Value)
    536            }
    537
    538            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    539            where
    540                E: de::Error,
    541            {
    542                self.visit_string(value.to_owned())
    543            }
    544
    545            fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
    546            where
    547                V: de::MapAccess<'de>,
    548            {
    549                let mvd = de::value::MapAccessDeserializer::new(map);
    550                TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
    551            }
    552        }
    553
    554        d.deserialize_any(Visitor)
    555    }
    556}
    557
    558pub type InheritableVecStringOrBool = InheritableField<VecStringOrBool>;
    559impl<'de> de::Deserialize<'de> for InheritableVecStringOrBool {
    560    fn deserialize<D>(d: D) -> Result<Self, D::Error>
    561    where
    562        D: de::Deserializer<'de>,
    563    {
    564        struct Visitor;
    565
    566        impl<'de> de::Visitor<'de> for Visitor {
    567            type Value = InheritableVecStringOrBool;
    568
    569            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
    570                f.write_str("a boolean, a vector of strings, or workspace")
    571            }
    572
    573            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
    574            where
    575                E: de::Error,
    576            {
    577                let b = de::value::BoolDeserializer::new(v);
    578                VecStringOrBool::deserialize(b).map(InheritableField::Value)
    579            }
    580
    581            fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
    582            where
    583                A: de::SeqAccess<'de>,
    584            {
    585                let seq = de::value::SeqAccessDeserializer::new(v);
    586                VecStringOrBool::deserialize(seq).map(InheritableField::Value)
    587            }
    588
    589            fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
    590            where
    591                V: de::MapAccess<'de>,
    592            {
    593                let mvd = de::value::MapAccessDeserializer::new(map);
    594                TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
    595            }
    596        }
    597
    598        d.deserialize_any(Visitor)
    599    }
    600}
    601
    602pub type InheritableBtreeMap = InheritableField<BTreeMap<String, BTreeMap<String, String>>>;
    603
    604impl<'de> de::Deserialize<'de> for InheritableBtreeMap {
    605    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    606    where
    607        D: de::Deserializer<'de>,
    608    {
    609        let value = serde_value::Value::deserialize(deserializer)?;
    610
    611        if let Ok(w) = TomlInheritedField::deserialize(
    612            serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
    613        ) {
    614            return Ok(InheritableField::Inherit(w));
    615        }
    616        BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
    617            .map(InheritableField::Value)
    618    }
    619}
    620
    621#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
    622#[serde(rename_all = "kebab-case")]
    623#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    624pub struct TomlInheritedField {
    625    workspace: WorkspaceValue,
    626}
    627
    628impl TomlInheritedField {
    629    pub fn new() -> Self {
    630        TomlInheritedField {
    631            workspace: WorkspaceValue,
    632        }
    633    }
    634}
    635
    636impl Default for TomlInheritedField {
    637    fn default() -> Self {
    638        Self::new()
    639    }
    640}
    641
    642#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
    643#[serde(try_from = "bool")]
    644#[serde(into = "bool")]
    645#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    646struct WorkspaceValue;
    647
    648impl TryFrom<bool> for WorkspaceValue {
    649    type Error = String;
    650    fn try_from(other: bool) -> Result<WorkspaceValue, Self::Error> {
    651        if other {
    652            Ok(WorkspaceValue)
    653        } else {
    654            Err("`workspace` cannot be false".to_owned())
    655        }
    656    }
    657}
    658
    659impl From<WorkspaceValue> for bool {
    660    fn from(_: WorkspaceValue) -> bool {
    661        true
    662    }
    663}
    664
    665#[derive(Serialize, Clone, Debug)]
    666#[serde(untagged)]
    667#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    668pub enum InheritableDependency {
    669    /// The type that is used when not inheriting from a workspace.
    670    Value(TomlDependency),
    671    /// The type when inheriting from a workspace.
    672    Inherit(TomlInheritedDependency),
    673}
    674
    675impl InheritableDependency {
    676    pub fn unused_keys(&self) -> Vec<String> {
    677        match self {
    678            InheritableDependency::Value(d) => d.unused_keys(),
    679            InheritableDependency::Inherit(w) => w._unused_keys.keys().cloned().collect(),
    680        }
    681    }
    682
    683    pub fn normalized(&self) -> Result<&TomlDependency, UnresolvedError> {
    684        match self {
    685            InheritableDependency::Value(d) => Ok(d),
    686            InheritableDependency::Inherit(_) => Err(UnresolvedError),
    687        }
    688    }
    689
    690    pub fn is_inherited(&self) -> bool {
    691        matches!(self, InheritableDependency::Inherit(_))
    692    }
    693}
    694
    695impl<'de> de::Deserialize<'de> for InheritableDependency {
    696    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    697    where
    698        D: de::Deserializer<'de>,
    699    {
    700        let value = serde_value::Value::deserialize(deserializer)?;
    701
    702        if let Ok(w) = TomlInheritedDependency::deserialize(serde_value::ValueDeserializer::<
    703            D::Error,
    704        >::new(value.clone()))
    705        {
    706            return if w.workspace {
    707                Ok(InheritableDependency::Inherit(w))
    708            } else {
    709                Err(de::Error::custom("`workspace` cannot be false"))
    710            };
    711        }
    712        TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
    713            .map(InheritableDependency::Value)
    714    }
    715}
    716
    717#[derive(Deserialize, Serialize, Clone, Debug)]
    718#[serde(rename_all = "kebab-case")]
    719#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    720pub struct TomlInheritedDependency {
    721    pub workspace: bool,
    722    pub features: Option<Vec<String>>,
    723    pub default_features: Option<bool>,
    724    #[serde(rename = "default_features")]
    725    pub default_features2: Option<bool>,
    726    pub optional: Option<bool>,
    727    pub public: Option<bool>,
    728
    729    /// This is here to provide a way to see the "unused manifest keys" when deserializing
    730    #[serde(skip_serializing)]
    731    #[serde(flatten)]
    732    #[cfg_attr(feature = "unstable-schema", schemars(skip))]
    733    pub _unused_keys: BTreeMap<String, toml::Value>,
    734}
    735
    736impl TomlInheritedDependency {
    737    pub fn default_features(&self) -> Option<bool> {
    738        self.default_features.or(self.default_features2)
    739    }
    740}
    741
    742#[derive(Clone, Debug, Serialize)]
    743#[serde(untagged)]
    744#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    745pub enum TomlDependency<P: Clone = String> {
    746    /// In the simple format, only a version is specified, eg.
    747    /// `package = "<version>"`
    748    Simple(String),
    749    /// The simple format is equivalent to a detailed dependency
    750    /// specifying only a version, eg.
    751    /// `package = { version = "<version>" }`
    752    Detailed(TomlDetailedDependency<P>),
    753}
    754
    755impl TomlDependency {
    756    pub fn is_version_specified(&self) -> bool {
    757        match self {
    758            TomlDependency::Detailed(d) => d.version.is_some(),
    759            TomlDependency::Simple(..) => true,
    760        }
    761    }
    762
    763    pub fn is_optional(&self) -> bool {
    764        match self {
    765            TomlDependency::Detailed(d) => d.optional.unwrap_or(false),
    766            TomlDependency::Simple(..) => false,
    767        }
    768    }
    769
    770    pub fn is_public(&self) -> bool {
    771        match self {
    772            TomlDependency::Detailed(d) => d.public.unwrap_or(false),
    773            TomlDependency::Simple(..) => false,
    774        }
    775    }
    776
    777    pub fn default_features(&self) -> Option<bool> {
    778        match self {
    779            TomlDependency::Detailed(d) => d.default_features(),
    780            TomlDependency::Simple(..) => None,
    781        }
    782    }
    783
    784    pub fn unused_keys(&self) -> Vec<String> {
    785        match self {
    786            TomlDependency::Simple(_) => vec![],
    787            TomlDependency::Detailed(detailed) => detailed._unused_keys.keys().cloned().collect(),
    788        }
    789    }
    790}
    791
    792impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P> {
    793    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    794    where
    795        D: de::Deserializer<'de>,
    796    {
    797        use serde::de::Error as _;
    798        let expected = "a version string like \"0.9.8\" or a \
    799                     detailed dependency like { version = \"0.9.8\" }";
    800        UntaggedEnumVisitor::new()
    801            .expecting(expected)
    802            .string(|value| Ok(TomlDependency::Simple(value.to_owned())))
    803            .bool(|value| {
    804                let expected = format!("invalid type: boolean `{value}`, expected {expected}");
    805                let err = if value {
    806                    format!(
    807                        "{expected}\n\
    808                    note: if you meant to use a workspace member, you can write\n \
    809                      dep.workspace = {value}"
    810                    )
    811                } else {
    812                    expected
    813                };
    814
    815                Err(serde_untagged::de::Error::custom(err))
    816            })
    817            .map(|value| value.deserialize().map(TomlDependency::Detailed))
    818            .deserialize(deserializer)
    819    }
    820}
    821
    822#[derive(Deserialize, Serialize, Clone, Debug)]
    823#[serde(rename_all = "kebab-case")]
    824#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    825pub struct TomlDetailedDependency<P: Clone = String> {
    826    pub version: Option<String>,
    827
    828    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    829    pub registry: Option<RegistryName>,
    830    /// The URL of the `registry` field.
    831    /// This is an internal implementation detail. When Cargo creates a
    832    /// package, it replaces `registry` with `registry-index` so that the
    833    /// manifest contains the correct URL. All users won't have the same
    834    /// registry names configured, so Cargo can't rely on just the name for
    835    /// crates published by other users.
    836    pub registry_index: Option<String>,
    837    // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
    838    // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
    839    pub path: Option<P>,
    840    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    841    pub base: Option<PathBaseName>,
    842    pub git: Option<String>,
    843    pub branch: Option<String>,
    844    pub tag: Option<String>,
    845    pub rev: Option<String>,
    846    pub features: Option<Vec<String>>,
    847    pub optional: Option<bool>,
    848    pub default_features: Option<bool>,
    849    #[serde(rename = "default_features")]
    850    pub default_features2: Option<bool>,
    851    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    852    pub package: Option<PackageName>,
    853    pub public: Option<bool>,
    854
    855    /// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
    856    pub artifact: Option<StringOrVec>,
    857    /// If set, the artifact should also be a dependency
    858    pub lib: Option<bool>,
    859    /// A platform name, like `x86_64-apple-darwin`
    860    pub target: Option<String>,
    861
    862    /// This is here to provide a way to see the "unused manifest keys" when deserializing
    863    #[serde(skip_serializing)]
    864    #[serde(flatten)]
    865    #[cfg_attr(feature = "unstable-schema", schemars(skip))]
    866    pub _unused_keys: BTreeMap<String, toml::Value>,
    867}
    868
    869impl<P: Clone> TomlDetailedDependency<P> {
    870    pub fn default_features(&self) -> Option<bool> {
    871        self.default_features.or(self.default_features2)
    872    }
    873}
    874
    875// Explicit implementation so we avoid pulling in P: Default
    876impl<P: Clone> Default for TomlDetailedDependency<P> {
    877    fn default() -> Self {
    878        Self {
    879            version: Default::default(),
    880            registry: Default::default(),
    881            registry_index: Default::default(),
    882            path: Default::default(),
    883            base: Default::default(),
    884            git: Default::default(),
    885            branch: Default::default(),
    886            tag: Default::default(),
    887            rev: Default::default(),
    888            features: Default::default(),
    889            optional: Default::default(),
    890            default_features: Default::default(),
    891            default_features2: Default::default(),
    892            package: Default::default(),
    893            public: Default::default(),
    894            artifact: Default::default(),
    895            lib: Default::default(),
    896            target: Default::default(),
    897            _unused_keys: Default::default(),
    898        }
    899    }
    900}
    901
    902#[derive(Deserialize, Serialize, Clone, Debug, Default)]
    903#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    904pub struct TomlProfiles(pub BTreeMap<ProfileName, TomlProfile>);
    905
    906impl TomlProfiles {
    907    pub fn get_all(&self) -> &BTreeMap<ProfileName, TomlProfile> {
    908        &self.0
    909    }
    910
    911    pub fn get(&self, name: &str) -> Option<&TomlProfile> {
    912        self.0.get(name)
    913    }
    914}
    915
    916#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
    917#[serde(default, rename_all = "kebab-case")]
    918#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    919pub struct TomlProfile {
    920    pub opt_level: Option<TomlOptLevel>,
    921    pub lto: Option<StringOrBool>,
    922    pub codegen_backend: Option<String>,
    923    pub codegen_units: Option<u32>,
    924    pub debug: Option<TomlDebugInfo>,
    925    pub split_debuginfo: Option<String>,
    926    pub debug_assertions: Option<bool>,
    927    pub rpath: Option<bool>,
    928    pub panic: Option<String>,
    929    pub overflow_checks: Option<bool>,
    930    pub incremental: Option<bool>,
    931    pub dir_name: Option<String>,
    932    pub inherits: Option<String>,
    933    pub strip: Option<StringOrBool>,
    934    // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
    935    pub rustflags: Option<Vec<String>>,
    936    // These two fields must be last because they are sub-tables, and TOML
    937    // requires all non-tables to be listed first.
    938    pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
    939    pub build_override: Option<Box<TomlProfile>>,
    940    /// Unstable feature `-Ztrim-paths`.
    941    pub trim_paths: Option<TomlTrimPaths>,
    942    /// Unstable feature `hint-mostly-unused`
    943    pub hint_mostly_unused: Option<bool>,
    944}
    945
    946impl TomlProfile {
    947    /// Overwrite self's values with the given profile.
    948    pub fn merge(&mut self, profile: &Self) {
    949        if let Some(v) = &profile.opt_level {
    950            self.opt_level = Some(v.clone());
    951        }
    952
    953        if let Some(v) = &profile.lto {
    954            self.lto = Some(v.clone());
    955        }
    956
    957        if let Some(v) = &profile.codegen_backend {
    958            self.codegen_backend = Some(v.clone());
    959        }
    960
    961        if let Some(v) = profile.codegen_units {
    962            self.codegen_units = Some(v);
    963        }
    964
    965        if let Some(v) = profile.debug {
    966            self.debug = Some(v);
    967        }
    968
    969        if let Some(v) = profile.debug_assertions {
    970            self.debug_assertions = Some(v);
    971        }
    972
    973        if let Some(v) = &profile.split_debuginfo {
    974            self.split_debuginfo = Some(v.clone());
    975        }
    976
    977        if let Some(v) = profile.rpath {
    978            self.rpath = Some(v);
    979        }
    980
    981        if let Some(v) = &profile.panic {
    982            self.panic = Some(v.clone());
    983        }
    984
    985        if let Some(v) = profile.overflow_checks {
    986            self.overflow_checks = Some(v);
    987        }
    988
    989        if let Some(v) = profile.incremental {
    990            self.incremental = Some(v);
    991        }
    992
    993        if let Some(v) = &profile.rustflags {
    994            self.rustflags = Some(v.clone());
    995        }
    996
    997        if let Some(other_package) = &profile.package {
    998            match &mut self.package {
    999                Some(self_package) => {
    1000                    for (spec, other_pkg_profile) in other_package {
    1001                        match self_package.get_mut(spec) {
    1002                            Some(p) => p.merge(other_pkg_profile),
    1003                            None => {
    1004                                self_package.insert(spec.clone(), other_pkg_profile.clone());
    1005                            }
    1006                        }
    1007                    }
    1008                }
    1009                None => self.package = Some(other_package.clone()),
    1010            }
    1011        }
    1012
    1013        if let Some(other_bo) = &profile.build_override {
    1014            match &mut self.build_override {
    1015                Some(self_bo) => self_bo.merge(other_bo),
    1016                None => self.build_override = Some(other_bo.clone()),
    1017            }
    1018        }
    1019
    1020        if let Some(v) = &profile.inherits {
    1021            self.inherits = Some(v.clone());
    1022        }
    1023
    1024        if let Some(v) = &profile.dir_name {
    1025            self.dir_name = Some(v.clone());
    1026        }
    1027
    1028        if let Some(v) = &profile.strip {
    1029            self.strip = Some(v.clone());
    1030        }
    1031
    1032        if let Some(v) = &profile.trim_paths {
    1033            self.trim_paths = Some(v.clone())
    1034        }
    1035
    1036        if let Some(v) = profile.hint_mostly_unused {
    1037            self.hint_mostly_unused = Some(v);
    1038        }
    1039    }
    1040}
    1041
    1042#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
    1043#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1044pub enum ProfilePackageSpec {
    1045    Spec(PackageIdSpec),
    1046    All,
    1047}
    1048
    1049impl fmt::Display for ProfilePackageSpec {
    1050    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    1051        match self {
    1052            ProfilePackageSpec::Spec(spec) => spec.fmt(f),
    1053            ProfilePackageSpec::All => f.write_str("*"),
    1054        }
    1055    }
    1056}
    1057
    1058impl ser::Serialize for ProfilePackageSpec {
    1059    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    1060    where
    1061        S: ser::Serializer,
    1062    {
    1063        self.to_string().serialize(s)
    1064    }
    1065}
    1066
    1067impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
    1068    fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
    1069    where
    1070        D: de::Deserializer<'de>,
    1071    {
    1072        let string = String::deserialize(d)?;
    1073        if string == "*" {
    1074            Ok(ProfilePackageSpec::All)
    1075        } else {
    1076            PackageIdSpec::parse(&string)
    1077                .map_err(de::Error::custom)
    1078                .map(ProfilePackageSpec::Spec)
    1079        }
    1080    }
    1081}
    1082
    1083#[derive(Clone, Debug, Eq, PartialEq)]
    1084#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1085pub struct TomlOptLevel(pub String);
    1086
    1087impl ser::Serialize for TomlOptLevel {
    1088    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    1089    where
    1090        S: ser::Serializer,
    1091    {
    1092        match self.0.parse::<u32>() {
    1093            Ok(n) => n.serialize(serializer),
    1094            Err(_) => self.0.serialize(serializer),
    1095        }
    1096    }
    1097}
    1098
    1099impl<'de> de::Deserialize<'de> for TomlOptLevel {
    1100    fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
    1101    where
    1102        D: de::Deserializer<'de>,
    1103    {
    1104        use serde::de::Error as _;
    1105        UntaggedEnumVisitor::new()
    1106            .expecting("an optimization level")
    1107            .i64(|value| Ok(TomlOptLevel(value.to_string())))
    1108            .string(|value| {
    1109                if value == "s" || value == "z" {
    1110                    Ok(TomlOptLevel(value.to_string()))
    1111                } else {
    1112                    Err(serde_untagged::de::Error::custom(format!(
    1113                        "must be `0`, `1`, `2`, `3`, `s` or `z`, \
    1114                         but found the string: \"{}\"",
    1115                        value
    1116                    )))
    1117                }
    1118            })
    1119            .deserialize(d)
    1120    }
    1121}
    1122
    1123#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
    1124#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1125pub enum TomlDebugInfo {
    1126    None,
    1127    LineDirectivesOnly,
    1128    LineTablesOnly,
    1129    Limited,
    1130    Full,
    1131}
    1132
    1133impl Display for TomlDebugInfo {
    1134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    1135        match self {
    1136            TomlDebugInfo::None => f.write_char('0'),
    1137            TomlDebugInfo::Limited => f.write_char('1'),
    1138            TomlDebugInfo::Full => f.write_char('2'),
    1139            TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
    1140            TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
    1141        }
    1142    }
    1143}
    1144
    1145impl ser::Serialize for TomlDebugInfo {
    1146    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    1147    where
    1148        S: ser::Serializer,
    1149    {
    1150        match self {
    1151            Self::None => 0.serialize(serializer),
    1152            Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
    1153            Self::LineTablesOnly => "line-tables-only".serialize(serializer),
    1154            Self::Limited => 1.serialize(serializer),
    1155            Self::Full => 2.serialize(serializer),
    1156        }
    1157    }
    1158}
    1159
    1160impl<'de> de::Deserialize<'de> for TomlDebugInfo {
    1161    fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
    1162    where
    1163        D: de::Deserializer<'de>,
    1164    {
    1165        use serde::de::Error as _;
    1166        let expecting = "a boolean, 0, 1, 2, \"none\", \"limited\", \"full\", \"line-tables-only\", or \"line-directives-only\"";
    1167        UntaggedEnumVisitor::new()
    1168            .expecting(expecting)
    1169            .bool(|value| {
    1170                Ok(if value {
    1171                    TomlDebugInfo::Full
    1172                } else {
    1173                    TomlDebugInfo::None
    1174                })
    1175            })
    1176            .i64(|value| {
    1177                let debuginfo = match value {
    1178                    0 => TomlDebugInfo::None,
    1179                    1 => TomlDebugInfo::Limited,
    1180                    2 => TomlDebugInfo::Full,
    1181                    _ => {
    1182                        return Err(serde_untagged::de::Error::invalid_value(
    1183                            Unexpected::Signed(value),
    1184                            &expecting,
    1185                        ));
    1186                    }
    1187                };
    1188                Ok(debuginfo)
    1189            })
    1190            .string(|value| {
    1191                let debuginfo = match value {
    1192                    "none" => TomlDebugInfo::None,
    1193                    "limited" => TomlDebugInfo::Limited,
    1194                    "full" => TomlDebugInfo::Full,
    1195                    "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
    1196                    "line-tables-only" => TomlDebugInfo::LineTablesOnly,
    1197                    _ => {
    1198                        return Err(serde_untagged::de::Error::invalid_value(
    1199                            Unexpected::Str(value),
    1200                            &expecting,
    1201                        ));
    1202                    }
    1203                };
    1204                Ok(debuginfo)
    1205            })
    1206            .deserialize(d)
    1207    }
    1208}
    1209
    1210#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
    1211#[serde(untagged, rename_all = "kebab-case")]
    1212#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1213pub enum TomlTrimPaths {
    1214    Values(Vec<TomlTrimPathsValue>),
    1215    All,
    1216}
    1217
    1218impl TomlTrimPaths {
    1219    pub fn none() -> Self {
    1220        TomlTrimPaths::Values(Vec::new())
    1221    }
    1222
    1223    pub fn is_none(&self) -> bool {
    1224        match self {
    1225            TomlTrimPaths::Values(v) => v.is_empty(),
    1226            TomlTrimPaths::All => false,
    1227        }
    1228    }
    1229}
    1230
    1231impl<'de> de::Deserialize<'de> for TomlTrimPaths {
    1232    fn deserialize<D>(d: D) -> Result<TomlTrimPaths, D::Error>
    1233    where
    1234        D: de::Deserializer<'de>,
    1235    {
    1236        use serde::de::Error as _;
    1237        let expecting = r#"a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options"#;
    1238        UntaggedEnumVisitor::new()
    1239            .expecting(expecting)
    1240            .bool(|value| {
    1241                Ok(if value {
    1242                    TomlTrimPaths::All
    1243                } else {
    1244                    TomlTrimPaths::none()
    1245                })
    1246            })
    1247            .string(|v| match v {
    1248                "none" => Ok(TomlTrimPaths::none()),
    1249                "all" => Ok(TomlTrimPaths::All),
    1250                v => {
    1251                    let d = v.into_deserializer();
    1252                    let err = |_: D::Error| {
    1253                        serde_untagged::de::Error::custom(format!("expected {expecting}"))
    1254                    };
    1255                    TomlTrimPathsValue::deserialize(d)
    1256                        .map_err(err)
    1257                        .map(|v| v.into())
    1258                }
    1259            })
    1260            .seq(|seq| {
    1261                let seq: Vec<String> = seq.deserialize()?;
    1262                let seq: Vec<_> = seq
    1263                    .into_iter()
    1264                    .map(|s| TomlTrimPathsValue::deserialize(s.into_deserializer()))
    1265                    .collect::<Result<_, _>>()?;
    1266                Ok(seq.into())
    1267            })
    1268            .deserialize(d)
    1269    }
    1270}
    1271
    1272impl fmt::Display for TomlTrimPaths {
    1273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    1274        match self {
    1275            TomlTrimPaths::All => write!(f, "all"),
    1276            TomlTrimPaths::Values(v) if v.is_empty() => write!(f, "none"),
    1277            TomlTrimPaths::Values(v) => {
    1278                let mut iter = v.iter();
    1279                if let Some(value) = iter.next() {
    1280                    write!(f, "{value}")?;
    1281                }
    1282                for value in iter {
    1283                    write!(f, ",{value}")?;
    1284                }
    1285                Ok(())
    1286            }
    1287        }
    1288    }
    1289}
    1290
    1291impl From<TomlTrimPathsValue> for TomlTrimPaths {
    1292    fn from(value: TomlTrimPathsValue) -> Self {
    1293        TomlTrimPaths::Values(vec![value])
    1294    }
    1295}
    1296
    1297impl From<Vec<TomlTrimPathsValue>> for TomlTrimPaths {
    1298    fn from(value: Vec<TomlTrimPathsValue>) -> Self {
    1299        TomlTrimPaths::Values(value)
    1300    }
    1301}
    1302
    1303#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
    1304#[serde(rename_all = "kebab-case")]
    1305#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1306pub enum TomlTrimPathsValue {
    1307    Diagnostics,
    1308    Macro,
    1309    Object,
    1310}
    1311
    1312impl TomlTrimPathsValue {
    1313    pub fn as_str(&self) -> &'static str {
    1314        match self {
    1315            TomlTrimPathsValue::Diagnostics => "diagnostics",
    1316            TomlTrimPathsValue::Macro => "macro",
    1317            TomlTrimPathsValue::Object => "object",
    1318        }
    1319    }
    1320}
    1321
    1322impl fmt::Display for TomlTrimPathsValue {
    1323    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    1324        write!(f, "{}", self.as_str())
    1325    }
    1326}
    1327
    1328pub type TomlLibTarget = TomlTarget;
    1329pub type TomlBinTarget = TomlTarget;
    1330pub type TomlExampleTarget = TomlTarget;
    1331pub type TomlTestTarget = TomlTarget;
    1332pub type TomlBenchTarget = TomlTarget;
    1333
    1334#[derive(Default, Serialize, Deserialize, Debug, Clone)]
    1335#[serde(rename_all = "kebab-case")]
    1336#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1337pub struct TomlTarget {
    1338    pub name: Option<String>,
    1339
    1340    // The intention was to only accept `crate-type` here but historical
    1341    // versions of Cargo also accepted `crate_type`, so look for both.
    1342    pub crate_type: Option<Vec<String>>,
    1343    #[serde(rename = "crate_type")]
    1344    pub crate_type2: Option<Vec<String>>,
    1345
    1346    #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
    1347    pub path: Option<PathValue>,
    1348    // Note that `filename` is used for the cargo-feature `different_binary_name`
    1349    pub filename: Option<String>,
    1350    pub test: Option<bool>,
    1351    pub doctest: Option<bool>,
    1352    pub bench: Option<bool>,
    1353    pub doc: Option<bool>,
    1354    pub doc_scrape_examples: Option<bool>,
    1355    pub proc_macro: Option<bool>,
    1356    #[serde(rename = "proc_macro")]
    1357    pub proc_macro2: Option<bool>,
    1358    pub harness: Option<bool>,
    1359    pub required_features: Option<Vec<String>>,
    1360    pub edition: Option<String>,
    1361}
    1362
    1363impl TomlTarget {
    1364    pub fn new() -> TomlTarget {
    1365        TomlTarget::default()
    1366    }
    1367
    1368    pub fn proc_macro(&self) -> Option<bool> {
    1369        self.proc_macro.or(self.proc_macro2).or_else(|| {
    1370            if let Some(types) = self.crate_types() {
    1371                if types.contains(&"proc-macro".to_string()) {
    1372                    return Some(true);
    1373                }
    1374            }
    1375            None
    1376        })
    1377    }
    1378
    1379    pub fn crate_types(&self) -> Option<&Vec<String>> {
    1380        self.crate_type
    1381            .as_ref()
    1382            .or_else(|| self.crate_type2.as_ref())
    1383    }
    1384}
    1385
    1386macro_rules! str_newtype {
    1387    ($name:ident) => {
    1388        /// Verified string newtype
    1389        #[derive(Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
    1390        #[serde(transparent)]
    1391        #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1392        pub struct $name<T: AsRef<str> = String>(T);
    1393
    1394        impl<T: AsRef<str>> $name<T> {
    1395            pub fn into_inner(self) -> T {
    1396                self.0
    1397            }
    1398        }
    1399
    1400        impl<T: AsRef<str>> AsRef<str> for $name<T> {
    1401            fn as_ref(&self) -> &str {
    1402                self.0.as_ref()
    1403            }
    1404        }
    1405
    1406        impl<T: AsRef<str>> std::ops::Deref for $name<T> {
    1407            type Target = T;
    1408
    1409            fn deref(&self) -> &Self::Target {
    1410                &self.0
    1411            }
    1412        }
    1413
    1414        impl<T: AsRef<str>> std::borrow::Borrow<str> for $name<T> {
    1415            fn borrow(&self) -> &str {
    1416                self.0.as_ref()
    1417            }
    1418        }
    1419
    1420        impl<'a> std::str::FromStr for $name<String> {
    1421            type Err = restricted_names::NameValidationError;
    1422
    1423            fn from_str(value: &str) -> Result<Self, Self::Err> {
    1424                Self::new(value.to_owned())
    1425            }
    1426        }
    1427
    1428        impl<'de, T: AsRef<str> + serde::Deserialize<'de>> serde::Deserialize<'de> for $name<T> {
    1429            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1430            where
    1431                D: serde::Deserializer<'de>,
    1432            {
    1433                let inner = T::deserialize(deserializer)?;
    1434                Self::new(inner).map_err(serde::de::Error::custom)
    1435            }
    1436        }
    1437
    1438        impl<T: AsRef<str>> Display for $name<T> {
    1439            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    1440                self.0.as_ref().fmt(f)
    1441            }
    1442        }
    1443    };
    1444}
    1445
    1446str_newtype!(PackageName);
    1447
    1448impl<T: AsRef<str>> PackageName<T> {
    1449    /// Validated package name
    1450    pub fn new(name: T) -> Result<Self, NameValidationError> {
    1451        restricted_names::validate_package_name(name.as_ref())?;
    1452        Ok(Self(name))
    1453    }
    1454}
    1455
    1456impl PackageName {
    1457    /// Coerce a value to be a validate package name
    1458    ///
    1459    /// Replaces invalid values with `placeholder`
    1460    pub fn sanitize(name: impl AsRef<str>, placeholder: char) -> Self {
    1461        PackageName(restricted_names::sanitize_package_name(
    1462            name.as_ref(),
    1463            placeholder,
    1464        ))
    1465    }
    1466}
    1467
    1468str_newtype!(RegistryName);
    1469
    1470impl<T: AsRef<str>> RegistryName<T> {
    1471    /// Validated registry name
    1472    pub fn new(name: T) -> Result<Self, NameValidationError> {
    1473        restricted_names::validate_registry_name(name.as_ref())?;
    1474        Ok(Self(name))
    1475    }
    1476}
    1477
    1478str_newtype!(ProfileName);
    1479
    1480impl<T: AsRef<str>> ProfileName<T> {
    1481    /// Validated profile name
    1482    pub fn new(name: T) -> Result<Self, NameValidationError> {
    1483        restricted_names::validate_profile_name(name.as_ref())?;
    1484        Ok(Self(name))
    1485    }
    1486}
    1487
    1488str_newtype!(FeatureName);
    1489
    1490impl<T: AsRef<str>> FeatureName<T> {
    1491    /// Validated feature name
    1492    pub fn new(name: T) -> Result<Self, NameValidationError> {
    1493        restricted_names::validate_feature_name(name.as_ref())?;
    1494        Ok(Self(name))
    1495    }
    1496}
    1497
    1498str_newtype!(PathBaseName);
    1499
    1500impl<T: AsRef<str>> PathBaseName<T> {
    1501    /// Validated path base name
    1502    pub fn new(name: T) -> Result<Self, NameValidationError> {
    1503        restricted_names::validate_path_base_name(name.as_ref())?;
    1504        Ok(Self(name))
    1505    }
    1506}
    1507
    1508/// Corresponds to a `target` entry, but `TomlTarget` is already used.
    1509#[derive(Serialize, Deserialize, Debug, Clone)]
    1510#[serde(rename_all = "kebab-case")]
    1511#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1512pub struct TomlPlatform {
    1513    pub dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
    1514    pub build_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
    1515    #[serde(rename = "build_dependencies")]
    1516    pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
    1517    pub dev_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
    1518    #[serde(rename = "dev_dependencies")]
    1519    pub dev_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
    1520}
    1521
    1522impl TomlPlatform {
    1523    pub fn dev_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
    1524        self.dev_dependencies
    1525            .as_ref()
    1526            .or(self.dev_dependencies2.as_ref())
    1527    }
    1528
    1529    pub fn build_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
    1530        self.build_dependencies
    1531            .as_ref()
    1532            .or(self.build_dependencies2.as_ref())
    1533    }
    1534}
    1535
    1536#[derive(Serialize, Debug, Clone)]
    1537#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1538pub struct InheritableLints {
    1539    #[serde(skip_serializing_if = "std::ops::Not::not")]
    1540    #[cfg_attr(feature = "unstable-schema", schemars(default))]
    1541    pub workspace: bool,
    1542    #[serde(flatten)]
    1543    pub lints: TomlLints,
    1544}
    1545
    1546impl InheritableLints {
    1547    pub fn normalized(&self) -> Result<&TomlLints, UnresolvedError> {
    1548        if self.workspace {
    1549            Err(UnresolvedError)
    1550        } else {
    1551            Ok(&self.lints)
    1552        }
    1553    }
    1554}
    1555
    1556impl<'de> Deserialize<'de> for InheritableLints {
    1557    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1558    where
    1559        D: de::Deserializer<'de>,
    1560    {
    1561        struct InheritableLintsVisitor;
    1562
    1563        impl<'de> de::Visitor<'de> for InheritableLintsVisitor {
    1564            // The type that our Visitor is going to produce.
    1565            type Value = InheritableLints;
    1566
    1567            // Format a message stating what data this Visitor expects to receive.
    1568            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    1569                formatter.write_str("a lints table")
    1570            }
    1571
    1572            // Deserialize MyMap from an abstract "map" provided by the
    1573            // Deserializer. The MapAccess input is a callback provided by
    1574            // the Deserializer to let us see each entry in the map.
    1575            fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    1576            where
    1577                M: de::MapAccess<'de>,
    1578            {
    1579                let mut lints = TomlLints::new();
    1580                let mut workspace = false;
    1581
    1582                // While there are entries remaining in the input, add them
    1583                // into our map.
    1584                while let Some(key) = access.next_key()? {
    1585                    if key == "workspace" {
    1586                        workspace = match access.next_value()? {
    1587                            Some(WorkspaceValue) => true,
    1588                            None => false,
    1589                        };
    1590                    } else {
    1591                        let value = access.next_value()?;
    1592                        lints.insert(key, value);
    1593                    }
    1594                }
    1595
    1596                Ok(InheritableLints { workspace, lints })
    1597            }
    1598        }
    1599
    1600        deserializer.deserialize_map(InheritableLintsVisitor)
    1601    }
    1602}
    1603
    1604pub type TomlLints = BTreeMap<String, TomlToolLints>;
    1605
    1606pub type TomlToolLints = BTreeMap<String, TomlLint>;
    1607
    1608#[derive(Serialize, Debug, Clone)]
    1609#[serde(untagged)]
    1610#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1611pub enum TomlLint {
    1612    Level(TomlLintLevel),
    1613    Config(TomlLintConfig),
    1614}
    1615
    1616impl<'de> Deserialize<'de> for TomlLint {
    1617    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1618    where
    1619        D: de::Deserializer<'de>,
    1620    {
    1621        UntaggedEnumVisitor::new()
    1622            .string(|string| {
    1623                TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
    1624            })
    1625            .map(|map| map.deserialize().map(TomlLint::Config))
    1626            .deserialize(deserializer)
    1627    }
    1628}
    1629
    1630impl TomlLint {
    1631    pub fn level(&self) -> TomlLintLevel {
    1632        match self {
    1633            Self::Level(level) => *level,
    1634            Self::Config(config) => config.level,
    1635        }
    1636    }
    1637
    1638    pub fn priority(&self) -> i8 {
    1639        match self {
    1640            Self::Level(_) => 0,
    1641            Self::Config(config) => config.priority,
    1642        }
    1643    }
    1644
    1645    pub fn config(&self) -> Option<&toml::Table> {
    1646        match self {
    1647            Self::Level(_) => None,
    1648            Self::Config(config) => Some(&config.config),
    1649        }
    1650    }
    1651}
    1652
    1653#[derive(Serialize, Deserialize, Debug, Clone)]
    1654#[serde(rename_all = "kebab-case")]
    1655#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1656pub struct TomlLintConfig {
    1657    pub level: TomlLintLevel,
    1658    #[serde(default)]
    1659    pub priority: i8,
    1660    #[serde(flatten)]
    1661    #[cfg_attr(
    1662        feature = "unstable-schema",
    1663        schemars(with = "HashMap<String, TomlValueWrapper>")
    1664    )]
    1665    pub config: toml::Table,
    1666}
    1667
    1668#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
    1669#[serde(rename_all = "kebab-case")]
    1670#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1671pub enum TomlLintLevel {
    1672    Forbid,
    1673    Deny,
    1674    Warn,
    1675    Allow,
    1676}
    1677
    1678#[derive(Serialize, Deserialize, Debug, Default, Clone)]
    1679#[serde(rename_all = "kebab-case")]
    1680#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1681pub struct Hints {
    1682    #[cfg_attr(
    1683        feature = "unstable-schema",
    1684        schemars(with = "Option<TomlValueWrapper>")
    1685    )]
    1686    pub mostly_unused: Option<toml::Value>,
    1687}
    1688
    1689#[derive(Copy, Clone, Debug)]
    1690pub struct InvalidCargoFeatures {}
    1691
    1692impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
    1693    fn deserialize<D>(_d: D) -> Result<Self, D::Error>
    1694    where
    1695        D: de::Deserializer<'de>,
    1696    {
    1697        use serde::de::Error as _;
    1698
    1699        Err(D::Error::custom(
    1700            "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
    1701        ))
    1702    }
    1703}
    1704
    1705/// This can be parsed from either a TOML string or array,
    1706/// but is always stored as a vector.
    1707#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
    1708#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1709pub struct StringOrVec(pub Vec<String>);
    1710
    1711impl StringOrVec {
    1712    pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
    1713        self.0.iter()
    1714    }
    1715}
    1716
    1717impl<'de> de::Deserialize<'de> for StringOrVec {
    1718    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1719    where
    1720        D: de::Deserializer<'de>,
    1721    {
    1722        UntaggedEnumVisitor::new()
    1723            .expecting("string or list of strings")
    1724            .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
    1725            .seq(|value| value.deserialize().map(StringOrVec))
    1726            .deserialize(deserializer)
    1727    }
    1728}
    1729
    1730#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
    1731#[serde(untagged)]
    1732#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1733pub enum StringOrBool {
    1734    String(String),
    1735    Bool(bool),
    1736}
    1737
    1738impl<'de> Deserialize<'de> for StringOrBool {
    1739    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1740    where
    1741        D: de::Deserializer<'de>,
    1742    {
    1743        UntaggedEnumVisitor::new()
    1744            .bool(|b| Ok(StringOrBool::Bool(b)))
    1745            .string(|s| Ok(StringOrBool::String(s.to_owned())))
    1746            .deserialize(deserializer)
    1747    }
    1748}
    1749
    1750#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
    1751#[serde(untagged)]
    1752#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1753pub enum TomlPackageBuild {
    1754    /// If build scripts are disabled or enabled.
    1755    /// If true, `build.rs` in the root folder will be the build script.
    1756    Auto(bool),
    1757
    1758    /// Path of Build Script if there's just one script.
    1759    SingleScript(String),
    1760
    1761    /// Vector of paths if multiple build script are to be used.
    1762    MultipleScript(Vec<String>),
    1763}
    1764
    1765impl<'de> Deserialize<'de> for TomlPackageBuild {
    1766    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1767    where
    1768        D: de::Deserializer<'de>,
    1769    {
    1770        UntaggedEnumVisitor::new()
    1771            .bool(|b| Ok(TomlPackageBuild::Auto(b)))
    1772            .string(|s| Ok(TomlPackageBuild::SingleScript(s.to_owned())))
    1773            .seq(|value| value.deserialize().map(TomlPackageBuild::MultipleScript))
    1774            .deserialize(deserializer)
    1775    }
    1776}
    1777
    1778#[derive(PartialEq, Clone, Debug, Serialize)]
    1779#[serde(untagged)]
    1780#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1781pub enum VecStringOrBool {
    1782    VecString(Vec<String>),
    1783    Bool(bool),
    1784}
    1785
    1786impl<'de> de::Deserialize<'de> for VecStringOrBool {
    1787    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1788    where
    1789        D: de::Deserializer<'de>,
    1790    {
    1791        UntaggedEnumVisitor::new()
    1792            .expecting("a boolean or vector of strings")
    1793            .bool(|value| Ok(VecStringOrBool::Bool(value)))
    1794            .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
    1795            .deserialize(deserializer)
    1796    }
    1797}
    1798
    1799#[derive(Clone)]
    1800pub struct PathValue(pub PathBuf);
    1801
    1802impl fmt::Debug for PathValue {
    1803    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    1804        self.0.fmt(f)
    1805    }
    1806}
    1807
    1808impl ser::Serialize for PathValue {
    1809    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    1810    where
    1811        S: ser::Serializer,
    1812    {
    1813        self.0.serialize(serializer)
    1814    }
    1815}
    1816
    1817impl<'de> de::Deserialize<'de> for PathValue {
    1818    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    1819    where
    1820        D: de::Deserializer<'de>,
    1821    {
    1822        Ok(PathValue(String::deserialize(deserializer)?.into()))
    1823    }
    1824}
    1825
    1826/// Error validating names in Cargo.
    1827#[derive(Debug, thiserror::Error)]
    1828#[error("manifest field was not resolved")]
    1829#[non_exhaustive]
    1830#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
    1831pub struct UnresolvedError;
    1832
    1833#[cfg(feature = "unstable-schema")]
    1834#[test]
    1835fn dump_manifest_schema() {
    1836    let schema = schemars::schema_for!(crate::manifest::TomlManifest);
    1837    let dump = serde_json::to_string_pretty(&schema).unwrap();
    1838    snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json").raw());
    1839}