##// END OF EJS Templates
rust: remove an unnecessary set of parentheses...
Martin von Zweigbergk -
r44654:3c265cef default
parent child Browse files
Show More
@@ -1,306 +1,306 b''
1 1 // status.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Rust implementation of dirstate.status (dirstate.py).
9 9 //! It is currently missing a lot of functionality compared to the Python one
10 10 //! and will only be triggered in narrow cases.
11 11
12 12 use crate::{
13 13 dirstate::SIZE_FROM_OTHER_PARENT,
14 14 matchers::Matcher,
15 15 utils::{
16 16 files::HgMetadata,
17 17 hg_path::{hg_path_to_path_buf, HgPath},
18 18 },
19 19 CopyMap, DirstateEntry, DirstateMap, EntryState,
20 20 };
21 21 use rayon::prelude::*;
22 22 use std::collections::HashSet;
23 23 use std::path::Path;
24 24
25 25 /// Marker enum used to dispatch new status entries into the right collections.
26 26 /// Is similar to `crate::EntryState`, but represents the transient state of
27 27 /// entries during the lifetime of a command.
28 28 enum Dispatch {
29 29 Unsure,
30 30 Modified,
31 31 Added,
32 32 Removed,
33 33 Deleted,
34 34 Clean,
35 35 Unknown,
36 36 }
37 37
38 38 type IoResult<T> = std::io::Result<T>;
39 39
40 40 /// Dates and times that are outside the 31-bit signed range are compared
41 41 /// modulo 2^31. This should prevent hg from behaving badly with very large
42 42 /// files or corrupt dates while still having a high probability of detecting
43 43 /// changes. (issue2608)
44 44 /// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
45 45 /// is not defined for `i32`, and there is no `As` trait. This forces the
46 46 /// caller to cast `b` as `i32`.
47 47 fn mod_compare(a: i32, b: i32) -> bool {
48 48 a & i32::max_value() != b & i32::max_value()
49 49 }
50 50
51 51 /// The file corresponding to the dirstate entry was found on the filesystem.
52 52 fn dispatch_found(
53 53 filename: impl AsRef<HgPath>,
54 54 entry: DirstateEntry,
55 55 metadata: HgMetadata,
56 56 copy_map: &CopyMap,
57 57 check_exec: bool,
58 58 list_clean: bool,
59 59 last_normal_time: i64,
60 60 ) -> Dispatch {
61 61 let DirstateEntry {
62 62 state,
63 63 mode,
64 64 mtime,
65 65 size,
66 66 } = entry;
67 67
68 68 let HgMetadata {
69 69 st_mode,
70 70 st_size,
71 71 st_mtime,
72 72 ..
73 73 } = metadata;
74 74
75 75 match state {
76 76 EntryState::Normal => {
77 77 let size_changed = mod_compare(size, st_size as i32);
78 78 let mode_changed =
79 79 (mode ^ st_mode as i32) & 0o100 != 0o000 && check_exec;
80 80 let metadata_changed = size >= 0 && (size_changed || mode_changed);
81 81 let other_parent = size == SIZE_FROM_OTHER_PARENT;
82 82 if metadata_changed
83 83 || other_parent
84 84 || copy_map.contains_key(filename.as_ref())
85 85 {
86 86 Dispatch::Modified
87 87 } else if mod_compare(mtime, st_mtime as i32) {
88 88 Dispatch::Unsure
89 89 } else if st_mtime == last_normal_time {
90 90 // the file may have just been marked as normal and
91 91 // it may have changed in the same second without
92 92 // changing its size. This can happen if we quickly
93 93 // do multiple commits. Force lookup, so we don't
94 94 // miss such a racy file change.
95 95 Dispatch::Unsure
96 96 } else if list_clean {
97 97 Dispatch::Clean
98 98 } else {
99 99 Dispatch::Unknown
100 100 }
101 101 }
102 102 EntryState::Merged => Dispatch::Modified,
103 103 EntryState::Added => Dispatch::Added,
104 104 EntryState::Removed => Dispatch::Removed,
105 105 EntryState::Unknown => Dispatch::Unknown,
106 106 }
107 107 }
108 108
109 109 /// The file corresponding to this Dirstate entry is missing.
110 110 fn dispatch_missing(state: EntryState) -> Dispatch {
111 111 match state {
112 112 // File was removed from the filesystem during commands
113 113 EntryState::Normal | EntryState::Merged | EntryState::Added => {
114 114 Dispatch::Deleted
115 115 }
116 116 // File was removed, everything is normal
117 117 EntryState::Removed => Dispatch::Removed,
118 118 // File is unknown to Mercurial, everything is normal
119 119 EntryState::Unknown => Dispatch::Unknown,
120 120 }
121 121 }
122 122
123 123 /// Get stat data about the files explicitly specified by match.
124 124 /// TODO subrepos
125 125 fn walk_explicit<'a>(
126 126 files: &'a HashSet<&HgPath>,
127 127 dmap: &'a DirstateMap,
128 128 root_dir: impl AsRef<Path> + Sync + Send,
129 129 check_exec: bool,
130 130 list_clean: bool,
131 131 last_normal_time: i64,
132 132 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> {
133 133 files.par_iter().filter_map(move |filename| {
134 134 // TODO normalization
135 135 let normalized = filename.as_ref();
136 136
137 137 let buf = match hg_path_to_path_buf(normalized) {
138 138 Ok(x) => x,
139 139 Err(e) => return Some(Err(e.into())),
140 140 };
141 141 let target = root_dir.as_ref().join(buf);
142 142 let st = target.symlink_metadata();
143 143 match st {
144 144 Ok(meta) => {
145 145 let file_type = meta.file_type();
146 146 if file_type.is_file() || file_type.is_symlink() {
147 147 if let Some(entry) = dmap.get(normalized) {
148 148 return Some(Ok((
149 149 normalized,
150 150 dispatch_found(
151 151 &normalized,
152 152 *entry,
153 153 HgMetadata::from_metadata(meta),
154 154 &dmap.copy_map,
155 155 check_exec,
156 156 list_clean,
157 157 last_normal_time,
158 158 ),
159 159 )));
160 160 }
161 161 } else {
162 162 if dmap.contains_key(normalized) {
163 163 return Some(Ok((normalized, Dispatch::Removed)));
164 164 }
165 165 }
166 166 }
167 167 Err(_) => {
168 168 if let Some(entry) = dmap.get(normalized) {
169 169 return Some(Ok((
170 170 normalized,
171 171 dispatch_missing(entry.state),
172 172 )));
173 173 }
174 174 }
175 175 };
176 176 None
177 177 })
178 178 }
179 179
180 180 /// Stat all entries in the `DirstateMap` and mark them for dispatch into
181 181 /// the relevant collections.
182 182 fn stat_dmap_entries(
183 183 dmap: &DirstateMap,
184 184 root_dir: impl AsRef<Path> + Sync + Send,
185 185 check_exec: bool,
186 186 list_clean: bool,
187 187 last_normal_time: i64,
188 188 ) -> impl ParallelIterator<Item = IoResult<(&HgPath, Dispatch)>> {
189 189 dmap.par_iter().map(move |(filename, entry)| {
190 190 let filename: &HgPath = filename;
191 191 let filename_as_path = hg_path_to_path_buf(filename)?;
192 192 let meta = root_dir.as_ref().join(filename_as_path).symlink_metadata();
193 193
194 194 match meta {
195 195 Ok(ref m)
196 196 if !(m.file_type().is_file()
197 197 || m.file_type().is_symlink()) =>
198 198 {
199 199 Ok((filename, dispatch_missing(entry.state)))
200 200 }
201 201 Ok(m) => Ok((
202 202 filename,
203 203 dispatch_found(
204 204 filename,
205 205 *entry,
206 206 HgMetadata::from_metadata(m),
207 207 &dmap.copy_map,
208 208 check_exec,
209 209 list_clean,
210 210 last_normal_time,
211 211 ),
212 212 )),
213 213 Err(ref e)
214 214 if e.kind() == std::io::ErrorKind::NotFound
215 215 || e.raw_os_error() == Some(20) =>
216 216 {
217 217 // Rust does not yet have an `ErrorKind` for
218 218 // `NotADirectory` (errno 20)
219 219 // It happens if the dirstate contains `foo/bar` and
220 220 // foo is not a directory
221 221 Ok((filename, dispatch_missing(entry.state)))
222 222 }
223 223 Err(e) => Err(e),
224 224 }
225 225 })
226 226 }
227 227
228 228 pub struct StatusResult<'a> {
229 229 pub modified: Vec<&'a HgPath>,
230 230 pub added: Vec<&'a HgPath>,
231 231 pub removed: Vec<&'a HgPath>,
232 232 pub deleted: Vec<&'a HgPath>,
233 233 pub clean: Vec<&'a HgPath>,
234 234 /* TODO ignored
235 235 * TODO unknown */
236 236 }
237 237
238 238 fn build_response<'a>(
239 239 results: impl IntoIterator<Item = IoResult<(&'a HgPath, Dispatch)>>,
240 240 ) -> IoResult<(Vec<&'a HgPath>, StatusResult<'a>)> {
241 241 let mut lookup = vec![];
242 242 let mut modified = vec![];
243 243 let mut added = vec![];
244 244 let mut removed = vec![];
245 245 let mut deleted = vec![];
246 246 let mut clean = vec![];
247 247
248 248 for res in results.into_iter() {
249 249 let (filename, dispatch) = res?;
250 250 match dispatch {
251 251 Dispatch::Unknown => {}
252 252 Dispatch::Unsure => lookup.push(filename),
253 253 Dispatch::Modified => modified.push(filename),
254 254 Dispatch::Added => added.push(filename),
255 255 Dispatch::Removed => removed.push(filename),
256 256 Dispatch::Deleted => deleted.push(filename),
257 257 Dispatch::Clean => clean.push(filename),
258 258 }
259 259 }
260 260
261 261 Ok((
262 262 lookup,
263 263 StatusResult {
264 264 modified,
265 265 added,
266 266 removed,
267 267 deleted,
268 268 clean,
269 269 },
270 270 ))
271 271 }
272 272
273 273 pub fn status<'a: 'c, 'b: 'c, 'c>(
274 274 dmap: &'a DirstateMap,
275 matcher: &'b (impl Matcher),
275 matcher: &'b impl Matcher,
276 276 root_dir: impl AsRef<Path> + Sync + Send + Copy,
277 277 list_clean: bool,
278 278 last_normal_time: i64,
279 279 check_exec: bool,
280 280 ) -> IoResult<(Vec<&'c HgPath>, StatusResult<'c>)> {
281 281 let files = matcher.file_set();
282 282 let mut results = vec![];
283 283 if let Some(files) = files {
284 284 results.par_extend(walk_explicit(
285 285 &files,
286 286 &dmap,
287 287 root_dir,
288 288 check_exec,
289 289 list_clean,
290 290 last_normal_time,
291 291 ));
292 292 }
293 293
294 294 if !matcher.is_exact() {
295 295 let stat_results = stat_dmap_entries(
296 296 &dmap,
297 297 root_dir,
298 298 check_exec,
299 299 list_clean,
300 300 last_normal_time,
301 301 );
302 302 results.par_extend(stat_results);
303 303 }
304 304
305 305 build_response(results)
306 306 }
General Comments 0
You need to be logged in to leave comments. Login now