##// END OF EJS Templates
rust-dirstatemap: expand the wrapping code a bit...
marmoute -
r48307:f6f25ab6 default
parent child Browse files
Show More
@@ -1,562 +1,570 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_tuple,
23 23 dirstate::non_normal_entries::{
24 24 NonNormalEntries, NonNormalEntriesIterator,
25 25 },
26 26 dirstate::owning::OwningDirstateMap,
27 27 parsers::dirstate_parents_to_pytuple,
28 28 };
29 29 use hg::{
30 30 dirstate::parsers::Timestamp,
31 31 dirstate_tree::dispatch::DirstateMapMethods,
32 32 dirstate_tree::on_disk::DirstateV2ParseError,
33 33 errors::HgError,
34 34 revlog::Node,
35 35 utils::files::normalize_case,
36 36 utils::hg_path::{HgPath, HgPathBuf},
37 37 DirstateEntry, DirstateError, DirstateMap as RustDirstateMap,
38 38 DirstateParents, EntryState, StateMapIter,
39 39 };
40 40
41 41 // TODO
42 42 // This object needs to share references to multiple members of its Rust
43 43 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
44 44 // Right now `CopyMap` is done, but it needs to have an explicit reference
45 45 // to `RustDirstateMap` which itself needs to have an encapsulation for
46 46 // every method in `CopyMap` (copymapcopy, etc.).
47 47 // This is ugly and hard to maintain.
48 48 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
49 49 // `py_class!` is already implemented and does not mention
50 50 // `RustDirstateMap`, rightfully so.
51 51 // All attributes also have to have a separate refcount data attribute for
52 52 // leaks, with all methods that go along for reference sharing.
53 53 py_class!(pub class DirstateMap |py| {
54 54 @shared data inner: Box<dyn DirstateMapMethods + Send>;
55 55
56 56 /// Returns a `(dirstate_map, parents)` tuple
57 57 @staticmethod
58 58 def new(
59 59 use_dirstate_tree: bool,
60 60 use_dirstate_v2: bool,
61 61 on_disk: PyBytes,
62 62 ) -> PyResult<PyObject> {
63 63 let dirstate_error = |e: DirstateError| {
64 64 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
65 65 };
66 66 let (inner, parents) = if use_dirstate_tree || use_dirstate_v2 {
67 67 let (map, parents) =
68 68 OwningDirstateMap::new(py, on_disk, use_dirstate_v2)
69 69 .map_err(dirstate_error)?;
70 70 (Box::new(map) as _, parents)
71 71 } else {
72 72 let bytes = on_disk.data(py);
73 73 let mut map = RustDirstateMap::default();
74 74 let parents = map.read(bytes).map_err(dirstate_error)?;
75 75 (Box::new(map) as _, parents)
76 76 };
77 77 let map = Self::create_instance(py, inner)?;
78 78 let parents = parents.map(|p| dirstate_parents_to_pytuple(py, &p));
79 79 Ok((map, parents).to_py_object(py).into_object())
80 80 }
81 81
82 82 def clear(&self) -> PyResult<PyObject> {
83 83 self.inner(py).borrow_mut().clear();
84 84 Ok(py.None())
85 85 }
86 86
87 87 def get(
88 88 &self,
89 89 key: PyObject,
90 90 default: Option<PyObject> = None
91 91 ) -> PyResult<Option<PyObject>> {
92 92 let key = key.extract::<PyBytes>(py)?;
93 93 match self
94 94 .inner(py)
95 95 .borrow()
96 96 .get(HgPath::new(key.data(py)))
97 97 .map_err(|e| v2_error(py, e))?
98 98 {
99 99 Some(entry) => {
100 100 Ok(Some(make_dirstate_tuple(py, &entry)?))
101 101 },
102 102 None => Ok(default)
103 103 }
104 104 }
105 105
106 106 def addfile(
107 107 &self,
108 108 f: PyObject,
109 109 oldstate: PyObject,
110 110 state: PyObject,
111 111 mode: PyObject,
112 112 size: PyObject,
113 113 mtime: PyObject
114 114 ) -> PyResult<PyObject> {
115 let f = f.extract::<PyBytes>(py)?;
116 let filename = HgPath::new(f.data(py));
117 let oldstate = oldstate.extract::<PyBytes>(py)?.data(py)[0]
118 .try_into()
119 .map_err(|e: HgError| {
120 PyErr::new::<exc::ValueError, _>(py, e.to_string())
121 })?;
122 let state = state.extract::<PyBytes>(py)?.data(py)[0]
123 .try_into()
124 .map_err(|e: HgError| {
125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
126 })?;
127 let mode = mode.extract(py)?;
128 let size = size.extract(py)?;
129 let mtime = mtime.extract(py)?;
130 let entry = DirstateEntry {
131 state: state,
132 mode: mode,
133 size: size,
134 mtime: mtime,
135 };
115 136 self.inner(py).borrow_mut().add_file(
116 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
117 oldstate.extract::<PyBytes>(py)?.data(py)[0]
118 .try_into()
119 .map_err(|e: HgError| {
120 PyErr::new::<exc::ValueError, _>(py, e.to_string())
121 })?,
122 DirstateEntry {
123 state: state.extract::<PyBytes>(py)?.data(py)[0]
124 .try_into()
125 .map_err(|e: HgError| {
126 PyErr::new::<exc::ValueError, _>(py, e.to_string())
127 })?,
128 mode: mode.extract(py)?,
129 size: size.extract(py)?,
130 mtime: mtime.extract(py)?,
131 },
137 filename,
138 oldstate,
139 entry,
132 140 ).and(Ok(py.None())).or_else(|e: DirstateError| {
133 141 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
134 142 })
135 143 }
136 144
137 145 def removefile(
138 146 &self,
139 147 f: PyObject,
140 148 in_merge: PyObject
141 149 ) -> PyResult<PyObject> {
142 150 self.inner(py).borrow_mut()
143 151 .remove_file(
144 152 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
145 153 in_merge.extract::<PyBool>(py)?.is_true(),
146 154 )
147 155 .or_else(|_| {
148 156 Err(PyErr::new::<exc::OSError, _>(
149 157 py,
150 158 "Dirstate error".to_string(),
151 159 ))
152 160 })?;
153 161 Ok(py.None())
154 162 }
155 163
156 164 def dropfile(
157 165 &self,
158 166 f: PyObject,
159 167 oldstate: PyObject
160 168 ) -> PyResult<PyBool> {
161 169 self.inner(py).borrow_mut()
162 170 .drop_file(
163 171 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
164 172 oldstate.extract::<PyBytes>(py)?.data(py)[0]
165 173 .try_into()
166 174 .map_err(|e: HgError| {
167 175 PyErr::new::<exc::ValueError, _>(py, e.to_string())
168 176 })?,
169 177 )
170 178 .and_then(|b| Ok(b.to_py_object(py)))
171 179 .or_else(|e| {
172 180 Err(PyErr::new::<exc::OSError, _>(
173 181 py,
174 182 format!("Dirstate error: {}", e.to_string()),
175 183 ))
176 184 })
177 185 }
178 186
179 187 def clearambiguoustimes(
180 188 &self,
181 189 files: PyObject,
182 190 now: PyObject
183 191 ) -> PyResult<PyObject> {
184 192 let files: PyResult<Vec<HgPathBuf>> = files
185 193 .iter(py)?
186 194 .map(|filename| {
187 195 Ok(HgPathBuf::from_bytes(
188 196 filename?.extract::<PyBytes>(py)?.data(py),
189 197 ))
190 198 })
191 199 .collect();
192 200 self.inner(py)
193 201 .borrow_mut()
194 202 .clear_ambiguous_times(files?, now.extract(py)?)
195 203 .map_err(|e| v2_error(py, e))?;
196 204 Ok(py.None())
197 205 }
198 206
199 207 def other_parent_entries(&self) -> PyResult<PyObject> {
200 208 let mut inner_shared = self.inner(py).borrow_mut();
201 209 let set = PySet::empty(py)?;
202 210 for path in inner_shared.iter_other_parent_paths() {
203 211 let path = path.map_err(|e| v2_error(py, e))?;
204 212 set.add(py, PyBytes::new(py, path.as_bytes()))?;
205 213 }
206 214 Ok(set.into_object())
207 215 }
208 216
209 217 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
210 218 NonNormalEntries::from_inner(py, self.clone_ref(py))
211 219 }
212 220
213 221 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
214 222 let key = key.extract::<PyBytes>(py)?;
215 223 self.inner(py)
216 224 .borrow_mut()
217 225 .non_normal_entries_contains(HgPath::new(key.data(py)))
218 226 .map_err(|e| v2_error(py, e))
219 227 }
220 228
221 229 def non_normal_entries_display(&self) -> PyResult<PyString> {
222 230 let mut inner = self.inner(py).borrow_mut();
223 231 let paths = inner
224 232 .iter_non_normal_paths()
225 233 .collect::<Result<Vec<_>, _>>()
226 234 .map_err(|e| v2_error(py, e))?;
227 235 let formatted = format!("NonNormalEntries: {}", hg::utils::join_display(paths, ", "));
228 236 Ok(PyString::new(py, &formatted))
229 237 }
230 238
231 239 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
232 240 let key = key.extract::<PyBytes>(py)?;
233 241 self
234 242 .inner(py)
235 243 .borrow_mut()
236 244 .non_normal_entries_remove(HgPath::new(key.data(py)));
237 245 Ok(py.None())
238 246 }
239 247
240 248 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
241 249 let mut inner = self.inner(py).borrow_mut();
242 250
243 251 let ret = PyList::new(py, &[]);
244 252 for filename in inner.non_normal_or_other_parent_paths() {
245 253 let filename = filename.map_err(|e| v2_error(py, e))?;
246 254 let as_pystring = PyBytes::new(py, filename.as_bytes());
247 255 ret.append(py, as_pystring.into_object());
248 256 }
249 257 Ok(ret)
250 258 }
251 259
252 260 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
253 261 // Make sure the sets are defined before we no longer have a mutable
254 262 // reference to the dmap.
255 263 self.inner(py)
256 264 .borrow_mut()
257 265 .set_non_normal_other_parent_entries(false);
258 266
259 267 let leaked_ref = self.inner(py).leak_immutable();
260 268
261 269 NonNormalEntriesIterator::from_inner(py, unsafe {
262 270 leaked_ref.map(py, |o| {
263 271 o.iter_non_normal_paths_panic()
264 272 })
265 273 })
266 274 }
267 275
268 276 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
269 277 let d = d.extract::<PyBytes>(py)?;
270 278 Ok(self.inner(py).borrow_mut()
271 279 .has_tracked_dir(HgPath::new(d.data(py)))
272 280 .map_err(|e| {
273 281 PyErr::new::<exc::ValueError, _>(py, e.to_string())
274 282 })?
275 283 .to_py_object(py))
276 284 }
277 285
278 286 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
279 287 let d = d.extract::<PyBytes>(py)?;
280 288 Ok(self.inner(py).borrow_mut()
281 289 .has_dir(HgPath::new(d.data(py)))
282 290 .map_err(|e| {
283 291 PyErr::new::<exc::ValueError, _>(py, e.to_string())
284 292 })?
285 293 .to_py_object(py))
286 294 }
287 295
288 296 def write(
289 297 &self,
290 298 use_dirstate_v2: bool,
291 299 p1: PyObject,
292 300 p2: PyObject,
293 301 now: PyObject
294 302 ) -> PyResult<PyBytes> {
295 303 let now = Timestamp(now.extract(py)?);
296 304 let parents = DirstateParents {
297 305 p1: extract_node_id(py, &p1)?,
298 306 p2: extract_node_id(py, &p2)?,
299 307 };
300 308
301 309 let mut inner = self.inner(py).borrow_mut();
302 310 let result = if use_dirstate_v2 {
303 311 inner.pack_v2(parents, now)
304 312 } else {
305 313 inner.pack_v1(parents, now)
306 314 };
307 315 match result {
308 316 Ok(packed) => Ok(PyBytes::new(py, &packed)),
309 317 Err(_) => Err(PyErr::new::<exc::OSError, _>(
310 318 py,
311 319 "Dirstate error".to_string(),
312 320 )),
313 321 }
314 322 }
315 323
316 324 def filefoldmapasdict(&self) -> PyResult<PyDict> {
317 325 let dict = PyDict::new(py);
318 326 for item in self.inner(py).borrow_mut().iter() {
319 327 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
320 328 if entry.state != EntryState::Removed {
321 329 let key = normalize_case(path);
322 330 let value = path;
323 331 dict.set_item(
324 332 py,
325 333 PyBytes::new(py, key.as_bytes()).into_object(),
326 334 PyBytes::new(py, value.as_bytes()).into_object(),
327 335 )?;
328 336 }
329 337 }
330 338 Ok(dict)
331 339 }
332 340
333 341 def __len__(&self) -> PyResult<usize> {
334 342 Ok(self.inner(py).borrow().len())
335 343 }
336 344
337 345 def __contains__(&self, key: PyObject) -> PyResult<bool> {
338 346 let key = key.extract::<PyBytes>(py)?;
339 347 self.inner(py)
340 348 .borrow()
341 349 .contains_key(HgPath::new(key.data(py)))
342 350 .map_err(|e| v2_error(py, e))
343 351 }
344 352
345 353 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
346 354 let key = key.extract::<PyBytes>(py)?;
347 355 let key = HgPath::new(key.data(py));
348 356 match self
349 357 .inner(py)
350 358 .borrow()
351 359 .get(key)
352 360 .map_err(|e| v2_error(py, e))?
353 361 {
354 362 Some(entry) => {
355 363 Ok(make_dirstate_tuple(py, &entry)?)
356 364 },
357 365 None => Err(PyErr::new::<exc::KeyError, _>(
358 366 py,
359 367 String::from_utf8_lossy(key.as_bytes()),
360 368 )),
361 369 }
362 370 }
363 371
364 372 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
365 373 let leaked_ref = self.inner(py).leak_immutable();
366 374 DirstateMapKeysIterator::from_inner(
367 375 py,
368 376 unsafe { leaked_ref.map(py, |o| o.iter()) },
369 377 )
370 378 }
371 379
372 380 def items(&self) -> PyResult<DirstateMapItemsIterator> {
373 381 let leaked_ref = self.inner(py).leak_immutable();
374 382 DirstateMapItemsIterator::from_inner(
375 383 py,
376 384 unsafe { leaked_ref.map(py, |o| o.iter()) },
377 385 )
378 386 }
379 387
380 388 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
381 389 let leaked_ref = self.inner(py).leak_immutable();
382 390 DirstateMapKeysIterator::from_inner(
383 391 py,
384 392 unsafe { leaked_ref.map(py, |o| o.iter()) },
385 393 )
386 394 }
387 395
388 396 // TODO all copymap* methods, see docstring above
389 397 def copymapcopy(&self) -> PyResult<PyDict> {
390 398 let dict = PyDict::new(py);
391 399 for item in self.inner(py).borrow().copy_map_iter() {
392 400 let (key, value) = item.map_err(|e| v2_error(py, e))?;
393 401 dict.set_item(
394 402 py,
395 403 PyBytes::new(py, key.as_bytes()),
396 404 PyBytes::new(py, value.as_bytes()),
397 405 )?;
398 406 }
399 407 Ok(dict)
400 408 }
401 409
402 410 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
403 411 let key = key.extract::<PyBytes>(py)?;
404 412 match self
405 413 .inner(py)
406 414 .borrow()
407 415 .copy_map_get(HgPath::new(key.data(py)))
408 416 .map_err(|e| v2_error(py, e))?
409 417 {
410 418 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
411 419 None => Err(PyErr::new::<exc::KeyError, _>(
412 420 py,
413 421 String::from_utf8_lossy(key.data(py)),
414 422 )),
415 423 }
416 424 }
417 425 def copymap(&self) -> PyResult<CopyMap> {
418 426 CopyMap::from_inner(py, self.clone_ref(py))
419 427 }
420 428
421 429 def copymaplen(&self) -> PyResult<usize> {
422 430 Ok(self.inner(py).borrow().copy_map_len())
423 431 }
424 432 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
425 433 let key = key.extract::<PyBytes>(py)?;
426 434 self.inner(py)
427 435 .borrow()
428 436 .copy_map_contains_key(HgPath::new(key.data(py)))
429 437 .map_err(|e| v2_error(py, e))
430 438 }
431 439 def copymapget(
432 440 &self,
433 441 key: PyObject,
434 442 default: Option<PyObject>
435 443 ) -> PyResult<Option<PyObject>> {
436 444 let key = key.extract::<PyBytes>(py)?;
437 445 match self
438 446 .inner(py)
439 447 .borrow()
440 448 .copy_map_get(HgPath::new(key.data(py)))
441 449 .map_err(|e| v2_error(py, e))?
442 450 {
443 451 Some(copy) => Ok(Some(
444 452 PyBytes::new(py, copy.as_bytes()).into_object(),
445 453 )),
446 454 None => Ok(default),
447 455 }
448 456 }
449 457 def copymapsetitem(
450 458 &self,
451 459 key: PyObject,
452 460 value: PyObject
453 461 ) -> PyResult<PyObject> {
454 462 let key = key.extract::<PyBytes>(py)?;
455 463 let value = value.extract::<PyBytes>(py)?;
456 464 self.inner(py)
457 465 .borrow_mut()
458 466 .copy_map_insert(
459 467 HgPathBuf::from_bytes(key.data(py)),
460 468 HgPathBuf::from_bytes(value.data(py)),
461 469 )
462 470 .map_err(|e| v2_error(py, e))?;
463 471 Ok(py.None())
464 472 }
465 473 def copymappop(
466 474 &self,
467 475 key: PyObject,
468 476 default: Option<PyObject>
469 477 ) -> PyResult<Option<PyObject>> {
470 478 let key = key.extract::<PyBytes>(py)?;
471 479 match self
472 480 .inner(py)
473 481 .borrow_mut()
474 482 .copy_map_remove(HgPath::new(key.data(py)))
475 483 .map_err(|e| v2_error(py, e))?
476 484 {
477 485 Some(_) => Ok(None),
478 486 None => Ok(default),
479 487 }
480 488 }
481 489
482 490 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
483 491 let leaked_ref = self.inner(py).leak_immutable();
484 492 CopyMapKeysIterator::from_inner(
485 493 py,
486 494 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
487 495 )
488 496 }
489 497
490 498 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
491 499 let leaked_ref = self.inner(py).leak_immutable();
492 500 CopyMapItemsIterator::from_inner(
493 501 py,
494 502 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
495 503 )
496 504 }
497 505
498 506 def directories(&self) -> PyResult<PyList> {
499 507 let dirs = PyList::new(py, &[]);
500 508 for item in self.inner(py).borrow().iter_directories() {
501 509 let (path, mtime) = item.map_err(|e| v2_error(py, e))?;
502 510 let path = PyBytes::new(py, path.as_bytes());
503 511 let mtime = mtime.map(|t| t.0).unwrap_or(-1);
504 512 let tuple = (path, (b'd', 0, 0, mtime));
505 513 dirs.append(py, tuple.to_py_object(py).into_object())
506 514 }
507 515 Ok(dirs)
508 516 }
509 517
510 518 });
511 519
512 520 impl DirstateMap {
513 521 pub fn get_inner_mut<'a>(
514 522 &'a self,
515 523 py: Python<'a>,
516 524 ) -> RefMut<'a, Box<dyn DirstateMapMethods + Send>> {
517 525 self.inner(py).borrow_mut()
518 526 }
519 527 fn translate_key(
520 528 py: Python,
521 529 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
522 530 ) -> PyResult<Option<PyBytes>> {
523 531 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
524 532 Ok(Some(PyBytes::new(py, f.as_bytes())))
525 533 }
526 534 fn translate_key_value(
527 535 py: Python,
528 536 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
529 537 ) -> PyResult<Option<(PyBytes, PyObject)>> {
530 538 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
531 539 Ok(Some((
532 540 PyBytes::new(py, f.as_bytes()),
533 541 make_dirstate_tuple(py, &entry)?,
534 542 )))
535 543 }
536 544 }
537 545
538 546 py_shared_iterator!(
539 547 DirstateMapKeysIterator,
540 548 UnsafePyLeaked<StateMapIter<'static>>,
541 549 DirstateMap::translate_key,
542 550 Option<PyBytes>
543 551 );
544 552
545 553 py_shared_iterator!(
546 554 DirstateMapItemsIterator,
547 555 UnsafePyLeaked<StateMapIter<'static>>,
548 556 DirstateMap::translate_key_value,
549 557 Option<(PyBytes, PyObject)>
550 558 );
551 559
552 560 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
553 561 let bytes = obj.extract::<PyBytes>(py)?;
554 562 match bytes.data(py).try_into() {
555 563 Ok(s) => Ok(s),
556 564 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
557 565 }
558 566 }
559 567
560 568 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
561 569 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
562 570 }
General Comments 0
You need to be logged in to leave comments. Login now