@@ -30,8 +30,7 @@ pub struct Context {
30
30
pub links : im_rc:: HashMap < InternedString , PackageId > ,
31
31
/// for each package the list of names it can see,
32
32
/// then for each name the exact version that name represents and weather the name is public.
33
- pub public_dependency :
34
- Option < im_rc:: HashMap < PackageId , im_rc:: HashMap < InternedString , ( PackageId , bool ) > > > ,
33
+ pub public_dependency : Option < PublicDependency > ,
35
34
36
35
/// a way to look up for a package in activations what packages required it
37
36
/// and all of the exact deps that it fulfilled.
@@ -86,7 +85,7 @@ impl Context {
86
85
resolve_features : im_rc:: HashMap :: new ( ) ,
87
86
links : im_rc:: HashMap :: new ( ) ,
88
87
public_dependency : if check_public_visible_dependencies {
89
- Some ( im_rc :: HashMap :: new ( ) )
88
+ Some ( PublicDependency :: new ( ) )
90
89
} else {
91
90
None
92
91
} ,
@@ -240,3 +239,110 @@ impl Context {
240
239
graph
241
240
}
242
241
}
242
+
243
+ #[ derive( Clone , Debug , Default ) ]
244
+ pub struct PublicDependency {
245
+ inner : im_rc:: HashMap < PackageId , im_rc:: HashMap < InternedString , ( PackageId , bool ) > > ,
246
+ }
247
+
248
+ impl PublicDependency {
249
+ fn new ( ) -> Self {
250
+ PublicDependency {
251
+ inner : im_rc:: HashMap :: new ( ) ,
252
+ }
253
+ }
254
+ pub fn add_edge (
255
+ & mut self ,
256
+ candidate_pid : PackageId ,
257
+ parent_pid : PackageId ,
258
+ dep : & Dependency ,
259
+ parents : & Graph < PackageId , Rc < Vec < Dependency > > > ,
260
+ ) {
261
+ // one tricky part is that `candidate_pid` may already be active and
262
+ // have public dependencies of its own. So we not only need to mark
263
+ // `candidate_pid` as visible to its parents but also all of its existing
264
+ // public dependencies.
265
+ let existing_public_deps: Vec < PackageId > = self
266
+ . inner
267
+ . get ( & candidate_pid)
268
+ . iter ( )
269
+ . flat_map ( |x| x. values ( ) )
270
+ . filter_map ( |x| if x. 1 { Some ( & x. 0 ) } else { None } )
271
+ . chain ( & Some ( candidate_pid) )
272
+ . cloned ( )
273
+ . collect ( ) ;
274
+ for c in existing_public_deps {
275
+ // for each (transitive) parent that can newly see `t`
276
+ let mut stack = vec ! [ ( parent_pid, dep. is_public( ) ) ] ;
277
+ while let Some ( ( p, public) ) = stack. pop ( ) {
278
+ match self . inner . entry ( p) . or_default ( ) . entry ( c. name ( ) ) {
279
+ im_rc:: hashmap:: Entry :: Occupied ( mut o) => {
280
+ // the (transitive) parent can already see something by `c`s name, it had better be `c`.
281
+ assert_eq ! ( o. get( ) . 0 , c) ;
282
+ if o. get ( ) . 1 {
283
+ // The previous time the parent saw `c`, it was a public dependency.
284
+ // So all of its parents already know about `c`
285
+ // and we can save some time by stopping now.
286
+ continue ;
287
+ }
288
+ if public {
289
+ // Mark that `c` has now bean seen publicly
290
+ o. insert ( ( c, public) ) ;
291
+ }
292
+ }
293
+ im_rc:: hashmap:: Entry :: Vacant ( v) => {
294
+ // The (transitive) parent does not have anything by `c`s name,
295
+ // so we add `c`.
296
+ v. insert ( ( c, public) ) ;
297
+ }
298
+ }
299
+ // if `candidate_pid` was a private dependency of `p` then `p` parents can't see `c` thru `p`
300
+ if public {
301
+ // if it was public, then we add all of `p`s parents to be checked
302
+ for & ( grand, ref d) in parents. edges ( & p) {
303
+ stack. push ( ( grand, d. iter ( ) . any ( |x| x. is_public ( ) ) ) ) ;
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ pub fn can_add_edge (
310
+ & self ,
311
+ b_id : PackageId ,
312
+ parent : PackageId ,
313
+ dep : & Dependency ,
314
+ parents : & Graph < PackageId , Rc < Vec < Dependency > > > ,
315
+ ) -> Result < ( ) , ( PackageId , ConflictReason ) > {
316
+ let existing_public_deps: Vec < PackageId > = self
317
+ . inner
318
+ . get ( & b_id)
319
+ . iter ( )
320
+ . flat_map ( |x| x. values ( ) )
321
+ . filter_map ( |x| if x. 1 { Some ( & x. 0 ) } else { None } )
322
+ . chain ( & Some ( b_id) )
323
+ . cloned ( )
324
+ . collect ( ) ;
325
+ for t in existing_public_deps {
326
+ // for each (transitive) parent that can newly see `t`
327
+ let mut stack = vec ! [ ( parent, dep. is_public( ) ) ] ;
328
+ while let Some ( ( p, public) ) = stack. pop ( ) {
329
+ // TODO: dont look at the same thing more then once
330
+ if let Some ( o) = self . inner . get ( & p) . and_then ( |x| x. get ( & t. name ( ) ) ) {
331
+ if o. 0 != t {
332
+ // the (transitive) parent can already see a different version by `t`s name.
333
+ // So, adding `b` will cause `p` to have a public dependency conflict on `t`.
334
+ return Err ( ( p, ConflictReason :: PublicDependency ) ) ;
335
+ }
336
+ }
337
+ // if `b` was a private dependency of `p` then `p` parents can't see `t` thru `p`
338
+ if public {
339
+ // if it was public, then we add all of `p`s parents to be checked
340
+ for & ( grand, ref d) in parents. edges ( & p) {
341
+ stack. push ( ( grand, d. iter ( ) . any ( |x| x. is_public ( ) ) ) ) ;
342
+ }
343
+ }
344
+ }
345
+ }
346
+ Ok ( ( ) )
347
+ }
348
+ }
0 commit comments