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