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