1
- use rayon:: iter:: { IntoParallelIterator , ParallelBridge , ParallelIterator } ;
2
- use std:: path:: { Path , PathBuf } ;
3
1
use crate :: structs:: { Buffer , Buffers , FileType , Output , Search } ;
4
-
5
- static FOUND : std:: sync :: atomic :: AtomicBool = std :: sync :: atomic :: AtomicBool :: new ( false ) ;
2
+ use rayon :: iter :: { ParallelBridge , ParallelIterator } ;
3
+ use std:: path :: Path ;
6
4
7
5
type Receiver = std:: sync:: mpsc:: Receiver < String > ;
8
6
type Sender = std:: sync:: mpsc:: Sender < String > ;
@@ -40,10 +38,14 @@ impl Search {
40
38
// Search in directories
41
39
// par_fold(dirs, |dir| search_path(dir.as_ref(), self, sender.clone()));
42
40
let received = rayon:: scope ( move |s| {
43
- s. spawn ( move |_| dirs. into_iter ( ) . par_bridge ( ) . for_each ( |dir| search_path ( dir. as_ref ( ) , self , sender. clone ( ) ) ) ) ;
41
+ s. spawn ( move |_| {
42
+ dirs. into_iter ( )
43
+ . par_bridge ( )
44
+ . for_each ( |dir| search_path ( dir. as_ref ( ) , self , sender. clone ( ) ) )
45
+ } ) ;
44
46
receive_paths ( receiver, self )
45
47
} ) ;
46
-
48
+
47
49
( Vec :: new ( ) , received)
48
50
}
49
51
}
@@ -52,14 +54,13 @@ fn receive_paths(receiver: Receiver, search: &Search) -> Buffer {
52
54
let stdout = std:: io:: stdout ( ) ;
53
55
let mut stdout = std:: io:: BufWriter :: new ( stdout. lock ( ) ) ;
54
56
let mut received = Vec :: new ( ) ;
55
-
57
+
56
58
while let Ok ( path) = receiver. recv ( ) {
57
59
use std:: io:: Write ;
58
60
if search. first {
59
61
println ! ( "{path}" ) ;
60
62
std:: process:: exit ( 0 )
61
- }
62
- else if search. output == Output :: SuperSimple {
63
+ } else if search. output == Output :: SuperSimple {
63
64
writeln ! ( stdout, "{path}" ) . unwrap ( ) ;
64
65
} else {
65
66
received. push ( path) ;
@@ -70,7 +71,9 @@ fn receive_paths(receiver: Receiver, search: &Search) -> Buffer {
70
71
71
72
fn search_path ( dir : & Path , search : & Search , sender : Sender ) {
72
73
if let Ok ( read) = std:: fs:: read_dir ( dir) {
73
- read. flatten ( ) . par_bridge ( ) . for_each ( |entry| search_dir ( entry, search, sender. clone ( ) ) ) ;
74
+ read. flatten ( )
75
+ . par_bridge ( )
76
+ . for_each ( |entry| search_dir ( entry, search, sender. clone ( ) ) ) ;
74
77
// return par_fold(read.flatten(), |entry| search_dir(entry, search, sender.clone()));
75
78
} else if search. verbose {
76
79
eprintln ! ( "Could not read {:?}" , dir) ;
@@ -113,7 +116,7 @@ fn search_dir(entry: std::fs::DirEntry, search: &Search, sender: Sender) {
113
116
114
117
let starts = search. starts . is_empty ( ) || sname. starts_with ( & search. starts ) ;
115
118
let ends = search. ends . is_empty ( ) || sname. ends_with ( & search. ends ) ;
116
-
119
+
117
120
if starts && ends && ftype {
118
121
// If file name is equal to search name, write it to the "Exact" buffer
119
122
if sname == search. name {
@@ -129,30 +132,86 @@ fn search_dir(entry: std::fs::DirEntry, search: &Search, sender: Sender) {
129
132
sender. send ( s) . unwrap ( ) ;
130
133
}
131
134
}
132
-
135
+
133
136
// If entry is not a directory, stop function
134
137
if !is_dir {
135
138
return ;
136
139
}
137
140
138
141
if let Ok ( read) = std:: fs:: read_dir ( & path) {
139
- read. flatten ( ) . par_bridge ( ) . for_each ( |entry| search_dir ( entry, search, sender. clone ( ) ) ) ;
142
+ read. flatten ( )
143
+ . par_bridge ( )
144
+ . for_each ( |entry| search_dir ( entry, search, sender. clone ( ) ) ) ;
140
145
} else if search. verbose {
141
146
eprintln ! ( "Could not read {:?}" , path) ;
142
147
}
143
148
}
144
149
145
- // OS-variable functions
146
- #[ cfg( windows) ]
147
- fn is_hidden ( entry : & std:: fs:: DirEntry ) -> bool {
148
- use std:: os:: windows:: prelude:: * ;
149
- let metadata = entry. metadata ( ) . unwrap ( ) ;
150
- let attributes = metadata. file_attributes ( ) ;
150
+ // from https://github.com/BurntSushi/ripgrep/blob/master/crates/ignore/src/pathutil.rs
151
151
152
- attributes & 0x2 > 0
152
+ /// Returns true if and only if this entry is considered to be hidden.
153
+ ///
154
+ /// This only returns true if the base name of the path starts with a `.`.
155
+ ///
156
+ /// On Unix, this implements a more optimized check.
157
+ #[ cfg( unix) ]
158
+ pub ( crate ) fn is_hidden ( entry : & std:: fs:: DirEntry ) -> bool {
159
+ use std:: os:: unix:: ffi:: OsStrExt ;
160
+
161
+ file_name ( & entry. path ( ) ) . is_some_and ( |name| name. as_bytes ( ) . first ( ) == Some ( & b'.' ) )
153
162
}
154
163
164
+ /// Returns true if and only if this entry is considered to be hidden.
165
+ ///
166
+ /// On Windows, this returns true if one of the following is true:
167
+ ///
168
+ /// * The base name of the path starts with a `.`.
169
+ /// * The file attributes have the `HIDDEN` property set.
170
+ #[ cfg( windows) ]
171
+ pub ( crate ) fn is_hidden ( entry : & std:: fs:: DirEntry ) -> bool {
172
+ use std:: os:: windows:: fs:: MetadataExt ;
173
+ use winapi_util:: file;
174
+
175
+ // This looks like we're doing an extra stat call, but on Windows, the
176
+ // directory traverser reuses the metadata retrieved from each directory
177
+ // entry and stores it on the DirEntry itself. So this is "free."
178
+ if let Ok ( md) = entry. metadata ( ) {
179
+ if file:: is_hidden ( md. file_attributes ( ) as u64 ) {
180
+ return true ;
181
+ }
182
+ }
183
+ if let Some ( name) = file_name ( entry. path ( ) ) {
184
+ name. to_str ( ) . map ( |s| s. starts_with ( "." ) ) . unwrap_or ( false )
185
+ } else {
186
+ false
187
+ }
188
+ }
189
+
190
+ /// The final component of the path, if it is a normal file.
191
+ ///
192
+ /// If the path terminates in ., .., or consists solely of a root of prefix,
193
+ /// file_name will return None.
155
194
#[ cfg( unix) ]
156
- fn is_hidden ( entry : & std:: fs:: DirEntry ) -> bool {
157
- entry. file_name ( ) . to_string_lossy ( ) . starts_with ( '.' )
195
+ pub ( crate ) fn file_name < P : AsRef < Path > + ?Sized > ( path : & P ) -> Option < & std:: ffi:: OsStr > {
196
+ use std:: os:: unix:: ffi:: OsStrExt ;
197
+
198
+ let path = path. as_ref ( ) . as_os_str ( ) . as_bytes ( ) ;
199
+ if path. is_empty ( )
200
+ || path. len ( ) == 1 && path[ 0 ] == b'.'
201
+ || path. last ( ) == Some ( & b'.' )
202
+ || path. len ( ) >= 2 && path[ path. len ( ) - 2 ..] == b".." [ ..]
203
+ {
204
+ return None ;
205
+ }
206
+ let last_slash = memchr:: memrchr ( b'/' , path) . map ( |i| i + 1 ) . unwrap_or ( 0 ) ;
207
+ Some ( std:: ffi:: OsStr :: from_bytes ( & path[ last_slash..] ) )
208
+ }
209
+
210
+ /// The final component of the path, if it is a normal file.
211
+ ///
212
+ /// If the path terminates in ., .., or consists solely of a root of prefix,
213
+ /// file_name will return None.
214
+ #[ cfg( not( unix) ) ]
215
+ pub ( crate ) fn file_name < ' a , P : AsRef < Path > + ?Sized > ( path : & ' a P ) -> Option < & ' a OsStr > {
216
+ path. as_ref ( ) . file_name ( )
158
217
}
0 commit comments