cargo_util_schemas/manifest/
mod.rs

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