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