use std::{collections::BTreeMap, ops::Index, path::PathBuf};
use petgraph::{
graph::{DiGraph, NodeIndex},
visit::EdgeRef,
};
use wasmer_config::package::PackageId;
use crate::runtime::resolver::{DistributionInfo, PackageInfo};
#[derive(Debug, Clone)]
pub struct Resolution {
pub package: ResolvedPackage,
pub graph: DependencyGraph,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ItemLocation {
pub name: String,
pub package: PackageId,
}
#[derive(Debug, Clone)]
pub struct DependencyGraph {
root: NodeIndex,
graph: DiGraph<Node, Edge>,
packages: BTreeMap<PackageId, NodeIndex>,
}
impl DependencyGraph {
pub(crate) fn new(
root: NodeIndex,
graph: DiGraph<Node, Edge>,
packages: BTreeMap<PackageId, NodeIndex>,
) -> Self {
if cfg!(debug_assertions) {
for (id, index) in &packages {
let node = &graph[*index];
assert_eq!(*id, node.id, "Mismatch for node {index:?}");
}
}
debug_assert!(
packages.values().any(|ix| *ix == root),
"The packages mapping doesn't contain the root node"
);
DependencyGraph {
root,
graph,
packages,
}
}
pub fn root_info(&self) -> &PackageInfo {
let Node { pkg, .. } = &self.graph[self.root];
pkg
}
pub fn id(&self) -> &PackageId {
let Node { id, .. } = &self.graph[self.root];
id
}
pub fn root(&self) -> NodeIndex {
self.root
}
pub fn graph(&self) -> &DiGraph<Node, Edge> {
&self.graph
}
pub fn packages(&self) -> &BTreeMap<PackageId, NodeIndex> {
&self.packages
}
pub fn iter_dependencies(
&self,
) -> impl Iterator<Item = (&'_ PackageId, BTreeMap<&'_ str, &'_ PackageId>)> + '_ {
self.packages.iter().map(move |(id, index)| {
let dependencies: BTreeMap<_, _> = self
.graph
.edges(*index)
.map(|edge_ref| {
(
edge_ref.weight().alias.as_str(),
&self.graph[edge_ref.target()].id,
)
})
.collect();
(id, dependencies)
})
}
pub fn visualise(&self) -> String {
let graph = self.graph.map(|_, node| &node.id, |_, edge| &edge.alias);
petgraph::dot::Dot::new(&graph).to_string()
}
}
impl Index<NodeIndex> for DependencyGraph {
type Output = Node;
#[track_caller]
fn index(&self, index: NodeIndex) -> &Self::Output {
&self.graph[index]
}
}
impl Index<&NodeIndex> for DependencyGraph {
type Output = Node;
#[track_caller]
fn index(&self, index: &NodeIndex) -> &Self::Output {
&self[*index]
}
}
impl Index<&PackageId> for DependencyGraph {
type Output = Node;
#[track_caller]
fn index(&self, index: &PackageId) -> &Self::Output {
let index = self.packages[index];
&self[index]
}
}
impl PartialEq for DependencyGraph {
fn eq(&self, other: &Self) -> bool {
let DependencyGraph {
root,
graph,
packages,
} = self;
let this_root = graph.node_weight(*root);
let other_root = other.graph.node_weight(other.root);
match (this_root, other_root) {
(Some(lhs), Some(rhs)) if lhs == rhs => {}
_ => return false,
}
let _ = packages;
petgraph::algo::is_isomorphic_matching(graph, &other.graph, Node::eq, Edge::eq)
}
}
impl Eq for DependencyGraph {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
pub id: PackageId,
pub pkg: PackageInfo,
pub dist: Option<DistributionInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Edge {
pub alias: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedFileSystemMapping {
pub mount_path: PathBuf,
pub volume_name: String,
pub original_path: Option<String>,
pub package: PackageId,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedPackage {
pub root_package: PackageId,
pub commands: BTreeMap<String, ItemLocation>,
pub entrypoint: Option<String>,
pub filesystem: Vec<ResolvedFileSystemMapping>,
}