##// END OF EJS Templates
rust: Remove the `rustext.parsers` module...
Simon Sapin -
r48832:11943945 default
parent child Browse files
Show More
@@ -1,679 +1,682 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::{RefCell, RefMut};
12 12 use std::convert::TryInto;
13 13
14 14 use cpython::{
15 15 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
16 16 PyObject, PyResult, PySet, PyString, Python, PythonObject, ToPyObject,
17 17 UnsafePyLeaked,
18 18 };
19 19
20 20 use crate::{
21 21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 22 dirstate::make_dirstate_item,
23 23 dirstate::make_dirstate_item_raw,
24 24 dirstate::non_normal_entries::{
25 25 NonNormalEntries, NonNormalEntriesIterator,
26 26 },
27 parsers::dirstate_parents_to_pytuple,
28 27 pybytes_deref::PyBytesDeref,
29 28 };
30 29 use hg::{
31 30 dirstate::parsers::Timestamp,
32 31 dirstate::MTIME_UNSET,
33 32 dirstate::SIZE_NON_NORMAL,
34 33 dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap,
35 34 dirstate_tree::dispatch::DirstateMapMethods,
36 35 dirstate_tree::on_disk::DirstateV2ParseError,
37 36 dirstate_tree::owning::OwningDirstateMap,
38 37 revlog::Node,
39 38 utils::files::normalize_case,
40 39 utils::hg_path::{HgPath, HgPathBuf},
41 40 DirstateEntry, DirstateError, DirstateMap as RustDirstateMap,
42 41 DirstateParents, EntryState, StateMapIter,
43 42 };
44 43
45 44 // TODO
46 45 // This object needs to share references to multiple members of its Rust
47 46 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
48 47 // Right now `CopyMap` is done, but it needs to have an explicit reference
49 48 // to `RustDirstateMap` which itself needs to have an encapsulation for
50 49 // every method in `CopyMap` (copymapcopy, etc.).
51 50 // This is ugly and hard to maintain.
52 51 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
53 52 // `py_class!` is already implemented and does not mention
54 53 // `RustDirstateMap`, rightfully so.
55 54 // All attributes also have to have a separate refcount data attribute for
56 55 // leaks, with all methods that go along for reference sharing.
57 56 py_class!(pub class DirstateMap |py| {
58 57 @shared data inner: Box<dyn DirstateMapMethods + Send>;
59 58
60 59 /// Returns a `(dirstate_map, parents)` tuple
61 60 @staticmethod
62 61 def new_v1(
63 62 use_dirstate_tree: bool,
64 63 on_disk: PyBytes,
65 64 ) -> PyResult<PyObject> {
66 65 let (inner, parents) = if use_dirstate_tree {
67 66 let on_disk = PyBytesDeref::new(py, on_disk);
68 67 let mut map = OwningDirstateMap::new_empty(on_disk);
69 68 let (on_disk, map_placeholder) = map.get_mut_pair();
70 69
71 70 let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk)
72 71 .map_err(|e| dirstate_error(py, e))?;
73 72 *map_placeholder = actual_map;
74 73 (Box::new(map) as _, parents)
75 74 } else {
76 75 let bytes = on_disk.data(py);
77 76 let mut map = RustDirstateMap::default();
78 77 let parents = map.read(bytes).map_err(|e| dirstate_error(py, e))?;
79 78 (Box::new(map) as _, parents)
80 79 };
81 80 let map = Self::create_instance(py, inner)?;
82 let parents = parents.map(|p| dirstate_parents_to_pytuple(py, &p));
81 let parents = parents.map(|p| {
82 let p1 = PyBytes::new(py, p.p1.as_bytes());
83 let p2 = PyBytes::new(py, p.p2.as_bytes());
84 (p1, p2)
85 });
83 86 Ok((map, parents).to_py_object(py).into_object())
84 87 }
85 88
86 89 /// Returns a DirstateMap
87 90 @staticmethod
88 91 def new_v2(
89 92 on_disk: PyBytes,
90 93 data_size: usize,
91 94 tree_metadata: PyBytes,
92 95 ) -> PyResult<PyObject> {
93 96 let dirstate_error = |e: DirstateError| {
94 97 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
95 98 };
96 99 let on_disk = PyBytesDeref::new(py, on_disk);
97 100 let mut map = OwningDirstateMap::new_empty(on_disk);
98 101 let (on_disk, map_placeholder) = map.get_mut_pair();
99 102 *map_placeholder = TreeDirstateMap::new_v2(
100 103 on_disk, data_size, tree_metadata.data(py),
101 104 ).map_err(dirstate_error)?;
102 105 let map = Self::create_instance(py, Box::new(map))?;
103 106 Ok(map.into_object())
104 107 }
105 108
106 109 def clear(&self) -> PyResult<PyObject> {
107 110 self.inner(py).borrow_mut().clear();
108 111 Ok(py.None())
109 112 }
110 113
111 114 def get(
112 115 &self,
113 116 key: PyObject,
114 117 default: Option<PyObject> = None
115 118 ) -> PyResult<Option<PyObject>> {
116 119 let key = key.extract::<PyBytes>(py)?;
117 120 match self
118 121 .inner(py)
119 122 .borrow()
120 123 .get(HgPath::new(key.data(py)))
121 124 .map_err(|e| v2_error(py, e))?
122 125 {
123 126 Some(entry) => {
124 127 Ok(Some(make_dirstate_item(py, &entry)?))
125 128 },
126 129 None => Ok(default)
127 130 }
128 131 }
129 132
130 133 def set_v1(&self, path: PyObject, item: PyObject) -> PyResult<PyObject> {
131 134 let f = path.extract::<PyBytes>(py)?;
132 135 let filename = HgPath::new(f.data(py));
133 136 let state = item.getattr(py, "state")?.extract::<PyBytes>(py)?;
134 137 let state = state.data(py)[0];
135 138 let entry = DirstateEntry {
136 139 state: state.try_into().expect("state is always valid"),
137 140 mtime: item.getattr(py, "mtime")?.extract(py)?,
138 141 size: item.getattr(py, "size")?.extract(py)?,
139 142 mode: item.getattr(py, "mode")?.extract(py)?,
140 143 };
141 144 self.inner(py).borrow_mut().set_v1(filename, entry);
142 145 Ok(py.None())
143 146 }
144 147
145 148 def addfile(
146 149 &self,
147 150 f: PyObject,
148 151 mode: PyObject,
149 152 size: PyObject,
150 153 mtime: PyObject,
151 154 added: PyObject,
152 155 merged: PyObject,
153 156 from_p2: PyObject,
154 157 possibly_dirty: PyObject,
155 158 ) -> PyResult<PyObject> {
156 159 let f = f.extract::<PyBytes>(py)?;
157 160 let filename = HgPath::new(f.data(py));
158 161 let mode = if mode.is_none(py) {
159 162 // fallback default value
160 163 0
161 164 } else {
162 165 mode.extract(py)?
163 166 };
164 167 let size = if size.is_none(py) {
165 168 // fallback default value
166 169 SIZE_NON_NORMAL
167 170 } else {
168 171 size.extract(py)?
169 172 };
170 173 let mtime = if mtime.is_none(py) {
171 174 // fallback default value
172 175 MTIME_UNSET
173 176 } else {
174 177 mtime.extract(py)?
175 178 };
176 179 let entry = DirstateEntry {
177 180 // XXX Arbitrary default value since the value is determined later
178 181 state: EntryState::Normal,
179 182 mode: mode,
180 183 size: size,
181 184 mtime: mtime,
182 185 };
183 186 let added = added.extract::<PyBool>(py)?.is_true();
184 187 let merged = merged.extract::<PyBool>(py)?.is_true();
185 188 let from_p2 = from_p2.extract::<PyBool>(py)?.is_true();
186 189 let possibly_dirty = possibly_dirty.extract::<PyBool>(py)?.is_true();
187 190 self.inner(py).borrow_mut().add_file(
188 191 filename,
189 192 entry,
190 193 added,
191 194 merged,
192 195 from_p2,
193 196 possibly_dirty
194 197 ).and(Ok(py.None())).or_else(|e: DirstateError| {
195 198 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
196 199 })
197 200 }
198 201
199 202 def removefile(
200 203 &self,
201 204 f: PyObject,
202 205 in_merge: PyObject
203 206 ) -> PyResult<PyObject> {
204 207 self.inner(py).borrow_mut()
205 208 .remove_file(
206 209 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
207 210 in_merge.extract::<PyBool>(py)?.is_true(),
208 211 )
209 212 .or_else(|_| {
210 213 Err(PyErr::new::<exc::OSError, _>(
211 214 py,
212 215 "Dirstate error".to_string(),
213 216 ))
214 217 })?;
215 218 Ok(py.None())
216 219 }
217 220
218 221 def dropfile(
219 222 &self,
220 223 f: PyObject,
221 224 ) -> PyResult<PyBool> {
222 225 self.inner(py).borrow_mut()
223 226 .drop_file(
224 227 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
225 228 )
226 229 .and_then(|b| Ok(b.to_py_object(py)))
227 230 .or_else(|e| {
228 231 Err(PyErr::new::<exc::OSError, _>(
229 232 py,
230 233 format!("Dirstate error: {}", e.to_string()),
231 234 ))
232 235 })
233 236 }
234 237
235 238 def clearambiguoustimes(
236 239 &self,
237 240 files: PyObject,
238 241 now: PyObject
239 242 ) -> PyResult<PyObject> {
240 243 let files: PyResult<Vec<HgPathBuf>> = files
241 244 .iter(py)?
242 245 .map(|filename| {
243 246 Ok(HgPathBuf::from_bytes(
244 247 filename?.extract::<PyBytes>(py)?.data(py),
245 248 ))
246 249 })
247 250 .collect();
248 251 self.inner(py)
249 252 .borrow_mut()
250 253 .clear_ambiguous_times(files?, now.extract(py)?)
251 254 .map_err(|e| v2_error(py, e))?;
252 255 Ok(py.None())
253 256 }
254 257
255 258 def other_parent_entries(&self) -> PyResult<PyObject> {
256 259 let mut inner_shared = self.inner(py).borrow_mut();
257 260 let set = PySet::empty(py)?;
258 261 for path in inner_shared.iter_other_parent_paths() {
259 262 let path = path.map_err(|e| v2_error(py, e))?;
260 263 set.add(py, PyBytes::new(py, path.as_bytes()))?;
261 264 }
262 265 Ok(set.into_object())
263 266 }
264 267
265 268 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
266 269 NonNormalEntries::from_inner(py, self.clone_ref(py))
267 270 }
268 271
269 272 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
270 273 let key = key.extract::<PyBytes>(py)?;
271 274 self.inner(py)
272 275 .borrow_mut()
273 276 .non_normal_entries_contains(HgPath::new(key.data(py)))
274 277 .map_err(|e| v2_error(py, e))
275 278 }
276 279
277 280 def non_normal_entries_display(&self) -> PyResult<PyString> {
278 281 let mut inner = self.inner(py).borrow_mut();
279 282 let paths = inner
280 283 .iter_non_normal_paths()
281 284 .collect::<Result<Vec<_>, _>>()
282 285 .map_err(|e| v2_error(py, e))?;
283 286 let formatted = format!("NonNormalEntries: {}", hg::utils::join_display(paths, ", "));
284 287 Ok(PyString::new(py, &formatted))
285 288 }
286 289
287 290 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
288 291 let key = key.extract::<PyBytes>(py)?;
289 292 let key = key.data(py);
290 293 let was_present = self
291 294 .inner(py)
292 295 .borrow_mut()
293 296 .non_normal_entries_remove(HgPath::new(key));
294 297 if !was_present {
295 298 let msg = String::from_utf8_lossy(key);
296 299 Err(PyErr::new::<exc::KeyError, _>(py, msg))
297 300 } else {
298 301 Ok(py.None())
299 302 }
300 303 }
301 304
302 305 def non_normal_entries_discard(&self, key: PyObject) -> PyResult<PyObject>
303 306 {
304 307 let key = key.extract::<PyBytes>(py)?;
305 308 self
306 309 .inner(py)
307 310 .borrow_mut()
308 311 .non_normal_entries_remove(HgPath::new(key.data(py)));
309 312 Ok(py.None())
310 313 }
311 314
312 315 def non_normal_entries_add(&self, key: PyObject) -> PyResult<PyObject> {
313 316 let key = key.extract::<PyBytes>(py)?;
314 317 self
315 318 .inner(py)
316 319 .borrow_mut()
317 320 .non_normal_entries_add(HgPath::new(key.data(py)));
318 321 Ok(py.None())
319 322 }
320 323
321 324 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
322 325 let mut inner = self.inner(py).borrow_mut();
323 326
324 327 let ret = PyList::new(py, &[]);
325 328 for filename in inner.non_normal_or_other_parent_paths() {
326 329 let filename = filename.map_err(|e| v2_error(py, e))?;
327 330 let as_pystring = PyBytes::new(py, filename.as_bytes());
328 331 ret.append(py, as_pystring.into_object());
329 332 }
330 333 Ok(ret)
331 334 }
332 335
333 336 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
334 337 // Make sure the sets are defined before we no longer have a mutable
335 338 // reference to the dmap.
336 339 self.inner(py)
337 340 .borrow_mut()
338 341 .set_non_normal_other_parent_entries(false);
339 342
340 343 let leaked_ref = self.inner(py).leak_immutable();
341 344
342 345 NonNormalEntriesIterator::from_inner(py, unsafe {
343 346 leaked_ref.map(py, |o| {
344 347 o.iter_non_normal_paths_panic()
345 348 })
346 349 })
347 350 }
348 351
349 352 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
350 353 let d = d.extract::<PyBytes>(py)?;
351 354 Ok(self.inner(py).borrow_mut()
352 355 .has_tracked_dir(HgPath::new(d.data(py)))
353 356 .map_err(|e| {
354 357 PyErr::new::<exc::ValueError, _>(py, e.to_string())
355 358 })?
356 359 .to_py_object(py))
357 360 }
358 361
359 362 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
360 363 let d = d.extract::<PyBytes>(py)?;
361 364 Ok(self.inner(py).borrow_mut()
362 365 .has_dir(HgPath::new(d.data(py)))
363 366 .map_err(|e| {
364 367 PyErr::new::<exc::ValueError, _>(py, e.to_string())
365 368 })?
366 369 .to_py_object(py))
367 370 }
368 371
369 372 def write_v1(
370 373 &self,
371 374 p1: PyObject,
372 375 p2: PyObject,
373 376 now: PyObject
374 377 ) -> PyResult<PyBytes> {
375 378 let now = Timestamp(now.extract(py)?);
376 379
377 380 let mut inner = self.inner(py).borrow_mut();
378 381 let parents = DirstateParents {
379 382 p1: extract_node_id(py, &p1)?,
380 383 p2: extract_node_id(py, &p2)?,
381 384 };
382 385 let result = inner.pack_v1(parents, now);
383 386 match result {
384 387 Ok(packed) => Ok(PyBytes::new(py, &packed)),
385 388 Err(_) => Err(PyErr::new::<exc::OSError, _>(
386 389 py,
387 390 "Dirstate error".to_string(),
388 391 )),
389 392 }
390 393 }
391 394
392 395 /// Returns new data together with whether that data should be appended to
393 396 /// the existing data file whose content is at `self.on_disk` (True),
394 397 /// instead of written to a new data file (False).
395 398 def write_v2(
396 399 &self,
397 400 now: PyObject,
398 401 can_append: bool,
399 402 ) -> PyResult<PyObject> {
400 403 let now = Timestamp(now.extract(py)?);
401 404
402 405 let mut inner = self.inner(py).borrow_mut();
403 406 let result = inner.pack_v2(now, can_append);
404 407 match result {
405 408 Ok((packed, tree_metadata, append)) => {
406 409 let packed = PyBytes::new(py, &packed);
407 410 let tree_metadata = PyBytes::new(py, &tree_metadata);
408 411 let tuple = (packed, tree_metadata, append);
409 412 Ok(tuple.to_py_object(py).into_object())
410 413 },
411 414 Err(_) => Err(PyErr::new::<exc::OSError, _>(
412 415 py,
413 416 "Dirstate error".to_string(),
414 417 )),
415 418 }
416 419 }
417 420
418 421 def filefoldmapasdict(&self) -> PyResult<PyDict> {
419 422 let dict = PyDict::new(py);
420 423 for item in self.inner(py).borrow_mut().iter() {
421 424 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
422 425 if entry.state != EntryState::Removed {
423 426 let key = normalize_case(path);
424 427 let value = path;
425 428 dict.set_item(
426 429 py,
427 430 PyBytes::new(py, key.as_bytes()).into_object(),
428 431 PyBytes::new(py, value.as_bytes()).into_object(),
429 432 )?;
430 433 }
431 434 }
432 435 Ok(dict)
433 436 }
434 437
435 438 def __len__(&self) -> PyResult<usize> {
436 439 Ok(self.inner(py).borrow().len())
437 440 }
438 441
439 442 def __contains__(&self, key: PyObject) -> PyResult<bool> {
440 443 let key = key.extract::<PyBytes>(py)?;
441 444 self.inner(py)
442 445 .borrow()
443 446 .contains_key(HgPath::new(key.data(py)))
444 447 .map_err(|e| v2_error(py, e))
445 448 }
446 449
447 450 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
448 451 let key = key.extract::<PyBytes>(py)?;
449 452 let key = HgPath::new(key.data(py));
450 453 match self
451 454 .inner(py)
452 455 .borrow()
453 456 .get(key)
454 457 .map_err(|e| v2_error(py, e))?
455 458 {
456 459 Some(entry) => {
457 460 Ok(make_dirstate_item(py, &entry)?)
458 461 },
459 462 None => Err(PyErr::new::<exc::KeyError, _>(
460 463 py,
461 464 String::from_utf8_lossy(key.as_bytes()),
462 465 )),
463 466 }
464 467 }
465 468
466 469 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
467 470 let leaked_ref = self.inner(py).leak_immutable();
468 471 DirstateMapKeysIterator::from_inner(
469 472 py,
470 473 unsafe { leaked_ref.map(py, |o| o.iter()) },
471 474 )
472 475 }
473 476
474 477 def items(&self) -> PyResult<DirstateMapItemsIterator> {
475 478 let leaked_ref = self.inner(py).leak_immutable();
476 479 DirstateMapItemsIterator::from_inner(
477 480 py,
478 481 unsafe { leaked_ref.map(py, |o| o.iter()) },
479 482 )
480 483 }
481 484
482 485 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
483 486 let leaked_ref = self.inner(py).leak_immutable();
484 487 DirstateMapKeysIterator::from_inner(
485 488 py,
486 489 unsafe { leaked_ref.map(py, |o| o.iter()) },
487 490 )
488 491 }
489 492
490 493 // TODO all copymap* methods, see docstring above
491 494 def copymapcopy(&self) -> PyResult<PyDict> {
492 495 let dict = PyDict::new(py);
493 496 for item in self.inner(py).borrow().copy_map_iter() {
494 497 let (key, value) = item.map_err(|e| v2_error(py, e))?;
495 498 dict.set_item(
496 499 py,
497 500 PyBytes::new(py, key.as_bytes()),
498 501 PyBytes::new(py, value.as_bytes()),
499 502 )?;
500 503 }
501 504 Ok(dict)
502 505 }
503 506
504 507 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
505 508 let key = key.extract::<PyBytes>(py)?;
506 509 match self
507 510 .inner(py)
508 511 .borrow()
509 512 .copy_map_get(HgPath::new(key.data(py)))
510 513 .map_err(|e| v2_error(py, e))?
511 514 {
512 515 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
513 516 None => Err(PyErr::new::<exc::KeyError, _>(
514 517 py,
515 518 String::from_utf8_lossy(key.data(py)),
516 519 )),
517 520 }
518 521 }
519 522 def copymap(&self) -> PyResult<CopyMap> {
520 523 CopyMap::from_inner(py, self.clone_ref(py))
521 524 }
522 525
523 526 def copymaplen(&self) -> PyResult<usize> {
524 527 Ok(self.inner(py).borrow().copy_map_len())
525 528 }
526 529 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
527 530 let key = key.extract::<PyBytes>(py)?;
528 531 self.inner(py)
529 532 .borrow()
530 533 .copy_map_contains_key(HgPath::new(key.data(py)))
531 534 .map_err(|e| v2_error(py, e))
532 535 }
533 536 def copymapget(
534 537 &self,
535 538 key: PyObject,
536 539 default: Option<PyObject>
537 540 ) -> PyResult<Option<PyObject>> {
538 541 let key = key.extract::<PyBytes>(py)?;
539 542 match self
540 543 .inner(py)
541 544 .borrow()
542 545 .copy_map_get(HgPath::new(key.data(py)))
543 546 .map_err(|e| v2_error(py, e))?
544 547 {
545 548 Some(copy) => Ok(Some(
546 549 PyBytes::new(py, copy.as_bytes()).into_object(),
547 550 )),
548 551 None => Ok(default),
549 552 }
550 553 }
551 554 def copymapsetitem(
552 555 &self,
553 556 key: PyObject,
554 557 value: PyObject
555 558 ) -> PyResult<PyObject> {
556 559 let key = key.extract::<PyBytes>(py)?;
557 560 let value = value.extract::<PyBytes>(py)?;
558 561 self.inner(py)
559 562 .borrow_mut()
560 563 .copy_map_insert(
561 564 HgPathBuf::from_bytes(key.data(py)),
562 565 HgPathBuf::from_bytes(value.data(py)),
563 566 )
564 567 .map_err(|e| v2_error(py, e))?;
565 568 Ok(py.None())
566 569 }
567 570 def copymappop(
568 571 &self,
569 572 key: PyObject,
570 573 default: Option<PyObject>
571 574 ) -> PyResult<Option<PyObject>> {
572 575 let key = key.extract::<PyBytes>(py)?;
573 576 match self
574 577 .inner(py)
575 578 .borrow_mut()
576 579 .copy_map_remove(HgPath::new(key.data(py)))
577 580 .map_err(|e| v2_error(py, e))?
578 581 {
579 582 Some(_) => Ok(None),
580 583 None => Ok(default),
581 584 }
582 585 }
583 586
584 587 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
585 588 let leaked_ref = self.inner(py).leak_immutable();
586 589 CopyMapKeysIterator::from_inner(
587 590 py,
588 591 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
589 592 )
590 593 }
591 594
592 595 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
593 596 let leaked_ref = self.inner(py).leak_immutable();
594 597 CopyMapItemsIterator::from_inner(
595 598 py,
596 599 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
597 600 )
598 601 }
599 602
600 603 def tracked_dirs(&self) -> PyResult<PyList> {
601 604 let dirs = PyList::new(py, &[]);
602 605 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
603 606 .map_err(|e |dirstate_error(py, e))?
604 607 {
605 608 let path = path.map_err(|e| v2_error(py, e))?;
606 609 let path = PyBytes::new(py, path.as_bytes());
607 610 dirs.append(py, path.into_object())
608 611 }
609 612 Ok(dirs)
610 613 }
611 614
612 615 def debug_iter(&self) -> PyResult<PyList> {
613 616 let dirs = PyList::new(py, &[]);
614 617 for item in self.inner(py).borrow().debug_iter() {
615 618 let (path, (state, mode, size, mtime)) =
616 619 item.map_err(|e| v2_error(py, e))?;
617 620 let path = PyBytes::new(py, path.as_bytes());
618 621 let item = make_dirstate_item_raw(py, state, mode, size, mtime)?;
619 622 dirs.append(py, (path, item).to_py_object(py).into_object())
620 623 }
621 624 Ok(dirs)
622 625 }
623 626 });
624 627
625 628 impl DirstateMap {
626 629 pub fn get_inner_mut<'a>(
627 630 &'a self,
628 631 py: Python<'a>,
629 632 ) -> RefMut<'a, Box<dyn DirstateMapMethods + Send>> {
630 633 self.inner(py).borrow_mut()
631 634 }
632 635 fn translate_key(
633 636 py: Python,
634 637 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
635 638 ) -> PyResult<Option<PyBytes>> {
636 639 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
637 640 Ok(Some(PyBytes::new(py, f.as_bytes())))
638 641 }
639 642 fn translate_key_value(
640 643 py: Python,
641 644 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
642 645 ) -> PyResult<Option<(PyBytes, PyObject)>> {
643 646 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
644 647 Ok(Some((
645 648 PyBytes::new(py, f.as_bytes()),
646 649 make_dirstate_item(py, &entry)?,
647 650 )))
648 651 }
649 652 }
650 653
651 654 py_shared_iterator!(
652 655 DirstateMapKeysIterator,
653 656 UnsafePyLeaked<StateMapIter<'static>>,
654 657 DirstateMap::translate_key,
655 658 Option<PyBytes>
656 659 );
657 660
658 661 py_shared_iterator!(
659 662 DirstateMapItemsIterator,
660 663 UnsafePyLeaked<StateMapIter<'static>>,
661 664 DirstateMap::translate_key_value,
662 665 Option<(PyBytes, PyObject)>
663 666 );
664 667
665 668 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
666 669 let bytes = obj.extract::<PyBytes>(py)?;
667 670 match bytes.data(py).try_into() {
668 671 Ok(s) => Ok(s),
669 672 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
670 673 }
671 674 }
672 675
673 676 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
674 677 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
675 678 }
676 679
677 680 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
678 681 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
679 682 }
@@ -1,76 +1,70 b''
1 1 // lib.rs
2 2 //
3 3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
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 //! Python bindings of `hg-core` objects using the `cpython` crate.
9 9 //! Once compiled, the resulting single shared library object can be placed in
10 10 //! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
11 11 //! It holds several modules, so that from the point of view of Python,
12 12 //! it behaves as the `cext` package.
13 13 //!
14 14 //! Example:
15 15 //!
16 16 //! ```text
17 17 //! >>> from mercurial.rustext import ancestor
18 18 //! >>> ancestor.__doc__
19 19 //! 'Generic DAG ancestor algorithms - Rust implementation'
20 20 //! ```
21 21
22 22 /// This crate uses nested private macros, `extern crate` is still needed in
23 23 /// 2018 edition.
24 24 #[macro_use]
25 25 extern crate cpython;
26 26
27 27 pub mod ancestors;
28 28 mod cindex;
29 29 mod conversion;
30 30 #[macro_use]
31 31 pub mod ref_sharing;
32 32 pub mod copy_tracing;
33 33 pub mod dagops;
34 34 pub mod debug;
35 35 pub mod dirstate;
36 36 pub mod discovery;
37 37 pub mod exceptions;
38 pub mod parsers;
39 38 mod pybytes_deref;
40 39 pub mod revlog;
41 40 pub mod utils;
42 41
43 42 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
44 43 m.add(
45 44 py,
46 45 "__doc__",
47 46 "Mercurial core concepts - Rust implementation",
48 47 )?;
49 48
50 49 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
51 50 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
52 51 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
53 52 m.add(py, "debug", debug::init_module(py, &dotted_name)?)?;
54 53 m.add(
55 54 py,
56 55 "copy_tracing",
57 56 copy_tracing::init_module(py, &dotted_name)?,
58 57 )?;
59 58 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
60 59 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
61 60 m.add(py, "revlog", revlog::init_module(py, &dotted_name)?)?;
62 m.add(
63 py,
64 "parsers",
65 parsers::init_parsers_module(py, &dotted_name)?,
66 )?;
67 61 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
68 62 Ok(())
69 63 });
70 64
71 65 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
72 66 #[test]
73 67 #[ignore]
74 68 fn libpython_must_be_linked_to_run_tests() {
75 69 // stub function to tell that some tests wouldn't run
76 70 }
@@ -1,106 +1,106 b''
1 1 # extension to emulate invoking 'dirstate.write()' at the time
2 2 # specified by '[fakedirstatewritetime] fakenow', only when
3 3 # 'dirstate.write()' is invoked via functions below:
4 4 #
5 5 # - 'workingctx._poststatusfixup()' (= 'repo.status()')
6 6 # - 'committablectx.markcommitted()'
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from mercurial import (
11 11 context,
12 12 dirstate,
13 13 dirstatemap as dirstatemapmod,
14 14 extensions,
15 15 policy,
16 16 registrar,
17 17 )
18 18 from mercurial.utils import dateutil
19 19
20 20 try:
21 21 from mercurial import rustext
22 22
23 23 rustext.__name__ # force actual import (see hgdemandimport)
24 24 except ImportError:
25 25 rustext = None
26 26
27 27 configtable = {}
28 28 configitem = registrar.configitem(configtable)
29 29
30 30 configitem(
31 31 b'fakedirstatewritetime',
32 32 b'fakenow',
33 33 default=None,
34 34 )
35 35
36 36 parsers = policy.importmod('parsers')
37 rustmod = policy.importrust('parsers')
37 has_rust_dirstate = policy.importrust('dirstate') is not None
38 38
39 39
40 40 def pack_dirstate(fakenow, orig, dmap, copymap, pl, now):
41 41 # execute what original parsers.pack_dirstate should do actually
42 42 # for consistency
43 43 actualnow = int(now)
44 44 for f, e in dmap.items():
45 45 if e.need_delay(actualnow):
46 46 e.set_possibly_dirty()
47 47
48 48 return orig(dmap, copymap, pl, fakenow)
49 49
50 50
51 51 def fakewrite(ui, func):
52 52 # fake "now" of 'pack_dirstate' only if it is invoked while 'func'
53 53
54 54 fakenow = ui.config(b'fakedirstatewritetime', b'fakenow')
55 55 if not fakenow:
56 56 # Execute original one, if fakenow isn't configured. This is
57 57 # useful to prevent subrepos from executing replaced one,
58 58 # because replacing 'parsers.pack_dirstate' is also effective
59 59 # in subrepos.
60 60 return func()
61 61
62 62 # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between
63 63 # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
64 64 fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0]
65 65
66 if rustmod is not None:
66 if has_rust_dirstate:
67 67 # The Rust implementation does not use public parse/pack dirstate
68 68 # to prevent conversion round-trips
69 69 orig_dirstatemap_write = dirstatemapmod.dirstatemap.write
70 70 wrapper = lambda self, tr, st, now: orig_dirstatemap_write(
71 71 self, tr, st, fakenow
72 72 )
73 73 dirstatemapmod.dirstatemap.write = wrapper
74 74
75 75 orig_dirstate_getfsnow = dirstate._getfsnow
76 76 wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args)
77 77
78 78 orig_module = parsers
79 79 orig_pack_dirstate = parsers.pack_dirstate
80 80
81 81 orig_module.pack_dirstate = wrapper
82 82 dirstate._getfsnow = lambda *args: fakenow
83 83 try:
84 84 return func()
85 85 finally:
86 86 orig_module.pack_dirstate = orig_pack_dirstate
87 87 dirstate._getfsnow = orig_dirstate_getfsnow
88 if rustmod is not None:
88 if has_rust_dirstate:
89 89 dirstatemapmod.dirstatemap.write = orig_dirstatemap_write
90 90
91 91
92 92 def _poststatusfixup(orig, workingctx, status, fixup):
93 93 ui = workingctx.repo().ui
94 94 return fakewrite(ui, lambda: orig(workingctx, status, fixup))
95 95
96 96
97 97 def markcommitted(orig, committablectx, node):
98 98 ui = committablectx.repo().ui
99 99 return fakewrite(ui, lambda: orig(committablectx, node))
100 100
101 101
102 102 def extsetup(ui):
103 103 extensions.wrapfunction(
104 104 context.workingctx, '_poststatusfixup', _poststatusfixup
105 105 )
106 106 extensions.wrapfunction(context.workingctx, 'markcommitted', markcommitted)
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now