cargo_toml/
cargo_toml.rs

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
10//! This crate defines `struct`s that can be deserialized with Serde
11//! to load and inspect `Cargo.toml` metadata.
12//!
13//! See [`Manifest::from_path`]. Note that `Cargo.toml` files are not self-contained. Correct interpretation of the manifest requires other files on disk:
14//!
15//! * List of files in order to auto-discover binaries, examples, benchmarks, and tests.
16//! * Potentially a `Manifest` from one of parent directories, that acts as a workspace root for inheritance of shared workspace information.
17//!
18//! Because of this filesystem-dependence, loading `Cargo.toml` [from a string](`Manifest::from_str`) is [an advanced operation](`Manifest::complete_from_abstract_filesystem`).
19//! The crate has methods for processing this information, but if you don't already have a full crate on disk, you will need to write some glue code to obtain it. See [`Manifest::complete_from_path_and_workspace`].
20
21use 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
29/// Dependencies. The keys in this map are not always crate names, this can be overriden by the `package` field, and there may be multiple copies of the same crate.
30///
31/// Optional dependencies may create implicit features, see the [`features`] module for dealing with this.
32pub type DepsSet = BTreeMap<String, Dependency>;
33/// Config target (see [`parse_cfg`](https://lib.rs/parse_cfg) crate) + deps for the target.
34pub type TargetDepsSet = BTreeMap<String, Target>;
35/// The `[features]` section. This set may be incomplete!
36///
37/// The `default` is special, and there may be more features
38/// implied by optional dependencies.
39/// See the [`features`] module for more info.
40pub type FeatureSet = BTreeMap<String, Vec<String>>;
41/// Locally replace dependencies
42pub type PatchSet = BTreeMap<String, DepsSet>;
43/// A set of lints.
44pub type LintSet = BTreeMap<String, Lint>;
45/// Lint groups such as [lints.rust].
46pub 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/// The top-level `Cargo.toml` structure. **This is the main type in this library.**
60///
61/// The `Metadata` is a generic type for `[package.metadata]` table. You can replace it with
62/// your own struct type if you use the metadata and don't want to use the catch-all `Value` type.
63#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64#[serde(rename_all = "kebab-case")]
65pub struct Manifest<Metadata = Value> {
66    /// Package definition (a cargo crate)
67    pub package: Option<Package<Metadata>>,
68
69    /// Workspace-wide settings
70    pub workspace: Option<Workspace<Metadata>>,
71
72    /// Normal dependencies
73    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
74    pub dependencies: DepsSet,
75
76    /// Dev/test-only deps
77    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
78    pub dev_dependencies: DepsSet,
79
80    /// Build-time deps
81    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
82    pub build_dependencies: DepsSet,
83
84    /// `[target.cfg.dependencies]`
85    #[serde(default, skip_serializing_if = "TargetDepsSet::is_empty")]
86    pub target: TargetDepsSet,
87
88    /// The `[features]` section. This set may be incomplete!
89    ///
90    /// Optional dependencies may create implied Cargo features.
91    /// This features section also supports microsyntax with `dep:`, `/`, and `?`
92    /// for managing dependencies and their features.io
93    ///
94    /// This crate has an optional [`features`] module for dealing with this
95    /// complexity and getting the real list of features.
96    #[serde(default, skip_serializing_if = "FeatureSet::is_empty", deserialize_with = "feature_set")]
97    pub features: FeatureSet,
98
99    /// Obsolete
100    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
101    #[deprecated(note = "Cargo recommends patch instead")]
102    pub replace: DepsSet,
103
104    /// `[patch.crates-io]` section
105    #[serde(default, skip_serializing_if = "PatchSet::is_empty")]
106    pub patch: PatchSet,
107
108    /// Note that due to autolibs feature this is not the complete list
109    /// unless you run [`Manifest::complete_from_path`]
110    pub lib: Option<Product>,
111
112    /// Compilation/optimization settings
113    #[serde(default, skip_serializing_if = "Profiles::should_skip_serializing")]
114    pub profile: Profiles,
115
116    /// `[badges]` section
117    #[serde(default, skip_serializing_if = "Badges::should_skip_serializing")]
118    pub badges: Badges,
119
120    /// Note that due to autobins feature this is not the complete list
121    /// unless you run [`Manifest::complete_from_path`]
122    #[serde(default, skip_serializing_if = "Vec::is_empty")]
123    pub bin: Vec<Product>,
124
125    /// Benchmarks
126    #[serde(default, skip_serializing_if = "Vec::is_empty")]
127    pub bench: Vec<Product>,
128
129    /// Integration tests
130    #[serde(default, skip_serializing_if = "Vec::is_empty")]
131    pub test: Vec<Product>,
132
133    /// Examples
134    #[serde(default, skip_serializing_if = "Vec::is_empty")]
135    pub example: Vec<Product>,
136
137    /// Lints
138    #[serde(default, skip_serializing_if = "Inheritable::<LintGroups>::is_empty")]
139    pub lints: Inheritable<LintGroups>,
140}
141
142/// A manifest can contain both a package and workspace-wide properties
143#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
144#[serde(rename_all = "kebab-case")]
145pub struct Workspace<Metadata = Value> {
146    /// Relative paths of crates in here
147    #[serde(default)]
148    pub members: Vec<String>,
149
150    /// Members to operate on when in the workspace root.
151    ///
152    /// When specified, `default-members` must expand to a subset of `members`.
153    #[serde(default, skip_serializing_if = "Vec::is_empty")]
154    pub default_members: Vec<String>,
155
156    /// Template for inheritance
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub package: Option<PackageTemplate>,
159
160    /// Ignore these dirs
161    #[serde(default, skip_serializing_if = "Vec::is_empty")]
162    pub exclude: Vec<String>,
163
164    /// Shared info
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub metadata: Option<Metadata>,
167
168    /// Compatibility setting
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub resolver: Option<Resolver>,
171
172    /// Template for `needs_workspace_inheritance`
173    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
174    pub dependencies: DepsSet,
175
176    /// Workspace-level lint groups
177    #[serde(default, skip_serializing_if = "LintGroups::is_empty")]
178    pub lints: LintGroups,
179}
180
181/// Workspace can predefine properties that can be inherited via `{ workspace = true }` in its member packages.
182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
183#[serde(rename_all = "kebab-case")]
184#[non_exhaustive]
185pub struct PackageTemplate {
186    /// Deprecated
187    #[serde(default, skip_serializing_if = "Option::is_none")]
188    pub authors: Option<Vec<String>>,
189
190    /// See <https://crates.io/category_slugs>
191    #[serde(default, skip_serializing_if = "Option::is_none")]
192    pub categories: Option<Vec<String>>,
193
194    /// Multi-line text, some people use Markdown here
195    #[serde(default, skip_serializing_if = "Option::is_none")]
196    pub description: Option<String>,
197
198    /// URL
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub documentation: Option<String>,
201
202    /// Opt-in to new Rust behaviors
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub edition: Option<Edition>,
205
206    /// Don't publish these files, relative to workspace
207    #[serde(default, skip_serializing_if = "Option::is_none")]
208    pub exclude: Option<Vec<String>>,
209
210    /// Homepage URL
211    #[serde(default, skip_serializing_if = "Option::is_none")]
212    pub homepage: Option<String>,
213
214    /// Publish these files, relative to workspace
215    #[serde(default, skip_serializing_if = "Option::is_none")]
216    pub include: Option<Vec<String>>,
217
218    /// For search
219    #[serde(default, skip_serializing_if = "Option::is_none")]
220    pub keywords: Option<Vec<String>>,
221
222    /// SPDX
223    #[serde(default, skip_serializing_if = "Option::is_none")]
224    pub license: Option<String>,
225
226    /// If not SPDX
227    #[serde(default, skip_serializing_if = "Option::is_none")]
228    pub license_file: Option<PathBuf>,
229
230    /// Block publishing or choose custom registries
231    #[serde(default, skip_serializing_if = "Publish::is_default")]
232    pub publish: Publish,
233
234    /// Opt-out or custom path, relative to workspace
235    #[serde(default, skip_serializing_if = "OptionalFile::is_default")]
236    pub readme: OptionalFile,
237
238    /// (HTTPS) repository URL
239    #[serde(default, skip_serializing_if = "Option::is_none")]
240    pub repository: Option<String>,
241
242    /// Minimum required rustc version in format `1.99`
243    #[serde(default, skip_serializing_if = "Option::is_none")]
244    pub rust_version: Option<String>,
245
246    /// Package version semver
247    #[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    /// Parse contents from a `Cargo.toml` file on disk.
265    ///
266    /// Calls [`Manifest::complete_from_path`] to discover implicit binaries, etc.
267    /// If needed, it will search the file system for a workspace, and fill in data inherited from the workspace.
268    #[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    /// Warning: this will make an incomplete manifest, which will be missing data and panic when using workspace inheritance.
274    ///
275    /// Parse contents of a `Cargo.toml` file already loaded as a byte slice. It's **not** a file name, but file's TOML-syntax content.
276    ///
277    /// If you don't call [`Manifest::complete_from_path`], it may be missing implicit data, and panic if workspace inheritance is used.
278    #[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    /// Warning: this will make an incomplete manifest, which will be missing data and panic when using workspace inheritance.
284    ///
285    /// It parses contents of a `Cargo.toml` file loaded as a string. It's **not** a file name, but file's TOML-syntax content.
286    ///
287    /// For a more reliable method, see `from_path`.
288    ///
289    /// If you don't call [`Manifest::complete_from_path`], it may be missing implicit data, and panic if workspace inheritance is used.
290    #[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    /// Warning: this will make an incomplete manifest, which will be missing data and panic when using workspace inheritance.
299    ///
300    /// Parse `Cargo.toml`, and parse its `[package.metadata]` into a custom Serde-compatible type.
301    ///
302    /// It does not call [`Manifest::complete_from_path`], so may be missing implicit data.
303    #[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            // This is a clumsy implementation of Cargo's rule that missing version defaults publish to false.
315            // Serde just doesn't support such relationship for default field values, so this will be incorrect
316            // for explicit `version = "0.0.0"` and `publish = true`.
317            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    /// Parse contents from `Cargo.toml` file on disk, with custom Serde-compatible metadata type.
325    ///
326    /// Calls [`Manifest::complete_from_path`], so it will load a workspace if necessary.
327    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    /// `Cargo.toml` doesn't contain explicit information about `[lib]` and `[[bin]]`,
338    /// which are inferred based on files on disk.
339    ///
340    /// This scans the disk to make the data in the manifest as complete as possible.
341    ///
342    /// It supports workspace inheritance and will search for a root workspace.
343    /// Use [`Manifest::complete_from_path_and_workspace`] to provide the workspace explicitly.
344    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    /// [`Manifest::complete_from_path`], but allows passing workspace manifest explicitly.
350    ///
351    /// `workspace_manifest_and_path` is the root workspace manifest already parsed,
352    /// and the path is the path to the root workspace's directory.
353    /// If it's `None`, the root workspace will be discovered automatically.
354    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    /// `Cargo.toml` doesn't contain explicit information about `[lib]` and `[[bin]]`,
360    /// which are inferred based on files on disk.
361    ///
362    /// You can provide any implementation of directory scan, which doesn't have to
363    /// be reading straight from disk (might scan a tarball or a git repo, for example).
364    ///
365    /// If `workspace_manifest_and_path` is set, it will inherit from this workspace.
366    /// If it's `None`, it will try to find a workspace if needed.
367    ///
368    /// Call it like `complete_from_abstract_filesystem::<cargo_toml::Value, _>(…)` if the arguments are ambiguous.
369    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            // Manifest may be both a workspace and a package
380            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    /// If `true`, some fields are unavailable. If `false`, it's fully usable as-is.
409    ///
410    /// It is `false` in manifests that use workspace inheritance, but had their data completed from the root manifest already.
411    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(); // not applicable
523        }
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    /// Return the set of path overrided in `Cargo.toml`.
592    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        // Remove partially overrided items
614        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    /// ensure bins are deterministic
663    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    /// Panics if it's not a package (only a workspace).
668    ///
669    /// You can access the `.package` field directly to handle the `Option`:
670    ///
671    /// ```rust,ignore
672    /// manifest.package.as_ref().ok_or(SomeError::NotAPackage)?;
673    /// ```
674    #[track_caller]
675    #[inline]
676    pub fn package(&self) -> &Package<Metadata> {
677        self.package.as_ref().expect("not a package")
678    }
679
680    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
681    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/// Build-in an custom build/optimization settings
736#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
737pub struct Profiles {
738    /// Used for `--release`
739    #[serde(skip_serializing_if = "Option::is_none")]
740    pub release: Option<Profile>,
741
742    /// Used by default, weirdly called `debug` profile.
743    #[serde(skip_serializing_if = "Option::is_none")]
744    pub dev: Option<Profile>,
745
746    /// Used for `cargo test`
747    #[serde(skip_serializing_if = "Option::is_none")]
748    pub test: Option<Profile>,
749
750    /// Used for `cargo bench` (nightly)
751    #[serde(skip_serializing_if = "Option::is_none")]
752    pub bench: Option<Profile>,
753
754    /// Unused
755    #[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    /// User-suppiled for `cargo --profile=name`
760    #[serde(flatten)]
761    pub custom: BTreeMap<String, Profile>,
762}
763
764impl Profiles {
765    /// Determine whether or not a Profiles struct should be serialized
766    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/// Verbosity of debug info in a [`Profile`]
776#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
777#[serde(try_from = "toml::Value")]
778pub enum DebugSetting {
779    /// 0 or false
780    None = 0,
781    /// 1 = line tables only
782    Lines = 1,
783    /// 2 or true
784    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/// Handling of debug symbols in a build profile
821#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
822#[serde(try_from = "toml::Value")]
823pub enum StripSetting {
824    /// Same as `strip = false`
825    None,
826    /// Detailed debug is stripped, but coarse debug is preserved
827    Debuginfo,
828    /// Stronger than the `Debuginfo` setting, same as `strip = true`
829    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/// Handling of LTO in a build profile
860#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
861#[serde(try_from = "toml::Value")]
862pub enum LtoSetting {
863    /// off
864    None,
865    /// false
866    ThinLocal,
867    Thin,
868    /// True
869    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/// Compilation/optimization settings for a workspace
902#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
903#[serde(rename_all = "kebab-case")]
904pub struct Profile {
905    /// num or z, s
906    #[serde(default, skip_serializing_if = "Option::is_none")]
907    pub opt_level: Option<Value>,
908
909    /// 0,1,2 or bool
910    #[serde(default, skip_serializing_if = "Option::is_none")]
911    pub debug: Option<DebugSetting>,
912
913    /// Move debug info to separate files
914    #[serde(default, skip_serializing_if = "Option::is_none")]
915    pub split_debuginfo: Option<String>,
916
917    /// For dynamic libraries
918    #[serde(default, skip_serializing_if = "Option::is_none")]
919    pub rpath: Option<bool>,
920
921    /// Link-time-optimization
922    #[serde(default, skip_serializing_if = "Option::is_none")]
923    pub lto: Option<LtoSetting>,
924
925    /// Extra assertions
926    #[serde(default, skip_serializing_if = "Option::is_none")]
927    pub debug_assertions: Option<bool>,
928
929    /// Parallel compilation
930    #[serde(default, skip_serializing_if = "Option::is_none")]
931    pub codegen_units: Option<u16>,
932
933    /// Handling of panics/unwinding
934    #[serde(default, skip_serializing_if = "Option::is_none")]
935    pub panic: Option<String>,
936
937    /// Support for incremental rebuilds
938    #[serde(default, skip_serializing_if = "Option::is_none")]
939    pub incremental: Option<bool>,
940
941    /// Check integer arithmetic
942    #[serde(default, skip_serializing_if = "Option::is_none")]
943    pub overflow_checks: Option<bool>,
944
945    /// Remove debug info
946    #[serde(default, skip_serializing_if = "Option::is_none")]
947    pub strip: Option<StripSetting>,
948
949    /// Profile overrides for dependencies, `*` is special.
950    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
951    pub package: BTreeMap<String, Value>,
952
953    /// Profile overrides for build dependencies, `*` is special.
954    #[serde(default, skip_serializing_if = "Option::is_none")]
955    pub build_override: Option<Value>,
956
957    /// Only relevant for non-standard profiles
958    #[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")]
964/// Cargo uses the term "target" for both "target platform" and "build target" (the thing to build),
965/// which makes it ambigous.
966/// Here Cargo's bin/lib **target** is renamed to **product**.
967pub struct Product {
968    /// This field points at where the crate is located, relative to the `Cargo.toml`.
969    pub path: Option<String>,
970
971    /// The name of a product is the name of the library or binary that will be generated.
972    /// This is defaulted to the name of the package, with any dashes replaced
973    /// with underscores. (Rust `extern crate` declarations reference this name;
974    /// therefore the value must be a valid Rust identifier to be usable.)
975    pub name: Option<String>,
976
977    /// A flag for enabling unit tests for this product. This is used by `cargo test`.
978    #[serde(default = "default_true", skip_serializing_if = "is_true")]
979    pub test: bool,
980
981    /// A flag for enabling documentation tests for this product. This is only relevant
982    /// for libraries, it has no effect on other sections. This is used by
983    /// `cargo test`.
984    #[serde(default = "default_true", skip_serializing_if = "is_true")]
985    pub doctest: bool,
986
987    /// A flag for enabling benchmarks for this product. This is used by `cargo bench`.
988    #[serde(default = "default_true", skip_serializing_if = "is_true")]
989    pub bench: bool,
990
991    /// A flag for enabling documentation of this product. This is used by `cargo doc`.
992    #[serde(default = "default_true", skip_serializing_if = "is_true")]
993    pub doc: bool,
994
995    /// If the product is meant to be a compiler plugin, this field must be set to true
996    /// for Cargo to correctly compile it and make it available for all dependencies.
997    #[serde(default, skip_serializing_if = "is_false")]
998    pub plugin: bool,
999
1000    /// If the product is meant to be a "macros 1.1" procedural macro, this field must
1001    /// be set to true.
1002    #[serde(default, alias = "proc_macro", alias = "proc-macro", skip_serializing_if = "is_false")]
1003    pub proc_macro: bool,
1004
1005    /// If set to false, `cargo test` will omit the `--test` flag to rustc, which
1006    /// stops it from generating a test harness. This is useful when the binary being
1007    /// built manages the test runner itself.
1008    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1009    pub harness: bool,
1010
1011    /// Deprecated. Edition should be set only per package.
1012    ///
1013    /// If set then a product can be configured to use a different edition than the
1014    /// `[package]` is configured to use, perhaps only compiling a library with the
1015    /// 2018 edition or only compiling one unit test with the 2015 edition. By default
1016    /// all products are compiled with the edition specified in `[package]`.
1017    #[serde(default, skip_serializing_if = "Option::is_none")]
1018    pub edition: Option<Edition>,
1019
1020    /// The available options are "dylib", "rlib", "staticlib", "cdylib", and "proc-macro".
1021    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1022    pub crate_type: Vec<String>,
1023
1024    /// The `required-features` field specifies which features the product needs in order to be built.
1025    /// If any of the required features are not selected, the product will be skipped.
1026    /// This is only relevant for the `[[bin]]`, `[[bench]]`, `[[test]]`, and `[[example]]` sections,
1027    /// it has no effect on `[lib]`.
1028    #[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/// Dependencies that are platform-specific or enabled through custom `cfg()`.
1052#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
1053#[serde(rename_all = "kebab-case")]
1054pub struct Target {
1055    /// platform-specific normal deps
1056    #[serde(default)]
1057    pub dependencies: DepsSet,
1058    /// platform-specific dev-only/test-only deps
1059    #[serde(default)]
1060    pub dev_dependencies: DepsSet,
1061    /// platform-specific build-time deps
1062    #[serde(default)]
1063    pub build_dependencies: DepsSet,
1064}
1065
1066/// Dependency definition. Note that this struct doesn't carry it's key/name, which you need to read from its section.
1067///
1068/// It can be simple version number, or detailed settings, or inherited.
1069#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1070#[serde(untagged)]
1071pub enum Dependency {
1072    /// Version requirement (e.g. `^1.5`)
1073    Simple(String),
1074    /// Incomplete data
1075    Inherited(InheritedDependencyDetail), // order is important for serde
1076    /// `{ version = "^1.5", features = ["a", "b"] }` etc.
1077    Detailed(Box<DependencyDetail>),
1078}
1079
1080impl Dependency {
1081    /// Get object with special dependency settings if it's not just a version number.
1082    ///
1083    /// Returns `None` if it's inherited and the value is not available
1084    #[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    /// Panics if inherited value is not available
1094    #[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    /// Returns error if inherited value is not available
1101    ///
1102    /// Makes it detailed otherwise
1103    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    /// Panics if inherited value is not available
1121    ///
1122    /// Version requirement
1123    #[inline]
1124    #[track_caller]
1125    #[must_use]
1126    pub fn req(&self) -> &str {
1127        self.try_req().unwrap()
1128    }
1129
1130    /// Version requirement
1131    ///
1132    /// Returns Error if inherited value is not available
1133    #[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    /// Enable extra features for this dep, in addition to the `default` features controlled via `default_features`.
1144    #[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    /// Is it optional. Note that optional deps can be used as features, unless features use `dep:`/`?` syntax for them.
1155    /// See the [`features`] module for more info.
1156    #[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    /// `Some` if it overrides the package name.
1167    /// If `None`, use the dependency name as the package name.
1168    #[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    /// Git URL of this dependency, if any
1178    #[inline]
1179    #[must_use]
1180    pub fn git(&self) -> Option<&str> {
1181        self.detail()?.git.as_deref()
1182    }
1183
1184    /// Git commit of this dependency, if any
1185    #[inline]
1186    #[must_use]
1187    pub fn git_rev(&self) -> Option<&str> {
1188        self.detail()?.rev.as_deref()
1189    }
1190
1191    /// `true` if it's an usual crates.io dependency,
1192    /// `false` if git/path/alternative registry
1193    #[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                // TODO: allow registry to be set to crates.io explicitly?
1200                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/// When definition of a dependency is more than just a version string.
1214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1215#[serde(rename_all = "kebab-case")]
1216pub struct DependencyDetail {
1217    /// Semver requirement. Note that a plain version number implies this version *or newer* compatible one.
1218    #[serde(skip_serializing_if = "Option::is_none")]
1219    pub version: Option<String>,
1220
1221    /// If `Some`, use this as the crate name instead of `[dependencies]`'s table key.
1222    ///
1223    /// By using this, a crate can have multiple versions of the same dependency.
1224    #[serde(skip_serializing_if = "Option::is_none")]
1225    pub package: Option<String>,
1226
1227    /// Fetch this dependency from a custom 3rd party registry (alias defined in Cargo config), not crates-io.
1228    ///
1229    /// This depends on local cargo configuration. It becomes `registry_index` after the crate is uploaded to a registry.
1230    #[serde(skip_serializing_if = "Option::is_none")]
1231    pub registry: Option<String>,
1232
1233    /// Directly define custom 3rd party registry URL (may be `sparse+https:`) instead of a config nickname.
1234    #[serde(skip_serializing_if = "Option::is_none")]
1235    pub registry_index: Option<String>,
1236
1237    /// This path is usually relative to the crate's manifest, but when using workspace inheritance, it may be relative to the workspace!
1238    ///
1239    /// When calling [`Manifest::complete_from_path_and_workspace`] use absolute path for the workspace manifest, and then this will be corrected to be an absolute
1240    /// path when inherited from the workspace.
1241    #[serde(skip_serializing_if = "Option::is_none")]
1242    pub path: Option<String>,
1243
1244    /// If true, the dependency has been defined at the workspace level, so the `path` is joined with workspace's base path.
1245    ///
1246    /// This is a field added by this crate, does not exist in TOML.
1247    /// Note that `Dependency::Simple` won't have this flag, even if it was inherited.
1248    #[serde(skip)]
1249    pub inherited: bool,
1250
1251    /// Read dependency from git repo URL, not allowed on crates-io.
1252    #[serde(skip_serializing_if = "Option::is_none")]
1253    pub git: Option<String>,
1254    /// Read dependency from git branch, not allowed on crates-io.
1255    #[serde(skip_serializing_if = "Option::is_none")]
1256    pub branch: Option<String>,
1257    /// Read dependency from git tag, not allowed on crates-io.
1258    #[serde(skip_serializing_if = "Option::is_none")]
1259    pub tag: Option<String>,
1260    /// Read dependency from git commit, not allowed on crates-io.
1261    #[serde(skip_serializing_if = "Option::is_none")]
1262    pub rev: Option<String>,
1263
1264    /// Enable these features of the dependency.
1265    ///
1266    /// Note that Cargo interprets `default` in a special way.
1267    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1268    pub features: Vec<String>,
1269
1270    /// NB: Not allowed at workspace level
1271    ///
1272    /// If not used with `dep:` or `?/` syntax in `[features]`, this also creates an implicit feature.
1273    /// See the [`features`] module for more info.
1274    #[serde(default, skip_serializing_if = "is_false")]
1275    pub optional: bool,
1276
1277    /// Enable the `default` set of features of the dependency (enabled by default).
1278    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1279    pub default_features: bool,
1280
1281    /// Contains the remaining unstable keys and values for the dependency.
1282    #[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, // != bool::default()
1301            package: None,
1302            unstable: BTreeMap::new(),
1303        }
1304    }
1305}
1306
1307/// When a dependency is defined as `{ workspace = true }`,
1308/// and workspace data hasn't been applied yet.
1309#[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/// The `[package]` section of the [`Manifest`]. This is where crate properties are.
1323///
1324/// Note that most of these properties can be inherited from a workspace, and therefore not available just from reading a single `Cargo.toml`. See [`Manifest::inherit_workspace`].
1325///
1326/// You can replace `Metadata` generic type with your own
1327/// to parse into something more useful than a generic toml `Value`
1328#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1329#[serde(rename_all = "kebab-case")]
1330#[non_exhaustive]
1331pub struct Package<Metadata = Value> {
1332    /// Careful: some names are uppercase, case-sensitive. `-` changes to `_` when used as a Rust identifier.
1333    pub name: String,
1334
1335    /// See [the `version()` getter for more info](`Package::version()`).
1336    ///
1337    /// Must parse as semver, e.g. "1.9.0"
1338    ///
1339    /// This field may have unknown value when using workspace inheritance,
1340    /// and when the `Manifest` has been loaded without its workspace.
1341    #[serde(default = "default_version")]
1342    pub version: Inheritable<String>,
1343
1344    /// Package's edition opt-in. Use [`Package::edition()`] to read it.
1345    #[serde(default)]
1346    pub edition: Inheritable<Edition>,
1347
1348    /// MSRV 1.x (beware: does not require semver formatting)
1349    #[serde(default, skip_serializing_if = "Option::is_none")]
1350    pub rust_version: Option<Inheritable<String>>,
1351
1352    /// Build script definition
1353    #[serde(default, skip_serializing_if = "Option::is_none")]
1354    pub build: Option<OptionalFile>,
1355
1356    /// Workspace this package is a member of (`None` if it's implicit)
1357    #[serde(default, skip_serializing_if = "Option::is_none")]
1358    pub workspace: Option<PathBuf>,
1359
1360    /// e.g. `["Author <e@mail>", "etc"]`
1361    ///
1362    /// Deprecated.
1363    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1364    pub authors: Inheritable<Vec<String>>,
1365
1366    /// It doesn't link to anything
1367    #[serde(default, skip_serializing_if = "Option::is_none")]
1368    pub links: Option<String>,
1369
1370    /// A short blurb about the package. This is not rendered in any format when
1371    /// uploaded to crates.io (aka this is not markdown).
1372    #[serde(default, skip_serializing_if = "Option::is_none")]
1373    pub description: Option<Inheritable<String>>,
1374
1375    /// Project's homepage
1376    #[serde(default, skip_serializing_if = "Option::is_none")]
1377    pub homepage: Option<Inheritable<String>>,
1378
1379    /// Path to your custom docs. Unnecssary if you rely on docs.rs.
1380    #[serde(default, skip_serializing_if = "Option::is_none")]
1381    pub documentation: Option<Inheritable<String>>,
1382
1383    /// This points to a file under the package root (relative to this `Cargo.toml`).
1384    /// implied if README.md, README.txt or README exists.
1385    #[serde(default, skip_serializing_if = "Inheritable::is_default")]
1386    pub readme: Inheritable<OptionalFile>,
1387
1388    /// Up to 5, for search
1389    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1390    pub keywords: Inheritable<Vec<String>>,
1391
1392    /// This is a list of up to five categories where this crate would fit.
1393    /// e.g. `["command-line-utilities", "development-tools::cargo-plugins"]`
1394    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1395    pub categories: Inheritable<Vec<String>>,
1396
1397    /// Don't publish these files
1398    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1399    pub exclude: Inheritable<Vec<String>>,
1400
1401    /// Publish these files
1402    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1403    pub include: Inheritable<Vec<String>>,
1404
1405    /// e.g. "MIT"
1406    #[serde(default, skip_serializing_if = "Option::is_none")]
1407    pub license: Option<Inheritable<String>>,
1408
1409    /// If `license` is not standard
1410    #[serde(default, skip_serializing_if = "Option::is_none")]
1411    pub license_file: Option<Inheritable<PathBuf>>,
1412
1413    /// (HTTPS) URL to crate's repository
1414    #[serde(default, skip_serializing_if = "Option::is_none")]
1415    pub repository: Option<Inheritable<String>>,
1416
1417    /// The default binary to run by cargo run.
1418    #[serde(default, skip_serializing_if = "Option::is_none")]
1419    pub default_run: Option<String>,
1420
1421    /// Discover binaries from the file system
1422    ///
1423    /// This may be incorrectly set to `true` if the crate uses 2015 edition and has explicit `[[bin]]` sections
1424    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1425    pub autobins: bool,
1426
1427    /// Discover libraries from the file system
1428    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1429    pub autolib: bool,
1430
1431    /// Discover examples from the file system
1432    ///
1433    /// This may be incorrectly set to `true` if the crate uses 2015 edition and has explicit `[[example]]` sections
1434    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1435    pub autoexamples: bool,
1436
1437    /// Discover tests from the file system
1438    ///
1439    /// This may be incorrectly set to `true` if the crate uses 2015 edition and has explicit `[[test]]` sections
1440    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1441    pub autotests: bool,
1442
1443    /// Discover benchmarks from the file system
1444    ///
1445    /// This may be incorrectly set to `true` if the crate uses 2015 edition and has explicit `[[bench]]` sections
1446    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1447    pub autobenches: bool,
1448
1449    /// Disable publishing or select custom registries.
1450    #[serde(default, skip_serializing_if = "Inheritable::is_default")]
1451    pub publish: Inheritable<Publish>,
1452
1453    /// The feature resolver version.
1454    #[serde(default, skip_serializing_if = "Option::is_none")]
1455    pub resolver: Option<Resolver>,
1456
1457    /// Arbitrary metadata of any type, an extension point for 3rd party tools.
1458    #[serde(skip_serializing_if = "Option::is_none")]
1459    pub metadata: Option<Metadata>,
1460}
1461
1462#[allow(deprecated)]
1463impl<Metadata> Package<Metadata> {
1464    /// Prefer creating it by parsing a [`Manifest`] instead.
1465    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    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1499    ///
1500    /// [`Manifest::from_str`] does not know where the TOML data came from, so it has no way of
1501    /// searching the file system (or tarball or git) for a matching
1502    /// [Cargo Workspace Manifest](https://doc.rust-lang.org/cargo/reference/workspaces.html).
1503    ///
1504    /// Without a workspace, properties that use
1505    /// [inheritance](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-package-table)
1506    /// are missing data, and therefore can't be returned, and will panic.
1507    ///
1508    /// You can access these properties directly, they are an [`Inheritable`] enum.
1509    ///
1510    /// The version will default to `0.0.0` if the `version` field was absent in the manifest.
1511    #[track_caller]
1512    #[inline]
1513    pub fn version(&self) -> &str {
1514        self.version.as_ref().unwrap()
1515    }
1516
1517    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1518    ///
1519    /// See [`version`](`Package::version()`) for more information.
1520    #[track_caller]
1521    #[inline]
1522    pub fn authors(&self) -> &[String] {
1523        self.authors.as_ref().unwrap()
1524    }
1525
1526    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1527    ///
1528    /// See [`version`](`Package::version()`) for more information.
1529    #[track_caller]
1530    #[inline]
1531    pub fn categories(&self) -> &[String] {
1532        self.categories.as_ref().unwrap()
1533    }
1534
1535    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1536    ///
1537    /// See [`version`](`Package::version()`) for more information.
1538    #[track_caller]
1539    #[inline]
1540    pub fn categories_mut(&mut self) -> &mut Vec<String> {
1541        self.categories.as_mut().unwrap()
1542    }
1543
1544    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1545    ///
1546    /// See [`version`](`Package::version()`) for more information.
1547    #[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    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1559    ///
1560    /// See [`version`](`Package::version()`) for more information.
1561    #[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    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1573    ///
1574    /// See [`version`](`Package::version()`) for more information.
1575    #[track_caller]
1576    #[inline]
1577    pub fn edition(&self) -> Edition {
1578        self.edition.unwrap()
1579    }
1580
1581    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1582    ///
1583    /// See [`version`](`Package::version()`) for more information.
1584    #[track_caller]
1585    #[inline]
1586    pub fn exclude(&self) -> &[String] {
1587        self.exclude.as_ref().unwrap()
1588    }
1589
1590    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1591    ///
1592    /// See [`version`](`Package::version()`) for more information.
1593    #[track_caller]
1594    #[inline]
1595    pub fn include(&self) -> &[String] {
1596        self.include.as_ref().unwrap()
1597    }
1598
1599    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1600    ///
1601    /// See [`version`](`Package::version()`) for more information.
1602    #[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    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1614    ///
1615    /// See [`version`](`Package::version()`) for more information.
1616    #[track_caller]
1617    #[inline]
1618    pub fn keywords(&self) -> &[String] {
1619        self.keywords.as_ref().unwrap()
1620    }
1621
1622    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1623    ///
1624    /// See [`version`](`Package::version()`) for more information.
1625    #[track_caller]
1626    #[inline]
1627    pub fn license(&self) -> Option<&str> {
1628        Some(self.license.as_ref()?.as_ref().unwrap())
1629    }
1630
1631    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1632    ///
1633    /// See [`version`](`Package::version()`) for more information.
1634    #[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    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1641    ///
1642    /// See [`version`](`Package::version()`) for more information.
1643    #[track_caller]
1644    #[inline]
1645    pub fn publish(&self) -> &Publish {
1646        self.publish.as_ref().unwrap()
1647    }
1648
1649    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1650    ///
1651    /// See [`version`](`Package::version()`) for more information.
1652    #[track_caller]
1653    #[inline]
1654    pub fn readme(&self) -> &OptionalFile {
1655        self.readme.as_ref().unwrap()
1656    }
1657
1658    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1659    ///
1660    /// See [`version`](`Package::version()`) for more information.
1661    #[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    /// Panics if the field is not available (inherited from a workspace that hasn't been loaded)
1672    ///
1673    /// See [`version`](`Package::version()`) for more information.
1674    #[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    /// The property that doesn't actually link with anything.
1685    ///
1686    /// Can't be inherited.
1687    #[inline]
1688    pub fn links(&self) -> Option<&str> {
1689        self.links.as_deref()
1690    }
1691
1692    /// Name of the package/crate. Libraries and binaries can override it.
1693    ///
1694    /// Can't be inherited.
1695    #[inline]
1696    pub fn name(&self) -> &str {
1697        &self.name
1698    }
1699
1700    /// If `true`, some fields are unavailable.
1701    ///
1702    /// It is `false` in manifests that use inheritance, but had their data completed from the root manifest already.
1703    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/// A way specify or disable README or `build.rs`.
1728#[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    /// Opt-in to default, or explicit opt-out
1732    Flag(bool),
1733    /// Explicit path
1734    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    /// This returns `none` even if `Flag(true)` is set.
1758    #[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/// Forbids or selects custom registry
1775#[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/// In badges section of Cargo.toml
1825///
1826/// Mostly obsolete.
1827#[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/// `[badges]` section of `Cargo.toml`, deprecated by crates-io except `maintenance`.
1855#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
1856#[serde(rename_all = "kebab-case")]
1857pub struct Badges {
1858    /// Appveyor: `repository` is required. `branch` is optional; default is `master`
1859    /// `service` is optional; valid values are `github` (default), `bitbucket`, and
1860    /// `gitlab`; `id` is optional; you can specify the appveyor project id if you
1861    /// want to use that instead. `project_name` is optional; use when the repository
1862    /// name differs from the appveyor project name.
1863    #[serde(default, deserialize_with = "ok_or_default")]
1864    pub appveyor: Option<Badge>,
1865
1866    /// Circle CI: `repository` is required. `branch` is optional; default is `master`
1867    #[serde(default, deserialize_with = "ok_or_default")]
1868    pub circle_ci: Option<Badge>,
1869
1870    /// GitLab: `repository` is required. `branch` is optional; default is `master`
1871    #[serde(default, deserialize_with = "ok_or_default")]
1872    pub gitlab: Option<Badge>,
1873
1874    /// Travis CI: `repository` in format `"<user>/<project>"` is required.
1875    /// `branch` is optional; default is `master`
1876    #[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    /// Codecov: `repository` is required. `branch` is optional; default is `master`
1881    /// `service` is optional; valid values are `github` (default), `bitbucket`, and
1882    /// `gitlab`.
1883    #[serde(default, deserialize_with = "ok_or_default")]
1884    pub codecov: Option<Badge>,
1885
1886    /// Coveralls: `repository` is required. `branch` is optional; default is `master`
1887    /// `service` is optional; valid values are `github` (default) and `bitbucket`.
1888    #[serde(default, deserialize_with = "ok_or_default")]
1889    pub coveralls: Option<Badge>,
1890
1891    /// Is it maintained resolution time: `repository` is required.
1892    #[serde(default, deserialize_with = "ok_or_default")]
1893    pub is_it_maintained_issue_resolution: Option<Badge>,
1894
1895    /// Is it maintained percentage of open issues: `repository` is required.
1896    #[serde(default, deserialize_with = "ok_or_default")]
1897    pub is_it_maintained_open_issues: Option<Badge>,
1898
1899    /// Maintenance: `status` is required. Available options are `actively-developed`,
1900    /// `passively-maintained`, `as-is`, `experimental`, `looking-for-maintainer`,
1901    /// `deprecated`, and the default `none`, which displays no badge on crates.io.
1902    ///
1903    /// ```toml
1904    /// [badges]
1905    /// maintenance.status = "as-is"
1906    /// ```
1907    #[serde(default, deserialize_with = "ok_or_default")]
1908    pub maintenance: Maintenance,
1909}
1910
1911impl Badges {
1912    #[allow(deprecated)]
1913    /// Determine whether or not a Profiles struct should be serialized
1914    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/// A [`Badges`] field with [`MaintenanceStatus`].
1928///
1929/// ```toml
1930/// [badges]
1931/// maintenance.status = "experimental"
1932/// ```
1933#[derive(Debug, PartialEq, Eq, Copy, Clone, Default, Serialize, Deserialize)]
1934pub struct Maintenance {
1935    pub status: MaintenanceStatus,
1936}
1937
1938/// Mainly used to deprecate crates.
1939///
1940/// ```toml
1941/// [badges]
1942/// maintenance.status = "deprecated"
1943/// ```
1944#[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/// Edition setting, which opts in to new Rust/Cargo behaviors.
1959#[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    /// 2015
1964    #[serde(rename = "2015")]
1965    #[default]
1966    E2015 = 2015,
1967    /// 2018
1968    #[serde(rename = "2018")]
1969    E2018 = 2018,
1970    /// 2021
1971    #[serde(rename = "2021")]
1972    E2021 = 2021,
1973    /// 2024
1974    #[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    /// Returns minor version (1.x) of the oldest rustc that supports this edition
1991    #[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/// The feature resolver version.
2003///
2004/// Needed in [`Workspace`], but implied by [`Edition`] in packages.
2005#[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    /// The default for editions prior to 2021.
2011    V1 = 1,
2012    /// The default for the 2021 edition.
2013    #[serde(rename = "2")]
2014    V2 = 2,
2015    /// The default for the 2024 edition.
2016    #[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/// Lint definition.
2031#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2032#[serde(from = "LintSerdeParser", into = "LintSerdeParser")]
2033pub struct Lint {
2034    /// allow/warn/deny
2035    pub level: LintLevel,
2036
2037    /// Controls which lints or lint groups override other lint groups.
2038    pub priority: i8,
2039
2040    /// Unstable
2041    pub config: BTreeMap<String, toml::Value>,
2042}
2043
2044/// Internal
2045#[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        /// Controls which lints or lint groups override other lint groups.
2052        #[serde(default)]
2053        priority: i8,
2054
2055        /// Unstable
2056        #[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/// Lint level.
2085#[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    /// `public` indicates whether or not the feature should be visible in documentation, and defaults to true
2103    #[serde(default = "default_true", skip_serializing_if = "is_true")]
2104    pub public: bool,
2105
2106    #[allow(unused)]
2107    /// Add a description to the feature
2108    #[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}