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