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}
911
912impl TomlProfile {
913 pub fn merge(&mut self, profile: &Self) {
915 if let Some(v) = &profile.opt_level {
916 self.opt_level = Some(v.clone());
917 }
918
919 if let Some(v) = &profile.lto {
920 self.lto = Some(v.clone());
921 }
922
923 if let Some(v) = &profile.codegen_backend {
924 self.codegen_backend = Some(v.clone());
925 }
926
927 if let Some(v) = profile.codegen_units {
928 self.codegen_units = Some(v);
929 }
930
931 if let Some(v) = profile.debug {
932 self.debug = Some(v);
933 }
934
935 if let Some(v) = profile.debug_assertions {
936 self.debug_assertions = Some(v);
937 }
938
939 if let Some(v) = &profile.split_debuginfo {
940 self.split_debuginfo = Some(v.clone());
941 }
942
943 if let Some(v) = profile.rpath {
944 self.rpath = Some(v);
945 }
946
947 if let Some(v) = &profile.panic {
948 self.panic = Some(v.clone());
949 }
950
951 if let Some(v) = profile.overflow_checks {
952 self.overflow_checks = Some(v);
953 }
954
955 if let Some(v) = profile.incremental {
956 self.incremental = Some(v);
957 }
958
959 if let Some(v) = &profile.rustflags {
960 self.rustflags = Some(v.clone());
961 }
962
963 if let Some(other_package) = &profile.package {
964 match &mut self.package {
965 Some(self_package) => {
966 for (spec, other_pkg_profile) in other_package {
967 match self_package.get_mut(spec) {
968 Some(p) => p.merge(other_pkg_profile),
969 None => {
970 self_package.insert(spec.clone(), other_pkg_profile.clone());
971 }
972 }
973 }
974 }
975 None => self.package = Some(other_package.clone()),
976 }
977 }
978
979 if let Some(other_bo) = &profile.build_override {
980 match &mut self.build_override {
981 Some(self_bo) => self_bo.merge(other_bo),
982 None => self.build_override = Some(other_bo.clone()),
983 }
984 }
985
986 if let Some(v) = &profile.inherits {
987 self.inherits = Some(v.clone());
988 }
989
990 if let Some(v) = &profile.dir_name {
991 self.dir_name = Some(v.clone());
992 }
993
994 if let Some(v) = &profile.strip {
995 self.strip = Some(v.clone());
996 }
997
998 if let Some(v) = &profile.trim_paths {
999 self.trim_paths = Some(v.clone())
1000 }
1001 }
1002}
1003
1004#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
1005pub enum ProfilePackageSpec {
1006 Spec(PackageIdSpec),
1007 All,
1008}
1009
1010impl fmt::Display for ProfilePackageSpec {
1011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1012 match self {
1013 ProfilePackageSpec::Spec(spec) => spec.fmt(f),
1014 ProfilePackageSpec::All => f.write_str("*"),
1015 }
1016 }
1017}
1018
1019impl ser::Serialize for ProfilePackageSpec {
1020 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
1021 where
1022 S: ser::Serializer,
1023 {
1024 self.to_string().serialize(s)
1025 }
1026}
1027
1028impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
1029 fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
1030 where
1031 D: de::Deserializer<'de>,
1032 {
1033 let string = String::deserialize(d)?;
1034 if string == "*" {
1035 Ok(ProfilePackageSpec::All)
1036 } else {
1037 PackageIdSpec::parse(&string)
1038 .map_err(de::Error::custom)
1039 .map(ProfilePackageSpec::Spec)
1040 }
1041 }
1042}
1043
1044#[derive(Clone, Debug, Eq, PartialEq)]
1045#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1046pub struct TomlOptLevel(pub String);
1047
1048impl ser::Serialize for TomlOptLevel {
1049 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1050 where
1051 S: ser::Serializer,
1052 {
1053 match self.0.parse::<u32>() {
1054 Ok(n) => n.serialize(serializer),
1055 Err(_) => self.0.serialize(serializer),
1056 }
1057 }
1058}
1059
1060impl<'de> de::Deserialize<'de> for TomlOptLevel {
1061 fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
1062 where
1063 D: de::Deserializer<'de>,
1064 {
1065 use serde::de::Error as _;
1066 UntaggedEnumVisitor::new()
1067 .expecting("an optimization level")
1068 .i64(|value| Ok(TomlOptLevel(value.to_string())))
1069 .string(|value| {
1070 if value == "s" || value == "z" {
1071 Ok(TomlOptLevel(value.to_string()))
1072 } else {
1073 Err(serde_untagged::de::Error::custom(format!(
1074 "must be `0`, `1`, `2`, `3`, `s` or `z`, \
1075 but found the string: \"{}\"",
1076 value
1077 )))
1078 }
1079 })
1080 .deserialize(d)
1081 }
1082}
1083
1084#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
1085#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1086pub enum TomlDebugInfo {
1087 None,
1088 LineDirectivesOnly,
1089 LineTablesOnly,
1090 Limited,
1091 Full,
1092}
1093
1094impl Display for TomlDebugInfo {
1095 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1096 match self {
1097 TomlDebugInfo::None => f.write_char('0'),
1098 TomlDebugInfo::Limited => f.write_char('1'),
1099 TomlDebugInfo::Full => f.write_char('2'),
1100 TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
1101 TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
1102 }
1103 }
1104}
1105
1106impl ser::Serialize for TomlDebugInfo {
1107 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1108 where
1109 S: ser::Serializer,
1110 {
1111 match self {
1112 Self::None => 0.serialize(serializer),
1113 Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
1114 Self::LineTablesOnly => "line-tables-only".serialize(serializer),
1115 Self::Limited => 1.serialize(serializer),
1116 Self::Full => 2.serialize(serializer),
1117 }
1118 }
1119}
1120
1121impl<'de> de::Deserialize<'de> for TomlDebugInfo {
1122 fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
1123 where
1124 D: de::Deserializer<'de>,
1125 {
1126 use serde::de::Error as _;
1127 let expecting = "a boolean, 0, 1, 2, \"none\", \"limited\", \"full\", \"line-tables-only\", or \"line-directives-only\"";
1128 UntaggedEnumVisitor::new()
1129 .expecting(expecting)
1130 .bool(|value| {
1131 Ok(if value {
1132 TomlDebugInfo::Full
1133 } else {
1134 TomlDebugInfo::None
1135 })
1136 })
1137 .i64(|value| {
1138 let debuginfo = match value {
1139 0 => TomlDebugInfo::None,
1140 1 => TomlDebugInfo::Limited,
1141 2 => TomlDebugInfo::Full,
1142 _ => {
1143 return Err(serde_untagged::de::Error::invalid_value(
1144 Unexpected::Signed(value),
1145 &expecting,
1146 ))
1147 }
1148 };
1149 Ok(debuginfo)
1150 })
1151 .string(|value| {
1152 let debuginfo = match value {
1153 "none" => TomlDebugInfo::None,
1154 "limited" => TomlDebugInfo::Limited,
1155 "full" => TomlDebugInfo::Full,
1156 "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
1157 "line-tables-only" => TomlDebugInfo::LineTablesOnly,
1158 _ => {
1159 return Err(serde_untagged::de::Error::invalid_value(
1160 Unexpected::Str(value),
1161 &expecting,
1162 ))
1163 }
1164 };
1165 Ok(debuginfo)
1166 })
1167 .deserialize(d)
1168 }
1169}
1170
1171#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
1172#[serde(untagged, rename_all = "kebab-case")]
1173#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1174pub enum TomlTrimPaths {
1175 Values(Vec<TomlTrimPathsValue>),
1176 All,
1177}
1178
1179impl TomlTrimPaths {
1180 pub fn none() -> Self {
1181 TomlTrimPaths::Values(Vec::new())
1182 }
1183
1184 pub fn is_none(&self) -> bool {
1185 match self {
1186 TomlTrimPaths::Values(v) => v.is_empty(),
1187 TomlTrimPaths::All => false,
1188 }
1189 }
1190}
1191
1192impl<'de> de::Deserialize<'de> for TomlTrimPaths {
1193 fn deserialize<D>(d: D) -> Result<TomlTrimPaths, D::Error>
1194 where
1195 D: de::Deserializer<'de>,
1196 {
1197 use serde::de::Error as _;
1198 let expecting = r#"a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options"#;
1199 UntaggedEnumVisitor::new()
1200 .expecting(expecting)
1201 .bool(|value| {
1202 Ok(if value {
1203 TomlTrimPaths::All
1204 } else {
1205 TomlTrimPaths::none()
1206 })
1207 })
1208 .string(|v| match v {
1209 "none" => Ok(TomlTrimPaths::none()),
1210 "all" => Ok(TomlTrimPaths::All),
1211 v => {
1212 let d = v.into_deserializer();
1213 let err = |_: D::Error| {
1214 serde_untagged::de::Error::custom(format!("expected {expecting}"))
1215 };
1216 TomlTrimPathsValue::deserialize(d)
1217 .map_err(err)
1218 .map(|v| v.into())
1219 }
1220 })
1221 .seq(|seq| {
1222 let seq: Vec<String> = seq.deserialize()?;
1223 let seq: Vec<_> = seq
1224 .into_iter()
1225 .map(|s| TomlTrimPathsValue::deserialize(s.into_deserializer()))
1226 .collect::<Result<_, _>>()?;
1227 Ok(seq.into())
1228 })
1229 .deserialize(d)
1230 }
1231}
1232
1233impl fmt::Display for TomlTrimPaths {
1234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1235 match self {
1236 TomlTrimPaths::All => write!(f, "all"),
1237 TomlTrimPaths::Values(v) if v.is_empty() => write!(f, "none"),
1238 TomlTrimPaths::Values(v) => {
1239 let mut iter = v.iter();
1240 if let Some(value) = iter.next() {
1241 write!(f, "{value}")?;
1242 }
1243 for value in iter {
1244 write!(f, ",{value}")?;
1245 }
1246 Ok(())
1247 }
1248 }
1249 }
1250}
1251
1252impl From<TomlTrimPathsValue> for TomlTrimPaths {
1253 fn from(value: TomlTrimPathsValue) -> Self {
1254 TomlTrimPaths::Values(vec![value])
1255 }
1256}
1257
1258impl From<Vec<TomlTrimPathsValue>> for TomlTrimPaths {
1259 fn from(value: Vec<TomlTrimPathsValue>) -> Self {
1260 TomlTrimPaths::Values(value)
1261 }
1262}
1263
1264#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
1265#[serde(rename_all = "kebab-case")]
1266#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1267pub enum TomlTrimPathsValue {
1268 Diagnostics,
1269 Macro,
1270 Object,
1271}
1272
1273impl TomlTrimPathsValue {
1274 pub fn as_str(&self) -> &'static str {
1275 match self {
1276 TomlTrimPathsValue::Diagnostics => "diagnostics",
1277 TomlTrimPathsValue::Macro => "macro",
1278 TomlTrimPathsValue::Object => "object",
1279 }
1280 }
1281}
1282
1283impl fmt::Display for TomlTrimPathsValue {
1284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1285 write!(f, "{}", self.as_str())
1286 }
1287}
1288
1289pub type TomlLibTarget = TomlTarget;
1290pub type TomlBinTarget = TomlTarget;
1291pub type TomlExampleTarget = TomlTarget;
1292pub type TomlTestTarget = TomlTarget;
1293pub type TomlBenchTarget = TomlTarget;
1294
1295#[derive(Default, Serialize, Deserialize, Debug, Clone)]
1296#[serde(rename_all = "kebab-case")]
1297#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1298pub struct TomlTarget {
1299 pub name: Option<String>,
1300
1301 pub crate_type: Option<Vec<String>>,
1304 #[serde(rename = "crate_type")]
1305 pub crate_type2: Option<Vec<String>>,
1306
1307 #[cfg_attr(feature = "unstable-schema", schemars(with = "Option<String>"))]
1308 pub path: Option<PathValue>,
1309 pub filename: Option<String>,
1311 pub test: Option<bool>,
1312 pub doctest: Option<bool>,
1313 pub bench: Option<bool>,
1314 pub doc: Option<bool>,
1315 pub doc_scrape_examples: Option<bool>,
1316 pub proc_macro: Option<bool>,
1317 #[serde(rename = "proc_macro")]
1318 pub proc_macro2: Option<bool>,
1319 pub harness: Option<bool>,
1320 pub required_features: Option<Vec<String>>,
1321 pub edition: Option<String>,
1322}
1323
1324impl TomlTarget {
1325 pub fn new() -> TomlTarget {
1326 TomlTarget::default()
1327 }
1328
1329 pub fn proc_macro(&self) -> Option<bool> {
1330 self.proc_macro.or(self.proc_macro2).or_else(|| {
1331 if let Some(types) = self.crate_types() {
1332 if types.contains(&"proc-macro".to_string()) {
1333 return Some(true);
1334 }
1335 }
1336 None
1337 })
1338 }
1339
1340 pub fn crate_types(&self) -> Option<&Vec<String>> {
1341 self.crate_type
1342 .as_ref()
1343 .or_else(|| self.crate_type2.as_ref())
1344 }
1345}
1346
1347macro_rules! str_newtype {
1348 ($name:ident) => {
1349 #[derive(Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1351 #[serde(transparent)]
1352 pub struct $name<T: AsRef<str> = String>(T);
1353
1354 impl<T: AsRef<str>> $name<T> {
1355 pub fn into_inner(self) -> T {
1356 self.0
1357 }
1358 }
1359
1360 impl<T: AsRef<str>> AsRef<str> for $name<T> {
1361 fn as_ref(&self) -> &str {
1362 self.0.as_ref()
1363 }
1364 }
1365
1366 impl<T: AsRef<str>> std::ops::Deref for $name<T> {
1367 type Target = T;
1368
1369 fn deref(&self) -> &Self::Target {
1370 &self.0
1371 }
1372 }
1373
1374 impl<T: AsRef<str>> std::borrow::Borrow<str> for $name<T> {
1375 fn borrow(&self) -> &str {
1376 self.0.as_ref()
1377 }
1378 }
1379
1380 impl<'a> std::str::FromStr for $name<String> {
1381 type Err = restricted_names::NameValidationError;
1382
1383 fn from_str(value: &str) -> Result<Self, Self::Err> {
1384 Self::new(value.to_owned())
1385 }
1386 }
1387
1388 impl<'de, T: AsRef<str> + serde::Deserialize<'de>> serde::Deserialize<'de> for $name<T> {
1389 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1390 where
1391 D: serde::Deserializer<'de>,
1392 {
1393 let inner = T::deserialize(deserializer)?;
1394 Self::new(inner).map_err(serde::de::Error::custom)
1395 }
1396 }
1397
1398 impl<T: AsRef<str>> Display for $name<T> {
1399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1400 self.0.as_ref().fmt(f)
1401 }
1402 }
1403 };
1404}
1405
1406str_newtype!(PackageName);
1407
1408impl<T: AsRef<str>> PackageName<T> {
1409 pub fn new(name: T) -> Result<Self, NameValidationError> {
1411 restricted_names::validate_package_name(name.as_ref())?;
1412 Ok(Self(name))
1413 }
1414}
1415
1416impl PackageName {
1417 pub fn sanitize(name: impl AsRef<str>, placeholder: char) -> Self {
1421 PackageName(restricted_names::sanitize_package_name(
1422 name.as_ref(),
1423 placeholder,
1424 ))
1425 }
1426}
1427
1428str_newtype!(RegistryName);
1429
1430impl<T: AsRef<str>> RegistryName<T> {
1431 pub fn new(name: T) -> Result<Self, NameValidationError> {
1433 restricted_names::validate_registry_name(name.as_ref())?;
1434 Ok(Self(name))
1435 }
1436}
1437
1438str_newtype!(ProfileName);
1439
1440impl<T: AsRef<str>> ProfileName<T> {
1441 pub fn new(name: T) -> Result<Self, NameValidationError> {
1443 restricted_names::validate_profile_name(name.as_ref())?;
1444 Ok(Self(name))
1445 }
1446}
1447
1448str_newtype!(FeatureName);
1449
1450impl<T: AsRef<str>> FeatureName<T> {
1451 pub fn new(name: T) -> Result<Self, NameValidationError> {
1453 restricted_names::validate_feature_name(name.as_ref())?;
1454 Ok(Self(name))
1455 }
1456}
1457
1458str_newtype!(PathBaseName);
1459
1460impl<T: AsRef<str>> PathBaseName<T> {
1461 pub fn new(name: T) -> Result<Self, NameValidationError> {
1463 restricted_names::validate_path_base_name(name.as_ref())?;
1464 Ok(Self(name))
1465 }
1466}
1467
1468#[derive(Serialize, Deserialize, Debug, Clone)]
1470#[serde(rename_all = "kebab-case")]
1471#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1472pub struct TomlPlatform {
1473 pub dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
1474 pub build_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
1475 #[serde(rename = "build_dependencies")]
1476 pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
1477 pub dev_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
1478 #[serde(rename = "dev_dependencies")]
1479 pub dev_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
1480}
1481
1482impl TomlPlatform {
1483 pub fn dev_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
1484 self.dev_dependencies
1485 .as_ref()
1486 .or(self.dev_dependencies2.as_ref())
1487 }
1488
1489 pub fn build_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
1490 self.build_dependencies
1491 .as_ref()
1492 .or(self.build_dependencies2.as_ref())
1493 }
1494}
1495
1496#[derive(Serialize, Debug, Clone)]
1497#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1498pub struct InheritableLints {
1499 #[serde(skip_serializing_if = "is_false")]
1500 #[cfg_attr(feature = "unstable-schema", schemars(default))]
1501 pub workspace: bool,
1502 #[serde(flatten)]
1503 pub lints: TomlLints,
1504}
1505
1506impl InheritableLints {
1507 pub fn normalized(&self) -> Result<&TomlLints, UnresolvedError> {
1508 if self.workspace {
1509 Err(UnresolvedError)
1510 } else {
1511 Ok(&self.lints)
1512 }
1513 }
1514}
1515
1516fn is_false(b: &bool) -> bool {
1517 !b
1518}
1519
1520impl<'de> Deserialize<'de> for InheritableLints {
1521 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1522 where
1523 D: de::Deserializer<'de>,
1524 {
1525 struct InheritableLintsVisitor;
1526
1527 impl<'de> de::Visitor<'de> for InheritableLintsVisitor {
1528 type Value = InheritableLints;
1530
1531 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1533 formatter.write_str("a lints table")
1534 }
1535
1536 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
1540 where
1541 M: de::MapAccess<'de>,
1542 {
1543 let mut lints = TomlLints::new();
1544 let mut workspace = false;
1545
1546 while let Some(key) = access.next_key()? {
1549 if key == "workspace" {
1550 workspace = match access.next_value()? {
1551 Some(WorkspaceValue) => true,
1552 None => false,
1553 };
1554 } else {
1555 let value = access.next_value()?;
1556 lints.insert(key, value);
1557 }
1558 }
1559
1560 Ok(InheritableLints { workspace, lints })
1561 }
1562 }
1563
1564 deserializer.deserialize_map(InheritableLintsVisitor)
1565 }
1566}
1567
1568pub type TomlLints = BTreeMap<String, TomlToolLints>;
1569
1570pub type TomlToolLints = BTreeMap<String, TomlLint>;
1571
1572#[derive(Serialize, Debug, Clone)]
1573#[serde(untagged)]
1574#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1575pub enum TomlLint {
1576 Level(TomlLintLevel),
1577 Config(TomlLintConfig),
1578}
1579
1580impl<'de> Deserialize<'de> for TomlLint {
1581 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1582 where
1583 D: de::Deserializer<'de>,
1584 {
1585 UntaggedEnumVisitor::new()
1586 .string(|string| {
1587 TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
1588 })
1589 .map(|map| map.deserialize().map(TomlLint::Config))
1590 .deserialize(deserializer)
1591 }
1592}
1593
1594impl TomlLint {
1595 pub fn level(&self) -> TomlLintLevel {
1596 match self {
1597 Self::Level(level) => *level,
1598 Self::Config(config) => config.level,
1599 }
1600 }
1601
1602 pub fn priority(&self) -> i8 {
1603 match self {
1604 Self::Level(_) => 0,
1605 Self::Config(config) => config.priority,
1606 }
1607 }
1608
1609 pub fn config(&self) -> Option<&toml::Table> {
1610 match self {
1611 Self::Level(_) => None,
1612 Self::Config(config) => Some(&config.config),
1613 }
1614 }
1615}
1616
1617#[derive(Serialize, Deserialize, Debug, Clone)]
1618#[serde(rename_all = "kebab-case")]
1619#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1620pub struct TomlLintConfig {
1621 pub level: TomlLintLevel,
1622 #[serde(default)]
1623 pub priority: i8,
1624 #[serde(flatten)]
1625 #[cfg_attr(
1626 feature = "unstable-schema",
1627 schemars(with = "HashMap<String, TomlValueWrapper>")
1628 )]
1629 pub config: toml::Table,
1630}
1631
1632#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
1633#[serde(rename_all = "kebab-case")]
1634#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1635pub enum TomlLintLevel {
1636 Forbid,
1637 Deny,
1638 Warn,
1639 Allow,
1640}
1641
1642#[derive(Copy, Clone, Debug)]
1643pub struct InvalidCargoFeatures {}
1644
1645impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
1646 fn deserialize<D>(_d: D) -> Result<Self, D::Error>
1647 where
1648 D: de::Deserializer<'de>,
1649 {
1650 use serde::de::Error as _;
1651
1652 Err(D::Error::custom(
1653 "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
1654 ))
1655 }
1656}
1657
1658#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
1661#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1662pub struct StringOrVec(pub Vec<String>);
1663
1664impl StringOrVec {
1665 pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
1666 self.0.iter()
1667 }
1668}
1669
1670impl<'de> de::Deserialize<'de> for StringOrVec {
1671 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1672 where
1673 D: de::Deserializer<'de>,
1674 {
1675 UntaggedEnumVisitor::new()
1676 .expecting("string or list of strings")
1677 .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
1678 .seq(|value| value.deserialize().map(StringOrVec))
1679 .deserialize(deserializer)
1680 }
1681}
1682
1683#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
1684#[serde(untagged)]
1685#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1686pub enum StringOrBool {
1687 String(String),
1688 Bool(bool),
1689}
1690
1691impl<'de> Deserialize<'de> for StringOrBool {
1692 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1693 where
1694 D: de::Deserializer<'de>,
1695 {
1696 UntaggedEnumVisitor::new()
1697 .bool(|b| Ok(StringOrBool::Bool(b)))
1698 .string(|s| Ok(StringOrBool::String(s.to_owned())))
1699 .deserialize(deserializer)
1700 }
1701}
1702
1703#[derive(PartialEq, Clone, Debug, Serialize)]
1704#[serde(untagged)]
1705#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1706pub enum VecStringOrBool {
1707 VecString(Vec<String>),
1708 Bool(bool),
1709}
1710
1711impl<'de> de::Deserialize<'de> for VecStringOrBool {
1712 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1713 where
1714 D: de::Deserializer<'de>,
1715 {
1716 UntaggedEnumVisitor::new()
1717 .expecting("a boolean or vector of strings")
1718 .bool(|value| Ok(VecStringOrBool::Bool(value)))
1719 .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
1720 .deserialize(deserializer)
1721 }
1722}
1723
1724#[derive(Clone)]
1725pub struct PathValue(pub PathBuf);
1726
1727impl fmt::Debug for PathValue {
1728 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1729 self.0.fmt(f)
1730 }
1731}
1732
1733impl ser::Serialize for PathValue {
1734 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1735 where
1736 S: ser::Serializer,
1737 {
1738 self.0.serialize(serializer)
1739 }
1740}
1741
1742impl<'de> de::Deserialize<'de> for PathValue {
1743 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1744 where
1745 D: de::Deserializer<'de>,
1746 {
1747 Ok(PathValue(String::deserialize(deserializer)?.into()))
1748 }
1749}
1750
1751#[derive(Debug, thiserror::Error)]
1753#[error("manifest field was not resolved")]
1754#[non_exhaustive]
1755#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
1756pub struct UnresolvedError;
1757
1758#[cfg(feature = "unstable-schema")]
1759#[test]
1760fn dump_manifest_schema() {
1761 let schema = schemars::schema_for!(crate::manifest::TomlManifest);
1762 let dump = serde_json::to_string_pretty(&schema).unwrap();
1763 snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json").raw());
1764}