##// END OF EJS Templates
rust: Remove DirstateMap::file_fold_map...
Simon Sapin -
r47879:33e5511b default
parent child Browse files
Show More
@@ -1,487 +1,459 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::clear_ambiguous_mtime;
9 9 use crate::dirstate::parsers::Timestamp;
10 10 use crate::errors::HgError;
11 11 use crate::revlog::node::NULL_NODE;
12 12 use crate::{
13 13 dirstate::{parsers::PARENT_SIZE, EntryState},
14 14 pack_dirstate, parse_dirstate,
15 utils::{
16 files::normalize_case,
17 hg_path::{HgPath, HgPathBuf},
18 },
15 utils::hg_path::{HgPath, HgPathBuf},
19 16 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
20 DirstateParents, FastHashMap, StateMap,
17 DirstateParents, StateMap,
21 18 };
22 19 use micro_timer::timed;
23 20 use std::collections::HashSet;
24 21 use std::convert::TryInto;
25 22 use std::iter::FromIterator;
26 23 use std::ops::Deref;
27 24
28 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
29
30 25 #[derive(Default)]
31 26 pub struct DirstateMap {
32 27 state_map: StateMap,
33 28 pub copy_map: CopyMap,
34 file_fold_map: Option<FileFoldMap>,
35 29 pub dirs: Option<DirsMultiset>,
36 30 pub all_dirs: Option<DirsMultiset>,
37 31 non_normal_set: Option<HashSet<HgPathBuf>>,
38 32 other_parent_set: Option<HashSet<HgPathBuf>>,
39 33 parents: Option<DirstateParents>,
40 34 dirty_parents: bool,
41 35 }
42 36
43 37 /// Should only really be used in python interface code, for clarity
44 38 impl Deref for DirstateMap {
45 39 type Target = StateMap;
46 40
47 41 fn deref(&self) -> &Self::Target {
48 42 &self.state_map
49 43 }
50 44 }
51 45
52 46 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 47 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 48 iter: I,
55 49 ) -> Self {
56 50 Self {
57 51 state_map: iter.into_iter().collect(),
58 52 ..Self::default()
59 53 }
60 54 }
61 55 }
62 56
63 57 impl DirstateMap {
64 58 pub fn new() -> Self {
65 59 Self::default()
66 60 }
67 61
68 62 pub fn clear(&mut self) {
69 63 self.state_map = StateMap::default();
70 64 self.copy_map.clear();
71 self.file_fold_map = None;
72 65 self.non_normal_set = None;
73 66 self.other_parent_set = None;
74 67 self.set_parents(&DirstateParents {
75 68 p1: NULL_NODE,
76 69 p2: NULL_NODE,
77 70 })
78 71 }
79 72
80 73 /// Add a tracked file to the dirstate
81 74 pub fn add_file(
82 75 &mut self,
83 76 filename: &HgPath,
84 77 old_state: EntryState,
85 78 entry: DirstateEntry,
86 79 ) -> Result<(), DirstateMapError> {
87 80 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 81 {
89 82 if let Some(ref mut dirs) = self.dirs {
90 83 dirs.add_path(filename)?;
91 84 }
92 85 }
93 86 if old_state == EntryState::Unknown {
94 87 if let Some(ref mut all_dirs) = self.all_dirs {
95 88 all_dirs.add_path(filename)?;
96 89 }
97 90 }
98 91 self.state_map.insert(filename.to_owned(), entry.to_owned());
99 92
100 93 if entry.is_non_normal() {
101 94 self.get_non_normal_other_parent_entries()
102 95 .0
103 96 .insert(filename.to_owned());
104 97 }
105 98
106 99 if entry.is_from_other_parent() {
107 100 self.get_non_normal_other_parent_entries()
108 101 .1
109 102 .insert(filename.to_owned());
110 103 }
111 104 Ok(())
112 105 }
113 106
114 107 /// Mark a file as removed in the dirstate.
115 108 ///
116 109 /// The `size` parameter is used to store sentinel values that indicate
117 110 /// the file's previous state. In the future, we should refactor this
118 111 /// to be more explicit about what that state is.
119 112 pub fn remove_file(
120 113 &mut self,
121 114 filename: &HgPath,
122 115 old_state: EntryState,
123 116 size: i32,
124 117 ) -> Result<(), DirstateMapError> {
125 118 if old_state != EntryState::Unknown && old_state != EntryState::Removed
126 119 {
127 120 if let Some(ref mut dirs) = self.dirs {
128 121 dirs.delete_path(filename)?;
129 122 }
130 123 }
131 124 if old_state == EntryState::Unknown {
132 125 if let Some(ref mut all_dirs) = self.all_dirs {
133 126 all_dirs.add_path(filename)?;
134 127 }
135 128 }
136 129
137 if let Some(ref mut file_fold_map) = self.file_fold_map {
138 file_fold_map.remove(&normalize_case(filename));
139 }
140 130 self.state_map.insert(
141 131 filename.to_owned(),
142 132 DirstateEntry {
143 133 state: EntryState::Removed,
144 134 mode: 0,
145 135 size,
146 136 mtime: 0,
147 137 },
148 138 );
149 139 self.get_non_normal_other_parent_entries()
150 140 .0
151 141 .insert(filename.to_owned());
152 142 Ok(())
153 143 }
154 144
155 145 /// Remove a file from the dirstate.
156 146 /// Returns `true` if the file was previously recorded.
157 147 pub fn drop_file(
158 148 &mut self,
159 149 filename: &HgPath,
160 150 old_state: EntryState,
161 151 ) -> Result<bool, DirstateMapError> {
162 152 let exists = self.state_map.remove(filename).is_some();
163 153
164 154 if exists {
165 155 if old_state != EntryState::Removed {
166 156 if let Some(ref mut dirs) = self.dirs {
167 157 dirs.delete_path(filename)?;
168 158 }
169 159 }
170 160 if let Some(ref mut all_dirs) = self.all_dirs {
171 161 all_dirs.delete_path(filename)?;
172 162 }
173 163 }
174 if let Some(ref mut file_fold_map) = self.file_fold_map {
175 file_fold_map.remove(&normalize_case(filename));
176 }
177 164 self.get_non_normal_other_parent_entries()
178 165 .0
179 166 .remove(filename);
180 167
181 168 Ok(exists)
182 169 }
183 170
184 171 pub fn clear_ambiguous_times(
185 172 &mut self,
186 173 filenames: Vec<HgPathBuf>,
187 174 now: i32,
188 175 ) {
189 176 for filename in filenames {
190 177 if let Some(entry) = self.state_map.get_mut(&filename) {
191 178 if clear_ambiguous_mtime(entry, now) {
192 179 self.get_non_normal_other_parent_entries()
193 180 .0
194 181 .insert(filename.to_owned());
195 182 }
196 183 }
197 184 }
198 185 }
199 186
200 187 pub fn non_normal_entries_remove(&mut self, key: impl AsRef<HgPath>) {
201 188 self.get_non_normal_other_parent_entries()
202 189 .0
203 190 .remove(key.as_ref());
204 191 }
205 192
206 193 pub fn non_normal_entries_union(
207 194 &mut self,
208 195 other: HashSet<HgPathBuf>,
209 196 ) -> Vec<HgPathBuf> {
210 197 self.get_non_normal_other_parent_entries()
211 198 .0
212 199 .union(&other)
213 200 .map(ToOwned::to_owned)
214 201 .collect()
215 202 }
216 203
217 204 pub fn get_non_normal_other_parent_entries(
218 205 &mut self,
219 206 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
220 207 self.set_non_normal_other_parent_entries(false);
221 208 (
222 209 self.non_normal_set.as_mut().unwrap(),
223 210 self.other_parent_set.as_mut().unwrap(),
224 211 )
225 212 }
226 213
227 214 /// Useful to get immutable references to those sets in contexts where
228 215 /// you only have an immutable reference to the `DirstateMap`, like when
229 216 /// sharing references with Python.
230 217 ///
231 218 /// TODO, get rid of this along with the other "setter/getter" stuff when
232 219 /// a nice typestate plan is defined.
233 220 ///
234 221 /// # Panics
235 222 ///
236 223 /// Will panic if either set is `None`.
237 224 pub fn get_non_normal_other_parent_entries_panic(
238 225 &self,
239 226 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
240 227 (
241 228 self.non_normal_set.as_ref().unwrap(),
242 229 self.other_parent_set.as_ref().unwrap(),
243 230 )
244 231 }
245 232
246 233 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
247 234 if !force
248 235 && self.non_normal_set.is_some()
249 236 && self.other_parent_set.is_some()
250 237 {
251 238 return;
252 239 }
253 240 let mut non_normal = HashSet::new();
254 241 let mut other_parent = HashSet::new();
255 242
256 243 for (filename, entry) in self.state_map.iter() {
257 244 if entry.is_non_normal() {
258 245 non_normal.insert(filename.to_owned());
259 246 }
260 247 if entry.is_from_other_parent() {
261 248 other_parent.insert(filename.to_owned());
262 249 }
263 250 }
264 251 self.non_normal_set = Some(non_normal);
265 252 self.other_parent_set = Some(other_parent);
266 253 }
267 254
268 255 /// Both of these setters and their uses appear to be the simplest way to
269 256 /// emulate a Python lazy property, but it is ugly and unidiomatic.
270 257 /// TODO One day, rewriting this struct using the typestate might be a
271 258 /// good idea.
272 259 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
273 260 if self.all_dirs.is_none() {
274 261 self.all_dirs = Some(DirsMultiset::from_dirstate(
275 262 self.state_map.iter(),
276 263 None,
277 264 )?);
278 265 }
279 266 Ok(())
280 267 }
281 268
282 269 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
283 270 if self.dirs.is_none() {
284 271 self.dirs = Some(DirsMultiset::from_dirstate(
285 272 &self.state_map,
286 273 Some(EntryState::Removed),
287 274 )?);
288 275 }
289 276 Ok(())
290 277 }
291 278
292 279 pub fn has_tracked_dir(
293 280 &mut self,
294 281 directory: &HgPath,
295 282 ) -> Result<bool, DirstateMapError> {
296 283 self.set_dirs()?;
297 284 Ok(self.dirs.as_ref().unwrap().contains(directory))
298 285 }
299 286
300 287 pub fn has_dir(
301 288 &mut self,
302 289 directory: &HgPath,
303 290 ) -> Result<bool, DirstateMapError> {
304 291 self.set_all_dirs()?;
305 292 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
306 293 }
307 294
308 295 pub fn parents(
309 296 &mut self,
310 297 file_contents: &[u8],
311 298 ) -> Result<&DirstateParents, DirstateError> {
312 299 if let Some(ref parents) = self.parents {
313 300 return Ok(parents);
314 301 }
315 302 let parents;
316 303 if file_contents.len() == PARENT_SIZE * 2 {
317 304 parents = DirstateParents {
318 305 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
319 306 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
320 307 .try_into()
321 308 .unwrap(),
322 309 };
323 310 } else if file_contents.is_empty() {
324 311 parents = DirstateParents {
325 312 p1: NULL_NODE,
326 313 p2: NULL_NODE,
327 314 };
328 315 } else {
329 316 return Err(
330 317 HgError::corrupted("Dirstate appears to be damaged").into()
331 318 );
332 319 }
333 320
334 321 self.parents = Some(parents);
335 322 Ok(self.parents.as_ref().unwrap())
336 323 }
337 324
338 325 pub fn set_parents(&mut self, parents: &DirstateParents) {
339 326 self.parents = Some(parents.clone());
340 327 self.dirty_parents = true;
341 328 }
342 329
343 330 #[timed]
344 331 pub fn read<'a>(
345 332 &mut self,
346 333 file_contents: &'a [u8],
347 334 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
348 335 if file_contents.is_empty() {
349 336 return Ok(None);
350 337 }
351 338
352 339 let (parents, entries, copies) = parse_dirstate(file_contents)?;
353 340 self.state_map.extend(
354 341 entries
355 342 .into_iter()
356 343 .map(|(path, entry)| (path.to_owned(), entry)),
357 344 );
358 345 self.copy_map.extend(
359 346 copies
360 347 .into_iter()
361 348 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
362 349 );
363 350
364 351 if !self.dirty_parents {
365 352 self.set_parents(&parents);
366 353 }
367 354
368 355 Ok(Some(parents))
369 356 }
370 357
371 358 pub fn pack(
372 359 &mut self,
373 360 parents: DirstateParents,
374 361 now: Timestamp,
375 362 ) -> Result<Vec<u8>, DirstateError> {
376 363 let packed =
377 364 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
378 365
379 366 self.dirty_parents = false;
380 367
381 368 self.set_non_normal_other_parent_entries(true);
382 369 Ok(packed)
383 370 }
384 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
385 if let Some(ref file_fold_map) = self.file_fold_map {
386 return file_fold_map;
387 }
388 let mut new_file_fold_map = FileFoldMap::default();
389
390 for (filename, DirstateEntry { state, .. }) in self.state_map.iter() {
391 if *state != EntryState::Removed {
392 new_file_fold_map
393 .insert(normalize_case(&filename), filename.to_owned());
394 }
395 }
396 self.file_fold_map = Some(new_file_fold_map);
397 self.file_fold_map.as_ref().unwrap()
398 }
399 371 }
400 372
401 373 #[cfg(test)]
402 374 mod tests {
403 375 use super::*;
404 376
405 377 #[test]
406 378 fn test_dirs_multiset() {
407 379 let mut map = DirstateMap::new();
408 380 assert!(map.dirs.is_none());
409 381 assert!(map.all_dirs.is_none());
410 382
411 383 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
412 384 assert!(map.all_dirs.is_some());
413 385 assert!(map.dirs.is_none());
414 386
415 387 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
416 388 assert!(map.dirs.is_some());
417 389 }
418 390
419 391 #[test]
420 392 fn test_add_file() {
421 393 let mut map = DirstateMap::new();
422 394
423 395 assert_eq!(0, map.len());
424 396
425 397 map.add_file(
426 398 HgPath::new(b"meh"),
427 399 EntryState::Normal,
428 400 DirstateEntry {
429 401 state: EntryState::Normal,
430 402 mode: 1337,
431 403 mtime: 1337,
432 404 size: 1337,
433 405 },
434 406 )
435 407 .unwrap();
436 408
437 409 assert_eq!(1, map.len());
438 410 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
439 411 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
440 412 }
441 413
442 414 #[test]
443 415 fn test_non_normal_other_parent_entries() {
444 416 let mut map: DirstateMap = [
445 417 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
446 418 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
447 419 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
448 420 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
449 421 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
450 422 (b"f6", (EntryState::Added, 1337, 1337, -1)),
451 423 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
452 424 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
453 425 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
454 426 (b"fa", (EntryState::Added, 1337, -2, 1337)),
455 427 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
456 428 ]
457 429 .iter()
458 430 .map(|(fname, (state, mode, size, mtime))| {
459 431 (
460 432 HgPathBuf::from_bytes(fname.as_ref()),
461 433 DirstateEntry {
462 434 state: *state,
463 435 mode: *mode,
464 436 size: *size,
465 437 mtime: *mtime,
466 438 },
467 439 )
468 440 })
469 441 .collect();
470 442
471 443 let mut non_normal = [
472 444 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
473 445 ]
474 446 .iter()
475 447 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
476 448 .collect();
477 449
478 450 let mut other_parent = HashSet::new();
479 451 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
480 452 let entries = map.get_non_normal_other_parent_entries();
481 453
482 454 assert_eq!(
483 455 (&mut non_normal, &mut other_parent),
484 456 (entries.0, entries.1)
485 457 );
486 458 }
487 459 }
@@ -1,663 +1,658 b''
1 1 use bytes_cast::BytesCast;
2 2 use std::path::PathBuf;
3 3 use std::{collections::BTreeMap, convert::TryInto};
4 4
5 5 use super::path_with_basename::WithBasename;
6 6 use crate::dirstate::parsers::clear_ambiguous_mtime;
7 7 use crate::dirstate::parsers::pack_entry;
8 8 use crate::dirstate::parsers::packed_entry_size;
9 9 use crate::dirstate::parsers::parse_dirstate_entries;
10 10 use crate::dirstate::parsers::parse_dirstate_parents;
11 11 use crate::dirstate::parsers::Timestamp;
12 12 use crate::matchers::Matcher;
13 13 use crate::revlog::node::NULL_NODE;
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 use crate::FastHashMap;
23 22 use crate::HgPathCow;
24 23 use crate::PatternFileWarning;
25 24 use crate::StateMapIter;
26 25 use crate::StatusError;
27 26 use crate::StatusOptions;
28 27
29 28 pub struct DirstateMap {
30 29 parents: Option<DirstateParents>,
31 30 dirty_parents: bool,
32 31 root: ChildNodes,
33 32
34 33 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
35 34 nodes_with_entry_count: usize,
36 35
37 36 /// Number of nodes anywhere in the tree that have
38 37 /// `.copy_source.is_some()`.
39 38 nodes_with_copy_source_count: usize,
40 39 }
41 40
42 41 /// Using a plain `HgPathBuf` of the full path from the repository root as a
43 42 /// map key would also work: all paths in a given map have the same parent
44 43 /// path, so comparing full paths gives the same result as comparing base
45 44 /// names. However `BTreeMap` would waste time always re-comparing the same
46 45 /// string prefix.
47 46 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
48 47
49 48 /// Represents a file or a directory
50 49 #[derive(Default)]
51 50 struct Node {
52 51 /// `None` for directories
53 52 entry: Option<DirstateEntry>,
54 53
55 54 copy_source: Option<HgPathBuf>,
56 55
57 56 children: ChildNodes,
58 57
59 58 /// How many (non-inclusive) descendants of this node are tracked files
60 59 tracked_descendants_count: usize,
61 60 }
62 61
63 62 impl Node {
64 63 /// Whether this node has a `DirstateEntry` with `.state.is_tracked()`
65 64 fn is_tracked_file(&self) -> bool {
66 65 if let Some(entry) = &self.entry {
67 66 entry.state.is_tracked()
68 67 } else {
69 68 false
70 69 }
71 70 }
72 71 }
73 72
74 73 /// `(full_path, entry, copy_source)`
75 74 type NodeDataMut<'a> = (
76 75 &'a WithBasename<HgPathBuf>,
77 76 &'a mut Option<DirstateEntry>,
78 77 &'a mut Option<HgPathBuf>,
79 78 );
80 79
81 80 impl DirstateMap {
82 81 pub fn new() -> Self {
83 82 Self {
84 83 parents: None,
85 84 dirty_parents: false,
86 85 root: ChildNodes::new(),
87 86 nodes_with_entry_count: 0,
88 87 nodes_with_copy_source_count: 0,
89 88 }
90 89 }
91 90
92 91 fn get_node(&self, path: &HgPath) -> Option<&Node> {
93 92 let mut children = &self.root;
94 93 let mut components = path.components();
95 94 let mut component =
96 95 components.next().expect("expected at least one components");
97 96 loop {
98 97 let child = children.get(component)?;
99 98 if let Some(next_component) = components.next() {
100 99 component = next_component;
101 100 children = &child.children;
102 101 } else {
103 102 return Some(child);
104 103 }
105 104 }
106 105 }
107 106
108 107 /// Returns a mutable reference to the node at `path` if it exists
109 108 ///
110 109 /// This takes `root` instead of `&mut self` so that callers can mutate
111 110 /// other fields while the returned borrow is still valid
112 111 fn get_node_mut<'tree>(
113 112 root: &'tree mut ChildNodes,
114 113 path: &HgPath,
115 114 ) -> Option<&'tree mut Node> {
116 115 Self::each_and_get(root, path, |_| {})
117 116 }
118 117
119 118 /// Call `each` for each ancestor node of the one at `path` (not including
120 119 /// that node itself), starting from nearest the root.
121 120 ///
122 121 /// Panics (possibly after some calls to `each`) if there is no node at
123 122 /// `path`.
124 123 fn for_each_ancestor_node<'tree>(
125 124 &mut self,
126 125 path: &HgPath,
127 126 each: impl FnMut(&mut Node),
128 127 ) {
129 128 let parent = path.parent();
130 129 if !parent.is_empty() {
131 130 Self::each_and_get(&mut self.root, parent, each)
132 131 .expect("missing dirstate node");
133 132 }
134 133 }
135 134
136 135 /// Common implementation detail of `get_node_mut` and
137 136 /// `for_each_ancestor_node`
138 137 fn each_and_get<'tree>(
139 138 root: &'tree mut ChildNodes,
140 139 path: &HgPath,
141 140 mut each: impl FnMut(&mut Node),
142 141 ) -> Option<&'tree mut Node> {
143 142 let mut children = root;
144 143 let mut components = path.components();
145 144 let mut component =
146 145 components.next().expect("expected at least one components");
147 146 loop {
148 147 let child = children.get_mut(component)?;
149 148 each(child);
150 149 if let Some(next_component) = components.next() {
151 150 component = next_component;
152 151 children = &mut child.children;
153 152 } else {
154 153 return Some(child);
155 154 }
156 155 }
157 156 }
158 157
159 158 fn get_or_insert_node<'tree>(
160 159 root: &'tree mut ChildNodes,
161 160 path: &HgPath,
162 161 ) -> &'tree mut Node {
163 162 let mut child_nodes = root;
164 163 let mut inclusive_ancestor_paths =
165 164 WithBasename::inclusive_ancestors_of(path);
166 165 let mut ancestor_path = inclusive_ancestor_paths
167 166 .next()
168 167 .expect("expected at least one inclusive ancestor");
169 168 loop {
170 169 // TODO: can we avoid double lookup in all cases without allocating
171 170 // an owned key in cases where the map already contains that key?
172 171 let child_node =
173 172 if child_nodes.contains_key(ancestor_path.base_name()) {
174 173 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
175 174 } else {
176 175 // This is always a vacant entry, using `.entry()` lets us
177 176 // return a `&mut Node` of the newly-inserted node without
178 177 // yet another lookup. `BTreeMap::insert` doesn’t do this.
179 178 child_nodes.entry(ancestor_path.to_owned()).or_default()
180 179 };
181 180 if let Some(next) = inclusive_ancestor_paths.next() {
182 181 ancestor_path = next;
183 182 child_nodes = &mut child_node.children;
184 183 } else {
185 184 return child_node;
186 185 }
187 186 }
188 187 }
189 188
190 189 /// The meaning of `new_copy_source` is:
191 190 ///
192 191 /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)`
193 192 /// * `Some(None)`: set `Node::copy_source` to `None`
194 193 /// * `None`: leave `Node::copy_source` unchanged
195 194 fn add_file_node(
196 195 &mut self,
197 196 path: &HgPath,
198 197 new_entry: DirstateEntry,
199 198 new_copy_source: Option<Option<HgPathBuf>>,
200 199 ) {
201 200 let node = Self::get_or_insert_node(&mut self.root, path);
202 201 if node.entry.is_none() {
203 202 self.nodes_with_entry_count += 1
204 203 }
205 204 if let Some(source) = &new_copy_source {
206 205 if node.copy_source.is_none() && source.is_some() {
207 206 self.nodes_with_copy_source_count += 1
208 207 }
209 208 if node.copy_source.is_some() && source.is_none() {
210 209 self.nodes_with_copy_source_count -= 1
211 210 }
212 211 }
213 212 let tracked_count_increment =
214 213 match (node.is_tracked_file(), new_entry.state.is_tracked()) {
215 214 (false, true) => 1,
216 215 (true, false) => -1,
217 216 _ => 0,
218 217 };
219 218
220 219 node.entry = Some(new_entry);
221 220 if let Some(source) = new_copy_source {
222 221 node.copy_source = source
223 222 }
224 223 // Borrow of `self.root` through `node` ends here
225 224
226 225 match tracked_count_increment {
227 226 1 => self.for_each_ancestor_node(path, |node| {
228 227 node.tracked_descendants_count += 1
229 228 }),
230 229 // We can’t use `+= -1` because the counter is unsigned
231 230 -1 => self.for_each_ancestor_node(path, |node| {
232 231 node.tracked_descendants_count -= 1
233 232 }),
234 233 _ => {}
235 234 }
236 235 }
237 236
238 237 fn iter_nodes<'a>(
239 238 &'a self,
240 239 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
241 240 {
242 241 // Depth first tree traversal.
243 242 //
244 243 // If we could afford internal iteration and recursion,
245 244 // this would look like:
246 245 //
247 246 // ```
248 247 // fn traverse_children(
249 248 // children: &ChildNodes,
250 249 // each: &mut impl FnMut(&Node),
251 250 // ) {
252 251 // for child in children.values() {
253 252 // traverse_children(&child.children, each);
254 253 // each(child);
255 254 // }
256 255 // }
257 256 // ```
258 257 //
259 258 // However we want an external iterator and therefore can’t use the
260 259 // call stack. Use an explicit stack instead:
261 260 let mut stack = Vec::new();
262 261 let mut iter = self.root.iter();
263 262 std::iter::from_fn(move || {
264 263 while let Some((key, child_node)) = iter.next() {
265 264 // Pseudo-recursion
266 265 let new_iter = child_node.children.iter();
267 266 let old_iter = std::mem::replace(&mut iter, new_iter);
268 267 stack.push((key, child_node, old_iter));
269 268 }
270 269 // Found the end of a `children.iter()` iterator.
271 270 if let Some((key, child_node, next_iter)) = stack.pop() {
272 271 // "Return" from pseudo-recursion by restoring state from the
273 272 // explicit stack
274 273 iter = next_iter;
275 274
276 275 Some((key, child_node))
277 276 } else {
278 277 // Reached the bottom of the stack, we’re done
279 278 None
280 279 }
281 280 })
282 281 }
283 282
284 283 /// Mutable iterator for the `(entry, copy source)` of each node.
285 284 ///
286 285 /// It would not be safe to yield mutable references to nodes themeselves
287 286 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
288 287 /// reachable from their ancestor nodes, potentially creating multiple
289 288 /// `&mut` references to a given node.
290 289 fn iter_node_data_mut<'a>(
291 290 &'a mut self,
292 291 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
293 292 // Explict stack for pseudo-recursion, see `iter_nodes` above.
294 293 let mut stack = Vec::new();
295 294 let mut iter = self.root.iter_mut();
296 295 std::iter::from_fn(move || {
297 296 while let Some((key, child_node)) = iter.next() {
298 297 // Pseudo-recursion
299 298 let data =
300 299 (key, &mut child_node.entry, &mut child_node.copy_source);
301 300 let new_iter = child_node.children.iter_mut();
302 301 let old_iter = std::mem::replace(&mut iter, new_iter);
303 302 stack.push((data, old_iter));
304 303 }
305 304 // Found the end of a `children.values_mut()` iterator.
306 305 if let Some((data, next_iter)) = stack.pop() {
307 306 // "Return" from pseudo-recursion by restoring state from the
308 307 // explicit stack
309 308 iter = next_iter;
310 309
311 310 Some(data)
312 311 } else {
313 312 // Reached the bottom of the stack, we’re done
314 313 None
315 314 }
316 315 })
317 316 }
318 317 }
319 318
320 319 impl super::dispatch::DirstateMapMethods for DirstateMap {
321 320 fn clear(&mut self) {
322 321 self.set_parents(&DirstateParents {
323 322 p1: NULL_NODE,
324 323 p2: NULL_NODE,
325 324 });
326 325 self.root.clear();
327 326 self.nodes_with_entry_count = 0;
328 327 self.nodes_with_copy_source_count = 0;
329 328 }
330 329
331 330 fn add_file(
332 331 &mut self,
333 332 filename: &HgPath,
334 333 _old_state: EntryState,
335 334 entry: DirstateEntry,
336 335 ) -> Result<(), DirstateMapError> {
337 336 self.add_file_node(filename, entry, None);
338 337 Ok(())
339 338 }
340 339
341 340 fn remove_file(
342 341 &mut self,
343 342 filename: &HgPath,
344 343 _old_state: EntryState,
345 344 size: i32,
346 345 ) -> Result<(), DirstateMapError> {
347 346 let entry = DirstateEntry {
348 347 state: EntryState::Removed,
349 348 mode: 0,
350 349 size,
351 350 mtime: 0,
352 351 };
353 352 self.add_file_node(filename, entry, None);
354 353 Ok(())
355 354 }
356 355
357 356 fn drop_file(
358 357 &mut self,
359 358 filename: &HgPath,
360 359 _old_state: EntryState,
361 360 ) -> Result<bool, DirstateMapError> {
362 361 if let Some(node) = Self::get_node_mut(&mut self.root, filename) {
363 362 let was_tracked = node.is_tracked_file();
364 363 let had_entry = node.entry.is_some();
365 364 let had_copy_source = node.copy_source.is_some();
366 365
367 366 // TODO: this leaves in the tree a "non-file" node. Should we
368 367 // remove the node instead, together with ancestor nodes for
369 368 // directories that become empty?
370 369 node.entry = None;
371 370 node.copy_source = None;
372 371
373 372 if had_entry {
374 373 self.nodes_with_entry_count -= 1
375 374 }
376 375 if had_copy_source {
377 376 self.nodes_with_copy_source_count -= 1
378 377 }
379 378 if was_tracked {
380 379 self.for_each_ancestor_node(filename, |node| {
381 380 node.tracked_descendants_count -= 1
382 381 })
383 382 }
384 383 Ok(had_entry)
385 384 } else {
386 385 Ok(false)
387 386 }
388 387 }
389 388
390 389 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
391 390 for filename in filenames {
392 391 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
393 392 if let Some(entry) = node.entry.as_mut() {
394 393 clear_ambiguous_mtime(entry, now);
395 394 }
396 395 }
397 396 }
398 397 }
399 398
400 399 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
401 400 self.get_node(key)
402 401 .and_then(|node| node.entry.as_ref())
403 402 .map_or(false, DirstateEntry::is_non_normal)
404 403 }
405 404
406 405 fn non_normal_entries_remove(&mut self, _key: &HgPath) {
407 406 // Do nothing, this `DirstateMap` does not have a separate "non normal
408 407 // entries" set that need to be kept up to date
409 408 }
410 409
411 410 fn non_normal_or_other_parent_paths(
412 411 &mut self,
413 412 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
414 413 Box::new(self.iter_nodes().filter_map(|(path, node)| {
415 414 node.entry
416 415 .as_ref()
417 416 .filter(|entry| {
418 417 entry.is_non_normal() || entry.is_from_other_parent()
419 418 })
420 419 .map(|_| path.full_path())
421 420 }))
422 421 }
423 422
424 423 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
425 424 // Do nothing, this `DirstateMap` does not have a separate "non normal
426 425 // entries" and "from other parent" sets that need to be recomputed
427 426 }
428 427
429 428 fn iter_non_normal_paths(
430 429 &mut self,
431 430 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
432 431 self.iter_non_normal_paths_panic()
433 432 }
434 433
435 434 fn iter_non_normal_paths_panic(
436 435 &self,
437 436 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
438 437 Box::new(self.iter_nodes().filter_map(|(path, node)| {
439 438 node.entry
440 439 .as_ref()
441 440 .filter(|entry| entry.is_non_normal())
442 441 .map(|_| path.full_path())
443 442 }))
444 443 }
445 444
446 445 fn iter_other_parent_paths(
447 446 &mut self,
448 447 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
449 448 Box::new(self.iter_nodes().filter_map(|(path, node)| {
450 449 node.entry
451 450 .as_ref()
452 451 .filter(|entry| entry.is_from_other_parent())
453 452 .map(|_| path.full_path())
454 453 }))
455 454 }
456 455
457 456 fn has_tracked_dir(
458 457 &mut self,
459 458 directory: &HgPath,
460 459 ) -> Result<bool, DirstateMapError> {
461 460 if let Some(node) = self.get_node(directory) {
462 461 // A node without a `DirstateEntry` was created to hold child
463 462 // nodes, and is therefore a directory.
464 463 Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
465 464 } else {
466 465 Ok(false)
467 466 }
468 467 }
469 468
470 469 fn has_dir(
471 470 &mut self,
472 471 directory: &HgPath,
473 472 ) -> Result<bool, DirstateMapError> {
474 473 if let Some(node) = self.get_node(directory) {
475 474 // A node without a `DirstateEntry` was created to hold child
476 475 // nodes, and is therefore a directory.
477 476 Ok(node.entry.is_none())
478 477 } else {
479 478 Ok(false)
480 479 }
481 480 }
482 481
483 482 fn parents(
484 483 &mut self,
485 484 file_contents: &[u8],
486 485 ) -> Result<&DirstateParents, DirstateError> {
487 486 if self.parents.is_none() {
488 487 let parents = if !file_contents.is_empty() {
489 488 parse_dirstate_parents(file_contents)?.clone()
490 489 } else {
491 490 DirstateParents {
492 491 p1: NULL_NODE,
493 492 p2: NULL_NODE,
494 493 }
495 494 };
496 495 self.parents = Some(parents);
497 496 }
498 497 Ok(self.parents.as_ref().unwrap())
499 498 }
500 499
501 500 fn set_parents(&mut self, parents: &DirstateParents) {
502 501 self.parents = Some(parents.clone());
503 502 self.dirty_parents = true;
504 503 }
505 504
506 505 fn read<'a>(
507 506 &mut self,
508 507 file_contents: &'a [u8],
509 508 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
510 509 if file_contents.is_empty() {
511 510 return Ok(None);
512 511 }
513 512
514 513 let parents = parse_dirstate_entries(
515 514 file_contents,
516 515 |path, entry, copy_source| {
517 516 self.add_file_node(
518 517 path,
519 518 *entry,
520 519 Some(copy_source.map(HgPath::to_owned)),
521 520 )
522 521 },
523 522 )?;
524 523
525 524 if !self.dirty_parents {
526 525 self.set_parents(parents);
527 526 }
528 527
529 528 Ok(Some(parents))
530 529 }
531 530
532 531 fn pack(
533 532 &mut self,
534 533 parents: DirstateParents,
535 534 now: Timestamp,
536 535 ) -> Result<Vec<u8>, DirstateError> {
537 536 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
538 537 // reallocations
539 538 let mut size = parents.as_bytes().len();
540 539 for (path, node) in self.iter_nodes() {
541 540 if node.entry.is_some() {
542 541 size += packed_entry_size(
543 542 path.full_path(),
544 543 node.copy_source.as_ref(),
545 544 )
546 545 }
547 546 }
548 547
549 548 let mut packed = Vec::with_capacity(size);
550 549 packed.extend(parents.as_bytes());
551 550
552 551 let now: i32 = now.0.try_into().expect("time overflow");
553 552 for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
554 553 if let Some(entry) = opt_entry {
555 554 clear_ambiguous_mtime(entry, now);
556 555 pack_entry(
557 556 path.full_path(),
558 557 entry,
559 558 copy_source.as_ref(),
560 559 &mut packed,
561 560 );
562 561 }
563 562 }
564 563 self.dirty_parents = false;
565 564 Ok(packed)
566 565 }
567 566
568 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
569 todo!()
570 }
571
572 567 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
573 568 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
574 569 // needs to be recomputed
575 570 Ok(())
576 571 }
577 572
578 573 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
579 574 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
580 575 // to be recomputed
581 576 Ok(())
582 577 }
583 578
584 579 fn status<'a>(
585 580 &'a self,
586 581 _matcher: &'a (dyn Matcher + Sync),
587 582 _root_dir: PathBuf,
588 583 _ignore_files: Vec<PathBuf>,
589 584 _options: StatusOptions,
590 585 ) -> Result<
591 586 (
592 587 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
593 588 Vec<PatternFileWarning>,
594 589 ),
595 590 StatusError,
596 591 > {
597 592 todo!()
598 593 }
599 594
600 595 fn copy_map_len(&self) -> usize {
601 596 self.nodes_with_copy_source_count
602 597 }
603 598
604 599 fn copy_map_iter(&self) -> CopyMapIter<'_> {
605 600 Box::new(self.iter_nodes().filter_map(|(path, node)| {
606 601 node.copy_source
607 602 .as_ref()
608 603 .map(|copy_source| (path.full_path(), copy_source))
609 604 }))
610 605 }
611 606
612 607 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
613 608 if let Some(node) = self.get_node(key) {
614 609 node.copy_source.is_some()
615 610 } else {
616 611 false
617 612 }
618 613 }
619 614
620 615 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
621 616 self.get_node(key)?.copy_source.as_ref()
622 617 }
623 618
624 619 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
625 620 let count = &mut self.nodes_with_copy_source_count;
626 621 Self::get_node_mut(&mut self.root, key).and_then(|node| {
627 622 if node.copy_source.is_some() {
628 623 *count -= 1
629 624 }
630 625 node.copy_source.take()
631 626 })
632 627 }
633 628
634 629 fn copy_map_insert(
635 630 &mut self,
636 631 key: HgPathBuf,
637 632 value: HgPathBuf,
638 633 ) -> Option<HgPathBuf> {
639 634 let node = Self::get_or_insert_node(&mut self.root, &key);
640 635 if node.copy_source.is_none() {
641 636 self.nodes_with_copy_source_count += 1
642 637 }
643 638 node.copy_source.replace(value)
644 639 }
645 640
646 641 fn len(&self) -> usize {
647 642 self.nodes_with_entry_count
648 643 }
649 644
650 645 fn contains_key(&self, key: &HgPath) -> bool {
651 646 self.get(key).is_some()
652 647 }
653 648
654 649 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
655 650 self.get_node(key)?.entry.as_ref()
656 651 }
657 652
658 653 fn iter(&self) -> StateMapIter<'_> {
659 654 Box::new(self.iter_nodes().filter_map(|(path, node)| {
660 655 node.entry.as_ref().map(|entry| (path.full_path(), entry))
661 656 }))
662 657 }
663 658 }
@@ -1,333 +1,326 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 use crate::FastHashMap;
15 14 use crate::HgPathCow;
16 15 use crate::PatternFileWarning;
17 16 use crate::StateMapIter;
18 17 use crate::StatusError;
19 18 use crate::StatusOptions;
20 19
21 20 pub trait DirstateMapMethods {
22 21 fn clear(&mut self);
23 22
24 23 fn add_file(
25 24 &mut self,
26 25 filename: &HgPath,
27 26 old_state: EntryState,
28 27 entry: DirstateEntry,
29 28 ) -> Result<(), DirstateMapError>;
30 29
31 30 fn remove_file(
32 31 &mut self,
33 32 filename: &HgPath,
34 33 old_state: EntryState,
35 34 size: i32,
36 35 ) -> Result<(), DirstateMapError>;
37 36
38 37 fn drop_file(
39 38 &mut self,
40 39 filename: &HgPath,
41 40 old_state: EntryState,
42 41 ) -> Result<bool, DirstateMapError>;
43 42
44 43 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32);
45 44
46 45 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool;
47 46
48 47 fn non_normal_entries_remove(&mut self, key: &HgPath);
49 48
50 49 fn non_normal_or_other_parent_paths(
51 50 &mut self,
52 51 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_>;
53 52
54 53 fn set_non_normal_other_parent_entries(&mut self, force: bool);
55 54
56 55 fn iter_non_normal_paths(
57 56 &mut self,
58 57 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
59 58
60 59 fn iter_non_normal_paths_panic(
61 60 &self,
62 61 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
63 62
64 63 fn iter_other_parent_paths(
65 64 &mut self,
66 65 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
67 66
68 67 fn has_tracked_dir(
69 68 &mut self,
70 69 directory: &HgPath,
71 70 ) -> Result<bool, DirstateMapError>;
72 71
73 72 fn has_dir(
74 73 &mut self,
75 74 directory: &HgPath,
76 75 ) -> Result<bool, DirstateMapError>;
77 76
78 77 fn parents(
79 78 &mut self,
80 79 file_contents: &[u8],
81 80 ) -> Result<&DirstateParents, DirstateError>;
82 81
83 82 fn set_parents(&mut self, parents: &DirstateParents);
84 83
85 84 fn read<'a>(
86 85 &mut self,
87 86 file_contents: &'a [u8],
88 87 ) -> Result<Option<&'a DirstateParents>, DirstateError>;
89 88
90 89 fn pack(
91 90 &mut self,
92 91 parents: DirstateParents,
93 92 now: Timestamp,
94 93 ) -> Result<Vec<u8>, DirstateError>;
95 94
96 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf>;
97
98 95 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError>;
99 96
100 97 fn set_dirs(&mut self) -> Result<(), DirstateMapError>;
101 98
102 99 fn status<'a>(
103 100 &'a self,
104 101 matcher: &'a (dyn Matcher + Sync),
105 102 root_dir: PathBuf,
106 103 ignore_files: Vec<PathBuf>,
107 104 options: StatusOptions,
108 105 ) -> Result<
109 106 (
110 107 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
111 108 Vec<PatternFileWarning>,
112 109 ),
113 110 StatusError,
114 111 >;
115 112
116 113 fn copy_map_len(&self) -> usize;
117 114
118 115 fn copy_map_iter(&self) -> CopyMapIter<'_>;
119 116
120 117 fn copy_map_contains_key(&self, key: &HgPath) -> bool;
121 118
122 119 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf>;
123 120
124 121 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf>;
125 122
126 123 fn copy_map_insert(
127 124 &mut self,
128 125 key: HgPathBuf,
129 126 value: HgPathBuf,
130 127 ) -> Option<HgPathBuf>;
131 128
132 129 fn len(&self) -> usize;
133 130
134 131 fn contains_key(&self, key: &HgPath) -> bool;
135 132
136 133 fn get(&self, key: &HgPath) -> Option<&DirstateEntry>;
137 134
138 135 fn iter(&self) -> StateMapIter<'_>;
139 136 }
140 137
141 138 impl DirstateMapMethods for DirstateMap {
142 139 fn clear(&mut self) {
143 140 self.clear()
144 141 }
145 142
146 143 fn add_file(
147 144 &mut self,
148 145 filename: &HgPath,
149 146 old_state: EntryState,
150 147 entry: DirstateEntry,
151 148 ) -> Result<(), DirstateMapError> {
152 149 self.add_file(filename, old_state, entry)
153 150 }
154 151
155 152 fn remove_file(
156 153 &mut self,
157 154 filename: &HgPath,
158 155 old_state: EntryState,
159 156 size: i32,
160 157 ) -> Result<(), DirstateMapError> {
161 158 self.remove_file(filename, old_state, size)
162 159 }
163 160
164 161 fn drop_file(
165 162 &mut self,
166 163 filename: &HgPath,
167 164 old_state: EntryState,
168 165 ) -> Result<bool, DirstateMapError> {
169 166 self.drop_file(filename, old_state)
170 167 }
171 168
172 169 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
173 170 self.clear_ambiguous_times(filenames, now)
174 171 }
175 172
176 173 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
177 174 let (non_normal, _other_parent) =
178 175 self.get_non_normal_other_parent_entries();
179 176 non_normal.contains(key)
180 177 }
181 178
182 179 fn non_normal_entries_remove(&mut self, key: &HgPath) {
183 180 self.non_normal_entries_remove(key)
184 181 }
185 182
186 183 fn non_normal_or_other_parent_paths(
187 184 &mut self,
188 185 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
189 186 let (non_normal, other_parent) =
190 187 self.get_non_normal_other_parent_entries();
191 188 Box::new(non_normal.union(other_parent))
192 189 }
193 190
194 191 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
195 192 self.set_non_normal_other_parent_entries(force)
196 193 }
197 194
198 195 fn iter_non_normal_paths(
199 196 &mut self,
200 197 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
201 198 let (non_normal, _other_parent) =
202 199 self.get_non_normal_other_parent_entries();
203 200 Box::new(non_normal.iter())
204 201 }
205 202
206 203 fn iter_non_normal_paths_panic(
207 204 &self,
208 205 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
209 206 let (non_normal, _other_parent) =
210 207 self.get_non_normal_other_parent_entries_panic();
211 208 Box::new(non_normal.iter())
212 209 }
213 210
214 211 fn iter_other_parent_paths(
215 212 &mut self,
216 213 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
217 214 let (_non_normal, other_parent) =
218 215 self.get_non_normal_other_parent_entries();
219 216 Box::new(other_parent.iter())
220 217 }
221 218
222 219 fn has_tracked_dir(
223 220 &mut self,
224 221 directory: &HgPath,
225 222 ) -> Result<bool, DirstateMapError> {
226 223 self.has_tracked_dir(directory)
227 224 }
228 225
229 226 fn has_dir(
230 227 &mut self,
231 228 directory: &HgPath,
232 229 ) -> Result<bool, DirstateMapError> {
233 230 self.has_dir(directory)
234 231 }
235 232
236 233 fn parents(
237 234 &mut self,
238 235 file_contents: &[u8],
239 236 ) -> Result<&DirstateParents, DirstateError> {
240 237 self.parents(file_contents)
241 238 }
242 239
243 240 fn set_parents(&mut self, parents: &DirstateParents) {
244 241 self.set_parents(parents)
245 242 }
246 243
247 244 fn read<'a>(
248 245 &mut self,
249 246 file_contents: &'a [u8],
250 247 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
251 248 self.read(file_contents)
252 249 }
253 250
254 251 fn pack(
255 252 &mut self,
256 253 parents: DirstateParents,
257 254 now: Timestamp,
258 255 ) -> Result<Vec<u8>, DirstateError> {
259 256 self.pack(parents, now)
260 257 }
261 258
262 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
263 self.build_file_fold_map()
264 }
265
266 259 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
267 260 self.set_all_dirs()
268 261 }
269 262
270 263 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
271 264 self.set_dirs()
272 265 }
273 266
274 267 fn status<'a>(
275 268 &'a self,
276 269 matcher: &'a (dyn Matcher + Sync),
277 270 root_dir: PathBuf,
278 271 ignore_files: Vec<PathBuf>,
279 272 options: StatusOptions,
280 273 ) -> Result<
281 274 (
282 275 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
283 276 Vec<PatternFileWarning>,
284 277 ),
285 278 StatusError,
286 279 > {
287 280 crate::status(self, matcher, root_dir, ignore_files, options)
288 281 }
289 282
290 283 fn copy_map_len(&self) -> usize {
291 284 self.copy_map.len()
292 285 }
293 286
294 287 fn copy_map_iter(&self) -> CopyMapIter<'_> {
295 288 Box::new(self.copy_map.iter())
296 289 }
297 290
298 291 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
299 292 self.copy_map.contains_key(key)
300 293 }
301 294
302 295 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
303 296 self.copy_map.get(key)
304 297 }
305 298
306 299 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
307 300 self.copy_map.remove(key)
308 301 }
309 302
310 303 fn copy_map_insert(
311 304 &mut self,
312 305 key: HgPathBuf,
313 306 value: HgPathBuf,
314 307 ) -> Option<HgPathBuf> {
315 308 self.copy_map.insert(key, value)
316 309 }
317 310
318 311 fn len(&self) -> usize {
319 312 (&**self).len()
320 313 }
321 314
322 315 fn contains_key(&self, key: &HgPath) -> bool {
323 316 (&**self).contains_key(key)
324 317 }
325 318
326 319 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
327 320 (&**self).get(key)
328 321 }
329 322
330 323 fn iter(&self) -> StateMapIter<'_> {
331 324 Box::new((&**self).iter())
332 325 }
333 326 }
@@ -1,571 +1,574 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::{Ref, RefCell};
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, PyTuple, Python, PythonObject,
17 17 ToPyObject, 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::{dirs_multiset::Dirs, make_dirstate_tuple},
26 26 parsers::dirstate_parents_to_pytuple,
27 27 };
28 28 use hg::{
29 29 dirstate::parsers::Timestamp,
30 30 dirstate_tree::dispatch::DirstateMapMethods,
31 31 errors::HgError,
32 32 revlog::Node,
33 utils::files::normalize_case,
33 34 utils::hg_path::{HgPath, HgPathBuf},
34 35 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
35 36 DirstateMapError, DirstateParents, EntryState, StateMapIter,
36 37 };
37 38
38 39 // TODO
39 40 // This object needs to share references to multiple members of its Rust
40 41 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
41 42 // Right now `CopyMap` is done, but it needs to have an explicit reference
42 43 // to `RustDirstateMap` which itself needs to have an encapsulation for
43 44 // every method in `CopyMap` (copymapcopy, etc.).
44 45 // This is ugly and hard to maintain.
45 46 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
46 47 // `py_class!` is already implemented and does not mention
47 48 // `RustDirstateMap`, rightfully so.
48 49 // All attributes also have to have a separate refcount data attribute for
49 50 // leaks, with all methods that go along for reference sharing.
50 51 py_class!(pub class DirstateMap |py| {
51 52 @shared data inner: Box<dyn DirstateMapMethods + Send>;
52 53
53 54 def __new__(_cls, use_dirstate_tree: bool) -> PyResult<Self> {
54 55 let inner = if use_dirstate_tree {
55 56 Box::new(hg::dirstate_tree::dirstate_map::DirstateMap::new()) as _
56 57 } else {
57 58 Box::new(RustDirstateMap::default()) as _
58 59 };
59 60 Self::create_instance(py, inner)
60 61 }
61 62
62 63 def clear(&self) -> PyResult<PyObject> {
63 64 self.inner(py).borrow_mut().clear();
64 65 Ok(py.None())
65 66 }
66 67
67 68 def get(
68 69 &self,
69 70 key: PyObject,
70 71 default: Option<PyObject> = None
71 72 ) -> PyResult<Option<PyObject>> {
72 73 let key = key.extract::<PyBytes>(py)?;
73 74 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
74 75 Some(entry) => {
75 76 Ok(Some(make_dirstate_tuple(py, entry)?))
76 77 },
77 78 None => Ok(default)
78 79 }
79 80 }
80 81
81 82 def addfile(
82 83 &self,
83 84 f: PyObject,
84 85 oldstate: PyObject,
85 86 state: PyObject,
86 87 mode: PyObject,
87 88 size: PyObject,
88 89 mtime: PyObject
89 90 ) -> PyResult<PyObject> {
90 91 self.inner(py).borrow_mut().add_file(
91 92 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
92 93 oldstate.extract::<PyBytes>(py)?.data(py)[0]
93 94 .try_into()
94 95 .map_err(|e: HgError| {
95 96 PyErr::new::<exc::ValueError, _>(py, e.to_string())
96 97 })?,
97 98 DirstateEntry {
98 99 state: state.extract::<PyBytes>(py)?.data(py)[0]
99 100 .try_into()
100 101 .map_err(|e: HgError| {
101 102 PyErr::new::<exc::ValueError, _>(py, e.to_string())
102 103 })?,
103 104 mode: mode.extract(py)?,
104 105 size: size.extract(py)?,
105 106 mtime: mtime.extract(py)?,
106 107 },
107 108 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
108 109 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
109 110 })
110 111 }
111 112
112 113 def removefile(
113 114 &self,
114 115 f: PyObject,
115 116 oldstate: PyObject,
116 117 size: PyObject
117 118 ) -> PyResult<PyObject> {
118 119 self.inner(py).borrow_mut()
119 120 .remove_file(
120 121 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
121 122 oldstate.extract::<PyBytes>(py)?.data(py)[0]
122 123 .try_into()
123 124 .map_err(|e: HgError| {
124 125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
125 126 })?,
126 127 size.extract(py)?,
127 128 )
128 129 .or_else(|_| {
129 130 Err(PyErr::new::<exc::OSError, _>(
130 131 py,
131 132 "Dirstate error".to_string(),
132 133 ))
133 134 })?;
134 135 Ok(py.None())
135 136 }
136 137
137 138 def dropfile(
138 139 &self,
139 140 f: PyObject,
140 141 oldstate: PyObject
141 142 ) -> PyResult<PyBool> {
142 143 self.inner(py).borrow_mut()
143 144 .drop_file(
144 145 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
145 146 oldstate.extract::<PyBytes>(py)?.data(py)[0]
146 147 .try_into()
147 148 .map_err(|e: HgError| {
148 149 PyErr::new::<exc::ValueError, _>(py, e.to_string())
149 150 })?,
150 151 )
151 152 .and_then(|b| Ok(b.to_py_object(py)))
152 153 .or_else(|e| {
153 154 Err(PyErr::new::<exc::OSError, _>(
154 155 py,
155 156 format!("Dirstate error: {}", e.to_string()),
156 157 ))
157 158 })
158 159 }
159 160
160 161 def clearambiguoustimes(
161 162 &self,
162 163 files: PyObject,
163 164 now: PyObject
164 165 ) -> PyResult<PyObject> {
165 166 let files: PyResult<Vec<HgPathBuf>> = files
166 167 .iter(py)?
167 168 .map(|filename| {
168 169 Ok(HgPathBuf::from_bytes(
169 170 filename?.extract::<PyBytes>(py)?.data(py),
170 171 ))
171 172 })
172 173 .collect();
173 174 self.inner(py).borrow_mut()
174 175 .clear_ambiguous_times(files?, now.extract(py)?);
175 176 Ok(py.None())
176 177 }
177 178
178 179 def other_parent_entries(&self) -> PyResult<PyObject> {
179 180 let mut inner_shared = self.inner(py).borrow_mut();
180 181 let set = PySet::empty(py)?;
181 182 for path in inner_shared.iter_other_parent_paths() {
182 183 set.add(py, PyBytes::new(py, path.as_bytes()))?;
183 184 }
184 185 Ok(set.into_object())
185 186 }
186 187
187 188 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
188 189 NonNormalEntries::from_inner(py, self.clone_ref(py))
189 190 }
190 191
191 192 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
192 193 let key = key.extract::<PyBytes>(py)?;
193 194 Ok(self
194 195 .inner(py)
195 196 .borrow_mut()
196 197 .non_normal_entries_contains(HgPath::new(key.data(py))))
197 198 }
198 199
199 200 def non_normal_entries_display(&self) -> PyResult<PyString> {
200 201 Ok(
201 202 PyString::new(
202 203 py,
203 204 &format!(
204 205 "NonNormalEntries: {}",
205 206 hg::utils::join_display(
206 207 self
207 208 .inner(py)
208 209 .borrow_mut()
209 210 .iter_non_normal_paths(),
210 211 ", "
211 212 )
212 213 )
213 214 )
214 215 )
215 216 }
216 217
217 218 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
218 219 let key = key.extract::<PyBytes>(py)?;
219 220 self
220 221 .inner(py)
221 222 .borrow_mut()
222 223 .non_normal_entries_remove(HgPath::new(key.data(py)));
223 224 Ok(py.None())
224 225 }
225 226
226 227 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
227 228 let mut inner = self.inner(py).borrow_mut();
228 229
229 230 let ret = PyList::new(py, &[]);
230 231 for filename in inner.non_normal_or_other_parent_paths() {
231 232 let as_pystring = PyBytes::new(py, filename.as_bytes());
232 233 ret.append(py, as_pystring.into_object());
233 234 }
234 235 Ok(ret)
235 236 }
236 237
237 238 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
238 239 // Make sure the sets are defined before we no longer have a mutable
239 240 // reference to the dmap.
240 241 self.inner(py)
241 242 .borrow_mut()
242 243 .set_non_normal_other_parent_entries(false);
243 244
244 245 let leaked_ref = self.inner(py).leak_immutable();
245 246
246 247 NonNormalEntriesIterator::from_inner(py, unsafe {
247 248 leaked_ref.map(py, |o| {
248 249 o.iter_non_normal_paths_panic()
249 250 })
250 251 })
251 252 }
252 253
253 254 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
254 255 let d = d.extract::<PyBytes>(py)?;
255 256 Ok(self.inner(py).borrow_mut()
256 257 .has_tracked_dir(HgPath::new(d.data(py)))
257 258 .map_err(|e| {
258 259 PyErr::new::<exc::ValueError, _>(py, e.to_string())
259 260 })?
260 261 .to_py_object(py))
261 262 }
262 263
263 264 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
264 265 let d = d.extract::<PyBytes>(py)?;
265 266 Ok(self.inner(py).borrow_mut()
266 267 .has_dir(HgPath::new(d.data(py)))
267 268 .map_err(|e| {
268 269 PyErr::new::<exc::ValueError, _>(py, e.to_string())
269 270 })?
270 271 .to_py_object(py))
271 272 }
272 273
273 274 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
274 275 self.inner(py).borrow_mut()
275 276 .parents(st.extract::<PyBytes>(py)?.data(py))
276 277 .map(|parents| dirstate_parents_to_pytuple(py, parents))
277 278 .or_else(|_| {
278 279 Err(PyErr::new::<exc::OSError, _>(
279 280 py,
280 281 "Dirstate error".to_string(),
281 282 ))
282 283 })
283 284 }
284 285
285 286 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
286 287 let p1 = extract_node_id(py, &p1)?;
287 288 let p2 = extract_node_id(py, &p2)?;
288 289
289 290 self.inner(py).borrow_mut()
290 291 .set_parents(&DirstateParents { p1, p2 });
291 292 Ok(py.None())
292 293 }
293 294
294 295 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
295 296 match self.inner(py).borrow_mut()
296 297 .read(st.extract::<PyBytes>(py)?.data(py))
297 298 {
298 299 Ok(Some(parents)) => Ok(Some(
299 300 dirstate_parents_to_pytuple(py, parents)
300 301 .into_object()
301 302 )),
302 303 Ok(None) => Ok(Some(py.None())),
303 304 Err(_) => Err(PyErr::new::<exc::OSError, _>(
304 305 py,
305 306 "Dirstate error".to_string(),
306 307 )),
307 308 }
308 309 }
309 310 def write(
310 311 &self,
311 312 p1: PyObject,
312 313 p2: PyObject,
313 314 now: PyObject
314 315 ) -> PyResult<PyBytes> {
315 316 let now = Timestamp(now.extract(py)?);
316 317 let parents = DirstateParents {
317 318 p1: extract_node_id(py, &p1)?,
318 319 p2: extract_node_id(py, &p2)?,
319 320 };
320 321
321 322 match self.inner(py).borrow_mut().pack(parents, now) {
322 323 Ok(packed) => Ok(PyBytes::new(py, &packed)),
323 324 Err(_) => Err(PyErr::new::<exc::OSError, _>(
324 325 py,
325 326 "Dirstate error".to_string(),
326 327 )),
327 328 }
328 329 }
329 330
330 331 def filefoldmapasdict(&self) -> PyResult<PyDict> {
331 332 let dict = PyDict::new(py);
332 for (key, value) in
333 self.inner(py).borrow_mut().build_file_fold_map().iter()
334 {
333 for (path, entry) in self.inner(py).borrow_mut().iter() {
334 if entry.state != EntryState::Removed {
335 let key = normalize_case(path);
336 let value = path;
335 337 dict.set_item(
336 338 py,
337 339 PyBytes::new(py, key.as_bytes()).into_object(),
338 340 PyBytes::new(py, value.as_bytes()).into_object(),
339 341 )?;
340 342 }
343 }
341 344 Ok(dict)
342 345 }
343 346
344 347 def __len__(&self) -> PyResult<usize> {
345 348 Ok(self.inner(py).borrow().len())
346 349 }
347 350
348 351 def __contains__(&self, key: PyObject) -> PyResult<bool> {
349 352 let key = key.extract::<PyBytes>(py)?;
350 353 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
351 354 }
352 355
353 356 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
354 357 let key = key.extract::<PyBytes>(py)?;
355 358 let key = HgPath::new(key.data(py));
356 359 match self.inner(py).borrow().get(key) {
357 360 Some(entry) => {
358 361 Ok(make_dirstate_tuple(py, entry)?)
359 362 },
360 363 None => Err(PyErr::new::<exc::KeyError, _>(
361 364 py,
362 365 String::from_utf8_lossy(key.as_bytes()),
363 366 )),
364 367 }
365 368 }
366 369
367 370 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
368 371 let leaked_ref = self.inner(py).leak_immutable();
369 372 DirstateMapKeysIterator::from_inner(
370 373 py,
371 374 unsafe { leaked_ref.map(py, |o| o.iter()) },
372 375 )
373 376 }
374 377
375 378 def items(&self) -> PyResult<DirstateMapItemsIterator> {
376 379 let leaked_ref = self.inner(py).leak_immutable();
377 380 DirstateMapItemsIterator::from_inner(
378 381 py,
379 382 unsafe { leaked_ref.map(py, |o| o.iter()) },
380 383 )
381 384 }
382 385
383 386 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
384 387 let leaked_ref = self.inner(py).leak_immutable();
385 388 DirstateMapKeysIterator::from_inner(
386 389 py,
387 390 unsafe { leaked_ref.map(py, |o| o.iter()) },
388 391 )
389 392 }
390 393
391 394 def getdirs(&self) -> PyResult<Dirs> {
392 395 // TODO don't copy, share the reference
393 396 self.inner(py).borrow_mut().set_dirs()
394 397 .map_err(|e| {
395 398 PyErr::new::<exc::ValueError, _>(py, e.to_string())
396 399 })?;
397 400 Dirs::from_inner(
398 401 py,
399 402 DirsMultiset::from_dirstate(
400 403 self.inner(py).borrow().iter(),
401 404 Some(EntryState::Removed),
402 405 )
403 406 .map_err(|e| {
404 407 PyErr::new::<exc::ValueError, _>(py, e.to_string())
405 408 })?,
406 409 )
407 410 }
408 411 def getalldirs(&self) -> PyResult<Dirs> {
409 412 // TODO don't copy, share the reference
410 413 self.inner(py).borrow_mut().set_all_dirs()
411 414 .map_err(|e| {
412 415 PyErr::new::<exc::ValueError, _>(py, e.to_string())
413 416 })?;
414 417 Dirs::from_inner(
415 418 py,
416 419 DirsMultiset::from_dirstate(
417 420 self.inner(py).borrow().iter(),
418 421 None,
419 422 ).map_err(|e| {
420 423 PyErr::new::<exc::ValueError, _>(py, e.to_string())
421 424 })?,
422 425 )
423 426 }
424 427
425 428 // TODO all copymap* methods, see docstring above
426 429 def copymapcopy(&self) -> PyResult<PyDict> {
427 430 let dict = PyDict::new(py);
428 431 for (key, value) in self.inner(py).borrow().copy_map_iter() {
429 432 dict.set_item(
430 433 py,
431 434 PyBytes::new(py, key.as_bytes()),
432 435 PyBytes::new(py, value.as_bytes()),
433 436 )?;
434 437 }
435 438 Ok(dict)
436 439 }
437 440
438 441 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
439 442 let key = key.extract::<PyBytes>(py)?;
440 443 match self.inner(py).borrow().copy_map_get(HgPath::new(key.data(py))) {
441 444 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
442 445 None => Err(PyErr::new::<exc::KeyError, _>(
443 446 py,
444 447 String::from_utf8_lossy(key.data(py)),
445 448 )),
446 449 }
447 450 }
448 451 def copymap(&self) -> PyResult<CopyMap> {
449 452 CopyMap::from_inner(py, self.clone_ref(py))
450 453 }
451 454
452 455 def copymaplen(&self) -> PyResult<usize> {
453 456 Ok(self.inner(py).borrow().copy_map_len())
454 457 }
455 458 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
456 459 let key = key.extract::<PyBytes>(py)?;
457 460 Ok(self
458 461 .inner(py)
459 462 .borrow()
460 463 .copy_map_contains_key(HgPath::new(key.data(py))))
461 464 }
462 465 def copymapget(
463 466 &self,
464 467 key: PyObject,
465 468 default: Option<PyObject>
466 469 ) -> PyResult<Option<PyObject>> {
467 470 let key = key.extract::<PyBytes>(py)?;
468 471 match self
469 472 .inner(py)
470 473 .borrow()
471 474 .copy_map_get(HgPath::new(key.data(py)))
472 475 {
473 476 Some(copy) => Ok(Some(
474 477 PyBytes::new(py, copy.as_bytes()).into_object(),
475 478 )),
476 479 None => Ok(default),
477 480 }
478 481 }
479 482 def copymapsetitem(
480 483 &self,
481 484 key: PyObject,
482 485 value: PyObject
483 486 ) -> PyResult<PyObject> {
484 487 let key = key.extract::<PyBytes>(py)?;
485 488 let value = value.extract::<PyBytes>(py)?;
486 489 self.inner(py).borrow_mut().copy_map_insert(
487 490 HgPathBuf::from_bytes(key.data(py)),
488 491 HgPathBuf::from_bytes(value.data(py)),
489 492 );
490 493 Ok(py.None())
491 494 }
492 495 def copymappop(
493 496 &self,
494 497 key: PyObject,
495 498 default: Option<PyObject>
496 499 ) -> PyResult<Option<PyObject>> {
497 500 let key = key.extract::<PyBytes>(py)?;
498 501 match self
499 502 .inner(py)
500 503 .borrow_mut()
501 504 .copy_map_remove(HgPath::new(key.data(py)))
502 505 {
503 506 Some(_) => Ok(None),
504 507 None => Ok(default),
505 508 }
506 509 }
507 510
508 511 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
509 512 let leaked_ref = self.inner(py).leak_immutable();
510 513 CopyMapKeysIterator::from_inner(
511 514 py,
512 515 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
513 516 )
514 517 }
515 518
516 519 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
517 520 let leaked_ref = self.inner(py).leak_immutable();
518 521 CopyMapItemsIterator::from_inner(
519 522 py,
520 523 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
521 524 )
522 525 }
523 526
524 527 });
525 528
526 529 impl DirstateMap {
527 530 pub fn get_inner<'a>(
528 531 &'a self,
529 532 py: Python<'a>,
530 533 ) -> Ref<'a, Box<dyn DirstateMapMethods + Send>> {
531 534 self.inner(py).borrow()
532 535 }
533 536 fn translate_key(
534 537 py: Python,
535 538 res: (&HgPathBuf, &DirstateEntry),
536 539 ) -> PyResult<Option<PyBytes>> {
537 540 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
538 541 }
539 542 fn translate_key_value(
540 543 py: Python,
541 544 res: (&HgPathBuf, &DirstateEntry),
542 545 ) -> PyResult<Option<(PyBytes, PyObject)>> {
543 546 let (f, entry) = res;
544 547 Ok(Some((
545 548 PyBytes::new(py, f.as_bytes()),
546 549 make_dirstate_tuple(py, &entry)?,
547 550 )))
548 551 }
549 552 }
550 553
551 554 py_shared_iterator!(
552 555 DirstateMapKeysIterator,
553 556 UnsafePyLeaked<StateMapIter<'static>>,
554 557 DirstateMap::translate_key,
555 558 Option<PyBytes>
556 559 );
557 560
558 561 py_shared_iterator!(
559 562 DirstateMapItemsIterator,
560 563 UnsafePyLeaked<StateMapIter<'static>>,
561 564 DirstateMap::translate_key_value,
562 565 Option<(PyBytes, PyObject)>
563 566 );
564 567
565 568 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
566 569 let bytes = obj.extract::<PyBytes>(py)?;
567 570 match bytes.data(py).try_into() {
568 571 Ok(s) => Ok(s),
569 572 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
570 573 }
571 574 }
General Comments 0
You need to be logged in to leave comments. Login now