D7928: rust-status: add function for sequential traversal of the working directory

2020-03-11 Thread Raphaël Gomès
Closed by commit rHG1debb5894b39: rust-status: add function for sequential 
traversal of the working directory (authored by Alphare).
This revision was automatically updated to reflect the committed changes.
This revision was not accepted when it landed; it landed in state "Needs 
Review".

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7928?vs=20461=20720

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,18 +11,21 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
 /// Wrong type of file from a `BadMatch`
@@ -238,6 +241,178 @@
 /// Whether we are on a filesystem with UNIX-like exec flags
 pub check_exec: bool,
 pub list_clean: bool,
+pub list_unknown: bool,
+pub list_ignored: bool,
+}
+
+/// Dispatch a single file found during `traverse`.
+/// If `file` is a folder that needs to be traversed, it will be pushed into
+/// `work`.
+fn traverse_worker<'a>(
+work:  VecDeque,
+matcher:  Matcher,
+dmap: ,
+filename: impl AsRef,
+dir_entry: ,
+ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+dir_ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+options: StatusOptions,
+) -> Option, Dispatch)>> {
+let file_type = match dir_entry.file_type() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+let filename = filename.as_ref();
+let entry_option = dmap.get(filename);
+
+if file_type.is_dir() {
+// Do we need to traverse it?
+if !ignore_fn() || options.list_ignored {
+work.push_front(filename.to_owned());
+}
+// Nested `if` until `rust-lang/rust#53668` is stable
+if let Some(entry) = entry_option {
+// Used to be a file, is now a folder
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+} else if file_type.is_file() || file_type.is_symlink() {
+if let Some(entry) = entry_option {
+if matcher.matches_everything() || matcher.matches() {
+let metadata = match dir_entry.metadata() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_found(
+,
+*entry,
+HgMetadata::from_metadata(metadata),
+_map,
+options,
+),
+)));
+}
+} else if (matcher.matches_everything() || matcher.matches())
+&& !ignore_fn()
+{
+if (options.list_ignored || matcher.exact_match())
+&& dir_ignore_fn()
+{
+if options.list_ignored {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Ignored,
+)));
+}
+} else {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Unknown,
+)));
+}
+}
+} else if let Some(entry) = entry_option {
+// Used to be a file or a folder, now something else.
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+None
+}
+
+/// Walk the working directory recursively to look for changes compared to the
+/// current `DirstateMap`.
+fn traverse<'a>(
+matcher: &(impl Matcher + Sync),
+root_dir: impl AsRef,
+dmap: ,
+path: impl AsRef,
+old_results: FastHashMap, Dispatch>,
+ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+dir_ignore_fn: 

D7928: rust-status: add function for sequential traversal of the working directory

2020-03-04 Thread Raphaël Gomès
Alphare updated this revision to Diff 20461.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7928?vs=20453=20461

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,18 +11,21 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
 /// Wrong type of file from a `BadMatch`
@@ -238,6 +241,178 @@
 /// Whether we are on a filesystem with UNIX-like exec flags
 pub check_exec: bool,
 pub list_clean: bool,
