use std::collections::VecDeque;
use std::fmt::{Debug, Display, Formatter};
use petgraph::Direction;
use petgraph::prelude::EdgeRef;
use rustc_hash::FxHashSet;
use version_ranges::Ranges;
use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_pep440::Version;
use crate::{
BuiltDist, Dist, DistRef, Edge, Name, Node, RequestedDist, Resolution, ResolvedDist, SourceDist,
};
pub trait IsBuildBackendError: std::error::Error + Send + Sync + 'static {
fn is_build_backend_error(&self) -> bool;
}
#[derive(Debug)]
pub enum DistErrorKind {
Download,
DownloadAndBuild,
Build,
BuildBackend,
Read,
}
impl DistErrorKind {
pub fn from_requested_dist(dist: &RequestedDist, err: &impl IsBuildBackendError) -> Self {
match dist {
RequestedDist::Installed(_) => Self::Read,
RequestedDist::Installable(dist) => Self::from_dist(dist, err),
}
}
pub fn from_dist(dist: &Dist, err: &impl IsBuildBackendError) -> Self {
if err.is_build_backend_error() {
Self::BuildBackend
} else {
match dist {
Dist::Built(BuiltDist::Path(_)) => Self::Read,
Dist::Source(SourceDist::Path(_) | SourceDist::Directory(_)) => Self::Build,
Dist::Built(_) => Self::Download,
Dist::Source(source_dist) => {
if source_dist.is_local() {
Self::Build
} else {
Self::DownloadAndBuild
}
}
}
}
}
}
impl Display for DistErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Download => f.write_str("Failed to download"),
Self::DownloadAndBuild => f.write_str("Failed to download and build"),
Self::Build => f.write_str("Failed to build"),
Self::BuildBackend => f.write_str("Failed to build"),
Self::Read => f.write_str("Failed to read"),
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct DerivationChain(Vec<DerivationStep>);
impl FromIterator<DerivationStep> for DerivationChain {
fn from_iter<T: IntoIterator<Item = DerivationStep>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
impl DerivationChain {
pub fn from_resolution(resolution: &Resolution, target: DistRef<'_>) -> Option<Self> {
let target = resolution.graph().node_indices().find(|node| {
let Node::Dist {
dist: ResolvedDist::Installable { dist, .. },
..
} = &resolution.graph()[*node]
else {
return false;
};
target == dist.as_ref().into()
})?;
let mut queue = VecDeque::new();
queue.push_back((target, None, None, Vec::new()));
let mut seen = FxHashSet::default();
while let Some((node, extra, group, mut path)) = queue.pop_front() {
if !seen.insert(node) {
continue;
}
match &resolution.graph()[node] {
Node::Root => {
path.reverse();
path.pop();
return Some(Self::from_iter(path));
}
Node::Dist { dist, .. } => {
for edge in resolution.graph().edges_directed(node, Direction::Incoming) {
let mut path = path.clone();
path.push(DerivationStep::new(
dist.name().clone(),
extra.clone(),
group.clone(),
dist.version().cloned(),
Ranges::empty(),
));
let target = edge.source();
let extra = match edge.weight() {
Edge::Optional(extra) => Some(extra.clone()),
_ => None,
};
let group = match edge.weight() {
Edge::Dev(group) => Some(group.clone()),
_ => None,
};
queue.push_back((target, extra, group, path));
}
}
}
}
None
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, DerivationStep> {
self.0.iter()
}
}
impl<'chain> IntoIterator for &'chain DerivationChain {
type Item = &'chain DerivationStep;
type IntoIter = std::slice::Iter<'chain, DerivationStep>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for DerivationChain {
type Item = DerivationStep;
type IntoIter = std::vec::IntoIter<DerivationStep>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DerivationStep {
pub name: PackageName,
pub extra: Option<ExtraName>,
pub group: Option<GroupName>,
pub version: Option<Version>,
pub range: Ranges<Version>,
}
impl DerivationStep {
pub fn new(
name: PackageName,
extra: Option<ExtraName>,
group: Option<GroupName>,
version: Option<Version>,
range: Ranges<Version>,
) -> Self {
Self {
name,
extra,
group,
version,
range,
}
}
}