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