+pub list_unknown: bool,
+pub list_ignored: bool,
+}
+
+/// Dispatch a single file found during `traverse`.
+/// If `file` is a folder that needs to be traversed, it will be pushed into
+/// `work`.
+fn traverse_worker<'a>(
+work:  VecDeque,
+matcher:  Matcher,
+dmap: ,
+filename: impl AsRef,
+dir_entry: ,
+ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+dir_ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+options: StatusOptions,
+) -> Option, Dispatch)>> {
+let file_type = match dir_entry.file_type() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+let filename = filename.as_ref();
+let entry_option = dmap.get(filename);
+
+if file_type.is_dir() {
+// Do we need to traverse it?
+if !ignore_fn() || options.list_ignored {
+work.push_front(filename.to_owned());
+}
+// Nested `if` until `rust-lang/rust#53668` is stable
+if let Some(entry) = entry_option {
+// Used to be a file, is now a folder
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+} else if file_type.is_file() || file_type.is_symlink() {
+if let Some(entry) = entry_option {
+if matcher.matches_everything() || matcher.matches() {
+let metadata = match dir_entry.metadata() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_found(
+,
+*entry,
+HgMetadata::from_metadata(metadata),
+_map,
+options,
+),
+)));
+}
+} else if (matcher.matches_everything() || matcher.matches())
+&& !ignore_fn()
+{
+if (options.list_ignored || matcher.exact_match())
+&& dir_ignore_fn()
+{
+if options.list_ignored {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Ignored,
+)));
+}
+} else {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Unknown,
+)));
+}
+}
+} else if let Some(entry) = entry_option {
+// Used to be a file or a folder, now something else.
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+None
+}
+
+/// Walk the working directory recursively to look for changes compared to the
+/// current `DirstateMap`.
+fn traverse<'a>(
+matcher: &(impl Matcher + Sync),
+root_dir: impl AsRef,
+dmap: ,
+path: impl AsRef,
+old_results: FastHashMap, Dispatch>,
+ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+dir_ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+options: StatusOptions,
+) -> IoResult, Dispatch>> {
+let root_dir = root_dir.as_ref();
+let mut new_results = FastHashMap::default();
+
+let mut work = 

D7928: rust-status: add function for sequential traversal of the working directory

2020-03-04 Thread Raphaël Gomès
Alphare updated this revision to Diff 20453.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7928?vs=20187=20453

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,18 +11,21 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
 /// Wrong type of file from a `BadMatch`
@@ -238,6 +241,164 @@
 /// Whether we are on a filesystem with UNIX-like exec flags
 pub check_exec: bool,
 pub list_clean: bool,
+pub list_unknown: bool,
+pub list_ignored: bool,
+}
+
+/// Dispatch a single file found during `traverse`.
+/// If `file` is a folder that needs to be traversed, it will be pushed into
+/// `work`.
+fn traverse_worker<'a>(
+work:  VecDeque,
+matcher:  Matcher,
+dmap: ,
+filename: impl AsRef,
+dir_entry: ,
+ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+options: StatusOptions,
+) -> Option, Dispatch)>> {
+let file_type = match dir_entry.file_type() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+let filename = filename.as_ref();
+let entry_option = dmap.get(filename);
+
+if file_type.is_dir() {
+// Do we need to traverse it?
+if !ignore_fn() || options.list_ignored {
+work.push_front(filename.to_owned());
+}
+// Nested `if` until `rust-lang/rust#53668` is stable
+if let Some(entry) = entry_option {
+// Used to be a file, is now a folder
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+} else if file_type.is_file() || file_type.is_symlink() {
+if let Some(entry) = entry_option {
+if matcher.matches_everything() || matcher.matches() {
+let metadata = match dir_entry.metadata() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_found(
+,
+*entry,
+HgMetadata::from_metadata(metadata),
+_map,
+options,
+),
+)));
+}
+} else if (matcher.matches_everything() || matcher.matches())
+&& !ignore_fn()
+{
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Unknown,
+)));
+} else if ignore_fn() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Ignored,
+)));
+}
+} else if let Some(entry) = entry_option {
+// Used to be a file or a folder, now something else.
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+None
+}
+
+/// Walk the working directory recursively to look for changes compared to the
+/// current `DirstateMap`.
+fn traverse<'a>(
+matcher: &(impl Matcher + Sync),
+root_dir: impl AsRef,
+dmap: ,
+path: impl AsRef,
+old_results: FastHashMap, Dispatch>,
+ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+options: StatusOptions,
+) -> IoResult, Dispatch>> {
+let root_dir = root_dir.as_ref();
+let mut new_results = FastHashMap::default();
+
+let mut work = VecDeque::new();
+work.push_front(path.as_ref().to_owned());
+
+while let Some(ref directory) = work.pop_front() {
+if directory.as_bytes() == b".hg" {
+continue;
+}
+let visit_entries = match matcher.visit_children_set(directory) {
+VisitChildrenSet::Empty => continue,
+VisitChildrenSet::This 

D7928: rust-status: add function for sequential traversal of the working directory

2020-02-13 Thread Raphaël Gomès
Alphare updated this revision to Diff 20187.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7928?vs=20047=20187

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,18 +11,21 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
 /// Wrong type of file from a `BadMatch`
@@ -238,6 +241,168 @@
 /// Whether we are on a filesystem with UNIX-like exec flags
 pub check_exec: bool,
 pub list_clean: bool,
+pub list_unknown: bool,
+pub list_ignored: bool,
+}
+
+/// Dispatch a single file found during `traverse`.
+/// If `file` is a folder that needs to be traversed, it will be pushed into
+/// `work`.
+fn traverse_worker<'a>(
+work:  VecDeque,
+matcher:  Matcher,
+dmap: ,
+filename: impl AsRef,
+dir_entry: ,
+ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+options: StatusOptions,
+) -> Option, Dispatch)>> {
+let file_type = match dir_entry.file_type() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+let filename = filename.as_ref();
+let entry_option = dmap.get(filename);
+
+if file_type.is_dir() {
+// Do we need to traverse it?
+if !ignore_fn() {
+work.push_front(filename.to_owned());
+} else {
+if options.list_ignored {
+work.push_front(filename.to_owned());
+}
+}
+// Nested `if` until `rust-lang/rust#53668` is stable
+if let Some(entry) = entry_option {
+// Used to be a file, is now a folder
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+} else if file_type.is_file() || file_type.is_symlink() {
+if let Some(entry) = entry_option {
+if matcher.matches_everything() || matcher.matches() {
+let metadata = match dir_entry.metadata() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_found(
+,
+*entry,
+HgMetadata::from_metadata(metadata),
+_map,
+options,
+),
+)));
+}
+} else if (matcher.matches_everything() || matcher.matches())
+&& !ignore_fn()
+{
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Unknown,
+)));
+} else if ignore_fn() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Ignored,
+)));
+}
+} else if let Some(entry) = entry_option {
+// Used to be a file or a folder, now something else.
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+None
+}
+
+/// Walk the working directory recursively to look for changes compared to the
+/// current `DirstateMap`.
+fn traverse<'a>(
+matcher: &(impl Matcher + Sync),
+root_dir: impl AsRef,
+dmap: ,
+path: impl AsRef,
+old_results: FastHashMap, Dispatch>,
+ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+options: StatusOptions,
+) -> IoResult, Dispatch>> {
+let root_dir = root_dir.as_ref();
+let mut new_results = FastHashMap::default();
+
+let mut work = VecDeque::new();
+work.push_front(path.as_ref().to_owned());
+
+while let Some(ref directory) = work.pop_front() {
+if directory.as_bytes() == b".hg" {
+continue;
+}
+let visit_entries = match 

D7928: rust-status: add function for sequential traversal of the working directory

2020-02-10 Thread Raphaël Gomès
Alphare updated this revision to Diff 20047.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7928?vs=19946=20047

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,18 +11,21 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
 /// Wrong type of file from a `BadMatch`
@@ -233,6 +236,168 @@
 /// Whether we are on a filesystem with UNIX-like exec flags
 pub check_exec: bool,
 pub list_clean: bool,
+pub list_unknown: bool,
+pub list_ignored: bool,
+}
+
+/// Dispatch a single file found during `traverse`.
+/// If `file` is a folder that needs to be traversed, it will be pushed into
+/// `work`.
+fn traverse_worker<'a>(
+work:  VecDeque,
+matcher:  Matcher,
+dmap: ,
+filename: impl AsRef,
+dir_entry: ,
+ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+options: StatusOptions,
+) -> Option, Dispatch)>> {
+let file_type = match dir_entry.file_type() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+let filename = filename.as_ref();
+let entry_option = dmap.get(filename);
+
+if file_type.is_dir() {
+// Do we need to traverse it?
+if !ignore_fn() {
+work.push_front(filename.to_owned());
+} else {
+if options.list_ignored {
+work.push_front(filename.to_owned());
+}
+}
+// Nested `if` until `rust-lang/rust#53668` is stable
+if let Some(entry) = entry_option {
+// Used to be a file, is now a folder
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+} else if file_type.is_file() || file_type.is_symlink() {
+if let Some(entry) = entry_option {
+if matcher.matches_everything() || matcher.matches() {
+let metadata = match dir_entry.metadata() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_found(
+,
+*entry,
+HgMetadata::from_metadata(metadata),
+_map,
+options,
+),
+)));
+}
+} else if (matcher.matches_everything() || matcher.matches())
+&& !ignore_fn()
+{
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Unknown,
+)));
+} else if ignore_fn() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Ignored,
+)));
+}
+} else if let Some(entry) = entry_option {
+// Used to be a file or a folder, now something else.
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+None
+}
+
+/// Walk the working directory recursively to look for changes compared to the
+/// current `DirstateMap`.
+fn traverse<'a>(
+matcher: &(impl Matcher + Sync),
+root_dir: impl AsRef,
+dmap: ,
+path: impl AsRef,
+old_results: FastHashMap, Dispatch>,
+ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+options: StatusOptions,
+) -> IoResult, Dispatch>> {
+let root_dir = root_dir.as_ref();
+let mut new_results = FastHashMap::default();
+
+let mut work = VecDeque::new();
+work.push_front(path.as_ref().to_owned());
+
+while let Some(ref directory) = work.pop_front() {
+if directory.as_bytes() == b".hg" {
+continue;
+}
+let visit_entries = match 

D7928: rust-status: add function for sequential traversal of the working directory

2020-02-06 Thread Raphaël Gomès
Alphare updated this revision to Diff 19946.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7928?vs=19417=19946

BRANCH
  default

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,18 +11,21 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
 /// Wrong type of file from a `BadMatch`
@@ -229,6 +232,168 @@
 pub last_normal_time: i64,
 pub check_exec: bool,
 pub list_clean: bool,
+pub list_unknown: bool,
+pub list_ignored: bool,
+}
+
+/// Dispatch a single file found during `traverse`.
+/// If `file` is a folder that needs to be traversed, it will be pushed into
+/// `work`.
+fn traverse_worker<'a>(
+work:  VecDeque,
+matcher:  Matcher,
+dmap: ,
+filename: impl AsRef,
+dir_entry: ,
+ignore_fn:  for<'r> Fn(&'r HgPath) -> bool,
+options: StatusOptions,
+) -> Option, Dispatch)>> {
+let file_type = match dir_entry.file_type() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+let filename = filename.as_ref();
+let entry_option = dmap.get(filename);
+
+if file_type.is_dir() {
+// Do we need to traverse it?
+if !ignore_fn() {
+work.push_front(filename.to_owned());
+} else {
+if options.list_ignored {
+work.push_front(filename.to_owned());
+}
+}
+// Nested `if` until `rust-lang/rust#53668` is stable
+if let Some(entry) = entry_option {
+// Used to be a file, is now a folder
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+} else if file_type.is_file() || file_type.is_symlink() {
+if let Some(entry) = entry_option {
+if matcher.matches_everything() || matcher.matches() {
+let metadata = match dir_entry.metadata() {
+Ok(x) => x,
+Err(e) => return Some(Err(e.into())),
+};
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_found(
+,
+*entry,
+HgMetadata::from_metadata(metadata),
+_map,
+options,
+),
+)));
+}
+} else if (matcher.matches_everything() || matcher.matches())
+&& !ignore_fn()
+{
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Unknown,
+)));
+} else if ignore_fn() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+Dispatch::Ignored,
+)));
+}
+} else if let Some(entry) = entry_option {
+// Used to be a file or a folder, now something else.
+if matcher.matches_everything() || matcher.matches() {
+return Some(Ok((
+Cow::Owned(filename.to_owned()),
+dispatch_missing(entry.state),
+)));
+}
+}
+None
+}
+
+/// Walk the working directory recursively to look for changes compared to the
+/// current `DirstateMap`.
+fn traverse<'a>(
+matcher: &(impl Matcher + Sync),
+root_dir: impl AsRef,
+dmap: ,
+path: impl AsRef,
+old_results: FastHashMap, Dispatch>,
+ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+options: StatusOptions,
+) -> IoResult, Dispatch>> {
+let root_dir = root_dir.as_ref();
+let mut new_results = FastHashMap::default();
+
+let mut work = VecDeque::new();
+work.push_front(path.as_ref().to_owned());
+
+while let Some(ref directory) = work.pop_front() {
+if directory.as_bytes() == b".hg" {
+continue;
+}
+let visit_entries = match matcher.visit_children_set(directory) {
+

D7928: rust-status: add function for sequential traversal of the working directory

2020-02-06 Thread Raphaël Gomès
Alphare added a comment.
Alphare marked an inline comment as done.


  In D7928#119428 , @marmoute 
wrote:
  
  > There is a lot going on in this changeset. I am counting at least:
  >
  > - new `BadType/BadMatch` structs,
  > - `StatusResult` → `DirstateStatus` change,
  > - new `StatusOptions` struct
  > - new `traverse_worker` function.
  >
  > Can we get the smaller cleanup as reparated chagneset before hand? It would 
make the thing clearer in my opinion.
  
  I don't think the `BadType/BadMatch` struct warrant a new changeset. I'll 
split into 3 then.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

To: Alphare, #hg-reviewers, marmoute
Cc: marmoute, durin42, kevincox, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D7928: rust-status: add function for sequential traversal of the working directory

2020-02-05 Thread marmoute (Pierre-Yves David)
This revision now requires changes to proceed.
marmoute added a comment.
marmoute requested changes to this revision.


  There is a lot going on in this changeset. I am counting at least:
  
  - new `BadType/BadMatch` structs,
  - `StatusResult` → `DirstateStatus` change,
  - new `StatusOptions` struct
  - new `traverse_worker` function.
  
  Can we get the smaller cleanup as reparated chagneset before hand? It would 
make the thing clearer in my opinion.

INLINE COMMENTS

> status.rs:61-62
> +None,
> +/// Was explicitly matched but cannot be found/accessed
> +Bad(BadMatch),
>  }

nits: It might be worth adding the same commentan extra comment to the 
BadType/BadMatch declaration to clarify their intent.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7928/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

To: Alphare, #hg-reviewers, marmoute
Cc: marmoute, durin42, kevincox, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D7928: rust-status: add function for sequential traversal of the working directory

2020-01-17 Thread Raphaël Gomès
Alphare created this revision.
Herald added subscribers: mercurial-devel, kevincox, durin42.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This change also introduces helper structs to make things clearer.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D7928

AFFECTED FILES
  rust/hg-core/src/dirstate/status.rs
  rust/hg-core/src/lib.rs
  rust/hg-cpython/src/dirstate/status.rs

CHANGE DETAILS

diff --git a/rust/hg-cpython/src/dirstate/status.rs 
b/rust/hg-cpython/src/dirstate/status.rs
--- a/rust/hg-cpython/src/dirstate/status.rs
+++ b/rust/hg-cpython/src/dirstate/status.rs
@@ -20,7 +20,7 @@
 matchers::{AlwaysMatcher, FileMatcher},
 status,
 utils::{files::get_path_from_bytes, hg_path::HgPath},
-StatusResult,
+DirstateStatus,
 };
 use std::borrow::Borrow;
 
@@ -114,7 +114,7 @@
 
 fn build_response(
 lookup: Vec<>,
-status_res: StatusResult,
+status_res: DirstateStatus,
 py: Python,
 ) -> PyResult<(PyList, PyList, PyList, PyList, PyList, PyList, PyList)> {
 let modified = collect_pybytes_list(py, status_res.modified.as_ref());
diff --git a/rust/hg-core/src/lib.rs b/rust/hg-core/src/lib.rs
--- a/rust/hg-core/src/lib.rs
+++ b/rust/hg-core/src/lib.rs
@@ -13,7 +13,7 @@
 dirs_multiset::{DirsMultiset, DirsMultisetIter},
 dirstate_map::DirstateMap,
 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
-status::{status, StatusResult},
+status::{status, DirstateStatus},
 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
 StateMap, StateMapIter,
 };
diff --git a/rust/hg-core/src/dirstate/status.rs 
b/rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -11,20 +11,39 @@
 
 use crate::{
 dirstate::SIZE_FROM_OTHER_PARENT,
-matchers::Matcher,
+matchers::{Matcher, VisitChildrenSet},
 utils::{
 files::HgMetadata,
 hg_path::{
 hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
 },
 },
-CopyMap, DirstateEntry, DirstateMap, EntryState,
+CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
 };
 use rayon::prelude::*;
-use std::collections::HashSet;
+use std::borrow::Cow;
+use std::collections::{HashSet, VecDeque};
 use std::fs::{read_dir, DirEntry};
+use std::io::ErrorKind;
+use std::ops::Deref;
 use std::path::Path;
 
+#[derive(Debug)]
+pub enum BadType {
+CharacterDevice,
+BlockDevice,
+FIFO,
+Socket,
+Directory,
+Unknown,
+}
+
+#[derive(Debug)]
+pub enum BadMatch {
+OsError(i32),
+BadType(BadType),
+}
+
 /// Marker enum used to dispatch new status entries into the right collections.
 /// Is similar to `crate::EntryState`, but represents the transient state of
 /// entries during the lifetime of a command.
@@ -36,6 +55,11 @@
 Deleted,
 Clean,
 Unknown,
+Ignored,
+/// Empty dispatch, the file is not worth listing
+None,
+/// Was explicitly matched but cannot be found/accessed
+Bad(BadMatch),
 }
 
 type IoResult = std::io::Result;
