use std::fmt::{Display, Formatter};
use anyhow::Context;
use guppy::graph::{PackageGraph, PackageMetadata, PackageSource};
use guppy::{PackageId, Version};
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
pub(crate) struct PackageIdSpecification {
pub(crate) source: Option<String>,
pub(crate) name: String,
pub(crate) version: Option<Version>,
}
impl PackageIdSpecification {
pub fn from_package_id(
package_id: &PackageId,
package_graph: &PackageGraph,
) -> Result<Self, anyhow::Error> {
let package_metadata = package_graph.metadata(package_id).with_context(|| {
format!(
"`{}` doesn't appear in the package graph",
package_id.repr()
)
})?;
Ok(Self::from_package_metadata(&package_metadata))
}
pub fn from_package_metadata(metadata: &PackageMetadata) -> Self {
let source = match metadata.source() {
PackageSource::Workspace(source) | PackageSource::Path(source) => {
let source = source.strip_prefix("path+").unwrap_or(source);
if source.as_str().is_empty() {
source.to_string()
} else {
let source = if source.is_relative() {
metadata.graph().workspace().root().join(source).to_string()
} else {
source.to_string()
};
format!("file://{source}")
}
}
PackageSource::External(source) => {
let s = if let Some(source) = source.strip_prefix("git+") {
source
} else if let Some(source) = source.strip_prefix("registry+") {
source
} else {
source
};
s.split('?').next().unwrap().to_owned()
}
};
let source = if source.is_empty() {
None
} else {
Some(source)
};
let name = metadata.name().to_owned();
let version = Some(metadata.version().to_owned());
Self {
source,
name,
version,
}
}
}
impl Display for PackageIdSpecification {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(source) = &self.source {
let source = source.split('#').next().unwrap();
write!(f, "{source}#")?;
}
write!(f, "{}", &self.name)?;
if let Some(version) = &self.version {
write!(f, "@{version}")?;
}
Ok(())
}
}