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 is_inherited(&self) -> bool {
356 matches!(self, Self::Inherit(_))
357 }
358}
359
360pub type InheritableSemverVersion = InheritableField<semver::Version>;
362impl<'de> de::Deserialize<'de> for InheritableSemverVersion {
363 fn deserialize<D>(d: D) -> Result<Self, D::Error>
364 where
365 D: de::Deserializer<'de>,
366 {
367 UntaggedEnumVisitor::new()
368 .expecting("SemVer version")
369 .string(
370 |value| match value.trim().parse().map_err(de::Error::custom) {
371 Ok(parsed) => Ok(InheritableField::Value(parsed)),
372 Err(e) => Err(e),
373 },
374 )
375 .map(|value| value.deserialize().map(InheritableField::Inherit))
376 .deserialize(d)
377 }
378}
379
380pub type InheritableString = InheritableField<String>;
381impl<'de> de::Deserialize<'de> for InheritableString {
382 fn deserialize<D>(d: D) -> Result<Self, D::Error>
383 where
384 D: de::Deserializer<'de>,
385 {
386 struct Visitor;
387
388 impl<'de> de::Visitor<'de> for Visitor {
389 type Value = InheritableString;
390
391 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
392 f.write_str("a string or workspace")
393 }
394
395 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
396 where
397 E: de::Error,
398 {
399 Ok(InheritableString::Value(value))
400 }
401
402 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
403 where
404 E: de::Error,
405 {
406 self.visit_string(value.to_owned())
407 }
408
409 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
410 where
411 V: de::MapAccess<'de>,
412 {
413 let mvd = de::value::MapAccessDeserializer::new(map);
414 TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
415 }
416 }
417
418 d.deserialize_any(Visitor)
419 }
420}
421
422pub type InheritableRustVersion = InheritableField<RustVersion>;
423impl<'de> de::Deserialize<'de> for InheritableRustVersion {
424 fn deserialize<D>(d: D) -> Result<Self, D::Error>
425 where
426 D: de::Deserializer<'de>,
427 {
428 struct Visitor;
429
430 impl<'de> de::Visitor<'de> for Visitor {
431 type Value = InheritableRustVersion;
432
433 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
434 f.write_str("a semver or workspace")
435 }
436
437 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
438 where
439 E: de::Error,
440 {
441 let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
442 Ok(InheritableRustVersion::Value(value))
443 }
444
445 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
446 where
447 E: de::Error,
448 {
449 self.visit_string(value.to_owned())
450 }
451
452 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
453 where
454 V: de::MapAccess<'de>,
455 {
456 let mvd = de::value::MapAccessDeserializer::new(map);
457 TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
458 }
459 }
460
461 d.deserialize_any(Visitor)
462 }
463}
464
465pub type InheritableVecString = InheritableField<Vec<String>>;
466impl<'de> de::Deserialize<'de> for InheritableVecString {
467 fn deserialize<D>(d: D) -> Result<Self, D::Error>
468 where
469 D: de::Deserializer<'de>,
470 {
471 struct Visitor;
472
473 impl<'de> de::Visitor<'de> for Visitor {
474 type Value = InheritableVecString;
475
476 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
477 f.write_str("a vector of strings or workspace")
478 }
479 fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
480 where
481 A: de::SeqAccess<'de>,
482 {
483 let seq = de::value::SeqAccessDeserializer::new(v);
484 Vec::deserialize(seq).map(InheritableField::Value)
485 }
486
487 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
488 where
489 V: de::MapAccess<'de>,
490 {
491 let mvd = de::value::MapAccessDeserializer::new(map);
492 TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
493 }
494 }
495
496 d.deserialize_any(Visitor)
497 }
498}
499
500pub type InheritableStringOrBool = InheritableField<StringOrBool>;
501impl<'de> de::Deserialize<'de> for InheritableStringOrBool {
502 fn deserialize<D>(d: D) -> Result<Self, D::Error>
503 where
504 D: de::Deserializer<'de>,
505 {
506 struct Visitor;
507
508 impl<'de> de::Visitor<'de> for Visitor {
509 type Value = InheritableStringOrBool;
510
511 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
512 f.write_str("a string, a bool, or workspace")
513 }
514
515 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
516 where
517 E: de::Error,
518 {
519 let b = de::value::BoolDeserializer::new(v);
520 StringOrBool::deserialize(b).map(InheritableField::Value)
521 }
522
523 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
524 where
525 E: de::Error,
526 {
527 let string = de::value::StringDeserializer::new(v);
528 StringOrBool::deserialize(string).map(InheritableField::Value)
529 }
530
531 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
532 where
533 E: de::Error,
534 {
535 self.visit_string(value.to_owned())
536 }
537
538 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
539 where
540 V: de::MapAccess<'de>,
541 {
542 let mvd = de::value::MapAccessDeserializer::new(map);
543 TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
544 }
545 }
546
547 d.deserialize_any(Visitor)
548 }
549}
550
551pub type InheritableVecStringOrBool = InheritableField<VecStringOrBool>;
552impl<'de> de::Deserialize<'de> for InheritableVecStringOrBool {
553 fn deserialize<D>(d: D) -> Result<Self, D::Error>
554 where
555 D: de::Deserializer<'de>,
556 {
557 struct Visitor;
558
559 impl<'de> de::Visitor<'de> for Visitor {
560 type Value = InheritableVecStringOrBool;
561
562 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
563 f.write_str("a boolean, a vector of strings, or workspace")
564 }
565
566 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
567 where
568 E: de::Error,
569 {
570 let b = de::value::BoolDeserializer::new(v);
571 VecStringOrBool::deserialize(b).map(InheritableField::Value)
572 }
573
574 fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
575 where
576 A: de::SeqAccess<'de>,
577 {
578 let seq = de::value::SeqAccessDeserializer::new(v);
579 VecStringOrBool::deserialize(seq).map(InheritableField::Value)
580 }
581
582 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
583 where
584 V: de::MapAccess<'de>,
585 {
586 let mvd = de::value::MapAccessDeserializer::new(map);
587 TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
588 }
589 }
590
591 d.deserialize_any(Visitor)
592 }
593}
594
595pub type InheritableBtreeMap = InheritableField<BTreeMap<String, BTreeMap<String, String>>>;
596
597impl<'de> de::Deserialize<'de> for InheritableBtreeMap {
598 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
599 where
600 D: de::Deserializer<'de>,
601 {
602 let value = serde_value::Value::deserialize(deserializer)?;
603
604 if let Ok(w) = TomlInheritedField::deserialize(
605 serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
606 ) {
607 return Ok(InheritableField::Inherit(w));
608 }
609 BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
610 .map(InheritableField::Value)
611 }
612}
613
614#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
615#[serde(rename_all = "kebab-case")]
616#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
617pub struct TomlInheritedField {
618 workspace: WorkspaceValue,
619}
620
621impl TomlInheritedField {
622 pub fn new() -> Self {
623 TomlInheritedField {
624 workspace: WorkspaceValue,
625 }
626 }
627}
628
629impl Default for TomlInheritedField {
630 fn default() -> Self {
631 Self::new()
632 }
633}
634
635#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
636#[serde(try_from = "bool")]
637#[serde(into = "bool")]
638#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
639struct WorkspaceValue;
640
641impl TryFrom<bool> for WorkspaceValue {
642 type Error = String;
643 fn try_from(other: bool) -> Result<WorkspaceValue, Self::Error> {
644 if other {
645 Ok(WorkspaceValue)
646 } else {
647 Err("`workspace` cannot be false".to_owned())
648 }
649 }
650}
651
652impl From<WorkspaceValue> for bool {
653 fn from(_: WorkspaceValue) -> bool {
654 true
655 }
656}
657
658#[derive(Serialize, Clone, Debug)]
659#[serde(untagged)]
660#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
661pub enum InheritableDependency {
662 Value(TomlDependency),
664 Inherit(TomlInheritedDependency),
666}
667
668impl InheritableDependency {
669 pub fn unused_keys(&self) -> Vec<String> {
670 match self {
671 InheritableDependency::Value(d) => d.unused_keys(),
672 InheritableDependency::Inherit(w) => w._unused_keys.keys().cloned().collect(),
673 }
674 }
675
676 pub fn normalized(&self) -> Result<&TomlDependency, UnresolvedError> {
677 match self {
678 InheritableDependency::Value(d) => Ok(d),
679 InheritableDependency::Inherit(_) => Err(UnresolvedError),
680 }
681 }
682
683 pub fn is_inherited(&self) -> bool {
684 matches!(self, InheritableDependency::Inherit(_))
685 }
686}
687
688impl<'de> de::Deserialize<'de> for InheritableDependency {
689 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
690 where
691 D: de::Deserializer<'de>,
692 {
693 let value = serde_value::Value::deserialize(deserializer)?;
694
695 if let Ok(w) = TomlInheritedDependency::deserialize(serde_value::ValueDeserializer::<
696 D::Error,
697 >::new(value.clone()))
698 {
699 return if w.workspace {
700 Ok(InheritableDependency::Inherit(w))
701 } else {
702 Err(de::Error::custom("`workspace` cannot be false"))
703 };
704 }
705 TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
706 .map(InheritableDependency::Value)
707 }
708}
709
710#[derive(Deserialize, Serialize, Clone, Debug)]
711#[serde(rename_all = "kebab-case")]
712#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
713pub struct TomlInheritedDependency {
714 pub workspace: bool,
715 pub features: Option<Vec<String>>,
716 pub default_features: Option<bool>,
717 #[serde(rename = "default_features")]
718 pub default_features2: Option<bool>,
719 pub optional: Option<bool>,
720 pub public: Option<bool>,
721
722 #[serde(skip_serializing)]
724 #[serde(flatten)]
725 #[cfg_attr(feature = "unstable-schema", schemars(skip))]
726 pub _unused_keys: BTreeMap<String, toml::Value>,
727}
728
729impl TomlInheritedDependency {
730 pub fn default_features(&self) -> Option<bool> {
731 self.default_features.or(self.default_features2)
732 }
733}
734
735#[derive(Clone, Debug, Serialize)]
736#[serde(untagged)]
737#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
738pub enum TomlDependency<P: Clone = String> {
739 Simple(String),
742 Detailed(TomlDetailedDependency<P>),
746}
747
748impl TomlDependency {
749 pub fn is_version_specified(&self) -> bool {
750 match self {
751 TomlDependency::Detailed(d) => d.version.is_some(),
752 TomlDependency::Simple(..) => true,
753 }
754 }
755
756 pub fn is_optional(&self) -> bool {
757 match self {
758 TomlDependency::Detailed(d) => d.optional.unwrap_or(false),
759 TomlDependency::Simple(..) => false,
760 }
761 }
762
763 pub fn is_public(&self) -> bool {
764 match self {
765 TomlDependency::Detailed(d) => d.public.unwrap_or(false),
766 TomlDependency::Simple(..) => false,
767 }
768 }
769
770 pub fn default_features(&self) -> Option<bool> {
771 match self {
772 TomlDependency::Detailed(d) => d.default_features(),
773 TomlDependency::Simple(..) => None,
774 }
775 }
776
777 pub fn unused_keys(&self) -> Vec<String> {
778 match self {
779 TomlDependency::Simple(_) => vec![],
780 TomlDependency::Detailed(detailed) => detailed._unused_keys.keys().cloned().collect(),
781 }
782 }
783}
784
785impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P> {
786 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
787 where
788 D: de::Deserializer<'de>,
789 {
790 use serde::de::Error as _;
791 let expected = "a version string like \"0.9.8\" or a \
792 detailed dependency like { version = \"0.9.8\" }";
793 UntaggedEnumVisitor::new()
794 .expecting(expected)
795 .string(|value| Ok(TomlDependency::Simple(value.to_owned())))
796 .bool(|value| {
797 let expected = format!("invalid type: boolean `{value}`, expected {expected}");
798 let err = if value {
799 format!(
800 "{expected}\n\
801 note: if you meant to use a workspace member, you can write\n \
802 dep.workspace = {value}"
803 )
804 } else {
805 expected
806 };
807
808 Err(serde_untagged::de::Error::custom(err))
809 })
810 .map(|value| value.deserialize().map(TomlDependency::Detailed))
811 .deserialize(deserializer)
812 }
813}
814
815#[derive(Deserialize, Serialize, Clone, Debug)]
816#[serde(rename_all = "kebab-case")]
817#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
818pub struct TomlDetailedDependency<P: Clone = String> {
819 pub version: Option<String>,
820
821 #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
822 pub registry: Option<RegistryName>,
823 pub registry_index: Option<String>,
830 pub path: Option<P>,
833 #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
834 pub base: Option<PathBaseName>,
835 pub git: Option<String>,
836 pub branch: Option<String>,
837 pub tag: Option<String>,
838 pub rev: Option<String>,
839 pub features: Option<Vec<String>>,
840 pub optional: Option<bool>,
841 pub default_features: Option<bool>,
842 #[serde(rename = "default_features")]
843 pub default_features2: Option<bool>,
844 #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
845 pub package: Option<PackageName>,
846 pub public: Option<bool>,
847
848 pub artifact: Option<StringOrVec>,
850 pub lib: Option<bool>,
852 pub target: Option<String>,
854
855 #[serde(skip_serializing)]
857 #[serde(flatten)]
858 #[cfg_attr(feature = "unstable-schema", schemars(skip))]
859 pub _unused_keys: BTreeMap<String, toml::Value>,
860}
861
862impl<P: Clone> TomlDetailedDependency<P> {
863 pub fn default_features(&self) -> Option<bool> {
864 self.default_features.or(self.default_features2)
865 }
866}
867
868impl<P: Clone> Default for TomlDetailedDependency<P> {
870 fn default() -> Self {
871 Self {
872 version: Default::default(),
873 registry: Default::default(),
874 registry_index: Default::default(),
875 path: Default::default(),
876 base: Default::default(),
877 git: Default::default(),
878 branch: Default::default(),
879 tag: Default::default(),
880 rev: Default::default(),
881 features: Default::default(),
882 optional: Default::default(),
883 default_features: Default::default(),
884 default_features2: Default::default(),
885 package: Default::default(),
886 public: Default::default(),
887 artifact: Default::default(),
888 lib: Default::default(),
889 target: Default::default(),
890 _unused_keys: Default::default(),
891 }
892 }
893}
894
895#[derive(Deserialize, Serialize, Clone, Debug, Default)]
896#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
897pub struct TomlProfiles(pub BTreeMap<ProfileName, TomlProfile>);
898
899impl TomlProfiles {
900 pub fn get_all(&self) -> &BTreeMap<ProfileName, TomlProfile> {
901 &self.0
902 }
903
904 pub fn get(&self, name: &str) -> Option<&TomlProfile> {
905 self.0.get(name)
906 }
907}
908
909#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
910#[serde(default, rename_all = "kebab-case")]
911#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
912pub struct TomlProfile {
913 pub opt_level: Option<TomlOptLevel>,
914 pub lto: Option<StringOrBool>,
915 pub codegen_backend: Option<String>,
916 pub codegen_units: Option<u32>,
917 pub debug: Option<TomlDebugInfo>,
918 pub split_debuginfo: Option<String>,
919 pub debug_assertions: Option<bool>,
920 pub rpath: Option<bool>,
921 pub panic: Option<String>,
922 pub overflow_checks: Option<bool>,
923 pub incremental: Option<bool>,
924 pub dir_name: Option<String>,
925 pub inherits: Option<String>,
926 pub strip: Option<StringOrBool>,
927 pub rustflags: Option<Vec<String>>,
929 pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
932 pub build_override: Option<Box<TomlProfile>>,
933 pub trim_paths: Option<TomlTrimPaths>,
935 pub hint_mostly_unused: Option<bool>,
937}
938
939impl TomlProfile {
940 pub fn merge(&mut self, profile: &Self) {
942 if let Some(v) = &profile.opt_level {
943 self.opt_level = Some(v.clone());
944 }
945
946 if let Some(v) = &profile.lto {
947 self.lto = Some(v.clone());
948 }
949
950 if let Some(v) = &profile.codegen_backend {
951 self.codegen_backend = Some(v.clone());
952 }
953
954 if let Some(v) = profile.codegen_units {
955 self.codegen_units = Some(v);
956 }
957
958 if let Some(v) = profile.debug {
959 self.debug = Some(v);
960 }
961
962 if let Some(v) = profile.debug_assertions {
963 self.debug_assertions = Some(v);
964 }
965
966 if let Some(v) = &profile.split_debuginfo {
967 self.split_debuginfo = Some(v.clone());
968 }
969
970 if let Some(v) = profile.rpath {
971 self.rpath = Some(v);
972 }
973
974 if let Some(v) = &profile.panic {
975 self.panic = Some(v.clone());
976 }
977
978 if let Some(v) = profile.overflow_checks {
979 self.overflow_checks = Some(v);
980 }
981
982 if let Some(v) = profile.incremental {
983 self.incremental = Some(v);
984 }
985
986 if let Some(v) = &profile.rustflags {
987 self.rustflags = Some(v.clone());
988 }
989
990 if let Some(other_package) = &profile.package {
991 match &mut self.package {
992 Some(self_package) => {
993 for (spec, other_pkg_profile) in other_package {
994 match self_package.get_mut(spec) {
995 Some(p) => p.merge(other_pkg_profile),
996 None => {
997 self_package.insert(spec.clone(), other_pkg_profile.clone());
998 }
999 }
1000 }
1001 }
1002 None => self.package = Some(other_package.clone()),
1003 }
1004 }
1005
1006 if let Some(other_bo) = &profile.build_override {
1007 match &mut self.build_override {
1008 Some(self_bo) => self_bo.merge(other_bo),
1009 None => self.build_override = Some(other_bo.clone()),
1010 }
1011 }
1012
1013 if let Some(v) = &profile.inherits {
1014 self.inherits = Some(v.clone());
1015 }
1016
1017 if let Some(v) = &profile.dir_name {
1018 self.dir_name = Some(v.clone());
1019 }
1020
1021 if let Some(v) = &profile.strip {
1022 self.strip = Some(v.clone());
1023 }
1024
1025 if let Some(v) = &profile.trim_paths {
1026 self.trim_paths = Some(v.clone())
1027 }
1028
1029 if let Some(v) = profile.hint_mostly_unused {
1030 self.hint_mostly_unused = Some(v);
1031 }
1032 }
1033}
1034
1035#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
1036#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1037pub enum ProfilePackageSpec {
1038 Spec(PackageIdSpec),
1039 All,
1040}
1041
1042impl fmt::Display for ProfilePackageSpec {
1043 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1044 match self {
1045 ProfilePackageSpec::Spec(spec) => spec.fmt(f),
1046 ProfilePackageSpec::All => f.write_str("*"),
1047 }
1048 }
1049}
1050
1051impl ser::Serialize for ProfilePackageSpec {
1052 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
1053 where
1054 S: ser::Serializer,
1055 {
1056 self.to_string().serialize(s)
1057 }
1058}
1059
1060impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
1061 fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
1062 where
1063 D: de::Deserializer<'de>,
1064 {
1065 let string = String::deserialize(d)?;
1066 if string == "*" {
1067 Ok(ProfilePackageSpec::All)
1068 } else {
1069 PackageIdSpec::parse(&string)
1070 .map_err(de::Error::custom)
1071 .map(ProfilePackageSpec::Spec)
1072 }
1073 }
1074}
1075
1076#[derive(Clone, Debug, Eq, PartialEq)]
1077#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1078pub struct TomlOptLevel(pub String);
1079
1080impl ser::Serialize for TomlOptLevel {
1081 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1082 where
1083 S: ser::Serializer,
1084 {
1085 match self.0.parse::<u32>() {
1086 Ok(n) => n.serialize(serializer),
1087 Err(_) => self.0.serialize(serializer),
1088 }
1089 }
1090}
1091
1092impl<'de> de::Deserialize<'de> for TomlOptLevel {
1093 fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
1094 where
1095 D: de::Deserializer<'de>,
1096 {
1097 use serde::de::Error as _;
1098 UntaggedEnumVisitor::new()
1099 .expecting("an optimization level")
1100 .i64(|value| Ok(TomlOptLevel(value.to_string())))
1101 .string(|value| {
1102 if value == "s" || value == "z" {
1103 Ok(TomlOptLevel(value.to_string()))
1104 } else {
1105 Err(serde_untagged::de::Error::custom(format!(
1106 "must be `0`, `1`, `2`, `3`, `s` or `z`, \
1107 but found the string: \"{}\"",
1108 value
1109 )))
1110 }
1111 })
1112 .deserialize(d)
1113 }
1114}
1115
1116#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
1117#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1118pub enum TomlDebugInfo {
1119 None,
1120 LineDirectivesOnly,
1121 LineTablesOnly,
1122 Limited,
1123 Full,
1124}
1125
1126impl Display for TomlDebugInfo {
1127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1128 match self {
1129 TomlDebugInfo::None => f.write_char('0'),
1130 TomlDebugInfo::Limited => f.write_char('1'),
1131 TomlDebugInfo::Full => f.write_char('2'),
1132 TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
1133 TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
1134 }
1135 }
1136}
1137
1138impl ser::Serialize for TomlDebugInfo {
1139 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1140 where
1141 S: ser::Serializer,
1142 {
1143 match self {
1144 Self::None => 0.serialize(serializer),
1145 Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
1146 Self::LineTablesOnly => "line-tables-only".serialize(serializer),
1147 Self::Limited => 1.serialize(serializer),
1148 Self::Full => 2.serialize(serializer),
1149 }
1150 }
1151}
1152
1153impl<'de> de::Deserialize<'de> for TomlDebugInfo {
1154 fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
1155 where
1156 D: de::Deserializer<'de>,
1157 {
1158 use serde::de::Error as _;
1159 let expecting = "a boolean, 0, 1, 2, \"none\", \"limited\", \"full\", \"line-tables-only\", or \"line-directives-only\"";
1160 UntaggedEnumVisitor::new()
1161 .expecting(expecting)
1162 .bool(|value| {
1163 Ok(if value {
1164 TomlDebugInfo::Full
1165 } else {
1166 TomlDebugInfo::None
1167 })
1168 })
1169 .i64(|value| {
1170 let debuginfo = match value {
1171 0 => TomlDebugInfo::None,
1172 1 => TomlDebugInfo::Limited,
1173 2 => TomlDebugInfo::Full,
1174 _ => {
1175 return Err(serde_untagged::de::Error::invalid_value(
1176 Unexpected::Signed(value),
1177 &expecting,
1178 ));
1179 }
1180 };
1181 Ok(debuginfo)
1182 })
1183 .string(|value| {
1184 let debuginfo = match value {
1185 "none" => TomlDebugInfo::None,
1186 "limited" => TomlDebugInfo::Limited,
1187 "full" => TomlDebugInfo::Full,
1188 "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
1189 "line-tables-only" => TomlDebugInfo::LineTablesOnly,
1190 _ => {
1191 return Err(serde_untagged::de::Error::invalid_value(
1192 Unexpected::Str(value),
1193 &expecting,
1194 ));
1195 }
1196 };
1197 Ok(debuginfo)
1198 })
1199 .deserialize(d)
1200 }
1201}
1202
1203#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
1204#[serde(untagged, rename_all = "kebab-case")]
1205#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1206pub enum TomlTrimPaths {
1207 Values(Vec<TomlTrimPathsValue>),
1208 All,
1209}
1210
1211impl TomlTrimPaths {
1212 pub fn none() -> Self {
1213 TomlTrimPaths::Values(Vec::new())
1214 }
1215
1216 pub fn is_none(&self) -> bool {
1217 match self {
1218 TomlTrimPaths::Values(v) => v.is_empty(),
1219 TomlTrimPaths::All => false,
1220 }
1221 }
1222}
1223
1224impl<'de> de::Deserialize<'de> for TomlTrimPaths {
1225 fn deserialize<D>(d: D) -> Result<TomlTrimPaths, D::Error>
1226 where
1227 D: de::Deserializer<'de>,
1228 {
1229 use serde::de::Error as _;
1230 let expecting = r#"a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options"#;
1231 UntaggedEnumVisitor::new()
1232 .expecting(expecting)
1233 .bool(|value| {
1234 Ok(if value {
1235 TomlTrimPaths::All
1236 } else {
1237 TomlTrimPaths::none()
1238 })
1239 })
1240 .string(|v| match v {
1241 "none" => Ok(TomlTrimPaths::none()),
1242 "all" => Ok(TomlTrimPaths::All),
1243 v => {
1244 let d = v.into_deserializer();
1245 let err = |_: D::Error| {
1246 serde_untagged::de::Error::custom(format!("expected {expecting}"))
1247 };
1248 TomlTrimPathsValue::deserialize(d)
1249 .map_err(err)
1250 .map(|v| v.into())
1251 }
1252 })
1253 .seq(|seq| {
1254 let seq: Vec<String> = seq.deserialize()?;
1255 let seq: Vec<_> = seq
1256 .into_iter()
1257 .map(|s| TomlTrimPathsValue::deserialize(s.into_deserializer()))
1258 .collect::<Result<_, _>>()?;
1259 Ok(seq.into())
1260 })
1261 .deserialize(d)
1262 }
1263}
1264
1265impl fmt::Display for TomlTrimPaths {
1266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1267 match self {
1268 TomlTrimPaths::All => write!(f, "all"),
1269 TomlTrimPaths::Values(v) if v.is_empty() => write!(f, "none"),
1270 TomlTrimPaths::Values(v) => {
1271 let mut iter = v.iter();
1272 if let Some(value) = iter.next() {
1273 write!(f, "{value}")?;
1274 }
1275 for value in iter {
1276 write!(f, ",{value}")?;
1277 }
1278 Ok(())
1279 }
1280 }
1281 }
1282}
1283
1284impl From<TomlTrimPathsValue> for TomlTrimPaths {
1285 fn from(value: TomlTrimPathsValue) -> Self {
1286 TomlTrimPaths::Values(vec![value])
1287 }
1288}
1289
1290impl From<Vec<TomlTrimPathsValue>> for TomlTrimPaths {
1291 fn from(value: Vec<TomlTrimPathsValue>) -> Self {
1292 TomlTrimPaths::Values(value)
1293 }
1294}
1295
1296#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
1297#[serde(rename_all = "kebab-case")]
1298#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1299pub enum TomlTrimPathsValue {
1300 Diagnostics,
1301 Macro,
1302 Object,
1303}
1304
1305impl TomlTrimPathsValue {
1306 pub fn as_str(&self) -> &'static str {
1307 match self {
1308 TomlTrimPathsValue::Diagnostics => "diagnostics",
1309 TomlTrimPathsValue::Macro => "macro",
1310 TomlTrimPathsValue::Object => "object",
1311 }
1312 }
1313}
1314
1315impl fmt::Display for TomlTrimPathsValue {
1316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1317 write!(f, "{}", self.as_str())
1318 }
1319}
1320
1321pub type TomlLibTarget = TomlTarget;
1322pub type TomlBinTarget = TomlTarget;
1323pub type TomlExampleTarget = TomlTarget;
1324pub type TomlTestTarget = TomlTarget;
1325pub type TomlBenchTarget = TomlTarget;
1326
1327#[derive(Default, Serialize, Deserialize, Debug, Clone)]
1328#[serde(rename_all = "kebab-case")]
1329#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1330pub struct TomlTarget {
1331 pub name: Option<String>,
1332
1333 pub crate_type: Option<Vec<String>>,
1336 #[serde(rename = "crate_type")]
1337 pub crate_type2: Option<Vec<String>>,
1338
1339 #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
1340 pub path: Option<PathValue>,
1341 pub filename: Option<String>,
1343 pub test: Option<bool>,
1344 pub doctest: Option<bool>,
1345 pub bench: Option<bool>,
1346 pub doc: Option<bool>,
1347 pub doc_scrape_examples: Option<bool>,
1348 pub proc_macro: Option<bool>,
1349 #[serde(rename = "proc_macro")]
1350 pub proc_macro2: Option<bool>,
1351 pub harness: Option<bool>,
1352 pub required_features: Option<Vec<String>>,
1353 pub edition: Option<String>,
1354}
1355
1356impl TomlTarget {
1357 pub fn new() -> TomlTarget {
1358 TomlTarget::default()
1359 }
1360
1361 pub fn proc_macro(&self) -> Option<bool> {
1362 self.proc_macro.or(self.proc_macro2).or_else(|| {
1363 if let Some(types) = self.crate_types() {
1364 if types.contains(&"proc-macro".to_string()) {
1365 return Some(true);
1366 }
1367 }
1368 None
1369 })
1370 }
1371
1372 pub fn crate_types(&self) -> Option<&Vec<String>> {
1373 self.crate_type
1374 .as_ref()
1375 .or_else(|| self.crate_type2.as_ref())
1376 }
1377}
1378
1379macro_rules! str_newtype {
1380 ($name:ident) => {
1381 #[derive(Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1383 #[serde(transparent)]
1384 #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1385 pub struct $name<T: AsRef<str> = String>(T);
1386
1387 impl<T: AsRef<str>> $name<T> {
1388 pub fn into_inner(self) -> T {
1389 self.0
1390 }
1391 }
1392
1393 impl<T: AsRef<str>> AsRef<str> for $name<T> {
1394 fn as_ref(&self) -> &str {
1395 self.0.as_ref()
1396 }
1397 }
1398
1399 impl<T: AsRef<str>> std::ops::Deref for $name<T> {
1400 type Target = T;
1401
1402 fn deref(&self) -> &Self::Target {
1403 &self.0
1404 }
1405 }
1406
1407 impl<T: AsRef<str>> std::borrow::Borrow<str> for $name<T> {
1408 fn borrow(&self) -> &str {
1409 self.0.as_ref()
1410 }
1411 }
1412
1413 impl<'a> std::str::FromStr for $name<String> {
1414 type Err = restricted_names::NameValidationError;
1415
1416 fn from_str(value: &str) -> Result<Self, Self::Err> {
1417 Self::new(value.to_owned())
1418 }
1419 }
1420
1421 impl<'de, T: AsRef<str> + serde::Deserialize<'de>> serde::Deserialize<'de> for $name<T> {
1422 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1423 where
1424 D: serde::Deserializer<'de>,
1425 {
1426 let inner = T::deserialize(deserializer)?;
1427 Self::new(inner).map_err(serde::de::Error::custom)
1428 }
1429 }
1430
1431 impl<T: AsRef<str>> Display for $name<T> {
1432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1433 self.0.as_ref().fmt(f)
1434 }
1435 }
1436 };
1437}
1438
1439str_newtype!(PackageName);
1440
1441impl<T: AsRef<str>> PackageName<T> {
1442 pub fn new(name: T) -> Result<Self, NameValidationError> {
1444 restricted_names::validate_package_name(name.as_ref())?;
1445 Ok(Self(name))
1446 }
1447}
1448
1449impl PackageName {
1450 pub fn sanitize(name: impl AsRef<str>, placeholder: char) -> Self {
1454 PackageName(restricted_names::sanitize_package_name(
1455 name.as_ref(),
1456 placeholder,
1457 ))
1458 }
1459}
1460
1461str_newtype!(RegistryName);
1462
1463impl<T: AsRef<str>> RegistryName<T> {
1464 pub fn new(name: T) -> Result<Self, NameValidationError> {
1466 restricted_names::validate_registry_name(name.as_ref())?;
1467 Ok(Self(name))
1468 }
1469}
1470
1471str_newtype!(ProfileName);
1472
1473impl<T: AsRef<str>> ProfileName<T> {
1474 pub fn new(name: T) -> Result<Self, NameValidationError> {
1476 restricted_names::validate_profile_name(name.as_ref())?;
1477 Ok(Self(name))
1478 }
1479}
1480
1481str_newtype!(FeatureName);
1482
1483impl<T: AsRef<str>> FeatureName<T> {
1484 pub fn new(name: T) -> Result<Self, NameValidationError> {
1486 restricted_names::validate_feature_name(name.as_ref())?;
1487 Ok(Self(name))
1488 }
1489}
1490
1491str_newtype!(PathBaseName);
1492
1493impl<T: AsRef<str>> PathBaseName<T> {
1494 pub fn new(name: T) -> Result<Self, NameValidationError> {
1496 restricted_names::validate_path_base_name(name.as_ref())?;
1497 Ok(Self(name))
1498 }
1499}
1500
1501#[derive(Serialize, Deserialize, Debug, Clone)]
1503#[serde(rename_all = "kebab-case")]
1504#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1505pub struct TomlPlatform {
1506 pub dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
1507 pub build_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
1508 #[serde(rename = "build_dependencies")]
1509 pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
1510 pub dev_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
1511 #[serde(rename = "dev_dependencies")]
1512 pub dev_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
1513}
1514
1515impl TomlPlatform {
1516 pub fn dev_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
1517 self.dev_dependencies
1518 .as_ref()
1519 .or(self.dev_dependencies2.as_ref())
1520 }
1521
1522 pub fn build_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
1523 self.build_dependencies
1524 .as_ref()
1525 .or(self.build_dependencies2.as_ref())
1526 }
1527}
1528
1529#[derive(Serialize, Debug, Clone)]
1530#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1531pub struct InheritableLints {
1532 #[serde(skip_serializing_if = "std::ops::Not::not")]
1533 #[cfg_attr(feature = "unstable-schema", schemars(default))]
1534 pub workspace: bool,
1535 #[serde(flatten)]
1536 pub lints: TomlLints,
1537}
1538
1539impl InheritableLints {
1540 pub fn normalized(&self) -> Result<&TomlLints, UnresolvedError> {
1541 if self.workspace {
1542 Err(UnresolvedError)
1543 } else {
1544 Ok(&self.lints)
1545 }
1546 }
1547}
1548
1549impl<'de> Deserialize<'de> for InheritableLints {
1550 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1551 where
1552 D: de::Deserializer<'de>,
1553 {
1554 struct InheritableLintsVisitor;
1555
1556 impl<'de> de::Visitor<'de> for InheritableLintsVisitor {
1557 type Value = InheritableLints;
1559
1560 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1562 formatter.write_str("a lints table")
1563 }
1564
1565 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
1569 where
1570 M: de::MapAccess<'de>,
1571 {
1572 let mut lints = TomlLints::new();
1573 let mut workspace = false;
1574
1575 while let Some(key) = access.next_key()? {
1578 if key == "workspace" {
1579 workspace = match access.next_value()? {
1580 Some(WorkspaceValue) => true,
1581 None => false,
1582 };
1583 } else {
1584 let value = access.next_value()?;
1585 lints.insert(key, value);
1586 }
1587 }
1588
1589 Ok(InheritableLints { workspace, lints })
1590 }
1591 }
1592
1593 deserializer.deserialize_map(InheritableLintsVisitor)
1594 }
1595}
1596
1597pub type TomlLints = BTreeMap<String, TomlToolLints>;
1598
1599pub type TomlToolLints = BTreeMap<String, TomlLint>;
1600
1601#[derive(Serialize, Debug, Clone)]
1602#[serde(untagged)]
1603#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1604pub enum TomlLint {
1605 Level(TomlLintLevel),
1606 Config(TomlLintConfig),
1607}
1608
1609impl<'de> Deserialize<'de> for TomlLint {
1610 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1611 where
1612 D: de::Deserializer<'de>,
1613 {
1614 UntaggedEnumVisitor::new()
1615 .string(|string| {
1616 TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
1617 })
1618 .map(|map| map.deserialize().map(TomlLint::Config))
1619 .deserialize(deserializer)
1620 }
1621}
1622
1623impl TomlLint {
1624 pub fn level(&self) -> TomlLintLevel {
1625 match self {
1626 Self::Level(level) => *level,
1627 Self::Config(config) => config.level,
1628 }
1629 }
1630
1631 pub fn priority(&self) -> i8 {
1632 match self {
1633 Self::Level(_) => 0,
1634 Self::Config(config) => config.priority,
1635 }
1636 }
1637
1638 pub fn config(&self) -> Option<&toml::Table> {
1639 match self {
1640 Self::Level(_) => None,
1641 Self::Config(config) => Some(&config.config),
1642 }
1643 }
1644}
1645
1646#[derive(Serialize, Deserialize, Debug, Clone)]
1647#[serde(rename_all = "kebab-case")]
1648#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1649pub struct TomlLintConfig {
1650 pub level: TomlLintLevel,
1651 #[serde(default)]
1652 pub priority: i8,
1653 #[serde(flatten)]
1654 #[cfg_attr(
1655 feature = "unstable-schema",
1656 schemars(with = "HashMap<String, TomlValueWrapper>")
1657 )]
1658 pub config: toml::Table,
1659}
1660
1661#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
1662#[serde(rename_all = "kebab-case")]
1663#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1664pub enum TomlLintLevel {
1665 Forbid,
1666 Deny,
1667 Warn,
1668 Allow,
1669}
1670
1671#[derive(Serialize, Deserialize, Debug, Default, Clone)]
1672#[serde(rename_all = "kebab-case")]
1673#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1674pub struct Hints {
1675 #[cfg_attr(
1676 feature = "unstable-schema",
1677 schemars(with = "Option<TomlValueWrapper>")
1678 )]
1679 pub mostly_unused: Option<toml::Value>,
1680}
1681
1682#[derive(Copy, Clone, Debug)]
1683pub struct InvalidCargoFeatures {}
1684
1685impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
1686 fn deserialize<D>(_d: D) -> Result<Self, D::Error>
1687 where
1688 D: de::Deserializer<'de>,
1689 {
1690 use serde::de::Error as _;
1691
1692 Err(D::Error::custom(
1693 "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
1694 ))
1695 }
1696}
1697
1698#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
1701#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1702pub struct StringOrVec(pub Vec<String>);
1703
1704impl StringOrVec {
1705 pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
1706 self.0.iter()
1707 }
1708}
1709
1710impl<'de> de::Deserialize<'de> for StringOrVec {
1711 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1712 where
1713 D: de::Deserializer<'de>,
1714 {
1715 UntaggedEnumVisitor::new()
1716 .expecting("string or list of strings")
1717 .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
1718 .seq(|value| value.deserialize().map(StringOrVec))
1719 .deserialize(deserializer)
1720 }
1721}
1722
1723#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
1724#[serde(untagged)]
1725#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1726pub enum StringOrBool {
1727 String(String),
1728 Bool(bool),
1729}
1730
1731impl<'de> Deserialize<'de> for StringOrBool {
1732 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1733 where
1734 D: de::Deserializer<'de>,
1735 {
1736 UntaggedEnumVisitor::new()
1737 .bool(|b| Ok(StringOrBool::Bool(b)))
1738 .string(|s| Ok(StringOrBool::String(s.to_owned())))
1739 .deserialize(deserializer)
1740 }
1741}
1742
1743#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
1744#[serde(untagged)]
1745#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1746pub enum TomlPackageBuild {
1747 Auto(bool),
1750
1751 SingleScript(String),
1753
1754 MultipleScript(Vec<String>),
1756}
1757
1758impl<'de> Deserialize<'de> for TomlPackageBuild {
1759 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1760 where
1761 D: de::Deserializer<'de>,
1762 {
1763 UntaggedEnumVisitor::new()
1764 .bool(|b| Ok(TomlPackageBuild::Auto(b)))
1765 .string(|s| Ok(TomlPackageBuild::SingleScript(s.to_owned())))
1766 .seq(|value| value.deserialize().map(TomlPackageBuild::MultipleScript))
1767 .deserialize(deserializer)
1768 }
1769}
1770
1771#[derive(PartialEq, Clone, Debug, Serialize)]
1772#[serde(untagged)]
1773#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1774pub enum VecStringOrBool {
1775 VecString(Vec<String>),
1776 Bool(bool),
1777}
1778
1779impl<'de> de::Deserialize<'de> for VecStringOrBool {
1780 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1781 where
1782 D: de::Deserializer<'de>,
1783 {
1784 UntaggedEnumVisitor::new()
1785 .expecting("a boolean or vector of strings")
1786 .bool(|value| Ok(VecStringOrBool::Bool(value)))
1787 .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
1788 .deserialize(deserializer)
1789 }
1790}
1791
1792#[derive(Clone)]
1793pub struct PathValue(pub PathBuf);
1794
1795impl fmt::Debug for PathValue {
1796 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1797 self.0.fmt(f)
1798 }
1799}
1800
1801impl ser::Serialize for PathValue {
1802 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1803 where
1804 S: ser::Serializer,
1805 {
1806 self.0.serialize(serializer)
1807 }
1808}
1809
1810impl<'de> de::Deserialize<'de> for PathValue {
1811 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1812 where
1813 D: de::Deserializer<'de>,
1814 {
1815 Ok(PathValue(String::deserialize(deserializer)?.into()))
1816 }
1817}
1818
1819#[derive(Debug, thiserror::Error)]
1821#[error("manifest field was not resolved")]
1822#[non_exhaustive]
1823#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1824pub struct UnresolvedError;
1825
1826#[cfg(feature = "unstable-schema")]
1827#[test]
1828fn dump_manifest_schema() {
1829 let schema = schemars::schema_for!(crate::manifest::TomlManifest);
1830 let dump = serde_json::to_string_pretty(&schema).unwrap();
1831 snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json").raw());
1832}