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>;
32pub type TargetDepsSet = BTreeMap<String, Target>;
34pub type FeatureSet = BTreeMap<String, Vec<String>>;
40pub type PatchSet = BTreeMap<String, DepsSet>;
42pub type LintSet = BTreeMap<String, Lint>;
44pub type LintGroups = BTreeMap<String, LintSet>;
46
47mod afs;
48mod error;
49mod inheritable;
50pub use crate::afs::*;
51pub use crate::error::Error;
52pub use crate::inheritable::Inheritable;
53
54#[cfg(feature = "features")]
55#[cfg_attr(docsrs, doc(cfg(feature = "features")))]
56pub mod features;
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63#[serde(rename_all = "kebab-case")]
64pub struct Manifest<Metadata = Value> {
65 pub package: Option<Package<Metadata>>,
67
68 pub workspace: Option<Workspace<Metadata>>,
70
71 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
73 pub dependencies: DepsSet,
74
75 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
77 pub dev_dependencies: DepsSet,
78
79 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
81 pub build_dependencies: DepsSet,
82
83 #[serde(default, skip_serializing_if = "TargetDepsSet::is_empty")]
85 pub target: TargetDepsSet,
86
87 #[serde(default, skip_serializing_if = "FeatureSet::is_empty", deserialize_with = "feature_set")]
96 pub features: FeatureSet,
97
98 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
100 #[deprecated(note = "Cargo recommends patch instead")]
101 pub replace: DepsSet,
102
103 #[serde(default, skip_serializing_if = "PatchSet::is_empty")]
105 pub patch: PatchSet,
106
107 pub lib: Option<Product>,
110
111 #[serde(default, skip_serializing_if = "Profiles::should_skip_serializing")]
113 pub profile: Profiles,
114
115 #[serde(default, skip_serializing_if = "Badges::should_skip_serializing")]
117 pub badges: Badges,
118
119 #[serde(default, skip_serializing_if = "Vec::is_empty")]
122 pub bin: Vec<Product>,
123
124 #[serde(default, skip_serializing_if = "Vec::is_empty")]
126 pub bench: Vec<Product>,
127
128 #[serde(default, skip_serializing_if = "Vec::is_empty")]
130 pub test: Vec<Product>,
131
132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
134 pub example: Vec<Product>,
135
136 #[serde(default, skip_serializing_if = "Inheritable::<LintGroups>::is_empty")]
138 pub lints: Inheritable<LintGroups>,
139}
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
143#[serde(rename_all = "kebab-case")]
144pub struct Workspace<Metadata = Value> {
145 #[serde(default)]
147 pub members: Vec<String>,
148
149 #[serde(default, skip_serializing_if = "Vec::is_empty")]
153 pub default_members: Vec<String>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub package: Option<PackageTemplate>,
158
159 #[serde(default, skip_serializing_if = "Vec::is_empty")]
161 pub exclude: Vec<String>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub metadata: Option<Metadata>,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub resolver: Option<Resolver>,
170
171 #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
173 pub dependencies: DepsSet,
174
175 #[serde(default, skip_serializing_if = "LintGroups::is_empty")]
177 pub lints: LintGroups,
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
182#[serde(rename_all = "kebab-case")]
183#[non_exhaustive]
184pub struct PackageTemplate {
185 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub authors: Option<Vec<String>>,
188
189 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub categories: Option<Vec<String>>,
192
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub description: Option<String>,
196
197 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub documentation: Option<String>,
200
201 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub edition: Option<Edition>,
204
205 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub exclude: Option<Vec<String>>,
208
209 #[serde(default, skip_serializing_if = "Option::is_none")]
211 pub homepage: Option<String>,
212
213 #[serde(default, skip_serializing_if = "Option::is_none")]
215 pub include: Option<Vec<String>>,
216
217 #[serde(default, skip_serializing_if = "Option::is_none")]
219 pub keywords: Option<Vec<String>>,
220
221 #[serde(default, skip_serializing_if = "Option::is_none")]
223 pub license: Option<String>,
224
225 #[serde(default, skip_serializing_if = "Option::is_none")]
227 pub license_file: Option<PathBuf>,
228
229 #[serde(default, skip_serializing_if = "Publish::is_default")]
231 pub publish: Publish,
232
233 #[serde(default, skip_serializing_if = "OptionalFile::is_default")]
235 pub readme: OptionalFile,
236
237 #[serde(default, skip_serializing_if = "Option::is_none")]
239 pub repository: Option<String>,
240
241 #[serde(default, skip_serializing_if = "Option::is_none")]
243 pub rust_version: Option<String>,
244
245 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub version: Option<String>,
248}
249
250fn default_true() -> bool {
251 true
252}
253
254fn is_true(val: &bool) -> bool {
255 *val
256}
257
258fn is_false(val: &bool) -> bool {
259 !*val
260}
261
262impl Manifest<Value> {
263 #[inline]
268 pub fn from_path(cargo_toml_path: impl AsRef<Path>) -> Result<Self, Error> {
269 Self::from_path_with_metadata(cargo_toml_path)
270 }
271
272 #[inline(always)]
278 pub fn from_slice(cargo_toml_content: &[u8]) -> Result<Self, Error> {
279 Self::from_slice_with_metadata(cargo_toml_content)
280 }
281
282 #[inline(always)]
290 #[allow(clippy::should_implement_trait)]
291 pub fn from_str(cargo_toml_content: &str) -> Result<Self, Error> {
292 Self::from_slice_with_metadata_str(cargo_toml_content)
293 }
294}
295
296impl<Metadata: for<'a> Deserialize<'a>> Manifest<Metadata> {
297 #[inline]
303 pub fn from_slice_with_metadata(cargo_toml_content: &[u8]) -> Result<Self, Error> {
304 let cargo_toml_content = std::str::from_utf8(cargo_toml_content).map_err(|_| Error::Other("utf8"))?;
305 Self::from_slice_with_metadata_str(cargo_toml_content)
306 }
307
308 #[inline(never)]
309 fn from_slice_with_metadata_str(cargo_toml_content: &str) -> Result<Self, Error> {
310 let mut manifest: Self = toml::from_str(cargo_toml_content)?;
311
312 if let Some(package) = &mut manifest.package {
313 if package.version.get().is_ok_and(|v| v == "0.0.0") && package.publish.get().is_ok_and(|p| p.is_default()) {
317 package.publish = Inheritable::Set(Publish::Flag(false));
318 }
319 }
320 Ok(manifest)
321 }
322
323 pub fn from_path_with_metadata<P: AsRef<Path>>(cargo_toml_path: P) -> Result<Self, Error> {
327 let cargo_toml_path = cargo_toml_path.as_ref();
328 let cargo_toml_content = fs::read_to_string(cargo_toml_path)?;
329 let mut manifest = Self::from_slice_with_metadata_str(&cargo_toml_content)?;
330 manifest.complete_from_path(cargo_toml_path)?;
331 Ok(manifest)
332 }
333}
334
335impl<Metadata> Manifest<Metadata> {
336 pub fn complete_from_path(&mut self, path: &Path) -> Result<(), Error> {
344 let manifest_dir = path.parent().ok_or(Error::Other("bad path"))?;
345 self.complete_from_abstract_filesystem::<Value, _>(Filesystem::new(manifest_dir), None)
346 }
347
348 pub fn complete_from_path_and_workspace<PackageMetadataTypeDoesNotMatterHere>(&mut self, package_manifest_path: &Path, workspace_manifest_and_path: Option<(&Manifest<PackageMetadataTypeDoesNotMatterHere>, &Path)>) -> Result<(), Error> {
354 let manifest_dir = package_manifest_path.parent().ok_or(Error::Other("bad path"))?;
355 self.complete_from_abstract_filesystem(Filesystem::new(manifest_dir), workspace_manifest_and_path)
356 }
357
358 pub fn complete_from_abstract_filesystem<PackageMetadataTypeDoesNotMatterHere, Fs: AbstractFilesystem>(
369 &mut self, fs: Fs, workspace_manifest_and_path: Option<(&Manifest<PackageMetadataTypeDoesNotMatterHere>, &Path)>,
370 ) -> Result<(), Error> {
371 let tmp;
372 let err_path;
373 let res = if let Some((ws, ws_path)) = workspace_manifest_and_path {
374 err_path = Some(ws_path);
375 self._inherit_workspace(ws.workspace.as_ref(), ws_path)
376 } else if let Some(ws) = self.workspace.take() {
377 err_path = Some(Path::new(""));
378 let res = self._inherit_workspace(Some(&ws), Path::new(""));
380 self.workspace = Some(ws);
381 res
382 } else if self.needs_workspace_inheritance() {
383 let ws_path_hint = self.package.as_ref().and_then(|p| p.workspace.as_deref());
384 match fs.parse_root_workspace(ws_path_hint) {
385 Ok((ws_manifest, base_path)) => {
386 tmp = base_path;
387 err_path = Some(&tmp);
388 self._inherit_workspace(ws_manifest.workspace.as_ref(), &tmp)
389 }
390 Err(e) => {
391 err_path = if let Some(p) = ws_path_hint { tmp = p.to_owned(); Some(&tmp) } else { None };
392 Err(e)
393 },
394 }
395 } else {
396 err_path = None;
397 Ok(())
398 };
399
400 match res.and_then(|()| self.complete_from_abstract_filesystem_inner(&fs)) {
401 Ok(()) => Ok(()),
402 Err(e @ Error::Workspace(_)) => return Err(e),
403 Err(e) => return Err(Error::Workspace(Box::new((e.into(), err_path.map(PathBuf::from))))),
404 }
405 }
406
407 pub fn needs_workspace_inheritance(&self) -> bool {
411 self.package.as_ref().is_some_and(Package::needs_workspace_inheritance) ||
412 !self.lints.is_set() ||
413 self.dependencies.values()
414 .chain(self.build_dependencies.values())
415 .chain(self.dev_dependencies.values())
416 .any(|dep| {
417 matches!(dep, Dependency::Inherited(_))
418 })
419 }
420
421 fn _inherit_workspace<Ignored>(&mut self, workspace: Option<&Workspace<Ignored>>, workspace_base_path: &Path) -> Result<(), Error> {
422 let workspace_base_path = if workspace_base_path.file_name() == Some("Cargo.toml".as_ref()) {
423 workspace_base_path.parent().ok_or(Error::Other("bad path"))?
424 } else {
425 workspace_base_path
426 };
427
428 inherit_dependencies(&mut self.dependencies, workspace, workspace_base_path)?;
429 inherit_dependencies(&mut self.build_dependencies, workspace, workspace_base_path)?;
430 inherit_dependencies(&mut self.dev_dependencies, workspace, workspace_base_path)?;
431
432 for target in self.target.values_mut() {
433 inherit_dependencies(&mut target.dependencies, workspace, workspace_base_path)?;
434 inherit_dependencies(&mut target.build_dependencies, workspace, workspace_base_path)?;
435 inherit_dependencies(&mut target.dev_dependencies, workspace, workspace_base_path)?;
436 }
437
438 if let Some(ws) = workspace {
439 self.lints.inherit(&ws.lints);
440 }
441
442 let package = match &mut self.package {
443 Some(p) => p,
444 None => return Ok(()),
445 };
446 if let Some(ws) = workspace.and_then(|w| w.package.as_ref()) {
447 Self::inherit_package_properties(package, ws, workspace_base_path)?;
448 }
449
450 if package.needs_workspace_inheritance() {
451 return Err(Error::WorkspaceIntegrity(format!("not all fields of `{}` have been present in workspace.package", package.name())));
452 }
453 Ok(())
454 }
455
456 fn inherit_package_properties(package: &mut Package<Metadata>, ws: &PackageTemplate, workspace_base_path: &Path) -> Result<(), Error> {
457 fn maybe_inherit<T: Clone>(to: Option<&mut Inheritable<T>>, from: Option<&T>) {
458 if let Some(from) = from {
459 if let Some(to) = to {
460 to.inherit(from);
461 }
462 }
463 }
464 fn inherit<T: Clone>(to: &mut Inheritable<T>, from: Option<&T>) {
465 if let Some(from) = from {
466 to.inherit(from);
467 }
468 }
469 inherit(&mut package.authors, ws.authors.as_ref());
470 inherit(&mut package.categories, ws.categories.as_ref());
471 inherit(&mut package.edition, ws.edition.as_ref());
472 inherit(&mut package.exclude, ws.exclude.as_ref());
473 inherit(&mut package.include, ws.include.as_ref());
474 inherit(&mut package.keywords, ws.keywords.as_ref());
475 inherit(&mut package.version, ws.version.as_ref());
476 maybe_inherit(package.description.as_mut(), ws.description.as_ref());
477 maybe_inherit(package.documentation.as_mut(), ws.documentation.as_ref());
478 maybe_inherit(package.homepage.as_mut(), ws.homepage.as_ref());
479 maybe_inherit(package.license.as_mut(), ws.license.as_ref());
480 maybe_inherit(package.repository.as_mut(), ws.repository.as_ref());
481 maybe_inherit(package.rust_version.as_mut(), ws.rust_version.as_ref());
482 package.publish.inherit(&ws.publish);
483 match (&mut package.readme, &ws.readme) {
484 (r @ Inheritable::Inherited { .. }, flag @ OptionalFile::Flag(_)) => {
485 r.set(flag.clone());
486 },
487 (r @ Inheritable::Inherited { .. }, OptionalFile::Path(path)) => {
488 r.set(OptionalFile::Path(workspace_base_path.join(path)));
489 },
490 _ => {},
491 }
492 if let Some((f, ws)) = package.license_file.as_mut().zip(ws.license_file.as_ref()) {
493 f.set(workspace_base_path.join(ws))
494 }
495 Ok(())
496 }
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 pub doc: Option<Profile>,
757
758 #[serde(flatten)]
760 pub custom: BTreeMap<String, Profile>,
761}
762
763impl Profiles {
764 fn should_skip_serializing(&self) -> bool {
766 self.release.is_none()
767 && self.dev.is_none()
768 && self.test.is_none()
769 && self.bench.is_none()
770 && self.doc.is_none()
771 && self.custom.is_empty()
772 }
773}
774
775#[derive(Debug, 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, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
822#[serde(try_from = "toml::Value")]
823pub enum StripSetting {
824 None,
826 Debuginfo,
827 Symbols,
829}
830
831impl Serialize for StripSetting {
832 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
833 match self {
834 Self::None => serializer.serialize_bool(false),
835 Self::Debuginfo => serializer.serialize_str("debuginfo"),
836 Self::Symbols => serializer.serialize_bool(true),
837 }
838 }
839}
840
841impl TryFrom<Value> for StripSetting {
842 type Error = Error;
843
844 fn try_from(v: Value) -> Result<Self, Error> {
845 Ok(match v {
846 Value::Boolean(b) => if b { Self::Symbols } else { Self::None },
847 Value::String(s) => match s.as_str() {
848 "none" => Self::None,
849 "debuginfo" => Self::Debuginfo,
850 "symbols" => Self::Symbols,
851 _ => return Err(Error::Other("strip setting has unknown string value")),
852 },
853 _ => return Err(Error::Other("wrong data type for strip setting")),
854 })
855 }
856}
857
858#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
860#[serde(try_from = "toml::Value")]
861pub enum LtoSetting {
862 None,
864 ThinLocal,
866 Thin,
867 Fat,
869}
870
871impl Serialize for LtoSetting {
872 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
873 match self {
874 Self::None => serializer.serialize_str("off"),
875 Self::ThinLocal => serializer.serialize_bool(false),
876 Self::Thin => serializer.serialize_str("thin"),
877 Self::Fat => serializer.serialize_bool(true),
878 }
879 }
880}
881
882impl TryFrom<Value> for LtoSetting {
883 type Error = Error;
884
885 fn try_from(v: Value) -> Result<Self, Error> {
886 Ok(match v {
887 Value::Boolean(b) => if b { Self::Fat } else { Self::ThinLocal },
888 Value::String(s) => match s.as_str() {
889 "off" | "n" | "no" => Self::None,
890 "thin" => Self::Thin,
891 "fat" | "on" | "y" | "yes" | "true" => Self::Fat,
892 "false" => Self::ThinLocal,
893 _ => return Err(Error::Other("lto setting has unknown string value")),
894 },
895 _ => return Err(Error::Other("wrong data type for lto setting")),
896 })
897 }
898}
899
900#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
902#[serde(rename_all = "kebab-case")]
903pub struct Profile {
904 #[serde(default, skip_serializing_if = "Option::is_none")]
906 pub opt_level: Option<Value>,
907
908 #[serde(default, skip_serializing_if = "Option::is_none")]
910 pub debug: Option<DebugSetting>,
911
912 #[serde(default, skip_serializing_if = "Option::is_none")]
914 pub split_debuginfo: Option<String>,
915
916 #[serde(default, skip_serializing_if = "Option::is_none")]
918 pub rpath: Option<bool>,
919
920 #[serde(default, skip_serializing_if = "Option::is_none")]
922 pub lto: Option<LtoSetting>,
923
924 #[serde(default, skip_serializing_if = "Option::is_none")]
926 pub debug_assertions: Option<bool>,
927
928 #[serde(default, skip_serializing_if = "Option::is_none")]
930 pub codegen_units: Option<u16>,
931
932 #[serde(default, skip_serializing_if = "Option::is_none")]
934 pub panic: Option<String>,
935
936 #[serde(default, skip_serializing_if = "Option::is_none")]
938 pub incremental: Option<bool>,
939
940 #[serde(default, skip_serializing_if = "Option::is_none")]
942 pub overflow_checks: Option<bool>,
943
944 #[serde(default, skip_serializing_if = "Option::is_none")]
946 pub strip: Option<StripSetting>,
947
948 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
950 pub package: BTreeMap<String, Value>,
951
952 #[serde(default, skip_serializing_if = "Option::is_none")]
954 pub build_override: Option<Value>,
955
956 #[serde(default, skip_serializing_if = "Option::is_none")]
958 pub inherits: Option<String>,
959}
960
961#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
962#[serde(rename_all = "kebab-case")]
963pub struct Product {
967 pub path: Option<String>,
969
970 pub name: Option<String>,
975
976 #[serde(default = "default_true", skip_serializing_if = "is_true")]
978 pub test: bool,
979
980 #[serde(default = "default_true", skip_serializing_if = "is_true")]
984 pub doctest: bool,
985
986 #[serde(default = "default_true", skip_serializing_if = "is_true")]
988 pub bench: bool,
989
990 #[serde(default = "default_true", skip_serializing_if = "is_true")]
992 pub doc: bool,
993
994 #[serde(default, skip_serializing_if = "is_false")]
997 pub plugin: bool,
998
999 #[serde(default, alias = "proc_macro", alias = "proc-macro", skip_serializing_if = "is_false")]
1002 pub proc_macro: bool,
1003
1004 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1008 pub harness: bool,
1009
1010 #[serde(default, skip_serializing_if = "Option::is_none")]
1017 pub edition: Option<Edition>,
1018
1019 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1021 pub crate_type: Vec<String>,
1022
1023 #[serde(default)]
1028 pub required_features: Vec<String>,
1029}
1030
1031impl Default for Product {
1032 fn default() -> Self {
1033 Self {
1034 path: None,
1035 name: None,
1036 test: true,
1037 doctest: true,
1038 bench: true,
1039 doc: true,
1040 harness: true,
1041 plugin: false,
1042 proc_macro: false,
1043 required_features: Vec::new(),
1044 crate_type: Vec::new(),
1045 edition: None,
1046 }
1047 }
1048}
1049
1050#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
1052#[serde(rename_all = "kebab-case")]
1053pub struct Target {
1054 #[serde(default)]
1056 pub dependencies: DepsSet,
1057 #[serde(default)]
1059 pub dev_dependencies: DepsSet,
1060 #[serde(default)]
1062 pub build_dependencies: DepsSet,
1063}
1064
1065#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1069#[serde(untagged)]
1070pub enum Dependency {
1071 Simple(String),
1073 Inherited(InheritedDependencyDetail), Detailed(Box<DependencyDetail>),
1077}
1078
1079impl Dependency {
1080 #[inline]
1084 #[must_use]
1085 pub fn detail(&self) -> Option<&DependencyDetail> {
1086 match self {
1087 Dependency::Detailed(d) => Some(d),
1088 Dependency::Simple(_) | Dependency::Inherited(_) => None,
1089 }
1090 }
1091
1092 #[inline]
1094 #[track_caller]
1095 pub fn detail_mut(&mut self) -> &mut DependencyDetail {
1096 self.try_detail_mut().expect("dependency not available due to workspace inheritance")
1097 }
1098
1099 pub fn try_detail_mut(&mut self) -> Result<&mut DependencyDetail, Error> {
1103 match self {
1104 Dependency::Detailed(d) => Ok(d),
1105 Dependency::Simple(ver) => {
1106 *self = Dependency::Detailed(Box::new(DependencyDetail {
1107 version: Some(ver.clone()),
1108 ..Default::default()
1109 }));
1110 match self {
1111 Dependency::Detailed(d) => Ok(d),
1112 _ => unreachable!(),
1113 }
1114 },
1115 Dependency::Inherited(_) => Err(Error::InheritedUnknownValue),
1116 }
1117 }
1118
1119 #[inline]
1123 #[track_caller]
1124 #[must_use]
1125 pub fn req(&self) -> &str {
1126 self.try_req().unwrap()
1127 }
1128
1129 #[inline]
1133 #[track_caller]
1134 pub fn try_req(&self) -> Result<&str, Error> {
1135 match self {
1136 Dependency::Simple(v) => Ok(v),
1137 Dependency::Detailed(d) => Ok(d.version.as_deref().unwrap_or("*")),
1138 Dependency::Inherited(_) => Err(Error::InheritedUnknownValue),
1139 }
1140 }
1141
1142 #[inline]
1144 #[must_use]
1145 pub fn req_features(&self) -> &[String] {
1146 match self {
1147 Dependency::Simple(_) => &[],
1148 Dependency::Detailed(d) => &d.features,
1149 Dependency::Inherited(d) => &d.features,
1150 }
1151 }
1152
1153 #[inline]
1156 #[must_use]
1157 pub fn optional(&self) -> bool {
1158 match self {
1159 Dependency::Simple(_) => false,
1160 Dependency::Detailed(d) => d.optional,
1161 Dependency::Inherited(d) => d.optional,
1162 }
1163 }
1164
1165 #[inline]
1168 #[must_use]
1169 pub fn package(&self) -> Option<&str> {
1170 match self {
1171 Dependency::Detailed(d) => d.package.as_deref(),
1172 Dependency::Simple(_) | Dependency::Inherited(_) => None,
1173 }
1174 }
1175
1176 #[inline]
1178 #[must_use]
1179 pub fn git(&self) -> Option<&str> {
1180 self.detail()?.git.as_deref()
1181 }
1182
1183 #[inline]
1185 #[must_use]
1186 pub fn git_rev(&self) -> Option<&str> {
1187 self.detail()?.rev.as_deref()
1188 }
1189
1190 #[track_caller]
1193 #[must_use]
1194 pub fn is_crates_io(&self) -> bool {
1195 match self {
1196 Dependency::Simple(_) => true,
1197 Dependency::Detailed(d) => {
1198 d.path.is_none() &&
1200 d.registry.is_none() &&
1201 d.registry_index.is_none() &&
1202 d.git.is_none() &&
1203 d.tag.is_none() &&
1204 d.branch.is_none() &&
1205 d.rev.is_none()
1206 },
1207 Dependency::Inherited(_) => panic!("data not available with workspace inheritance"),
1208 }
1209 }
1210}
1211
1212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1214#[serde(rename_all = "kebab-case")]
1215pub struct DependencyDetail {
1216 #[serde(skip_serializing_if = "Option::is_none")]
1218 pub version: Option<String>,
1219
1220 #[serde(skip_serializing_if = "Option::is_none")]
1224 pub package: Option<String>,
1225
1226 #[serde(skip_serializing_if = "Option::is_none")]
1230 pub registry: Option<String>,
1231
1232 #[serde(skip_serializing_if = "Option::is_none")]
1234 pub registry_index: Option<String>,
1235
1236 #[serde(skip_serializing_if = "Option::is_none")]
1241 pub path: Option<String>,
1242
1243 #[serde(skip)]
1248 pub inherited: bool,
1249
1250 #[serde(skip_serializing_if = "Option::is_none")]
1252 pub git: Option<String>,
1253 #[serde(skip_serializing_if = "Option::is_none")]
1255 pub branch: Option<String>,
1256 #[serde(skip_serializing_if = "Option::is_none")]
1258 pub tag: Option<String>,
1259 #[serde(skip_serializing_if = "Option::is_none")]
1261 pub rev: Option<String>,
1262
1263 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1267 pub features: Vec<String>,
1268
1269 #[serde(default, skip_serializing_if = "is_false")]
1274 pub optional: bool,
1275
1276 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1278 pub default_features: bool,
1279
1280 #[serde(flatten)]
1282 pub unstable: BTreeMap<String, Value>,
1283}
1284
1285impl Default for DependencyDetail {
1286 fn default() -> Self {
1287 DependencyDetail {
1288 version: None,
1289 registry: None,
1290 registry_index: None,
1291 path: None,
1292 inherited: false,
1293 git: None,
1294 branch: None,
1295 tag: None,
1296 rev: None,
1297 features: Vec::new(),
1298 optional: false,
1299 default_features: true, package: None,
1301 unstable: BTreeMap::new(),
1302 }
1303 }
1304}
1305
1306#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
1309#[serde(rename_all = "kebab-case")]
1310pub struct InheritedDependencyDetail {
1311 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1312 pub features: Vec<String>,
1313
1314 #[serde(default, skip_serializing_if = "is_false")]
1315 pub optional: bool,
1316
1317 #[serde(skip_serializing_if = "is_false")]
1318 pub workspace: bool,
1319}
1320
1321#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1328#[serde(rename_all = "kebab-case")]
1329#[non_exhaustive]
1330pub struct Package<Metadata = Value> {
1331 pub name: String,
1333
1334 #[serde(default = "default_version")]
1341 pub version: Inheritable<String>,
1342
1343 #[serde(default)]
1345 pub edition: Inheritable<Edition>,
1346
1347 #[serde(default, skip_serializing_if = "Option::is_none")]
1349 pub rust_version: Option<Inheritable<String>>,
1350
1351 #[serde(default, skip_serializing_if = "Option::is_none")]
1353 pub build: Option<OptionalFile>,
1354
1355 #[serde(default, skip_serializing_if = "Option::is_none")]
1357 pub workspace: Option<PathBuf>,
1358
1359 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1363 pub authors: Inheritable<Vec<String>>,
1364
1365 #[serde(default, skip_serializing_if = "Option::is_none")]
1367 pub links: Option<String>,
1368
1369 #[serde(default, skip_serializing_if = "Option::is_none")]
1372 pub description: Option<Inheritable<String>>,
1373
1374 #[serde(default, skip_serializing_if = "Option::is_none")]
1376 pub homepage: Option<Inheritable<String>>,
1377
1378 #[serde(default, skip_serializing_if = "Option::is_none")]
1380 pub documentation: Option<Inheritable<String>>,
1381
1382 #[serde(default, skip_serializing_if = "Inheritable::is_default")]
1385 pub readme: Inheritable<OptionalFile>,
1386
1387 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1389 pub keywords: Inheritable<Vec<String>>,
1390
1391 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1394 pub categories: Inheritable<Vec<String>>,
1395
1396 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1398 pub exclude: Inheritable<Vec<String>>,
1399
1400 #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1402 pub include: Inheritable<Vec<String>>,
1403
1404 #[serde(default, skip_serializing_if = "Option::is_none")]
1406 pub license: Option<Inheritable<String>>,
1407
1408 #[serde(default, skip_serializing_if = "Option::is_none")]
1410 pub license_file: Option<Inheritable<PathBuf>>,
1411
1412 #[serde(default, skip_serializing_if = "Option::is_none")]
1414 pub repository: Option<Inheritable<String>>,
1415
1416 #[serde(default, skip_serializing_if = "Option::is_none")]
1418 pub default_run: Option<String>,
1419
1420 #[serde(default = "default_true", skip_serializing_if = "is_true")]
1424 pub autobins: bool,
1425
1426 #[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 pub fn display(&self) -> &str {
1744 match self {
1745 Self::Path(p) => p.to_str().unwrap_or("<non-utf8>"),
1746 Self::Flag(true) => "<default>",
1747 Self::Flag(false) => "<disabled>",
1748 }
1749 }
1750
1751 #[inline]
1752 fn is_default(&self) -> bool {
1753 matches!(self, Self::Flag(flag) if *flag)
1754 }
1755
1756 #[inline]
1758 #[must_use]
1759 pub fn as_path(&self) -> Option<&Path> {
1760 match self {
1761 Self::Path(p) => Some(p),
1762 Self::Flag(_) => None,
1763 }
1764 }
1765
1766 #[inline]
1767 #[must_use]
1768 pub fn is_some(&self) -> bool {
1769 matches!(self, Self::Flag(true) | Self::Path(_))
1770 }
1771}
1772
1773#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
1775#[serde(untagged, expecting = "the value should be either a boolean, or an array of registry names")]
1776pub enum Publish {
1777 Flag(bool),
1778 Registry(Vec<String>),
1779}
1780
1781impl Publish {
1782 fn is_default(&self) -> bool {
1783 matches!(self, Publish::Flag(flag) if *flag)
1784 }
1785}
1786
1787impl Default for Publish {
1788 #[inline]
1789 fn default() -> Self { Publish::Flag(true) }
1790}
1791
1792impl PartialEq<Publish> for bool {
1793 #[inline]
1794 fn eq(&self, p: &Publish) -> bool {
1795 match p {
1796 Publish::Flag(flag) => *flag == *self,
1797 Publish::Registry(reg) => reg.is_empty() != *self,
1798 }
1799 }
1800}
1801
1802impl PartialEq<bool> for Publish {
1803 #[inline]
1804 fn eq(&self, b: &bool) -> bool {
1805 b.eq(self)
1806 }
1807}
1808
1809impl PartialEq<bool> for &Publish {
1810 #[inline]
1811 fn eq(&self, b: &bool) -> bool {
1812 b.eq(*self)
1813 }
1814}
1815
1816impl PartialEq<&Publish> for bool {
1817 #[inline]
1818 fn eq(&self, b: &&Publish) -> bool {
1819 (*self).eq(*b)
1820 }
1821}
1822
1823#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1827#[serde(rename_all = "kebab-case")]
1828pub struct Badge {
1829 pub repository: String,
1830 #[serde(default = "default_master")]
1831 pub branch: String,
1832 pub service: Option<String>,
1833 pub id: Option<String>,
1834 pub project_name: Option<String>,
1835}
1836
1837fn default_master() -> String {
1838 "master".to_string()
1839}
1840
1841fn ok_or_default<'de, T, D>(deserializer: D) -> Result<T, D::Error>
1842where
1843 T: Deserialize<'de> + Default,
1844 D: Deserializer<'de>,
1845{
1846 Ok(Deserialize::deserialize(deserializer).unwrap_or_default())
1847}
1848
1849fn default_version() -> Inheritable<String> {
1850 Inheritable::Set("0.0.0".into())
1851}
1852
1853#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
1855#[serde(rename_all = "kebab-case")]
1856pub struct Badges {
1857 #[serde(default, deserialize_with = "ok_or_default")]
1863 pub appveyor: Option<Badge>,
1864
1865 #[serde(default, deserialize_with = "ok_or_default")]
1867 pub circle_ci: Option<Badge>,
1868
1869 #[serde(default, deserialize_with = "ok_or_default")]
1871 pub gitlab: Option<Badge>,
1872
1873 #[serde(default, deserialize_with = "ok_or_default")]
1876 #[deprecated(note = "badges are deprecated, and travis is dead")]
1877 pub travis_ci: Option<Badge>,
1878
1879 #[serde(default, deserialize_with = "ok_or_default")]
1883 pub codecov: Option<Badge>,
1884
1885 #[serde(default, deserialize_with = "ok_or_default")]
1888 pub coveralls: Option<Badge>,
1889
1890 #[serde(default, deserialize_with = "ok_or_default")]
1892 pub is_it_maintained_issue_resolution: Option<Badge>,
1893
1894 #[serde(default, deserialize_with = "ok_or_default")]
1896 pub is_it_maintained_open_issues: Option<Badge>,
1897
1898 #[serde(default, deserialize_with = "ok_or_default")]
1907 pub maintenance: Maintenance,
1908}
1909
1910impl Badges {
1911 #[allow(deprecated)]
1912 fn should_skip_serializing(&self) -> bool {
1914 self.appveyor.is_none() &&
1915 self.circle_ci.is_none() &&
1916 self.gitlab.is_none() &&
1917 self.travis_ci.is_none() &&
1918 self.codecov.is_none() &&
1919 self.coveralls.is_none() &&
1920 self.is_it_maintained_issue_resolution.is_none() &&
1921 self.is_it_maintained_open_issues.is_none() &&
1922 matches!(self.maintenance.status, MaintenanceStatus::None)
1923 }
1924}
1925
1926#[derive(Debug, PartialEq, Eq, Copy, Clone, Default, Serialize, Deserialize)]
1933pub struct Maintenance {
1934 pub status: MaintenanceStatus,
1935}
1936
1937#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)]
1944#[serde(rename_all = "kebab-case")]
1945#[derive(Default)]
1946pub enum MaintenanceStatus {
1947 #[default]
1948 None,
1949 ActivelyDeveloped,
1950 PassivelyMaintained,
1951 AsIs,
1952 Experimental,
1953 LookingForMaintainer,
1954 Deprecated,
1955}
1956
1957#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
1959#[non_exhaustive]
1960#[serde(expecting = "if there's a newer edition, then this parser (cargo_toml crate) has to be updated")]
1961pub enum Edition {
1962 #[serde(rename = "2015")]
1964 #[default]
1965 E2015 = 2015,
1966 #[serde(rename = "2018")]
1968 E2018 = 2018,
1969 #[serde(rename = "2021")]
1971 E2021 = 2021,
1972 #[serde(rename = "2024")]
1974 E2024 = 2024,
1975}
1976
1977impl std::fmt::Display for Edition {
1978 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1979 f.write_str(match self {
1980 Edition::E2015 => "2015",
1981 Edition::E2018 => "2018",
1982 Edition::E2021 => "2021",
1983 Edition::E2024 => "2024",
1984 })
1985 }
1986}
1987
1988impl Edition {
1989 #[must_use]
1991 pub fn min_rust_version_minor(self) -> u16 {
1992 match self {
1993 Edition::E2015 => 1,
1994 Edition::E2018 => 31,
1995 Edition::E2021 => 56,
1996 Edition::E2024 => 85,
1997 }
1998 }
1999}
2000
2001#[derive(Debug, Default, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize)]
2005#[serde(expecting = "if there's a newer resolver, then this parser (cargo_toml crate) has to be updated")]
2006pub enum Resolver {
2007 #[serde(rename = "1")]
2008 #[default]
2009 V1 = 1,
2011 #[serde(rename = "2")]
2013 V2 = 2,
2014 #[serde(rename = "3")]
2016 V3 = 3,
2017}
2018
2019impl Display for Resolver {
2020 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2021 f.write_str(match self {
2022 Resolver::V1 => "1",
2023 Resolver::V2 => "2",
2024 Resolver::V3 => "3",
2025 })
2026 }
2027}
2028
2029#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2031#[serde(from = "LintSerdeParser", into = "LintSerdeParser")]
2032pub struct Lint {
2033 pub level: LintLevel,
2035
2036 pub priority: i8,
2038
2039 pub config: BTreeMap<String, toml::Value>,
2041}
2042
2043#[derive(Serialize, Deserialize)]
2045#[serde(untagged, expecting = "lints' values should be a string or { level = \"…\", priority = 1 }")]
2046enum LintSerdeParser {
2047 Simple(LintLevel),
2048 Detailed {
2049 level: LintLevel,
2050 #[serde(default)]
2052 priority: i8,
2053
2054 #[serde(default, flatten)]
2056 config: BTreeMap<String, toml::Value>,
2057 },
2058}
2059
2060impl From<LintSerdeParser> for Lint {
2061 fn from(parsed: LintSerdeParser) -> Self {
2062 match parsed {
2063 LintSerdeParser::Simple(level) => Self { level, priority: 0, config: Default::default() },
2064 LintSerdeParser::Detailed { level, priority, config } => Self { level, priority, config },
2065 }
2066 }
2067}
2068
2069impl From<Lint> for LintSerdeParser {
2070 fn from(orig: Lint) -> Self {
2071 if orig.priority == 0 && orig.config.is_empty() {
2072 Self::Simple(orig.level)
2073 } else {
2074 Self::Detailed { level: orig.level, priority: orig.priority, config: orig.config }
2075 }
2076 }
2077}
2078
2079#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)]
2081#[serde(rename_all = "kebab-case")]
2082pub enum LintLevel {
2083 Allow,
2084 Warn,
2085 ForceWarn,
2086 Deny,
2087 Forbid,
2088}
2089
2090#[derive(Deserialize)]
2091#[non_exhaustive]
2092struct Rfc3416FeatureDetail {
2093 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2094 pub enables: Vec<String>,
2095
2096 #[allow(unused)]
2097 #[serde(default = "default_true", skip_serializing_if = "is_true")]
2099 pub public: bool,
2100
2101 #[allow(unused)]
2102 #[serde(default, skip_serializing_if = "Option::is_none")]
2104 pub doc: Option<String>,
2105}
2106
2107#[derive(Deserialize)]
2108#[serde(untagged)]
2109enum Rfc3416Feature {
2110 Simple(Vec<String>),
2111 Detailed(Rfc3416FeatureDetail),
2112
2113}
2114
2115fn feature_set<'de, D>(deserializer: D) -> Result<FeatureSet, D::Error> where D: Deserializer<'de> {
2116 let detailed = BTreeMap::<String, Rfc3416Feature>::deserialize(deserializer)?;
2117 Ok(detailed.into_iter().map(|(k, v)| (k, match v {
2118 Rfc3416Feature::Simple(f) => f,
2119 Rfc3416Feature::Detailed(d) => d.enables,
2120 })).collect())
2121}