##// END OF EJS Templates
relnotes: mention the merging of index and nodemap
relnotes: mention the merging of index and nodemap

File last commit:

r44315:bc7d8f45 default
r44341:458504a8 default
Show More
dirs_multiset.rs
346 lines | 10.8 KiB | application/rls-services+xml | RustLexer
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 // dirs_multiset.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.
//! A multiset of directory names.
//!
//! Used to counts the references to directories in a manifest or dirstate.
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 use crate::utils::hg_path::{HgPath, HgPathBuf};
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 use crate::{
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 FastHashMap,
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 };
Yuya Nishihara
rust-dirstate: specify concrete return type of DirsMultiset::iter()...
r43155 use std::collections::hash_map::{self, Entry};
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
Yuya Nishihara
rust-dirstate: specify concrete return type of DirsMultiset::iter()...
r43155 // could be encapsulated if we care API stability more seriously
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>;
Yuya Nishihara
rust-dirstate: specify concrete return type of DirsMultiset::iter()...
r43155
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 #[derive(PartialEq, Debug)]
pub struct DirsMultiset {
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 inner: FastHashMap<HgPathBuf, u32>,
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
impl DirsMultiset {
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 /// Initializes the multiset from a dirstate.
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 ///
/// If `skip_state` is provided, skips dirstate entries with equal state.
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 pub fn from_dirstate(
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 dirstate: &FastHashMap<HgPathBuf, DirstateEntry>,
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 skip_state: Option<EntryState>,
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 ) -> Result<Self, DirstateMapError> {
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 let mut multiset = DirsMultiset {
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 inner: FastHashMap::default(),
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 };
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 for (filename, DirstateEntry { state, .. }) in dirstate {
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 // This `if` is optimized out of the loop
if let Some(skip) = skip_state {
if skip != *state {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 multiset.add_path(filename)?;
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 } else {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 multiset.add_path(filename)?;
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
}
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 Ok(multiset)
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 /// Initializes the multiset from a manifest.
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 pub fn from_manifest(
manifest: &[impl AsRef<HgPath>],
) -> Result<Self, DirstateMapError> {
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 let mut multiset = DirsMultiset {
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 inner: FastHashMap::default(),
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 };
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 for filename in manifest {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 multiset.add_path(filename.as_ref())?;
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 }
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 Ok(multiset)
Yuya Nishihara
rust-dirstate: split DirsMultiset constructor per input type...
r43070 }
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 /// Increases the count of deepest directory contained in the path.
///
/// If the directory is not yet in the map, adds its parents.
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 pub fn add_path(
&mut self,
path: impl AsRef<HgPath>,
) -> Result<(), DirstateMapError> {
for subpath in files::find_dirs(path.as_ref()) {
Raphaël Gomès
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
r44227 if subpath.as_bytes().last() == Some(&b'/') {
// TODO Remove this once PathAuditor is certified
// as the only entrypoint for path data
return Err(DirstateMapError::ConsecutiveSlashes);
}
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 if let Some(val) = self.inner.get_mut(subpath) {
*val += 1;
break;
}
self.inner.insert(subpath.to_owned(), 1);
}
Raphaël Gomès
rust-dirs: address failing tests for `dirs` impl with a temporary fix...
r44227 Ok(())
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
/// Decreases the count of deepest directory contained in the path.
///
/// If it is the only reference, decreases all parents until one is
/// removed.
/// If the directory is not in the map, something horrible has happened.
pub fn delete_path(
&mut self,
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 path: impl AsRef<HgPath>,
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 ) -> Result<(), DirstateMapError> {
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 for subpath in files::find_dirs(path.as_ref()) {
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 match self.inner.entry(subpath.to_owned()) {
Entry::Occupied(mut entry) => {
let val = entry.get().clone();
if val > 1 {
entry.insert(val - 1);
break;
}
entry.remove();
}
Entry::Vacant(_) => {
Raphaël Gomès
rust: run rfmt on all hg-core/hg-cpython code...
r42760 return Err(DirstateMapError::PathNotFound(
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 path.as_ref().to_owned(),
Raphaël Gomès
rust: run rfmt on all hg-core/hg-cpython code...
r42760 ))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
};
}
Ok(())
}
Raphaël Gomès
rust: remove Deref in favor of explicit methods...
r42762
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 pub fn contains(&self, key: impl AsRef<HgPath>) -> bool {
self.inner.contains_key(key.as_ref())
Raphaël Gomès
rust: remove Deref in favor of explicit methods...
r42762 }
Yuya Nishihara
rust-dirstate: specify concrete return type of DirsMultiset::iter()...
r43155 pub fn iter(&self) -> DirsMultisetIter {
Raphaël Gomès
rust-dirstate: improve API of `DirsMultiset`...
r42995 self.inner.keys()
Raphaël Gomès
rust: remove Deref in favor of explicit methods...
r42762 }
pub fn len(&self) -> usize {
self.inner.len()
}
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_delete_path_path_not_found() {
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 let manifest: Vec<HgPathBuf> = vec![];
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 let path = HgPathBuf::from_bytes(b"doesnotexist/");
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 Err(DirstateMapError::PathNotFound(path.to_owned())),
map.delete_path(&path)
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 );
}
#[test]
fn test_delete_path_empty_path() {
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let mut map =
DirsMultiset::from_manifest(&vec![HgPathBuf::new()]).unwrap();
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 let path = HgPath::new(b"");
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(Ok(()), map.delete_path(path));
assert_eq!(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 Err(DirstateMapError::PathNotFound(path.to_owned())),
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 map.delete_path(path)
);
}
#[test]
fn test_delete_path_successful() {
let mut map = DirsMultiset {
inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
.iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .collect(),
};
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
eprintln!("{:?}", map);
assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
eprintln!("{:?}", map);
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
b"a/b/"
))),
map.delete_path(HgPath::new(b"a/b/"))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 );
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 eprintln!("{:?}", map);
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 eprintln!("{:?}", map);
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
b"a/c/"
))),
map.delete_path(HgPath::new(b"a/c/"))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 );
}
#[test]
fn test_add_path_empty_path() {
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 let manifest: Vec<HgPathBuf> = vec![];
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 let path = HgPath::new(b"");
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 map.add_path(path);
assert_eq!(1, map.len());
}
#[test]
fn test_add_path_successful() {
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 let manifest: Vec<HgPathBuf> = vec![];
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a/"));
assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(2, map.len());
// Non directory should be ignored
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a"));
assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(2, map.len());
// Non directory will still add its base
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a/b"));
assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 assert_eq!(2, map.len());
// Duplicate path works
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a/"));
assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
// Nested dir adds to its base
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a/b/"));
assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
// but not its base's base, because it already existed
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a/b/c/"));
assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 map.add_path(HgPath::new(b"a/c/"));
assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
let expected = DirsMultiset {
inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
.iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .collect(),
};
assert_eq!(map, expected);
}
#[test]
fn test_dirsmultiset_new_empty() {
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 let manifest: Vec<HgPathBuf> = vec![];
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let new = DirsMultiset::from_manifest(&manifest).unwrap();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 let expected = DirsMultiset {
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 inner: FastHashMap::default(),
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 };
assert_eq!(expected, new);
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let new = DirsMultiset::from_dirstate(&FastHashMap::default(), None)
.unwrap();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 let expected = DirsMultiset {
Raphaël Gomès
rust-performance: introduce FastHashMap type alias for HashMap...
r44278 inner: FastHashMap::default(),
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 };
assert_eq!(expected, new);
}
#[test]
fn test_dirsmultiset_new_no_skip() {
Raphaël Gomès
rust-dirs-multiset: use `AsRef` instead of concrete types when possible...
r44283 let input_vec: Vec<HgPathBuf> = ["a/", "b/", "a/c", "a/d/"]
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .collect();
let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
.iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .collect();
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let new = DirsMultiset::from_manifest(&input_vec).unwrap();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 let expected = DirsMultiset {
inner: expected_inner,
};
assert_eq!(expected, new);
let input_map = ["a/", "b/", "a/c", "a/d/"]
.iter()
.map(|f| {
(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 HgPathBuf::from_bytes(f.as_bytes()),
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 DirstateEntry {
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 state: EntryState::Normal,
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 mode: 0,
mtime: 0,
size: 0,
},
)
})
.collect();
let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
.iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .collect();
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 let new = DirsMultiset::from_dirstate(&input_map, None).unwrap();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 let expected = DirsMultiset {
inner: expected_inner,
};
assert_eq!(expected, new);
}
#[test]
fn test_dirsmultiset_new_skip() {
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 let input_map = [
("a/", EntryState::Normal),
("a/b/", EntryState::Normal),
("a/c", EntryState::Removed),
("a/d/", EntryState::Merged),
]
.iter()
.map(|(f, state)| {
(
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 HgPathBuf::from_bytes(f.as_bytes()),
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 DirstateEntry {
state: *state,
mode: 0,
mtime: 0,
size: 0,
},
)
})
.collect();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736
// "a" incremented with "a/c" and "a/d/"
let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
.iter()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 .collect();
Raphaël Gomès
rust-dirstate: use EntryState enum instead of literals...
r42994 let new =
Raphaël Gomès
rust-dirs: handle forgotten `Result`s...
r44315 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal))
.unwrap();
Raphaël Gomès
rust-dirstate: add "dirs" Rust implementation...
r42736 let expected = DirsMultiset {
inner: expected_inner,
};
assert_eq!(expected, new);
}
}