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/// Optional dependencies may create implicit features, see the [`features`] module for dealing with this.
31pub type DepsSet = BTreeMap<String, Dependency>;
32/// Config target (see [`parse_cfg`](https://lib.rs/parse_cfg) crate) + deps for the target.
33pub type TargetDepsSet = BTreeMap<String, Target>;
34/// The `[features]` section. This set may be incomplete!
35///
36/// The `default` is special, and there may be more features
37/// implied by optional dependencies.
38/// See the [`features`] module for more info.
39pub type FeatureSet = BTreeMap<String, Vec<String>>;
40/// Locally replace dependencies
41pub type PatchSet = BTreeMap<String, DepsSet>;
42/// A set of lints.
43pub type LintSet = BTreeMap<String, Lint>;
44/// Lint groups such as [lints.rust].
45pub 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/// The top-level `Cargo.toml` structure. **This is the main type in this library.**
59///
60/// The `Metadata` is a generic type for `[package.metadata]` table. You can replace it with
61/// your own struct type if you use the metadata and don't want to use the catch-all `Value` type.
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63#[serde(rename_all = "kebab-case")]
64pub struct Manifest<Metadata = Value> {
65    /// Package definition (a cargo crate)
66    pub package: Option<Package<Metadata>>,
67
68    /// Workspace-wide settings
69    pub workspace: Option<Workspace<Metadata>>,
70
71    /// Normal dependencies
72    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
73    pub dependencies: DepsSet,
74
75    /// Dev/test-only deps
76    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
77    pub dev_dependencies: DepsSet,
78
79    /// Build-time deps
80    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
81    pub build_dependencies: DepsSet,
82
83    /// `[target.cfg.dependencies]`
84    #[serde(default, skip_serializing_if = "TargetDepsSet::is_empty")]
85    pub target: TargetDepsSet,
86
87    /// The `[features]` section. This set may be incomplete!
88    ///
89    /// Optional dependencies may create implied Cargo features.
90    /// This features section also supports microsyntax with `dep:`, `/`, and `?`
91    /// for managing dependencies and their features.io
92    ///
93    /// This crate has an optional [`features`] module for dealing with this
94    /// complexity and getting the real list of features.
95    #[serde(default, skip_serializing_if = "FeatureSet::is_empty", deserialize_with = "feature_set")]
96    pub features: FeatureSet,
97
98    /// Obsolete
99    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
100    #[deprecated(note = "Cargo recommends patch instead")]
101    pub replace: DepsSet,
102
103    /// `[patch.crates-io]` section
104    #[serde(default, skip_serializing_if = "PatchSet::is_empty")]
105    pub patch: PatchSet,
106
107    /// Note that due to autolibs feature this is not the complete list
108    /// unless you run [`Manifest::complete_from_path`]
109    pub lib: Option<Product>,
110
111    /// Compilation/optimization settings
112    #[serde(default, skip_serializing_if = "Profiles::should_skip_serializing")]
113    pub profile: Profiles,
114
115    /// `[badges]` section
116    #[serde(default, skip_serializing_if = "Badges::should_skip_serializing")]
117    pub badges: Badges,
118
119    /// Note that due to autobins feature this is not the complete list
120    /// unless you run [`Manifest::complete_from_path`]
121    #[serde(default, skip_serializing_if = "Vec::is_empty")]
122    pub bin: Vec<Product>,
123
124    /// Benchmarks
125    #[serde(default, skip_serializing_if = "Vec::is_empty")]
126    pub bench: Vec<Product>,
127
128    /// Integration tests
129    #[serde(default, skip_serializing_if = "Vec::is_empty")]
130    pub test: Vec<Product>,
131
132    /// Examples
133    #[serde(default, skip_serializing_if = "Vec::is_empty")]
134    pub example: Vec<Product>,
135
136    /// Lints
137    #[serde(default, skip_serializing_if = "Inheritable::<LintGroups>::is_empty")]
138    pub lints: Inheritable<LintGroups>,
139}
140
141/// A manifest can contain both a package and workspace-wide properties
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
143#[serde(rename_all = "kebab-case")]
144pub struct Workspace<Metadata = Value> {
145    /// Relative paths of crates in here
146    #[serde(default)]
147    pub members: Vec<String>,
148
149    /// Members to operate on when in the workspace root.
150    ///
151    /// When specified, `default-members` must expand to a subset of `members`.
152    #[serde(default, skip_serializing_if = "Vec::is_empty")]
153    pub default_members: Vec<String>,
154
155    /// Template for inheritance
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub package: Option<PackageTemplate>,
158
159    /// Ignore these dirs
160    #[serde(default, skip_serializing_if = "Vec::is_empty")]
161    pub exclude: Vec<String>,
162
163    /// Shared info
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub metadata: Option<Metadata>,
166
167    /// Compatibility setting
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub resolver: Option<Resolver>,
170
171    /// Template for `needs_workspace_inheritance`
172    #[serde(default, skip_serializing_if = "DepsSet::is_empty")]
173    pub dependencies: DepsSet,
174
175    /// Workspace-level lint groups
176    #[serde(default, skip_serializing_if = "LintGroups::is_empty")]
177    pub lints: LintGroups,
178}
179
180/// Workspace can predefine properties that can be inherited via `{ workspace = true }` in its member packages.
181#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
182#[serde(rename_all = "kebab-case")]
183#[non_exhaustive]
184pub struct PackageTemplate {
185    /// Deprecated
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub authors: Option<Vec<String>>,
188
189    /// See <https://crates.io/category_slugs>
190    #[serde(default, skip_serializing_if = "Option::is_none")]
191    pub categories: Option<Vec<String>>,
192
193    /// Multi-line text, some people use Markdown here
194    #[serde(default, skip_serializing_if = "Option::is_none")]
195    pub description: Option<String>,
196
197    /// URL
198    #[serde(default, skip_serializing_if = "Option::is_none")]
199    pub documentation: Option<String>,
200
201    /// Opt-in to new Rust behaviors
202    #[serde(default, skip_serializing_if = "Option::is_none")]
203    pub edition: Option<Edition>,
204
205    /// Don't publish these files, relative to workspace
206    #[serde(default, skip_serializing_if = "Option::is_none")]
207    pub exclude: Option<Vec<String>>,
208
209    /// Homepage URL
210    #[serde(default, skip_serializing_if = "Option::is_none")]
211    pub homepage: Option<String>,
212
213    /// Publish these files, relative to workspace
214    #[serde(default, skip_serializing_if = "Option::is_none")]
215    pub include: Option<Vec<String>>,
216
217    /// For search
218    #[serde(default, skip_serializing_if = "Option::is_none")]
219    pub keywords: Option<Vec<String>>,
220
221    /// SPDX
222    #[serde(default, skip_serializing_if = "Option::is_none")]
223    pub license: Option<String>,
224
225    /// If not SPDX
226    #[serde(default, skip_serializing_if = "Option::is_none")]
227    pub license_file: Option<PathBuf>,
228
229    /// Block publishing or choose custom registries
230    #[serde(default, skip_serializing_if = "Publish::is_default")]
231    pub publish: Publish,
232
233    /// Opt-out or custom path, relative to workspace
234    #[serde(default, skip_serializing_if = "OptionalFile::is_default")]
235    pub readme: OptionalFile,
236
237    /// (HTTPS) repository URL
238    #[serde(default, skip_serializing_if = "Option::is_none")]
239    pub repository: Option<String>,
240
241    /// Minimum required rustc version in format `1.99`
242    #[serde(default, skip_serializing_if = "Option::is_none")]
243    pub rust_version: Option<String>,
244
245    /// Package version semver
246    #[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    /// Parse contents from a `Cargo.toml` file on disk.
264    ///
265    /// Calls [`Manifest::complete_from_path`] to discover implicit binaries, etc.
266    /// If needed, it will search the file system for a workspace, and fill in data inherited from the workspace.
267    #[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    /// Warning: this will make an incomplete manifest, which will be missing data and panic when using workspace inheritance.
273    ///
274    /// 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.
275    ///
276    /// If you don't call [`Manifest::complete_from_path`], it may be missing implicit data, and panic if workspace inheritance is used.
277    #[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    /// Warning: this will make an incomplete manifest, which will be missing data and panic when using workspace inheritance.
283    ///
284    /// It parses contents of a `Cargo.toml` file loaded as a string. It's **not** a file name, but file's TOML-syntax content.
285    ///
286    /// For a more reliable method, see `from_path`.
287    ///
288    /// If you don't call [`Manifest::complete_from_path`], it may be missing implicit data, and panic if workspace inheritance is used.
289    #[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    /// Warning: this will make an incomplete manifest, which will be missing data and panic when using workspace inheritance.
298    ///
299    /// Parse `Cargo.toml`, and parse its `[package.metadata]` into a custom Serde-compatible type.
300    ///
301    /// It does not call [`Manifest::complete_from_path`], so may be missing implicit data.
302    #[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            // This is a clumsy implementation of Cargo's rule that missing version defaults publish to false.
314            // Serde just doesn't support such relationship for default field values, so this will be incorrect
315            // for explicit `version = "0.0.0"` and `publish = true`.
316            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    /// Parse contents from `Cargo.toml` file on disk, with custom Serde-compatible metadata type.
324    ///
325    /// Calls [`Manifest::complete_from_path`], so it will load a workspace if necessary.
326    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    /// `Cargo.toml` doesn't contain explicit information about `[lib]` and `[[bin]]`,
337    /// which are inferred based on files on disk.
338    ///
339    /// This scans the disk to make the data in the manifest as complete as possible.
340    ///
341    /// It supports workspace inheritance and will search for a root workspace.
342    /// Use [`Manifest::complete_from_path_and_workspace`] to provide the workspace explicitly.
343    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    /// [`Manifest::complete_from_path`], but allows passing workspace manifest explicitly.
349    ///
350    /// `workspace_manifest_and_path` is the root workspace manifest already parsed,
351    /// and the path is the path to the root workspace's directory.
352    /// If it's `None`, the root workspace will be discovered automatically.
353    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    /// `Cargo.toml` doesn't contain explicit information about `[lib]` and `[[bin]]`,
359    /// which are inferred based on files on disk.
360    ///
361    /// You can provide any implementation of directory scan, which doesn't have to
362    /// be reading straight from disk (might scan a tarball or a git repo, for example).
363    ///
364    /// If `workspace_manifest_and_path` is set, it will inherit from this workspace.
365    /// If it's `None`, it will try to find a workspace if needed.
366    ///
367    /// Call it like `complete_from_abstract_filesystem::<cargo_toml::Value, _>(…)` if the arguments are ambiguous.
368    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            // Manifest may be both a workspace and a package
379            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    /// If `true`, some fields are unavailable. If `false`, it's fully usable as-is.
408    ///
409    /// It is `false` in manifests that use workspace inheritance, but had their data completed from the root manifest already.
410    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(); // 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    /// Used for `cargo doc`
755    #[serde(skip_serializing_if = "Option::is_none")]
756    pub doc: Option<Profile>,
757
758    /// User-suppiled for `cargo --profile=name`
759    #[serde(flatten)]
760    pub custom: BTreeMap<String, Profile>,
761}
762
763impl Profiles {
764    /// Determine whether or not a Profiles struct should be serialized
765    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/// Verbosity of debug info in a [`Profile`]
776#[derive(Debug, 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, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
822#[serde(try_from = "toml::Value")]
823pub enum StripSetting {
824    /// false
825    None,
826    Debuginfo,
827    /// true
828    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/// Handling of LTO in a build profile
859#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
860#[serde(try_from = "toml::Value")]
861pub enum LtoSetting {
862    /// off
863    None,
864    /// false
865    ThinLocal,
866    Thin,
867    /// True
868    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/// Compilation/optimization settings for a workspace
901#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
902#[serde(rename_all = "kebab-case")]
903pub struct Profile {
904    /// num or z, s
905    #[serde(default, skip_serializing_if = "Option::is_none")]
906    pub opt_level: Option<Value>,
907
908    /// 0,1,2 or bool
909    #[serde(default, skip_serializing_if = "Option::is_none")]
910    pub debug: Option<DebugSetting>,
911
912    /// Move debug info to separate files
913    #[serde(default, skip_serializing_if = "Option::is_none")]
914    pub split_debuginfo: Option<String>,
915
916    /// For dynamic libraries
917    #[serde(default, skip_serializing_if = "Option::is_none")]
918    pub rpath: Option<bool>,
919
920    /// Link-time-optimization
921    #[serde(default, skip_serializing_if = "Option::is_none")]
922    pub lto: Option<LtoSetting>,
923
924    /// Extra assertions
925    #[serde(default, skip_serializing_if = "Option::is_none")]
926    pub debug_assertions: Option<bool>,
927
928    /// Parallel compilation
929    #[serde(default, skip_serializing_if = "Option::is_none")]
930    pub codegen_units: Option<u16>,
931
932    /// Handling of panics/unwinding
933    #[serde(default, skip_serializing_if = "Option::is_none")]
934    pub panic: Option<String>,
935
936    /// Support for incremental rebuilds
937    #[serde(default, skip_serializing_if = "Option::is_none")]
938    pub incremental: Option<bool>,
939
940    /// Check integer arithmetic
941    #[serde(default, skip_serializing_if = "Option::is_none")]
942    pub overflow_checks: Option<bool>,
943
944    /// Remove debug info
945    #[serde(default, skip_serializing_if = "Option::is_none")]
946    pub strip: Option<StripSetting>,
947
948    /// Profile overrides for dependencies, `*` is special.
949    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
950    pub package: BTreeMap<String, Value>,
951
952    /// Profile overrides for build dependencies, `*` is special.
953    #[serde(default, skip_serializing_if = "Option::is_none")]
954    pub build_override: Option<Value>,
955
956    /// Only relevant for non-standard profiles
957    #[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")]
963/// Cargo uses the term "target" for both "target platform" and "build target" (the thing to build),
964/// which makes it ambigous.
965/// Here Cargo's bin/lib **target** is renamed to **product**.
966pub struct Product {
967    /// This field points at where the crate is located, relative to the `Cargo.toml`.
968    pub path: Option<String>,
969
970    /// The name of a product is the name of the library or binary that will be generated.
971    /// This is defaulted to the name of the package, with any dashes replaced
972    /// with underscores. (Rust `extern crate` declarations reference this name;
973    /// therefore the value must be a valid Rust identifier to be usable.)
974    pub name: Option<String>,
975
976    /// A flag for enabling unit tests for this product. This is used by `cargo test`.
977    #[serde(default = "default_true", skip_serializing_if = "is_true")]
978    pub test: bool,
979
980    /// A flag for enabling documentation tests for this product. This is only relevant
981    /// for libraries, it has no effect on other sections. This is used by
982    /// `cargo test`.
983    #[serde(default = "default_true", skip_serializing_if = "is_true")]
984    pub doctest: bool,
985
986    /// A flag for enabling benchmarks for this product. This is used by `cargo bench`.
987    #[serde(default = "default_true", skip_serializing_if = "is_true")]
988    pub bench: bool,
989
990    /// A flag for enabling documentation of this product. This is used by `cargo doc`.
991    #[serde(default = "default_true", skip_serializing_if = "is_true")]
992    pub doc: bool,
993
994    /// If the product is meant to be a compiler plugin, this field must be set to true
995    /// for Cargo to correctly compile it and make it available for all dependencies.
996    #[serde(default, skip_serializing_if = "is_false")]
997    pub plugin: bool,
998
999    /// If the product is meant to be a "macros 1.1" procedural macro, this field must
1000    /// be set to true.
1001    #[serde(default, alias = "proc_macro", alias = "proc-macro", skip_serializing_if = "is_false")]
1002    pub proc_macro: bool,
1003
1004    /// If set to false, `cargo test` will omit the `--test` flag to rustc, which
1005    /// stops it from generating a test harness. This is useful when the binary being
1006    /// built manages the test runner itself.
1007    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1008    pub harness: bool,
1009
1010    /// Deprecated. Edition should be set only per package.
1011    ///
1012    /// If set then a product can be configured to use a different edition than the
1013    /// `[package]` is configured to use, perhaps only compiling a library with the
1014    /// 2018 edition or only compiling one unit test with the 2015 edition. By default
1015    /// all products are compiled with the edition specified in `[package]`.
1016    #[serde(default, skip_serializing_if = "Option::is_none")]
1017    pub edition: Option<Edition>,
1018
1019    /// The available options are "dylib", "rlib", "staticlib", "cdylib", and "proc-macro".
1020    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1021    pub crate_type: Vec<String>,
1022
1023    /// The `required-features` field specifies which features the product needs in order to be built.
1024    /// If any of the required features are not selected, the product will be skipped.
1025    /// This is only relevant for the `[[bin]]`, `[[bench]]`, `[[test]]`, and `[[example]]` sections,
1026    /// it has no effect on `[lib]`.
1027    #[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/// Dependencies that are platform-specific or enabled through custom `cfg()`.
1051#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
1052#[serde(rename_all = "kebab-case")]
1053pub struct Target {
1054    /// platform-specific normal deps
1055    #[serde(default)]
1056    pub dependencies: DepsSet,
1057    /// platform-specific dev-only/test-only deps
1058    #[serde(default)]
1059    pub dev_dependencies: DepsSet,
1060    /// platform-specific build-time deps
1061    #[serde(default)]
1062    pub build_dependencies: DepsSet,
1063}
1064
1065/// Dependency definition. Note that this struct doesn't carry it's key/name, which you need to read from its section.
1066///
1067/// It can be simple version number, or detailed settings, or inherited.
1068#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1069#[serde(untagged)]
1070pub enum Dependency {
1071    /// Version requirement (e.g. `^1.5`)
1072    Simple(String),
1073    /// Incomplete data
1074    Inherited(InheritedDependencyDetail), // order is important for serde
1075    /// `{ version = "^1.5", features = ["a", "b"] }` etc.
1076    Detailed(Box<DependencyDetail>),
1077}
1078
1079impl Dependency {
1080    /// Get object with special dependency settings if it's not just a version number.
1081    ///
1082    /// Returns `None` if it's inherited and the value is not available
1083    #[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    /// Panics if inherited value is not available
1093    #[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    /// Returns error if inherited value is not available
1100    ///
1101    /// Makes it detailed otherwise
1102    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    /// Panics if inherited value is not available
1120    ///
1121    /// Version requirement
1122    #[inline]
1123    #[track_caller]
1124    #[must_use]
1125    pub fn req(&self) -> &str {
1126        self.try_req().unwrap()
1127    }
1128
1129    /// Version requirement
1130    ///
1131    /// Returns Error if inherited value is not available
1132    #[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    /// Enable extra features for this dep, in addition to the `default` features controlled via `default_features`.
1143    #[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    /// Is it optional. Note that optional deps can be used as features, unless features use `dep:`/`?` syntax for them.
1154    /// See the [`features`] module for more info.
1155    #[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    /// `Some` if it overrides the package name.
1166    /// If `None`, use the dependency name as the package name.
1167    #[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    /// Git URL of this dependency, if any
1177    #[inline]
1178    #[must_use]
1179    pub fn git(&self) -> Option<&str> {
1180        self.detail()?.git.as_deref()
1181    }
1182
1183    /// Git commit of this dependency, if any
1184    #[inline]
1185    #[must_use]
1186    pub fn git_rev(&self) -> Option<&str> {
1187        self.detail()?.rev.as_deref()
1188    }
1189
1190    /// `true` if it's an usual crates.io dependency,
1191    /// `false` if git/path/alternative registry
1192    #[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                // TODO: allow registry to be set to crates.io explicitly?
1199                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/// When definition of a dependency is more than just a version string.
1213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1214#[serde(rename_all = "kebab-case")]
1215pub struct DependencyDetail {
1216    /// Semver requirement. Note that a plain version number implies this version *or newer* compatible one.
1217    #[serde(skip_serializing_if = "Option::is_none")]
1218    pub version: Option<String>,
1219
1220    /// If `Some`, use this as the crate name instead of `[dependencies]`'s table key.
1221    ///
1222    /// By using this, a crate can have multiple versions of the same dependency.
1223    #[serde(skip_serializing_if = "Option::is_none")]
1224    pub package: Option<String>,
1225
1226    /// Fetch this dependency from a custom 3rd party registry (alias defined in Cargo config), not crates-io.
1227    ///
1228    /// This depends on local cargo configuration. It becomes `registry_index` after the crate is uploaded to a registry.
1229    #[serde(skip_serializing_if = "Option::is_none")]
1230    pub registry: Option<String>,
1231
1232    /// Directly define custom 3rd party registry URL (may be `sparse+https:`) instead of a config nickname.
1233    #[serde(skip_serializing_if = "Option::is_none")]
1234    pub registry_index: Option<String>,
1235
1236    /// This path is usually relative to the crate's manifest, but when using workspace inheritance, it may be relative to the workspace!
1237    ///
1238    /// 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
1239    /// path when inherited from the workspace.
1240    #[serde(skip_serializing_if = "Option::is_none")]
1241    pub path: Option<String>,
1242
1243    /// If true, the dependency has been defined at the workspace level, so the `path` is joined with workspace's base path.
1244    ///
1245    /// This is a field added by this crate, does not exist in TOML.
1246    /// Note that `Dependency::Simple` won't have this flag, even if it was inherited.
1247    #[serde(skip)]
1248    pub inherited: bool,
1249
1250    /// Read dependency from git repo URL, not allowed on crates-io.
1251    #[serde(skip_serializing_if = "Option::is_none")]
1252    pub git: Option<String>,
1253    /// Read dependency from git branch, not allowed on crates-io.
1254    #[serde(skip_serializing_if = "Option::is_none")]
1255    pub branch: Option<String>,
1256    /// Read dependency from git tag, not allowed on crates-io.
1257    #[serde(skip_serializing_if = "Option::is_none")]
1258    pub tag: Option<String>,
1259    /// Read dependency from git commit, not allowed on crates-io.
1260    #[serde(skip_serializing_if = "Option::is_none")]
1261    pub rev: Option<String>,
1262
1263    /// Enable these features of the dependency.
1264    ///
1265    /// Note that Cargo interprets `default` in a special way.
1266    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1267    pub features: Vec<String>,
1268
1269    /// NB: Not allowed at workspace level
1270    ///
1271    /// If not used with `dep:` or `?/` syntax in `[features]`, this also creates an implicit feature.
1272    /// See the [`features`] module for more info.
1273    #[serde(default, skip_serializing_if = "is_false")]
1274    pub optional: bool,
1275
1276    /// Enable the `default` set of features of the dependency (enabled by default).
1277    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1278    pub default_features: bool,
1279
1280    /// Contains the remaining unstable keys and values for the dependency.
1281    #[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, // != bool::default()
1300            package: None,
1301            unstable: BTreeMap::new(),
1302        }
1303    }
1304}
1305
1306/// When a dependency is defined as `{ workspace = true }`,
1307/// and workspace data hasn't been applied yet.
1308#[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/// The `[package]` section of the [`Manifest`]. This is where crate properties are.
1322///
1323/// 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`].
1324///
1325/// You can replace `Metadata` generic type with your own
1326/// to parse into something more useful than a generic toml `Value`
1327#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1328#[serde(rename_all = "kebab-case")]
1329#[non_exhaustive]
1330pub struct Package<Metadata = Value> {
1331    /// Careful: some names are uppercase, case-sensitive. `-` changes to `_` when used as a Rust identifier.
1332    pub name: String,
1333
1334    /// See [the `version()` getter for more info](`Package::version()`).
1335    ///
1336    /// Must parse as semver, e.g. "1.9.0"
1337    ///
1338    /// This field may have unknown value when using workspace inheritance,
1339    /// and when the `Manifest` has been loaded without its workspace.
1340    #[serde(default = "default_version")]
1341    pub version: Inheritable<String>,
1342
1343    /// Package's edition opt-in. Use [`Package::edition()`] to read it.
1344    #[serde(default)]
1345    pub edition: Inheritable<Edition>,
1346
1347    /// MSRV 1.x (beware: does not require semver formatting)
1348    #[serde(default, skip_serializing_if = "Option::is_none")]
1349    pub rust_version: Option<Inheritable<String>>,
1350
1351    /// Build script definition
1352    #[serde(default, skip_serializing_if = "Option::is_none")]
1353    pub build: Option<OptionalFile>,
1354
1355    /// Workspace this package is a member of (`None` if it's implicit)
1356    #[serde(default, skip_serializing_if = "Option::is_none")]
1357    pub workspace: Option<PathBuf>,
1358
1359    /// e.g. `["Author <e@mail>", "etc"]`
1360    ///
1361    /// Deprecated.
1362    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1363    pub authors: Inheritable<Vec<String>>,
1364
1365    /// It doesn't link to anything
1366    #[serde(default, skip_serializing_if = "Option::is_none")]
1367    pub links: Option<String>,
1368
1369    /// A short blurb about the package. This is not rendered in any format when
1370    /// uploaded to crates.io (aka this is not markdown).
1371    #[serde(default, skip_serializing_if = "Option::is_none")]
1372    pub description: Option<Inheritable<String>>,
1373
1374    /// Project's homepage
1375    #[serde(default, skip_serializing_if = "Option::is_none")]
1376    pub homepage: Option<Inheritable<String>>,
1377
1378    /// Path to your custom docs. Unnecssary if you rely on docs.rs.
1379    #[serde(default, skip_serializing_if = "Option::is_none")]
1380    pub documentation: Option<Inheritable<String>>,
1381
1382    /// This points to a file under the package root (relative to this `Cargo.toml`).
1383    /// implied if README.md, README.txt or README exists.
1384    #[serde(default, skip_serializing_if = "Inheritable::is_default")]
1385    pub readme: Inheritable<OptionalFile>,
1386
1387    /// Up to 5, for search
1388    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1389    pub keywords: Inheritable<Vec<String>>,
1390
1391    /// This is a list of up to five categories where this crate would fit.
1392    /// e.g. `["command-line-utilities", "development-tools::cargo-plugins"]`
1393    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1394    pub categories: Inheritable<Vec<String>>,
1395
1396    /// Don't publish these files
1397    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1398    pub exclude: Inheritable<Vec<String>>,
1399
1400    /// Publish these files
1401    #[serde(default, skip_serializing_if = "Inheritable::<Vec<_>>::is_empty")]
1402    pub include: Inheritable<Vec<String>>,
1403
1404    /// e.g. "MIT"
1405    #[serde(default, skip_serializing_if = "Option::is_none")]
1406    pub license: Option<Inheritable<String>>,
1407
1408    /// If `license` is not standard
1409    #[serde(default, skip_serializing_if = "Option::is_none")]
1410    pub license_file: Option<Inheritable<PathBuf>>,
1411
1412    /// (HTTPS) URL to crate's repository
1413    #[serde(default, skip_serializing_if = "Option::is_none")]
1414    pub repository: Option<Inheritable<String>>,
1415
1416    /// The default binary to run by cargo run.
1417    #[serde(default, skip_serializing_if = "Option::is_none")]
1418    pub default_run: Option<String>,
1419
1420    /// Discover binaries from the file system
1421    ///
1422    /// This may be incorrectly set to `true` if the crate uses 2015 edition and has explicit `[[bin]]` sections
1423    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1424    pub autobins: bool,
1425
1426    /// Discover libraries from the file system
1427    ///
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    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    /// This returns `none` even if `Flag(true)` is set.
1757    #[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/// Forbids or selects custom registry
1774#[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/// In badges section of Cargo.toml
1824///
1825/// Mostly obsolete.
1826#[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/// `[badges]` section of `Cargo.toml`, deprecated by crates-io except `maintenance`.
1854#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
1855#[serde(rename_all = "kebab-case")]
1856pub struct Badges {
1857    /// Appveyor: `repository` is required. `branch` is optional; default is `master`
1858    /// `service` is optional; valid values are `github` (default), `bitbucket`, and
1859    /// `gitlab`; `id` is optional; you can specify the appveyor project id if you
1860    /// want to use that instead. `project_name` is optional; use when the repository
1861    /// name differs from the appveyor project name.
1862    #[serde(default, deserialize_with = "ok_or_default")]
1863    pub appveyor: Option<Badge>,
1864
1865    /// Circle CI: `repository` is required. `branch` is optional; default is `master`
1866    #[serde(default, deserialize_with = "ok_or_default")]
1867    pub circle_ci: Option<Badge>,
1868
1869    /// GitLab: `repository` is required. `branch` is optional; default is `master`
1870    #[serde(default, deserialize_with = "ok_or_default")]
1871    pub gitlab: Option<Badge>,
1872
1873    /// Travis CI: `repository` in format `"<user>/<project>"` is required.
1874    /// `branch` is optional; default is `master`
1875    #[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    /// Codecov: `repository` is required. `branch` is optional; default is `master`
1880    /// `service` is optional; valid values are `github` (default), `bitbucket`, and
1881    /// `gitlab`.
1882    #[serde(default, deserialize_with = "ok_or_default")]
1883    pub codecov: Option<Badge>,
1884
1885    /// Coveralls: `repository` is required. `branch` is optional; default is `master`
1886    /// `service` is optional; valid values are `github` (default) and `bitbucket`.
1887    #[serde(default, deserialize_with = "ok_or_default")]
1888    pub coveralls: Option<Badge>,
1889
1890    /// Is it maintained resolution time: `repository` is required.
1891    #[serde(default, deserialize_with = "ok_or_default")]
1892    pub is_it_maintained_issue_resolution: Option<Badge>,
1893
1894    /// Is it maintained percentage of open issues: `repository` is required.
1895    #[serde(default, deserialize_with = "ok_or_default")]
1896    pub is_it_maintained_open_issues: Option<Badge>,
1897
1898    /// Maintenance: `status` is required. Available options are `actively-developed`,
1899    /// `passively-maintained`, `as-is`, `experimental`, `looking-for-maintainer`,
1900    /// `deprecated`, and the default `none`, which displays no badge on crates.io.
1901    ///
1902    /// ```toml
1903    /// [badges]
1904    /// maintenance.status = "as-is"
1905    /// ```
1906    #[serde(default, deserialize_with = "ok_or_default")]
1907    pub maintenance: Maintenance,
1908}
1909
1910impl Badges {
1911    #[allow(deprecated)]
1912    /// Determine whether or not a Profiles struct should be serialized
1913    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/// A [`Badges`] field with [`MaintenanceStatus`].
1927///
1928/// ```toml
1929/// [badges]
1930/// maintenance.status = "experimental"
1931/// ```
1932#[derive(Debug, PartialEq, Eq, Copy, Clone, Default, Serialize, Deserialize)]
1933pub struct Maintenance {
1934    pub status: MaintenanceStatus,
1935}
1936
1937/// Mainly used to deprecate crates.
1938///
1939/// ```toml
1940/// [badges]
1941/// maintenance.status = "deprecated"
1942/// ```
1943#[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/// Edition setting, which opts in to new Rust/Cargo behaviors.
1958#[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    /// 2015
1963    #[serde(rename = "2015")]
1964    #[default]
1965    E2015 = 2015,
1966    /// 2018
1967    #[serde(rename = "2018")]
1968    E2018 = 2018,
1969    /// 2021
1970    #[serde(rename = "2021")]
1971    E2021 = 2021,
1972    /// 2024
1973    #[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    /// Returns minor version (1.x) of the oldest rustc that supports this edition
1990    #[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/// The feature resolver version.
2002///
2003/// Needed in [`Workspace`], but implied by [`Edition`] in packages.
2004#[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    /// The default for editions prior to 2021.
2010    V1 = 1,
2011    /// The default for the 2021 edition.
2012    #[serde(rename = "2")]
2013    V2 = 2,
2014    /// The default for the 2024 edition.
2015    #[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/// Lint definition.
2030#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2031#[serde(from = "LintSerdeParser", into = "LintSerdeParser")]
2032pub struct Lint {
2033    /// allow/warn/deny
2034    pub level: LintLevel,
2035
2036    /// Controls which lints or lint groups override other lint groups.
2037    pub priority: i8,
2038
2039    /// Unstable
2040    pub config: BTreeMap<String, toml::Value>,
2041}
2042
2043/// Internal
2044#[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        /// Controls which lints or lint groups override other lint groups.
2051        #[serde(default)]
2052        priority: i8,
2053
2054        /// Unstable
2055        #[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/// Lint level.
2080#[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    /// `public` indicates whether or not the feature should be visible in documentation, and defaults to true
2098    #[serde(default = "default_true", skip_serializing_if = "is_true")]
2099    pub public: bool,
2100
2101    #[allow(unused)]
2102    /// Add a description to the feature
2103    #[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}