Skip to content

Commit 2e007b5

Browse files
committed
refactor(tree): Use a newtype to track node indexes
The primary goal is to make the code more type safe / easier to follow. This also can allow tracking debug information.
1 parent 0659425 commit 2e007b5

File tree

3 files changed

+66
-55
lines changed

3 files changed

+66
-55
lines changed

src/cargo/ops/tree/format/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33
use anyhow::{bail, Error};
44

55
use self::parse::{Parser, RawChunk};
6-
use super::{Graph, Node};
6+
use super::{Graph, Node, NodeId};
77

88
mod parse;
99

@@ -41,7 +41,7 @@ impl Pattern {
4141
Ok(Pattern(chunks))
4242
}
4343

44-
pub fn display<'a>(&'a self, graph: &'a Graph<'a>, node_index: usize) -> Display<'a> {
44+
pub fn display<'a>(&'a self, graph: &'a Graph<'a>, node_index: NodeId) -> Display<'a> {
4545
Display {
4646
pattern: self,
4747
graph,
@@ -53,7 +53,7 @@ impl Pattern {
5353
pub struct Display<'a> {
5454
pattern: &'a Pattern,
5555
graph: &'a Graph<'a>,
56-
node_index: usize,
56+
node_index: NodeId,
5757
}
5858

5959
impl<'a> fmt::Display for Display<'a> {

src/cargo/ops/tree/graph.rs

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ use crate::util::interning::{InternedString, INTERNED_DEFAULT};
1010
use crate::util::CargoResult;
1111
use std::collections::{HashMap, HashSet};
1212

13+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
14+
pub struct NodeId {
15+
index: usize,
16+
}
17+
18+
impl NodeId {
19+
fn with_index(index: usize) -> Self {
20+
Self { index }
21+
}
22+
}
23+
1324
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1425
pub enum Node {
1526
Package {
@@ -20,7 +31,7 @@ pub enum Node {
2031
},
2132
Feature {
2233
/// Index of the package node this feature is for.
23-
node_index: usize,
34+
node_index: NodeId,
2435
/// Name of the feature.
2536
name: InternedString,
2637
},
@@ -29,15 +40,15 @@ pub enum Node {
2940
#[derive(Debug, Copy, Hash, Eq, Clone, PartialEq)]
3041
pub struct Edge {
3142
kind: EdgeKind,
32-
node: usize,
43+
node: NodeId,
3344
}
3445

3546
impl Edge {
3647
pub fn kind(&self) -> EdgeKind {
3748
self.kind
3849
}
3950

40-
pub fn node(&self) -> usize {
51+
pub fn node(&self) -> NodeId {
4152
self.node
4253
}
4354
}
@@ -94,19 +105,19 @@ pub struct Graph<'a> {
94105
/// sync.
95106
edges: Vec<Edges>,
96107
/// Index maps a node to an index, for fast lookup.
97-
index: HashMap<Node, usize>,
108+
index: HashMap<Node, NodeId>,
98109
/// Map for looking up packages.
99110
package_map: HashMap<PackageId, &'a Package>,
100111
/// Set of indexes of feature nodes that were added via the command-line.
101112
///
102113
/// For example `--features foo` will mark the "foo" node here.
103-
cli_features: HashSet<usize>,
114+
cli_features: HashSet<NodeId>,
104115
/// Map of dependency names, used for building internal feature map for
105116
/// `dep_name/feat_name` syntax.
106117
///
107118
/// Key is the index of a package node, value is a map of `dep_name` to a
108119
/// set of `(pkg_node_index, is_optional)`.
109-
dep_name_map: HashMap<usize, HashMap<InternedString, HashSet<(usize, bool)>>>,
120+
dep_name_map: HashMap<NodeId, HashMap<InternedString, HashSet<(NodeId, bool)>>>,
110121
}
111122

112123
impl<'a> Graph<'a> {
@@ -122,52 +133,52 @@ impl<'a> Graph<'a> {
122133
}
123134

124135
/// Adds a new node to the graph, returning its new index.
125-
fn add_node(&mut self, node: Node) -> usize {
126-
let from_index = self.nodes.len();
136+
fn add_node(&mut self, node: Node) -> NodeId {
137+
let from_index = NodeId::with_index(self.nodes.len());
127138
self.nodes.push(node);
128139
self.edges.push(Edges::new());
129140
self.index.insert(self.node(from_index).clone(), from_index);
130141
from_index
131142
}
132143

133144
/// Returns a list of nodes the given node index points to for the given kind.
134-
pub fn edges_of_kind(&self, from: usize, kind: &EdgeKind) -> Vec<Edge> {
145+
pub fn edges_of_kind(&self, from: NodeId, kind: &EdgeKind) -> Vec<Edge> {
135146
let edges = self.edges(from).of_kind(kind);
136147
// Created a sorted list for consistent output.
137148
let mut edges = edges.to_owned();
138149
edges.sort_unstable_by(|a, b| self.node(a.node()).cmp(&self.node(b.node())));
139150
edges
140151
}
141152

142-
fn edges(&self, from: usize) -> &Edges {
143-
&self.edges[from]
153+
fn edges(&self, from: NodeId) -> &Edges {
154+
&self.edges[from.index]
144155
}
145156

146-
fn edges_mut(&mut self, from: usize) -> &mut Edges {
147-
&mut self.edges[from]
157+
fn edges_mut(&mut self, from: NodeId) -> &mut Edges {
158+
&mut self.edges[from.index]
148159
}
149160

150161
/// Returns `true` if the given node has any outgoing edges.
151-
pub fn has_outgoing_edges(&self, index: usize) -> bool {
162+
pub fn has_outgoing_edges(&self, index: NodeId) -> bool {
152163
!self.edges(index).is_empty()
153164
}
154165

155166
/// Gets a node by index.
156-
pub fn node(&self, index: usize) -> &Node {
157-
&self.nodes[index]
167+
pub fn node(&self, index: NodeId) -> &Node {
168+
&self.nodes[index.index]
158169
}
159170

160171
/// Given a slice of `PackageIds`, returns the indexes of all nodes that match.
161-
pub fn indexes_from_ids(&self, package_ids: &[PackageId]) -> Vec<usize> {
162-
let mut result: Vec<(&Node, usize)> = self
172+
pub fn indexes_from_ids(&self, package_ids: &[PackageId]) -> Vec<NodeId> {
173+
let mut result: Vec<(&Node, NodeId)> = self
163174
.nodes
164175
.iter()
165176
.enumerate()
166177
.filter(|(_i, node)| match node {
167178
Node::Package { package_id, .. } => package_ids.contains(package_id),
168179
_ => false,
169180
})
170-
.map(|(i, node)| (node, i))
181+
.map(|(i, node)| (node, NodeId::with_index(i)))
171182
.collect();
172183
// Sort for consistent output (the same command should always return
173184
// the same output). "unstable" since nodes should always be unique.
@@ -179,7 +190,7 @@ impl<'a> Graph<'a> {
179190
self.package_map[&id]
180191
}
181192

182-
fn package_id_for_index(&self, index: usize) -> PackageId {
193+
fn package_id_for_index(&self, index: NodeId) -> PackageId {
183194
match self.node(index) {
184195
Node::Package { package_id, .. } => *package_id,
185196
Node::Feature { .. } => panic!("unexpected feature node"),
@@ -188,32 +199,32 @@ impl<'a> Graph<'a> {
188199

189200
/// Returns `true` if the given feature node index is a feature enabled
190201
/// via the command-line.
191-
pub fn is_cli_feature(&self, index: usize) -> bool {
202+
pub fn is_cli_feature(&self, index: NodeId) -> bool {
192203
self.cli_features.contains(&index)
193204
}
194205

195206
/// Returns a new graph by removing all nodes not reachable from the
196207
/// given nodes.
197-
pub fn from_reachable(&self, roots: &[usize]) -> Graph<'a> {
208+
pub fn from_reachable(&self, roots: &[NodeId]) -> Graph<'a> {
198209
// Graph built with features does not (yet) support --duplicates.
199210
assert!(self.dep_name_map.is_empty());
200211
let mut new_graph = Graph::new(self.package_map.clone());
201212
// Maps old index to new index. None if not yet visited.
202-
let mut remap: Vec<Option<usize>> = vec![None; self.nodes.len()];
213+
let mut remap: Vec<Option<NodeId>> = vec![None; self.nodes.len()];
203214

204215
fn visit(
205216
graph: &Graph<'_>,
206217
new_graph: &mut Graph<'_>,
207-
remap: &mut Vec<Option<usize>>,
208-
index: usize,
209-
) -> usize {
210-
if let Some(new_index) = remap[index] {
218+
remap: &mut Vec<Option<NodeId>>,
219+
index: NodeId,
220+
) -> NodeId {
221+
if let Some(new_index) = remap[index.index] {
211222
// Already visited.
212223
return new_index;
213224
}
214225
let node = graph.node(index).clone();
215226
let new_from = new_graph.add_node(node);
216-
remap[index] = Some(new_from);
227+
remap[index.index] = Some(new_from);
217228
// Visit dependencies.
218229
for edge in graph.edges(index).all() {
219230
let new_to_index = visit(graph, new_graph, remap, edge.node());
@@ -241,32 +252,32 @@ impl<'a> Graph<'a> {
241252
for edge in node_edges.all() {
242253
let new_edge = Edge {
243254
kind: edge.kind(),
244-
node: from_idx,
255+
node: NodeId::with_index(from_idx),
245256
};
246-
new_edges[edge.node()].add_edge(new_edge);
257+
new_edges[edge.node().index].add_edge(new_edge);
247258
}
248259
}
249260
self.edges = new_edges;
250261
}
251262

252263
/// Returns a list of nodes that are considered "duplicates" (same package
253264
/// name, with different versions/features/source/etc.).
254-
pub fn find_duplicates(&self) -> Vec<usize> {
265+
pub fn find_duplicates(&self) -> Vec<NodeId> {
255266
// Graph built with features does not (yet) support --duplicates.
256267
assert!(self.dep_name_map.is_empty());
257268

258-
// Collect a map of package name to Vec<(&Node, usize)>.
269+
// Collect a map of package name to Vec<(&Node, NodeId)>.
259270
let mut packages = HashMap::new();
260271
for (i, node) in self.nodes.iter().enumerate() {
261272
if let Node::Package { package_id, .. } = node {
262273
packages
263274
.entry(package_id.name())
264275
.or_insert_with(Vec::new)
265-
.push((node, i));
276+
.push((node, NodeId::with_index(i)));
266277
}
267278
}
268279

269-
let mut dupes: Vec<(&Node, usize)> = packages
280+
let mut dupes: Vec<(&Node, NodeId)> = packages
270281
.into_iter()
271282
.filter(|(_name, indexes)| {
272283
indexes
@@ -356,7 +367,7 @@ fn add_pkg(
356367
target_data: &RustcTargetData<'_>,
357368
requested_kind: CompileKind,
358369
opts: &TreeOptions,
359-
) -> usize {
370+
) -> NodeId {
360371
let node_features = resolved_features.activated_features(package_id, features_for);
361372
let node_kind = match features_for {
362373
FeaturesFor::HostDep => CompileKind::Host,
@@ -373,7 +384,7 @@ fn add_pkg(
373384
}
374385
let from_index = graph.add_node(node);
375386
// Compute the dep name map which is later used for foo/bar feature lookups.
376-
let mut dep_name_map: HashMap<InternedString, HashSet<(usize, bool)>> = HashMap::new();
387+
let mut dep_name_map: HashMap<InternedString, HashSet<(NodeId, bool)>> = HashMap::new();
377388
let mut deps: Vec<_> = resolve.deps(package_id).collect();
378389
deps.sort_unstable_by_key(|(dep_id, _)| *dep_id);
379390
let show_all_targets = opts.target == super::Target::All;
@@ -512,9 +523,9 @@ fn add_pkg(
512523
fn add_feature(
513524
graph: &mut Graph<'_>,
514525
name: InternedString,
515-
from: Option<usize>,
526+
from: Option<NodeId>,
516527
to: Edge,
517-
) -> (bool, usize) {
528+
) -> (bool, NodeId) {
518529
// `to` *must* point to a package node.
519530
assert!(matches! {graph.node(to.node()), Node::Package{..}});
520531
let node = Node::Feature {
@@ -547,7 +558,7 @@ fn add_feature(
547558
/// `--invert`.
548559
fn add_cli_features(
549560
graph: &mut Graph<'_>,
550-
package_index: usize,
561+
package_index: NodeId,
551562
cli_features: &CliFeatures,
552563
feature_map: &FeatureMap,
553564
) {
@@ -596,7 +607,7 @@ fn add_cli_features(
596607
"missing dep graph connection for CLI feature `{}` for member {:?}\n\
597608
Please file a bug report at https://github.com/rust-lang/cargo/issues",
598609
fv,
599-
graph.nodes.get(package_index)
610+
graph.nodes.get(package_index.index)
600611
);
601612
}
602613
};
@@ -626,15 +637,15 @@ fn add_cli_features(
626637
/// for every package.
627638
fn add_internal_features(graph: &mut Graph<'_>, resolve: &Resolve) {
628639
// Collect features already activated by dependencies or command-line.
629-
let feature_nodes: Vec<(PackageId, usize, usize, InternedString)> = graph
640+
let feature_nodes: Vec<(PackageId, NodeId, NodeId, InternedString)> = graph
630641
.nodes
631642
.iter()
632643
.enumerate()
633644
.filter_map(|(i, node)| match node {
634645
Node::Package { .. } => None,
635646
Node::Feature { node_index, name } => {
636647
let package_id = graph.package_id_for_index(*node_index);
637-
Some((package_id, *node_index, i, *name))
648+
Some((package_id, *node_index, NodeId::with_index(i), *name))
638649
}
639650
})
640651
.collect();
@@ -660,8 +671,8 @@ fn add_feature_rec(
660671
resolve: &Resolve,
661672
feature_name: InternedString,
662673
package_id: PackageId,
663-
from: usize,
664-
package_index: usize,
674+
from: NodeId,
675+
package_index: NodeId,
665676
) {
666677
let feature_map = resolve.summary(package_id).features();
667678
let Some(fvs) = feature_map.get(&feature_name) else {

src/cargo/ops/tree/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::str::FromStr;
1616
mod format;
1717
mod graph;
1818

19-
pub use {graph::EdgeKind, graph::Node};
19+
pub use {graph::EdgeKind, graph::Node, graph::NodeId};
2020

2121
pub struct TreeOptions {
2222
pub cli_features: CliFeatures,
@@ -240,7 +240,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<()
240240
fn print(
241241
ws: &Workspace<'_>,
242242
opts: &TreeOptions,
243-
roots: Vec<usize>,
243+
roots: Vec<NodeId>,
244244
pkgs_to_prune: &[PackageIdSpec],
245245
graph: &Graph<'_>,
246246
) -> CargoResult<()> {
@@ -292,16 +292,16 @@ fn print(
292292
fn print_node<'a>(
293293
ws: &Workspace<'_>,
294294
graph: &'a Graph<'_>,
295-
node_index: usize,
295+
node_index: NodeId,
296296
format: &Pattern,
297297
symbols: &Symbols,
298298
pkgs_to_prune: &[PackageIdSpec],
299299
prefix: Prefix,
300300
no_dedupe: bool,
301301
display_depth: DisplayDepth,
302-
visited_deps: &mut HashSet<usize>,
302+
visited_deps: &mut HashSet<NodeId>,
303303
levels_continue: &mut Vec<bool>,
304-
print_stack: &mut Vec<usize>,
304+
print_stack: &mut Vec<NodeId>,
305305
) {
306306
let new = no_dedupe || visited_deps.insert(node_index);
307307

@@ -371,16 +371,16 @@ fn print_node<'a>(
371371
fn print_dependencies<'a>(
372372
ws: &Workspace<'_>,
373373
graph: &'a Graph<'_>,
374-
node_index: usize,
374+
node_index: NodeId,
375375
format: &Pattern,
376376
symbols: &Symbols,
377377
pkgs_to_prune: &[PackageIdSpec],
378378
prefix: Prefix,
379379
no_dedupe: bool,
380380
display_depth: DisplayDepth,
381-
visited_deps: &mut HashSet<usize>,
381+
visited_deps: &mut HashSet<NodeId>,
382382
levels_continue: &mut Vec<bool>,
383-
print_stack: &mut Vec<usize>,
383+
print_stack: &mut Vec<NodeId>,
384384
kind: &EdgeKind,
385385
) {
386386
let deps = graph.edges_of_kind(node_index, kind);

0 commit comments

Comments
 (0)