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