##// END OF EJS Templates
rust-cpython: make `NonNormalEntires` iterable to fix `fsmonitor` (issue6276)...
Raphaël Gomès -
r44903:8ac5726d default
parent child Browse files
Show More
@@ -1,478 +1,497 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::{
9 9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
10 10 pack_dirstate, parse_dirstate,
11 11 utils::{
12 12 files::normalize_case,
13 13 hg_path::{HgPath, HgPathBuf},
14 14 },
15 15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
16 16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
17 17 };
18 18 use core::borrow::Borrow;
19 19 use std::collections::HashSet;
20 20 use std::convert::TryInto;
21 21 use std::iter::FromIterator;
22 22 use std::ops::Deref;
23 23 use std::time::Duration;
24 24
25 25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
26 26
27 27 const NULL_ID: [u8; 20] = [0; 20];
28 28 const MTIME_UNSET: i32 = -1;
29 29
30 30 #[derive(Default)]
31 31 pub struct DirstateMap {
32 32 state_map: StateMap,
33 33 pub copy_map: CopyMap,
34 34 file_fold_map: Option<FileFoldMap>,
35 35 pub dirs: Option<DirsMultiset>,
36 36 pub all_dirs: Option<DirsMultiset>,
37 37 non_normal_set: Option<HashSet<HgPathBuf>>,
38 38 other_parent_set: Option<HashSet<HgPathBuf>>,
39 39 parents: Option<DirstateParents>,
40 40 dirty_parents: bool,
41 41 }
42 42
43 43 /// Should only really be used in python interface code, for clarity
44 44 impl Deref for DirstateMap {
45 45 type Target = StateMap;
46 46
47 47 fn deref(&self) -> &Self::Target {
48 48 &self.state_map
49 49 }
50 50 }
51 51
52 52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 54 iter: I,
55 55 ) -> Self {
56 56 Self {
57 57 state_map: iter.into_iter().collect(),
58 58 ..Self::default()
59 59 }
60 60 }
61 61 }
62 62
63 63 impl DirstateMap {
64 64 pub fn new() -> Self {
65 65 Self::default()
66 66 }
67 67
68 68 pub fn clear(&mut self) {
69 69 self.state_map.clear();
70 70 self.copy_map.clear();
71 71 self.file_fold_map = None;
72 72 self.non_normal_set = None;
73 73 self.other_parent_set = None;
74 74 self.set_parents(&DirstateParents {
75 75 p1: NULL_ID,
76 76 p2: NULL_ID,
77 77 })
78 78 }
79 79
80 80 /// Add a tracked file to the dirstate
81 81 pub fn add_file(
82 82 &mut self,
83 83 filename: &HgPath,
84 84 old_state: EntryState,
85 85 entry: DirstateEntry,
86 86 ) -> Result<(), DirstateMapError> {
87 87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 88 {
89 89 if let Some(ref mut dirs) = self.dirs {
90 90 dirs.add_path(filename)?;
91 91 }
92 92 }
93 93 if old_state == EntryState::Unknown {
94 94 if let Some(ref mut all_dirs) = self.all_dirs {
95 95 all_dirs.add_path(filename)?;
96 96 }
97 97 }
98 98 self.state_map.insert(filename.to_owned(), entry.to_owned());
99 99
100 100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 101 self.get_non_normal_other_parent_entries()
102 102 .0
103 103 .insert(filename.to_owned());
104 104 }
105 105
106 106 if entry.size == SIZE_FROM_OTHER_PARENT {
107 107 self.get_non_normal_other_parent_entries()
108 108 .1
109 109 .insert(filename.to_owned());
110 110 }
111 111 Ok(())
112 112 }
113 113
114 114 /// Mark a file as removed in the dirstate.
115 115 ///
116 116 /// The `size` parameter is used to store sentinel values that indicate
117 117 /// the file's previous state. In the future, we should refactor this
118 118 /// to be more explicit about what that state is.
119 119 pub fn remove_file(
120 120 &mut self,
121 121 filename: &HgPath,
122 122 old_state: EntryState,
123 123 size: i32,
124 124 ) -> Result<(), DirstateMapError> {
125 125 if old_state != EntryState::Unknown && old_state != EntryState::Removed
126 126 {
127 127 if let Some(ref mut dirs) = self.dirs {
128 128 dirs.delete_path(filename)?;
129 129 }
130 130 }
131 131 if old_state == EntryState::Unknown {
132 132 if let Some(ref mut all_dirs) = self.all_dirs {
133 133 all_dirs.add_path(filename)?;
134 134 }
135 135 }
136 136
137 137 if let Some(ref mut file_fold_map) = self.file_fold_map {
138 138 file_fold_map.remove(&normalize_case(filename));
139 139 }
140 140 self.state_map.insert(
141 141 filename.to_owned(),
142 142 DirstateEntry {
143 143 state: EntryState::Removed,
144 144 mode: 0,
145 145 size,
146 146 mtime: 0,
147 147 },
148 148 );
149 149 self.get_non_normal_other_parent_entries()
150 150 .0
151 151 .insert(filename.to_owned());
152 152 Ok(())
153 153 }
154 154
155 155 /// Remove a file from the dirstate.
156 156 /// Returns `true` if the file was previously recorded.
157 157 pub fn drop_file(
158 158 &mut self,
159 159 filename: &HgPath,
160 160 old_state: EntryState,
161 161 ) -> Result<bool, DirstateMapError> {
162 162 let exists = self.state_map.remove(filename).is_some();
163 163
164 164 if exists {
165 165 if old_state != EntryState::Removed {
166 166 if let Some(ref mut dirs) = self.dirs {
167 167 dirs.delete_path(filename)?;
168 168 }
169 169 }
170 170 if let Some(ref mut all_dirs) = self.all_dirs {
171 171 all_dirs.delete_path(filename)?;
172 172 }
173 173 }
174 174 if let Some(ref mut file_fold_map) = self.file_fold_map {
175 175 file_fold_map.remove(&normalize_case(filename));
176 176 }
177 177 self.get_non_normal_other_parent_entries()
178 178 .0
179 179 .remove(filename);
180 180
181 181 Ok(exists)
182 182 }
183 183
184 184 pub fn clear_ambiguous_times(
185 185 &mut self,
186 186 filenames: Vec<HgPathBuf>,
187 187 now: i32,
188 188 ) {
189 189 for filename in filenames {
190 190 let mut changed = false;
191 191 self.state_map
192 192 .entry(filename.to_owned())
193 193 .and_modify(|entry| {
194 194 if entry.state == EntryState::Normal && entry.mtime == now
195 195 {
196 196 changed = true;
197 197 *entry = DirstateEntry {
198 198 mtime: MTIME_UNSET,
199 199 ..*entry
200 200 };
201 201 }
202 202 });
203 203 if changed {
204 204 self.get_non_normal_other_parent_entries()
205 205 .0
206 206 .insert(filename.to_owned());
207 207 }
208 208 }
209 209 }
210 210
211 211 pub fn non_normal_entries_remove(
212 212 &mut self,
213 213 key: impl AsRef<HgPath>,
214 214 ) -> bool {
215 215 self.get_non_normal_other_parent_entries()
216 216 .0
217 217 .remove(key.as_ref())
218 218 }
219 219 pub fn non_normal_entries_union(
220 220 &mut self,
221 221 other: HashSet<HgPathBuf>,
222 222 ) -> Vec<HgPathBuf> {
223 223 self.get_non_normal_other_parent_entries()
224 224 .0
225 225 .union(&other)
226 226 .map(|e| e.to_owned())
227 227 .collect()
228 228 }
229 229
230 230 pub fn get_non_normal_other_parent_entries(
231 231 &mut self,
232 232 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
233 233 self.set_non_normal_other_parent_entries(false);
234 234 (
235 235 self.non_normal_set.as_mut().unwrap(),
236 236 self.other_parent_set.as_mut().unwrap(),
237 237 )
238 238 }
239 239
240 /// Useful to get immutable references to those sets in contexts where
241 /// you only have an immutable reference to the `DirstateMap`, like when
242 /// sharing references with Python.
243 ///
244 /// TODO, get rid of this along with the other "setter/getter" stuff when
245 /// a nice typestate plan is defined.
246 ///
247 /// # Panics
248 ///
249 /// Will panic if either set is `None`.
250 pub fn get_non_normal_other_parent_entries_panic(
251 &self,
252 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
253 (
254 self.non_normal_set.as_ref().unwrap(),
255 self.other_parent_set.as_ref().unwrap(),
256 )
257 }
258
240 259 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
241 260 if !force
242 261 && self.non_normal_set.is_some()
243 262 && self.other_parent_set.is_some()
244 263 {
245 264 return;
246 265 }
247 266 let mut non_normal = HashSet::new();
248 267 let mut other_parent = HashSet::new();
249 268
250 269 for (
251 270 filename,
252 271 DirstateEntry {
253 272 state, size, mtime, ..
254 273 },
255 274 ) in self.state_map.iter()
256 275 {
257 276 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
258 277 non_normal.insert(filename.to_owned());
259 278 }
260 279 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
261 280 {
262 281 other_parent.insert(filename.to_owned());
263 282 }
264 283 }
265 284 self.non_normal_set = Some(non_normal);
266 285 self.other_parent_set = Some(other_parent);
267 286 }
268 287
269 288 /// Both of these setters and their uses appear to be the simplest way to
270 289 /// emulate a Python lazy property, but it is ugly and unidiomatic.
271 290 /// TODO One day, rewriting this struct using the typestate might be a
272 291 /// good idea.
273 292 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
274 293 if self.all_dirs.is_none() {
275 294 self.all_dirs =
276 295 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
277 296 }
278 297 Ok(())
279 298 }
280 299
281 300 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
282 301 if self.dirs.is_none() {
283 302 self.dirs = Some(DirsMultiset::from_dirstate(
284 303 &self.state_map,
285 304 Some(EntryState::Removed),
286 305 )?);
287 306 }
288 307 Ok(())
289 308 }
290 309
291 310 pub fn has_tracked_dir(
292 311 &mut self,
293 312 directory: &HgPath,
294 313 ) -> Result<bool, DirstateMapError> {
295 314 self.set_dirs()?;
296 315 Ok(self.dirs.as_ref().unwrap().contains(directory))
297 316 }
298 317
299 318 pub fn has_dir(
300 319 &mut self,
301 320 directory: &HgPath,
302 321 ) -> Result<bool, DirstateMapError> {
303 322 self.set_all_dirs()?;
304 323 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
305 324 }
306 325
307 326 pub fn parents(
308 327 &mut self,
309 328 file_contents: &[u8],
310 329 ) -> Result<&DirstateParents, DirstateError> {
311 330 if let Some(ref parents) = self.parents {
312 331 return Ok(parents);
313 332 }
314 333 let parents;
315 334 if file_contents.len() == PARENT_SIZE * 2 {
316 335 parents = DirstateParents {
317 336 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
318 337 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
319 338 .try_into()
320 339 .unwrap(),
321 340 };
322 341 } else if file_contents.is_empty() {
323 342 parents = DirstateParents {
324 343 p1: NULL_ID,
325 344 p2: NULL_ID,
326 345 };
327 346 } else {
328 347 return Err(DirstateError::Parse(DirstateParseError::Damaged));
329 348 }
330 349
331 350 self.parents = Some(parents);
332 351 Ok(self.parents.as_ref().unwrap())
333 352 }
334 353
335 354 pub fn set_parents(&mut self, parents: &DirstateParents) {
336 355 self.parents = Some(parents.clone());
337 356 self.dirty_parents = true;
338 357 }
339 358
340 359 pub fn read(
341 360 &mut self,
342 361 file_contents: &[u8],
343 362 ) -> Result<Option<DirstateParents>, DirstateError> {
344 363 if file_contents.is_empty() {
345 364 return Ok(None);
346 365 }
347 366
348 367 let parents = parse_dirstate(
349 368 &mut self.state_map,
350 369 &mut self.copy_map,
351 370 file_contents,
352 371 )?;
353 372
354 373 if !self.dirty_parents {
355 374 self.set_parents(&parents);
356 375 }
357 376
358 377 Ok(Some(parents))
359 378 }
360 379
361 380 pub fn pack(
362 381 &mut self,
363 382 parents: DirstateParents,
364 383 now: Duration,
365 384 ) -> Result<Vec<u8>, DirstateError> {
366 385 let packed =
367 386 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
368 387
369 388 self.dirty_parents = false;
370 389
371 390 self.set_non_normal_other_parent_entries(true);
372 391 Ok(packed)
373 392 }
374 393
375 394 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
376 395 if let Some(ref file_fold_map) = self.file_fold_map {
377 396 return file_fold_map;
378 397 }
379 398 let mut new_file_fold_map = FileFoldMap::default();
380 399 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
381 400 {
382 401 if *state == EntryState::Removed {
383 402 new_file_fold_map
384 403 .insert(normalize_case(filename), filename.to_owned());
385 404 }
386 405 }
387 406 self.file_fold_map = Some(new_file_fold_map);
388 407 self.file_fold_map.as_ref().unwrap()
389 408 }
390 409 }
391 410
392 411 #[cfg(test)]
393 412 mod tests {
394 413 use super::*;
395 414
396 415 #[test]
397 416 fn test_dirs_multiset() {
398 417 let mut map = DirstateMap::new();
399 418 assert!(map.dirs.is_none());
400 419 assert!(map.all_dirs.is_none());
401 420
402 421 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
403 422 assert!(map.all_dirs.is_some());
404 423 assert!(map.dirs.is_none());
405 424
406 425 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
407 426 assert!(map.dirs.is_some());
408 427 }
409 428
410 429 #[test]
411 430 fn test_add_file() {
412 431 let mut map = DirstateMap::new();
413 432
414 433 assert_eq!(0, map.len());
415 434
416 435 map.add_file(
417 436 HgPath::new(b"meh"),
418 437 EntryState::Normal,
419 438 DirstateEntry {
420 439 state: EntryState::Normal,
421 440 mode: 1337,
422 441 mtime: 1337,
423 442 size: 1337,
424 443 },
425 444 )
426 445 .unwrap();
427 446
428 447 assert_eq!(1, map.len());
429 448 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
430 449 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
431 450 }
432 451
433 452 #[test]
434 453 fn test_non_normal_other_parent_entries() {
435 454 let mut map: DirstateMap = [
436 455 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
437 456 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
438 457 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
439 458 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
440 459 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
441 460 (b"f6", (EntryState::Added, 1337, 1337, -1)),
442 461 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
443 462 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
444 463 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
445 464 (b"fa", (EntryState::Added, 1337, -2, 1337)),
446 465 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
447 466 ]
448 467 .iter()
449 468 .map(|(fname, (state, mode, size, mtime))| {
450 469 (
451 470 HgPathBuf::from_bytes(fname.as_ref()),
452 471 DirstateEntry {
453 472 state: *state,
454 473 mode: *mode,
455 474 size: *size,
456 475 mtime: *mtime,
457 476 },
458 477 )
459 478 })
460 479 .collect();
461 480
462 481 let mut non_normal = [
463 482 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
464 483 ]
465 484 .iter()
466 485 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
467 486 .collect();
468 487
469 488 let mut other_parent = HashSet::new();
470 489 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
471 490 let entries = map.get_non_normal_other_parent_entries();
472 491
473 492 assert_eq!(
474 493 (&mut non_normal, &mut other_parent),
475 494 (entries.0, entries.1)
476 495 );
477 496 }
478 497 }
@@ -1,568 +1,586 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 use std::time::Duration;
14 14
15 15 use cpython::{
16 16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
17 17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
18 18 UnsafePyLeaked,
19 19 };
20 20
21 21 use crate::{
22 22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::non_normal_entries::NonNormalEntries,
23 dirstate::non_normal_entries::{
24 NonNormalEntries, NonNormalEntriesIterator,
25 },
24 26 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
25 27 };
26 28 use hg::{
27 29 utils::hg_path::{HgPath, HgPathBuf},
28 30 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
29 31 DirstateMapError, DirstateParents, DirstateParseError, EntryState,
30 32 StateMapIter, PARENT_SIZE,
31 33 };
32 34
33 35 // TODO
34 36 // This object needs to share references to multiple members of its Rust
35 37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
36 38 // Right now `CopyMap` is done, but it needs to have an explicit reference
37 39 // to `RustDirstateMap` which itself needs to have an encapsulation for
38 40 // every method in `CopyMap` (copymapcopy, etc.).
39 41 // This is ugly and hard to maintain.
40 42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
41 43 // `py_class!` is already implemented and does not mention
42 44 // `RustDirstateMap`, rightfully so.
43 45 // All attributes also have to have a separate refcount data attribute for
44 46 // leaks, with all methods that go along for reference sharing.
45 47 py_class!(pub class DirstateMap |py| {
46 48 @shared data inner: RustDirstateMap;
47 49
48 50 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
49 51 let inner = RustDirstateMap::default();
50 52 Self::create_instance(py, inner)
51 53 }
52 54
53 55 def clear(&self) -> PyResult<PyObject> {
54 56 self.inner(py).borrow_mut().clear();
55 57 Ok(py.None())
56 58 }
57 59
58 60 def get(
59 61 &self,
60 62 key: PyObject,
61 63 default: Option<PyObject> = None
62 64 ) -> PyResult<Option<PyObject>> {
63 65 let key = key.extract::<PyBytes>(py)?;
64 66 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
65 67 Some(entry) => {
66 68 Ok(Some(make_dirstate_tuple(py, entry)?))
67 69 },
68 70 None => Ok(default)
69 71 }
70 72 }
71 73
72 74 def addfile(
73 75 &self,
74 76 f: PyObject,
75 77 oldstate: PyObject,
76 78 state: PyObject,
77 79 mode: PyObject,
78 80 size: PyObject,
79 81 mtime: PyObject
80 82 ) -> PyResult<PyObject> {
81 83 self.inner(py).borrow_mut().add_file(
82 84 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
83 85 oldstate.extract::<PyBytes>(py)?.data(py)[0]
84 86 .try_into()
85 87 .map_err(|e: DirstateParseError| {
86 88 PyErr::new::<exc::ValueError, _>(py, e.to_string())
87 89 })?,
88 90 DirstateEntry {
89 91 state: state.extract::<PyBytes>(py)?.data(py)[0]
90 92 .try_into()
91 93 .map_err(|e: DirstateParseError| {
92 94 PyErr::new::<exc::ValueError, _>(py, e.to_string())
93 95 })?,
94 96 mode: mode.extract(py)?,
95 97 size: size.extract(py)?,
96 98 mtime: mtime.extract(py)?,
97 99 },
98 100 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
99 101 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
100 102 })
101 103 }
102 104
103 105 def removefile(
104 106 &self,
105 107 f: PyObject,
106 108 oldstate: PyObject,
107 109 size: PyObject
108 110 ) -> PyResult<PyObject> {
109 111 self.inner(py).borrow_mut()
110 112 .remove_file(
111 113 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
112 114 oldstate.extract::<PyBytes>(py)?.data(py)[0]
113 115 .try_into()
114 116 .map_err(|e: DirstateParseError| {
115 117 PyErr::new::<exc::ValueError, _>(py, e.to_string())
116 118 })?,
117 119 size.extract(py)?,
118 120 )
119 121 .or_else(|_| {
120 122 Err(PyErr::new::<exc::OSError, _>(
121 123 py,
122 124 "Dirstate error".to_string(),
123 125 ))
124 126 })?;
125 127 Ok(py.None())
126 128 }
127 129
128 130 def dropfile(
129 131 &self,
130 132 f: PyObject,
131 133 oldstate: PyObject
132 134 ) -> PyResult<PyBool> {
133 135 self.inner(py).borrow_mut()
134 136 .drop_file(
135 137 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
136 138 oldstate.extract::<PyBytes>(py)?.data(py)[0]
137 139 .try_into()
138 140 .map_err(|e: DirstateParseError| {
139 141 PyErr::new::<exc::ValueError, _>(py, e.to_string())
140 142 })?,
141 143 )
142 144 .and_then(|b| Ok(b.to_py_object(py)))
143 145 .or_else(|_| {
144 146 Err(PyErr::new::<exc::OSError, _>(
145 147 py,
146 148 "Dirstate error".to_string(),
147 149 ))
148 150 })
149 151 }
150 152
151 153 def clearambiguoustimes(
152 154 &self,
153 155 files: PyObject,
154 156 now: PyObject
155 157 ) -> PyResult<PyObject> {
156 158 let files: PyResult<Vec<HgPathBuf>> = files
157 159 .iter(py)?
158 160 .map(|filename| {
159 161 Ok(HgPathBuf::from_bytes(
160 162 filename?.extract::<PyBytes>(py)?.data(py),
161 163 ))
162 164 })
163 165 .collect();
164 166 self.inner(py).borrow_mut()
165 167 .clear_ambiguous_times(files?, now.extract(py)?);
166 168 Ok(py.None())
167 169 }
168 170
169 171 def other_parent_entries(&self) -> PyResult<PyObject> {
170 172 let mut inner_shared = self.inner(py).borrow_mut();
171 173 let (_, other_parent) =
172 174 inner_shared.get_non_normal_other_parent_entries();
173 175
174 176 let locals = PyDict::new(py);
175 177 locals.set_item(
176 178 py,
177 179 "other_parent",
178 180 other_parent
179 181 .iter()
180 182 .map(|v| PyBytes::new(py, v.as_ref()))
181 183 .collect::<Vec<PyBytes>>()
182 184 .to_py_object(py),
183 185 )?;
184 186
185 187 py.eval("set(other_parent)", None, Some(&locals))
186 188 }
187 189
188 190 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
189 191 NonNormalEntries::from_inner(py, self.clone_ref(py))
190 192 }
191 193
192 194 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
193 195 let key = key.extract::<PyBytes>(py)?;
194 196 Ok(self
195 197 .inner(py)
196 198 .borrow_mut()
197 199 .get_non_normal_other_parent_entries().0
198 200 .contains(HgPath::new(key.data(py))))
199 201 }
200 202
201 203 def non_normal_entries_display(&self) -> PyResult<PyString> {
202 204 Ok(
203 205 PyString::new(
204 206 py,
205 207 &format!(
206 208 "NonNormalEntries: {:?}",
207 209 self
208 210 .inner(py)
209 211 .borrow_mut()
210 212 .get_non_normal_other_parent_entries().0
211 213 .iter().map(|o| o))
212 214 )
213 215 )
214 216 }
215 217
216 218 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
217 219 let key = key.extract::<PyBytes>(py)?;
218 220 self
219 221 .inner(py)
220 222 .borrow_mut()
221 223 .non_normal_entries_remove(HgPath::new(key.data(py)));
222 224 Ok(py.None())
223 225 }
224 226
225 227 def non_normal_entries_union(&self, other: PyObject) -> PyResult<PyList> {
226 228 let other: PyResult<_> = other.iter(py)?
227 229 .map(|f| {
228 230 Ok(HgPathBuf::from_bytes(
229 231 f?.extract::<PyBytes>(py)?.data(py),
230 232 ))
231 233 })
232 234 .collect();
233 235
234 236 let res = self
235 237 .inner(py)
236 238 .borrow_mut()
237 239 .non_normal_entries_union(other?);
238 240
239 241 let ret = PyList::new(py, &[]);
240 242 for filename in res.iter() {
241 243 let as_pystring = PyBytes::new(py, filename.as_bytes());
242 244 ret.append(py, as_pystring.into_object());
243 245 }
244 246 Ok(ret)
245 247 }
246 248
249 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
250 // Make sure the sets are defined before we no longer have a mutable
251 // reference to the dmap.
252 self.inner(py)
253 .borrow_mut()
254 .set_non_normal_other_parent_entries(false);
255
256 let leaked_ref = self.inner(py).leak_immutable();
257
258 NonNormalEntriesIterator::from_inner(py, unsafe {
259 leaked_ref.map(py, |o| {
260 o.get_non_normal_other_parent_entries_panic().0.iter()
261 })
262 })
263 }
264
247 265 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
248 266 let d = d.extract::<PyBytes>(py)?;
249 267 Ok(self.inner(py).borrow_mut()
250 268 .has_tracked_dir(HgPath::new(d.data(py)))
251 269 .map_err(|e| {
252 270 PyErr::new::<exc::ValueError, _>(py, e.to_string())
253 271 })?
254 272 .to_py_object(py))
255 273 }
256 274
257 275 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
258 276 let d = d.extract::<PyBytes>(py)?;
259 277 Ok(self.inner(py).borrow_mut()
260 278 .has_dir(HgPath::new(d.data(py)))
261 279 .map_err(|e| {
262 280 PyErr::new::<exc::ValueError, _>(py, e.to_string())
263 281 })?
264 282 .to_py_object(py))
265 283 }
266 284
267 285 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
268 286 self.inner(py).borrow_mut()
269 287 .parents(st.extract::<PyBytes>(py)?.data(py))
270 288 .and_then(|d| {
271 289 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
272 290 .to_py_object(py))
273 291 })
274 292 .or_else(|_| {
275 293 Err(PyErr::new::<exc::OSError, _>(
276 294 py,
277 295 "Dirstate error".to_string(),
278 296 ))
279 297 })
280 298 }
281 299
282 300 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
283 301 let p1 = extract_node_id(py, &p1)?;
284 302 let p2 = extract_node_id(py, &p2)?;
285 303
286 304 self.inner(py).borrow_mut()
287 305 .set_parents(&DirstateParents { p1, p2 });
288 306 Ok(py.None())
289 307 }
290 308
291 309 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
292 310 match self.inner(py).borrow_mut()
293 311 .read(st.extract::<PyBytes>(py)?.data(py))
294 312 {
295 313 Ok(Some(parents)) => Ok(Some(
296 314 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
297 315 .to_py_object(py)
298 316 .into_object(),
299 317 )),
300 318 Ok(None) => Ok(Some(py.None())),
301 319 Err(_) => Err(PyErr::new::<exc::OSError, _>(
302 320 py,
303 321 "Dirstate error".to_string(),
304 322 )),
305 323 }
306 324 }
307 325 def write(
308 326 &self,
309 327 p1: PyObject,
310 328 p2: PyObject,
311 329 now: PyObject
312 330 ) -> PyResult<PyBytes> {
313 331 let now = Duration::new(now.extract(py)?, 0);
314 332 let parents = DirstateParents {
315 333 p1: extract_node_id(py, &p1)?,
316 334 p2: extract_node_id(py, &p2)?,
317 335 };
318 336
319 337 match self.inner(py).borrow_mut().pack(parents, now) {
320 338 Ok(packed) => Ok(PyBytes::new(py, &packed)),
321 339 Err(_) => Err(PyErr::new::<exc::OSError, _>(
322 340 py,
323 341 "Dirstate error".to_string(),
324 342 )),
325 343 }
326 344 }
327 345
328 346 def filefoldmapasdict(&self) -> PyResult<PyDict> {
329 347 let dict = PyDict::new(py);
330 348 for (key, value) in
331 349 self.inner(py).borrow_mut().build_file_fold_map().iter()
332 350 {
333 351 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
334 352 }
335 353 Ok(dict)
336 354 }
337 355
338 356 def __len__(&self) -> PyResult<usize> {
339 357 Ok(self.inner(py).borrow().len())
340 358 }
341 359
342 360 def __contains__(&self, key: PyObject) -> PyResult<bool> {
343 361 let key = key.extract::<PyBytes>(py)?;
344 362 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
345 363 }
346 364
347 365 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
348 366 let key = key.extract::<PyBytes>(py)?;
349 367 let key = HgPath::new(key.data(py));
350 368 match self.inner(py).borrow().get(key) {
351 369 Some(entry) => {
352 370 Ok(make_dirstate_tuple(py, entry)?)
353 371 },
354 372 None => Err(PyErr::new::<exc::KeyError, _>(
355 373 py,
356 374 String::from_utf8_lossy(key.as_bytes()),
357 375 )),
358 376 }
359 377 }
360 378
361 379 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
362 380 let leaked_ref = self.inner(py).leak_immutable();
363 381 DirstateMapKeysIterator::from_inner(
364 382 py,
365 383 unsafe { leaked_ref.map(py, |o| o.iter()) },
366 384 )
367 385 }
368 386
369 387 def items(&self) -> PyResult<DirstateMapItemsIterator> {
370 388 let leaked_ref = self.inner(py).leak_immutable();
371 389 DirstateMapItemsIterator::from_inner(
372 390 py,
373 391 unsafe { leaked_ref.map(py, |o| o.iter()) },
374 392 )
375 393 }
376 394
377 395 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
378 396 let leaked_ref = self.inner(py).leak_immutable();
379 397 DirstateMapKeysIterator::from_inner(
380 398 py,
381 399 unsafe { leaked_ref.map(py, |o| o.iter()) },
382 400 )
383 401 }
384 402
385 403 def getdirs(&self) -> PyResult<Dirs> {
386 404 // TODO don't copy, share the reference
387 405 self.inner(py).borrow_mut().set_dirs()
388 406 .map_err(|e| {
389 407 PyErr::new::<exc::ValueError, _>(py, e.to_string())
390 408 })?;
391 409 Dirs::from_inner(
392 410 py,
393 411 DirsMultiset::from_dirstate(
394 412 &self.inner(py).borrow(),
395 413 Some(EntryState::Removed),
396 414 )
397 415 .map_err(|e| {
398 416 PyErr::new::<exc::ValueError, _>(py, e.to_string())
399 417 })?,
400 418 )
401 419 }
402 420 def getalldirs(&self) -> PyResult<Dirs> {
403 421 // TODO don't copy, share the reference
404 422 self.inner(py).borrow_mut().set_all_dirs()
405 423 .map_err(|e| {
406 424 PyErr::new::<exc::ValueError, _>(py, e.to_string())
407 425 })?;
408 426 Dirs::from_inner(
409 427 py,
410 428 DirsMultiset::from_dirstate(
411 429 &self.inner(py).borrow(),
412 430 None,
413 431 ).map_err(|e| {
414 432 PyErr::new::<exc::ValueError, _>(py, e.to_string())
415 433 })?,
416 434 )
417 435 }
418 436
419 437 // TODO all copymap* methods, see docstring above
420 438 def copymapcopy(&self) -> PyResult<PyDict> {
421 439 let dict = PyDict::new(py);
422 440 for (key, value) in self.inner(py).borrow().copy_map.iter() {
423 441 dict.set_item(
424 442 py,
425 443 PyBytes::new(py, key.as_ref()),
426 444 PyBytes::new(py, value.as_ref()),
427 445 )?;
428 446 }
429 447 Ok(dict)
430 448 }
431 449
432 450 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
433 451 let key = key.extract::<PyBytes>(py)?;
434 452 match self.inner(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
435 453 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
436 454 None => Err(PyErr::new::<exc::KeyError, _>(
437 455 py,
438 456 String::from_utf8_lossy(key.data(py)),
439 457 )),
440 458 }
441 459 }
442 460 def copymap(&self) -> PyResult<CopyMap> {
443 461 CopyMap::from_inner(py, self.clone_ref(py))
444 462 }
445 463
446 464 def copymaplen(&self) -> PyResult<usize> {
447 465 Ok(self.inner(py).borrow().copy_map.len())
448 466 }
449 467 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
450 468 let key = key.extract::<PyBytes>(py)?;
451 469 Ok(self
452 470 .inner(py)
453 471 .borrow()
454 472 .copy_map
455 473 .contains_key(HgPath::new(key.data(py))))
456 474 }
457 475 def copymapget(
458 476 &self,
459 477 key: PyObject,
460 478 default: Option<PyObject>
461 479 ) -> PyResult<Option<PyObject>> {
462 480 let key = key.extract::<PyBytes>(py)?;
463 481 match self
464 482 .inner(py)
465 483 .borrow()
466 484 .copy_map
467 485 .get(HgPath::new(key.data(py)))
468 486 {
469 487 Some(copy) => Ok(Some(
470 488 PyBytes::new(py, copy.as_ref()).into_object(),
471 489 )),
472 490 None => Ok(default),
473 491 }
474 492 }
475 493 def copymapsetitem(
476 494 &self,
477 495 key: PyObject,
478 496 value: PyObject
479 497 ) -> PyResult<PyObject> {
480 498 let key = key.extract::<PyBytes>(py)?;
481 499 let value = value.extract::<PyBytes>(py)?;
482 500 self.inner(py).borrow_mut().copy_map.insert(
483 501 HgPathBuf::from_bytes(key.data(py)),
484 502 HgPathBuf::from_bytes(value.data(py)),
485 503 );
486 504 Ok(py.None())
487 505 }
488 506 def copymappop(
489 507 &self,
490 508 key: PyObject,
491 509 default: Option<PyObject>
492 510 ) -> PyResult<Option<PyObject>> {
493 511 let key = key.extract::<PyBytes>(py)?;
494 512 match self
495 513 .inner(py)
496 514 .borrow_mut()
497 515 .copy_map
498 516 .remove(HgPath::new(key.data(py)))
499 517 {
500 518 Some(_) => Ok(None),
501 519 None => Ok(default),
502 520 }
503 521 }
504 522
505 523 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
506 524 let leaked_ref = self.inner(py).leak_immutable();
507 525 CopyMapKeysIterator::from_inner(
508 526 py,
509 527 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
510 528 )
511 529 }
512 530
513 531 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
514 532 let leaked_ref = self.inner(py).leak_immutable();
515 533 CopyMapItemsIterator::from_inner(
516 534 py,
517 535 unsafe { leaked_ref.map(py, |o| o.copy_map.iter()) },
518 536 )
519 537 }
520 538
521 539 });
522 540
523 541 impl DirstateMap {
524 542 pub fn get_inner<'a>(
525 543 &'a self,
526 544 py: Python<'a>,
527 545 ) -> Ref<'a, RustDirstateMap> {
528 546 self.inner(py).borrow()
529 547 }
530 548 fn translate_key(
531 549 py: Python,
532 550 res: (&HgPathBuf, &DirstateEntry),
533 551 ) -> PyResult<Option<PyBytes>> {
534 552 Ok(Some(PyBytes::new(py, res.0.as_ref())))
535 553 }
536 554 fn translate_key_value(
537 555 py: Python,
538 556 res: (&HgPathBuf, &DirstateEntry),
539 557 ) -> PyResult<Option<(PyBytes, PyObject)>> {
540 558 let (f, entry) = res;
541 559 Ok(Some((
542 560 PyBytes::new(py, f.as_ref()),
543 561 make_dirstate_tuple(py, entry)?,
544 562 )))
545 563 }
546 564 }
547 565
548 566 py_shared_iterator!(
549 567 DirstateMapKeysIterator,
550 568 UnsafePyLeaked<StateMapIter<'static>>,
551 569 DirstateMap::translate_key,
552 570 Option<PyBytes>
553 571 );
554 572
555 573 py_shared_iterator!(
556 574 DirstateMapItemsIterator,
557 575 UnsafePyLeaked<StateMapIter<'static>>,
558 576 DirstateMap::translate_key_value,
559 577 Option<(PyBytes, PyObject)>
560 578 );
561 579
562 580 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
563 581 let bytes = obj.extract::<PyBytes>(py)?;
564 582 match bytes.data(py).try_into() {
565 583 Ok(s) => Ok(s),
566 584 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
567 585 }
568 586 }
@@ -1,52 +1,76 b''
1 1 // non_normal_other_parent_entries.rs
2 2 //
3 3 // Copyright 2020 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 cpython::{
9 exc::NotImplementedError, CompareOp, ObjectProtocol, PyErr, PyList,
10 PyObject, PyResult, PyString, Python, PythonObject, ToPyObject,
9 exc::NotImplementedError, CompareOp, ObjectProtocol, PyBytes, PyClone,
10 PyErr, PyList, PyObject, PyResult, PyString, Python, PythonObject,
11 ToPyObject, UnsafePyLeaked,
11 12 };
12 13
13 14 use crate::dirstate::DirstateMap;
15 use hg::utils::hg_path::HgPathBuf;
16 use std::cell::RefCell;
17 use std::collections::hash_set;
14 18
15 19 py_class!(pub class NonNormalEntries |py| {
16 20 data dmap: DirstateMap;
17 21
18 22 def __contains__(&self, key: PyObject) -> PyResult<bool> {
19 23 self.dmap(py).non_normal_entries_contains(py, key)
20 24 }
21 25 def remove(&self, key: PyObject) -> PyResult<PyObject> {
22 26 self.dmap(py).non_normal_entries_remove(py, key)
23 27 }
24 28 def union(&self, other: PyObject) -> PyResult<PyList> {
25 29 self.dmap(py).non_normal_entries_union(py, other)
26 30 }
27 31 def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> {
28 32 match op {
29 33 CompareOp::Eq => self.is_equal_to(py, other),
30 34 CompareOp::Ne => Ok(!self.is_equal_to(py, other)?),
31 35 _ => Err(PyErr::new::<NotImplementedError, _>(py, ""))
32 36 }
33 37 }
34 38 def __repr__(&self) -> PyResult<PyString> {
35 39 self.dmap(py).non_normal_entries_display(py)
36 40 }
41
42 def __iter__(&self) -> PyResult<NonNormalEntriesIterator> {
43 self.dmap(py).non_normal_entries_iter(py)
44 }
37 45 });
38 46
39 47 impl NonNormalEntries {
40 48 pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
41 49 Self::create_instance(py, dm)
42 50 }
43 51
44 52 fn is_equal_to(&self, py: Python, other: PyObject) -> PyResult<bool> {
45 53 for item in other.iter(py)? {
46 54 if !self.dmap(py).non_normal_entries_contains(py, item?)? {
47 55 return Ok(false);
48 56 }
49 57 }
50 58 Ok(true)
51 59 }
60
61 fn translate_key(
62 py: Python,
63 key: &HgPathBuf,
64 ) -> PyResult<Option<PyBytes>> {
65 Ok(Some(PyBytes::new(py, key.as_ref())))
66 }
52 67 }
68
69 type NonNormalEntriesIter<'a> = hash_set::Iter<'a, HgPathBuf>;
70
71 py_shared_iterator!(
72 NonNormalEntriesIterator,
73 UnsafePyLeaked<NonNormalEntriesIter<'static>>,
74 NonNormalEntries::translate_key,
75 Option<PyBytes>
76 );
General Comments 0
You need to be logged in to leave comments. Login now