1
1
use pipe_trait:: Pipe ;
2
- use std:: { collections:: HashSet , fs:: canonicalize, io, mem:: take, path:: PathBuf } ;
2
+ use std:: {
3
+ collections:: HashSet ,
4
+ fs:: { canonicalize, symlink_metadata} ,
5
+ io,
6
+ mem:: take,
7
+ path:: PathBuf ,
8
+ } ;
3
9
4
10
/// Mockable APIs to interact with the system.
5
11
pub trait Api {
6
12
type Argument ;
7
13
type RealPath : Eq ;
8
14
type RealPathError ;
9
15
fn canonicalize ( path : & Self :: Argument ) -> Result < Self :: RealPath , Self :: RealPathError > ;
16
+ fn is_real_dir ( path : & Self :: Argument ) -> bool ;
10
17
fn starts_with ( a : & Self :: RealPath , b : & Self :: RealPath ) -> bool ;
11
18
}
12
19
@@ -21,6 +28,11 @@ impl Api for RealApi {
21
28
canonicalize ( path)
22
29
}
23
30
31
+ fn is_real_dir ( path : & Self :: Argument ) -> bool {
32
+ path. pipe ( symlink_metadata)
33
+ . is_ok_and ( |metadata| !metadata. is_symlink ( ) && metadata. is_dir ( ) )
34
+ }
35
+
24
36
fn starts_with ( a : & Self :: RealPath , b : & Self :: RealPath ) -> bool {
25
37
a. starts_with ( b)
26
38
}
@@ -43,13 +55,20 @@ pub fn deduplicate_arguments<Api: self::Api>(arguments: &mut Vec<Api::Argument>)
43
55
pub fn find_argument_duplications_to_remove < Api : self :: Api > (
44
56
arguments : & [ Api :: Argument ] ,
45
57
) -> HashSet < usize > {
46
- let real_paths: Vec < _ > = arguments. iter ( ) . map ( Api :: canonicalize) . collect ( ) ;
58
+ let real_paths: Vec < _ > = arguments
59
+ . iter ( )
60
+ . map ( |path| {
61
+ Api :: is_real_dir ( path)
62
+ . then ( || Api :: canonicalize ( path) )
63
+ . and_then ( Result :: ok)
64
+ } )
65
+ . collect ( ) ;
47
66
assert_eq ! ( arguments. len( ) , real_paths. len( ) ) ;
48
67
49
68
let mut to_remove = HashSet :: new ( ) ;
50
69
for left_index in 0 ..arguments. len ( ) {
51
70
for right_index in ( left_index + 1 ) ..arguments. len ( ) {
52
- if let ( Ok ( left) , Ok ( right) ) = ( & real_paths[ left_index] , & real_paths[ right_index] ) {
71
+ if let ( Some ( left) , Some ( right) ) = ( & real_paths[ left_index] , & real_paths[ right_index] ) {
53
72
// both paths are the same, remove the second one
54
73
if left == right {
55
74
to_remove. insert ( right_index) ;
@@ -148,6 +167,13 @@ mod tests {
148
167
. pipe ( Ok )
149
168
}
150
169
170
+ fn is_real_dir ( path : & Self :: Argument ) -> bool {
171
+ let path = PathBuf :: from ( path) . normalize ( ) ;
172
+ MOCKED_SYMLINKS
173
+ . iter ( )
174
+ . all ( |( link, _) | PathBuf :: from ( link) . normalize ( ) != path)
175
+ }
176
+
151
177
fn starts_with ( a : & Self :: RealPath , b : & Self :: RealPath ) -> bool {
152
178
a. starts_with ( b)
153
179
}
0 commit comments