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