1#![forbid(unsafe_code)]
2#![allow(clippy::inline_always)]
3#![allow(clippy::missing_errors_doc)]
4#![allow(clippy::needless_for_each)]
5#![allow(clippy::new_without_default)]
6#![allow(clippy::redundant_closure_for_method_calls)]
7#![allow(clippy::trivially_copy_pass_by_ref)]
8#![cfg_attr(docsrs, feature(doc_cfg))]
9
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
22use std::collections::{BTreeMap, BTreeSet};
23use std::fmt::Display;
24use std::mem::take;
25use std::path::{Path, PathBuf};
26use std::{fs, io};
27pub use toml::Value;
28
29pub type DepsSet = BTreeMap<String, Dependency>;
33pub type TargetDepsSet = BTreeMap<String, Target>;
35pub type FeatureSet = BTreeMap<String, Vec<String>>;
41pub type PatchSet = BTreeMap<String, DepsSet>;
43pub type LintSet = BTreeMap<String, Lint>;
45pub type LintGroups = BTreeMap<String, LintSet>;
47
48mod afs;
49mod error;
50mod inheritable;
51pub use crate::afs::*;
52pub use crate::error::Error;
53pub use crate::inheritable::Inheritable;
54
55#[cfg(feature = "features")]
56#[cfg_attr(docsrs, doc(cfg(feature = "features")))]
57pub mod features;
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64#[serde(rename_all = "kebab-case")]
65pub struct Manifest<Metadata = Value> {
66 pub package: Option<Package<Metadata>>,
68
69 pub workspace: Option<Workspace<Metadata>>,
71
72 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
74 pub dependencies: DepsSet,
75
76 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
78 pub dev_dependencies: DepsSet,
79
80 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
82 pub build_dependencies: DepsSet,
83
84 #[serde(default, skip_serializing_if = "TargetDepsSet::is_empty")]
86 pub target: TargetDepsSet,
87
88 #[serde(default, skip_serializing_if = "FeatureSet::is_empty", deserialize_with = "feature_set")]
97 pub features: FeatureSet,
98
99 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
101 #[deprecated(note = "Cargo recommends patch instead")]
102 pub replace: DepsSet,
103
104 #[serde(default, skip_serializing_if = "PatchSet::is_empty")]
106 pub patch: PatchSet,
107
108 pub lib: Option<Product>,
111
112 #[serde(default, skip_serializing_if = "Profiles::should_skip_serializing")]
114 pub profile: Profiles,
115
116 #[serde(default, skip_serializing_if = "Badges::should_skip_serializing")]
118 pub badges: Badges,
119
120 #[serde(default, skip_serializing_if = "Vec::is_empty")]
123 pub bin: Vec<Product>,
124
125 #[serde(default, skip_serializing_if = "Vec::is_empty")]
127 pub bench: Vec<Product>,
128
129 #[serde(default, skip_serializing_if = "Vec::is_empty")]
131 pub test: Vec<Product>,
132
133 #[serde(default, skip_serializing_if = "Vec::is_empty")]
135 pub example: Vec<Product>,
136
137 #[serde(default, skip_serializing_if = "Inheritable::<LintGroups>::is_empty")]
139 pub lints: Inheritable<LintGroups>,
140}
141
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
144#[serde(rename_all = "kebab-case")]
145pub struct Workspace<Metadata = Value> {
146 #[serde(default)]
148 pub members: Vec<String>,
149
150 #[serde(default, skip_serializing_if = "Vec::is_empty")]
154 pub default_members: Vec<String>,
155
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub package: Option<PackageTemplate>,
159
160 #[serde(default, skip_serializing_if = "Vec::is_empty")]
162 pub exclude: Vec<String>,
163
164 #[serde(skip_serializing_if = "Option::is_none")]
166 pub metadata: Option<Metadata>,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub resolver: Option<Resolver>,
171
172 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
174 pub dependencies: DepsSet,
175
176 #[serde(default, skip_serializing_if = "LintGroups::is_empty")]
178 pub lints: LintGroups,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
183#[serde(rename_all = "kebab-case")]
184#[non_exhaustive]
185pub struct PackageTemplate {
186 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub authors: Option<Vec<String>>,
189
190 #[serde(default, skip_serializing_if = "Option::is_none")]
192 pub categories: Option<Vec<String>>,
193
194 #[serde(default, skip_serializing_if = "Option::is_none")]
196 pub description: Option<String>,
197
198 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub documentation: Option<String>,
201
202 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub edition: Option<Edition>,
205
206 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub exclude: Option<Vec<String>>,
209
210 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub homepage: Option<String>,
213
214 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub include: Option<Vec<String>>,
217
218 #[serde(default, skip_serializing_if = "Option::is_none")]
220 pub keywords: Option<Vec<String>>,
221
222 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub license: Option<String>,
225
226 #[serde(default, skip_serializing_if = "Option::is_none")]
228 pub license_file: Option<PathBuf>,
229
230 #[serde(default, skip_serializing_if = "Publish::is_default")]
232 pub publish: Publish,
233
234 #[serde(default, skip_serializing_if = "OptionalFile::is_default")]
236 pub readme: OptionalFile,
237
238 #[serde(default, skip_serializing_if = "Option::is_none")]
240 pub repository: Option<String>,
241
242 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub rust_version: Option<String>,
245
246 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub version: Option<String>,
249}
250
251fn default_true() -> bool {
252 true
253}
254
255fn is_true(val: &bool) -> bool {
256 *val
257}
258
259fn is_false(val: &bool) -> bool {
260 !*val
261}
262
263impl Manifest<Value> {
264 #[inline]
269 pub fn from_path(cargo_toml_path: impl AsRef<Path>) -> Result<Self, Error> {
270 Self::from_path_with_metadata(cargo_toml_path)
271 }
272
273 #[inline(always)]
279 pub fn from_slice(cargo_toml_content: &[u8]) -> Result<Self, Error> {
280 Self::from_slice_with_metadata(cargo_toml_content)
281 }
282
283 #[inline(always)]
291 #[allow(clippy::should_implement_trait)]
292 pub fn from_str(cargo_toml_content: &str) -> Result<Self, Error> {
293 Self::from_slice_with_metadata_str(cargo_toml_content)
294 }
295}
296
297impl<Metadata: for<'a> Deserialize<'a>> Manifest<Metadata> {
298 #[inline]
304 pub fn from_slice_with_metadata(cargo_toml_content: &[u8]) -> Result<Self, Error> {
305 let cargo_toml_content = std::str::from_utf8(cargo_toml_content).map_err(|_| Error::Other("utf8"))?;
306 Self::from_slice_with_metadata_str(cargo_toml_content)
307 }
308
309 #[inline(never)]
310 fn from_slice_with_metadata_str(cargo_toml_content: &str) -> Result<Self, Error> {
311 let mut manifest: Self = toml::from_str(cargo_toml_content)?;
312
313 if let Some(package) = &mut manifest.package {
314 if package.version.get().is_ok_and(|v| v == "0.0.0") && package.publish.get().is_ok_and(|p| p.is_default()) {
318 package.publish = Inheritable::Set(Publish::Flag(false));
319 }
320 }
321 Ok(manifest)
322 }
323
324 pub fn from_path_with_metadata<P: AsRef<Path>>(cargo_toml_path: P) -> Result<Self, Error> {
328 let cargo_toml_path = cargo_toml_path.as_ref();
329 let cargo_toml_content = fs::read_to_string(cargo_toml_path)?;
330 let mut manifest = Self::from_slice_with_metadata_str(&cargo_toml_content)?;
331 manifest.complete_from_path(cargo_toml_path)?;
332 Ok(manifest)
333 }
334}
335
336impl<Metadata> Manifest<Metadata> {
337 pub fn complete_from_path(&mut self, path: &Path) -> Result<(), Error> {
345 let manifest_dir = path.parent().ok_or(Error::Other("bad path"))?;
346 self.complete_from_abstract_filesystem::<Value, _>(Filesystem::new(manifest_dir), None)
347 }
348
349 pub fn complete_from_path_and_workspace<PackageMetadataTypeDoesNotMatterHere>(&mut self, package_manifest_path: &Path, workspace_manifest_and_path: Option<(&Manifest<PackageMetadataTypeDoesNotMatterHere>, &Path)>) -> Result<(), Error> {
355 let manifest_dir = package_manifest_path.parent().ok_or(Error::Other("bad path"))?;
356 self.complete_from_abstract_filesystem(Filesystem::new(manifest_dir), workspace_manifest_and_path)
357 }
358
359 pub fn complete_from_abstract_filesystem<PackageMetadataTypeDoesNotMatterHere, Fs: AbstractFilesystem>(
370 &mut self, fs: Fs, workspace_manifest_and_path: Option<(&Manifest<PackageMetadataTypeDoesNotMatterHere>, &Path)>,
371 ) -> Result<(), Error> {
372 let tmp;
373 let err_path;
374 let res = if let Some((ws, ws_path)) = workspace_manifest_and_path {
375 err_path = Some(ws_path);
376 self._inherit_workspace(ws.workspace.as_ref(), ws_path)
377 } else if let Some(ws) = self.workspace.take() {
378 err_path = Some(Path::new(""));
379 let res = self._inherit_workspace(Some(&ws), Path::new(""));
381 self.workspace = Some(ws);
382 res
383 } else if self.needs_workspace_inheritance() {
384 let ws_path_hint = self.package.as_ref().and_then(|p| p.workspace.as_deref());
385 match fs.parse_root_workspace(ws_path_hint) {
386 Ok((ws_manifest, base_path)) => {
387 tmp = base_path;
388 err_path = Some(&tmp);
389 self._inherit_workspace(ws_manifest.workspace.as_ref(), &tmp)
390 },
391 Err(e) => {
392 err_path = if let Some(p) = ws_path_hint { tmp = p.to_owned(); Some(&tmp) } else { None };
393 Err(e)
394 },
395 }
396 } else {
397 err_path = None;
398 Ok(())
399 };
400
401 match res.and_then(|()| self.complete_from_abstract_filesystem_inner(&fs)) {
402 Ok(()) => Ok(()),
403 Err(e @ Error::Workspace(_)) => Err(e),
404 Err(e) => Err(Error::Workspace(Box::new((e, err_path.map(PathBuf::from))))),
405 }
406 }
407
408 pub fn needs_workspace_inheritance(&self) -> bool {
412 self.package.as_ref().is_some_and(Package::needs_workspace_inheritance) ||
413 !self.lints.is_set() ||
414 self.dependencies.values()
415 .chain(self.build_dependencies.values())
416 .chain(self.dev_dependencies.values())
417 .any(|dep| {
418 matches!(dep, Dependency::Inherited(_))
419 })
420 }
421
422 fn _inherit_workspace<Ignored>(&mut self, workspace: Option<&Workspace<Ignored>>, workspace_base_path: &Path) -> Result<(), Error> {
423 let workspace_base_path = if workspace_base_path.file_name() == Some("Cargo.toml".as_ref()) {
424 workspace_base_path.parent().ok_or(Error::Other("bad path"))?
425 } else {
426 workspace_base_path
427 };
428
429 inherit_dependencies(&mut self.dependencies, workspace, workspace_base_path)?;
430 inherit_dependencies(&mut self.build_dependencies, workspace, workspace_base_path)?;
431 inherit_dependencies(&mut self.dev_dependencies, workspace, workspace_base_path)?;
432
433 for target in self.target.values_mut() {
434 inherit_dependencies(&mut target.dependencies, workspace, workspace_base_path)?;
435 inherit_dependencies(&mut target.build_dependencies, workspace, workspace_base_path)?;
436 inherit_dependencies(&mut target.dev_dependencies, workspace, workspace_base_path)?;
437 }
438
439 if let Some(ws) = workspace {
440 self.lints.inherit(&ws.lints);
441 }
442
443 let package = match &mut self.package {
444 Some(p) => p,
445 None => return Ok(()),
446 };
447 if let Some(ws) = workspace.and_then(|w| w.package.as_ref()) {
448 Self::inherit_package_properties(package, ws, workspace_base_path)?;
449 }
450
451 if package.needs_workspace_inheritance() {
452 return Err(Error::WorkspaceIntegrity(format!("not all fields of `{}` have been present in workspace.package", package.name())));
453 }
454 Ok(())
455 }
456
457 fn inherit_package_properties(package: &mut Package<Metadata>, ws: &PackageTemplate, workspace_base_path: &Path) -> Result<(), Error> {
458 fn maybe_inherit<T: Clone>(to: Option<&mut Inheritable<T>>, from: Option<&T>) {
459 if let Some(from) = from {
460 if let Some(to) = to {
461 to.inherit(from);
462 }
463 }
464 }
465 fn inherit<T: Clone>(to: &mut Inheritable<T>, from: Option<&T>) {
466 if let Some(from) = from {
467 to.inherit(from);
468 }
469 }
470 inherit(&mut package.authors, ws.authors.as_ref());
471 inherit(&mut package.categories, ws.categories.as_ref());
472 inherit(&mut package.edition, ws.edition.as_ref());
473 inherit(&mut package.exclude, ws.exclude.as_ref());
474 inherit(&mut package.include, ws.include.as_ref());
475 inherit(&mut package.keywords, ws.keywords.as_ref());
476 inherit(&mut package.version, ws.version.as_ref());
477 maybe_inherit(package.description.as_mut(), ws.description.as_ref());
478 maybe_inherit(package.documentation.as_mut(), ws.documentation.as_ref());
479 maybe_inherit(package.homepage.as_mut(), ws.homepage.as_ref());
480 maybe_inherit(package.license.as_mut(), ws.license.as_ref());
481 maybe_inherit(package.repository.as_mut(), ws.repository.as_ref());
482 maybe_inherit(package.rust_version.as_mut(), ws.rust_version.as_ref());
483 package.publish.inherit(&ws.publish);
484 match (&mut package.readme, &ws.readme) {
485 (r @ Inheritable::Inherited, flag @ OptionalFile::Flag(_)) => {
486 r.set(flag.clone());
487 },
488 (r @ Inheritable::Inherited, OptionalFile::Path(path)) => {
489 r.set(OptionalFile::Path(workspace_base_path.join(path)));
490 },
491 _ => {},
492 }
493 if let Some((f, ws)) = package.license_file.as_mut().zip(ws.license_file.as_ref()) {
494 f.set(workspace_base_path.join(ws));
495 }
496 Ok(())
497 }
498
499 fn complete_from_abstract_filesystem_inner(&mut self, fs: &dyn AbstractFilesystem) -> Result<(), Error> {
500 let Some(package) = &self.package else { return Ok(()) };
501
502 let src = match fs.file_names_in("src") {
503 Ok(src) => src,
504 Err(err) if err.kind() == io::ErrorKind::NotFound => Default::default(),
505 Err(err) => return Err(err.into()),
506 };
507
508 let has_path = self.lib.as_ref().is_some_and(|l| l.path.is_some());
509 if !has_path && (package.autolib || self.lib.is_some()) && src.contains("lib.rs") {
510 self.lib
511 .get_or_insert_with(Product::default)
512 .path = Some("src/lib.rs".to_string());
513 }
514 if let Some(lib) = &mut self.lib {
515 lib.name.get_or_insert_with(|| package.name.replace('-', "_"));
516 if lib.edition.is_none() {
517 lib.edition = Some(*package.edition.get()?);
518 }
519 if lib.crate_type.is_empty() {
520 lib.crate_type.push("lib".to_string());
521 }
522 lib.required_features.clear(); }
524
525 if package.autobins {
526 let mut bin = take(&mut self.bin);
527 let (fully_overrided, mut partial_overrided) = self.autoset(&mut bin, "src/bin", fs)?;
528 self.bin = bin;
529
530 if src.contains("main.rs") && !fully_overrided.contains("src/main.rs") {
531 let rel_path = "src/main.rs".to_string();
532 let name = &package.name;
533
534 let product = if let Some(mut product) = partial_overrided.remove(name) {
535 product.path = Some(rel_path);
536 product
537 } else {
538 Product {
539 name: Some(name.clone()),
540 path: Some(rel_path),
541 edition: Some(*package.edition.get()?),
542 ..Product::default()
543 }
544 };
545 self.bin.push(product);
546 }
547 }
548
549 Self::sort_products(&mut self.bin);
550
551 if package.autoexamples {
552 let mut example = take(&mut self.example);
553 self.autoset(&mut example, "examples", fs)?;
554 self.example = example;
555 }
556
557 Self::sort_products(&mut self.example);
558
559 if package.autotests {
560 let mut test = take(&mut self.test);
561 self.autoset(&mut test, "tests", fs)?;
562 self.test = test;
563 }
564
565 Self::sort_products(&mut self.test);
566
567 if package.autobenches {
568 let mut bench = take(&mut self.bench);
569 self.autoset(&mut bench, "benches", fs)?;
570 self.bench = bench;
571 }
572
573 Self::sort_products(&mut self.bench);
574
575 let Some(package) = &mut self.package else { return Ok(()) };
576
577 let root_files = fs.file_names_in("")?;
578
579 if matches!(package.build, None | Some(OptionalFile::Flag(true))) && root_files.contains("build.rs") {
580 package.build = Some(OptionalFile::Path("build.rs".into()));
581 }
582
583 if matches!(package.readme.get()?, OptionalFile::Flag(true)) {
584 if let Some(name) = root_files.get("README.md").or_else(|| root_files.get("README.txt")).or_else(|| root_files.get("README")) {
585 package.readme = Inheritable::Set(OptionalFile::Path(PathBuf::from(&**name)));
586 }
587 }
588 Ok(())
589 }
590
591 fn autoset(
593 &self,
594 out: &mut Vec<Product>,
595 dir: &str,
596 fs: &dyn AbstractFilesystem,
597 ) -> Result<(BTreeSet<String>, BTreeMap<String, Product>), Error> {
598 let fully_overrided: BTreeSet<_> = out.iter()
599 .filter_map(|product| product.path.clone())
600 .collect();
601
602 let mut partial_overrided: BTreeMap<String, Product> = out.iter()
603 .filter_map(|product| {
604 match (&product.path, &product.name) {
605 (None, Some(name)) => {
606 Some((name.clone(), product.clone()))
607 },
608 _ => None
609 }
610 })
611 .collect();
612
613 out.retain(|product| product.path.is_some());
615
616 if let Some(package) = &self.package {
617 if let Ok(bins) = fs.file_names_in(dir) {
618 for name in bins {
619 let rel_path = format!("{dir}/{name}");
620
621 if name.ends_with(".rs") {
622 if !fully_overrided.contains(&rel_path) {
623 let name = name.trim_end_matches(".rs");
624
625 let product = if let Some(mut product) = partial_overrided.remove(name) {
626 product.path = Some(rel_path);
627 product
628 } else {
629 Product {
630 name: Some(name.to_string()),
631 path: Some(rel_path),
632 edition: Some(*package.edition.get()?),
633 ..Product::default()
634 }
635 };
636 out.push(product);
637 }
638 } else if let Ok(sub) = fs.file_names_in(&rel_path) {
639 let rel_path = format!("{rel_path}/main.rs");
640
641 if sub.contains("main.rs") && !fully_overrided.contains(&rel_path) {
642 let product = if let Some(mut product) = partial_overrided.remove(&*name) {
643 product.path = Some(rel_path);
644 product
645 } else {
646 Product {
647 name: Some(name.into()),
648 path: Some(rel_path),
649 edition: Some(*package.edition.get()?),
650 ..Product::default()
651 }
652 };
653 out.push(product);
654 }
655 }
656 }
657 }
658 }
659 Ok((fully_overrided, partial_overrided))
660 }
661
662 fn sort_products(products: &mut [Product]) {
664 products.sort_unstable_by(|a, b| a.name.cmp(&b.name).then(a.path.cmp(&b.path)));
665 }
666
667 #[track_caller]
675 #[inline]
676 pub fn package(&self) -> &Package<Metadata> {
677 self.package.as_ref().expect("not a package")
678 }
679
680 pub fn lints(&self) -> &LintGroups {
682 self.lints.as_ref().unwrap()
683 }
684}
685
686fn inherit_dependencies<Ignored>(deps_to_inherit: &mut BTreeMap<String, Dependency>, workspace: Option<&Workspace<Ignored>>, workspace_base_path: &Path) -> Result<(), Error> {
687 for (key, dep) in deps_to_inherit {
688 if let Dependency::Inherited(overrides) = dep {
689 let template = workspace.and_then(|ws| ws.dependencies.get(key))
690 .ok_or_else(|| Error::WorkspaceIntegrity(format!("workspace dependencies are missing `{key}`")))?;
691 let mut overrides = overrides.clone();
692 *dep = template.clone();
693 if overrides.optional {
694 dep.try_detail_mut()?.optional = true;
695 }
696 if !overrides.features.is_empty() {
697 dep.try_detail_mut()?.features.append(&mut overrides.features);
698 }
699 if let Dependency::Detailed(dep) = dep {
700 dep.inherited = true;
701 if let Some(path) = &mut dep.path {
702 *path = workspace_base_path.join(&path).display().to_string();
703 }
704 }
705 }
706 }
707 Ok(())
708}
709
710impl<Metadata: Default> Default for Manifest<Metadata> {
711 #[allow(deprecated)]
712 fn default() -> Self {
713 Self {
714 package: Default::default(),
715 workspace: Default::default(),
716 dependencies: Default::default(),
717 dev_dependencies: Default::default(),
718 build_dependencies: Default::default(),
719 target: Default::default(),
720 features: Default::default(),
721 replace: Default::default(),
722 patch: Default::default(),
723 lib: Default::default(),
724 profile: Default::default(),
725 badges: Default::default(),
726 bin: Default::default(),
727 bench: Default::default(),
728 test: Default::default(),
729 example: Default::default(),
730 lints: Default::default(),
731 }
732 }
733}
734
735#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
737pub struct Profiles {
738 #[serde(skip_serializing_if = "Option::is_none")]
740 pub release: Option<Profile>,
741
742 #[serde(skip_serializing_if = "Option::is_none")]
744 pub dev: Option<Profile>,
745
746 #[serde(skip_serializing_if = "Option::is_none")]
748 pub test: Option<Profile>,
749
750 #[serde(skip_serializing_if = "Option::is_none")]
752 pub bench: Option<Profile>,
753
754 #[serde(skip_serializing_if = "Option::is_none")]
756 #[deprecated(note = "the `doc` profile is obsolete. `cargo doc` uses the `dev` profile")]
757 pub doc: Option<Profile>,
758
759 #[serde(flatten)]
761 pub custom: BTreeMap<String, Profile>,
762}
763
764impl Profiles {
765 fn should_skip_serializing(&self) -> bool {
767 self.release.is_none()
768 && self.dev.is_none()
769 && self.test.is_none()
770 && self.bench.is_none()
771 && self.custom.is_empty()
772 }
773}
774
775#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
777#[serde(try_from = "toml::Value")]
778pub enum DebugSetting {
779 None = 0,
781 Lines = 1,
783 Full = 2,
785}
786
787impl TryFrom<Value> for DebugSetting {
788 type Error = Error;
789
790 fn try_from(v: Value) -> Result<Self, Error> {
791 Ok(match v {
792 Value::Boolean(b) => if b { Self::Full } else { Self::None },
793 Value::Integer(n) => match n {
794 0 => Self::None,
795 1 => Self::Lines,
796 2 => Self::Full,
797 _ => return Err(Error::Other("wrong number for debug setting")),
798 },
799 Value::String(s) => match s.as_str() {
800 "none" => Self::None,
801 "limited" | "line-directives-only" | "line-tables-only" => Self::Lines,
802 "full" => Self::Full,
803 _ => return Err(Error::Other("wrong name for debug setting")),
804 },
805 _ => return Err(Error::Other("wrong data type for debug setting")),
806 })
807 }
808}
809
810impl Serialize for DebugSetting {
811 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
812 match self {
813 Self::None => serializer.serialize_bool(false),
814 Self::Lines => serializer.serialize_i8(1),
815 Self::Full => serializer.serialize_bool(true),
816 }
817 }
818}
819
820#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
822#[serde(try_from = "toml::Value")]
823pub enum StripSetting {
824 None,
826 Debuginfo,
828 Symbols,
830}
831
832impl Serialize for StripSetting {
833 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
834 match self {
835 Self::None => serializer.serialize_bool(false),
836 Self::Debuginfo => serializer.serialize_str("debuginfo"),
837 Self::Symbols => serializer.serialize_bool(true),
838 }
839 }
840}
841
842impl TryFrom<Value> for StripSetting {
843 type Error = Error;
844
845 fn try_from(v: Value) -> Result<Self, Error> {
846 Ok(match v {
847 Value::Boolean(b) => if b { Self::Symbols } else { Self::None },
848 Value::String(s) => match s.as_str() {
849 "none" => Self::None,
850 "debuginfo" => Self::Debuginfo,
851 "symbols" => Self::Symbols,
852 _ => return Err(Error::Other("strip setting has unknown string value")),
853 },
854 _ => return Err(Error::Other("wrong data type for strip setting")),
855 })
856 }
857}
858
859#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
861#[serde(try_from = "toml::Value")]
862pub enum LtoSetting {
863 None,
865 ThinLocal,
867 Thin,
868 Fat,
870}
871
872impl Serialize for LtoSetting {
873 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
874 match self {
875 Self::None => serializer.serialize_str("off"),
876 Self::ThinLocal => serializer.serialize_bool(false),
877 Self::Thin => serializer.serialize_str("thin"),
878 Self::Fat => serializer.serialize_bool(true),
879 }
880 }
881}
882
883impl TryFrom<Value> for LtoSetting {
884 type Error = Error;
885
886 fn try_from(v: Value) -> Result<Self, Error> {
887 Ok(match v {
888 Value::Boolean(b) => if b { Self::Fat } else { Self::ThinLocal },
889 Value::String(s) => match s.as_str() {
890 "off" | "n" | "no" => Self::None,
891 "thin" => Self::Thin,
892 "fat" | "on" | "y" | "yes" | "true" => Self::Fat,
893 "false" => Self::ThinLocal,
894 _ => return Err(Error::Other("lto setting has unknown string value")),
895 },
896 _ => return Err(Error::Other("wrong data type for lto setting")),
897 })
898 }
899}
900
901#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
903#[serde(rename_all = "kebab-case")]
904pub struct Profile {
905 #[serde(default, skip_serializing_if = "Option::is_none")]
907 pub opt_level: Option<Value>,
908
909 #[serde(default, skip_serializing_if = "Option::is_none")]
911 pub debug: Option<DebugSetting>,
912
913 #[serde(default, skip_serializing_if = "Option::is_none")]
915 pub split_debuginfo: Option<String>,
916
917 #[serde(default, skip_serializing_if = "Option::is_none")]
919 pub rpath: Option<bool>,
920
921 #[serde(default, skip_serializing_if = "Option::is_none")]
923 pub lto: Option<LtoSetting>,
924
925 #[serde(default, skip_serializing_if = "Option::is_none")]
927 pub debug_assertions: Option<bool>,
928
929 #[serde(default, skip_serializing_if = "Option::is_none")]
931 pub codegen_units: Option<u16>,
932
933 #[serde(default, skip_serializing_if = "Option::is_none")]
935 pub panic: Option<String>,
936
937 #[serde(default, skip_serializing_if = "Option::is_none")]
939 pub incremental: Option<bool>,
940
941 #[serde(default, skip_serializing_if = "Option::is_none")]
943 pub overflow_checks: Option<bool>,
944
945 #[serde(default, skip_serializing_if = "Option::is_none")]
947 pub strip: Option<StripSetting>,
948
949 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
951 pub package: BTreeMap<String, Value>,
952
953 #[serde(default, skip_serializing_if = "Option::is_none")]
955 pub build_override: Option<Value>,
956
957 #[serde(default, skip_serializing_if = "Option::is_none")]
959 pub inherits: Option<String>,
960}
961
962#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
963#[serde(rename_all = "kebab-case")]
964pub struct Product {
968 pub path: Option<String>,
970
971 pub name: Option<String>,
976
977 #[serde(default = "default_true", skip_serializing_if = "is_true")]
979 pub test: bool,
980
981 #[serde(default = "default_true", skip_serializing_if = "is_true")]
985 pub doctest: bool,
986
987 #[serde(default = "default_true", skip_serializing_if = "is_true")]
989 pub bench: bool,
990
991 #[serde(default = "default_true", skip_serializing_if = "is_true")]
993 pub doc: bool,
994
995 #[serde(default, skip_serializing_if = "is_false")]
998 pub plugin: bool,
999
1000 #[serde(default, alias = "proc_macro", alias = "proc-macro", skip_serializing_if = "is_false")]
1003 pub proc_macro: bool,
1004
1005 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1009 pub harness: bool,
1010
1011 #[serde(default, skip_serializing_if = "Option::is_none")]
1018 pub edition: Option<Edition>,
1019
1020 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1022 pub crate_type: Vec<String>,
1023
1024 #[serde(default)]
1029 pub required_features: Vec<String>,
1030}
1031
1032impl Default for Product {
1033 fn default() -> Self {
1034 Self {
1035 path: None,
1036 name: None,
1037 test: true,
1038 doctest: true,
1039 bench: true,
1040 doc: true,
1041 harness: true,
1042 plugin: false,
1043 proc_macro: false,
1044 required_features: Vec::new(),
1045 crate_type: Vec::new(),
1046 edition: None,
1047 }
1048 }
1049}
1050
1051#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
1053#[serde(rename_all = "kebab-case")]
1054pub struct Target {
1055 #[serde(default)]
1057 pub dependencies: DepsSet,
1058 #[serde(default)]
1060 pub dev_dependencies: DepsSet,
1061 #[serde(default)]
1063 pub build_dependencies: DepsSet,
1064}
1065
1066#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1070#[serde(untagged)]
1071pub enum Dependency {
1072 Simple(String),
1074 Inherited(InheritedDependencyDetail), Detailed(Box<DependencyDetail>),
1078}
1079
1080impl Dependency {
1081 #[inline]
1085 #[must_use]
1086 pub fn detail(&self) -> Option<&DependencyDetail> {
1087 match self {
1088 Self::Detailed(d) => Some(d),
1089 Self::Simple(_) | Self::Inherited(_) => None,
1090 }
1091 }
1092
1093 #[inline]
1095 #[track_caller]
1096 pub fn detail_mut(&mut self) -> &mut DependencyDetail {
1097 self.try_detail_mut().expect("dependency not available due to workspace inheritance")
1098 }
1099
1100 pub fn try_detail_mut(&mut self) -> Result<&mut DependencyDetail, Error> {
1104 match self {
1105 Self::Detailed(d) => Ok(d),
1106 Self::Simple(ver) => {
1107 *self = Self::Detailed(Box::new(DependencyDetail {
1108 version: Some(ver.clone()),
1109 ..Default::default()
1110 }));
1111 match self {
1112 Self::Detailed(d) => Ok(d),
1113 _ => unreachable!(),
1114 }
1115 },
1116 Self::Inherited(_) => Err(Error::InheritedUnknownValue),
1117 }
1118 }
1119
1120 #[inline]
1124 #[track_caller]
1125 #[must_use]
1126 pub fn req(&self) -> &str {
1127 self.try_req().unwrap()
1128 }
1129
1130 #[inline]
1134 #[track_caller]
1135 pub fn try_req(&self) -> Result<&str, Error> {
1136 match self {
1137 Self::Simple(v) => Ok(v),
1138 Self::Detailed(d) => Ok(d.version.as_deref().unwrap_or("*")),
1139 Self::Inherited(_) => Err(Error::InheritedUnknownValue),
1140 }
1141 }
1142
1143 #[inline]
1145 #[must_use]
1146 pub fn req_features(&self) -> &[String] {
1147 match self {
1148 Self::Simple(_) => &[],
1149 Self::Detailed(d) => &d.features,
1150 Self::Inherited(d) => &d.features,
1151 }
1152 }
1153
1154 #[inline]
1157 #[must_use]
1158 pub fn optional(&self) -> bool {
1159 match self {
1160 Self::Simple(_) => false,
1161 Self::Detailed(d) => d.optional,
1162 Self::Inherited(d) => d.optional,
1163 }
1164 }
1165
1166 #[inline]
1169 #[must_use]
1170 pub fn package(&self) -> Option<&str> {
1171 match self {
1172 Self::Detailed(d) => d.package.as_deref(),
1173 Self::Simple(_) | Self::Inherited(_) => None,
1174 }
1175 }
1176
1177 #[inline]
1179 #[must_use]
1180 pub fn git(&self) -> Option<&str> {
1181 self.detail()?.git.as_deref()
1182 }
1183
1184 #[inline]
1186 #[must_use]
1187 pub fn git_rev(&self) -> Option<&str> {
1188 self.detail()?.rev.as_deref()
1189 }
1190
1191 #[track_caller]
1194 #[must_use]
1195 pub fn is_crates_io(&self) -> bool {
1196 match self {
1197 Self::Simple(_) => true,
1198 Self::Detailed(d) => {
1199 d.path.is_none() &&
1201 d.registry.is_none() &&
1202 d.registry_index.is_none() &&
1203 d.git.is_none() &&
1204 d.tag.is_none() &&
1205 d.branch.is_none() &&
1206 d.rev.is_none()
1207 },
1208 Self::Inherited(_) => panic!("data not available with workspace inheritance"),
1209 }
1210 }
1211}
1212
1213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1215#[serde(rename_all = "kebab-case")]
1216pub struct DependencyDetail {
1217 #[serde(skip_serializing_if = "Option::is_none")]
1219 pub version: Option<String>,
1220
1221 #[serde(skip_serializing_if = "Option::is_none")]
1225 pub package: Option<String>,
1226
1227 #[serde(skip_serializing_if = "Option::is_none")]
1231 pub registry: Option<String>,
1232
1233 #[serde(skip_serializing_if = "Option::is_none")]
1235 pub registry_index: Option<String>,
1236
1237 #[serde(skip_serializing_if = "Option::is_none")]
1242 pub path: Option<String>,
1243
1244 #[serde(skip)]
1249 pub inherited: bool,
1250
1251 #[serde(skip_serializing_if = "Option::is_none")]
1253 pub git: Option<String>,
1254 #[serde(skip_serializing_if = "Option::is_none")]
1256 pub branch: Option<String>,
1257 #[serde(skip_serializing_if = "Option::is_none")]
1259 pub tag: Option<String>,
1260 #[serde(skip_serializing_if = "Option::is_none")]
1262 pub rev: Option<String>,
1263
1264 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1268 pub features: Vec<String>,
1269
1270 #[serde(default, skip_serializing_if = "is_false")]
1275 pub optional: bool,
1276
1277 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1279 pub default_features: bool,
1280
1281 #[serde(flatten)]
1283 pub unstable: BTreeMap<String, Value>,
1284}
1285
1286impl Default for DependencyDetail {
1287 fn default() -> Self {
1288 Self {
1289 version: None,
1290 registry: None,
1291 registry_index: None,
1292 path: None,
1293 inherited: false,
1294 git: None,
1295 branch: None,
1296 tag: None,
1297 rev: None,
1298 features: Vec::new(),
1299 optional: false,
1300 default_features: true, package: None,
1302 unstable: BTreeMap::new(),
1303 }
1304 }
1305}
1306
1307#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
1310#[serde(rename_all = "kebab-case")]
1311pub struct InheritedDependencyDetail {
1312 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1313 pub features: Vec<String>,
1314
1315 #[serde(default, skip_serializing_if = "is_false")]
1316 pub optional: bool,
1317
1318 #[serde(skip_serializing_if = "is_false")]
1319 pub workspace: bool,
1320}
1321
1322#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1329#[serde(rename_all = "kebab-case")]
1330#[non_exhaustive]
1331pub struct Package<Metadata = Value> {
1332 pub name: String,
1334
1335 #[serde(default = "default_version")]
1342 pub version: Inheritable<String>,
1343
1344 #[serde(default)]
1346 pub edition: Inheritable<Edition>,
1347
1348 #[serde(default, skip_serializing_if = "Option::is_none")]
1350 pub rust_version: Option<Inheritable<String>>,
1351
1352 #[serde(default, skip_serializing_if = "Option::is_none")]
1354 pub build: Option<OptionalFile>,
1355
1356 #[serde(default, skip_serializing_if = "Option::is_none")]
1358 pub workspace: Option<PathBuf>,
1359
1360 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1364 pub authors: Inheritable<Vec<String>>,
1365
1366 #[serde(default, skip_serializing_if = "Option::is_none")]
1368 pub links: Option<String>,
1369
1370 #[serde(default, skip_serializing_if = "Option::is_none")]
1373 pub description: Option<Inheritable<String>>,
1374
1375 #[serde(default, skip_serializing_if = "Option::is_none")]
1377 pub homepage: Option<Inheritable<String>>,
1378
1379 #[serde(default, skip_serializing_if = "Option::is_none")]
1381 pub documentation: Option<Inheritable<String>>,
1382
1383 #[serde(default, skip_serializing_if = "Inheritable::is_default")]
1386 pub readme: Inheritable<OptionalFile>,
1387
1388 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1390 pub keywords: Inheritable<Vec<String>>,
1391
1392 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1395 pub categories: Inheritable<Vec<String>>,
1396
1397 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1399 pub exclude: Inheritable<Vec<String>>,
1400
1401 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1403 pub include: Inheritable<Vec<String>>,
1404
1405 #[serde(default, skip_serializing_if = "Option::is_none")]
1407 pub license: Option<Inheritable<String>>,
1408
1409 #[serde(default, skip_serializing_if = "Option::is_none")]
1411 pub license_file: Option<Inheritable<PathBuf>>,
1412
1413 #[serde(default, skip_serializing_if = "Option::is_none")]
1415 pub repository: Option<Inheritable<String>>,
1416
1417 #[serde(default, skip_serializing_if = "Option::is_none")]
1419 pub default_run: Option<String>,
1420
1421 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1425 pub autobins: bool,
1426
1427 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1429 pub autolib: bool,
1430
1431 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1435 pub autoexamples: bool,
1436
1437 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1441 pub autotests: bool,
1442
1443 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1447 pub autobenches: bool,
1448
1449 #[serde(default, skip_serializing_if = "Inheritable::is_default")]
1451 pub publish: Inheritable<Publish>,
1452
1453 #[serde(default, skip_serializing_if = "Option::is_none")]
1455 pub resolver: Option<Resolver>,
1456
1457 #[serde(skip_serializing_if = "Option::is_none")]
1459 pub metadata: Option<Metadata>,
1460}
1461
1462#[allow(deprecated)]
1463impl<Metadata> Package<Metadata> {
1464 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
1466 Self {
1467 name: name.into(),
1468 version: Inheritable::Set(version.into()),
1469 edition: Inheritable::Set(Edition::E2021),
1470 rust_version: None,
1471 build: None,
1472 workspace: None,
1473 authors: Default::default(),
1474 links: None,
1475 description: None,
1476 homepage: None,
1477 documentation: None,
1478 readme: Inheritable::Set(OptionalFile::Flag(true)),
1479 keywords: Default::default(),
1480 categories: Default::default(),
1481 exclude: Default::default(),
1482 include: Default::default(),
1483 license: None,
1484 license_file: None,
1485 repository: None,
1486 default_run: None,
1487 autolib: true,
1488 autobins: true,
1489 autoexamples: true,
1490 autotests: true,
1491 autobenches: true,
1492 publish: Inheritable::Set(Publish::Flag(true)),
1493 resolver: None,
1494 metadata: None,
1495 }
1496 }
1497
1498 #[track_caller]
1512 #[inline]
1513 pub fn version(&self) -> &str {
1514 self.version.as_ref().unwrap()
1515 }
1516
1517 #[track_caller]
1521 #[inline]
1522 pub fn authors(&self) -> &[String] {
1523 self.authors.as_ref().unwrap()
1524 }
1525
1526 #[track_caller]
1530 #[inline]
1531 pub fn categories(&self) -> &[String] {
1532 self.categories.as_ref().unwrap()
1533 }
1534
1535 #[track_caller]
1539 #[inline]
1540 pub fn categories_mut(&mut self) -> &mut Vec<String> {
1541 self.categories.as_mut().unwrap()
1542 }
1543
1544 #[track_caller]
1548 #[inline]
1549 pub fn description(&self) -> Option<&str> {
1550 Some(self.description.as_ref()?.as_ref().unwrap())
1551 }
1552
1553 #[inline]
1554 pub fn set_description(&mut self, description: Option<String>) {
1555 self.description = description.map(Inheritable::Set);
1556 }
1557
1558 #[track_caller]
1562 #[inline]
1563 pub fn documentation(&self) -> Option<&str> {
1564 Some(self.documentation.as_ref()?.as_ref().unwrap())
1565 }
1566
1567 #[inline]
1568 pub fn set_documentation(&mut self, documentation: Option<String>) {
1569 self.documentation = documentation.map(Inheritable::Set);
1570 }
1571
1572 #[track_caller]
1576 #[inline]
1577 pub fn edition(&self) -> Edition {
1578 self.edition.unwrap()
1579 }
1580
1581 #[track_caller]
1585 #[inline]
1586 pub fn exclude(&self) -> &[String] {
1587 self.exclude.as_ref().unwrap()
1588 }
1589
1590 #[track_caller]
1594 #[inline]
1595 pub fn include(&self) -> &[String] {
1596 self.include.as_ref().unwrap()
1597 }
1598
1599 #[track_caller]
1603 #[inline]
1604 pub fn homepage(&self) -> Option<&str> {
1605 Some(self.homepage.as_ref()?.as_ref().unwrap())
1606 }
1607
1608 #[inline]
1609 pub fn set_homepage(&mut self, homepage: Option<String>) {
1610 self.homepage = homepage.map(Inheritable::Set);
1611 }
1612
1613 #[track_caller]
1617 #[inline]
1618 pub fn keywords(&self) -> &[String] {
1619 self.keywords.as_ref().unwrap()
1620 }
1621
1622 #[track_caller]
1626 #[inline]
1627 pub fn license(&self) -> Option<&str> {
1628 Some(self.license.as_ref()?.as_ref().unwrap())
1629 }
1630
1631 #[track_caller]
1635 #[inline]
1636 pub fn license_file(&self) -> Option<&Path> {
1637 Some(self.license_file.as_ref()?.as_ref().unwrap())
1638 }
1639
1640 #[track_caller]
1644 #[inline]
1645 pub fn publish(&self) -> &Publish {
1646 self.publish.as_ref().unwrap()
1647 }
1648
1649 #[track_caller]
1653 #[inline]
1654 pub fn readme(&self) -> &OptionalFile {
1655 self.readme.as_ref().unwrap()
1656 }
1657
1658 #[track_caller]
1662 #[inline]
1663 pub fn repository(&self) -> Option<&str> {
1664 Some(self.repository.as_ref()?.as_ref().unwrap())
1665 }
1666
1667 pub fn set_repository(&mut self, repository: Option<String>) {
1668 self.repository = repository.map(Inheritable::Set);
1669 }
1670
1671 #[track_caller]
1675 #[inline]
1676 pub fn rust_version(&self) -> Option<&str> {
1677 Some(self.rust_version.as_ref()?.as_ref().unwrap())
1678 }
1679
1680 pub fn set_rust_version(&mut self, rust_version: Option<String>) {
1681 self.rust_version = rust_version.map(Inheritable::Set);
1682 }
1683
1684 #[inline]
1688 pub fn links(&self) -> Option<&str> {
1689 self.links.as_deref()
1690 }
1691
1692 #[inline]
1696 pub fn name(&self) -> &str {
1697 &self.name
1698 }
1699
1700 fn needs_workspace_inheritance(&self) -> bool {
1704 !(self.authors.is_set() &&
1705 self.categories.is_set() &&
1706 self.edition.is_set() &&
1707 self.exclude.is_set() &&
1708 self.include.is_set() &&
1709 self.keywords.is_set() &&
1710 self.version.is_set() &&
1711 self.description.as_ref().map_or(true, Inheritable::is_set) &&
1712 self.documentation.as_ref().map_or(true, Inheritable::is_set) &&
1713 self.homepage.as_ref().map_or(true, Inheritable::is_set) &&
1714 self.license.as_ref().map_or(true, Inheritable::is_set) &&
1715 self.license_file.as_ref().map_or(true, Inheritable::is_set) &&
1716 self.repository.as_ref().map_or(true, Inheritable::is_set) &&
1717 self.rust_version.as_ref().map_or(true, Inheritable::is_set) &&
1718 self.publish.is_set() &&
1719 self.readme.is_set())
1720 }
1721}
1722
1723impl<Metadata: Default> Default for Package<Metadata> {
1724 fn default() -> Self { Self::new("", "") }
1725}
1726
1727#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
1729#[serde(untagged, expecting = "the value should be either a boolean or a file path")]
1730pub enum OptionalFile {
1731 Flag(bool),
1733 Path(PathBuf),
1735}
1736
1737impl Default for OptionalFile {
1738 #[inline]
1739 fn default() -> Self { Self::Flag(true) }
1740}
1741
1742impl OptionalFile {
1743 #[must_use]
1744 pub fn display(&self) -> &str {
1745 match self {
1746 Self::Path(p) => p.to_str().unwrap_or("<non-utf8>"),
1747 Self::Flag(true) => "<default>",
1748 Self::Flag(false) => "<disabled>",
1749 }
1750 }
1751
1752 #[inline]
1753 fn is_default(&self) -> bool {
1754 matches!(self, Self::Flag(flag) if *flag)
1755 }
1756
1757 #[inline]
1759 #[must_use]
1760 pub fn as_path(&self) -> Option<&Path> {
1761 match self {
1762 Self::Path(p) => Some(p),
1763 Self::Flag(_) => None,
1764 }
1765 }
1766
1767 #[inline]
1768 #[must_use]
1769 pub fn is_some(&self) -> bool {
1770 matches!(self, Self::Flag(true) | Self::Path(_))
1771 }
1772}
1773
1774#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
1776#[serde(untagged, expecting = "the value should be either a boolean, or an array of registry names")]
1777pub enum Publish {
1778 Flag(bool),
1779 Registry(Vec<String>),
1780}
1781
1782impl Publish {
1783 fn is_default(&self) -> bool {
1784 matches!(self, Self::Flag(flag) if *flag)
1785 }
1786}
1787
1788impl Default for Publish {
1789 #[inline]
1790 fn default() -> Self { Self::Flag(true) }
1791}
1792
1793impl PartialEq<Publish> for bool {
1794 #[inline]
1795 fn eq(&self, p: &Publish) -> bool {
1796 match p {
1797 Publish::Flag(flag) => *flag == *self,
1798 Publish::Registry(reg) => reg.is_empty() != *self,
1799 }
1800 }
1801}
1802
1803impl PartialEq<bool> for Publish {
1804 #[inline]
1805 fn eq(&self, b: &bool) -> bool {
1806 b.eq(self)
1807 }
1808}
1809
1810impl PartialEq<bool> for &Publish {
1811 #[inline]
1812 fn eq(&self, b: &bool) -> bool {
1813 b.eq(*self)
1814 }
1815}
1816
1817impl PartialEq<&Publish> for bool {
1818 #[inline]
1819 fn eq(&self, b: &&Publish) -> bool {
1820 (*self).eq(*b)
1821 }
1822}
1823
1824#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1828#[serde(rename_all = "kebab-case")]
1829pub struct Badge {
1830 pub repository: String,
1831 #[serde(default = "default_master")]
1832 pub branch: String,
1833 pub service: Option<String>,
1834 pub id: Option<String>,
1835 pub project_name: Option<String>,
1836}
1837
1838fn default_master() -> String {
1839 "master".to_string()
1840}
1841
1842fn ok_or_default<'de, T, D>(deserializer: D) -> Result<T, D::Error>
1843where
1844 T: Deserialize<'de> + Default,
1845 D: Deserializer<'de>,
1846{
1847 Ok(Deserialize::deserialize(deserializer).unwrap_or_default())
1848}
1849
1850fn default_version() -> Inheritable<String> {
1851 Inheritable::Set("0.0.0".into())
1852}
1853
1854#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
1856#[serde(rename_all = "kebab-case")]
1857pub struct Badges {
1858 #[serde(default, deserialize_with = "ok_or_default")]
1864 pub appveyor: Option<Badge>,
1865
1866 #[serde(default, deserialize_with = "ok_or_default")]
1868 pub circle_ci: Option<Badge>,
1869
1870 #[serde(default, deserialize_with = "ok_or_default")]
1872 pub gitlab: Option<Badge>,
1873
1874 #[serde(default, deserialize_with = "ok_or_default")]
1877 #[deprecated(note = "badges are deprecated, and travis is dead")]
1878 pub travis_ci: Option<Badge>,
1879
1880 #[serde(default, deserialize_with = "ok_or_default")]
1884 pub codecov: Option<Badge>,
1885
1886 #[serde(default, deserialize_with = "ok_or_default")]
1889 pub coveralls: Option<Badge>,
1890
1891 #[serde(default, deserialize_with = "ok_or_default")]
1893 pub is_it_maintained_issue_resolution: Option<Badge>,
1894
1895 #[serde(default, deserialize_with = "ok_or_default")]
1897 pub is_it_maintained_open_issues: Option<Badge>,
1898
1899 #[serde(default, deserialize_with = "ok_or_default")]
1908 pub maintenance: Maintenance,
1909}
1910
1911impl Badges {
1912 #[allow(deprecated)]
1913 fn should_skip_serializing(&self) -> bool {
1915 self.appveyor.is_none() &&
1916 self.circle_ci.is_none() &&
1917 self.gitlab.is_none() &&
1918 self.travis_ci.is_none() &&
1919 self.codecov.is_none() &&
1920 self.coveralls.is_none() &&
1921 self.is_it_maintained_issue_resolution.is_none() &&
1922 self.is_it_maintained_open_issues.is_none() &&
1923 matches!(self.maintenance.status, MaintenanceStatus::None)
1924 }
1925}
1926
1927#[derive(Debug, PartialEq, Eq, Copy, Clone, Default, Serialize, Deserialize)]
1934pub struct Maintenance {
1935 pub status: MaintenanceStatus,
1936}
1937
1938#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)]
1945#[serde(rename_all = "kebab-case")]
1946#[derive(Default)]
1947pub enum MaintenanceStatus {
1948 #[default]
1949 None,
1950 ActivelyDeveloped,
1951 PassivelyMaintained,
1952 AsIs,
1953 Experimental,
1954 LookingForMaintainer,
1955 Deprecated,
1956}
1957
1958#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
1960#[non_exhaustive]
1961#[serde(expecting = "if there's a newer edition, then this parser (cargo_toml crate) has to be updated")]
1962pub enum Edition {
1963 #[serde(rename = "2015")]
1965 #[default]
1966 E2015 = 2015,
1967 #[serde(rename = "2018")]
1969 E2018 = 2018,
1970 #[serde(rename = "2021")]
1972 E2021 = 2021,
1973 #[serde(rename = "2024")]
1975 E2024 = 2024,
1976}
1977
1978impl std::fmt::Display for Edition {
1979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1980 f.write_str(match self {
1981 Self::E2015 => "2015",
1982 Self::E2018 => "2018",
1983 Self::E2021 => "2021",
1984 Self::E2024 => "2024",
1985 })
1986 }
1987}
1988
1989impl Edition {
1990 #[must_use]
1992 pub fn min_rust_version_minor(self) -> u16 {
1993 match self {
1994 Self::E2015 => 1,
1995 Self::E2018 => 31,
1996 Self::E2021 => 56,
1997 Self::E2024 => 85,
1998 }
1999 }
2000}
2001
2002#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
2006#[serde(expecting = "if there's a newer resolver, then this parser (cargo_toml crate) has to be updated")]
2007pub enum Resolver {
2008 #[serde(rename = "1")]
2009 #[default]
2010 V1 = 1,
2012 #[serde(rename = "2")]
2014 V2 = 2,
2015 #[serde(rename = "3")]
2017 V3 = 3,
2018}
2019
2020impl Display for Resolver {
2021 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2022 f.write_str(match self {
2023 Self::V1 => "1",
2024 Self::V2 => "2",
2025 Self::V3 => "3",
2026 })
2027 }
2028}
2029
2030#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2032#[serde(from = "LintSerdeParser", into = "LintSerdeParser")]
2033pub struct Lint {
2034 pub level: LintLevel,
2036
2037 pub priority: i8,
2039
2040 pub config: BTreeMap<String, toml::Value>,
2042}
2043
2044#[derive(Serialize, Deserialize)]
2046#[serde(untagged, expecting = "lints' values should be a string or { level = \"…\", priority = 1 }")]
2047enum LintSerdeParser {
2048 Simple(LintLevel),
2049 Detailed {
2050 level: LintLevel,
2051 #[serde(default)]
2053 priority: i8,
2054
2055 #[serde(default, flatten)]
2057 config: BTreeMap<String, toml::Value>,
2058 },
2059}
2060
2061impl From<LintSerdeParser> for Lint {
2062 fn from(parsed: LintSerdeParser) -> Self {
2063 match parsed {
2064 LintSerdeParser::Simple(level) => Self { level, priority: 0, config: Default::default() },
2065 LintSerdeParser::Detailed { level, priority, config } => Self { level, priority, config },
2066 }
2067 }
2068}
2069
2070impl From<Lint> for LintSerdeParser {
2071 fn from(orig: Lint) -> Self {
2072 if orig.priority == 0 && orig.config.is_empty() {
2073 Self::Simple(orig.level)
2074 } else {
2075 Self::Detailed {
2076 level: orig.level,
2077 priority: orig.priority,
2078 config: orig.config,
2079 }
2080 }
2081 }
2082}
2083
2084#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)]
2086#[serde(rename_all = "kebab-case")]
2087pub enum LintLevel {
2088 Allow,
2089 Warn,
2090 ForceWarn,
2091 Deny,
2092 Forbid,
2093}
2094
2095#[derive(Deserialize)]
2096#[non_exhaustive]
2097struct Rfc3416FeatureDetail {
2098 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2099 pub enables: Vec<String>,
2100
2101 #[allow(unused)]
2102 #[serde(default = "default_true", skip_serializing_if = "is_true")]
2104 pub public: bool,
2105
2106 #[allow(unused)]
2107 #[serde(default, skip_serializing_if = "Option::is_none")]
2109 pub doc: Option<String>,
2110}
2111
2112#[derive(Deserialize)]
2113#[serde(untagged)]
2114enum Rfc3416Feature {
2115 Simple(Vec<String>),
2116 Detailed(Rfc3416FeatureDetail),
2117}
2118
2119fn feature_set<'de, D>(deserializer: D) -> Result<FeatureSet, D::Error> where D: Deserializer<'de> {
2120 let detailed = BTreeMap::<String, Rfc3416Feature>::deserialize(deserializer)?;
2121 Ok(detailed.into_iter().map(|(k, v)| (k, match v {
2122 Rfc3416Feature::Simple(f) => f,
2123 Rfc3416Feature::Detailed(d) => d.enables,
2124 })).collect())
2125}