pub use camino::Utf8PathBuf as PathBuf;
use semver::Version;
use std::{collections::BTreeMap, fmt, str::FromStr};
mod cmd;
mod de;
mod errors;
#[cfg(feature = "serialize")]
mod ser;
pub use cmd::MetadataCommand;
pub use errors::Error;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PackageId {
pub repr: String,
}
impl fmt::Display for PackageId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.repr, f)
}
}
#[derive(Clone, Debug)]
pub struct Metadata {
pub packages: Vec<Package>,
pub workspace_members: Vec<PackageId>,
pub workspace_default_members: WorkspaceDefaultMembers,
pub resolve: Option<Resolve>,
pub workspace_root: PathBuf,
pub target_directory: PathBuf,
pub workspace_metadata: serde_json::Value,
pub version: usize,
}
impl Metadata {
pub fn root_package(&self) -> Option<&Package> {
if let Some(resolve) = &self.resolve {
let root = resolve.root.as_ref()?;
self.packages.iter().find(|pkg| &pkg.id == root)
} else {
let root_manifest_path = self.workspace_root.join("Cargo.toml");
self.packages
.iter()
.find(|pkg| pkg.manifest_path == root_manifest_path)
}
}
pub fn workspace_packages(&self) -> Vec<&Package> {
self.packages
.iter()
.filter(|&p| self.workspace_members.contains(&p.id))
.collect()
}
pub fn workspace_default_packages(&self) -> Vec<&Package> {
self.packages
.iter()
.filter(|&p| self.workspace_default_members.contains(&p.id))
.collect()
}
}
impl<'a> std::ops::Index<&'a PackageId> for Metadata {
type Output = Package;
fn index(&self, idx: &'a PackageId) -> &Self::Output {
self.packages
.iter()
.find(|p| p.id == *idx)
.unwrap_or_else(|| panic!("no package with this id: {:?}", idx))
}
}
#[derive(Clone, Debug)]
pub struct WorkspaceDefaultMembers(pub Option<Vec<PackageId>>);
impl core::ops::Deref for WorkspaceDefaultMembers {
type Target = [PackageId];
fn deref(&self) -> &Self::Target {
self.0
.as_ref()
.expect("WorkspaceDefaultMembers should only be dereferenced on Cargo versions >= 1.71")
}
}
#[derive(Clone, Debug)]
pub struct Resolve {
pub nodes: Vec<Node>,
pub root: Option<PackageId>,
}
#[derive(Clone, Debug)]
pub struct Node {
pub id: PackageId,
pub deps: Vec<NodeDep>,
pub dependencies: Vec<PackageId>,
pub features: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct NodeDep {
pub name: String,
pub pkg: PackageId,
pub dep_kinds: Vec<DepKindInfo>,
}
#[derive(Clone, Debug)]
pub struct DepKindInfo {
pub kind: DependencyKind,
pub target: Option<String>,
}
#[derive(Eq, PartialEq, Clone, Debug, Copy, Hash)]
pub enum DependencyKind {
Normal,
Development,
Build,
}
#[allow(clippy::derivable_impls)]
impl Default for DependencyKind {
fn default() -> Self {
Self::Normal
}
}
impl fmt::Display for DependencyKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Normal => f.write_str("normal"),
Self::Development => f.write_str("dev"),
Self::Build => f.write_str("build"),
}
}
}
#[derive(Clone, Debug)]
pub struct Dependency {
pub name: String,
pub source: Option<String>,
pub req: semver::VersionReq,
pub kind: DependencyKind,
pub optional: bool,
pub uses_default_features: bool,
pub features: Vec<String>,
pub target: Option<String>,
pub rename: Option<String>,
pub registry: Option<String>,
pub path: Option<camino::Utf8PathBuf>,
}
#[derive(Clone, Debug)]
pub struct Package {
pub name: String,
pub version: Version,
pub authors: Vec<String>,
pub id: PackageId,
pub source: Option<Source>,
pub description: Option<String>,
pub dependencies: Vec<Dependency>,
pub license: Option<String>,
pub license_file: Option<PathBuf>,
pub targets: Vec<Target>,
pub features: BTreeMap<String, Vec<String>>,
pub manifest_path: PathBuf,
pub categories: Vec<String>,
pub keywords: Vec<String>,
pub readme: Option<PathBuf>,
pub repository: Option<String>,
pub homepage: Option<String>,
pub documentation: Option<String>,
pub edition: Edition,
pub metadata: serde_json::Value,
pub links: Option<String>,
pub publish: Option<Vec<String>>,
pub default_run: Option<String>,
pub rust_version: Option<Version>,
}
impl Package {
pub fn license_file(&self) -> Option<PathBuf> {
self.license_file.as_ref().map(|file| {
self.manifest_path
.parent()
.unwrap_or(&self.manifest_path)
.join(file)
})
}
pub fn readme(&self) -> Option<PathBuf> {
self.readme.as_ref().map(|file| {
self.manifest_path
.parent()
.unwrap_or(&self.manifest_path)
.join(file)
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Source {
pub repr: String,
}
impl Source {
pub fn is_crates_io(&self) -> bool {
self.repr == "registry+https://github.com/rust-lang/crates.io-index"
}
}
impl fmt::Display for Source {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.repr, f)
}
}
#[derive(Clone, Debug)]
pub struct Target {
pub name: String,
pub kind: Vec<TargetKind>,
pub crate_types: Vec<CrateType>,
pub required_features: Vec<String>,
pub src_path: PathBuf,
pub edition: Edition,
pub doctest: bool,
pub test: bool,
pub doc: bool,
}
impl Target {
fn is_kind(&self, name: TargetKind) -> bool {
self.kind.iter().any(|kind| kind == &name)
}
pub fn is_lib(&self) -> bool {
self.is_kind(TargetKind::Lib)
}
pub fn is_bin(&self) -> bool {
self.is_kind(TargetKind::Bin)
}
pub fn is_example(&self) -> bool {
self.is_kind(TargetKind::Example)
}
pub fn is_test(&self) -> bool {
self.is_kind(TargetKind::Test)
}
pub fn is_bench(&self) -> bool {
self.is_kind(TargetKind::Bench)
}
pub fn is_custom_build(&self) -> bool {
self.is_kind(TargetKind::CustomBuild)
}
pub fn is_proc_macro(&self) -> bool {
self.is_kind(TargetKind::ProcMacro)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum TargetKind {
Bench,
Bin,
CustomBuild,
CDyLib,
DyLib,
Example,
Lib,
ProcMacro,
RLib,
StaticLib,
Test,
}
impl FromStr for TargetKind {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(match value {
"example" => Self::Example,
"test" => Self::Test,
"bench" => Self::Bench,
"custom-build" => Self::CustomBuild,
"bin" => Self::Bin,
"lib" => Self::Lib,
"rlib" => Self::RLib,
"dylib" => Self::DyLib,
"cdylib" => Self::CDyLib,
"staticlib" => Self::StaticLib,
"proc-macro" => Self::ProcMacro,
x => return Err(format!("unknown target kind {x}")),
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CrateType {
Bin,
CDyLib,
DyLib,
Lib,
ProcMacro,
RLib,
StaticLib,
}
impl FromStr for CrateType {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(match value {
"bin" => Self::Bin,
"lib" => Self::Lib,
"rlib" => Self::RLib,
"dylib" => Self::DyLib,
"cdylib" => Self::CDyLib,
"staticlib" => Self::StaticLib,
"proc-macro" => Self::ProcMacro,
x => return Err(format!("unknown crate type {x}")),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Edition {
E2015,
E2018,
E2021,
E2024,
}
impl Edition {
pub fn as_str(&self) -> &'static str {
match self {
Self::E2015 => "2015",
Self::E2018 => "2018",
Self::E2021 => "2021",
Self::E2024 => "2024",
}
}
}
impl FromStr for Edition {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(match value {
"2015" => Self::E2015,
"2018" => Self::E2018,
"2021" => Self::E2021,
"2024" => Self::E2024,
x => return Err(format!("unknown edition {x}")),
})
}
}
impl fmt::Display for Edition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[allow(clippy::derivable_impls)]
impl Default for Edition {
fn default() -> Self {
Self::E2015
}
}