use cargo_platform::Platform;
use log::trace;
use semver::ReqParseError;
use semver::VersionReq;
use serde::ser;
use serde::Serialize;
use std::rc::Rc;
use crate::core::interning::InternedString;
use crate::core::{PackageId, SourceId, Summary};
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::Config;
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
pub struct Dependency {
inner: Rc<Inner>,
}
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
struct Inner {
name: InternedString,
source_id: SourceId,
registry_id: Option<SourceId>,
req: VersionReq,
specified_req: bool,
kind: Kind,
only_match_name: bool,
explicit_name_in_toml: Option<InternedString>,
optional: bool,
public: bool,
default_features: bool,
features: Vec<InternedString>,
platform: Option<Platform>,
}
#[derive(Serialize)]
struct SerializedDependency<'a> {
name: &'a str,
source: SourceId,
req: String,
kind: Kind,
rename: Option<&'a str>,
optional: bool,
uses_default_features: bool,
features: &'a [InternedString],
target: Option<&'a Platform>,
registry: Option<&'a str>,
}
impl ser::Serialize for Dependency {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let registry_id = self.registry_id();
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(),
target: self.platform(),
rename: self.explicit_name_in_toml().map(|s| s.as_str()),
registry: registry_id.as_ref().map(|sid| sid.url().as_str()),
}
.serialize(s)
}
}
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
pub enum Kind {
Normal,
Development,
Build,
}
fn parse_req_with_deprecated(
name: InternedString,
req: &str,
extra: Option<(PackageId, &Config)>,
) -> CargoResult<VersionReq> {
match VersionReq::parse(req) {
Err(ReqParseError::DeprecatedVersionRequirement(requirement)) => {
let (inside, config) = match extra {
Some(pair) => pair,
None => return Err(ReqParseError::DeprecatedVersionRequirement(requirement).into()),
};
let msg = format!(
"\
parsed version requirement `{}` is no longer valid
Previous versions of Cargo accepted this malformed requirement,
but it is being deprecated. This was found when parsing the manifest
of {} {}, and the correct version requirement is `{}`.
This will soon become a hard error, so it's either recommended to
update to a fixed version or contact the upstream maintainer about
this warning.
",
req,
inside.name(),
inside.version(),
requirement
);
config.shell().warn(&msg)?;
Ok(requirement)
}
Err(e) => {
let err: CargoResult<VersionReq> = Err(e.into());
let v: VersionReq = err.chain_err(|| {
format!(
"failed to parse the version requirement `{}` for dependency `{}`",
req, name
)
})?;
Ok(v)
}
Ok(v) => Ok(v),
}
}
impl ser::Serialize for Kind {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
match *self {
Kind::Normal => None,
Kind::Development => Some("dev"),
Kind::Build => Some("build"),
}
.serialize(s)
}
}
impl Dependency {
pub fn parse(
name: impl Into<InternedString>,
version: Option<&str>,
source_id: SourceId,
inside: PackageId,
config: &Config,
) -> CargoResult<Dependency> {
let name = name.into();
let arg = Some((inside, config));
let (specified_req, version_req) = match version {
Some(v) => (true, parse_req_with_deprecated(name, v, arg)?),
None => (false, VersionReq::any()),
};
let mut ret = Dependency::new_override(name, source_id);
{
let ptr = Rc::make_mut(&mut ret.inner);
ptr.only_match_name = false;
ptr.req = version_req;
ptr.specified_req = specified_req;
}
Ok(ret)
}
pub fn parse_no_deprecated(
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) => (true, parse_req_with_deprecated(name, v, None)?),
None => (false, VersionReq::any()),
};
let mut ret = Dependency::new_override(name, source_id);
{
let ptr = Rc::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: Rc::new(Inner {
name,
source_id,
registry_id: None,
req: VersionReq::any(),
kind: Kind::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,
}),
}
}
pub fn version_req(&self) -> &VersionReq {
&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 {
Rc::make_mut(&mut self.inner).registry_id = Some(registry_id);
self
}
pub fn kind(&self) -> Kind {
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(), Kind::Normal);
}
Rc::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: Kind) -> &mut Dependency {
if self.is_public() {
assert_eq!(kind, Kind::Normal);
}
Rc::make_mut(&mut self.inner).kind = kind;
self
}
pub fn set_features(
&mut self,
features: impl IntoIterator<Item = impl Into<InternedString>>,
) -> &mut Dependency {
Rc::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 {
Rc::make_mut(&mut self.inner).default_features = default_features;
self
}
pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
Rc::make_mut(&mut self.inner).optional = optional;
self
}
pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
Rc::make_mut(&mut self.inner).source_id = id;
self
}
pub fn set_version_req(&mut self, req: VersionReq) -> &mut Dependency {
Rc::make_mut(&mut self.inner).req = req;
self
}
pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
Rc::make_mut(&mut self.inner).platform = platform;
self
}
pub fn set_explicit_name_in_toml(
&mut self,
name: impl Into<InternedString>,
) -> &mut Dependency {
Rc::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());
assert!(self.inner.req.matches(id.version()));
trace!(
"locking dep from `{}` with `{}` at {} to {}",
self.package_name(),
self.version_req(),
self.source_id(),
id
);
self.set_version_req(VersionReq::exact(id.version()))
.set_source_id(id.source_id())
}
pub fn is_locked(&self) -> bool {
self.inner.req.to_string().starts_with('=')
}
pub fn is_transitive(&self) -> bool {
match self.inner.kind {
Kind::Normal | Kind::Build => true,
Kind::Development => false,
}
}
pub fn is_build(&self) -> bool {
match self.inner.kind {
Kind::Build => true,
_ => false,
}
}
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_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
} else {
self.set_source_id(replace_with);
self
}
}
}