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