##// END OF EJS Templates
rust-status: rename `StatusResult` to `DirstateStatus`...
Raphaël Gomès -
r45012:f13d1954 default
parent child Browse files
Show More
@@ -1,321 +1,321 b''
1 // status.rs
1 // status.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Rust implementation of dirstate.status (dirstate.py).
8 //! Rust implementation of dirstate.status (dirstate.py).
9 //! It is currently missing a lot of functionality compared to the Python one
9 //! It is currently missing a lot of functionality compared to the Python one
10 //! and will only be triggered in narrow cases.
10 //! and will only be triggered in narrow cases.
11
11
12 use crate::{
12 use crate::{
13 dirstate::SIZE_FROM_OTHER_PARENT,
13 dirstate::SIZE_FROM_OTHER_PARENT,
14 matchers::Matcher,
14 matchers::Matcher,
15 utils::{
15 utils::{
16 files::HgMetadata,
16 files::HgMetadata,
17 hg_path::{
17 hg_path::{
18 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
18 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
19 },
19 },
20 },
20 },
21 CopyMap, DirstateEntry, DirstateMap, EntryState,
21 CopyMap, DirstateEntry, DirstateMap, EntryState,
22 };
22 };
23 use rayon::prelude::*;
23 use rayon::prelude::*;
24 use std::collections::HashSet;
24 use std::collections::HashSet;
25 use std::fs::{read_dir, DirEntry};
25 use std::fs::{read_dir, DirEntry};
26 use std::path::Path;
26 use std::path::Path;
27
27
28 /// Marker enum used to dispatch new status entries into the right collections.
28 /// Marker enum used to dispatch new status entries into the right collections.
29 /// Is similar to `crate::EntryState`, but represents the transient state of
29 /// Is similar to `crate::EntryState`, but represents the transient state of
30 /// entries during the lifetime of a command.
30 /// entries during the lifetime of a command.
31 enum Dispatch {
31 enum Dispatch {
32 Unsure,
32 Unsure,
33 Modified,
33 Modified,
34 Added,
34 Added,
35 Removed,
35 Removed,
36 Deleted,
36 Deleted,
37 Clean,
37 Clean,
38 Unknown,
38 Unknown,
39 }
39 }
40
40
41 type IoResult<T> = std::io::Result<T>;
41 type IoResult<T> = std::io::Result<T>;
42
42
43 /// Dates and times that are outside the 31-bit signed range are compared
43 /// Dates and times that are outside the 31-bit signed range are compared
44 /// modulo 2^31. This should prevent hg from behaving badly with very large
44 /// modulo 2^31. This should prevent hg from behaving badly with very large
45 /// files or corrupt dates while still having a high probability of detecting
45 /// files or corrupt dates while still having a high probability of detecting
46 /// changes. (issue2608)
46 /// changes. (issue2608)
47 /// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
47 /// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
48 /// is not defined for `i32`, and there is no `As` trait. This forces the
48 /// is not defined for `i32`, and there is no `As` trait. This forces the
49 /// caller to cast `b` as `i32`.
49 /// caller to cast `b` as `i32`.
50 fn mod_compare(a: i32, b: i32) -> bool {
50 fn mod_compare(a: i32, b: i32) -> bool {
51 a & i32::max_value() != b & i32::max_value()
51 a & i32::max_value() != b & i32::max_value()
52 }
52 }
53
53
54 /// Return a sorted list containing information about the entries
54 /// Return a sorted list containing information about the entries
55 /// in the directory.
55 /// in the directory.
56 ///
56 ///
57 /// * `skip_dot_hg` - Return an empty vec if `path` contains a `.hg` directory
57 /// * `skip_dot_hg` - Return an empty vec if `path` contains a `.hg` directory
58 fn list_directory(
58 fn list_directory(
59 path: impl AsRef<Path>,
59 path: impl AsRef<Path>,
60 skip_dot_hg: bool,
60 skip_dot_hg: bool,
61 ) -> std::io::Result<Vec<(HgPathBuf, DirEntry)>> {
61 ) -> std::io::Result<Vec<(HgPathBuf, DirEntry)>> {
62 let mut results = vec![];
62 let mut results = vec![];
63 let entries = read_dir(path.as_ref())?;
63 let entries = read_dir(path.as_ref())?;
64
64
65 for entry in entries {
65 for entry in entries {
66 let entry = entry?;
66 let entry = entry?;
67 let filename = os_string_to_hg_path_buf(entry.file_name())?;
67 let filename = os_string_to_hg_path_buf(entry.file_name())?;
68 let file_type = entry.file_type()?;
68 let file_type = entry.file_type()?;
69 if skip_dot_hg && filename.as_bytes() == b".hg" && file_type.is_dir() {
69 if skip_dot_hg && filename.as_bytes() == b".hg" && file_type.is_dir() {
70 return Ok(vec![]);
70 return Ok(vec![]);
71 } else {
71 } else {
72 results.push((HgPathBuf::from(filename), entry))
72 results.push((HgPathBuf::from(filename), entry))
73 }
73 }
74 }
74 }
75
75
76 results.sort_unstable_by_key(|e| e.0.clone());
76 results.sort_unstable_by_key(|e| e.0.clone());
77 Ok(results)
77 Ok(results)
78 }
78 }
79
79
80 /// The file corresponding to the dirstate entry was found on the filesystem.
80 /// The file corresponding to the dirstate entry was found on the filesystem.
81 fn dispatch_found(
81 fn dispatch_found(
82 filename: impl AsRef<HgPath>,
82 filename: impl AsRef<HgPath>,
83 entry: DirstateEntry,
83 entry: DirstateEntry,
84 metadata: HgMetadata,
84 metadata: HgMetadata,
85 copy_map: &CopyMap,
85 copy_map: &CopyMap,
86 options: StatusOptions,
86 options: StatusOptions,
87 ) -> Dispatch {
87 ) -> Dispatch {
88 let DirstateEntry {
88 let DirstateEntry {
89 state,
89 state,
90 mode,
90 mode,
91 mtime,
91 mtime,
92 size,
92 size,
93 } = entry;
93 } = entry;
94
94
95 let HgMetadata {
95 let HgMetadata {
96 st_mode,
96 st_mode,
97 st_size,
97 st_size,
98 st_mtime,
98 st_mtime,
99 ..
99 ..
100 } = metadata;
100 } = metadata;
101
101
102 match state {
102 match state {
103 EntryState::Normal => {
103 EntryState::Normal => {
104 let size_changed = mod_compare(size, st_size as i32);
104 let size_changed = mod_compare(size, st_size as i32);
105 let mode_changed =
105 let mode_changed =
106 (mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec;
106 (mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec;
107 let metadata_changed = size >= 0 && (size_changed || mode_changed);
107 let metadata_changed = size >= 0 && (size_changed || mode_changed);
108 let other_parent = size == SIZE_FROM_OTHER_PARENT;
108 let other_parent = size == SIZE_FROM_OTHER_PARENT;
109 if metadata_changed
109 if metadata_changed
110 || other_parent
110 || other_parent
111 || copy_map.contains_key(filename.as_ref())
111 || copy_map.contains_key(filename.as_ref())
112 {
112 {
113 Dispatch::Modified
113 Dispatch::Modified
114 } else if mod_compare(mtime, st_mtime as i32) {
114 } else if mod_compare(mtime, st_mtime as i32) {
115 Dispatch::Unsure
115 Dispatch::Unsure
116 } else if st_mtime == options.last_normal_time {
116 } else if st_mtime == options.last_normal_time {
117 // the file may have just been marked as normal and
117 // the file may have just been marked as normal and
118 // it may have changed in the same second without
118 // it may have changed in the same second without
119 // changing its size. This can happen if we quickly
119 // changing its size. This can happen if we quickly
120 // do multiple commits. Force lookup, so we don't
120 // do multiple commits. Force lookup, so we don't
121 // miss such a racy file change.
121 // miss such a racy file change.
122 Dispatch::Unsure
122 Dispatch::Unsure
123 } else if options.list_clean {
123 } else if options.list_clean {
124 Dispatch::Clean
124 Dispatch::Clean
125 } else {
125 } else {
126 Dispatch::Unknown
126 Dispatch::Unknown
127 }
127 }
128 }
128 }
129 EntryState::Merged => Dispatch::Modified,
129 EntryState::Merged => Dispatch::Modified,
130 EntryState::Added => Dispatch::Added,
130 EntryState::Added => Dispatch::Added,
131 EntryState::Removed => Dispatch::Removed,
131 EntryState::Removed => Dispatch::Removed,
132 EntryState::Unknown => Dispatch::Unknown,
132 EntryState::Unknown => Dispatch::Unknown,
133 }
133 }
134 }
134 }
135
135
136 /// The file corresponding to this Dirstate entry is missing.
136 /// The file corresponding to this Dirstate entry is missing.
137 fn dispatch_missing(state: EntryState) -> Dispatch {
137 fn dispatch_missing(state: EntryState) -> Dispatch {
138 match state {
138 match state {
139 // File was removed from the filesystem during commands
139 // File was removed from the filesystem during commands
140 EntryState::Normal | EntryState::Merged | EntryState::Added => {
140 EntryState::Normal | EntryState::Merged | EntryState::Added => {
141 Dispatch::Deleted
141 Dispatch::Deleted
142 }
142 }
143 // File was removed, everything is normal
143 // File was removed, everything is normal
144 EntryState::Removed => Dispatch::Removed,
144 EntryState::Removed => Dispatch::Removed,
145 // File is unknown to Mercurial, everything is normal
145 // File is unknown to Mercurial, everything is normal
146 EntryState::Unknown => Dispatch::Unknown,
146 EntryState::Unknown => Dispatch::Unknown,
147 }
147 }
148 }
148 }
149
149
150 /// Get stat data about the files explicitly specified by match.
150 /// Get stat data about the files explicitly specified by match.
151 /// TODO subrepos
151 /// TODO subrepos
152 fn walk_explicit<'a>(
152 fn walk_explicit<'a>(
153 files: &'a HashSet<&HgPath>,
153 files: &'a HashSet<&HgPath>,
154 dmap: &'a DirstateMap,
154 dmap: &'a DirstateMap,
155 root_dir: impl AsRef<Path> + Sync + Send,
155 root_dir: impl AsRef<Path> + Sync + Send,
156 options: StatusOptions,
156 options: StatusOptions,
157 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> {
157 ) -> impl ParallelIterator<Item = IoResult<(&'a HgPath, Dispatch)>> {
158 files.par_iter().filter_map(move |filename| {
158 files.par_iter().filter_map(move |filename| {
159 // TODO normalization
159 // TODO normalization
160 let normalized = filename.as_ref();
160 let normalized = filename.as_ref();
161
161
162 let buf = match hg_path_to_path_buf(normalized) {
162 let buf = match hg_path_to_path_buf(normalized) {
163 Ok(x) => x,
163 Ok(x) => x,
164 Err(e) => return Some(Err(e.into())),
164 Err(e) => return Some(Err(e.into())),
165 };
165 };
166 let target = root_dir.as_ref().join(buf);
166 let target = root_dir.as_ref().join(buf);
167 let st = target.symlink_metadata();
167 let st = target.symlink_metadata();
168 match st {
168 match st {
169 Ok(meta) => {
169 Ok(meta) => {
170 let file_type = meta.file_type();
170 let file_type = meta.file_type();
171 if file_type.is_file() || file_type.is_symlink() {
171 if file_type.is_file() || file_type.is_symlink() {
172 if let Some(entry) = dmap.get(normalized) {
172 if let Some(entry) = dmap.get(normalized) {
173 return Some(Ok((
173 return Some(Ok((
174 normalized,
174 normalized,
175 dispatch_found(
175 dispatch_found(
176 &normalized,
176 &normalized,
177 *entry,
177 *entry,
178 HgMetadata::from_metadata(meta),
178 HgMetadata::from_metadata(meta),
179 &dmap.copy_map,
179 &dmap.copy_map,
180 options,
180 options,
181 ),
181 ),
182 )));
182 )));
183 }
183 }
184 } else {
184 } else {
185 if dmap.contains_key(normalized) {
185 if dmap.contains_key(normalized) {
186 return Some(Ok((normalized, Dispatch::Removed)));
186 return Some(Ok((normalized, Dispatch::Removed)));
187 }
187 }
188 }
188 }
189 }
189 }
190 Err(_) => {
190 Err(_) => {
191 if let Some(entry) = dmap.get(normalized) {
191 if let Some(entry) = dmap.get(normalized) {
192 return Some(Ok((
192 return Some(Ok((
193 normalized,
193 normalized,
194 dispatch_missing(entry.state),
194 dispatch_missing(entry.state),
195 )));
195 )));
196 }
196 }
197 }
197 }
198 };
198 };
199 None
199 None
200 })
200 })
201 }
201 }
202
202
203 #[derive(Debug, Copy, Clone)]
203 #[derive(Debug, Copy, Clone)]
204 pub struct StatusOptions {
204 pub struct StatusOptions {
205 /// Remember the most recent modification timeslot for status, to make
205 /// Remember the most recent modification timeslot for status, to make
206 /// sure we won't miss future size-preserving file content modifications
206 /// sure we won't miss future size-preserving file content modifications
207 /// that happen within the same timeslot.
207 /// that happen within the same timeslot.
208 pub last_normal_time: i64,
208 pub last_normal_time: i64,
209 /// Whether we are on a filesystem with UNIX-like exec flags
209 /// Whether we are on a filesystem with UNIX-like exec flags
210 pub check_exec: bool,
210 pub check_exec: bool,
211 pub list_clean: bool,
211 pub list_clean: bool,
212 }
212 }
213
213
214 /// Stat all entries in the `DirstateMap` and mark them for dispatch into
214 /// Stat all entries in the `DirstateMap` and mark them for dispatch into
215 /// the relevant collections.
215 /// the relevant collections.
216 fn stat_dmap_entries(
216 fn stat_dmap_entries(
217 dmap: &DirstateMap,
217 dmap: &DirstateMap,
218 root_dir: impl AsRef<Path> + Sync + Send,
218 root_dir: impl AsRef<Path> + Sync + Send,
219 options: StatusOptions,
219 options: StatusOptions,
220 ) -> impl ParallelIterator<Item = IoResult<(&HgPath, Dispatch)>> {
220 ) -> impl ParallelIterator<Item = IoResult<(&HgPath, Dispatch)>> {
221 dmap.par_iter().map(move |(filename, entry)| {
221 dmap.par_iter().map(move |(filename, entry)| {
222 let filename: &HgPath = filename;
222 let filename: &HgPath = filename;
223 let filename_as_path = hg_path_to_path_buf(filename)?;
223 let filename_as_path = hg_path_to_path_buf(filename)?;
224 let meta = root_dir.as_ref().join(filename_as_path).symlink_metadata();
224 let meta = root_dir.as_ref().join(filename_as_path).symlink_metadata();
225
225
226 match meta {
226 match meta {
227 Ok(ref m)
227 Ok(ref m)
228 if !(m.file_type().is_file()
228 if !(m.file_type().is_file()
229 || m.file_type().is_symlink()) =>
229 || m.file_type().is_symlink()) =>
230 {
230 {
231 Ok((filename, dispatch_missing(entry.state)))
231 Ok((filename, dispatch_missing(entry.state)))
232 }
232 }
233 Ok(m) => Ok((
233 Ok(m) => Ok((
234 filename,
234 filename,
235 dispatch_found(
235 dispatch_found(
236 filename,
236 filename,
237 *entry,
237 *entry,
238 HgMetadata::from_metadata(m),
238 HgMetadata::from_metadata(m),
239 &dmap.copy_map,
239 &dmap.copy_map,
240 options,
240 options,
241 ),
241 ),
242 )),
242 )),
243 Err(ref e)
243 Err(ref e)
244 if e.kind() == std::io::ErrorKind::NotFound
244 if e.kind() == std::io::ErrorKind::NotFound
245 || e.raw_os_error() == Some(20) =>
245 || e.raw_os_error() == Some(20) =>
246 {
246 {
247 // Rust does not yet have an `ErrorKind` for
247 // Rust does not yet have an `ErrorKind` for
248 // `NotADirectory` (errno 20)
248 // `NotADirectory` (errno 20)
249 // It happens if the dirstate contains `foo/bar` and
249 // It happens if the dirstate contains `foo/bar` and
250 // foo is not a directory
250 // foo is not a directory
251 Ok((filename, dispatch_missing(entry.state)))
251 Ok((filename, dispatch_missing(entry.state)))
252 }
252 }
253 Err(e) => Err(e),
253 Err(e) => Err(e),
254 }
254 }
255 })
255 })
256 }
256 }
257
257
258 pub struct StatusResult<'a> {
258 pub struct DirstateStatus<'a> {
259 pub modified: Vec<&'a HgPath>,
259 pub modified: Vec<&'a HgPath>,
260 pub added: Vec<&'a HgPath>,
260 pub added: Vec<&'a HgPath>,
261 pub removed: Vec<&'a HgPath>,
261 pub removed: Vec<&'a HgPath>,
262 pub deleted: Vec<&'a HgPath>,
262 pub deleted: Vec<&'a HgPath>,
263 pub clean: Vec<&'a HgPath>,
263 pub clean: Vec<&'a HgPath>,
264 /* TODO ignored
264 /* TODO ignored
265 * TODO unknown */
265 * TODO unknown */
266 }
266 }
267
267
268 fn build_response<'a>(
268 fn build_response<'a>(
269 results: impl IntoIterator<Item = IoResult<(&'a HgPath, Dispatch)>>,
269 results: impl IntoIterator<Item = IoResult<(&'a HgPath, Dispatch)>>,
270 ) -> IoResult<(Vec<&'a HgPath>, StatusResult<'a>)> {
270 ) -> IoResult<(Vec<&'a HgPath>, DirstateStatus<'a>)> {
271 let mut lookup = vec![];
271 let mut lookup = vec![];
272 let mut modified = vec![];
272 let mut modified = vec![];
273 let mut added = vec![];
273 let mut added = vec![];
274 let mut removed = vec![];
274 let mut removed = vec![];
275 let mut deleted = vec![];
275 let mut deleted = vec![];
276 let mut clean = vec![];
276 let mut clean = vec![];
277
277
278 for res in results.into_iter() {
278 for res in results.into_iter() {
279 let (filename, dispatch) = res?;
279 let (filename, dispatch) = res?;
280 match dispatch {
280 match dispatch {
281 Dispatch::Unknown => {}
281 Dispatch::Unknown => {}
282 Dispatch::Unsure => lookup.push(filename),
282 Dispatch::Unsure => lookup.push(filename),
283 Dispatch::Modified => modified.push(filename),
283 Dispatch::Modified => modified.push(filename),
284 Dispatch::Added => added.push(filename),
284 Dispatch::Added => added.push(filename),
285 Dispatch::Removed => removed.push(filename),
285 Dispatch::Removed => removed.push(filename),
286 Dispatch::Deleted => deleted.push(filename),
286 Dispatch::Deleted => deleted.push(filename),
287 Dispatch::Clean => clean.push(filename),
287 Dispatch::Clean => clean.push(filename),
288 }
288 }
289 }
289 }
290
290
291 Ok((
291 Ok((
292 lookup,
292 lookup,
293 StatusResult {
293 DirstateStatus {
294 modified,
294 modified,
295 added,
295 added,
296 removed,
296 removed,
297 deleted,
297 deleted,
298 clean,
298 clean,
299 },
299 },
300 ))
300 ))
301 }
301 }
302
302
303 pub fn status<'a: 'c, 'b: 'c, 'c>(
303 pub fn status<'a: 'c, 'b: 'c, 'c>(
304 dmap: &'a DirstateMap,
304 dmap: &'a DirstateMap,
305 matcher: &'b impl Matcher,
305 matcher: &'b impl Matcher,
306 root_dir: impl AsRef<Path> + Sync + Send + Copy,
306 root_dir: impl AsRef<Path> + Sync + Send + Copy,
307 options: StatusOptions,
307 options: StatusOptions,
308 ) -> IoResult<(Vec<&'c HgPath>, StatusResult<'c>)> {
308 ) -> IoResult<(Vec<&'c HgPath>, DirstateStatus<'c>)> {
309 let files = matcher.file_set();
309 let files = matcher.file_set();
310 let mut results = vec![];
310 let mut results = vec![];
311 if let Some(files) = files {
311 if let Some(files) = files {
312 results.par_extend(walk_explicit(&files, &dmap, root_dir, options));
312 results.par_extend(walk_explicit(&files, &dmap, root_dir, options));
313 }
313 }
314
314
315 if !matcher.is_exact() {
315 if !matcher.is_exact() {
316 let stat_results = stat_dmap_entries(&dmap, root_dir, options);
316 let stat_results = stat_dmap_entries(&dmap, root_dir, options);
317 results.par_extend(stat_results);
317 results.par_extend(stat_results);
318 }
318 }
319
319
320 build_response(results)
320 build_response(results)
321 }
321 }
@@ -1,184 +1,184 b''
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 // and Mercurial contributors
2 // and Mercurial contributors
3 //
3 //
4 // This software may be used and distributed according to the terms of the
4 // This software may be used and distributed according to the terms of the
5 // GNU General Public License version 2 or any later version.
5 // GNU General Public License version 2 or any later version.
6 mod ancestors;
6 mod ancestors;
7 pub mod dagops;
7 pub mod dagops;
8 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
8 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
9 mod dirstate;
9 mod dirstate;
10 pub mod discovery;
10 pub mod discovery;
11 pub mod testing; // unconditionally built, for use from integration tests
11 pub mod testing; // unconditionally built, for use from integration tests
12 pub use dirstate::{
12 pub use dirstate::{
13 dirs_multiset::{DirsMultiset, DirsMultisetIter},
13 dirs_multiset::{DirsMultiset, DirsMultisetIter},
14 dirstate_map::DirstateMap,
14 dirstate_map::DirstateMap,
15 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
15 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
16 status::{status, StatusOptions, StatusResult},
16 status::{status, DirstateStatus, StatusOptions},
17 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
17 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
18 StateMap, StateMapIter,
18 StateMap, StateMapIter,
19 };
19 };
20 mod filepatterns;
20 mod filepatterns;
21 pub mod matchers;
21 pub mod matchers;
22 pub mod revlog;
22 pub mod revlog;
23 pub use revlog::*;
23 pub use revlog::*;
24 #[cfg(feature = "with-re2")]
24 #[cfg(feature = "with-re2")]
25 pub mod re2;
25 pub mod re2;
26 pub mod utils;
26 pub mod utils;
27
27
28 use crate::utils::hg_path::{HgPathBuf, HgPathError};
28 use crate::utils::hg_path::{HgPathBuf, HgPathError};
29 pub use filepatterns::{
29 pub use filepatterns::{
30 parse_pattern_syntax, read_pattern_file, IgnorePattern,
30 parse_pattern_syntax, read_pattern_file, IgnorePattern,
31 PatternFileWarning, PatternSyntax,
31 PatternFileWarning, PatternSyntax,
32 };
32 };
33 use std::collections::HashMap;
33 use std::collections::HashMap;
34 use twox_hash::RandomXxHashBuilder64;
34 use twox_hash::RandomXxHashBuilder64;
35
35
36 pub type LineNumber = usize;
36 pub type LineNumber = usize;
37
37
38 /// Rust's default hasher is too slow because it tries to prevent collision
38 /// Rust's default hasher is too slow because it tries to prevent collision
39 /// attacks. We are not concerned about those: if an ill-minded person has
39 /// attacks. We are not concerned about those: if an ill-minded person has
40 /// write access to your repository, you have other issues.
40 /// write access to your repository, you have other issues.
41 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
41 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
42
42
43 #[derive(Clone, Debug, PartialEq)]
43 #[derive(Clone, Debug, PartialEq)]
44 pub enum DirstateParseError {
44 pub enum DirstateParseError {
45 TooLittleData,
45 TooLittleData,
46 Overflow,
46 Overflow,
47 CorruptedEntry(String),
47 CorruptedEntry(String),
48 Damaged,
48 Damaged,
49 }
49 }
50
50
51 impl From<std::io::Error> for DirstateParseError {
51 impl From<std::io::Error> for DirstateParseError {
52 fn from(e: std::io::Error) -> Self {
52 fn from(e: std::io::Error) -> Self {
53 DirstateParseError::CorruptedEntry(e.to_string())
53 DirstateParseError::CorruptedEntry(e.to_string())
54 }
54 }
55 }
55 }
56
56
57 impl ToString for DirstateParseError {
57 impl ToString for DirstateParseError {
58 fn to_string(&self) -> String {
58 fn to_string(&self) -> String {
59 use crate::DirstateParseError::*;
59 use crate::DirstateParseError::*;
60 match self {
60 match self {
61 TooLittleData => "Too little data for dirstate.".to_string(),
61 TooLittleData => "Too little data for dirstate.".to_string(),
62 Overflow => "Overflow in dirstate.".to_string(),
62 Overflow => "Overflow in dirstate.".to_string(),
63 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
63 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
64 Damaged => "Dirstate appears to be damaged.".to_string(),
64 Damaged => "Dirstate appears to be damaged.".to_string(),
65 }
65 }
66 }
66 }
67 }
67 }
68
68
69 #[derive(Debug, PartialEq)]
69 #[derive(Debug, PartialEq)]
70 pub enum DirstatePackError {
70 pub enum DirstatePackError {
71 CorruptedEntry(String),
71 CorruptedEntry(String),
72 CorruptedParent,
72 CorruptedParent,
73 BadSize(usize, usize),
73 BadSize(usize, usize),
74 }
74 }
75
75
76 impl From<std::io::Error> for DirstatePackError {
76 impl From<std::io::Error> for DirstatePackError {
77 fn from(e: std::io::Error) -> Self {
77 fn from(e: std::io::Error) -> Self {
78 DirstatePackError::CorruptedEntry(e.to_string())
78 DirstatePackError::CorruptedEntry(e.to_string())
79 }
79 }
80 }
80 }
81 #[derive(Debug, PartialEq)]
81 #[derive(Debug, PartialEq)]
82 pub enum DirstateMapError {
82 pub enum DirstateMapError {
83 PathNotFound(HgPathBuf),
83 PathNotFound(HgPathBuf),
84 EmptyPath,
84 EmptyPath,
85 InvalidPath(HgPathError),
85 InvalidPath(HgPathError),
86 }
86 }
87
87
88 impl ToString for DirstateMapError {
88 impl ToString for DirstateMapError {
89 fn to_string(&self) -> String {
89 fn to_string(&self) -> String {
90 match self {
90 match self {
91 DirstateMapError::PathNotFound(_) => {
91 DirstateMapError::PathNotFound(_) => {
92 "expected a value, found none".to_string()
92 "expected a value, found none".to_string()
93 }
93 }
94 DirstateMapError::EmptyPath => "Overflow in dirstate.".to_string(),
94 DirstateMapError::EmptyPath => "Overflow in dirstate.".to_string(),
95 DirstateMapError::InvalidPath(e) => e.to_string(),
95 DirstateMapError::InvalidPath(e) => e.to_string(),
96 }
96 }
97 }
97 }
98 }
98 }
99
99
100 pub enum DirstateError {
100 pub enum DirstateError {
101 Parse(DirstateParseError),
101 Parse(DirstateParseError),
102 Pack(DirstatePackError),
102 Pack(DirstatePackError),
103 Map(DirstateMapError),
103 Map(DirstateMapError),
104 IO(std::io::Error),
104 IO(std::io::Error),
105 }
105 }
106
106
107 impl From<DirstateParseError> for DirstateError {
107 impl From<DirstateParseError> for DirstateError {
108 fn from(e: DirstateParseError) -> Self {
108 fn from(e: DirstateParseError) -> Self {
109 DirstateError::Parse(e)
109 DirstateError::Parse(e)
110 }
110 }
111 }
111 }
112
112
113 impl From<DirstatePackError> for DirstateError {
113 impl From<DirstatePackError> for DirstateError {
114 fn from(e: DirstatePackError) -> Self {
114 fn from(e: DirstatePackError) -> Self {
115 DirstateError::Pack(e)
115 DirstateError::Pack(e)
116 }
116 }
117 }
117 }
118
118
119 #[derive(Debug)]
119 #[derive(Debug)]
120 pub enum PatternError {
120 pub enum PatternError {
121 Path(HgPathError),
121 Path(HgPathError),
122 UnsupportedSyntax(String),
122 UnsupportedSyntax(String),
123 UnsupportedSyntaxInFile(String, String, usize),
123 UnsupportedSyntaxInFile(String, String, usize),
124 TooLong(usize),
124 TooLong(usize),
125 IO(std::io::Error),
125 IO(std::io::Error),
126 /// Needed a pattern that can be turned into a regex but got one that
126 /// Needed a pattern that can be turned into a regex but got one that
127 /// can't. This should only happen through programmer error.
127 /// can't. This should only happen through programmer error.
128 NonRegexPattern(IgnorePattern),
128 NonRegexPattern(IgnorePattern),
129 /// This is temporary, see `re2/mod.rs`.
129 /// This is temporary, see `re2/mod.rs`.
130 /// This will cause a fallback to Python.
130 /// This will cause a fallback to Python.
131 Re2NotInstalled,
131 Re2NotInstalled,
132 }
132 }
133
133
134 impl ToString for PatternError {
134 impl ToString for PatternError {
135 fn to_string(&self) -> String {
135 fn to_string(&self) -> String {
136 match self {
136 match self {
137 PatternError::UnsupportedSyntax(syntax) => {
137 PatternError::UnsupportedSyntax(syntax) => {
138 format!("Unsupported syntax {}", syntax)
138 format!("Unsupported syntax {}", syntax)
139 }
139 }
140 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
140 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
141 format!(
141 format!(
142 "{}:{}: unsupported syntax {}",
142 "{}:{}: unsupported syntax {}",
143 file_path, line, syntax
143 file_path, line, syntax
144 )
144 )
145 }
145 }
146 PatternError::TooLong(size) => {
146 PatternError::TooLong(size) => {
147 format!("matcher pattern is too long ({} bytes)", size)
147 format!("matcher pattern is too long ({} bytes)", size)
148 }
148 }
149 PatternError::IO(e) => e.to_string(),
149 PatternError::IO(e) => e.to_string(),
150 PatternError::Path(e) => e.to_string(),
150 PatternError::Path(e) => e.to_string(),
151 PatternError::NonRegexPattern(pattern) => {
151 PatternError::NonRegexPattern(pattern) => {
152 format!("'{:?}' cannot be turned into a regex", pattern)
152 format!("'{:?}' cannot be turned into a regex", pattern)
153 }
153 }
154 PatternError::Re2NotInstalled => {
154 PatternError::Re2NotInstalled => {
155 "Re2 is not installed, cannot use regex functionality."
155 "Re2 is not installed, cannot use regex functionality."
156 .to_string()
156 .to_string()
157 }
157 }
158 }
158 }
159 }
159 }
160 }
160 }
161
161
162 impl From<DirstateMapError> for DirstateError {
162 impl From<DirstateMapError> for DirstateError {
163 fn from(e: DirstateMapError) -> Self {
163 fn from(e: DirstateMapError) -> Self {
164 DirstateError::Map(e)
164 DirstateError::Map(e)
165 }
165 }
166 }
166 }
167
167
168 impl From<std::io::Error> for DirstateError {
168 impl From<std::io::Error> for DirstateError {
169 fn from(e: std::io::Error) -> Self {
169 fn from(e: std::io::Error) -> Self {
170 DirstateError::IO(e)
170 DirstateError::IO(e)
171 }
171 }
172 }
172 }
173
173
174 impl From<std::io::Error> for PatternError {
174 impl From<std::io::Error> for PatternError {
175 fn from(e: std::io::Error) -> Self {
175 fn from(e: std::io::Error) -> Self {
176 PatternError::IO(e)
176 PatternError::IO(e)
177 }
177 }
178 }
178 }
179
179
180 impl From<HgPathError> for PatternError {
180 impl From<HgPathError> for PatternError {
181 fn from(e: HgPathError) -> Self {
181 fn from(e: HgPathError) -> Self {
182 PatternError::Path(e)
182 PatternError::Path(e)
183 }
183 }
184 }
184 }
@@ -1,129 +1,129 b''
1 // status.rs
1 // status.rs
2 //
2 //
3 // Copyright 2019, Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019, Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::status` module provided by the
8 //! Bindings for the `hg::status` module provided by the
9 //! `hg-core` crate. From Python, this will be seen as
9 //! `hg-core` crate. From Python, this will be seen as
10 //! `rustext.dirstate.status`.
10 //! `rustext.dirstate.status`.
11
11
12 use crate::dirstate::DirstateMap;
12 use crate::dirstate::DirstateMap;
13 use cpython::exc::ValueError;
13 use cpython::exc::ValueError;
14 use cpython::{
14 use cpython::{
15 ObjectProtocol, PyBytes, PyErr, PyList, PyObject, PyResult, PyTuple,
15 ObjectProtocol, PyBytes, PyErr, PyList, PyObject, PyResult, PyTuple,
16 Python, PythonObject, ToPyObject,
16 Python, PythonObject, ToPyObject,
17 };
17 };
18 use hg::utils::hg_path::HgPathBuf;
18 use hg::utils::hg_path::HgPathBuf;
19 use hg::{
19 use hg::{
20 matchers::{AlwaysMatcher, FileMatcher},
20 matchers::{AlwaysMatcher, FileMatcher},
21 status,
21 status,
22 utils::{files::get_path_from_bytes, hg_path::HgPath},
22 utils::{files::get_path_from_bytes, hg_path::HgPath},
23 StatusResult,
23 DirstateStatus,
24 };
24 };
25 use std::borrow::Borrow;
25 use std::borrow::Borrow;
26
26
27 /// This will be useless once trait impls for collection are added to `PyBytes`
27 /// This will be useless once trait impls for collection are added to `PyBytes`
28 /// upstream.
28 /// upstream.
29 fn collect_pybytes_list<P: AsRef<HgPath>>(
29 fn collect_pybytes_list<P: AsRef<HgPath>>(
30 py: Python,
30 py: Python,
31 collection: &[P],
31 collection: &[P],
32 ) -> PyList {
32 ) -> PyList {
33 let list = PyList::new(py, &[]);
33 let list = PyList::new(py, &[]);
34
34
35 for (i, path) in collection.iter().enumerate() {
35 for (i, path) in collection.iter().enumerate() {
36 list.insert(
36 list.insert(
37 py,
37 py,
38 i,
38 i,
39 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
39 PyBytes::new(py, path.as_ref().as_bytes()).into_object(),
40 )
40 )
41 }
41 }
42
42
43 list
43 list
44 }
44 }
45
45
46 pub fn status_wrapper(
46 pub fn status_wrapper(
47 py: Python,
47 py: Python,
48 dmap: DirstateMap,
48 dmap: DirstateMap,
49 matcher: PyObject,
49 matcher: PyObject,
50 root_dir: PyObject,
50 root_dir: PyObject,
51 list_clean: bool,
51 list_clean: bool,
52 last_normal_time: i64,
52 last_normal_time: i64,
53 check_exec: bool,
53 check_exec: bool,
54 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
54 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
55 let bytes = root_dir.extract::<PyBytes>(py)?;
55 let bytes = root_dir.extract::<PyBytes>(py)?;
56 let root_dir = get_path_from_bytes(bytes.data(py));
56 let root_dir = get_path_from_bytes(bytes.data(py));
57
57
58 let dmap: DirstateMap = dmap.to_py_object(py);
58 let dmap: DirstateMap = dmap.to_py_object(py);
59 let dmap = dmap.get_inner(py);
59 let dmap = dmap.get_inner(py);
60
60
61 match matcher.get_type(py).name(py).borrow() {
61 match matcher.get_type(py).name(py).borrow() {
62 "alwaysmatcher" => {
62 "alwaysmatcher" => {
63 let matcher = AlwaysMatcher;
63 let matcher = AlwaysMatcher;
64 let (lookup, status_res) = status(
64 let (lookup, status_res) = status(
65 &dmap,
65 &dmap,
66 &matcher,
66 &matcher,
67 &root_dir,
67 &root_dir,
68 list_clean,
68 list_clean,
69 last_normal_time,
69 last_normal_time,
70 check_exec,
70 check_exec,
71 )
71 )
72 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
72 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
73 build_response(lookup, status_res, py)
73 build_response(lookup, status_res, py)
74 }
74 }
75 "exactmatcher" => {
75 "exactmatcher" => {
76 let files = matcher.call_method(
76 let files = matcher.call_method(
77 py,
77 py,
78 "files",
78 "files",
79 PyTuple::new(py, &[]),
79 PyTuple::new(py, &[]),
80 None,
80 None,
81 )?;
81 )?;
82 let files: PyList = files.cast_into(py)?;
82 let files: PyList = files.cast_into(py)?;
83 let files: PyResult<Vec<HgPathBuf>> = files
83 let files: PyResult<Vec<HgPathBuf>> = files
84 .iter(py)
84 .iter(py)
85 .map(|f| {
85 .map(|f| {
86 Ok(HgPathBuf::from_bytes(
86 Ok(HgPathBuf::from_bytes(
87 f.extract::<PyBytes>(py)?.data(py),
87 f.extract::<PyBytes>(py)?.data(py),
88 ))
88 ))
89 })
89 })
90 .collect();
90 .collect();
91
91
92 let files = files?;
92 let files = files?;
93 let matcher = FileMatcher::new(&files)
93 let matcher = FileMatcher::new(&files)
94 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
94 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
95 let (lookup, status_res) = status(
95 let (lookup, status_res) = status(
96 &dmap,
96 &dmap,
97 &matcher,
97 &matcher,
98 &root_dir,
98 &root_dir,
99 list_clean,
99 list_clean,
100 last_normal_time,
100 last_normal_time,
101 check_exec,
101 check_exec,
102 )
102 )
103 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
103 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
104 build_response(lookup, status_res, py)
104 build_response(lookup, status_res, py)
105 }
105 }
106 e => {
106 e => {
107 return Err(PyErr::new::<ValueError, _>(
107 return Err(PyErr::new::<ValueError, _>(
108 py,
108 py,
109 format!("Unsupported matcher {}", e),
109 format!("Unsupported matcher {}", e),
110 ));
110 ));
111 }
111 }
112 }
112 }
113 }
113 }
114
114
115 fn build_response(
115 fn build_response(
116 lookup: Vec<&HgPath>,
116 lookup: Vec<&HgPath>,
117 status_res: StatusResult,
117 status_res: DirstateStatus,
118 py: Python,
118 py: Python,
119 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
119 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
120 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
120 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
121 let added = collect_pybytes_list(py, status_res.added.as_ref());
121 let added = collect_pybytes_list(py, status_res.added.as_ref());
122 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
122 let removed = collect_pybytes_list(py, status_res.removed.as_ref());
123 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
123 let deleted = collect_pybytes_list(py, status_res.deleted.as_ref());
124 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
124 let clean = collect_pybytes_list(py, status_res.clean.as_ref());
125 let lookup = collect_pybytes_list(py, lookup.as_ref());
125 let lookup = collect_pybytes_list(py, lookup.as_ref());
126 let unknown = PyList::new(py, &[]);
126 let unknown = PyList::new(py, &[]);
127
127
128 Ok((lookup, modified, added, removed, deleted, unknown, clean))
128 Ok((lookup, modified, added, removed, deleted, unknown, clean))
129 }
129 }
General Comments 0
You need to be logged in to leave comments. Login now