Skip to main content

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