diff --git a/rust/hg-core/src/dirstate_tree/dispatch.rs b/rust/hg-core/src/dirstate_tree/dispatch.rs --- a/rust/hg-core/src/dirstate_tree/dispatch.rs +++ b/rust/hg-core/src/dirstate_tree/dispatch.rs @@ -16,9 +16,34 @@ use crate::StatusError; use crate::StatusOptions; +/// `rust/hg-cpython/src/dirstate/dirstate_map.rs` implements in Rust a +/// `DirstateMap` Python class that wraps `Box`, +/// a trait object of this trait. Except for constructors, this trait defines +/// all APIs that the class needs to interact with its inner dirstate map. +/// +/// A trait object is used to support two different concrete types: +/// +/// * `rust/hg-core/src/dirstate/dirstate_map.rs` defines the "flat dirstate +/// map" which is based on a few large `HgPath`-keyed `HashMap` and `HashSet` +/// fields. +/// * `rust/hg-core/src/dirstate_tree/dirstate_map.rs` defines the "tree +/// dirstate map" based on a tree data struture with nodes for directories +/// containing child nodes for their files and sub-directories. This tree +/// enables a more efficient algorithm for `hg status`, but its details are +/// abstracted in this trait. +/// +/// The dirstate map associates paths of files in the working directory to +/// various information about the state of those files. pub trait DirstateMapMethods { + /// Remove information about all files in this map fn clear(&mut self); + /// Add or change the information associated to a given file. + /// + /// `old_state` is the state in the entry that `get` would have returned + /// before this call, or `EntryState::Unknown` if there was no such entry. + /// + /// `entry.state` should never be `EntryState::Unknown`. fn add_file( &mut self, filename: &HgPath, @@ -26,6 +51,13 @@ entry: DirstateEntry, ) -> Result<(), DirstateError>; + /// Mark a file as "removed" (as in `hg rm`). + /// + /// `old_state` is the state in the entry that `get` would have returned + /// before this call, or `EntryState::Unknown` if there was no such entry. + /// + /// `size` is not actually a size but the 0 or -1 or -2 value that would be + /// put in the size field in the dirstate-v1 format. fn remove_file( &mut self, filename: &HgPath, @@ -33,68 +65,138 @@ size: i32, ) -> Result<(), DirstateError>; + /// Drop information about this file from the map if any, and return + /// whether there was any. + /// + /// `get` will now return `None` for this filename. + /// + /// `old_state` is the state in the entry that `get` would have returned + /// before this call, or `EntryState::Unknown` if there was no such entry. fn drop_file( &mut self, filename: &HgPath, old_state: EntryState, ) -> Result; + /// Among given files, mark the stored `mtime` as ambiguous if there is one + /// (if `state == EntryState::Normal`) equal to the given current Unix + /// timestamp. fn clear_ambiguous_times( &mut self, filenames: Vec, now: i32, ) -> Result<(), DirstateV2ParseError>; + /// Return whether the map has an "non-normal" entry for the given + /// filename. That is, any entry with a `state` other than + /// `EntryState::Normal` or with an ambiguous `mtime`. fn non_normal_entries_contains( &mut self, key: &HgPath, ) -> Result; + /// Mark the given path as "normal" file. This is only relevant in the flat + /// dirstate map where there is a separate `HashSet` that needs to be kept + /// up to date. fn non_normal_entries_remove(&mut self, key: &HgPath); + /// Return an iterator of paths whose respective entry are either + /// "non-normal" (see `non_normal_entries_contains`) or "from other + /// parent". + /// + /// If that information is cached, create the cache as needed. + /// + /// "From other parent" is defined as `state == Normal && size == -2`. + /// + /// Because parse errors can happen during iteration, the iterated items + /// are `Result`s. fn non_normal_or_other_parent_paths( &mut self, ) -> Box> + '_>; + /// Create the cache for `non_normal_or_other_parent_paths` if needed. + /// + /// If `force` is true, the cache is re-created even if it already exists. fn set_non_normal_other_parent_entries(&mut self, force: bool); + /// Return an iterator of paths whose respective entry are "non-normal" + /// (see `non_normal_entries_contains`). + /// + /// If that information is cached, create the cache as needed. + /// + /// Because parse errors can happen during iteration, the iterated items + /// are `Result`s. fn iter_non_normal_paths( &mut self, ) -> Box< dyn Iterator> + Send + '_, >; + /// Same as `iter_non_normal_paths`, but takes `&self` instead of `&mut + /// self`. + /// + /// Panics if a cache is necessary but does not exist yet. fn iter_non_normal_paths_panic( &self, ) -> Box< dyn Iterator> + Send + '_, >; + /// Return an iterator of paths whose respective entry are "from other + /// parent". + /// + /// If that information is cached, create the cache as needed. + /// + /// "From other parent" is defined as `state == Normal && size == -2`. + /// + /// Because parse errors can happen during iteration, the iterated items + /// are `Result`s. fn iter_other_parent_paths( &mut self, ) -> Box< dyn Iterator> + Send + '_, >; + /// Returns whether the sub-tree rooted at the given directory contains any + /// tracked file. + /// + /// A file is tracked if it has a `state` other than `EntryState::Removed`. fn has_tracked_dir( &mut self, directory: &HgPath, ) -> Result; + /// Returns whether the sub-tree rooted at the given directory contains any + /// file with a dirstate entry. fn has_dir(&mut self, directory: &HgPath) -> Result; + /// Clear mtimes that are ambigous with `now` (similar to + /// `clear_ambiguous_times` but for all files in the dirstate map), and + /// serialize bytes to write the `.hg/dirstate` file to disk in dirstate-v1 + /// format. fn pack_v1( &mut self, parents: DirstateParents, now: Timestamp, ) -> Result, DirstateError>; + /// Clear mtimes that are ambigous with `now` (similar to + /// `clear_ambiguous_times` but for all files in the dirstate map), and + /// serialize bytes to write the `.hg/dirstate` file to disk in dirstate-v2 + /// format. + /// + /// Note: this is only supported by the tree dirstate map. fn pack_v2( &mut self, parents: DirstateParents, now: Timestamp, ) -> Result, DirstateError>; + /// Run the status algorithm. + /// + /// This is not sematically a method of the dirstate map, but a different + /// algorithm is used for the flat v.s. tree dirstate map so having it in + /// this trait enables the same dynamic dispatch as with other methods. fn status<'a>( &'a mut self, matcher: &'a (dyn Matcher + Sync), @@ -103,43 +205,66 @@ options: StatusOptions, ) -> Result<(DirstateStatus<'a>, Vec), StatusError>; + /// Returns how many files in the dirstate map have a recorded copy source. fn copy_map_len(&self) -> usize; + /// Returns an iterator of `(path, copy_source)` for all files that have a + /// copy source. fn copy_map_iter(&self) -> CopyMapIter<'_>; + /// Returns whether the givef file has a copy source. fn copy_map_contains_key( &self, key: &HgPath, ) -> Result; + /// Returns the copy source for the given file. fn copy_map_get( &self, key: &HgPath, ) -> Result, DirstateV2ParseError>; + /// Removes the recorded copy source if any for the given file, and returns + /// it. fn copy_map_remove( &mut self, key: &HgPath, ) -> Result, DirstateV2ParseError>; + /// Set the given `value` copy source for the given `key` file. fn copy_map_insert( &mut self, key: HgPathBuf, value: HgPathBuf, ) -> Result, DirstateV2ParseError>; + /// Returns the number of files that have an entry. fn len(&self) -> usize; + /// Returns whether the given file has an entry. fn contains_key(&self, key: &HgPath) -> Result; + /// Returns the entry, if any, for the given file. fn get( &self, key: &HgPath, ) -> Result, DirstateV2ParseError>; + /// Returns a `(path, entry)` iterator of files that have an entry. + /// + /// Because parse errors can happen during iteration, the iterated items + /// are `Result`s. fn iter(&self) -> StateMapIter<'_>; + /// In the tree dirstate, return an iterator of "directory" (entry-less) + /// nodes with the data stored for them. This is for `hg debugdirstate + /// --dirs`. + /// + /// In the flat dirstate, returns an empty iterator. + /// + /// Because parse errors can happen during iteration, the iterated items + /// are `Result`s. fn iter_directories( &self, ) -> Box< diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs @@ -79,7 +79,7 @@ pub(super) descendants_with_entry_count: Size, pub(super) tracked_descendants_count: Size, - /// Dependending on the value of `state`: + /// Depending on the value of `state`: /// /// * A null byte: `data` is not used. ///