cargo_util_schemas/manifest/
mod.rs

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