diff --git a/rust/hg-core/src/dirstate.rs b/rust/hg-core/src/dirstate.rs --- a/rust/hg-core/src/dirstate.rs +++ b/rust/hg-core/src/dirstate.rs @@ -5,7 +5,7 @@ // 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::DirstateParseError; +use crate::{utils::hg_path::HgPathBuf, DirstateParseError}; use std::collections::HashMap; use std::convert::TryFrom; @@ -30,8 +30,8 @@ pub size: i32, } -pub type StateMap = HashMap, DirstateEntry>; -pub type CopyMap = HashMap, Vec>; +pub type StateMap = HashMap; +pub type CopyMap = HashMap; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum EntryState { diff --git a/rust/hg-core/src/dirstate/dirs_multiset.rs b/rust/hg-core/src/dirstate/dirs_multiset.rs --- a/rust/hg-core/src/dirstate/dirs_multiset.rs +++ b/rust/hg-core/src/dirstate/dirs_multiset.rs @@ -8,6 +8,7 @@ //! A multiset of directory names. //! //! Used to counts the references to directories in a manifest or dirstate. +use crate::utils::hg_path::{HgPath, HgPathBuf}; use crate::{ dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError, }; @@ -16,7 +17,7 @@ #[derive(PartialEq, Debug)] pub struct DirsMultiset { - inner: HashMap, u32>, + inner: HashMap, } impl DirsMultiset { @@ -24,7 +25,7 @@ /// /// If `skip_state` is provided, skips dirstate entries with equal state. pub fn from_dirstate( - vec: &HashMap, DirstateEntry>, + vec: &HashMap, skip_state: Option, ) -> Self { let mut multiset = DirsMultiset { @@ -46,7 +47,7 @@ } /// Initializes the multiset from a manifest. - pub fn from_manifest(vec: &Vec>) -> Self { + pub fn from_manifest(vec: &Vec) -> Self { let mut multiset = DirsMultiset { inner: HashMap::new(), }; @@ -61,7 +62,7 @@ /// Increases the count of deepest directory contained in the path. /// /// If the directory is not yet in the map, adds its parents. - pub fn add_path(&mut self, path: &[u8]) { + pub fn add_path(&mut self, path: &HgPath) { for subpath in files::find_dirs(path) { if let Some(val) = self.inner.get_mut(subpath) { *val += 1; @@ -78,7 +79,7 @@ /// If the directory is not in the map, something horrible has happened. pub fn delete_path( &mut self, - path: &[u8], + path: &HgPath, ) -> Result<(), DirstateMapError> { for subpath in files::find_dirs(path) { match self.inner.entry(subpath.to_owned()) { @@ -101,11 +102,11 @@ Ok(()) } - pub fn contains(&self, key: &[u8]) -> bool { + pub fn contains(&self, key: &HgPath) -> bool { self.inner.contains_key(key) } - pub fn iter(&self) -> impl Iterator> { + pub fn iter(&self) -> impl Iterator { self.inner.keys() } @@ -122,20 +123,20 @@ #[test] fn test_delete_path_path_not_found() { let mut map = DirsMultiset::from_manifest(&vec![]); - let path = b"doesnotexist/"; + let path = HgPathBuf::from_bytes(b"doesnotexist/"); assert_eq!( - Err(DirstateMapError::PathNotFound(path.to_vec())), - map.delete_path(path) + Err(DirstateMapError::PathNotFound(path.to_owned())), + map.delete_path(&path) ); } #[test] fn test_delete_path_empty_path() { - let mut map = DirsMultiset::from_manifest(&vec![vec![]]); - let path = b""; + let mut map = DirsMultiset::from_manifest(&vec![HgPathBuf::new()]); + let path = HgPath::new(b""); assert_eq!(Ok(()), map.delete_path(path)); assert_eq!( - Err(DirstateMapError::PathNotFound(path.to_vec())), + Err(DirstateMapError::PathNotFound(path.to_owned())), map.delete_path(path) ); } @@ -145,34 +146,40 @@ let mut map = DirsMultiset { inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)] .iter() - .map(|(k, v)| (k.as_bytes().to_vec(), *v)) + .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v)) .collect(), }; - assert_eq!(Ok(()), map.delete_path(b"a/b/")); - assert_eq!(Ok(()), map.delete_path(b"a/b/")); + 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); assert_eq!( - Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())), - map.delete_path(b"a/b/") + Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes( + b"a/b/" + ))), + map.delete_path(HgPath::new(b"a/b/")) ); - assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap()); - assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap()); + assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap()); + assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap()); eprintln!("{:?}", map); - assert_eq!(Ok(()), map.delete_path(b"a/")); + assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/"))); eprintln!("{:?}", map); - assert_eq!(Ok(()), map.delete_path(b"a/c/")); + assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/"))); assert_eq!( - Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())), - map.delete_path(b"a/c/") + Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes( + b"a/c/" + ))), + map.delete_path(HgPath::new(b"a/c/")) ); } #[test] fn test_add_path_empty_path() { let mut map = DirsMultiset::from_manifest(&vec![]); - let path = b""; + let path = HgPath::new(b""); map.add_path(path); assert_eq!(1, map.len()); @@ -182,42 +189,42 @@ fn test_add_path_successful() { let mut map = DirsMultiset::from_manifest(&vec![]); - map.add_path(b"a/"); - assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap()); - assert_eq!(1, *map.inner.get(&Vec::new()).unwrap()); + 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()); assert_eq!(2, map.len()); // Non directory should be ignored - map.add_path(b"a"); - assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap()); + map.add_path(HgPath::new(b"a")); + assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap()); assert_eq!(2, map.len()); // Non directory will still add its base - map.add_path(b"a/b"); - assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap()); + map.add_path(HgPath::new(b"a/b")); + assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap()); assert_eq!(2, map.len()); // Duplicate path works - map.add_path(b"a/"); - assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap()); + map.add_path(HgPath::new(b"a/")); + assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap()); // Nested dir adds to its base - map.add_path(b"a/b/"); - assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap()); - assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap()); + 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()); // but not its base's base, because it already existed - map.add_path(b"a/b/c/"); - assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap()); - assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap()); + 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()); - map.add_path(b"a/c/"); - assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap()); + map.add_path(HgPath::new(b"a/c/")); + assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap()); let expected = DirsMultiset { inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)] .iter() - .map(|(k, v)| (k.as_bytes().to_vec(), *v)) + .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v)) .collect(), }; assert_eq!(map, expected); @@ -242,11 +249,11 @@ fn test_dirsmultiset_new_no_skip() { let input_vec = ["a/", "b/", "a/c", "a/d/"] .iter() - .map(|e| e.as_bytes().to_vec()) + .map(|e| HgPathBuf::from_bytes(e.as_bytes())) .collect(); let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] .iter() - .map(|(k, v)| (k.as_bytes().to_vec(), *v)) + .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v)) .collect(); let new = DirsMultiset::from_manifest(&input_vec); @@ -259,7 +266,7 @@ .iter() .map(|f| { ( - f.as_bytes().to_vec(), + HgPathBuf::from_bytes(f.as_bytes()), DirstateEntry { state: EntryState::Normal, mode: 0, @@ -271,7 +278,7 @@ .collect(); let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] .iter() - .map(|(k, v)| (k.as_bytes().to_vec(), *v)) + .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v)) .collect(); let new = DirsMultiset::from_dirstate(&input_map, None); @@ -292,7 +299,7 @@ .iter() .map(|(f, state)| { ( - f.as_bytes().to_vec(), + HgPathBuf::from_bytes(f.as_bytes()), DirstateEntry { state: *state, mode: 0, @@ -306,7 +313,7 @@ // "a" incremented with "a/c" and "a/d/" let expected_inner = [("", 1), ("a", 2), ("a/d", 1)] .iter() - .map(|(k, v)| (k.as_bytes().to_vec(), *v)) + .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v)) .collect(); let new = diff --git a/rust/hg-core/src/dirstate/dirstate_map.rs b/rust/hg-core/src/dirstate/dirstate_map.rs --- a/rust/hg-core/src/dirstate/dirstate_map.rs +++ b/rust/hg-core/src/dirstate/dirstate_map.rs @@ -5,6 +5,7 @@ // 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::utils::hg_path::{HgPath, HgPathBuf}; use crate::{ dirstate::{parsers::PARENT_SIZE, EntryState}, pack_dirstate, parse_dirstate, CopyMap, DirsMultiset, DirstateEntry, @@ -18,7 +19,7 @@ use std::ops::Deref; use std::time::Duration; -pub type FileFoldMap = HashMap, Vec>; +pub type FileFoldMap = HashMap; const NULL_ID: [u8; 20] = [0; 20]; const MTIME_UNSET: i32 = -1; @@ -31,8 +32,8 @@ file_fold_map: Option, pub dirs: Option, pub all_dirs: Option, - non_normal_set: HashSet>, - other_parent_set: HashSet>, + non_normal_set: HashSet, + other_parent_set: HashSet, parents: Option, dirty_parents: bool, } @@ -46,8 +47,8 @@ } } -impl FromIterator<(Vec, DirstateEntry)> for DirstateMap { - fn from_iter, DirstateEntry)>>( +impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap { + fn from_iter>( iter: I, ) -> Self { Self { @@ -77,7 +78,7 @@ /// Add a tracked file to the dirstate pub fn add_file( &mut self, - filename: &[u8], + filename: &HgPath, old_state: EntryState, entry: DirstateEntry, ) { @@ -110,7 +111,7 @@ /// to be more explicit about what that state is. pub fn remove_file( &mut self, - filename: &[u8], + filename: &HgPath, old_state: EntryState, size: i32, ) -> Result<(), DirstateMapError> { @@ -146,7 +147,7 @@ /// Returns `true` if the file was previously recorded. pub fn drop_file( &mut self, - filename: &[u8], + filename: &HgPath, old_state: EntryState, ) -> Result { let exists = self.state_map.remove(filename).is_some(); @@ -171,7 +172,7 @@ pub fn clear_ambiguous_times( &mut self, - filenames: Vec>, + filenames: Vec, now: i32, ) { for filename in filenames { @@ -196,7 +197,7 @@ pub fn non_normal_other_parent_entries( &self, - ) -> (HashSet>, HashSet>) { + ) -> (HashSet, HashSet) { let mut non_normal = HashSet::new(); let mut other_parent = HashSet::new(); @@ -238,12 +239,12 @@ } } - pub fn has_tracked_dir(&mut self, directory: &[u8]) -> bool { + pub fn has_tracked_dir(&mut self, directory: &HgPath) -> bool { self.set_dirs(); self.dirs.as_ref().unwrap().contains(directory) } - pub fn has_dir(&mut self, directory: &[u8]) -> bool { + pub fn has_dir(&mut self, directory: &HgPath) -> bool { self.set_all_dirs(); self.all_dirs.as_ref().unwrap().contains(directory) } @@ -347,11 +348,11 @@ assert!(map.dirs.is_none()); assert!(map.all_dirs.is_none()); - assert_eq!(false, map.has_dir(b"nope")); + assert_eq!(false, map.has_dir(HgPath::new(b"nope"))); assert!(map.all_dirs.is_some()); assert!(map.dirs.is_none()); - assert_eq!(false, map.has_tracked_dir(b"nope")); + assert_eq!(false, map.has_tracked_dir(HgPath::new(b"nope"))); assert!(map.dirs.is_some()); } @@ -362,7 +363,7 @@ assert_eq!(0, map.len()); map.add_file( - b"meh", + HgPath::new(b"meh"), EntryState::Normal, DirstateEntry { state: EntryState::Normal, @@ -395,7 +396,7 @@ .iter() .map(|(fname, (state, mode, size, mtime))| { ( - fname.to_vec(), + HgPathBuf::from_bytes(fname.as_ref()), DirstateEntry { state: *state, mode: *mode, @@ -410,11 +411,11 @@ b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb", ] .iter() - .map(|x| x.to_vec()) + .map(|x| HgPathBuf::from_bytes(x.as_ref())) .collect(); let mut other_parent = HashSet::new(); - other_parent.insert(b"f4".to_vec()); + other_parent.insert(HgPathBuf::from_bytes(b"f4")); assert_eq!( (non_normal, other_parent), diff --git a/rust/hg-core/src/dirstate/parsers.rs b/rust/hg-core/src/dirstate/parsers.rs --- a/rust/hg-core/src/dirstate/parsers.rs +++ b/rust/hg-core/src/dirstate/parsers.rs @@ -3,6 +3,7 @@ // 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::utils::hg_path::HgPath; use crate::{ dirstate::{CopyMap, EntryState, StateMap}, DirstateEntry, DirstatePackError, DirstateParents, DirstateParseError, @@ -60,10 +61,13 @@ }; if let Some(copy_path) = copy { - copy_map.insert(path.to_owned(), copy_path.to_owned()); + copy_map.insert( + HgPath::new(path).to_owned(), + HgPath::new(copy_path).to_owned(), + ); }; state_map.insert( - path.to_owned(), + HgPath::new(path).to_owned(), DirstateEntry { state, mode, @@ -106,7 +110,7 @@ packed.extend(&parents.p2); for (filename, entry) in state_map.iter() { - let mut new_filename: Vec = filename.to_owned(); + let new_filename = filename.to_owned(); let mut new_mtime: i32 = entry.mtime; if entry.state == EntryState::Normal && entry.mtime == now { // The file was last modified "simultaneously" with the current @@ -127,10 +131,10 @@ }, )); } - + let mut new_filename = new_filename.into_vec(); if let Some(copy) = copy_map.get(filename) { new_filename.push('\0' as u8); - new_filename.extend(copy); + new_filename.extend(copy.bytes()); } packed.write_u8(entry.state.into())?; @@ -153,6 +157,7 @@ #[cfg(test)] mod tests { use super::*; + use crate::utils::hg_path::HgPathBuf; use std::collections::HashMap; #[test] @@ -176,7 +181,7 @@ #[test] fn test_pack_dirstate_one_entry() { let expected_state_map: StateMap = [( - b"f1".to_vec(), + HgPathBuf::from_bytes(b"f1"), DirstateEntry { state: EntryState::Normal, mode: 0o644, @@ -213,7 +218,7 @@ #[test] fn test_pack_dirstate_one_entry_with_copy() { let expected_state_map: StateMap = [( - b"f1".to_vec(), + HgPathBuf::from_bytes(b"f1"), DirstateEntry { state: EntryState::Normal, mode: 0o644, @@ -226,7 +231,10 @@ .collect(); let mut state_map = expected_state_map.clone(); let mut copymap = HashMap::new(); - copymap.insert(b"f1".to_vec(), b"copyname".to_vec()); + copymap.insert( + HgPathBuf::from_bytes(b"f1"), + HgPathBuf::from_bytes(b"copyname"), + ); let parents = DirstateParents { p1: *b"12345678910111213141", p2: *b"00000000000000000000", @@ -251,7 +259,7 @@ #[test] fn test_parse_pack_one_entry_with_copy() { let mut state_map: StateMap = [( - b"f1".to_vec(), + HgPathBuf::from_bytes(b"f1"), DirstateEntry { state: EntryState::Normal, mode: 0o644, @@ -263,7 +271,10 @@ .cloned() .collect(); let mut copymap = HashMap::new(); - copymap.insert(b"f1".to_vec(), b"copyname".to_vec()); + copymap.insert( + HgPathBuf::from_bytes(b"f1"), + HgPathBuf::from_bytes(b"copyname"), + ); let parents = DirstateParents { p1: *b"12345678910111213141", p2: *b"00000000000000000000", @@ -291,7 +302,7 @@ fn test_parse_pack_multiple_entries_with_copy() { let mut state_map: StateMap = [ ( - b"f1".to_vec(), + HgPathBuf::from_bytes(b"f1"), DirstateEntry { state: EntryState::Normal, mode: 0o644, @@ -300,7 +311,7 @@ }, ), ( - b"f2".to_vec(), + HgPathBuf::from_bytes(b"f2"), DirstateEntry { state: EntryState::Merged, mode: 0o777, @@ -309,7 +320,7 @@ }, ), ( - b"f3".to_vec(), + HgPathBuf::from_bytes(b"f3"), DirstateEntry { state: EntryState::Removed, mode: 0o644, @@ -318,7 +329,7 @@ }, ), ( - b"f4\xF6".to_vec(), + HgPathBuf::from_bytes(b"f4\xF6"), DirstateEntry { state: EntryState::Added, mode: 0o644, @@ -331,8 +342,14 @@ .cloned() .collect(); let mut copymap = HashMap::new(); - copymap.insert(b"f1".to_vec(), b"copyname".to_vec()); - copymap.insert(b"f4\xF6".to_vec(), b"copyname2".to_vec()); + copymap.insert( + HgPathBuf::from_bytes(b"f1"), + HgPathBuf::from_bytes(b"copyname"), + ); + copymap.insert( + HgPathBuf::from_bytes(b"f4\xF6"), + HgPathBuf::from_bytes(b"copyname2"), + ); let parents = DirstateParents { p1: *b"12345678910111213141", p2: *b"00000000000000000000", @@ -360,7 +377,7 @@ /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4 fn test_parse_pack_one_entry_with_copy_and_time_conflict() { let mut state_map: StateMap = [( - b"f1".to_vec(), + HgPathBuf::from_bytes(b"f1"), DirstateEntry { state: EntryState::Normal, mode: 0o644, @@ -372,7 +389,10 @@ .cloned() .collect(); let mut copymap = HashMap::new(); - copymap.insert(b"f1".to_vec(), b"copyname".to_vec()); + copymap.insert( + HgPathBuf::from_bytes(b"f1"), + HgPathBuf::from_bytes(b"copyname"), + ); let parents = DirstateParents { p1: *b"12345678910111213141", p2: *b"00000000000000000000", @@ -395,7 +415,7 @@ ( parents, [( - b"f1".to_vec(), + HgPathBuf::from_bytes(b"f1"), DirstateEntry { state: EntryState::Normal, mode: 0o644, diff --git a/rust/hg-core/src/filepatterns.rs b/rust/hg-core/src/filepatterns.rs --- a/rust/hg-core/src/filepatterns.rs +++ b/rust/hg-core/src/filepatterns.rs @@ -8,14 +8,19 @@ //! Handling of Mercurial-specific patterns. use crate::{ - utils::{files::get_path_from_bytes, SliceExt}, + utils::{ + hg_path::{HgPath, HgPathBuf}, + SliceExt, + }, LineNumber, PatternError, PatternFileError, }; use lazy_static::lazy_static; use regex::bytes::{NoExpand, Regex}; use std::collections::HashMap; +use std::convert::TryInto; use std::fs::File; use std::io::Read; +use std::path::PathBuf; use std::vec::Vec; lazy_static! { @@ -236,11 +241,11 @@ } pub type PatternTuple = (Vec, LineNumber, Vec); -type WarningTuple = (Vec, Vec); +type WarningTuple = (HgPathBuf, Vec); pub fn parse_pattern_file_contents( lines: &[u8], - file_path: &[u8], + file_path: &HgPath, warn: bool, ) -> (Vec, Vec) { let comment_regex = Regex::new(r"((?:^|[^\\])(?:\\\\)*)#.*").unwrap(); @@ -303,10 +308,11 @@ } pub fn read_pattern_file( - file_path: &[u8], + file_path: &HgPath, warn: bool, ) -> Result<(Vec, Vec), PatternFileError> { - let mut f = File::open(get_path_from_bytes(file_path))?; + let path: PathBuf = file_path.to_owned().try_into()?; + let mut f = File::open(path)?; let mut contents = Vec::new(); f.read_to_end(&mut contents)?; @@ -348,18 +354,33 @@ assert_eq!( vec![(b"relglob:*.elc".to_vec(), 2, b"*.elc".to_vec())], - parse_pattern_file_contents(lines, b"file_path", false).0, + parse_pattern_file_contents( + lines, + HgPath::new(b"file_path"), + false + ) + .0, ); let lines = b"syntax: include\nsyntax: glob"; assert_eq!( - parse_pattern_file_contents(lines, b"file_path", false).0, + parse_pattern_file_contents( + lines, + HgPath::new(b"file_path"), + false + ) + .0, vec![] ); let lines = b"glob:**.o"; assert_eq!( - parse_pattern_file_contents(lines, b"file_path", false).0, + parse_pattern_file_contents( + lines, + HgPath::new(b"file_path"), + false + ) + .0, vec![(b"relglob:**.o".to_vec(), 1, b"**.o".to_vec())] ); } 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 @@ -17,6 +17,7 @@ mod filepatterns; pub mod utils; +use crate::utils::hg_path::HgPathBuf; pub use filepatterns::{ build_single_regex, read_pattern_file, PatternSyntax, PatternTuple, }; @@ -95,7 +96,7 @@ } #[derive(Debug, PartialEq)] pub enum DirstateMapError { - PathNotFound(Vec), + PathNotFound(HgPathBuf), EmptyPath, } diff --git a/rust/hg-core/src/utils/files.rs b/rust/hg-core/src/utils/files.rs --- a/rust/hg-core/src/utils/files.rs +++ b/rust/hg-core/src/utils/files.rs @@ -9,43 +9,24 @@ //! Functions for fiddling with files. +use crate::utils::hg_path::{HgPath, HgPathBuf}; use std::iter::FusedIterator; -use std::path::Path; - -pub fn get_path_from_bytes(bytes: &[u8]) -> &Path { - let os_str; - #[cfg(unix)] - { - use std::os::unix::ffi::OsStrExt; - os_str = std::ffi::OsStr::from_bytes(bytes); - } - #[cfg(windows)] - { - // TODO: convert from Windows MBCS (ANSI encoding) to WTF8. - // Perhaps, the return type would have to be Result. - use std::os::windows::ffi::OsStrExt; - os_str = std::ffi::OsString::from_wide(bytes); - } - - Path::new(os_str) -} /// An iterator over repository path yielding itself and its ancestors. #[derive(Copy, Clone, Debug)] pub struct Ancestors<'a> { - next: Option<&'a [u8]>, + next: Option<&'a HgPath>, } impl<'a> Iterator for Ancestors<'a> { - // if we had an HgPath type, this would yield &'a HgPath - type Item = &'a [u8]; + type Item = &'a HgPath; fn next(&mut self) -> Option { let next = self.next; self.next = match self.next { Some(s) if s.is_empty() => None, Some(s) => { - let p = s.iter().rposition(|&c| c == b'/').unwrap_or(0); + let p = s.bytes().rposition(|c| c == b'/').unwrap_or(0); Some(&s[..p]) } None => None, @@ -63,7 +44,7 @@ /// /// The path itself isn't included unless it is b"" (meaning the root /// directory.) -pub fn find_dirs<'a>(path: &'a [u8]) -> Ancestors<'a> { +pub fn find_dirs<'a>(path: &'a HgPath) -> Ancestors<'a> { let mut dirs = Ancestors { next: Some(path) }; if !path.is_empty() { dirs.next(); // skip itself @@ -71,14 +52,24 @@ dirs } +/// TODO more than ASCII? +pub fn normalize_case(path: &HgPath) -> HgPathBuf { + #[cfg(windows)] // NTFS compares via upper() + return path.to_ascii_uppercase(); + #[cfg(unix)] + path.to_ascii_lowercase() +} + #[cfg(test)] mod tests { + use super::*; + #[test] fn find_dirs_some() { - let mut dirs = super::find_dirs(b"foo/bar/baz"); - assert_eq!(dirs.next(), Some(b"foo/bar".as_ref())); - assert_eq!(dirs.next(), Some(b"foo".as_ref())); - assert_eq!(dirs.next(), Some(b"".as_ref())); + let mut dirs = super::find_dirs(HgPath::new(b"foo/bar/baz")); + assert_eq!(dirs.next(), Some(HgPath::new(b"foo/bar"))); + assert_eq!(dirs.next(), Some(HgPath::new(b"foo"))); + assert_eq!(dirs.next(), Some(HgPath::new(b""))); assert_eq!(dirs.next(), None); assert_eq!(dirs.next(), None); } @@ -86,8 +77,8 @@ #[test] fn find_dirs_empty() { // looks weird, but mercurial.util.finddirs(b"") yields b"" - let mut dirs = super::find_dirs(b""); - assert_eq!(dirs.next(), Some(b"".as_ref())); + let mut dirs = super::find_dirs(HgPath::new(b"")); + assert_eq!(dirs.next(), Some(HgPath::new(b""))); assert_eq!(dirs.next(), None); assert_eq!(dirs.next(), None); } diff --git a/rust/hg-cpython/src/dirstate.rs b/rust/hg-cpython/src/dirstate.rs --- a/rust/hg-cpython/src/dirstate.rs +++ b/rust/hg-cpython/src/dirstate.rs @@ -17,7 +17,10 @@ exc, PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence, Python, }; -use hg::{DirstateEntry, DirstateParseError, EntryState, StateMap}; +use hg::{ + utils::hg_path::HgPathBuf, DirstateEntry, DirstateParseError, EntryState, + StateMap, +}; use libc::{c_char, c_int}; #[cfg(feature = "python27")] use python27_sys::PyCapsule_Import; @@ -75,7 +78,7 @@ let filename = filename.extract::(py)?; let filename = filename.data(py); Ok(( - filename.to_owned(), + HgPathBuf::from(filename.to_owned()), DirstateEntry { state, mode, diff --git a/rust/hg-cpython/src/dirstate/copymap.rs b/rust/hg-cpython/src/dirstate/copymap.rs --- a/rust/hg-cpython/src/dirstate/copymap.rs +++ b/rust/hg-cpython/src/dirstate/copymap.rs @@ -12,6 +12,7 @@ use std::cell::RefCell; use crate::dirstate::dirstate_map::{DirstateMap, DirstateMapLeakedRef}; +use hg::utils::hg_path::HgPathBuf; py_class!(pub class CopyMap |py| { data dirstate_map: DirstateMap; @@ -84,24 +85,27 @@ } fn translate_key( py: Python, - res: (&Vec, &Vec), + res: (&HgPathBuf, &HgPathBuf), ) -> PyResult> { - Ok(Some(PyBytes::new(py, res.0))) + Ok(Some(PyBytes::new(py, res.0.as_ref()))) } fn translate_key_value( py: Python, - res: (&Vec, &Vec), + res: (&HgPathBuf, &HgPathBuf), ) -> PyResult> { let (k, v) = res; - Ok(Some((PyBytes::new(py, k), PyBytes::new(py, v)))) + Ok(Some(( + PyBytes::new(py, k.as_ref()), + PyBytes::new(py, v.as_ref()), + ))) } } py_shared_mapping_iterator!( CopyMapKeysIterator, DirstateMapLeakedRef, - Vec, - Vec, + HgPathBuf, + HgPathBuf, CopyMap::translate_key, Option ); @@ -109,8 +113,8 @@ py_shared_mapping_iterator!( CopyMapItemsIterator, DirstateMapLeakedRef, - Vec, - Vec, + HgPathBuf, + HgPathBuf, CopyMap::translate_key_value, Option<(PyBytes, PyBytes)> ); diff --git a/rust/hg-cpython/src/dirstate/dirs_multiset.rs b/rust/hg-cpython/src/dirstate/dirs_multiset.rs --- a/rust/hg-cpython/src/dirstate/dirs_multiset.rs +++ b/rust/hg-cpython/src/dirstate/dirs_multiset.rs @@ -17,7 +17,10 @@ }; use crate::{dirstate::extract_dirstate, ref_sharing::PySharedState}; -use hg::{DirsMultiset, DirstateMapError, DirstateParseError, EntryState}; +use hg::{ + utils::hg_path::{HgPath, HgPathBuf}, + DirsMultiset, DirstateMapError, DirstateParseError, EntryState, +}; py_class!(pub class Dirs |py| { data inner: RefCell; @@ -44,9 +47,13 @@ let dirstate = extract_dirstate(py, &map)?; DirsMultiset::from_dirstate(&dirstate, skip_state) } else { - let map: Result>, PyErr> = map + let map: Result, PyErr> = map .iter(py)? - .map(|o| Ok(o?.extract::(py)?.data(py).to_owned())) + .map(|o| { + Ok(HgPathBuf::from_bytes( + o?.extract::(py)?.data(py), + )) + }) .collect(); DirsMultiset::from_manifest(&map?) }; @@ -60,14 +67,14 @@ def addpath(&self, path: PyObject) -> PyResult { self.borrow_mut(py)?.add_path( - path.extract::(py)?.data(py), + HgPath::new(path.extract::(py)?.data(py)), ); Ok(py.None()) } def delpath(&self, path: PyObject) -> PyResult { self.borrow_mut(py)?.delete_path( - path.extract::(py)?.data(py), + HgPath::new(path.extract::(py)?.data(py)), ) .and(Ok(py.None())) .or_else(|e| { @@ -93,10 +100,9 @@ } def __contains__(&self, item: PyObject) -> PyResult { - Ok(self - .inner(py) - .borrow() - .contains(item.extract::(py)?.data(py).as_ref())) + Ok(self.inner(py).borrow().contains(HgPath::new( + item.extract::(py)?.data(py).as_ref(), + ))) } }); @@ -107,15 +113,18 @@ Self::create_instance(py, RefCell::new(d), PySharedState::default()) } - fn translate_key(py: Python, res: &Vec) -> PyResult> { - Ok(Some(PyBytes::new(py, res))) + fn translate_key( + py: Python, + res: &HgPathBuf, + ) -> PyResult> { + Ok(Some(PyBytes::new(py, res.as_ref()))) } } py_shared_sequence_iterator!( DirsMultisetKeysIterator, DirsMultisetLeakedRef, - Vec, + HgPathBuf, Dirs::translate_key, Option ); diff --git a/rust/hg-cpython/src/dirstate/dirstate_map.rs b/rust/hg-cpython/src/dirstate/dirstate_map.rs --- a/rust/hg-cpython/src/dirstate/dirstate_map.rs +++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs @@ -24,6 +24,7 @@ ref_sharing::PySharedState, }; use hg::{ + utils::hg_path::{HgPath, HgPathBuf}, DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap, DirstateParents, DirstateParseError, EntryState, PARENT_SIZE, }; @@ -64,7 +65,7 @@ default: Option = None ) -> PyResult> { let key = key.extract::(py)?; - match self.inner(py).borrow().get(key.data(py)) { + match self.inner(py).borrow().get(HgPath::new(key.data(py))) { Some(entry) => { // Explicitly go through u8 first, then cast to // platform-specific `c_char`. @@ -90,7 +91,7 @@ mtime: PyObject ) -> PyResult { self.borrow_mut(py)?.add_file( - f.extract::(py)?.data(py), + HgPath::new(f.extract::(py)?.data(py)), oldstate.extract::(py)?.data(py)[0] .try_into() .map_err(|e: DirstateParseError| { @@ -118,7 +119,7 @@ ) -> PyResult { self.borrow_mut(py)? .remove_file( - f.extract::(py)?.data(py), + HgPath::new(f.extract::(py)?.data(py)), oldstate.extract::(py)?.data(py)[0] .try_into() .map_err(|e: DirstateParseError| { @@ -142,7 +143,7 @@ ) -> PyResult { self.borrow_mut(py)? .drop_file( - f.extract::(py)?.data(py), + HgPath::new(f.extract::(py)?.data(py)), oldstate.extract::(py)?.data(py)[0] .try_into() .map_err(|e: DirstateParseError| { @@ -163,10 +164,12 @@ files: PyObject, now: PyObject ) -> PyResult { - let files: PyResult>> = files + let files: PyResult> = files .iter(py)? .map(|filename| { - Ok(filename?.extract::(py)?.data(py).to_owned()) + Ok(HgPathBuf::from_bytes( + filename?.extract::(py)?.data(py), + )) }) .collect(); self.inner(py) @@ -186,7 +189,7 @@ "non_normal", non_normal .iter() - .map(|v| PyBytes::new(py, &v)) + .map(|v| PyBytes::new(py, v.as_ref())) .collect::>() .to_py_object(py), )?; @@ -195,7 +198,7 @@ "other_parent", other_parent .iter() - .map(|v| PyBytes::new(py, &v)) + .map(|v| PyBytes::new(py, v.as_ref())) .collect::>() .to_py_object(py), )?; @@ -208,7 +211,7 @@ Ok(self .inner(py) .borrow_mut() - .has_tracked_dir(d.data(py)) + .has_tracked_dir(HgPath::new(d.data(py))) .to_py_object(py)) } @@ -217,7 +220,7 @@ Ok(self .inner(py) .borrow_mut() - .has_dir(d.data(py)) + .has_dir(HgPath::new(d.data(py))) .to_py_object(py)) } @@ -291,7 +294,7 @@ for (key, value) in self.borrow_mut(py)?.build_file_fold_map().iter() { - dict.set_item(py, key, value)?; + dict.set_item(py, key.as_vec(), value.as_vec())?; } Ok(dict) } @@ -302,12 +305,12 @@ def __contains__(&self, key: PyObject) -> PyResult { let key = key.extract::(py)?; - Ok(self.inner(py).borrow().contains_key(key.data(py))) + Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py)))) } def __getitem__(&self, key: PyObject) -> PyResult { let key = key.extract::(py)?; - let key = key.data(py); + let key = HgPath::new(key.data(py)); match self.inner(py).borrow().get(key) { Some(entry) => { // Explicitly go through u8 first, then cast to @@ -322,7 +325,7 @@ }, None => Err(PyErr::new::( py, - String::from_utf8_lossy(key), + String::from_utf8_lossy(key.as_bytes()), )), } } @@ -378,15 +381,19 @@ def copymapcopy(&self) -> PyResult { let dict = PyDict::new(py); for (key, value) in self.inner(py).borrow().copy_map.iter() { - dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?; + dict.set_item( + py, + PyBytes::new(py, key.as_ref()), + PyBytes::new(py, value.as_ref()), + )?; } Ok(dict) } def copymapgetitem(&self, key: PyObject) -> PyResult { let key = key.extract::(py)?; - match self.inner(py).borrow().copy_map.get(key.data(py)) { - Some(copy) => Ok(PyBytes::new(py, copy)), + match self.inner(py).borrow().copy_map.get(HgPath::new(key.data(py))) { + Some(copy) => Ok(PyBytes::new(py, copy.as_ref())), None => Err(PyErr::new::( py, String::from_utf8_lossy(key.data(py)), @@ -402,7 +409,11 @@ } def copymapcontains(&self, key: PyObject) -> PyResult { let key = key.extract::(py)?; - Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py))) + Ok(self + .inner(py) + .borrow() + .copy_map + .contains_key(HgPath::new(key.data(py)))) } def copymapget( &self, @@ -410,8 +421,15 @@ default: Option ) -> PyResult> { let key = key.extract::(py)?; - match self.inner(py).borrow().copy_map.get(key.data(py)) { - Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())), + match self + .inner(py) + .borrow() + .copy_map + .get(HgPath::new(key.data(py))) + { + Some(copy) => Ok(Some( + PyBytes::new(py, copy.as_ref()).into_object(), + )), None => Ok(default), } } @@ -422,10 +440,10 @@ ) -> PyResult { let key = key.extract::(py)?; let value = value.extract::(py)?; - self.inner(py) - .borrow_mut() - .copy_map - .insert(key.data(py).to_vec(), value.data(py).to_vec()); + self.inner(py).borrow_mut().copy_map.insert( + HgPathBuf::from_bytes(key.data(py)), + HgPathBuf::from_bytes(value.data(py)), + ); Ok(py.None()) } def copymappop( @@ -434,7 +452,12 @@ default: Option ) -> PyResult> { let key = key.extract::(py)?; - match self.inner(py).borrow_mut().copy_map.remove(key.data(py)) { + match self + .inner(py) + .borrow_mut() + .copy_map + .remove(HgPath::new(key.data(py))) + { Some(_) => Ok(None), None => Ok(default), } @@ -461,13 +484,13 @@ impl DirstateMap { fn translate_key( py: Python, - res: (&Vec, &DirstateEntry), + res: (&HgPathBuf, &DirstateEntry), ) -> PyResult> { - Ok(Some(PyBytes::new(py, res.0))) + Ok(Some(PyBytes::new(py, res.0.as_ref()))) } fn translate_key_value( py: Python, - res: (&Vec, &DirstateEntry), + res: (&HgPathBuf, &DirstateEntry), ) -> PyResult> { let (f, entry) = res; @@ -475,7 +498,7 @@ // platform-specific `c_char`. let state: u8 = entry.state.into(); Ok(Some(( - PyBytes::new(py, f), + PyBytes::new(py, f.as_ref()), decapsule_make_dirstate_tuple(py)?( state as c_char, entry.mode, @@ -491,7 +514,7 @@ py_shared_mapping_iterator!( DirstateMapKeysIterator, DirstateMapLeakedRef, - Vec, + HgPathBuf, DirstateEntry, DirstateMap::translate_key, Option @@ -500,7 +523,7 @@ py_shared_mapping_iterator!( DirstateMapItemsIterator, DirstateMapLeakedRef, - Vec, + HgPathBuf, DirstateEntry, DirstateMap::translate_key_value, Option<(PyBytes, PyObject)> diff --git a/rust/hg-cpython/src/filepatterns.rs b/rust/hg-cpython/src/filepatterns.rs --- a/rust/hg-cpython/src/filepatterns.rs +++ b/rust/hg-cpython/src/filepatterns.rs @@ -14,7 +14,11 @@ use cpython::{ PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject, }; -use hg::{build_single_regex, read_pattern_file, LineNumber, PatternTuple}; +use hg::{ + build_single_regex, read_pattern_file, + utils::hg_path::{HgPath, HgPathBuf}, + LineNumber, PatternTuple, +}; /// Rust does not like functions with different return signatures. /// The 3-tuple version is always returned by the hg-core function, @@ -32,7 +36,10 @@ warn: bool, source_info: bool, ) -> PyResult { - match read_pattern_file(file_path.extract::(py)?.data(py), warn) { + match read_pattern_file( + HgPath::new(file_path.extract::(py)?.data(py)), + warn, + ) { Ok((patterns, warnings)) => { if source_info { let itemgetter = |x: &PatternTuple| { @@ -57,11 +64,13 @@ fn warnings_to_py_bytes( py: Python, - warnings: &[(Vec, Vec)], + warnings: &[(HgPathBuf, Vec)], ) -> Vec<(PyBytes, PyBytes)> { warnings .iter() - .map(|(path, syn)| (PyBytes::new(py, path), PyBytes::new(py, syn))) + .map(|(path, syn)| { + (PyBytes::new(py, path.as_ref()), PyBytes::new(py, syn)) + }) .collect() } diff --git a/rust/hg-cpython/src/parsers.rs b/rust/hg-cpython/src/parsers.rs --- a/rust/hg-cpython/src/parsers.rs +++ b/rust/hg-cpython/src/parsers.rs @@ -15,8 +15,8 @@ PythonObject, ToPyObject, }; use hg::{ - pack_dirstate, parse_dirstate, DirstateEntry, DirstatePackError, - DirstateParents, DirstateParseError, PARENT_SIZE, + pack_dirstate, parse_dirstate, utils::hg_path::HgPathBuf, DirstateEntry, + DirstatePackError, DirstateParents, DirstateParseError, PARENT_SIZE, }; use std::collections::HashMap; use std::convert::TryInto; @@ -46,7 +46,7 @@ dmap.set_item( py, - PyBytes::new(py, &filename), + PyBytes::new(py, filename.as_ref()), decapsule_make_dirstate_tuple(py)?( state as c_char, entry.mode, @@ -58,8 +58,8 @@ for (path, copy_path) in copies { copymap.set_item( py, - PyBytes::new(py, &path), - PyBytes::new(py, ©_path), + PyBytes::new(py, path.as_ref()), + PyBytes::new(py, copy_path.as_ref()), )?; } Ok( @@ -99,13 +99,13 @@ let mut dirstate_map = extract_dirstate(py, &dmap)?; - let copies: Result, Vec>, PyErr> = copymap + let copies: Result, PyErr> = copymap .items(py) .iter() .map(|(key, value)| { Ok(( - key.extract::(py)?.data(py).to_owned(), - value.extract::(py)?.data(py).to_owned(), + HgPathBuf::from_bytes(key.extract::(py)?.data(py)), + HgPathBuf::from_bytes(value.extract::(py)?.data(py)), )) }) .collect(); @@ -144,7 +144,7 @@ let state: u8 = state.into(); dmap.set_item( py, - PyBytes::new(py, &filename[..]), + PyBytes::new(py, filename.as_ref()), decapsule_make_dirstate_tuple(py)?( state as c_char, mode,