##// END OF EJS Templates
rust: Return owned instead of borrowed DirstateEntry in DirstateMap APIs...
Simon Sapin -
r48123:4ee9f419 default
parent child Browse files
Show More
@@ -1,133 +1,133 b''
1 1 // dirstate module
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::errors::HgError;
9 9 use crate::revlog::Node;
10 10 use crate::utils::hg_path::{HgPath, HgPathBuf};
11 11 use crate::FastHashMap;
12 12 use bytes_cast::{unaligned, BytesCast};
13 13 use std::convert::TryFrom;
14 14
15 15 pub mod dirs_multiset;
16 16 pub mod dirstate_map;
17 17 pub mod parsers;
18 18 pub mod status;
19 19
20 20 #[derive(Debug, PartialEq, Clone, BytesCast)]
21 21 #[repr(C)]
22 22 pub struct DirstateParents {
23 23 pub p1: Node,
24 24 pub p2: Node,
25 25 }
26 26
27 27 /// The C implementation uses all signed types. This will be an issue
28 28 /// either when 4GB+ source files are commonplace or in 2038, whichever
29 29 /// comes first.
30 30 #[derive(Debug, PartialEq, Copy, Clone)]
31 31 pub struct DirstateEntry {
32 32 pub state: EntryState,
33 33 pub mode: i32,
34 34 pub mtime: i32,
35 35 pub size: i32,
36 36 }
37 37
38 38 impl DirstateEntry {
39 39 pub fn is_non_normal(&self) -> bool {
40 40 self.state != EntryState::Normal || self.mtime == MTIME_UNSET
41 41 }
42 42
43 43 pub fn is_from_other_parent(&self) -> bool {
44 44 self.state == EntryState::Normal && self.size == SIZE_FROM_OTHER_PARENT
45 45 }
46 46
47 47 // TODO: other platforms
48 48 #[cfg(unix)]
49 49 pub fn mode_changed(
50 50 &self,
51 51 filesystem_metadata: &std::fs::Metadata,
52 52 ) -> bool {
53 53 use std::os::unix::fs::MetadataExt;
54 54 const EXEC_BIT_MASK: u32 = 0o100;
55 55 let dirstate_exec_bit = (self.mode as u32) & EXEC_BIT_MASK;
56 56 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
57 57 dirstate_exec_bit != fs_exec_bit
58 58 }
59 59 }
60 60
61 61 #[derive(BytesCast)]
62 62 #[repr(C)]
63 63 struct RawEntry {
64 64 state: u8,
65 65 mode: unaligned::I32Be,
66 66 size: unaligned::I32Be,
67 67 mtime: unaligned::I32Be,
68 68 length: unaligned::I32Be,
69 69 }
70 70
71 71 const MTIME_UNSET: i32 = -1;
72 72
73 73 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
74 74 /// other parent. This allows revert to pick the right status back during a
75 75 /// merge.
76 76 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
77 77
78 78 pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
79 79 pub type StateMapIter<'a> =
80 Box<dyn Iterator<Item = (&'a HgPath, &'a DirstateEntry)> + Send + 'a>;
80 Box<dyn Iterator<Item = (&'a HgPath, DirstateEntry)> + Send + 'a>;
81 81
82 82 pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
83 83 pub type CopyMapIter<'a> =
84 84 Box<dyn Iterator<Item = (&'a HgPath, &'a HgPath)> + Send + 'a>;
85 85
86 86 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
87 87 pub enum EntryState {
88 88 Normal,
89 89 Added,
90 90 Removed,
91 91 Merged,
92 92 Unknown,
93 93 }
94 94
95 95 impl EntryState {
96 96 pub fn is_tracked(self) -> bool {
97 97 use EntryState::*;
98 98 match self {
99 99 Normal | Added | Merged => true,
100 100 Removed | Unknown => false,
101 101 }
102 102 }
103 103 }
104 104
105 105 impl TryFrom<u8> for EntryState {
106 106 type Error = HgError;
107 107
108 108 fn try_from(value: u8) -> Result<Self, Self::Error> {
109 109 match value {
110 110 b'n' => Ok(EntryState::Normal),
111 111 b'a' => Ok(EntryState::Added),
112 112 b'r' => Ok(EntryState::Removed),
113 113 b'm' => Ok(EntryState::Merged),
114 114 b'?' => Ok(EntryState::Unknown),
115 115 _ => Err(HgError::CorruptedRepository(format!(
116 116 "Incorrect dirstate entry state {}",
117 117 value
118 118 ))),
119 119 }
120 120 }
121 121 }
122 122
123 123 impl Into<u8> for EntryState {
124 124 fn into(self) -> u8 {
125 125 match self {
126 126 EntryState::Normal => b'n',
127 127 EntryState::Added => b'a',
128 128 EntryState::Removed => b'r',
129 129 EntryState::Merged => b'm',
130 130 EntryState::Unknown => b'?',
131 131 }
132 132 }
133 133 }
@@ -1,427 +1,427 b''
1 1 // dirs_multiset.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! A multiset of directory names.
9 9 //!
10 10 //! Used to counts the references to directories in a manifest or dirstate.
11 11 use crate::{
12 12 dirstate::EntryState,
13 13 utils::{
14 14 files,
15 15 hg_path::{HgPath, HgPathBuf, HgPathError},
16 16 },
17 17 DirstateEntry, DirstateMapError, FastHashMap,
18 18 };
19 19 use std::collections::{hash_map, hash_map::Entry, HashMap, HashSet};
20 20
21 21 // could be encapsulated if we care API stability more seriously
22 22 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>;
23 23
24 24 #[derive(PartialEq, Debug)]
25 25 pub struct DirsMultiset {
26 26 inner: FastHashMap<HgPathBuf, u32>,
27 27 }
28 28
29 29 impl DirsMultiset {
30 30 /// Initializes the multiset from a dirstate.
31 31 ///
32 32 /// If `skip_state` is provided, skips dirstate entries with equal state.
33 pub fn from_dirstate<'a, I, P>(
33 pub fn from_dirstate<I, P>(
34 34 dirstate: I,
35 35 skip_state: Option<EntryState>,
36 36 ) -> Result<Self, DirstateMapError>
37 37 where
38 I: IntoIterator<Item = (P, &'a DirstateEntry)>,
38 I: IntoIterator<Item = (P, DirstateEntry)>,
39 39 P: AsRef<HgPath>,
40 40 {
41 41 let mut multiset = DirsMultiset {
42 42 inner: FastHashMap::default(),
43 43 };
44 44 for (filename, entry) in dirstate {
45 45 let filename = filename.as_ref();
46 46 // This `if` is optimized out of the loop
47 47 if let Some(skip) = skip_state {
48 48 if skip != entry.state {
49 49 multiset.add_path(filename)?;
50 50 }
51 51 } else {
52 52 multiset.add_path(filename)?;
53 53 }
54 54 }
55 55
56 56 Ok(multiset)
57 57 }
58 58
59 59 /// Initializes the multiset from a manifest.
60 60 pub fn from_manifest(
61 61 manifest: &[impl AsRef<HgPath>],
62 62 ) -> Result<Self, DirstateMapError> {
63 63 let mut multiset = DirsMultiset {
64 64 inner: FastHashMap::default(),
65 65 };
66 66
67 67 for filename in manifest {
68 68 multiset.add_path(filename.as_ref())?;
69 69 }
70 70
71 71 Ok(multiset)
72 72 }
73 73
74 74 /// Increases the count of deepest directory contained in the path.
75 75 ///
76 76 /// If the directory is not yet in the map, adds its parents.
77 77 pub fn add_path(
78 78 &mut self,
79 79 path: impl AsRef<HgPath>,
80 80 ) -> Result<(), DirstateMapError> {
81 81 for subpath in files::find_dirs(path.as_ref()) {
82 82 if subpath.as_bytes().last() == Some(&b'/') {
83 83 // TODO Remove this once PathAuditor is certified
84 84 // as the only entrypoint for path data
85 85 let second_slash_index = subpath.len() - 1;
86 86
87 87 return Err(DirstateMapError::InvalidPath(
88 88 HgPathError::ConsecutiveSlashes {
89 89 bytes: path.as_ref().as_bytes().to_owned(),
90 90 second_slash_index,
91 91 },
92 92 ));
93 93 }
94 94 if let Some(val) = self.inner.get_mut(subpath) {
95 95 *val += 1;
96 96 break;
97 97 }
98 98 self.inner.insert(subpath.to_owned(), 1);
99 99 }
100 100 Ok(())
101 101 }
102 102
103 103 /// Decreases the count of deepest directory contained in the path.
104 104 ///
105 105 /// If it is the only reference, decreases all parents until one is
106 106 /// removed.
107 107 /// If the directory is not in the map, something horrible has happened.
108 108 pub fn delete_path(
109 109 &mut self,
110 110 path: impl AsRef<HgPath>,
111 111 ) -> Result<(), DirstateMapError> {
112 112 for subpath in files::find_dirs(path.as_ref()) {
113 113 match self.inner.entry(subpath.to_owned()) {
114 114 Entry::Occupied(mut entry) => {
115 115 let val = *entry.get();
116 116 if val > 1 {
117 117 entry.insert(val - 1);
118 118 break;
119 119 }
120 120 entry.remove();
121 121 }
122 122 Entry::Vacant(_) => {
123 123 return Err(DirstateMapError::PathNotFound(
124 124 path.as_ref().to_owned(),
125 125 ))
126 126 }
127 127 };
128 128 }
129 129
130 130 Ok(())
131 131 }
132 132
133 133 pub fn contains(&self, key: impl AsRef<HgPath>) -> bool {
134 134 self.inner.contains_key(key.as_ref())
135 135 }
136 136
137 137 pub fn iter(&self) -> DirsMultisetIter {
138 138 self.inner.keys()
139 139 }
140 140
141 141 pub fn len(&self) -> usize {
142 142 self.inner.len()
143 143 }
144 144
145 145 pub fn is_empty(&self) -> bool {
146 146 self.len() == 0
147 147 }
148 148 }
149 149
150 150 /// This is basically a reimplementation of `DirsMultiset` that stores the
151 151 /// children instead of just a count of them, plus a small optional
152 152 /// optimization to avoid some directories we don't need.
153 153 #[derive(PartialEq, Debug)]
154 154 pub struct DirsChildrenMultiset<'a> {
155 155 inner: FastHashMap<&'a HgPath, HashSet<&'a HgPath>>,
156 156 only_include: Option<HashSet<&'a HgPath>>,
157 157 }
158 158
159 159 impl<'a> DirsChildrenMultiset<'a> {
160 160 pub fn new(
161 161 paths: impl Iterator<Item = &'a HgPathBuf>,
162 162 only_include: Option<&'a HashSet<impl AsRef<HgPath> + 'a>>,
163 163 ) -> Self {
164 164 let mut new = Self {
165 165 inner: HashMap::default(),
166 166 only_include: only_include
167 167 .map(|s| s.iter().map(AsRef::as_ref).collect()),
168 168 };
169 169
170 170 for path in paths {
171 171 new.add_path(path)
172 172 }
173 173
174 174 new
175 175 }
176 176 fn add_path(&mut self, path: &'a (impl AsRef<HgPath> + 'a)) {
177 177 if path.as_ref().is_empty() {
178 178 return;
179 179 }
180 180 for (directory, basename) in files::find_dirs_with_base(path.as_ref())
181 181 {
182 182 if !self.is_dir_included(directory) {
183 183 continue;
184 184 }
185 185 self.inner
186 186 .entry(directory)
187 187 .and_modify(|e| {
188 188 e.insert(basename);
189 189 })
190 190 .or_insert_with(|| {
191 191 let mut set = HashSet::new();
192 192 set.insert(basename);
193 193 set
194 194 });
195 195 }
196 196 }
197 197 fn is_dir_included(&self, dir: impl AsRef<HgPath>) -> bool {
198 198 match &self.only_include {
199 199 None => false,
200 200 Some(i) => i.contains(dir.as_ref()),
201 201 }
202 202 }
203 203
204 204 pub fn get(
205 205 &self,
206 206 path: impl AsRef<HgPath>,
207 207 ) -> Option<&HashSet<&'a HgPath>> {
208 208 self.inner.get(path.as_ref())
209 209 }
210 210 }
211 211
212 212 #[cfg(test)]
213 213 mod tests {
214 214 use super::*;
215 215 use crate::StateMap;
216 216
217 217 #[test]
218 218 fn test_delete_path_path_not_found() {
219 219 let manifest: Vec<HgPathBuf> = vec![];
220 220 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
221 221 let path = HgPathBuf::from_bytes(b"doesnotexist/");
222 222 assert_eq!(
223 223 Err(DirstateMapError::PathNotFound(path.to_owned())),
224 224 map.delete_path(&path)
225 225 );
226 226 }
227 227
228 228 #[test]
229 229 fn test_delete_path_empty_path() {
230 230 let mut map =
231 231 DirsMultiset::from_manifest(&vec![HgPathBuf::new()]).unwrap();
232 232 let path = HgPath::new(b"");
233 233 assert_eq!(Ok(()), map.delete_path(path));
234 234 assert_eq!(
235 235 Err(DirstateMapError::PathNotFound(path.to_owned())),
236 236 map.delete_path(path)
237 237 );
238 238 }
239 239
240 240 #[test]
241 241 fn test_delete_path_successful() {
242 242 let mut map = DirsMultiset {
243 243 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
244 244 .iter()
245 245 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
246 246 .collect(),
247 247 };
248 248
249 249 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
250 250 eprintln!("{:?}", map);
251 251 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
252 252 eprintln!("{:?}", map);
253 253 assert_eq!(
254 254 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
255 255 b"a/b/"
256 256 ))),
257 257 map.delete_path(HgPath::new(b"a/b/"))
258 258 );
259 259
260 260 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
261 261 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
262 262 eprintln!("{:?}", map);
263 263 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
264 264 eprintln!("{:?}", map);
265 265
266 266 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
267 267 assert_eq!(
268 268 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
269 269 b"a/c/"
270 270 ))),
271 271 map.delete_path(HgPath::new(b"a/c/"))
272 272 );
273 273 }
274 274
275 275 #[test]
276 276 fn test_add_path_empty_path() {
277 277 let manifest: Vec<HgPathBuf> = vec![];
278 278 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
279 279 let path = HgPath::new(b"");
280 280 map.add_path(path).unwrap();
281 281
282 282 assert_eq!(1, map.len());
283 283 }
284 284
285 285 #[test]
286 286 fn test_add_path_successful() {
287 287 let manifest: Vec<HgPathBuf> = vec![];
288 288 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
289 289
290 290 map.add_path(HgPath::new(b"a/")).unwrap();
291 291 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
292 292 assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
293 293 assert_eq!(2, map.len());
294 294
295 295 // Non directory should be ignored
296 296 map.add_path(HgPath::new(b"a")).unwrap();
297 297 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
298 298 assert_eq!(2, map.len());
299 299
300 300 // Non directory will still add its base
301 301 map.add_path(HgPath::new(b"a/b")).unwrap();
302 302 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
303 303 assert_eq!(2, map.len());
304 304
305 305 // Duplicate path works
306 306 map.add_path(HgPath::new(b"a/")).unwrap();
307 307 assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
308 308
309 309 // Nested dir adds to its base
310 310 map.add_path(HgPath::new(b"a/b/")).unwrap();
311 311 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
312 312 assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
313 313
314 314 // but not its base's base, because it already existed
315 315 map.add_path(HgPath::new(b"a/b/c/")).unwrap();
316 316 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
317 317 assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
318 318
319 319 map.add_path(HgPath::new(b"a/c/")).unwrap();
320 320 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
321 321
322 322 let expected = DirsMultiset {
323 323 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
324 324 .iter()
325 325 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
326 326 .collect(),
327 327 };
328 328 assert_eq!(map, expected);
329 329 }
330 330
331 331 #[test]
332 332 fn test_dirsmultiset_new_empty() {
333 333 let manifest: Vec<HgPathBuf> = vec![];
334 334 let new = DirsMultiset::from_manifest(&manifest).unwrap();
335 335 let expected = DirsMultiset {
336 336 inner: FastHashMap::default(),
337 337 };
338 338 assert_eq!(expected, new);
339 339
340 340 let new =
341 DirsMultiset::from_dirstate(&StateMap::default(), None).unwrap();
341 DirsMultiset::from_dirstate(StateMap::default(), None).unwrap();
342 342 let expected = DirsMultiset {
343 343 inner: FastHashMap::default(),
344 344 };
345 345 assert_eq!(expected, new);
346 346 }
347 347
348 348 #[test]
349 349 fn test_dirsmultiset_new_no_skip() {
350 350 let input_vec: Vec<HgPathBuf> = ["a/", "b/", "a/c", "a/d/"]
351 351 .iter()
352 352 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
353 353 .collect();
354 354 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
355 355 .iter()
356 356 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
357 357 .collect();
358 358
359 359 let new = DirsMultiset::from_manifest(&input_vec).unwrap();
360 360 let expected = DirsMultiset {
361 361 inner: expected_inner,
362 362 };
363 363 assert_eq!(expected, new);
364 364
365 365 let input_map: HashMap<_, _> = ["b/x", "a/c", "a/d/x"]
366 366 .iter()
367 367 .map(|f| {
368 368 (
369 369 HgPathBuf::from_bytes(f.as_bytes()),
370 370 DirstateEntry {
371 371 state: EntryState::Normal,
372 372 mode: 0,
373 373 mtime: 0,
374 374 size: 0,
375 375 },
376 376 )
377 377 })
378 378 .collect();
379 379 let expected_inner = [("", 2), ("a", 2), ("b", 1), ("a/d", 1)]
380 380 .iter()
381 381 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
382 382 .collect();
383 383
384 let new = DirsMultiset::from_dirstate(&input_map, None).unwrap();
384 let new = DirsMultiset::from_dirstate(input_map, None).unwrap();
385 385 let expected = DirsMultiset {
386 386 inner: expected_inner,
387 387 };
388 388 assert_eq!(expected, new);
389 389 }
390 390
391 391 #[test]
392 392 fn test_dirsmultiset_new_skip() {
393 393 let input_map: HashMap<_, _> = [
394 394 ("a/", EntryState::Normal),
395 395 ("a/b", EntryState::Normal),
396 396 ("a/c", EntryState::Removed),
397 397 ("a/d", EntryState::Merged),
398 398 ]
399 399 .iter()
400 400 .map(|(f, state)| {
401 401 (
402 402 HgPathBuf::from_bytes(f.as_bytes()),
403 403 DirstateEntry {
404 404 state: *state,
405 405 mode: 0,
406 406 mtime: 0,
407 407 size: 0,
408 408 },
409 409 )
410 410 })
411 411 .collect();
412 412
413 413 // "a" incremented with "a/c" and "a/d/"
414 414 let expected_inner = [("", 1), ("a", 2)]
415 415 .iter()
416 416 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
417 417 .collect();
418 418
419 419 let new =
420 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal))
420 DirsMultiset::from_dirstate(input_map, Some(EntryState::Normal))
421 421 .unwrap();
422 422 let expected = DirsMultiset {
423 423 inner: expected_inner,
424 424 };
425 425 assert_eq!(expected, new);
426 426 }
427 427 }
@@ -1,407 +1,407 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::dirstate::parsers::Timestamp;
9 9 use crate::{
10 10 dirstate::EntryState,
11 11 pack_dirstate, parse_dirstate,
12 12 utils::hg_path::{HgPath, HgPathBuf},
13 13 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
14 14 DirstateParents, StateMap,
15 15 };
16 16 use micro_timer::timed;
17 17 use std::collections::HashSet;
18 18 use std::iter::FromIterator;
19 19 use std::ops::Deref;
20 20
21 21 #[derive(Default)]
22 22 pub struct DirstateMap {
23 23 state_map: StateMap,
24 24 pub copy_map: CopyMap,
25 25 pub dirs: Option<DirsMultiset>,
26 26 pub all_dirs: Option<DirsMultiset>,
27 27 non_normal_set: Option<HashSet<HgPathBuf>>,
28 28 other_parent_set: Option<HashSet<HgPathBuf>>,
29 29 }
30 30
31 31 /// Should only really be used in python interface code, for clarity
32 32 impl Deref for DirstateMap {
33 33 type Target = StateMap;
34 34
35 35 fn deref(&self) -> &Self::Target {
36 36 &self.state_map
37 37 }
38 38 }
39 39
40 40 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
41 41 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
42 42 iter: I,
43 43 ) -> Self {
44 44 Self {
45 45 state_map: iter.into_iter().collect(),
46 46 ..Self::default()
47 47 }
48 48 }
49 49 }
50 50
51 51 impl DirstateMap {
52 52 pub fn new() -> Self {
53 53 Self::default()
54 54 }
55 55
56 56 pub fn clear(&mut self) {
57 57 self.state_map = StateMap::default();
58 58 self.copy_map.clear();
59 59 self.non_normal_set = None;
60 60 self.other_parent_set = None;
61 61 }
62 62
63 63 /// Add a tracked file to the dirstate
64 64 pub fn add_file(
65 65 &mut self,
66 66 filename: &HgPath,
67 67 old_state: EntryState,
68 68 entry: DirstateEntry,
69 69 ) -> Result<(), DirstateMapError> {
70 70 if old_state == EntryState::Unknown || old_state == EntryState::Removed
71 71 {
72 72 if let Some(ref mut dirs) = self.dirs {
73 73 dirs.add_path(filename)?;
74 74 }
75 75 }
76 76 if old_state == EntryState::Unknown {
77 77 if let Some(ref mut all_dirs) = self.all_dirs {
78 78 all_dirs.add_path(filename)?;
79 79 }
80 80 }
81 81 self.state_map.insert(filename.to_owned(), entry.to_owned());
82 82
83 83 if entry.is_non_normal() {
84 84 self.get_non_normal_other_parent_entries()
85 85 .0
86 86 .insert(filename.to_owned());
87 87 }
88 88
89 89 if entry.is_from_other_parent() {
90 90 self.get_non_normal_other_parent_entries()
91 91 .1
92 92 .insert(filename.to_owned());
93 93 }
94 94 Ok(())
95 95 }
96 96
97 97 /// Mark a file as removed in the dirstate.
98 98 ///
99 99 /// The `size` parameter is used to store sentinel values that indicate
100 100 /// the file's previous state. In the future, we should refactor this
101 101 /// to be more explicit about what that state is.
102 102 pub fn remove_file(
103 103 &mut self,
104 104 filename: &HgPath,
105 105 old_state: EntryState,
106 106 size: i32,
107 107 ) -> Result<(), DirstateMapError> {
108 108 if old_state != EntryState::Unknown && old_state != EntryState::Removed
109 109 {
110 110 if let Some(ref mut dirs) = self.dirs {
111 111 dirs.delete_path(filename)?;
112 112 }
113 113 }
114 114 if old_state == EntryState::Unknown {
115 115 if let Some(ref mut all_dirs) = self.all_dirs {
116 116 all_dirs.add_path(filename)?;
117 117 }
118 118 }
119 119
120 120 self.state_map.insert(
121 121 filename.to_owned(),
122 122 DirstateEntry {
123 123 state: EntryState::Removed,
124 124 mode: 0,
125 125 size,
126 126 mtime: 0,
127 127 },
128 128 );
129 129 self.get_non_normal_other_parent_entries()
130 130 .0
131 131 .insert(filename.to_owned());
132 132 Ok(())
133 133 }
134 134
135 135 /// Remove a file from the dirstate.
136 136 /// Returns `true` if the file was previously recorded.
137 137 pub fn drop_file(
138 138 &mut self,
139 139 filename: &HgPath,
140 140 old_state: EntryState,
141 141 ) -> Result<bool, DirstateMapError> {
142 142 let exists = self.state_map.remove(filename).is_some();
143 143
144 144 if exists {
145 145 if old_state != EntryState::Removed {
146 146 if let Some(ref mut dirs) = self.dirs {
147 147 dirs.delete_path(filename)?;
148 148 }
149 149 }
150 150 if let Some(ref mut all_dirs) = self.all_dirs {
151 151 all_dirs.delete_path(filename)?;
152 152 }
153 153 }
154 154 self.get_non_normal_other_parent_entries()
155 155 .0
156 156 .remove(filename);
157 157
158 158 Ok(exists)
159 159 }
160 160
161 161 pub fn clear_ambiguous_times(
162 162 &mut self,
163 163 filenames: Vec<HgPathBuf>,
164 164 now: i32,
165 165 ) {
166 166 for filename in filenames {
167 167 if let Some(entry) = self.state_map.get_mut(&filename) {
168 168 if entry.clear_ambiguous_mtime(now) {
169 169 self.get_non_normal_other_parent_entries()
170 170 .0
171 171 .insert(filename.to_owned());
172 172 }
173 173 }
174 174 }
175 175 }
176 176
177 177 pub fn non_normal_entries_remove(&mut self, key: impl AsRef<HgPath>) {
178 178 self.get_non_normal_other_parent_entries()
179 179 .0
180 180 .remove(key.as_ref());
181 181 }
182 182
183 183 pub fn non_normal_entries_union(
184 184 &mut self,
185 185 other: HashSet<HgPathBuf>,
186 186 ) -> Vec<HgPathBuf> {
187 187 self.get_non_normal_other_parent_entries()
188 188 .0
189 189 .union(&other)
190 190 .map(ToOwned::to_owned)
191 191 .collect()
192 192 }
193 193
194 194 pub fn get_non_normal_other_parent_entries(
195 195 &mut self,
196 196 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
197 197 self.set_non_normal_other_parent_entries(false);
198 198 (
199 199 self.non_normal_set.as_mut().unwrap(),
200 200 self.other_parent_set.as_mut().unwrap(),
201 201 )
202 202 }
203 203
204 204 /// Useful to get immutable references to those sets in contexts where
205 205 /// you only have an immutable reference to the `DirstateMap`, like when
206 206 /// sharing references with Python.
207 207 ///
208 208 /// TODO, get rid of this along with the other "setter/getter" stuff when
209 209 /// a nice typestate plan is defined.
210 210 ///
211 211 /// # Panics
212 212 ///
213 213 /// Will panic if either set is `None`.
214 214 pub fn get_non_normal_other_parent_entries_panic(
215 215 &self,
216 216 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
217 217 (
218 218 self.non_normal_set.as_ref().unwrap(),
219 219 self.other_parent_set.as_ref().unwrap(),
220 220 )
221 221 }
222 222
223 223 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
224 224 if !force
225 225 && self.non_normal_set.is_some()
226 226 && self.other_parent_set.is_some()
227 227 {
228 228 return;
229 229 }
230 230 let mut non_normal = HashSet::new();
231 231 let mut other_parent = HashSet::new();
232 232
233 233 for (filename, entry) in self.state_map.iter() {
234 234 if entry.is_non_normal() {
235 235 non_normal.insert(filename.to_owned());
236 236 }
237 237 if entry.is_from_other_parent() {
238 238 other_parent.insert(filename.to_owned());
239 239 }
240 240 }
241 241 self.non_normal_set = Some(non_normal);
242 242 self.other_parent_set = Some(other_parent);
243 243 }
244 244
245 245 /// Both of these setters and their uses appear to be the simplest way to
246 246 /// emulate a Python lazy property, but it is ugly and unidiomatic.
247 247 /// TODO One day, rewriting this struct using the typestate might be a
248 248 /// good idea.
249 249 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
250 250 if self.all_dirs.is_none() {
251 251 self.all_dirs = Some(DirsMultiset::from_dirstate(
252 self.state_map.iter(),
252 self.state_map.iter().map(|(k, v)| (k, *v)),
253 253 None,
254 254 )?);
255 255 }
256 256 Ok(())
257 257 }
258 258
259 259 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
260 260 if self.dirs.is_none() {
261 261 self.dirs = Some(DirsMultiset::from_dirstate(
262 self.state_map.iter(),
262 self.state_map.iter().map(|(k, v)| (k, *v)),
263 263 Some(EntryState::Removed),
264 264 )?);
265 265 }
266 266 Ok(())
267 267 }
268 268
269 269 pub fn has_tracked_dir(
270 270 &mut self,
271 271 directory: &HgPath,
272 272 ) -> Result<bool, DirstateMapError> {
273 273 self.set_dirs()?;
274 274 Ok(self.dirs.as_ref().unwrap().contains(directory))
275 275 }
276 276
277 277 pub fn has_dir(
278 278 &mut self,
279 279 directory: &HgPath,
280 280 ) -> Result<bool, DirstateMapError> {
281 281 self.set_all_dirs()?;
282 282 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
283 283 }
284 284
285 285 #[timed]
286 286 pub fn read(
287 287 &mut self,
288 288 file_contents: &[u8],
289 289 ) -> Result<Option<DirstateParents>, DirstateError> {
290 290 if file_contents.is_empty() {
291 291 return Ok(None);
292 292 }
293 293
294 294 let (parents, entries, copies) = parse_dirstate(file_contents)?;
295 295 self.state_map.extend(
296 296 entries
297 297 .into_iter()
298 298 .map(|(path, entry)| (path.to_owned(), entry)),
299 299 );
300 300 self.copy_map.extend(
301 301 copies
302 302 .into_iter()
303 303 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
304 304 );
305 305 Ok(Some(parents.clone()))
306 306 }
307 307
308 308 pub fn pack(
309 309 &mut self,
310 310 parents: DirstateParents,
311 311 now: Timestamp,
312 312 ) -> Result<Vec<u8>, DirstateError> {
313 313 let packed =
314 314 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
315 315
316 316 self.set_non_normal_other_parent_entries(true);
317 317 Ok(packed)
318 318 }
319 319 }
320 320
321 321 #[cfg(test)]
322 322 mod tests {
323 323 use super::*;
324 324
325 325 #[test]
326 326 fn test_dirs_multiset() {
327 327 let mut map = DirstateMap::new();
328 328 assert!(map.dirs.is_none());
329 329 assert!(map.all_dirs.is_none());
330 330
331 331 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
332 332 assert!(map.all_dirs.is_some());
333 333 assert!(map.dirs.is_none());
334 334
335 335 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
336 336 assert!(map.dirs.is_some());
337 337 }
338 338
339 339 #[test]
340 340 fn test_add_file() {
341 341 let mut map = DirstateMap::new();
342 342
343 343 assert_eq!(0, map.len());
344 344
345 345 map.add_file(
346 346 HgPath::new(b"meh"),
347 347 EntryState::Normal,
348 348 DirstateEntry {
349 349 state: EntryState::Normal,
350 350 mode: 1337,
351 351 mtime: 1337,
352 352 size: 1337,
353 353 },
354 354 )
355 355 .unwrap();
356 356
357 357 assert_eq!(1, map.len());
358 358 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
359 359 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
360 360 }
361 361
362 362 #[test]
363 363 fn test_non_normal_other_parent_entries() {
364 364 let mut map: DirstateMap = [
365 365 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
366 366 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
367 367 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
368 368 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
369 369 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
370 370 (b"f6", (EntryState::Added, 1337, 1337, -1)),
371 371 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
372 372 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
373 373 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
374 374 (b"fa", (EntryState::Added, 1337, -2, 1337)),
375 375 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
376 376 ]
377 377 .iter()
378 378 .map(|(fname, (state, mode, size, mtime))| {
379 379 (
380 380 HgPathBuf::from_bytes(fname.as_ref()),
381 381 DirstateEntry {
382 382 state: *state,
383 383 mode: *mode,
384 384 size: *size,
385 385 mtime: *mtime,
386 386 },
387 387 )
388 388 })
389 389 .collect();
390 390
391 391 let mut non_normal = [
392 392 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
393 393 ]
394 394 .iter()
395 395 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
396 396 .collect();
397 397
398 398 let mut other_parent = HashSet::new();
399 399 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
400 400 let entries = map.get_non_normal_other_parent_entries();
401 401
402 402 assert_eq!(
403 403 (&mut non_normal, &mut other_parent),
404 404 (entries.0, entries.1)
405 405 );
406 406 }
407 407 }
@@ -1,639 +1,639 b''
1 1 use bytes_cast::BytesCast;
2 2 use micro_timer::timed;
3 3 use std::borrow::Cow;
4 4 use std::convert::TryInto;
5 5 use std::path::PathBuf;
6 6
7 7 use super::on_disk;
8 8 use super::path_with_basename::WithBasename;
9 9 use crate::dirstate::parsers::pack_entry;
10 10 use crate::dirstate::parsers::packed_entry_size;
11 11 use crate::dirstate::parsers::parse_dirstate_entries;
12 12 use crate::dirstate::parsers::Timestamp;
13 13 use crate::matchers::Matcher;
14 14 use crate::utils::hg_path::{HgPath, HgPathBuf};
15 15 use crate::CopyMapIter;
16 16 use crate::DirstateEntry;
17 17 use crate::DirstateError;
18 18 use crate::DirstateMapError;
19 19 use crate::DirstateParents;
20 20 use crate::DirstateStatus;
21 21 use crate::EntryState;
22 22 use crate::FastHashMap;
23 23 use crate::PatternFileWarning;
24 24 use crate::StateMapIter;
25 25 use crate::StatusError;
26 26 use crate::StatusOptions;
27 27
28 28 pub struct DirstateMap<'on_disk> {
29 29 /// Contents of the `.hg/dirstate` file
30 30 pub(super) on_disk: &'on_disk [u8],
31 31
32 32 pub(super) root: ChildNodes<'on_disk>,
33 33
34 34 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
35 35 pub(super) nodes_with_entry_count: u32,
36 36
37 37 /// Number of nodes anywhere in the tree that have
38 38 /// `.copy_source.is_some()`.
39 39 pub(super) nodes_with_copy_source_count: u32,
40 40 }
41 41
42 42 /// Using a plain `HgPathBuf` of the full path from the repository root as a
43 43 /// map key would also work: all paths in a given map have the same parent
44 44 /// path, so comparing full paths gives the same result as comparing base
45 45 /// names. However `HashMap` would waste time always re-hashing the same
46 46 /// string prefix.
47 47 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
48 48 pub(super) type ChildNodes<'on_disk> =
49 49 FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>;
50 50
51 51 /// Represents a file or a directory
52 52 #[derive(Default)]
53 53 pub(super) struct Node<'on_disk> {
54 54 /// `None` for directories
55 55 pub(super) entry: Option<DirstateEntry>,
56 56
57 57 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
58 58
59 59 pub(super) children: ChildNodes<'on_disk>,
60 60
61 61 /// How many (non-inclusive) descendants of this node are tracked files
62 62 pub(super) tracked_descendants_count: u32,
63 63 }
64 64
65 65 impl<'on_disk> Node<'on_disk> {
66 66 pub(super) fn state(&self) -> Option<EntryState> {
67 67 self.entry.as_ref().map(|entry| entry.state)
68 68 }
69 69
70 70 pub(super) fn sorted<'tree>(
71 71 nodes: &'tree ChildNodes<'on_disk>,
72 72 ) -> Vec<(&'tree NodeKey<'on_disk>, &'tree Self)> {
73 73 let mut vec: Vec<_> = nodes.iter().collect();
74 74 // `sort_unstable_by_key` doesn’t allow keys borrowing from the value:
75 75 // https://github.com/rust-lang/rust/issues/34162
76 76 vec.sort_unstable_by(|(path1, _), (path2, _)| path1.cmp(path2));
77 77 vec
78 78 }
79 79 }
80 80
81 81 impl<'on_disk> DirstateMap<'on_disk> {
82 82 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
83 83 Self {
84 84 on_disk,
85 85 root: ChildNodes::default(),
86 86 nodes_with_entry_count: 0,
87 87 nodes_with_copy_source_count: 0,
88 88 }
89 89 }
90 90
91 91 #[timed]
92 92 pub fn new_v2(
93 93 on_disk: &'on_disk [u8],
94 94 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
95 95 on_disk::read(on_disk)
96 96 }
97 97
98 98 #[timed]
99 99 pub fn new_v1(
100 100 on_disk: &'on_disk [u8],
101 101 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
102 102 let mut map = Self::empty(on_disk);
103 103 if map.on_disk.is_empty() {
104 104 return Ok((map, None));
105 105 }
106 106
107 107 let parents = parse_dirstate_entries(
108 108 map.on_disk,
109 109 |path, entry, copy_source| {
110 110 let tracked = entry.state.is_tracked();
111 111 let node = Self::get_or_insert_node(
112 112 &mut map.root,
113 113 path,
114 114 WithBasename::to_cow_borrowed,
115 115 |ancestor| {
116 116 if tracked {
117 117 ancestor.tracked_descendants_count += 1
118 118 }
119 119 },
120 120 );
121 121 assert!(
122 122 node.entry.is_none(),
123 123 "duplicate dirstate entry in read"
124 124 );
125 125 assert!(
126 126 node.copy_source.is_none(),
127 127 "duplicate dirstate entry in read"
128 128 );
129 129 node.entry = Some(*entry);
130 130 node.copy_source = copy_source.map(Cow::Borrowed);
131 131 map.nodes_with_entry_count += 1;
132 132 if copy_source.is_some() {
133 133 map.nodes_with_copy_source_count += 1
134 134 }
135 135 },
136 136 )?;
137 137 let parents = Some(parents.clone());
138 138
139 139 Ok((map, parents))
140 140 }
141 141
142 142 fn get_node(&self, path: &HgPath) -> Option<&Node> {
143 143 let mut children = &self.root;
144 144 let mut components = path.components();
145 145 let mut component =
146 146 components.next().expect("expected at least one components");
147 147 loop {
148 148 let child = children.get(component)?;
149 149 if let Some(next_component) = components.next() {
150 150 component = next_component;
151 151 children = &child.children;
152 152 } else {
153 153 return Some(child);
154 154 }
155 155 }
156 156 }
157 157
158 158 /// Returns a mutable reference to the node at `path` if it exists
159 159 ///
160 160 /// This takes `root` instead of `&mut self` so that callers can mutate
161 161 /// other fields while the returned borrow is still valid
162 162 fn get_node_mut<'tree>(
163 163 root: &'tree mut ChildNodes<'on_disk>,
164 164 path: &HgPath,
165 165 ) -> Option<&'tree mut Node<'on_disk>> {
166 166 let mut children = root;
167 167 let mut components = path.components();
168 168 let mut component =
169 169 components.next().expect("expected at least one components");
170 170 loop {
171 171 let child = children.get_mut(component)?;
172 172 if let Some(next_component) = components.next() {
173 173 component = next_component;
174 174 children = &mut child.children;
175 175 } else {
176 176 return Some(child);
177 177 }
178 178 }
179 179 }
180 180
181 181 fn get_or_insert_node<'tree, 'path>(
182 182 root: &'tree mut ChildNodes<'on_disk>,
183 183 path: &'path HgPath,
184 184 to_cow: impl Fn(
185 185 WithBasename<&'path HgPath>,
186 186 ) -> WithBasename<Cow<'on_disk, HgPath>>,
187 187 mut each_ancestor: impl FnMut(&mut Node),
188 188 ) -> &'tree mut Node<'on_disk> {
189 189 let mut child_nodes = root;
190 190 let mut inclusive_ancestor_paths =
191 191 WithBasename::inclusive_ancestors_of(path);
192 192 let mut ancestor_path = inclusive_ancestor_paths
193 193 .next()
194 194 .expect("expected at least one inclusive ancestor");
195 195 loop {
196 196 // TODO: can we avoid allocating an owned key in cases where the
197 197 // map already contains that key, without introducing double
198 198 // lookup?
199 199 let child_node =
200 200 child_nodes.entry(to_cow(ancestor_path)).or_default();
201 201 if let Some(next) = inclusive_ancestor_paths.next() {
202 202 each_ancestor(child_node);
203 203 ancestor_path = next;
204 204 child_nodes = &mut child_node.children;
205 205 } else {
206 206 return child_node;
207 207 }
208 208 }
209 209 }
210 210
211 211 fn add_or_remove_file(
212 212 &mut self,
213 213 path: &HgPath,
214 214 old_state: EntryState,
215 215 new_entry: DirstateEntry,
216 216 ) {
217 217 let tracked_count_increment =
218 218 match (old_state.is_tracked(), new_entry.state.is_tracked()) {
219 219 (false, true) => 1,
220 220 (true, false) => -1,
221 221 _ => 0,
222 222 };
223 223
224 224 let node = Self::get_or_insert_node(
225 225 &mut self.root,
226 226 path,
227 227 WithBasename::to_cow_owned,
228 228 |ancestor| {
229 229 // We can’t use `+= increment` because the counter is unsigned,
230 230 // and we want debug builds to detect accidental underflow
231 231 // through zero
232 232 match tracked_count_increment {
233 233 1 => ancestor.tracked_descendants_count += 1,
234 234 -1 => ancestor.tracked_descendants_count -= 1,
235 235 _ => {}
236 236 }
237 237 },
238 238 );
239 239 if node.entry.is_none() {
240 240 self.nodes_with_entry_count += 1
241 241 }
242 242 node.entry = Some(new_entry)
243 243 }
244 244
245 245 fn iter_nodes<'a>(
246 246 &'a self,
247 247 ) -> impl Iterator<Item = (&'a Cow<'on_disk, HgPath>, &'a Node)> + 'a {
248 248 // Depth first tree traversal.
249 249 //
250 250 // If we could afford internal iteration and recursion,
251 251 // this would look like:
252 252 //
253 253 // ```
254 254 // fn traverse_children(
255 255 // children: &ChildNodes,
256 256 // each: &mut impl FnMut(&Node),
257 257 // ) {
258 258 // for child in children.values() {
259 259 // traverse_children(&child.children, each);
260 260 // each(child);
261 261 // }
262 262 // }
263 263 // ```
264 264 //
265 265 // However we want an external iterator and therefore can’t use the
266 266 // call stack. Use an explicit stack instead:
267 267 let mut stack = Vec::new();
268 268 let mut iter = self.root.iter();
269 269 std::iter::from_fn(move || {
270 270 while let Some((key, child_node)) = iter.next() {
271 271 // Pseudo-recursion
272 272 let new_iter = child_node.children.iter();
273 273 let old_iter = std::mem::replace(&mut iter, new_iter);
274 274 let key = key.full_path();
275 275 stack.push((key, child_node, old_iter));
276 276 }
277 277 // Found the end of a `children.iter()` iterator.
278 278 if let Some((key, child_node, next_iter)) = stack.pop() {
279 279 // "Return" from pseudo-recursion by restoring state from the
280 280 // explicit stack
281 281 iter = next_iter;
282 282
283 283 Some((key, child_node))
284 284 } else {
285 285 // Reached the bottom of the stack, we’re done
286 286 None
287 287 }
288 288 })
289 289 }
290 290
291 291 fn clear_known_ambiguous_mtimes(&mut self, paths: &[impl AsRef<HgPath>]) {
292 292 for path in paths {
293 293 if let Some(node) =
294 294 Self::get_node_mut(&mut self.root, path.as_ref())
295 295 {
296 296 if let Some(entry) = node.entry.as_mut() {
297 297 entry.clear_mtime();
298 298 }
299 299 }
300 300 }
301 301 }
302 302 }
303 303
304 304 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> {
305 305 fn clear(&mut self) {
306 306 self.root.clear();
307 307 self.nodes_with_entry_count = 0;
308 308 self.nodes_with_copy_source_count = 0;
309 309 }
310 310
311 311 fn add_file(
312 312 &mut self,
313 313 filename: &HgPath,
314 314 old_state: EntryState,
315 315 entry: DirstateEntry,
316 316 ) -> Result<(), DirstateMapError> {
317 317 self.add_or_remove_file(filename, old_state, entry);
318 318 Ok(())
319 319 }
320 320
321 321 fn remove_file(
322 322 &mut self,
323 323 filename: &HgPath,
324 324 old_state: EntryState,
325 325 size: i32,
326 326 ) -> Result<(), DirstateMapError> {
327 327 let entry = DirstateEntry {
328 328 state: EntryState::Removed,
329 329 mode: 0,
330 330 size,
331 331 mtime: 0,
332 332 };
333 333 self.add_or_remove_file(filename, old_state, entry);
334 334 Ok(())
335 335 }
336 336
337 337 fn drop_file(
338 338 &mut self,
339 339 filename: &HgPath,
340 340 old_state: EntryState,
341 341 ) -> Result<bool, DirstateMapError> {
342 342 struct Dropped {
343 343 was_tracked: bool,
344 344 had_entry: bool,
345 345 had_copy_source: bool,
346 346 }
347 347 fn recur(nodes: &mut ChildNodes, path: &HgPath) -> Option<Dropped> {
348 348 let (first_path_component, rest_of_path) =
349 349 path.split_first_component();
350 350 let node = nodes.get_mut(first_path_component)?;
351 351 let dropped;
352 352 if let Some(rest) = rest_of_path {
353 353 dropped = recur(&mut node.children, rest)?;
354 354 if dropped.was_tracked {
355 355 node.tracked_descendants_count -= 1;
356 356 }
357 357 } else {
358 358 dropped = Dropped {
359 359 was_tracked: node
360 360 .entry
361 361 .as_ref()
362 362 .map_or(false, |entry| entry.state.is_tracked()),
363 363 had_entry: node.entry.take().is_some(),
364 364 had_copy_source: node.copy_source.take().is_some(),
365 365 };
366 366 }
367 367 // After recursion, for both leaf (rest_of_path is None) nodes and
368 368 // parent nodes, remove a node if it just became empty.
369 369 if node.entry.is_none()
370 370 && node.copy_source.is_none()
371 371 && node.children.is_empty()
372 372 {
373 373 nodes.remove(first_path_component);
374 374 }
375 375 Some(dropped)
376 376 }
377 377
378 378 if let Some(dropped) = recur(&mut self.root, filename) {
379 379 if dropped.had_entry {
380 380 self.nodes_with_entry_count -= 1
381 381 }
382 382 if dropped.had_copy_source {
383 383 self.nodes_with_copy_source_count -= 1
384 384 }
385 385 Ok(dropped.had_entry)
386 386 } else {
387 387 debug_assert!(!old_state.is_tracked());
388 388 Ok(false)
389 389 }
390 390 }
391 391
392 392 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
393 393 for filename in filenames {
394 394 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
395 395 if let Some(entry) = node.entry.as_mut() {
396 396 entry.clear_ambiguous_mtime(now);
397 397 }
398 398 }
399 399 }
400 400 }
401 401
402 402 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
403 403 self.get_node(key)
404 404 .and_then(|node| node.entry.as_ref())
405 405 .map_or(false, DirstateEntry::is_non_normal)
406 406 }
407 407
408 408 fn non_normal_entries_remove(&mut self, _key: &HgPath) {
409 409 // Do nothing, this `DirstateMap` does not have a separate "non normal
410 410 // entries" set that need to be kept up to date
411 411 }
412 412
413 413 fn non_normal_or_other_parent_paths(
414 414 &mut self,
415 415 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
416 416 Box::new(self.iter_nodes().filter_map(|(path, node)| {
417 417 node.entry
418 418 .as_ref()
419 419 .filter(|entry| {
420 420 entry.is_non_normal() || entry.is_from_other_parent()
421 421 })
422 422 .map(|_| &**path)
423 423 }))
424 424 }
425 425
426 426 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
427 427 // Do nothing, this `DirstateMap` does not have a separate "non normal
428 428 // entries" and "from other parent" sets that need to be recomputed
429 429 }
430 430
431 431 fn iter_non_normal_paths(
432 432 &mut self,
433 433 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
434 434 self.iter_non_normal_paths_panic()
435 435 }
436 436
437 437 fn iter_non_normal_paths_panic(
438 438 &self,
439 439 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
440 440 Box::new(self.iter_nodes().filter_map(|(path, node)| {
441 441 node.entry
442 442 .as_ref()
443 443 .filter(|entry| entry.is_non_normal())
444 444 .map(|_| &**path)
445 445 }))
446 446 }
447 447
448 448 fn iter_other_parent_paths(
449 449 &mut self,
450 450 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
451 451 Box::new(self.iter_nodes().filter_map(|(path, node)| {
452 452 node.entry
453 453 .as_ref()
454 454 .filter(|entry| entry.is_from_other_parent())
455 455 .map(|_| &**path)
456 456 }))
457 457 }
458 458
459 459 fn has_tracked_dir(
460 460 &mut self,
461 461 directory: &HgPath,
462 462 ) -> Result<bool, DirstateMapError> {
463 463 if let Some(node) = self.get_node(directory) {
464 464 // A node without a `DirstateEntry` was created to hold child
465 465 // nodes, and is therefore a directory.
466 466 Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
467 467 } else {
468 468 Ok(false)
469 469 }
470 470 }
471 471
472 472 fn has_dir(
473 473 &mut self,
474 474 directory: &HgPath,
475 475 ) -> Result<bool, DirstateMapError> {
476 476 if let Some(node) = self.get_node(directory) {
477 477 // A node without a `DirstateEntry` was created to hold child
478 478 // nodes, and is therefore a directory.
479 479 Ok(node.entry.is_none())
480 480 } else {
481 481 Ok(false)
482 482 }
483 483 }
484 484
485 485 #[timed]
486 486 fn pack_v1(
487 487 &mut self,
488 488 parents: DirstateParents,
489 489 now: Timestamp,
490 490 ) -> Result<Vec<u8>, DirstateError> {
491 491 let now: i32 = now.0.try_into().expect("time overflow");
492 492 let mut ambiguous_mtimes = Vec::new();
493 493 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
494 494 // reallocations
495 495 let mut size = parents.as_bytes().len();
496 496 for (path, node) in self.iter_nodes() {
497 497 if let Some(entry) = &node.entry {
498 498 size += packed_entry_size(
499 499 path,
500 500 node.copy_source.as_ref().map(|p| &**p),
501 501 );
502 502 if entry.mtime_is_ambiguous(now) {
503 503 ambiguous_mtimes.push(path.clone())
504 504 }
505 505 }
506 506 }
507 507 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes);
508 508
509 509 let mut packed = Vec::with_capacity(size);
510 510 packed.extend(parents.as_bytes());
511 511
512 512 for (path, node) in self.iter_nodes() {
513 513 if let Some(entry) = &node.entry {
514 514 pack_entry(
515 515 path,
516 516 entry,
517 517 node.copy_source.as_ref().map(|p| &**p),
518 518 &mut packed,
519 519 );
520 520 }
521 521 }
522 522 Ok(packed)
523 523 }
524 524
525 525 #[timed]
526 526 fn pack_v2(
527 527 &mut self,
528 528 parents: DirstateParents,
529 529 now: Timestamp,
530 530 ) -> Result<Vec<u8>, DirstateError> {
531 531 // TODO: how do we want to handle this in 2038?
532 532 let now: i32 = now.0.try_into().expect("time overflow");
533 533 let mut paths = Vec::new();
534 534 for (path, node) in self.iter_nodes() {
535 535 if let Some(entry) = &node.entry {
536 536 if entry.mtime_is_ambiguous(now) {
537 537 paths.push(path.clone())
538 538 }
539 539 }
540 540 }
541 541 // Borrow of `self` ends here since we collect cloned paths
542 542
543 543 self.clear_known_ambiguous_mtimes(&paths);
544 544
545 545 on_disk::write(self, parents)
546 546 }
547 547
548 548 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
549 549 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
550 550 // needs to be recomputed
551 551 Ok(())
552 552 }
553 553
554 554 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
555 555 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
556 556 // to be recomputed
557 557 Ok(())
558 558 }
559 559
560 560 fn status<'a>(
561 561 &'a mut self,
562 562 matcher: &'a (dyn Matcher + Sync),
563 563 root_dir: PathBuf,
564 564 ignore_files: Vec<PathBuf>,
565 565 options: StatusOptions,
566 566 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
567 567 {
568 568 super::status::status(self, matcher, root_dir, ignore_files, options)
569 569 }
570 570
571 571 fn copy_map_len(&self) -> usize {
572 572 self.nodes_with_copy_source_count as usize
573 573 }
574 574
575 575 fn copy_map_iter(&self) -> CopyMapIter<'_> {
576 576 Box::new(self.iter_nodes().filter_map(|(path, node)| {
577 577 node.copy_source
578 578 .as_ref()
579 579 .map(|copy_source| (&**path, &**copy_source))
580 580 }))
581 581 }
582 582
583 583 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
584 584 if let Some(node) = self.get_node(key) {
585 585 node.copy_source.is_some()
586 586 } else {
587 587 false
588 588 }
589 589 }
590 590
591 591 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
592 592 self.get_node(key)?.copy_source.as_ref().map(|p| &**p)
593 593 }
594 594
595 595 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
596 596 let count = &mut self.nodes_with_copy_source_count;
597 597 Self::get_node_mut(&mut self.root, key).and_then(|node| {
598 598 if node.copy_source.is_some() {
599 599 *count -= 1
600 600 }
601 601 node.copy_source.take().map(Cow::into_owned)
602 602 })
603 603 }
604 604
605 605 fn copy_map_insert(
606 606 &mut self,
607 607 key: HgPathBuf,
608 608 value: HgPathBuf,
609 609 ) -> Option<HgPathBuf> {
610 610 let node = Self::get_or_insert_node(
611 611 &mut self.root,
612 612 &key,
613 613 WithBasename::to_cow_owned,
614 614 |_ancestor| {},
615 615 );
616 616 if node.copy_source.is_none() {
617 617 self.nodes_with_copy_source_count += 1
618 618 }
619 619 node.copy_source.replace(value.into()).map(Cow::into_owned)
620 620 }
621 621
622 622 fn len(&self) -> usize {
623 623 self.nodes_with_entry_count as usize
624 624 }
625 625
626 626 fn contains_key(&self, key: &HgPath) -> bool {
627 627 self.get(key).is_some()
628 628 }
629 629
630 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
631 self.get_node(key)?.entry.as_ref()
630 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
631 self.get_node(key)?.entry
632 632 }
633 633
634 634 fn iter(&self) -> StateMapIter<'_> {
635 635 Box::new(self.iter_nodes().filter_map(|(path, node)| {
636 node.entry.as_ref().map(|entry| (&**path, entry))
636 node.entry.map(|entry| (&**path, entry))
637 637 }))
638 638 }
639 639 }
@@ -1,300 +1,300 b''
1 1 use std::path::PathBuf;
2 2
3 3 use crate::dirstate::parsers::Timestamp;
4 4 use crate::matchers::Matcher;
5 5 use crate::utils::hg_path::{HgPath, HgPathBuf};
6 6 use crate::CopyMapIter;
7 7 use crate::DirstateEntry;
8 8 use crate::DirstateError;
9 9 use crate::DirstateMap;
10 10 use crate::DirstateMapError;
11 11 use crate::DirstateParents;
12 12 use crate::DirstateStatus;
13 13 use crate::EntryState;
14 14 use crate::PatternFileWarning;
15 15 use crate::StateMapIter;
16 16 use crate::StatusError;
17 17 use crate::StatusOptions;
18 18
19 19 pub trait DirstateMapMethods {
20 20 fn clear(&mut self);
21 21
22 22 fn add_file(
23 23 &mut self,
24 24 filename: &HgPath,
25 25 old_state: EntryState,
26 26 entry: DirstateEntry,
27 27 ) -> Result<(), DirstateMapError>;
28 28
29 29 fn remove_file(
30 30 &mut self,
31 31 filename: &HgPath,
32 32 old_state: EntryState,
33 33 size: i32,
34 34 ) -> Result<(), DirstateMapError>;
35 35
36 36 fn drop_file(
37 37 &mut self,
38 38 filename: &HgPath,
39 39 old_state: EntryState,
40 40 ) -> Result<bool, DirstateMapError>;
41 41
42 42 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32);
43 43
44 44 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool;
45 45
46 46 fn non_normal_entries_remove(&mut self, key: &HgPath);
47 47
48 48 fn non_normal_or_other_parent_paths(
49 49 &mut self,
50 50 ) -> Box<dyn Iterator<Item = &HgPath> + '_>;
51 51
52 52 fn set_non_normal_other_parent_entries(&mut self, force: bool);
53 53
54 54 fn iter_non_normal_paths(
55 55 &mut self,
56 56 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_>;
57 57
58 58 fn iter_non_normal_paths_panic(
59 59 &self,
60 60 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_>;
61 61
62 62 fn iter_other_parent_paths(
63 63 &mut self,
64 64 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_>;
65 65
66 66 fn has_tracked_dir(
67 67 &mut self,
68 68 directory: &HgPath,
69 69 ) -> Result<bool, DirstateMapError>;
70 70
71 71 fn has_dir(
72 72 &mut self,
73 73 directory: &HgPath,
74 74 ) -> Result<bool, DirstateMapError>;
75 75
76 76 fn pack_v1(
77 77 &mut self,
78 78 parents: DirstateParents,
79 79 now: Timestamp,
80 80 ) -> Result<Vec<u8>, DirstateError>;
81 81
82 82 fn pack_v2(
83 83 &mut self,
84 84 parents: DirstateParents,
85 85 now: Timestamp,
86 86 ) -> Result<Vec<u8>, DirstateError>;
87 87
88 88 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError>;
89 89
90 90 fn set_dirs(&mut self) -> Result<(), DirstateMapError>;
91 91
92 92 fn status<'a>(
93 93 &'a mut self,
94 94 matcher: &'a (dyn Matcher + Sync),
95 95 root_dir: PathBuf,
96 96 ignore_files: Vec<PathBuf>,
97 97 options: StatusOptions,
98 98 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>;
99 99
100 100 fn copy_map_len(&self) -> usize;
101 101
102 102 fn copy_map_iter(&self) -> CopyMapIter<'_>;
103 103
104 104 fn copy_map_contains_key(&self, key: &HgPath) -> bool;
105 105
106 106 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath>;
107 107
108 108 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf>;
109 109
110 110 fn copy_map_insert(
111 111 &mut self,
112 112 key: HgPathBuf,
113 113 value: HgPathBuf,
114 114 ) -> Option<HgPathBuf>;
115 115
116 116 fn len(&self) -> usize;
117 117
118 118 fn contains_key(&self, key: &HgPath) -> bool;
119 119
120 fn get(&self, key: &HgPath) -> Option<&DirstateEntry>;
120 fn get(&self, key: &HgPath) -> Option<DirstateEntry>;
121 121
122 122 fn iter(&self) -> StateMapIter<'_>;
123 123 }
124 124
125 125 impl DirstateMapMethods for DirstateMap {
126 126 fn clear(&mut self) {
127 127 self.clear()
128 128 }
129 129
130 130 fn add_file(
131 131 &mut self,
132 132 filename: &HgPath,
133 133 old_state: EntryState,
134 134 entry: DirstateEntry,
135 135 ) -> Result<(), DirstateMapError> {
136 136 self.add_file(filename, old_state, entry)
137 137 }
138 138
139 139 fn remove_file(
140 140 &mut self,
141 141 filename: &HgPath,
142 142 old_state: EntryState,
143 143 size: i32,
144 144 ) -> Result<(), DirstateMapError> {
145 145 self.remove_file(filename, old_state, size)
146 146 }
147 147
148 148 fn drop_file(
149 149 &mut self,
150 150 filename: &HgPath,
151 151 old_state: EntryState,
152 152 ) -> Result<bool, DirstateMapError> {
153 153 self.drop_file(filename, old_state)
154 154 }
155 155
156 156 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
157 157 self.clear_ambiguous_times(filenames, now)
158 158 }
159 159
160 160 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
161 161 let (non_normal, _other_parent) =
162 162 self.get_non_normal_other_parent_entries();
163 163 non_normal.contains(key)
164 164 }
165 165
166 166 fn non_normal_entries_remove(&mut self, key: &HgPath) {
167 167 self.non_normal_entries_remove(key)
168 168 }
169 169
170 170 fn non_normal_or_other_parent_paths(
171 171 &mut self,
172 172 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
173 173 let (non_normal, other_parent) =
174 174 self.get_non_normal_other_parent_entries();
175 175 Box::new(non_normal.union(other_parent).map(|p| &**p))
176 176 }
177 177
178 178 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
179 179 self.set_non_normal_other_parent_entries(force)
180 180 }
181 181
182 182 fn iter_non_normal_paths(
183 183 &mut self,
184 184 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
185 185 let (non_normal, _other_parent) =
186 186 self.get_non_normal_other_parent_entries();
187 187 Box::new(non_normal.iter().map(|p| &**p))
188 188 }
189 189
190 190 fn iter_non_normal_paths_panic(
191 191 &self,
192 192 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
193 193 let (non_normal, _other_parent) =
194 194 self.get_non_normal_other_parent_entries_panic();
195 195 Box::new(non_normal.iter().map(|p| &**p))
196 196 }
197 197
198 198 fn iter_other_parent_paths(
199 199 &mut self,
200 200 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
201 201 let (_non_normal, other_parent) =
202 202 self.get_non_normal_other_parent_entries();
203 203 Box::new(other_parent.iter().map(|p| &**p))
204 204 }
205 205
206 206 fn has_tracked_dir(
207 207 &mut self,
208 208 directory: &HgPath,
209 209 ) -> Result<bool, DirstateMapError> {
210 210 self.has_tracked_dir(directory)
211 211 }
212 212
213 213 fn has_dir(
214 214 &mut self,
215 215 directory: &HgPath,
216 216 ) -> Result<bool, DirstateMapError> {
217 217 self.has_dir(directory)
218 218 }
219 219
220 220 fn pack_v1(
221 221 &mut self,
222 222 parents: DirstateParents,
223 223 now: Timestamp,
224 224 ) -> Result<Vec<u8>, DirstateError> {
225 225 self.pack(parents, now)
226 226 }
227 227
228 228 fn pack_v2(
229 229 &mut self,
230 230 _parents: DirstateParents,
231 231 _now: Timestamp,
232 232 ) -> Result<Vec<u8>, DirstateError> {
233 233 panic!(
234 234 "should have used dirstate_tree::DirstateMap to use the v2 format"
235 235 )
236 236 }
237 237
238 238 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
239 239 self.set_all_dirs()
240 240 }
241 241
242 242 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
243 243 self.set_dirs()
244 244 }
245 245
246 246 fn status<'a>(
247 247 &'a mut self,
248 248 matcher: &'a (dyn Matcher + Sync),
249 249 root_dir: PathBuf,
250 250 ignore_files: Vec<PathBuf>,
251 251 options: StatusOptions,
252 252 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
253 253 {
254 254 crate::status(self, matcher, root_dir, ignore_files, options)
255 255 }
256 256
257 257 fn copy_map_len(&self) -> usize {
258 258 self.copy_map.len()
259 259 }
260 260
261 261 fn copy_map_iter(&self) -> CopyMapIter<'_> {
262 262 Box::new(self.copy_map.iter().map(|(key, value)| (&**key, &**value)))
263 263 }
264 264
265 265 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
266 266 self.copy_map.contains_key(key)
267 267 }
268 268
269 269 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
270 270 self.copy_map.get(key).map(|p| &**p)
271 271 }
272 272
273 273 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
274 274 self.copy_map.remove(key)
275 275 }
276 276
277 277 fn copy_map_insert(
278 278 &mut self,
279 279 key: HgPathBuf,
280 280 value: HgPathBuf,
281 281 ) -> Option<HgPathBuf> {
282 282 self.copy_map.insert(key, value)
283 283 }
284 284
285 285 fn len(&self) -> usize {
286 286 (&**self).len()
287 287 }
288 288
289 289 fn contains_key(&self, key: &HgPath) -> bool {
290 290 (&**self).contains_key(key)
291 291 }
292 292
293 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
294 (&**self).get(key)
293 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
294 (&**self).get(key).cloned()
295 295 }
296 296
297 297 fn iter(&self) -> StateMapIter<'_> {
298 Box::new((&**self).iter().map(|(key, value)| (&**key, value)))
298 Box::new((&**self).iter().map(|(key, value)| (&**key, *value)))
299 299 }
300 300 }
@@ -1,140 +1,141 b''
1 1 // dirs_multiset.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::RefCell;
12 12 use std::convert::TryInto;
13 13
14 14 use cpython::{
15 15 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
16 16 Python, UnsafePyLeaked,
17 17 };
18 18
19 19 use crate::dirstate::extract_dirstate;
20 20 use hg::{
21 21 errors::HgError,
22 22 utils::hg_path::{HgPath, HgPathBuf},
23 23 DirsMultiset, DirsMultisetIter, DirstateMapError, EntryState,
24 24 };
25 25
26 26 py_class!(pub class Dirs |py| {
27 27 @shared data inner: DirsMultiset;
28 28
29 29 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
30 30 // a `list`)
31 31 def __new__(
32 32 _cls,
33 33 map: PyObject,
34 34 skip: Option<PyObject> = None
35 35 ) -> PyResult<Self> {
36 36 let mut skip_state: Option<EntryState> = None;
37 37 if let Some(skip) = skip {
38 38 skip_state = Some(
39 39 skip.extract::<PyBytes>(py)?.data(py)[0]
40 40 .try_into()
41 41 .map_err(|e: HgError| {
42 42 PyErr::new::<exc::ValueError, _>(py, e.to_string())
43 43 })?,
44 44 );
45 45 }
46 46 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
47 47 let dirstate = extract_dirstate(py, &map)?;
48 DirsMultiset::from_dirstate(&dirstate, skip_state)
48 let dirstate = dirstate.iter().map(|(k, v)| (k, *v));
49 DirsMultiset::from_dirstate(dirstate, skip_state)
49 50 .map_err(|e: DirstateMapError| {
50 51 PyErr::new::<exc::ValueError, _>(py, e.to_string())
51 52 })?
52 53 } else {
53 54 let map: Result<Vec<HgPathBuf>, PyErr> = map
54 55 .iter(py)?
55 56 .map(|o| {
56 57 Ok(HgPathBuf::from_bytes(
57 58 o?.extract::<PyBytes>(py)?.data(py),
58 59 ))
59 60 })
60 61 .collect();
61 62 DirsMultiset::from_manifest(&map?)
62 63 .map_err(|e| {
63 64 PyErr::new::<exc::ValueError, _>(py, e.to_string())
64 65 })?
65 66 };
66 67
67 68 Self::create_instance(py, inner)
68 69 }
69 70
70 71 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
71 72 self.inner(py).borrow_mut().add_path(
72 73 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
73 74 ).and(Ok(py.None())).or_else(|e| {
74 75 match e {
75 76 DirstateMapError::EmptyPath => {
76 77 Ok(py.None())
77 78 },
78 79 e => {
79 80 Err(PyErr::new::<exc::ValueError, _>(
80 81 py,
81 82 e.to_string(),
82 83 ))
83 84 }
84 85 }
85 86 })
86 87 }
87 88
88 89 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
89 90 self.inner(py).borrow_mut().delete_path(
90 91 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
91 92 )
92 93 .and(Ok(py.None()))
93 94 .or_else(|e| {
94 95 match e {
95 96 DirstateMapError::EmptyPath => {
96 97 Ok(py.None())
97 98 },
98 99 e => {
99 100 Err(PyErr::new::<exc::ValueError, _>(
100 101 py,
101 102 e.to_string(),
102 103 ))
103 104 }
104 105 }
105 106 })
106 107 }
107 108 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
108 109 let leaked_ref = self.inner(py).leak_immutable();
109 110 DirsMultisetKeysIterator::from_inner(
110 111 py,
111 112 unsafe { leaked_ref.map(py, |o| o.iter()) },
112 113 )
113 114 }
114 115
115 116 def __contains__(&self, item: PyObject) -> PyResult<bool> {
116 117 Ok(self.inner(py).borrow().contains(HgPath::new(
117 118 item.extract::<PyBytes>(py)?.data(py).as_ref(),
118 119 )))
119 120 }
120 121 });
121 122
122 123 impl Dirs {
123 124 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
124 125 Self::create_instance(py, d)
125 126 }
126 127
127 128 fn translate_key(
128 129 py: Python,
129 130 res: &HgPathBuf,
130 131 ) -> PyResult<Option<PyBytes>> {
131 132 Ok(Some(PyBytes::new(py, res.as_bytes())))
132 133 }
133 134 }
134 135
135 136 py_shared_iterator!(
136 137 DirsMultisetKeysIterator,
137 138 UnsafePyLeaked<DirsMultisetIter<'static>>,
138 139 Dirs::translate_key,
139 140 Option<PyBytes>
140 141 );
@@ -1,564 +1,564 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::{RefCell, RefMut};
12 12 use std::convert::TryInto;
13 13
14 14 use cpython::{
15 15 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
16 16 PyObject, PyResult, PySet, PyString, Python, PythonObject, ToPyObject,
17 17 UnsafePyLeaked,
18 18 };
19 19
20 20 use crate::{
21 21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 22 dirstate::non_normal_entries::{
23 23 NonNormalEntries, NonNormalEntriesIterator,
24 24 },
25 25 dirstate::owning::OwningDirstateMap,
26 26 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
27 27 parsers::dirstate_parents_to_pytuple,
28 28 };
29 29 use hg::{
30 30 dirstate::parsers::Timestamp,
31 31 dirstate_tree::dispatch::DirstateMapMethods,
32 32 errors::HgError,
33 33 revlog::Node,
34 34 utils::files::normalize_case,
35 35 utils::hg_path::{HgPath, HgPathBuf},
36 36 DirsMultiset, DirstateEntry, DirstateError,
37 37 DirstateMap as RustDirstateMap, DirstateMapError, DirstateParents,
38 38 EntryState, StateMapIter,
39 39 };
40 40
41 41 // TODO
42 42 // This object needs to share references to multiple members of its Rust
43 43 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
44 44 // Right now `CopyMap` is done, but it needs to have an explicit reference
45 45 // to `RustDirstateMap` which itself needs to have an encapsulation for
46 46 // every method in `CopyMap` (copymapcopy, etc.).
47 47 // This is ugly and hard to maintain.
48 48 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
49 49 // `py_class!` is already implemented and does not mention
50 50 // `RustDirstateMap`, rightfully so.
51 51 // All attributes also have to have a separate refcount data attribute for
52 52 // leaks, with all methods that go along for reference sharing.
53 53 py_class!(pub class DirstateMap |py| {
54 54 @shared data inner: Box<dyn DirstateMapMethods + Send>;
55 55
56 56 /// Returns a `(dirstate_map, parents)` tuple
57 57 @staticmethod
58 58 def new(
59 59 use_dirstate_tree: bool,
60 60 use_dirstate_v2: bool,
61 61 on_disk: PyBytes,
62 62 ) -> PyResult<PyObject> {
63 63 let dirstate_error = |e: DirstateError| {
64 64 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
65 65 };
66 66 let (inner, parents) = if use_dirstate_tree || use_dirstate_v2 {
67 67 let (map, parents) =
68 68 OwningDirstateMap::new(py, on_disk, use_dirstate_v2)
69 69 .map_err(dirstate_error)?;
70 70 (Box::new(map) as _, parents)
71 71 } else {
72 72 let bytes = on_disk.data(py);
73 73 let mut map = RustDirstateMap::default();
74 74 let parents = map.read(bytes).map_err(dirstate_error)?;
75 75 (Box::new(map) as _, parents)
76 76 };
77 77 let map = Self::create_instance(py, inner)?;
78 78 let parents = parents.map(|p| dirstate_parents_to_pytuple(py, &p));
79 79 Ok((map, parents).to_py_object(py).into_object())
80 80 }
81 81
82 82 def clear(&self) -> PyResult<PyObject> {
83 83 self.inner(py).borrow_mut().clear();
84 84 Ok(py.None())
85 85 }
86 86
87 87 def get(
88 88 &self,
89 89 key: PyObject,
90 90 default: Option<PyObject> = None
91 91 ) -> PyResult<Option<PyObject>> {
92 92 let key = key.extract::<PyBytes>(py)?;
93 93 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
94 94 Some(entry) => {
95 Ok(Some(make_dirstate_tuple(py, entry)?))
95 Ok(Some(make_dirstate_tuple(py, &entry)?))
96 96 },
97 97 None => Ok(default)
98 98 }
99 99 }
100 100
101 101 def addfile(
102 102 &self,
103 103 f: PyObject,
104 104 oldstate: PyObject,
105 105 state: PyObject,
106 106 mode: PyObject,
107 107 size: PyObject,
108 108 mtime: PyObject
109 109 ) -> PyResult<PyObject> {
110 110 self.inner(py).borrow_mut().add_file(
111 111 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
112 112 oldstate.extract::<PyBytes>(py)?.data(py)[0]
113 113 .try_into()
114 114 .map_err(|e: HgError| {
115 115 PyErr::new::<exc::ValueError, _>(py, e.to_string())
116 116 })?,
117 117 DirstateEntry {
118 118 state: state.extract::<PyBytes>(py)?.data(py)[0]
119 119 .try_into()
120 120 .map_err(|e: HgError| {
121 121 PyErr::new::<exc::ValueError, _>(py, e.to_string())
122 122 })?,
123 123 mode: mode.extract(py)?,
124 124 size: size.extract(py)?,
125 125 mtime: mtime.extract(py)?,
126 126 },
127 127 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
128 128 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
129 129 })
130 130 }
131 131
132 132 def removefile(
133 133 &self,
134 134 f: PyObject,
135 135 oldstate: PyObject,
136 136 size: PyObject
137 137 ) -> PyResult<PyObject> {
138 138 self.inner(py).borrow_mut()
139 139 .remove_file(
140 140 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
141 141 oldstate.extract::<PyBytes>(py)?.data(py)[0]
142 142 .try_into()
143 143 .map_err(|e: HgError| {
144 144 PyErr::new::<exc::ValueError, _>(py, e.to_string())
145 145 })?,
146 146 size.extract(py)?,
147 147 )
148 148 .or_else(|_| {
149 149 Err(PyErr::new::<exc::OSError, _>(
150 150 py,
151 151 "Dirstate error".to_string(),
152 152 ))
153 153 })?;
154 154 Ok(py.None())
155 155 }
156 156
157 157 def dropfile(
158 158 &self,
159 159 f: PyObject,
160 160 oldstate: PyObject
161 161 ) -> PyResult<PyBool> {
162 162 self.inner(py).borrow_mut()
163 163 .drop_file(
164 164 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
165 165 oldstate.extract::<PyBytes>(py)?.data(py)[0]
166 166 .try_into()
167 167 .map_err(|e: HgError| {
168 168 PyErr::new::<exc::ValueError, _>(py, e.to_string())
169 169 })?,
170 170 )
171 171 .and_then(|b| Ok(b.to_py_object(py)))
172 172 .or_else(|e| {
173 173 Err(PyErr::new::<exc::OSError, _>(
174 174 py,
175 175 format!("Dirstate error: {}", e.to_string()),
176 176 ))
177 177 })
178 178 }
179 179
180 180 def clearambiguoustimes(
181 181 &self,
182 182 files: PyObject,
183 183 now: PyObject
184 184 ) -> PyResult<PyObject> {
185 185 let files: PyResult<Vec<HgPathBuf>> = files
186 186 .iter(py)?
187 187 .map(|filename| {
188 188 Ok(HgPathBuf::from_bytes(
189 189 filename?.extract::<PyBytes>(py)?.data(py),
190 190 ))
191 191 })
192 192 .collect();
193 193 self.inner(py).borrow_mut()
194 194 .clear_ambiguous_times(files?, now.extract(py)?);
195 195 Ok(py.None())
196 196 }
197 197
198 198 def other_parent_entries(&self) -> PyResult<PyObject> {
199 199 let mut inner_shared = self.inner(py).borrow_mut();
200 200 let set = PySet::empty(py)?;
201 201 for path in inner_shared.iter_other_parent_paths() {
202 202 set.add(py, PyBytes::new(py, path.as_bytes()))?;
203 203 }
204 204 Ok(set.into_object())
205 205 }
206 206
207 207 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
208 208 NonNormalEntries::from_inner(py, self.clone_ref(py))
209 209 }
210 210
211 211 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
212 212 let key = key.extract::<PyBytes>(py)?;
213 213 Ok(self
214 214 .inner(py)
215 215 .borrow_mut()
216 216 .non_normal_entries_contains(HgPath::new(key.data(py))))
217 217 }
218 218
219 219 def non_normal_entries_display(&self) -> PyResult<PyString> {
220 220 Ok(
221 221 PyString::new(
222 222 py,
223 223 &format!(
224 224 "NonNormalEntries: {}",
225 225 hg::utils::join_display(
226 226 self
227 227 .inner(py)
228 228 .borrow_mut()
229 229 .iter_non_normal_paths(),
230 230 ", "
231 231 )
232 232 )
233 233 )
234 234 )
235 235 }
236 236
237 237 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
238 238 let key = key.extract::<PyBytes>(py)?;
239 239 self
240 240 .inner(py)
241 241 .borrow_mut()
242 242 .non_normal_entries_remove(HgPath::new(key.data(py)));
243 243 Ok(py.None())
244 244 }
245 245
246 246 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
247 247 let mut inner = self.inner(py).borrow_mut();
248 248
249 249 let ret = PyList::new(py, &[]);
250 250 for filename in inner.non_normal_or_other_parent_paths() {
251 251 let as_pystring = PyBytes::new(py, filename.as_bytes());
252 252 ret.append(py, as_pystring.into_object());
253 253 }
254 254 Ok(ret)
255 255 }
256 256
257 257 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
258 258 // Make sure the sets are defined before we no longer have a mutable
259 259 // reference to the dmap.
260 260 self.inner(py)
261 261 .borrow_mut()
262 262 .set_non_normal_other_parent_entries(false);
263 263
264 264 let leaked_ref = self.inner(py).leak_immutable();
265 265
266 266 NonNormalEntriesIterator::from_inner(py, unsafe {
267 267 leaked_ref.map(py, |o| {
268 268 o.iter_non_normal_paths_panic()
269 269 })
270 270 })
271 271 }
272 272
273 273 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
274 274 let d = d.extract::<PyBytes>(py)?;
275 275 Ok(self.inner(py).borrow_mut()
276 276 .has_tracked_dir(HgPath::new(d.data(py)))
277 277 .map_err(|e| {
278 278 PyErr::new::<exc::ValueError, _>(py, e.to_string())
279 279 })?
280 280 .to_py_object(py))
281 281 }
282 282
283 283 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
284 284 let d = d.extract::<PyBytes>(py)?;
285 285 Ok(self.inner(py).borrow_mut()
286 286 .has_dir(HgPath::new(d.data(py)))
287 287 .map_err(|e| {
288 288 PyErr::new::<exc::ValueError, _>(py, e.to_string())
289 289 })?
290 290 .to_py_object(py))
291 291 }
292 292
293 293 def write(
294 294 &self,
295 295 use_dirstate_v2: bool,
296 296 p1: PyObject,
297 297 p2: PyObject,
298 298 now: PyObject
299 299 ) -> PyResult<PyBytes> {
300 300 let now = Timestamp(now.extract(py)?);
301 301 let parents = DirstateParents {
302 302 p1: extract_node_id(py, &p1)?,
303 303 p2: extract_node_id(py, &p2)?,
304 304 };
305 305
306 306 let mut inner = self.inner(py).borrow_mut();
307 307 let result = if use_dirstate_v2 {
308 308 inner.pack_v2(parents, now)
309 309 } else {
310 310 inner.pack_v1(parents, now)
311 311 };
312 312 match result {
313 313 Ok(packed) => Ok(PyBytes::new(py, &packed)),
314 314 Err(_) => Err(PyErr::new::<exc::OSError, _>(
315 315 py,
316 316 "Dirstate error".to_string(),
317 317 )),
318 318 }
319 319 }
320 320
321 321 def filefoldmapasdict(&self) -> PyResult<PyDict> {
322 322 let dict = PyDict::new(py);
323 323 for (path, entry) in self.inner(py).borrow_mut().iter() {
324 324 if entry.state != EntryState::Removed {
325 325 let key = normalize_case(path);
326 326 let value = path;
327 327 dict.set_item(
328 328 py,
329 329 PyBytes::new(py, key.as_bytes()).into_object(),
330 330 PyBytes::new(py, value.as_bytes()).into_object(),
331 331 )?;
332 332 }
333 333 }
334 334 Ok(dict)
335 335 }
336 336
337 337 def __len__(&self) -> PyResult<usize> {
338 338 Ok(self.inner(py).borrow().len())
339 339 }
340 340
341 341 def __contains__(&self, key: PyObject) -> PyResult<bool> {
342 342 let key = key.extract::<PyBytes>(py)?;
343 343 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
344 344 }
345 345
346 346 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
347 347 let key = key.extract::<PyBytes>(py)?;
348 348 let key = HgPath::new(key.data(py));
349 349 match self.inner(py).borrow().get(key) {
350 350 Some(entry) => {
351 Ok(make_dirstate_tuple(py, entry)?)
351 Ok(make_dirstate_tuple(py, &entry)?)
352 352 },
353 353 None => Err(PyErr::new::<exc::KeyError, _>(
354 354 py,
355 355 String::from_utf8_lossy(key.as_bytes()),
356 356 )),
357 357 }
358 358 }
359 359
360 360 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
361 361 let leaked_ref = self.inner(py).leak_immutable();
362 362 DirstateMapKeysIterator::from_inner(
363 363 py,
364 364 unsafe { leaked_ref.map(py, |o| o.iter()) },
365 365 )
366 366 }
367 367
368 368 def items(&self) -> PyResult<DirstateMapItemsIterator> {
369 369 let leaked_ref = self.inner(py).leak_immutable();
370 370 DirstateMapItemsIterator::from_inner(
371 371 py,
372 372 unsafe { leaked_ref.map(py, |o| o.iter()) },
373 373 )
374 374 }
375 375
376 376 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
377 377 let leaked_ref = self.inner(py).leak_immutable();
378 378 DirstateMapKeysIterator::from_inner(
379 379 py,
380 380 unsafe { leaked_ref.map(py, |o| o.iter()) },
381 381 )
382 382 }
383 383
384 384 def getdirs(&self) -> PyResult<Dirs> {
385 385 // TODO don't copy, share the reference
386 386 self.inner(py).borrow_mut().set_dirs()
387 387 .map_err(|e| {
388 388 PyErr::new::<exc::ValueError, _>(py, e.to_string())
389 389 })?;
390 390 Dirs::from_inner(
391 391 py,
392 392 DirsMultiset::from_dirstate(
393 393 self.inner(py).borrow().iter(),
394 394 Some(EntryState::Removed),
395 395 )
396 396 .map_err(|e| {
397 397 PyErr::new::<exc::ValueError, _>(py, e.to_string())
398 398 })?,
399 399 )
400 400 }
401 401 def getalldirs(&self) -> PyResult<Dirs> {
402 402 // TODO don't copy, share the reference
403 403 self.inner(py).borrow_mut().set_all_dirs()
404 404 .map_err(|e| {
405 405 PyErr::new::<exc::ValueError, _>(py, e.to_string())
406 406 })?;
407 407 Dirs::from_inner(
408 408 py,
409 409 DirsMultiset::from_dirstate(
410 410 self.inner(py).borrow().iter(),
411 411 None,
412 412 ).map_err(|e| {
413 413 PyErr::new::<exc::ValueError, _>(py, e.to_string())
414 414 })?,
415 415 )
416 416 }
417 417
418 418 // TODO all copymap* methods, see docstring above
419 419 def copymapcopy(&self) -> PyResult<PyDict> {
420 420 let dict = PyDict::new(py);
421 421 for (key, value) in self.inner(py).borrow().copy_map_iter() {
422 422 dict.set_item(
423 423 py,
424 424 PyBytes::new(py, key.as_bytes()),
425 425 PyBytes::new(py, value.as_bytes()),
426 426 )?;
427 427 }
428 428 Ok(dict)
429 429 }
430 430
431 431 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
432 432 let key = key.extract::<PyBytes>(py)?;
433 433 match self.inner(py).borrow().copy_map_get(HgPath::new(key.data(py))) {
434 434 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
435 435 None => Err(PyErr::new::<exc::KeyError, _>(
436 436 py,
437 437 String::from_utf8_lossy(key.data(py)),
438 438 )),
439 439 }
440 440 }
441 441 def copymap(&self) -> PyResult<CopyMap> {
442 442 CopyMap::from_inner(py, self.clone_ref(py))
443 443 }
444 444
445 445 def copymaplen(&self) -> PyResult<usize> {
446 446 Ok(self.inner(py).borrow().copy_map_len())
447 447 }
448 448 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
449 449 let key = key.extract::<PyBytes>(py)?;
450 450 Ok(self
451 451 .inner(py)
452 452 .borrow()
453 453 .copy_map_contains_key(HgPath::new(key.data(py))))
454 454 }
455 455 def copymapget(
456 456 &self,
457 457 key: PyObject,
458 458 default: Option<PyObject>
459 459 ) -> PyResult<Option<PyObject>> {
460 460 let key = key.extract::<PyBytes>(py)?;
461 461 match self
462 462 .inner(py)
463 463 .borrow()
464 464 .copy_map_get(HgPath::new(key.data(py)))
465 465 {
466 466 Some(copy) => Ok(Some(
467 467 PyBytes::new(py, copy.as_bytes()).into_object(),
468 468 )),
469 469 None => Ok(default),
470 470 }
471 471 }
472 472 def copymapsetitem(
473 473 &self,
474 474 key: PyObject,
475 475 value: PyObject
476 476 ) -> PyResult<PyObject> {
477 477 let key = key.extract::<PyBytes>(py)?;
478 478 let value = value.extract::<PyBytes>(py)?;
479 479 self.inner(py).borrow_mut().copy_map_insert(
480 480 HgPathBuf::from_bytes(key.data(py)),
481 481 HgPathBuf::from_bytes(value.data(py)),
482 482 );
483 483 Ok(py.None())
484 484 }
485 485 def copymappop(
486 486 &self,
487 487 key: PyObject,
488 488 default: Option<PyObject>
489 489 ) -> PyResult<Option<PyObject>> {
490 490 let key = key.extract::<PyBytes>(py)?;
491 491 match self
492 492 .inner(py)
493 493 .borrow_mut()
494 494 .copy_map_remove(HgPath::new(key.data(py)))
495 495 {
496 496 Some(_) => Ok(None),
497 497 None => Ok(default),
498 498 }
499 499 }
500 500
501 501 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
502 502 let leaked_ref = self.inner(py).leak_immutable();
503 503 CopyMapKeysIterator::from_inner(
504 504 py,
505 505 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
506 506 )
507 507 }
508 508
509 509 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
510 510 let leaked_ref = self.inner(py).leak_immutable();
511 511 CopyMapItemsIterator::from_inner(
512 512 py,
513 513 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
514 514 )
515 515 }
516 516
517 517 });
518 518
519 519 impl DirstateMap {
520 520 pub fn get_inner_mut<'a>(
521 521 &'a self,
522 522 py: Python<'a>,
523 523 ) -> RefMut<'a, Box<dyn DirstateMapMethods + Send>> {
524 524 self.inner(py).borrow_mut()
525 525 }
526 526 fn translate_key(
527 527 py: Python,
528 res: (&HgPath, &DirstateEntry),
528 res: (&HgPath, DirstateEntry),
529 529 ) -> PyResult<Option<PyBytes>> {
530 530 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
531 531 }
532 532 fn translate_key_value(
533 533 py: Python,
534 res: (&HgPath, &DirstateEntry),
534 res: (&HgPath, DirstateEntry),
535 535 ) -> PyResult<Option<(PyBytes, PyObject)>> {
536 536 let (f, entry) = res;
537 537 Ok(Some((
538 538 PyBytes::new(py, f.as_bytes()),
539 539 make_dirstate_tuple(py, &entry)?,
540 540 )))
541 541 }
542 542 }
543 543
544 544 py_shared_iterator!(
545 545 DirstateMapKeysIterator,
546 546 UnsafePyLeaked<StateMapIter<'static>>,
547 547 DirstateMap::translate_key,
548 548 Option<PyBytes>
549 549 );
550 550
551 551 py_shared_iterator!(
552 552 DirstateMapItemsIterator,
553 553 UnsafePyLeaked<StateMapIter<'static>>,
554 554 DirstateMap::translate_key_value,
555 555 Option<(PyBytes, PyObject)>
556 556 );
557 557
558 558 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
559 559 let bytes = obj.extract::<PyBytes>(py)?;
560 560 match bytes.data(py).try_into() {
561 561 Ok(s) => Ok(s),
562 562 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
563 563 }
564 564 }
@@ -1,183 +1,183 b''
1 1 use crate::dirstate::owning::OwningDirstateMap;
2 2 use hg::dirstate::parsers::Timestamp;
3 3 use hg::dirstate_tree::dispatch::DirstateMapMethods;
4 4 use hg::matchers::Matcher;
5 5 use hg::utils::hg_path::{HgPath, HgPathBuf};
6 6 use hg::CopyMapIter;
7 7 use hg::DirstateEntry;
8 8 use hg::DirstateError;
9 9 use hg::DirstateMapError;
10 10 use hg::DirstateParents;
11 11 use hg::DirstateStatus;
12 12 use hg::EntryState;
13 13 use hg::PatternFileWarning;
14 14 use hg::StateMapIter;
15 15 use hg::StatusError;
16 16 use hg::StatusOptions;
17 17 use std::path::PathBuf;
18 18
19 19 impl DirstateMapMethods for OwningDirstateMap {
20 20 fn clear(&mut self) {
21 21 self.get_mut().clear()
22 22 }
23 23
24 24 fn add_file(
25 25 &mut self,
26 26 filename: &HgPath,
27 27 old_state: EntryState,
28 28 entry: DirstateEntry,
29 29 ) -> Result<(), DirstateMapError> {
30 30 self.get_mut().add_file(filename, old_state, entry)
31 31 }
32 32
33 33 fn remove_file(
34 34 &mut self,
35 35 filename: &HgPath,
36 36 old_state: EntryState,
37 37 size: i32,
38 38 ) -> Result<(), DirstateMapError> {
39 39 self.get_mut().remove_file(filename, old_state, size)
40 40 }
41 41
42 42 fn drop_file(
43 43 &mut self,
44 44 filename: &HgPath,
45 45 old_state: EntryState,
46 46 ) -> Result<bool, DirstateMapError> {
47 47 self.get_mut().drop_file(filename, old_state)
48 48 }
49 49
50 50 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
51 51 self.get_mut().clear_ambiguous_times(filenames, now)
52 52 }
53 53
54 54 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
55 55 self.get_mut().non_normal_entries_contains(key)
56 56 }
57 57
58 58 fn non_normal_entries_remove(&mut self, key: &HgPath) {
59 59 self.get_mut().non_normal_entries_remove(key)
60 60 }
61 61
62 62 fn non_normal_or_other_parent_paths(
63 63 &mut self,
64 64 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
65 65 self.get_mut().non_normal_or_other_parent_paths()
66 66 }
67 67
68 68 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
69 69 self.get_mut().set_non_normal_other_parent_entries(force)
70 70 }
71 71
72 72 fn iter_non_normal_paths(
73 73 &mut self,
74 74 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
75 75 self.get_mut().iter_non_normal_paths()
76 76 }
77 77
78 78 fn iter_non_normal_paths_panic(
79 79 &self,
80 80 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
81 81 self.get().iter_non_normal_paths_panic()
82 82 }
83 83
84 84 fn iter_other_parent_paths(
85 85 &mut self,
86 86 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
87 87 self.get_mut().iter_other_parent_paths()
88 88 }
89 89
90 90 fn has_tracked_dir(
91 91 &mut self,
92 92 directory: &HgPath,
93 93 ) -> Result<bool, DirstateMapError> {
94 94 self.get_mut().has_tracked_dir(directory)
95 95 }
96 96
97 97 fn has_dir(
98 98 &mut self,
99 99 directory: &HgPath,
100 100 ) -> Result<bool, DirstateMapError> {
101 101 self.get_mut().has_dir(directory)
102 102 }
103 103
104 104 fn pack_v1(
105 105 &mut self,
106 106 parents: DirstateParents,
107 107 now: Timestamp,
108 108 ) -> Result<Vec<u8>, DirstateError> {
109 109 self.get_mut().pack_v1(parents, now)
110 110 }
111 111
112 112 fn pack_v2(
113 113 &mut self,
114 114 parents: DirstateParents,
115 115 now: Timestamp,
116 116 ) -> Result<Vec<u8>, DirstateError> {
117 117 self.get_mut().pack_v2(parents, now)
118 118 }
119 119
120 120 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
121 121 self.get_mut().set_all_dirs()
122 122 }
123 123
124 124 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
125 125 self.get_mut().set_dirs()
126 126 }
127 127
128 128 fn status<'a>(
129 129 &'a mut self,
130 130 matcher: &'a (dyn Matcher + Sync),
131 131 root_dir: PathBuf,
132 132 ignore_files: Vec<PathBuf>,
133 133 options: StatusOptions,
134 134 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
135 135 {
136 136 self.get_mut()
137 137 .status(matcher, root_dir, ignore_files, options)
138 138 }
139 139
140 140 fn copy_map_len(&self) -> usize {
141 141 self.get().copy_map_len()
142 142 }
143 143
144 144 fn copy_map_iter(&self) -> CopyMapIter<'_> {
145 145 self.get().copy_map_iter()
146 146 }
147 147
148 148 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
149 149 self.get().copy_map_contains_key(key)
150 150 }
151 151
152 152 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
153 153 self.get().copy_map_get(key)
154 154 }
155 155
156 156 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
157 157 self.get_mut().copy_map_remove(key)
158 158 }
159 159
160 160 fn copy_map_insert(
161 161 &mut self,
162 162 key: HgPathBuf,
163 163 value: HgPathBuf,
164 164 ) -> Option<HgPathBuf> {
165 165 self.get_mut().copy_map_insert(key, value)
166 166 }
167 167
168 168 fn len(&self) -> usize {
169 169 self.get().len()
170 170 }
171 171
172 172 fn contains_key(&self, key: &HgPath) -> bool {
173 173 self.get().contains_key(key)
174 174 }
175 175
176 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
176 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
177 177 self.get().get(key)
178 178 }
179 179
180 180 fn iter(&self) -> StateMapIter<'_> {
181 181 self.get().iter()
182 182 }
183 183 }
General Comments 0
You need to be logged in to leave comments. Login now