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::hash_map; use std::collections::HashMap; use std::convert::TryFrom; @@ -31,10 +31,10 @@ pub size: i32, } -pub type StateMap = HashMap, DirstateEntry>; -pub type StateMapIter<'a> = hash_map::Iter<'a, Vec, DirstateEntry>; -pub type CopyMap = HashMap, Vec>; -pub type CopyMapIter<'a> = hash_map::Iter<'a, Vec, Vec>; +pub type StateMap = HashMap; +pub type StateMapIter<'a> = hash_map::Iter<'a, HgPathBuf, DirstateEntry>; +pub type CopyMap = HashMap; +pub type CopyMapIter<'a> = hash_map::Iter<'a, HgPathBuf, HgPathBuf>; #[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, }; @@ -15,11 +16,11 @@ use std::collections::HashMap; // could be encapsulated if we care API stability more seriously -pub type DirsMultisetIter<'a> = hash_map::Keys<'a, Vec, u32>; +pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>; #[derive(PartialEq, Debug)] pub struct DirsMultiset { - inner: HashMap, u32>, + inner: HashMap, } impl DirsMultiset { @@ -27,7 +28,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 { @@ -49,7 +50,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(), }; @@ -64,7 +65,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; @@ -81,7 +82,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()) { @@ -104,7 +105,7 @@ Ok(()) } - pub fn contains(&self, key: &[u8]) -> bool { + pub fn contains(&self, key: &HgPath) -> bool { self.inner.contains_key(key) } @@ -125,20 +126,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) ); } @@ -148,34 +149,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()); @@ -185,42 +192,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); @@ -245,11 +252,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); @@ -262,7 +269,7 @@ .iter() .map(|f| { ( - f.as_bytes().to_vec(), + HgPathBuf::from_bytes(f.as_bytes()), DirstateEntry { state: EntryState::Normal, mode: 0, @@ -274,7 +281,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); @@ -295,7 +302,7 @@ .iter() .map(|(f, state)| { ( - f.as_bytes().to_vec(), + HgPathBuf::from_bytes(f.as_bytes()), DirstateEntry { state: *state, mode: 0, @@ -309,7 +316,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, @@ -19,7 +20,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; @@ -32,8 +33,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, } @@ -47,8 +48,8 @@ } } -impl FromIterator<(Vec, DirstateEntry)> for DirstateMap { - fn from_iter, DirstateEntry)>>( +impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap { + fn from_iter>( iter: I, ) -> Self { Self { @@ -78,7 +79,7 @@ /// Add a tracked file to the dirstate pub fn add_file( &mut self, - filename: &[u8], + filename: &HgPath, old_state: EntryState, entry: DirstateEntry, ) { @@ -111,7 +112,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> { @@ -147,7 +148,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(); @@ -172,7 +173,7 @@ pub fn clear_ambiguous_times( &mut self, - filenames: Vec>, + filenames: Vec, now: i32, ) { for filename in filenames { @@ -197,7 +198,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(); @@ -239,12 +240,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) } @@ -346,11 +347,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()); } @@ -361,7 +362,7 @@ assert_eq!(0, map.len()); map.add_file( - b"meh", + HgPath::new(b"meh"), EntryState::Normal, DirstateEntry { state: EntryState::Normal, @@ -394,7 +395,7 @@ .iter() .map(|(fname, (state, mode, size, mtime))| { ( - fname.to_vec(), + HgPathBuf::from_bytes(fname.as_ref()), DirstateEntry { state: *state, mode: *mode, @@ -409,11 +410,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 @@ -7,15 +7,13 @@ //! Handling of Mercurial-specific patterns. -use crate::{ - utils::{files::get_path_from_bytes, SliceExt}, - LineNumber, PatternError, PatternFileError, -}; +use crate::{utils::SliceExt, LineNumber, PatternError, PatternFileError}; use lazy_static::lazy_static; use regex::bytes::{NoExpand, Regex}; use std::collections::HashMap; use std::fs::File; use std::io::Read; +use std::path::{Path, PathBuf}; use std::vec::Vec; lazy_static! { @@ -230,11 +228,11 @@ } pub type PatternTuple = (Vec, LineNumber, Vec); -type WarningTuple = (Vec, Vec); +type WarningTuple = (PathBuf, Vec); -pub fn parse_pattern_file_contents( +pub fn parse_pattern_file_contents>( lines: &[u8], - file_path: &[u8], + file_path: P, warn: bool, ) -> (Vec, Vec) { let comment_regex = Regex::new(r"((?:^|[^\\])(?:\\\\)*)#.*").unwrap(); @@ -268,7 +266,8 @@ if let Some(rel_syntax) = SYNTAXES.get(syntax) { current_syntax = rel_syntax; } else if warn { - warnings.push((file_path.to_owned(), syntax.to_owned())); + warnings + .push((file_path.as_ref().to_owned(), syntax.to_owned())); } continue; } @@ -297,11 +296,11 @@ (inputs, warnings) } -pub fn read_pattern_file( - file_path: &[u8], +pub fn read_pattern_file>( + file_path: P, warn: bool, ) -> Result<(Vec, Vec), PatternFileError> { - let mut f = File::open(get_path_from_bytes(file_path))?; + let mut f = File::open(file_path.as_ref())?; let mut contents = Vec::new(); f.read_to_end(&mut contents)?; @@ -343,18 +342,21 @@ 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, Path::new("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, Path::new("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, Path::new("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 @@ -18,6 +18,7 @@ mod filepatterns; pub mod utils; +use crate::utils::hg_path::HgPathBuf; pub use filepatterns::{ build_single_regex, read_pattern_file, PatternSyntax, PatternTuple, }; @@ -96,7 +97,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,7 +9,9 @@ //! 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 { @@ -23,8 +25,7 @@ { // 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); + unimplemented!() } Path::new(os_str) @@ -33,20 +34,19 @@ /// 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); - Some(&s[..p]) + let p = s.bytes().rposition(|c| *c == b'/').unwrap_or(0); + Some(HgPath::new(&s.as_bytes()[..p])) } None => None, }; @@ -63,7 +63,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,23 +71,24 @@ dirs } -/// TODO improve handling of utf8 file names. Our overall strategy for -/// filenames has to be revisited anyway, since Windows is UTF-16. -pub fn normalize_case(bytes: &[u8]) -> Vec { +/// TODO more than ASCII? +pub fn normalize_case(path: &HgPath) -> HgPathBuf { #[cfg(windows)] // NTFS compares via upper() - return bytes.to_ascii_uppercase(); + return path.to_ascii_uppercase(); #[cfg(unix)] - bytes.to_ascii_lowercase() + 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); } @@ -95,8 +96,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,7 +12,7 @@ use std::cell::RefCell; use crate::dirstate::dirstate_map::{DirstateMap, DirstateMapLeakedRef}; -use hg::CopyMapIter; +use hg::{utils::hg_path::HgPathBuf, CopyMapIter}; py_class!(pub class CopyMap |py| { data dirstate_map: DirstateMap; @@ -85,16 +85,19 @@ } 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()), + ))) } } 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 @@ -16,9 +16,12 @@ Python, }; -use crate::dirstate::extract_dirstate; -use crate::ref_sharing::{PySharedRefCell, PySharedState}; +use crate::{ + dirstate::extract_dirstate, + ref_sharing::{PySharedRefCell, PySharedState}, +}; use hg::{ + utils::hg_path::{HgPath, HgPathBuf}, DirsMultiset, DirsMultisetIter, DirstateMapError, DirstateParseError, EntryState, }; @@ -48,9 +51,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?) }; @@ -64,14 +71,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| { @@ -98,10 +105,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(), + ))) } }); @@ -116,8 +122,11 @@ ) } - 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()))) } } 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::{PySharedRefCell, PySharedState}, }; use hg::{ + utils::hg_path::{HgPath, HgPathBuf}, DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap, DirstateParents, DirstateParseError, EntryState, StateMapIter, PARENT_SIZE, @@ -65,7 +66,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`. @@ -91,7 +92,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| { @@ -119,7 +120,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| { @@ -143,7 +144,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| { @@ -164,10 +165,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.borrow_mut(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), )?; @@ -206,14 +209,14 @@ def hastrackeddir(&self, d: PyObject) -> PyResult { let d = d.extract::(py)?; Ok(self.borrow_mut(py)? - .has_tracked_dir(d.data(py)) + .has_tracked_dir(HgPath::new(d.data(py))) .to_py_object(py)) } def hasdir(&self, d: PyObject) -> PyResult { let d = d.extract::(py)?; Ok(self.borrow_mut(py)? - .has_dir(d.data(py)) + .has_dir(HgPath::new(d.data(py))) .to_py_object(py)) } @@ -280,10 +283,8 @@ def filefoldmapasdict(&self) -> PyResult { let dict = PyDict::new(py); - for (key, value) in - self.borrow_mut(py)?.build_file_fold_map().iter() - { - dict.set_item(py, key, value)?; + for (key, value) in self.borrow_mut(py)?.build_file_fold_map().iter() { + dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?; } Ok(dict) } @@ -294,12 +295,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 @@ -314,7 +315,7 @@ }, None => Err(PyErr::new::( py, - String::from_utf8_lossy(key), + String::from_utf8_lossy(key.as_bytes()), )), } } @@ -373,15 +374,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)), @@ -397,7 +402,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, @@ -405,8 +414,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), } } @@ -417,9 +433,10 @@ ) -> PyResult { let key = key.extract::(py)?; let value = value.extract::(py)?; - self.borrow_mut(py)? - .copy_map - .insert(key.data(py).to_vec(), value.data(py).to_vec()); + self.borrow_mut(py)?.copy_map.insert( + HgPathBuf::from_bytes(key.data(py)), + HgPathBuf::from_bytes(value.data(py)), + ); Ok(py.None()) } def copymappop( @@ -428,7 +445,11 @@ default: Option ) -> PyResult> { let key = key.extract::(py)?; - match self.borrow_mut(py)?.copy_map.remove(key.data(py)) { + match self + .borrow_mut(py)? + .copy_map + .remove(HgPath::new(key.data(py))) + { Some(_) => Ok(None), None => Ok(default), } @@ -457,13 +478,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; @@ -471,7 +492,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, diff --git a/rust/hg-cpython/src/exceptions.rs b/rust/hg-cpython/src/exceptions.rs --- a/rust/hg-cpython/src/exceptions.rs +++ b/rust/hg-cpython/src/exceptions.rs @@ -41,6 +41,7 @@ py_exception!(rustext, PatternError, RuntimeError); py_exception!(rustext, PatternFileError, RuntimeError); +py_exception!(rustext, HgPathPyError, RuntimeError); impl PatternError { pub fn pynew(py: Python, inner: hg::PatternError) -> PyErr { 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 @@ -13,9 +13,14 @@ //! use crate::exceptions::{PatternError, PatternFileError}; use cpython::{ - PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject, + PyBytes, PyDict, PyModule, PyObject, PyResult, PyString, PyTuple, Python, + ToPyObject, }; -use hg::{build_single_regex, read_pattern_file, LineNumber, PatternTuple}; +use hg::{ + build_single_regex, read_pattern_file, utils::files::get_path_from_bytes, + LineNumber, PatternTuple, +}; +use std::path::PathBuf; /// Rust does not like functions with different return signatures. /// The 3-tuple version is always returned by the hg-core function, @@ -33,7 +38,9 @@ warn: bool, source_info: bool, ) -> PyResult { - match read_pattern_file(file_path.extract::(py)?.data(py), warn) { + let bytes = file_path.extract::(py)?; + let path = get_path_from_bytes(bytes.data(py)); + match read_pattern_file(path, warn) { Ok((patterns, warnings)) => { if source_info { let itemgetter = |x: &PatternTuple| { @@ -58,11 +65,16 @@ fn warnings_to_py_bytes( py: Python, - warnings: &[(Vec, Vec)], -) -> Vec<(PyBytes, PyBytes)> { + warnings: &[(PathBuf, Vec)], +) -> Vec<(PyString, PyBytes)> { warnings .iter() - .map(|(path, syn)| (PyBytes::new(py, path), PyBytes::new(py, syn))) + .map(|(path, syn)| { + ( + PyString::new(py, &path.to_string_lossy()), + 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,