1
1
use std:: borrow:: Borrow ;
2
- use std:: collections:: BTreeSet ;
2
+ use std:: collections:: { BTreeMap , BTreeSet , VecDeque } ;
3
3
use std:: fmt;
4
4
5
5
pub struct Graph < N : Clone , E : Clone > {
@@ -87,84 +87,107 @@ impl<N: Eq + Ord + Clone, E: Default + Clone> Graph<N, E> {
87
87
false
88
88
}
89
89
90
- /// Resolves one of the paths from the given dependent package down to
91
- /// a leaf.
90
+ /// Resolves one of the paths from the given dependent package down to a leaf.
91
+ ///
92
+ /// The path return will be the shortest path, or more accurately one of the paths with the shortest length.
92
93
///
93
94
/// Each element contains a node along with an edge except the first one.
94
95
/// The representation would look like:
95
96
///
96
97
/// (Node0,) -> (Node1, Edge01) -> (Node2, Edge12)...
97
- pub fn path_to_bottom < ' a > ( & ' a self , mut pkg : & ' a N ) -> Vec < ( & ' a N , Option < & ' a E > ) > {
98
- let mut result = vec ! [ ( pkg, None ) ] ;
99
- while let Some ( p) = self . nodes . get ( pkg) . and_then ( |p| {
100
- p. iter ( )
101
- // Note that we can have "cycles" introduced through dev-dependency
102
- // edges, so make sure we don't loop infinitely.
103
- . find ( |& ( node, _) | result. iter ( ) . all ( |p| p. 0 != node) )
104
- . map ( |( node, edge) | ( node, Some ( edge) ) )
105
- } ) {
106
- result. push ( p) ;
107
- pkg = p. 0 ;
108
- }
109
- #[ cfg( debug_assertions) ]
110
- {
111
- for x in result. windows ( 2 ) {
112
- let [ ( n1, _) , ( n2, Some ( e12) ) ] = x else {
113
- unreachable ! ( )
114
- } ;
115
- assert ! ( std:: ptr:: eq( self . edge( n1, n2) . unwrap( ) , * e12) ) ;
116
- }
117
- let last = result. last ( ) . unwrap ( ) . 0 ;
118
- // fixme: this may be wrong when there are cycles, but we dont have them in tests.
119
- assert ! ( !self . nodes. contains_key( last) ) ;
120
- }
121
- result
98
+ pub fn path_to_bottom < ' a > ( & ' a self , pkg : & ' a N ) -> Vec < ( & ' a N , Option < & ' a E > ) > {
99
+ self . path_to ( pkg, |s, p| s. edges ( p) )
122
100
}
123
101
124
- /// Resolves one of the paths from the given dependent package up to
125
- /// the root.
102
+ /// Resolves one of the paths from the given dependent package up to the root.
103
+ ///
104
+ /// The path return will be the shortest path, or more accurately one of the paths with the shortest length.
126
105
///
127
106
/// Each element contains a node along with an edge except the first one.
128
107
/// The representation would look like:
129
108
///
130
109
/// (Node0,) -> (Node1, Edge01) -> (Node2, Edge12)...
131
- pub fn path_to_top < ' a > ( & ' a self , mut pkg : & ' a N ) -> Vec < ( & ' a N , Option < & ' a E > ) > {
132
- // Note that this implementation isn't the most robust per se, we'll
133
- // likely have to tweak this over time. For now though it works for what
134
- // it's used for!
135
- let mut result = vec ! [ ( pkg, None ) ] ;
136
- let first_pkg_depending_on = |pkg, res : & [ ( & N , Option < & E > ) ] | {
137
- self . nodes
110
+ pub fn path_to_top < ' a > ( & ' a self , pkg : & ' a N ) -> Vec < ( & ' a N , Option < & ' a E > ) > {
111
+ self . path_to ( pkg, |s, pk| {
112
+ // Note that this implementation isn't the most robust per se, we'll
113
+ // likely have to tweak this over time. For now though it works for what
114
+ // it's used for!
115
+ s. nodes
138
116
. iter ( )
139
- . filter ( |( _, adjacent) | adjacent. contains_key ( pkg) )
140
- // Note that we can have "cycles" introduced through dev-dependency
141
- // edges, so make sure we don't loop infinitely.
142
- . find ( |& ( node, _) | !res. iter ( ) . any ( |p| p. 0 == node) )
143
- . map ( |( p, adjacent) | ( p, adjacent. get ( pkg) ) )
144
- } ;
145
- while let Some ( p) = first_pkg_depending_on ( pkg, & result) {
146
- result. push ( p) ;
147
- pkg = p. 0 ;
117
+ . filter_map ( |( p, adjacent) | adjacent. get ( pk) . map ( |e| ( p, e) ) )
118
+ } )
119
+ }
120
+ }
121
+
122
+ impl < ' s , N : Eq + Ord + Clone + ' s , E : Default + Clone + ' s > Graph < N , E > {
123
+ fn path_to < ' a , F , I > ( & ' s self , pkg : & ' a N , fn_edge : F ) -> Vec < ( & ' a N , Option < & ' a E > ) >
124
+ where
125
+ I : Iterator < Item = ( & ' a N , & ' a E ) > ,
126
+ F : Fn ( & ' s Self , & ' a N ) -> I ,
127
+ ' a : ' s ,
128
+ {
129
+ let mut back_link = BTreeMap :: new ( ) ;
130
+ let mut queue = VecDeque :: from ( [ pkg] ) ;
131
+ let mut bottom = None ;
132
+
133
+ while let Some ( p) = queue. pop_front ( ) {
134
+ bottom = Some ( p) ;
135
+ for ( child, edge) in fn_edge ( & self , p) {
136
+ bottom = None ;
137
+ back_link. entry ( child) . or_insert_with ( || {
138
+ queue. push_back ( child) ;
139
+ ( p, edge)
140
+ } ) ;
141
+ }
142
+ if bottom. is_some ( ) {
143
+ break ;
144
+ }
148
145
}
146
+
147
+ let mut result = Vec :: new ( ) ;
148
+ let mut next =
149
+ bottom. expect ( "the only path was a cycle, no dependency graph has this shape" ) ;
150
+ while let Some ( ( p, e) ) = back_link. remove ( & next) {
151
+ result. push ( ( next, Some ( e) ) ) ;
152
+ next = p;
153
+ }
154
+ result. push ( ( next, None ) ) ;
155
+ result. reverse ( ) ;
149
156
#[ cfg( debug_assertions) ]
150
157
{
151
158
for x in result. windows ( 2 ) {
152
159
let [ ( n2, _) , ( n1, Some ( e12) ) ] = x else {
153
160
unreachable ! ( )
154
161
} ;
155
- assert ! ( std:: ptr:: eq( self . edge( n1, n2) . unwrap( ) , * e12) ) ;
162
+ assert ! ( std:: ptr:: eq(
163
+ self . edge( n1, n2) . or( self . edge( n2, n1) ) . unwrap( ) ,
164
+ * e12
165
+ ) ) ;
156
166
}
157
167
let last = result. last ( ) . unwrap ( ) . 0 ;
158
- // fixme: this may be wrong when there are cycles, but we dont have them in tests .
159
- assert ! ( ! self
160
- . nodes
161
- . iter ( )
162
- . any ( | ( _ , adjacent ) | adjacent . contains_key ( last ) ) ) ;
168
+ // fixme: this may sometimes be wrong when there are cycles.
169
+ if ! fn_edge ( & self , last ) . next ( ) . is_none ( ) {
170
+ self . print_for_test ( ) ;
171
+ unreachable ! ( "The last element in the path should not have outgoing edges" ) ;
172
+ }
163
173
}
164
174
result
165
175
}
166
176
}
167
177
178
+ #[ test]
179
+ fn path_to_case ( ) {
180
+ let mut new = Graph :: new ( ) ;
181
+ new. link ( 0 , 3 ) ;
182
+ new. link ( 1 , 0 ) ;
183
+ new. link ( 2 , 0 ) ;
184
+ new. link ( 2 , 1 ) ;
185
+ assert_eq ! (
186
+ new. path_to_bottom( & 2 ) ,
187
+ vec![ ( & 2 , None ) , ( & 0 , Some ( & ( ) ) ) , ( & 3 , Some ( & ( ) ) ) ]
188
+ ) ;
189
+ }
190
+
168
191
impl < N : Eq + Ord + Clone , E : Default + Clone > Default for Graph < N , E > {
169
192
fn default ( ) -> Graph < N , E > {
170
193
Graph :: new ( )
@@ -189,6 +212,36 @@ impl<N: fmt::Display + Eq + Ord + Clone, E: Clone> fmt::Debug for Graph<N, E> {
189
212
}
190
213
}
191
214
215
+ impl < N : Eq + Ord + Clone , E : Clone > Graph < N , E > {
216
+ /// Prints the graph for constructing unit tests.
217
+ ///
218
+ /// For purposes of graph traversal algorithms the edge values do not matter,
219
+ /// and the only value of the node we care about is the order it gets compared in.
220
+ /// This constructs a graph with the same topology but with integer keys and unit edges.
221
+ #[ cfg( debug_assertions) ]
222
+ #[ allow( clippy:: print_stderr) ]
223
+ fn print_for_test ( & self ) {
224
+ // Isolate and print a test case.
225
+ let names = self
226
+ . nodes
227
+ . keys ( )
228
+ . chain ( self . nodes . values ( ) . flat_map ( |vs| vs. keys ( ) ) )
229
+ . collect :: < BTreeSet < _ > > ( )
230
+ . into_iter ( )
231
+ . collect :: < Vec < _ > > ( ) ;
232
+ let mut new = Graph :: new ( ) ;
233
+ for n1 in self . nodes . keys ( ) {
234
+ let name1 = names. binary_search ( & n1) . unwrap ( ) ;
235
+ new. add ( name1) ;
236
+ for n2 in self . nodes [ n1] . keys ( ) {
237
+ let name2 = names. binary_search ( & n2) . unwrap ( ) ;
238
+ * new. link ( name1, name2) = ( ) ;
239
+ }
240
+ }
241
+ eprintln ! ( "Graph for tests = {new:#?}" ) ;
242
+ }
243
+ }
244
+
192
245
impl < N : Eq + Ord + Clone , E : Eq + Clone > PartialEq for Graph < N , E > {
193
246
fn eq ( & self , other : & Graph < N , E > ) -> bool {
194
247
self . nodes . eq ( & other. nodes )
0 commit comments