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