cargo_util_schemas/manifest/
mod.rs

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