hg-core: define a `dirstate_status` `Operation`
authorRaphaël Gomès <rgomes@octobus.net>
Wed, 24 Jun 2020 17:53:44 +0200
changeset 45113 98817e5daca7
parent 45112 470d306e616c
child 45114 e2d17974a869
hg-core: define a `dirstate_status` `Operation` This is 3/3 in a series of patches to improve dirstate status' code. Following in the footsteps of a46e36b82461, we move the main status functionality to an `Operation`. This will most likely be subject to change in the future (what function signature, what parameters, etc., but we will see when `rhg` gets `hg status` support. Differential Revision: https://phab.mercurial-scm.org/D8663
rust/hg-core/src/dirstate/status.rs
rust/hg-core/src/operations/dirstate_status.rs
rust/hg-core/src/operations/mod.rs
--- a/rust/hg-core/src/dirstate/status.rs	Wed Jun 24 17:20:39 2020 +0200
+++ b/rust/hg-core/src/dirstate/status.rs	Wed Jun 24 17:53:44 2020 +0200
@@ -13,6 +13,7 @@
     dirstate::SIZE_FROM_OTHER_PARENT,
     filepatterns::PatternFileWarning,
     matchers::{get_ignore_function, Matcher, VisitChildrenSet},
+    operations::Operation,
     utils::{
         files::{find_dirs, HgMetadata},
         hg_path::{
@@ -101,7 +102,7 @@
 
 /// We have a good mix of owned (from directory traversal) and borrowed (from
 /// the dirstate/explicit) paths, this comes up a lot.
-type HgPathCow<'a> = Cow<'a, HgPath>;
+pub type HgPathCow<'a> = Cow<'a, HgPath>;
 
 /// A path with its computed ``Dispatch`` information
 type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch);
@@ -294,9 +295,9 @@
 /// and how, compared to the revision we're based on
 pub struct Status<'a, M: Matcher + Sync> {
     dmap: &'a DirstateMap,
-    matcher: &'a M,
+    pub(crate) matcher: &'a M,
     root_dir: PathBuf,
-    options: StatusOptions,
+    pub(crate) options: StatusOptions,
     ignore_fn: IgnoreFnType<'a>,
 }
 
@@ -708,7 +709,7 @@
     /// This takes a mutable reference to the results to account for the
     /// `extend` in timings
     #[timed]
