wasmer_wasix/runtime/resolver/
outputs.rs

1use std::{collections::BTreeMap, ops::Index, path::PathBuf};
2
3use petgraph::{
4    graph::{DiGraph, NodeIndex},
5    visit::EdgeRef,
6};
7use wasmer_config::package::PackageId;
8
9use crate::runtime::resolver::{DistributionInfo, PackageInfo};
10
11#[derive(Debug, Clone)]
12pub struct Resolution {
13    pub package: ResolvedPackage,
14    pub graph: DependencyGraph,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct ItemLocation {
19    /// The item's original name.
20    pub name: String,
21    /// The package this item comes from.
22    pub package: PackageId,
23}
24
25/// An acyclic, directed dependency graph.
26#[derive(Debug, Clone)]
27pub struct DependencyGraph {
28    root: NodeIndex,
29    graph: DiGraph<Node, Edge>,
30    packages: BTreeMap<PackageId, NodeIndex>,
31}
32
33impl DependencyGraph {
34    pub(crate) fn new(
35        root: NodeIndex,
36        graph: DiGraph<Node, Edge>,
37        packages: BTreeMap<PackageId, NodeIndex>,
38    ) -> Self {
39        if cfg!(debug_assertions) {
40            // Note: We assume the packages table correctly maps PackageIds to
41            // node indices as part of the PartialEq implementation.
42            for (id, index) in &packages {
43                let node = &graph[*index];
44                assert_eq!(*id, node.id, "Mismatch for node {index:?}");
45            }
46        }
47        debug_assert!(
48            packages.values().any(|ix| *ix == root),
49            "The packages mapping doesn't contain the root node"
50        );
51
52        DependencyGraph {
53            root,
54            graph,
55            packages,
56        }
57    }
58
59    pub fn root_info(&self) -> &PackageInfo {
60        let Node { pkg, .. } = &self.graph[self.root];
61        pkg
62    }
63
64    pub fn id(&self) -> &PackageId {
65        let Node { id, .. } = &self.graph[self.root];
66        id
67    }
68
69    pub fn root(&self) -> NodeIndex {
70        self.root
71    }
72
73    pub fn graph(&self) -> &DiGraph<Node, Edge> {
74        &self.graph
75    }
76
77    /// Get a mapping from [`PackageId`]s to [`NodeIndex`]s.
78    pub fn packages(&self) -> &BTreeMap<PackageId, NodeIndex> {
79        &self.packages
80    }
81
82    /// Get an iterator over all the packages in this dependency graph and their
83    /// dependency mappings.
84    pub fn iter_dependencies(
85        &self,
86    ) -> impl Iterator<Item = (&'_ PackageId, BTreeMap<&'_ str, &'_ PackageId>)> + '_ {
87        self.packages.iter().map(move |(id, index)| {
88            let dependencies: BTreeMap<_, _> = self
89                .graph
90                .edges(*index)
91                .map(|edge_ref| {
92                    (
93                        edge_ref.weight().alias.as_str(),
94                        &self.graph[edge_ref.target()].id,
95                    )
96                })
97                .collect();
98            (id, dependencies)
99        })
100    }
101
102    /// Visualise this graph as a DOT program.
103    pub fn visualise(&self) -> String {
104        let graph = self.graph.map(|_, node| &node.id, |_, edge| &edge.alias);
105        petgraph::dot::Dot::new(&graph).to_string()
106    }
107}
108
109impl Index<NodeIndex> for DependencyGraph {
110    type Output = Node;
111
112    #[track_caller]
113    fn index(&self, index: NodeIndex) -> &Self::Output {
114        &self.graph[index]
115    }
116}
117
118impl Index<&NodeIndex> for DependencyGraph {
119    type Output = Node;
120
121    #[track_caller]
122    fn index(&self, index: &NodeIndex) -> &Self::Output {
123        &self[*index]
124    }
125}
126
127impl Index<&PackageId> for DependencyGraph {
128    type Output = Node;
129
130    #[track_caller]
131    fn index(&self, index: &PackageId) -> &Self::Output {
132        let index = self.packages[index];
133        &self[index]
134    }
135}
136
137impl PartialEq for DependencyGraph {
138    fn eq(&self, other: &Self) -> bool {
139        let DependencyGraph {
140            root,
141            graph,
142            packages,
143        } = self;
144
145        // Make sure their roots are the same package
146        let this_root = graph.node_weight(*root);
147        let other_root = other.graph.node_weight(other.root);
148
149        match (this_root, other_root) {
150            (Some(lhs), Some(rhs)) if lhs == rhs => {}
151            _ => return false,
152        }
153
154        // the packages table *should* just be an optimisation. We've checked
155        // it is valid as part of DependencyGraph::new() and the entire graph
156        // is immutable, so it's fine to ignore.
157        let _ = packages;
158
159        // Most importantly, the graphs should be "the same" (i.e. if a node
160        // in one graph is a
161        // nodes are connected to the same nodes in both)
162        petgraph::algo::is_isomorphic_matching(graph, &other.graph, Node::eq, Edge::eq)
163    }
164}
165
166impl Eq for DependencyGraph {}
167
168/// A node in the [`DependencyGraph`].
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct Node {
171    pub id: PackageId,
172    pub pkg: PackageInfo,
173    /// Information about how the package is distributed.
174    ///
175    /// This will only ever be missing for the root package.
176    pub dist: Option<DistributionInfo>,
177}
178
179/// An edge in the [`DependencyGraph`].
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub struct Edge {
182    /// The name used by the package when referring to this dependency.
183    pub alias: String,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct ResolvedFileSystemMapping {
188    // TODO: Change this to a new type that isn't coupled to the OS
189    pub mount_path: PathBuf,
190    pub volume_name: String,
191    pub original_path: Option<String>,
192    pub package: PackageId,
193}
194
195/// A package that has been resolved, but is not yet runnable.
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct ResolvedPackage {
198    pub root_package: PackageId,
199    pub commands: BTreeMap<String, ItemLocation>,
200    pub entrypoint: Option<String>,
201    /// A mapping from paths to the volumes that should be mounted there.
202    /// Note: mappings at the start of the list obscure mappings at the end of the list
203    /// thus this list represents an inheritance tree
204    pub filesystem: Vec<ResolvedFileSystemMapping>,
205}