use cargo_platform::Platform;
use semver::VersionReq;
use serde::ser;
use serde::Serialize;
use std::borrow::Cow;
use std::fmt;
use std::path::PathBuf;
use std::sync::Arc;
use tracing::trace;
use crate::core::compiler::{CompileKind, CompileTarget};
use crate::core::{CliUnstable, Feature, Features, PackageId, SourceId, Summary};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::OptVersionReq;
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Dependency {
inner: Arc<Inner>,
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
struct Inner {
name: InternedString,
source_id: SourceId,
registry_id: Option<SourceId>,
req: OptVersionReq,
specified_req: bool,
kind: DepKind,
only_match_name: bool,
explicit_name_in_toml: Option<InternedString>,
optional: bool,
public: bool,
default_features: bool,
features: Vec<InternedString>,
artifact: Option<Artifact>,
platform: Option<Platform>,
}
#[derive(Serialize)]
pub struct SerializedDependency {
name: InternedString,
source: SourceId,
req: String,
kind: DepKind,
rename: Option<InternedString>,
optional: bool,
uses_default_features: bool,
features: Vec<InternedString>,
#[serde(skip_serializing_if = "Option::is_none")]
artifact: Option<Artifact>,
target: Option<Platform>,
registry: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
path: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
public: Option<bool>,
}
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
pub enum DepKind {
Normal,
Development,
Build,
}
impl DepKind {
pub fn kind_table(&self) -> &'static str {
match self {
DepKind::Normal => "dependencies",
DepKind::Development => "dev-dependencies",
DepKind::Build => "build-dependencies",
}
}
}
impl ser::Serialize for DepKind {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
match *self {
DepKind::Normal => None,
DepKind::Development => Some("dev"),
DepKind::Build => Some("build"),
}
.serialize(s)
}
}
impl Dependency {
pub fn parse(
name: impl Into<InternedString>,
version: Option<&str>,
source_id: SourceId,
) -> CargoResult<Dependency> {
let name = name.into();
let (specified_req, version_req) = match version {
Some(v) => match VersionReq::parse(v) {
Ok(req) => (true, OptVersionReq::Req(req)),
Err(err) => {
return Err(anyhow::Error::new(err).context(format!(
"failed to parse the version requirement `{}` for dependency `{}`",
v, name,
)))
}
},
None => (false, OptVersionReq::Any),
};
let mut ret = Dependency::new_override(name, source_id);
{
let ptr = Arc::make_mut(&mut ret.inner);
ptr.only_match_name = false;
ptr.req = version_req;
ptr.specified_req = specified_req;
}
Ok(ret)
}
pub fn new_override(name: InternedString, source_id: SourceId) -> Dependency {
assert!(!name.is_empty());
Dependency {
inner: Arc::new(Inner {
name,
source_id,
registry_id: None,
req: OptVersionReq::Any,
kind: DepKind::Normal,
only_match_name: true,
optional: false,
public: false,
features: Vec::new(),
default_features: true,
specified_req: false,
platform: None,
explicit_name_in_toml: None,
artifact: None,
}),
}
}
pub fn serialized(
&self,
unstable_flags: &CliUnstable,
features: &Features,
) -> SerializedDependency {
SerializedDependency {
name: self.package_name(),
source: self.source_id(),
req: self.version_req().to_string(),
kind: self.kind(),
optional: self.is_optional(),
uses_default_features: self.uses_default_features(),
features: self.features().to_vec(),
target: self.inner.platform.clone(),
rename: self.explicit_name_in_toml(),
registry: self.registry_id().as_ref().map(|sid| sid.url().to_string()),
path: self.source_id().local_path(),
artifact: self.inner.artifact.clone(),
public: if unstable_flags.public_dependency
|| features.is_enabled(Feature::public_dependency())
{
Some(self.inner.public)
} else {
None
},
}
}
pub fn version_req(&self) -> &OptVersionReq {
&self.inner.req
}
pub fn name_in_toml(&self) -> InternedString {
self.explicit_name_in_toml().unwrap_or(self.inner.name)
}
pub fn package_name(&self) -> InternedString {
self.inner.name
}
pub fn source_id(&self) -> SourceId {
self.inner.source_id
}
pub fn registry_id(&self) -> Option<SourceId> {
self.inner.registry_id
}
pub fn set_registry_id(&mut self, registry_id: SourceId) -> &mut Dependency {
Arc::make_mut(&mut self.inner).registry_id = Some(registry_id);
self
}
pub fn kind(&self) -> DepKind {
self.inner.kind
}
pub fn is_public(&self) -> bool {
self.inner.public
}
pub fn set_public(&mut self, public: bool) -> &mut Dependency {
if public {
assert_eq!(self.kind(), DepKind::Normal);
}
Arc::make_mut(&mut self.inner).public = public;
self
}
pub fn specified_req(&self) -> bool {
self.inner.specified_req
}
pub fn platform(&self) -> Option<&Platform> {
self.inner.platform.as_ref()
}
pub fn explicit_name_in_toml(&self) -> Option<InternedString> {
self.inner.explicit_name_in_toml
}
pub fn set_kind(&mut self, kind: DepKind) -> &mut Dependency {
if self.is_public() {
assert_eq!(kind, DepKind::Normal);
}
Arc::make_mut(&mut self.inner).kind = kind;
self
}
pub fn set_features(
&mut self,
features: impl IntoIterator<Item = impl Into<InternedString>>,
) -> &mut Dependency {
Arc::make_mut(&mut self.inner).features = features.into_iter().map(|s| s.into()).collect();
self
}
pub fn set_default_features(&mut self, default_features: bool) -> &mut Dependency {
Arc::make_mut(&mut self.inner).default_features = default_features;
self
}
pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
Arc::make_mut(&mut self.inner).optional = optional;
self
}
pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
Arc::make_mut(&mut self.inner).source_id = id;
self
}
pub fn set_version_req(&mut self, req: OptVersionReq) -> &mut Dependency {
Arc::make_mut(&mut self.inner).req = req;
self
}
pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
Arc::make_mut(&mut self.inner).platform = platform;
self
}
pub fn set_explicit_name_in_toml(
&mut self,
name: impl Into<InternedString>,
) -> &mut Dependency {
Arc::make_mut(&mut self.inner).explicit_name_in_toml = Some(name.into());
self
}
pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
assert_eq!(self.inner.source_id, id.source_id());
trace!(
"locking dep from `{}` with `{}` at {} to {}",
self.package_name(),
self.version_req(),
self.source_id(),
id
);
let me = Arc::make_mut(&mut self.inner);
me.req.lock_to(id.version());
me.source_id = me.source_id.with_precise_from(id.source_id());
self
}
pub fn lock_version(&mut self, version: &semver::Version) -> &mut Dependency {
let me = Arc::make_mut(&mut self.inner);
me.req.lock_to(version);
self
}
pub fn is_locked(&self) -> bool {
self.inner.req.is_locked()
}
pub fn is_transitive(&self) -> bool {
match self.inner.kind {
DepKind::Normal | DepKind::Build => true,
DepKind::Development => false,
}
}
pub fn is_build(&self) -> bool {
matches!(self.inner.kind, DepKind::Build)
}
pub fn is_optional(&self) -> bool {
self.inner.optional
}
pub fn uses_default_features(&self) -> bool {
self.inner.default_features
}
pub fn features(&self) -> &[InternedString] {
&self.inner.features
}
pub fn matches(&self, sum: &Summary) -> bool {
self.matches_id(sum.package_id())
}
pub fn matches_prerelease(&self, sum: &Summary) -> bool {
let id = sum.package_id();
self.inner.name == id.name()
&& (self.inner.only_match_name
|| (self.inner.req.matches_prerelease(id.version())
&& self.inner.source_id == id.source_id()))
}
pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
self.package_name() == id.name() && self.version_req().matches(id.version())
}
pub fn matches_id(&self, id: PackageId) -> bool {
self.inner.name == id.name()
&& (self.inner.only_match_name
|| (self.inner.req.matches(id.version()) && self.inner.source_id == id.source_id()))
}
pub fn map_source(mut self, to_replace: SourceId, replace_with: SourceId) -> Dependency {
if self.source_id() == to_replace {
self.set_source_id(replace_with);
}
self
}
pub(crate) fn set_artifact(&mut self, artifact: Artifact) {
Arc::make_mut(&mut self.inner).artifact = Some(artifact);
}
pub(crate) fn artifact(&self) -> Option<&Artifact> {
self.inner.artifact.as_ref()
}
pub(crate) fn maybe_lib(&self) -> bool {
self.artifact().map(|a| a.is_lib).unwrap_or(true)
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Artifact {
inner: Arc<Vec<ArtifactKind>>,
is_lib: bool,
target: Option<ArtifactTarget>,
}
#[derive(Serialize)]
pub struct SerializedArtifact<'a> {
kinds: &'a [ArtifactKind],
lib: bool,
target: Option<&'a str>,
}
impl ser::Serialize for Artifact {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
SerializedArtifact {
kinds: self.kinds(),
lib: self.is_lib,
target: self.target.as_ref().map(ArtifactTarget::as_str),
}
.serialize(s)
}
}
impl Artifact {
pub(crate) fn parse(
artifacts: &[impl AsRef<str>],
is_lib: bool,
target: Option<&str>,
) -> CargoResult<Self> {
let kinds = ArtifactKind::validate(
artifacts
.iter()
.map(|s| ArtifactKind::parse(s.as_ref()))
.collect::<Result<Vec<_>, _>>()?,
)?;
Ok(Artifact {
inner: Arc::new(kinds),
is_lib,
target: target.map(ArtifactTarget::parse).transpose()?,
})
}
pub(crate) fn kinds(&self) -> &[ArtifactKind] {
&self.inner
}
pub(crate) fn is_lib(&self) -> bool {
self.is_lib
}
pub(crate) fn target(&self) -> Option<ArtifactTarget> {
self.target
}
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug)]
pub enum ArtifactTarget {
BuildDependencyAssumeTarget,
Force(CompileTarget),
}
impl ArtifactTarget {
pub fn parse(target: &str) -> CargoResult<ArtifactTarget> {
Ok(match target {
"target" => ArtifactTarget::BuildDependencyAssumeTarget,
name => ArtifactTarget::Force(CompileTarget::new(name)?),
})
}
pub fn as_str(&self) -> &str {
match self {
ArtifactTarget::BuildDependencyAssumeTarget => "target",
ArtifactTarget::Force(target) => target.rustc_target().as_str(),
}
}
pub fn to_compile_kind(&self) -> Option<CompileKind> {
self.to_compile_target().map(CompileKind::Target)
}
pub fn to_compile_target(&self) -> Option<CompileTarget> {
match self {
ArtifactTarget::BuildDependencyAssumeTarget => None,
ArtifactTarget::Force(target) => Some(*target),
}
}
pub(crate) fn to_resolved_compile_kind(
&self,
root_unit_compile_kind: CompileKind,
) -> CompileKind {
match self {
ArtifactTarget::Force(target) => CompileKind::Target(*target),
ArtifactTarget::BuildDependencyAssumeTarget => root_unit_compile_kind,
}
}
pub(crate) fn to_resolved_compile_target(
&self,
root_unit_compile_kind: CompileKind,
) -> Option<CompileTarget> {
match self.to_resolved_compile_kind(root_unit_compile_kind) {
CompileKind::Host => None,
CompileKind::Target(target) => Some(target),
}
}
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug)]
pub enum ArtifactKind {
AllBinaries,
SelectedBinary(InternedString),
Cdylib,
Staticlib,
}
impl ser::Serialize for ArtifactKind {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
self.as_str().serialize(s)
}
}
impl fmt::Display for ArtifactKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.as_str())
}
}
impl ArtifactKind {
pub fn crate_type(&self) -> &'static str {
match self {
ArtifactKind::AllBinaries | ArtifactKind::SelectedBinary(_) => "bin",
ArtifactKind::Cdylib => "cdylib",
ArtifactKind::Staticlib => "staticlib",
}
}
pub fn as_str(&self) -> Cow<'static, str> {
match *self {
ArtifactKind::SelectedBinary(name) => format!("bin:{}", name.as_str()).into(),
_ => self.crate_type().into(),
}
}
pub fn parse(kind: &str) -> CargoResult<Self> {
Ok(match kind {
"bin" => ArtifactKind::AllBinaries,
"cdylib" => ArtifactKind::Cdylib,
"staticlib" => ArtifactKind::Staticlib,
_ => {
return kind
.strip_prefix("bin:")
.map(|bin_name| ArtifactKind::SelectedBinary(InternedString::new(bin_name)))
.ok_or_else(|| anyhow::anyhow!("'{}' is not a valid artifact specifier", kind))
}
})
}
fn validate(kinds: Vec<ArtifactKind>) -> CargoResult<Vec<ArtifactKind>> {
if kinds.iter().any(|k| matches!(k, ArtifactKind::AllBinaries))
&& kinds
.iter()
.any(|k| matches!(k, ArtifactKind::SelectedBinary(_)))
{
anyhow::bail!("Cannot specify both 'bin' and 'bin:<name>' binary artifacts, as 'bin' selects all available binaries.");
}
let mut kinds_without_dupes = kinds.clone();
kinds_without_dupes.sort();
kinds_without_dupes.dedup();
let num_dupes = kinds.len() - kinds_without_dupes.len();
if num_dupes != 0 {
anyhow::bail!(
"Found {} duplicate binary artifact{}",
num_dupes,
(num_dupes > 1).then(|| "s").unwrap_or("")
);
}
Ok(kinds)
}
}