-    fn handle_unknowns(
+    pub fn handle_unknowns(
         &self,
         results: &mut Vec<DispatchedPath<'a>>,
     ) -> IoResult<()> {
@@ -787,7 +788,7 @@
     /// This takes a mutable reference to the results to account for the
     /// `extend` in timings
     #[timed]
-    fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
+    pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
         results.par_extend(self.dmap.par_iter().flat_map(
             move |(filename, entry)| {
                 let filename: &HgPath = filename;
@@ -837,7 +838,7 @@
 }
 
 #[timed]
-fn build_response<'a>(
+pub fn build_response<'a>(
     results: impl IntoIterator<Item = DispatchedPath<'a>>,
     traversed: Vec<HgPathBuf>,
 ) -> (Vec<HgPathCow<'a>>, DirstateStatus<'a>) {
@@ -899,56 +900,8 @@
     (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
     Vec<PatternFileWarning>,
 )> {
-    let (traversed_sender, traversed_receiver) =
-        crossbeam::channel::unbounded();
-    let (st, warnings) =
+    let (status, warnings) =
         Status::new(dmap, matcher, root_dir, ignore_files, options)?;
 
-    // Step 1: check the files explicitly mentioned by the user
-    let (work, mut results) = st.walk_explicit(traversed_sender.clone());
-
-    if !work.is_empty() {
-        // Hashmaps are quite a bit slower to build than vecs, so only build it
-        // if needed.
-        let old_results = results.iter().cloned().collect();
-
-        // Step 2: recursively check the working directory for changes if
-        // needed
-        for (dir, dispatch) in work {
-            match dispatch {
-                Dispatch::Directory { was_file } => {
-                    if was_file {
-                        results.push((dir.to_owned(), Dispatch::Removed));
-                    }
-                    if options.list_ignored
-                        || options.list_unknown && !st.dir_ignore(&dir)
-                    {
-                        st.traverse(
-                            &dir,
-                            &old_results,
-                            &mut results,
-                            traversed_sender.clone(),
-                        )?;
-                    }
-                }
-                _ => unreachable!("There can only be directories in `work`"),
-            }
-        }
-    }
-
-    if !matcher.is_exact() {
-        if options.list_unknown {
-            st.handle_unknowns(&mut results)?;
-        } else {
-            // TODO this is incorrect, see issue6335
-            // This requires a fix in both Python and Rust that can happen
-            // with other pending changes to `status`.
-            st.extend_from_dmap(&mut results);
-        }
-    }
-
-    drop(traversed_sender);
-    let traversed = traversed_receiver.into_iter().collect();
-
-    Ok((build_response(results, traversed), warnings))
+    Ok((status.run()?, warnings))
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/operations/dirstate_status.rs	Wed Jun 24 17:53:44 2020 +0200
@@ -0,0 +1,76 @@
+// dirstate_status.rs
+//
+// Copyright 2019, Raphaël Gomès <rgomes@octobus.net>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+use crate::dirstate::status::{build_response, Dispatch, HgPathCow, Status};
+use crate::matchers::Matcher;
+use crate::operations::Operation;
+use crate::{DirstateStatus, StatusError};
+
+/// A tuple of the paths that need to be checked in the filelog because it's
+/// ambiguous whether they've changed, and the rest of the already dispatched
+/// files.
+pub type LookupAndStatus<'a> = (Vec<HgPathCow<'a>>, DirstateStatus<'a>);
+
+impl<'a, M: Matcher + Sync> Operation<LookupAndStatus<'a>> for Status<'a, M> {
+    type Error = StatusError;
+
+    fn run(&self) -> Result<LookupAndStatus<'a>, Self::Error> {
+        let (traversed_sender, traversed_receiver) =
+            crossbeam::channel::unbounded();
+
+        // Step 1: check the files explicitly mentioned by the user
+        let (work, mut results) = self.walk_explicit(traversed_sender.clone());
+
+        if !work.is_empty() {
+            // Hashmaps are quite a bit slower to build than vecs, so only
+            // build it if needed.
+            let old_results = results.iter().cloned().collect();
+
+            // Step 2: recursively check the working directory for changes if
+            // needed
+            for (dir, dispatch) in work {
+                match dispatch {
+                    Dispatch::Directory { was_file } => {
+                        if was_file {
+                            results.push((dir.to_owned(), Dispatch::Removed));
+                        }
+                        if self.options.list_ignored
+                            || self.options.list_unknown
+                                && !self.dir_ignore(&dir)
+                        {
+                            self.traverse(
+                                &dir,
+                                &old_results,
+                                &mut results,
+                                traversed_sender.clone(),
+                            )?;
+                        }
+                    }
+                    _ => {
+                        unreachable!("There can only be directories in `work`")
+                    }
+                }
+            }
+        }
+
+        if !self.matcher.is_exact() {
+            if self.options.list_unknown {
+                self.handle_unknowns(&mut results)?;
+            } else {
+                // TODO this is incorrect, see issue6335
+                // This requires a fix in both Python and Rust that can happen
+                // with other pending changes to `status`.
+                self.extend_from_dmap(&mut results);
+            }
+        }
+
+        drop(traversed_sender);
+        let traversed = traversed_receiver.into_iter().collect();
+
+        Ok(build_response(results, traversed))
+    }
+}
--- a/rust/hg-core/src/operations/mod.rs	Wed Jun 24 17:20:39 2020 +0200
+++ b/rust/hg-core/src/operations/mod.rs	Wed Jun 24 17:53:44 2020 +0200
@@ -1,3 +1,4 @@
+mod dirstate_status;
 mod find_root;
 pub use find_root::{FindRoot, FindRootError, FindRootErrorKind};