@@ -81,9 +105,7 @@
 entry: DirstateEntry,
 metadata: HgMetadata,
 copy_map: ,
-check_exec: bool,
-list_clean: bool,
-last_normal_time: i64,
+options: StatusOptions,
 ) -> Dispatch {
 let DirstateEntry {
 state,
@@ -103,7 +125,7 @@
 EntryState::Normal => {
 let size_changed = mod_compare(size, st_size as i32);
 let mode_changed =
-(mode ^ st_mode as i32) & 0o100 != 0o000 && check_exec;
+(mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec;
 let metadata_changed = size >= 0 && (size_changed || mode_changed);
 let other_parent = size == SIZE_FROM_OTHER_PARENT;
 if metadata_changed
@@ -113,14 +135,14 @@
 Dispatch::Modified
 } else if mod_compare(mtime, st_mtime as i32) {
 Dispatch::Unsure
-} else if st_mtime == last_normal_time {
+} else if st_mtime == options.last_normal_time {
 // the file may have just been marked as normal and
 // it may have changed in the same second without
 // changing its size. This can happen if we quickly
 // do multiple commits. Force lookup, so we don't
 // miss such a racy file change.
 Dispatch::Unsure
-} else if list_clean {
+} else if options.list_clean {
 Dispatch::Clean
 } else {
 Dispatch::Unknown
@@ -153,9 +175,7 @@
 files: &'a HashSet<>,
 dmap: &'a DirstateMap,
 root_dir: impl AsRef + Sync + Send,
-check_exec: bool,
-list_clean: bool,
-last_normal_time: i64,
+options: StatusOptions,
 ) -> impl ParallelIterator> {
 files.par_iter().filter_map(move |filename| {
 // TODO normalization
@@