dispatch.rs
558 lines
| 17.1 KiB
| application/rls-services+xml
|
RustLexer
|
r47863 | use std::path::PathBuf; | ||
|
r47871 | use crate::dirstate::parsers::Timestamp; | ||
|
r48126 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; | ||
|
r47863 | use crate::matchers::Matcher; | ||
use crate::utils::hg_path::{HgPath, HgPathBuf}; | ||||
use crate::CopyMapIter; | ||||
use crate::DirstateEntry; | ||||
use crate::DirstateError; | ||||
use crate::DirstateMap; | ||||
use crate::DirstateParents; | ||||
use crate::DirstateStatus; | ||||
use crate::PatternFileWarning; | ||||
use crate::StateMapIter; | ||||
use crate::StatusError; | ||||
use crate::StatusOptions; | ||||
|
r48285 | /// `rust/hg-cpython/src/dirstate/dirstate_map.rs` implements in Rust a | ||
/// `DirstateMap` Python class that wraps `Box<dyn DirstateMapMethods + Send>`, | ||||
/// 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. | ||||
|
r47863 | pub trait DirstateMapMethods { | ||
|
r48285 | /// Remove information about all files in this map | ||
|
r47863 | fn clear(&mut self); | ||
|
r48859 | /// Add the given filename to the map if it is not already there, and | ||
/// associate the given entry with it. | ||||
|
r48861 | fn set_entry( | ||
&mut self, | ||||
filename: &HgPath, | ||||
entry: DirstateEntry, | ||||
) -> Result<(), DirstateV2ParseError>; | ||||
r48492 | ||||
|
r48285 | /// Add or change the information associated to a given file. | ||
|
r47863 | fn add_file( | ||
&mut self, | ||||
filename: &HgPath, | ||||
entry: DirstateEntry, | ||||
r48314 | added: bool, | |||
r48316 | merged: bool, | |||
r48310 | from_p2: bool, | |||
possibly_dirty: bool, | ||||
|
r48126 | ) -> Result<(), DirstateError>; | ||
|
r47863 | |||
|
r48285 | /// Mark a file as "removed" (as in `hg rm`). | ||
|
r47863 | fn remove_file( | ||
&mut self, | ||||
filename: &HgPath, | ||||
r48300 | in_merge: bool, | |||
|
r48126 | ) -> Result<(), DirstateError>; | ||
|
r47863 | |||
|
r48863 | /// Drop information about this file from the map if any. | ||
|
r48285 | /// | ||
/// `get` will now return `None` for this filename. | ||||
|
r48862 | fn drop_file(&mut self, filename: &HgPath) -> Result<(), DirstateError>; | ||
|
r47863 | |||
|
r48285 | /// Among given files, mark the stored `mtime` as ambiguous if there is one | ||
/// (if `state == EntryState::Normal`) equal to the given current Unix | ||||
/// timestamp. | ||||
|
r48126 | fn clear_ambiguous_times( | ||
&mut self, | ||||
filenames: Vec<HgPathBuf>, | ||||
now: i32, | ||||
) -> Result<(), DirstateV2ParseError>; | ||||
|
r47863 | |||
|
r48285 | /// 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`. | ||||
|
r48126 | fn non_normal_entries_contains( | ||
&mut self, | ||||
key: &HgPath, | ||||
) -> Result<bool, DirstateV2ParseError>; | ||||
|
r47864 | |||
|
r48285 | /// 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. | ||||
r48492 | /// Returns whether the key was present in the set. | |||
fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool; | ||||
/// Mark the given path as "non-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_add(&mut self, key: &HgPath); | ||||
|
r47863 | |||
|
r48285 | /// 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. | ||||
|
r47864 | fn non_normal_or_other_parent_paths( | ||
|
r47863 | &mut self, | ||
|
r48126 | ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>; | ||
|
r47863 | |||
|
r48285 | /// 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. | ||||
|
r47863 | fn set_non_normal_other_parent_entries(&mut self, force: bool); | ||
|
r48285 | /// 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. | ||||
|
r47864 | fn iter_non_normal_paths( | ||
&mut self, | ||||
|
r48126 | ) -> Box< | ||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | ||||
>; | ||||
|
r47863 | |||
|
r48285 | /// Same as `iter_non_normal_paths`, but takes `&self` instead of `&mut | ||
/// self`. | ||||
/// | ||||
/// Panics if a cache is necessary but does not exist yet. | ||||
|
r47864 | fn iter_non_normal_paths_panic( | ||
&self, | ||||
|
r48126 | ) -> Box< | ||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | ||||
>; | ||||
|
r47864 | |||
|
r48285 | /// 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. | ||||
|
r47864 | fn iter_other_parent_paths( | ||
|
r47863 | &mut self, | ||
|
r48126 | ) -> Box< | ||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | ||||
>; | ||||
|
r47863 | |||
|
r48285 | /// 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`. | ||||
|
r47863 | fn has_tracked_dir( | ||
&mut self, | ||||
directory: &HgPath, | ||||
|
r48126 | ) -> Result<bool, DirstateError>; | ||
|
r47863 | |||
|
r48285 | /// Returns whether the sub-tree rooted at the given directory contains any | ||
/// file with a dirstate entry. | ||||
|
r48126 | fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError>; | ||
|
r47863 | |||
|
r48285 | /// 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. | ||||
|
r48055 | fn pack_v1( | ||
&mut self, | ||||
parents: DirstateParents, | ||||
now: Timestamp, | ||||
) -> Result<Vec<u8>, DirstateError>; | ||||
|
r48285 | /// Clear mtimes that are ambigous with `now` (similar to | ||
/// `clear_ambiguous_times` but for all files in the dirstate map), and | ||||
|
r48478 | /// serialize bytes to write a dirstate data file to disk in dirstate-v2 | ||
|
r48285 | /// format. | ||
/// | ||||
|
r48482 | /// Returns new data and metadata together with whether that data should be | ||
/// appended to the existing data file whose content is at | ||||
/// `self.on_disk` (true), instead of written to a new data file | ||||
/// (false). | ||||
|
r48478 | /// | ||
|
r48285 | /// Note: this is only supported by the tree dirstate map. | ||
|
r48478 | fn pack_v2( | ||
&mut self, | ||||
now: Timestamp, | ||||
can_append: bool, | ||||
|
r48482 | ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError>; | ||
|
r47863 | |||
|
r48285 | /// 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. | ||||
|
r47863 | fn status<'a>( | ||
|
r47882 | &'a mut self, | ||
|
r47863 | matcher: &'a (dyn Matcher + Sync), | ||
root_dir: PathBuf, | ||||
ignore_files: Vec<PathBuf>, | ||||
options: StatusOptions, | ||||
|
r47880 | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>; | ||
|
r47863 | |||
|
r48285 | /// Returns how many files in the dirstate map have a recorded copy source. | ||
|
r47863 | fn copy_map_len(&self) -> usize; | ||
|
r48285 | /// Returns an iterator of `(path, copy_source)` for all files that have a | ||
/// copy source. | ||||
|
r47863 | fn copy_map_iter(&self) -> CopyMapIter<'_>; | ||
|
r48285 | /// Returns whether the givef file has a copy source. | ||
|
r48126 | fn copy_map_contains_key( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<bool, DirstateV2ParseError>; | ||||
|
r47863 | |||
|
r48285 | /// Returns the copy source for the given file. | ||
|
r48126 | fn copy_map_get( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<Option<&HgPath>, DirstateV2ParseError>; | ||||
|
r47863 | |||
|
r48285 | /// Removes the recorded copy source if any for the given file, and returns | ||
/// it. | ||||
|
r48126 | fn copy_map_remove( | ||
&mut self, | ||||
key: &HgPath, | ||||
) -> Result<Option<HgPathBuf>, DirstateV2ParseError>; | ||||
|
r47863 | |||
|
r48285 | /// Set the given `value` copy source for the given `key` file. | ||
|
r47863 | fn copy_map_insert( | ||
&mut self, | ||||
key: HgPathBuf, | ||||
value: HgPathBuf, | ||||
|
r48126 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError>; | ||
|
r47863 | |||
|
r48285 | /// Returns the number of files that have an entry. | ||
|
r47863 | fn len(&self) -> usize; | ||
|
r48285 | /// Returns whether the given file has an entry. | ||
|
r48126 | fn contains_key(&self, key: &HgPath) | ||
-> Result<bool, DirstateV2ParseError>; | ||||
|
r47863 | |||
|
r48285 | /// Returns the entry, if any, for the given file. | ||
|
r48126 | fn get( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<Option<DirstateEntry>, DirstateV2ParseError>; | ||||
|
r47863 | |||
|
r48285 | /// Returns a `(path, entry)` iterator of files that have an entry. | ||
/// | ||||
/// Because parse errors can happen during iteration, the iterated items | ||||
/// are `Result`s. | ||||
|
r47863 | fn iter(&self) -> StateMapIter<'_>; | ||
|
r48140 | |||
|
r48483 | /// Returns an iterator of tracked directories. | ||
|
r48285 | /// | ||
|
r48483 | /// This is the paths for which `has_tracked_dir` would return true. | ||
/// Or, in other words, the union of ancestor paths of all paths that have | ||||
/// an associated entry in a "tracked" state in this dirstate map. | ||||
|
r48285 | /// | ||
/// Because parse errors can happen during iteration, the iterated items | ||||
/// are `Result`s. | ||||
|
r48483 | fn iter_tracked_dirs( | ||
&mut self, | ||||
) -> Result< | ||||
Box< | ||||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> | ||||
+ Send | ||||
+ '_, | ||||
>, | ||||
DirstateError, | ||||
>; | ||||
/// Return an iterator of `(path, (state, mode, size, mtime))` for every | ||||
/// node stored in this dirstate map, for the purpose of the `hg | ||||
/// debugdirstate` command. | ||||
/// | ||||
|
r48835 | /// If `all` is true, include nodes that don’t have an entry. | ||
/// For such nodes `state` is the ASCII space. | ||||
|
r48483 | /// An `mtime` may still be present. It is used to optimize `status`. | ||
/// | ||||
/// Because parse errors can happen during iteration, the iterated items | ||||
/// are `Result`s. | ||||
fn debug_iter( | ||||
|
r48140 | &self, | ||
|
r48835 | all: bool, | ||
|
r48140 | ) -> Box< | ||
dyn Iterator< | ||||
Item = Result< | ||||
|
r48483 | (&HgPath, (u8, i32, i32, i32)), | ||
|
r48140 | DirstateV2ParseError, | ||
>, | ||||
> + Send | ||||
+ '_, | ||||
>; | ||||
|
r47863 | } | ||
impl DirstateMapMethods for DirstateMap { | ||||
fn clear(&mut self) { | ||||
self.clear() | ||||
} | ||||
r48492 | /// Used to set a value directory. | |||
/// | ||||
/// XXX Is temporary during a refactor of V1 dirstate and will disappear | ||||
/// shortly. | ||||
|
r48861 | fn set_entry( | ||
&mut self, | ||||
filename: &HgPath, | ||||
entry: DirstateEntry, | ||||
) -> Result<(), DirstateV2ParseError> { | ||||
self.set_entry(&filename, entry); | ||||
Ok(()) | ||||
r48492 | } | |||
|
r47863 | fn add_file( | ||
&mut self, | ||||
filename: &HgPath, | ||||
entry: DirstateEntry, | ||||
r48314 | added: bool, | |||
r48316 | merged: bool, | |||
r48310 | from_p2: bool, | |||
possibly_dirty: bool, | ||||
|
r48126 | ) -> Result<(), DirstateError> { | ||
r48316 | self.add_file(filename, entry, added, merged, from_p2, possibly_dirty) | |||
|
r47863 | } | ||
fn remove_file( | ||||
&mut self, | ||||
filename: &HgPath, | ||||
r48300 | in_merge: bool, | |||
|
r48126 | ) -> Result<(), DirstateError> { | ||
r48300 | self.remove_file(filename, in_merge) | |||
|
r47863 | } | ||
|
r48862 | fn drop_file(&mut self, filename: &HgPath) -> Result<(), DirstateError> { | ||
r48324 | self.drop_file(filename) | |||
|
r47863 | } | ||
|
r48126 | fn clear_ambiguous_times( | ||
&mut self, | ||||
filenames: Vec<HgPathBuf>, | ||||
now: i32, | ||||
) -> Result<(), DirstateV2ParseError> { | ||||
Ok(self.clear_ambiguous_times(filenames, now)) | ||||
|
r47863 | } | ||
|
r48126 | fn non_normal_entries_contains( | ||
&mut self, | ||||
key: &HgPath, | ||||
) -> Result<bool, DirstateV2ParseError> { | ||||
|
r47864 | let (non_normal, _other_parent) = | ||
self.get_non_normal_other_parent_entries(); | ||||
|
r48126 | Ok(non_normal.contains(key)) | ||
|
r47864 | } | ||
r48492 | fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool { | |||
|
r47863 | self.non_normal_entries_remove(key) | ||
} | ||||
r48492 | fn non_normal_entries_add(&mut self, key: &HgPath) { | |||
self.non_normal_entries_add(key) | ||||
} | ||||
|
r47864 | fn non_normal_or_other_parent_paths( | ||
|
r47863 | &mut self, | ||
|
r48126 | ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_> | ||
{ | ||||
|
r47864 | let (non_normal, other_parent) = | ||
self.get_non_normal_other_parent_entries(); | ||||
|
r48126 | Box::new(non_normal.union(other_parent).map(|p| Ok(&**p))) | ||
|
r47863 | } | ||
fn set_non_normal_other_parent_entries(&mut self, force: bool) { | ||||
self.set_non_normal_other_parent_entries(force) | ||||
} | ||||
|
r47864 | fn iter_non_normal_paths( | ||
&mut self, | ||||
|
r48126 | ) -> Box< | ||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | ||||
> { | ||||
|
r47864 | let (non_normal, _other_parent) = | ||
self.get_non_normal_other_parent_entries(); | ||||
|
r48126 | Box::new(non_normal.iter().map(|p| Ok(&**p))) | ||
|
r47863 | } | ||
|
r47864 | fn iter_non_normal_paths_panic( | ||
&self, | ||||
|
r48126 | ) -> Box< | ||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | ||||
> { | ||||
|
r47864 | let (non_normal, _other_parent) = | ||
self.get_non_normal_other_parent_entries_panic(); | ||||
|
r48126 | Box::new(non_normal.iter().map(|p| Ok(&**p))) | ||
|
r47864 | } | ||
fn iter_other_parent_paths( | ||||
|
r47863 | &mut self, | ||
|
r48126 | ) -> Box< | ||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | ||||
> { | ||||
|
r47864 | let (_non_normal, other_parent) = | ||
self.get_non_normal_other_parent_entries(); | ||||
|
r48126 | Box::new(other_parent.iter().map(|p| Ok(&**p))) | ||
|
r47863 | } | ||
fn has_tracked_dir( | ||||
&mut self, | ||||
directory: &HgPath, | ||||
|
r48126 | ) -> Result<bool, DirstateError> { | ||
|
r47863 | self.has_tracked_dir(directory) | ||
} | ||||
|
r48126 | fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> { | ||
|
r47863 | self.has_dir(directory) | ||
} | ||||
|
r48055 | fn pack_v1( | ||
|
r47863 | &mut self, | ||
parents: DirstateParents, | ||||
|
r47871 | now: Timestamp, | ||
|
r47863 | ) -> Result<Vec<u8>, DirstateError> { | ||
self.pack(parents, now) | ||||
} | ||||
|
r48478 | fn pack_v2( | ||
&mut self, | ||||
_now: Timestamp, | ||||
_can_append: bool, | ||||
|
r48482 | ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> { | ||
|
r48055 | panic!( | ||
"should have used dirstate_tree::DirstateMap to use the v2 format" | ||||
) | ||||
} | ||||
|
r47863 | fn status<'a>( | ||
|
r47882 | &'a mut self, | ||
|
r47863 | matcher: &'a (dyn Matcher + Sync), | ||
root_dir: PathBuf, | ||||
ignore_files: Vec<PathBuf>, | ||||
options: StatusOptions, | ||||
|
r47880 | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> | ||
{ | ||||
|
r47863 | crate::status(self, matcher, root_dir, ignore_files, options) | ||
} | ||||
fn copy_map_len(&self) -> usize { | ||||
self.copy_map.len() | ||||
} | ||||
fn copy_map_iter(&self) -> CopyMapIter<'_> { | ||||
|
r48126 | Box::new( | ||
self.copy_map | ||||
.iter() | ||||
.map(|(key, value)| Ok((&**key, &**value))), | ||||
) | ||||
|
r47863 | } | ||
|
r48126 | fn copy_map_contains_key( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<bool, DirstateV2ParseError> { | ||||
Ok(self.copy_map.contains_key(key)) | ||||
|
r47863 | } | ||
|
r48126 | fn copy_map_get( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<Option<&HgPath>, DirstateV2ParseError> { | ||||
Ok(self.copy_map.get(key).map(|p| &**p)) | ||||
} | ||||
fn copy_map_remove( | ||||
&mut self, | ||||
key: &HgPath, | ||||
) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | ||||
Ok(self.copy_map.remove(key)) | ||||
|
r47863 | } | ||
fn copy_map_insert( | ||||
&mut self, | ||||
key: HgPathBuf, | ||||
value: HgPathBuf, | ||||
|
r48126 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | ||
Ok(self.copy_map.insert(key, value)) | ||||
|
r47863 | } | ||
fn len(&self) -> usize { | ||||
(&**self).len() | ||||
} | ||||
|
r48126 | fn contains_key( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<bool, DirstateV2ParseError> { | ||||
Ok((&**self).contains_key(key)) | ||||
|
r47863 | } | ||
|
r48126 | fn get( | ||
&self, | ||||
key: &HgPath, | ||||
) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { | ||||
Ok((&**self).get(key).cloned()) | ||||
|
r47863 | } | ||
fn iter(&self) -> StateMapIter<'_> { | ||||
|
r48126 | Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value)))) | ||
|
r47863 | } | ||
|
r48140 | |||
|
r48483 | fn iter_tracked_dirs( | ||
&mut self, | ||||
) -> Result< | ||||
Box< | ||||
dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> | ||||
+ Send | ||||
+ '_, | ||||
>, | ||||
DirstateError, | ||||
> { | ||||
self.set_all_dirs()?; | ||||
Ok(Box::new( | ||||
self.all_dirs | ||||
.as_ref() | ||||
.unwrap() | ||||
.iter() | ||||
.map(|path| Ok(&**path)), | ||||
)) | ||||
} | ||||
fn debug_iter( | ||||
|
r48140 | &self, | ||
|
r48835 | all: bool, | ||
|
r48140 | ) -> Box< | ||
dyn Iterator< | ||||
Item = Result< | ||||
|
r48483 | (&HgPath, (u8, i32, i32, i32)), | ||
|
r48140 | DirstateV2ParseError, | ||
>, | ||||
> + Send | ||||
+ '_, | ||||
> { | ||||
|
r48835 | // Not used for the flat (not tree-based) DirstateMap | ||
let _ = all; | ||||
|
r48483 | Box::new( | ||
(&**self) | ||||
.iter() | ||||
.map(|(path, entry)| Ok((&**path, entry.debug_tuple()))), | ||||
) | ||||
|
r48140 | } | ||
|
r47863 | } | ||