##// END OF EJS Templates
rust-cpython: move $leaked struct out of macro...
Yuya Nishihara -
r43447:5cb8867c default
parent child Browse files
Show More
@@ -1,118 +1,119 b''
1 // copymap.rs
1 // copymap.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for `hg::dirstate::dirstate_map::CopyMap` provided by the
8 //! Bindings for `hg::dirstate::dirstate_map::CopyMap` provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use cpython::{PyBytes, PyClone, PyDict, PyObject, PyResult, Python};
11 use cpython::{PyBytes, PyClone, PyDict, PyObject, PyResult, Python};
12 use std::cell::RefCell;
12 use std::cell::RefCell;
13
13
14 use crate::dirstate::dirstate_map::{DirstateMap, DirstateMapLeakedRef};
14 use crate::dirstate::dirstate_map::DirstateMap;
15 use crate::ref_sharing::PyLeakedRef;
15 use hg::{utils::hg_path::HgPathBuf, CopyMapIter};
16 use hg::{utils::hg_path::HgPathBuf, CopyMapIter};
16
17
17 py_class!(pub class CopyMap |py| {
18 py_class!(pub class CopyMap |py| {
18 data dirstate_map: DirstateMap;
19 data dirstate_map: DirstateMap;
19
20
20 def __getitem__(&self, key: PyObject) -> PyResult<PyBytes> {
21 def __getitem__(&self, key: PyObject) -> PyResult<PyBytes> {
21 (*self.dirstate_map(py)).copymapgetitem(py, key)
22 (*self.dirstate_map(py)).copymapgetitem(py, key)
22 }
23 }
23
24
24 def __len__(&self) -> PyResult<usize> {
25 def __len__(&self) -> PyResult<usize> {
25 self.dirstate_map(py).copymaplen(py)
26 self.dirstate_map(py).copymaplen(py)
26 }
27 }
27
28
28 def __contains__(&self, key: PyObject) -> PyResult<bool> {
29 def __contains__(&self, key: PyObject) -> PyResult<bool> {
29 self.dirstate_map(py).copymapcontains(py, key)
30 self.dirstate_map(py).copymapcontains(py, key)
30 }
31 }
31
32
32 def get(
33 def get(
33 &self,
34 &self,
34 key: PyObject,
35 key: PyObject,
35 default: Option<PyObject> = None
36 default: Option<PyObject> = None
36 ) -> PyResult<Option<PyObject>> {
37 ) -> PyResult<Option<PyObject>> {
37 self.dirstate_map(py).copymapget(py, key, default)
38 self.dirstate_map(py).copymapget(py, key, default)
38 }
39 }
39
40
40 def pop(
41 def pop(
41 &self,
42 &self,
42 key: PyObject,
43 key: PyObject,
43 default: Option<PyObject> = None
44 default: Option<PyObject> = None
44 ) -> PyResult<Option<PyObject>> {
45 ) -> PyResult<Option<PyObject>> {
45 self.dirstate_map(py).copymappop(py, key, default)
46 self.dirstate_map(py).copymappop(py, key, default)
46 }
47 }
47
48
48 def __iter__(&self) -> PyResult<CopyMapKeysIterator> {
49 def __iter__(&self) -> PyResult<CopyMapKeysIterator> {
49 self.dirstate_map(py).copymapiter(py)
50 self.dirstate_map(py).copymapiter(py)
50 }
51 }
51
52
52 // Python's `dict()` builtin works with either a subclass of dict
53 // Python's `dict()` builtin works with either a subclass of dict
53 // or an abstract mapping. Said mapping needs to implement `__getitem__`
54 // or an abstract mapping. Said mapping needs to implement `__getitem__`
54 // and `keys`.
55 // and `keys`.
55 def keys(&self) -> PyResult<CopyMapKeysIterator> {
56 def keys(&self) -> PyResult<CopyMapKeysIterator> {
56 self.dirstate_map(py).copymapiter(py)
57 self.dirstate_map(py).copymapiter(py)
57 }
58 }
58
59
59 def items(&self) -> PyResult<CopyMapItemsIterator> {
60 def items(&self) -> PyResult<CopyMapItemsIterator> {
60 self.dirstate_map(py).copymapitemsiter(py)
61 self.dirstate_map(py).copymapitemsiter(py)
61 }
62 }
62
63
63 def iteritems(&self) -> PyResult<CopyMapItemsIterator> {
64 def iteritems(&self) -> PyResult<CopyMapItemsIterator> {
64 self.dirstate_map(py).copymapitemsiter(py)
65 self.dirstate_map(py).copymapitemsiter(py)
65 }
66 }
66
67
67 def __setitem__(
68 def __setitem__(
68 &self,
69 &self,
69 key: PyObject,
70 key: PyObject,
70 item: PyObject
71 item: PyObject
71 ) -> PyResult<()> {
72 ) -> PyResult<()> {
72 self.dirstate_map(py).copymapsetitem(py, key, item)?;
73 self.dirstate_map(py).copymapsetitem(py, key, item)?;
73 Ok(())
74 Ok(())
74 }
75 }
75
76
76 def copy(&self) -> PyResult<PyDict> {
77 def copy(&self) -> PyResult<PyDict> {
77 self.dirstate_map(py).copymapcopy(py)
78 self.dirstate_map(py).copymapcopy(py)
78 }
79 }
79
80
80 });
81 });
81
82
82 impl CopyMap {
83 impl CopyMap {
83 pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
84 pub fn from_inner(py: Python, dm: DirstateMap) -> PyResult<Self> {
84 Self::create_instance(py, dm)
85 Self::create_instance(py, dm)
85 }
86 }
86 fn translate_key(
87 fn translate_key(
87 py: Python,
88 py: Python,
88 res: (&HgPathBuf, &HgPathBuf),
89 res: (&HgPathBuf, &HgPathBuf),
89 ) -> PyResult<Option<PyBytes>> {
90 ) -> PyResult<Option<PyBytes>> {
90 Ok(Some(PyBytes::new(py, res.0.as_ref())))
91 Ok(Some(PyBytes::new(py, res.0.as_ref())))
91 }
92 }
92 fn translate_key_value(
93 fn translate_key_value(
93 py: Python,
94 py: Python,
94 res: (&HgPathBuf, &HgPathBuf),
95 res: (&HgPathBuf, &HgPathBuf),
95 ) -> PyResult<Option<(PyBytes, PyBytes)>> {
96 ) -> PyResult<Option<(PyBytes, PyBytes)>> {
96 let (k, v) = res;
97 let (k, v) = res;
97 Ok(Some((
98 Ok(Some((
98 PyBytes::new(py, k.as_ref()),
99 PyBytes::new(py, k.as_ref()),
99 PyBytes::new(py, v.as_ref()),
100 PyBytes::new(py, v.as_ref()),
100 )))
101 )))
101 }
102 }
102 }
103 }
103
104
104 py_shared_iterator!(
105 py_shared_iterator!(
105 CopyMapKeysIterator,
106 CopyMapKeysIterator,
106 DirstateMapLeakedRef,
107 PyLeakedRef,
107 CopyMapIter<'static>,
108 CopyMapIter<'static>,
108 CopyMap::translate_key,
109 CopyMap::translate_key,
109 Option<PyBytes>
110 Option<PyBytes>
110 );
111 );
111
112
112 py_shared_iterator!(
113 py_shared_iterator!(
113 CopyMapItemsIterator,
114 CopyMapItemsIterator,
114 DirstateMapLeakedRef,
115 PyLeakedRef,
115 CopyMapIter<'static>,
116 CopyMapIter<'static>,
116 CopyMap::translate_key_value,
117 CopyMap::translate_key_value,
117 Option<(PyBytes, PyBytes)>
118 Option<(PyBytes, PyBytes)>
118 );
119 );
@@ -1,130 +1,131 b''
1 // dirs_multiset.rs
1 // dirs_multiset.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::RefCell;
11 use std::cell::RefCell;
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13
13
14 use cpython::{
14 use cpython::{
15 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
15 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
16 Python,
16 Python,
17 };
17 };
18
18
19 use crate::{dirstate::extract_dirstate, ref_sharing::PySharedRefCell};
19 use crate::dirstate::extract_dirstate;
20 use crate::ref_sharing::{PyLeakedRef, PySharedRefCell};
20 use hg::{
21 use hg::{
21 utils::hg_path::{HgPath, HgPathBuf},
22 utils::hg_path::{HgPath, HgPathBuf},
22 DirsMultiset, DirsMultisetIter, DirstateMapError, DirstateParseError,
23 DirsMultiset, DirsMultisetIter, DirstateMapError, DirstateParseError,
23 EntryState,
24 EntryState,
24 };
25 };
25
26
26 py_class!(pub class Dirs |py| {
27 py_class!(pub class Dirs |py| {
27 data inner: PySharedRefCell<DirsMultiset>;
28 data inner: PySharedRefCell<DirsMultiset>;
28
29
29 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
30 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
30 // a `list`)
31 // a `list`)
31 def __new__(
32 def __new__(
32 _cls,
33 _cls,
33 map: PyObject,
34 map: PyObject,
34 skip: Option<PyObject> = None
35 skip: Option<PyObject> = None
35 ) -> PyResult<Self> {
36 ) -> PyResult<Self> {
36 let mut skip_state: Option<EntryState> = None;
37 let mut skip_state: Option<EntryState> = None;
37 if let Some(skip) = skip {
38 if let Some(skip) = skip {
38 skip_state = Some(
39 skip_state = Some(
39 skip.extract::<PyBytes>(py)?.data(py)[0]
40 skip.extract::<PyBytes>(py)?.data(py)[0]
40 .try_into()
41 .try_into()
41 .map_err(|e: DirstateParseError| {
42 .map_err(|e: DirstateParseError| {
42 PyErr::new::<exc::ValueError, _>(py, e.to_string())
43 PyErr::new::<exc::ValueError, _>(py, e.to_string())
43 })?,
44 })?,
44 );
45 );
45 }
46 }
46 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
47 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
47 let dirstate = extract_dirstate(py, &map)?;
48 let dirstate = extract_dirstate(py, &map)?;
48 DirsMultiset::from_dirstate(&dirstate, skip_state)
49 DirsMultiset::from_dirstate(&dirstate, skip_state)
49 } else {
50 } else {
50 let map: Result<Vec<HgPathBuf>, PyErr> = map
51 let map: Result<Vec<HgPathBuf>, PyErr> = map
51 .iter(py)?
52 .iter(py)?
52 .map(|o| {
53 .map(|o| {
53 Ok(HgPathBuf::from_bytes(
54 Ok(HgPathBuf::from_bytes(
54 o?.extract::<PyBytes>(py)?.data(py),
55 o?.extract::<PyBytes>(py)?.data(py),
55 ))
56 ))
56 })
57 })
57 .collect();
58 .collect();
58 DirsMultiset::from_manifest(&map?)
59 DirsMultiset::from_manifest(&map?)
59 };
60 };
60
61
61 Self::create_instance(
62 Self::create_instance(
62 py,
63 py,
63 PySharedRefCell::new(inner),
64 PySharedRefCell::new(inner),
64 )
65 )
65 }
66 }
66
67
67 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
68 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
68 self.borrow_mut(py)?.add_path(
69 self.borrow_mut(py)?.add_path(
69 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
70 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
70 );
71 );
71 Ok(py.None())
72 Ok(py.None())
72 }
73 }
73
74
74 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
75 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
75 self.borrow_mut(py)?.delete_path(
76 self.borrow_mut(py)?.delete_path(
76 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
77 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
77 )
78 )
78 .and(Ok(py.None()))
79 .and(Ok(py.None()))
79 .or_else(|e| {
80 .or_else(|e| {
80 match e {
81 match e {
81 DirstateMapError::PathNotFound(_p) => {
82 DirstateMapError::PathNotFound(_p) => {
82 Err(PyErr::new::<exc::ValueError, _>(
83 Err(PyErr::new::<exc::ValueError, _>(
83 py,
84 py,
84 "expected a value, found none".to_string(),
85 "expected a value, found none".to_string(),
85 ))
86 ))
86 }
87 }
87 DirstateMapError::EmptyPath => {
88 DirstateMapError::EmptyPath => {
88 Ok(py.None())
89 Ok(py.None())
89 }
90 }
90 }
91 }
91 })
92 })
92 }
93 }
93 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
94 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
94 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
95 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
95 DirsMultisetKeysIterator::from_inner(
96 DirsMultisetKeysIterator::from_inner(
96 py,
97 py,
97 leak_handle,
98 leak_handle,
98 leaked_ref.iter(),
99 leaked_ref.iter(),
99 )
100 )
100 }
101 }
101
102
102 def __contains__(&self, item: PyObject) -> PyResult<bool> {
103 def __contains__(&self, item: PyObject) -> PyResult<bool> {
103 Ok(self.inner(py).borrow().contains(HgPath::new(
104 Ok(self.inner(py).borrow().contains(HgPath::new(
104 item.extract::<PyBytes>(py)?.data(py).as_ref(),
105 item.extract::<PyBytes>(py)?.data(py).as_ref(),
105 )))
106 )))
106 }
107 }
107 });
108 });
108
109
109 py_shared_ref!(Dirs, DirsMultiset, inner, DirsMultisetLeakedRef,);
110 py_shared_ref!(Dirs, DirsMultiset, inner);
110
111
111 impl Dirs {
112 impl Dirs {
112 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
113 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
113 Self::create_instance(py, PySharedRefCell::new(d))
114 Self::create_instance(py, PySharedRefCell::new(d))
114 }
115 }
115
116
116 fn translate_key(
117 fn translate_key(
117 py: Python,
118 py: Python,
118 res: &HgPathBuf,
119 res: &HgPathBuf,
119 ) -> PyResult<Option<PyBytes>> {
120 ) -> PyResult<Option<PyBytes>> {
120 Ok(Some(PyBytes::new(py, res.as_ref())))
121 Ok(Some(PyBytes::new(py, res.as_ref())))
121 }
122 }
122 }
123 }
123
124
124 py_shared_iterator!(
125 py_shared_iterator!(
125 DirsMultisetKeysIterator,
126 DirsMultisetKeysIterator,
126 DirsMultisetLeakedRef,
127 PyLeakedRef,
127 DirsMultisetIter<'static>,
128 DirsMultisetIter<'static>,
128 Dirs::translate_key,
129 Dirs::translate_key,
129 Option<PyBytes>
130 Option<PyBytes>
130 );
131 );
@@ -1,528 +1,528 b''
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::RefCell;
11 use std::cell::RefCell;
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13 use std::time::Duration;
13 use std::time::Duration;
14
14
15 use cpython::{
15 use cpython::{
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
18 };
18 };
19 use libc::c_char;
19 use libc::c_char;
20
20
21 use crate::{
21 use crate::{
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
24 ref_sharing::PySharedRefCell,
24 ref_sharing::{PyLeakedRef, PySharedRefCell},
25 };
25 };
26 use hg::{
26 use hg::{
27 utils::hg_path::{HgPath, HgPathBuf},
27 utils::hg_path::{HgPath, HgPathBuf},
28 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
28 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
29 DirstateParents, DirstateParseError, EntryState, StateMapIter,
29 DirstateParents, DirstateParseError, EntryState, StateMapIter,
30 PARENT_SIZE,
30 PARENT_SIZE,
31 };
31 };
32
32
33 // TODO
33 // TODO
34 // This object needs to share references to multiple members of its Rust
34 // This object needs to share references to multiple members of its Rust
35 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
35 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
36 // Right now `CopyMap` is done, but it needs to have an explicit reference
36 // Right now `CopyMap` is done, but it needs to have an explicit reference
37 // to `RustDirstateMap` which itself needs to have an encapsulation for
37 // to `RustDirstateMap` which itself needs to have an encapsulation for
38 // every method in `CopyMap` (copymapcopy, etc.).
38 // every method in `CopyMap` (copymapcopy, etc.).
39 // This is ugly and hard to maintain.
39 // This is ugly and hard to maintain.
40 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
40 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
41 // `py_class!` is already implemented and does not mention
41 // `py_class!` is already implemented and does not mention
42 // `RustDirstateMap`, rightfully so.
42 // `RustDirstateMap`, rightfully so.
43 // All attributes also have to have a separate refcount data attribute for
43 // All attributes also have to have a separate refcount data attribute for
44 // leaks, with all methods that go along for reference sharing.
44 // leaks, with all methods that go along for reference sharing.
45 py_class!(pub class DirstateMap |py| {
45 py_class!(pub class DirstateMap |py| {
46 data inner: PySharedRefCell<RustDirstateMap>;
46 data inner: PySharedRefCell<RustDirstateMap>;
47
47
48 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
48 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
49 let inner = RustDirstateMap::default();
49 let inner = RustDirstateMap::default();
50 Self::create_instance(
50 Self::create_instance(
51 py,
51 py,
52 PySharedRefCell::new(inner),
52 PySharedRefCell::new(inner),
53 )
53 )
54 }
54 }
55
55
56 def clear(&self) -> PyResult<PyObject> {
56 def clear(&self) -> PyResult<PyObject> {
57 self.borrow_mut(py)?.clear();
57 self.borrow_mut(py)?.clear();
58 Ok(py.None())
58 Ok(py.None())
59 }
59 }
60
60
61 def get(
61 def get(
62 &self,
62 &self,
63 key: PyObject,
63 key: PyObject,
64 default: Option<PyObject> = None
64 default: Option<PyObject> = None
65 ) -> PyResult<Option<PyObject>> {
65 ) -> PyResult<Option<PyObject>> {
66 let key = key.extract::<PyBytes>(py)?;
66 let key = key.extract::<PyBytes>(py)?;
67 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
67 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
68 Some(entry) => {
68 Some(entry) => {
69 // Explicitly go through u8 first, then cast to
69 // Explicitly go through u8 first, then cast to
70 // platform-specific `c_char`.
70 // platform-specific `c_char`.
71 let state: u8 = entry.state.into();
71 let state: u8 = entry.state.into();
72 Ok(Some(decapsule_make_dirstate_tuple(py)?(
72 Ok(Some(decapsule_make_dirstate_tuple(py)?(
73 state as c_char,
73 state as c_char,
74 entry.mode,
74 entry.mode,
75 entry.size,
75 entry.size,
76 entry.mtime,
76 entry.mtime,
77 )))
77 )))
78 },
78 },
79 None => Ok(default)
79 None => Ok(default)
80 }
80 }
81 }
81 }
82
82
83 def addfile(
83 def addfile(
84 &self,
84 &self,
85 f: PyObject,
85 f: PyObject,
86 oldstate: PyObject,
86 oldstate: PyObject,
87 state: PyObject,
87 state: PyObject,
88 mode: PyObject,
88 mode: PyObject,
89 size: PyObject,
89 size: PyObject,
90 mtime: PyObject
90 mtime: PyObject
91 ) -> PyResult<PyObject> {
91 ) -> PyResult<PyObject> {
92 self.borrow_mut(py)?.add_file(
92 self.borrow_mut(py)?.add_file(
93 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
93 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
94 oldstate.extract::<PyBytes>(py)?.data(py)[0]
94 oldstate.extract::<PyBytes>(py)?.data(py)[0]
95 .try_into()
95 .try_into()
96 .map_err(|e: DirstateParseError| {
96 .map_err(|e: DirstateParseError| {
97 PyErr::new::<exc::ValueError, _>(py, e.to_string())
97 PyErr::new::<exc::ValueError, _>(py, e.to_string())
98 })?,
98 })?,
99 DirstateEntry {
99 DirstateEntry {
100 state: state.extract::<PyBytes>(py)?.data(py)[0]
100 state: state.extract::<PyBytes>(py)?.data(py)[0]
101 .try_into()
101 .try_into()
102 .map_err(|e: DirstateParseError| {
102 .map_err(|e: DirstateParseError| {
103 PyErr::new::<exc::ValueError, _>(py, e.to_string())
103 PyErr::new::<exc::ValueError, _>(py, e.to_string())
104 })?,
104 })?,
105 mode: mode.extract(py)?,
105 mode: mode.extract(py)?,
106 size: size.extract(py)?,
106 size: size.extract(py)?,
107 mtime: mtime.extract(py)?,
107 mtime: mtime.extract(py)?,
108 },
108 },
109 );
109 );
110 Ok(py.None())
110 Ok(py.None())
111 }
111 }
112
112
113 def removefile(
113 def removefile(
114 &self,
114 &self,
115 f: PyObject,
115 f: PyObject,
116 oldstate: PyObject,
116 oldstate: PyObject,
117 size: PyObject
117 size: PyObject
118 ) -> PyResult<PyObject> {
118 ) -> PyResult<PyObject> {
119 self.borrow_mut(py)?
119 self.borrow_mut(py)?
120 .remove_file(
120 .remove_file(
121 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
121 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
122 oldstate.extract::<PyBytes>(py)?.data(py)[0]
122 oldstate.extract::<PyBytes>(py)?.data(py)[0]
123 .try_into()
123 .try_into()
124 .map_err(|e: DirstateParseError| {
124 .map_err(|e: DirstateParseError| {
125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
126 })?,
126 })?,
127 size.extract(py)?,
127 size.extract(py)?,
128 )
128 )
129 .or_else(|_| {
129 .or_else(|_| {
130 Err(PyErr::new::<exc::OSError, _>(
130 Err(PyErr::new::<exc::OSError, _>(
131 py,
131 py,
132 "Dirstate error".to_string(),
132 "Dirstate error".to_string(),
133 ))
133 ))
134 })?;
134 })?;
135 Ok(py.None())
135 Ok(py.None())
136 }
136 }
137
137
138 def dropfile(
138 def dropfile(
139 &self,
139 &self,
140 f: PyObject,
140 f: PyObject,
141 oldstate: PyObject
141 oldstate: PyObject
142 ) -> PyResult<PyBool> {
142 ) -> PyResult<PyBool> {
143 self.borrow_mut(py)?
143 self.borrow_mut(py)?
144 .drop_file(
144 .drop_file(
145 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
145 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
146 oldstate.extract::<PyBytes>(py)?.data(py)[0]
146 oldstate.extract::<PyBytes>(py)?.data(py)[0]
147 .try_into()
147 .try_into()
148 .map_err(|e: DirstateParseError| {
148 .map_err(|e: DirstateParseError| {
149 PyErr::new::<exc::ValueError, _>(py, e.to_string())
149 PyErr::new::<exc::ValueError, _>(py, e.to_string())
150 })?,
150 })?,
151 )
151 )
152 .and_then(|b| Ok(b.to_py_object(py)))
152 .and_then(|b| Ok(b.to_py_object(py)))
153 .or_else(|_| {
153 .or_else(|_| {
154 Err(PyErr::new::<exc::OSError, _>(
154 Err(PyErr::new::<exc::OSError, _>(
155 py,
155 py,
156 "Dirstate error".to_string(),
156 "Dirstate error".to_string(),
157 ))
157 ))
158 })
158 })
159 }
159 }
160
160
161 def clearambiguoustimes(
161 def clearambiguoustimes(
162 &self,
162 &self,
163 files: PyObject,
163 files: PyObject,
164 now: PyObject
164 now: PyObject
165 ) -> PyResult<PyObject> {
165 ) -> PyResult<PyObject> {
166 let files: PyResult<Vec<HgPathBuf>> = files
166 let files: PyResult<Vec<HgPathBuf>> = files
167 .iter(py)?
167 .iter(py)?
168 .map(|filename| {
168 .map(|filename| {
169 Ok(HgPathBuf::from_bytes(
169 Ok(HgPathBuf::from_bytes(
170 filename?.extract::<PyBytes>(py)?.data(py),
170 filename?.extract::<PyBytes>(py)?.data(py),
171 ))
171 ))
172 })
172 })
173 .collect();
173 .collect();
174 self.borrow_mut(py)?
174 self.borrow_mut(py)?
175 .clear_ambiguous_times(files?, now.extract(py)?);
175 .clear_ambiguous_times(files?, now.extract(py)?);
176 Ok(py.None())
176 Ok(py.None())
177 }
177 }
178
178
179 // TODO share the reference
179 // TODO share the reference
180 def nonnormalentries(&self) -> PyResult<PyObject> {
180 def nonnormalentries(&self) -> PyResult<PyObject> {
181 let (non_normal, other_parent) =
181 let (non_normal, other_parent) =
182 self.inner(py).borrow().non_normal_other_parent_entries();
182 self.inner(py).borrow().non_normal_other_parent_entries();
183
183
184 let locals = PyDict::new(py);
184 let locals = PyDict::new(py);
185 locals.set_item(
185 locals.set_item(
186 py,
186 py,
187 "non_normal",
187 "non_normal",
188 non_normal
188 non_normal
189 .iter()
189 .iter()
190 .map(|v| PyBytes::new(py, v.as_ref()))
190 .map(|v| PyBytes::new(py, v.as_ref()))
191 .collect::<Vec<PyBytes>>()
191 .collect::<Vec<PyBytes>>()
192 .to_py_object(py),
192 .to_py_object(py),
193 )?;
193 )?;
194 locals.set_item(
194 locals.set_item(
195 py,
195 py,
196 "other_parent",
196 "other_parent",
197 other_parent
197 other_parent
198 .iter()
198 .iter()
199 .map(|v| PyBytes::new(py, v.as_ref()))
199 .map(|v| PyBytes::new(py, v.as_ref()))
200 .collect::<Vec<PyBytes>>()
200 .collect::<Vec<PyBytes>>()
201 .to_py_object(py),
201 .to_py_object(py),
202 )?;
202 )?;
203
203
204 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
204 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
205 }
205 }
206
206
207 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
207 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
208 let d = d.extract::<PyBytes>(py)?;
208 let d = d.extract::<PyBytes>(py)?;
209 Ok(self.borrow_mut(py)?
209 Ok(self.borrow_mut(py)?
210 .has_tracked_dir(HgPath::new(d.data(py)))
210 .has_tracked_dir(HgPath::new(d.data(py)))
211 .to_py_object(py))
211 .to_py_object(py))
212 }
212 }
213
213
214 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
214 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
215 let d = d.extract::<PyBytes>(py)?;
215 let d = d.extract::<PyBytes>(py)?;
216 Ok(self.borrow_mut(py)?
216 Ok(self.borrow_mut(py)?
217 .has_dir(HgPath::new(d.data(py)))
217 .has_dir(HgPath::new(d.data(py)))
218 .to_py_object(py))
218 .to_py_object(py))
219 }
219 }
220
220
221 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
221 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
222 self.borrow_mut(py)?
222 self.borrow_mut(py)?
223 .parents(st.extract::<PyBytes>(py)?.data(py))
223 .parents(st.extract::<PyBytes>(py)?.data(py))
224 .and_then(|d| {
224 .and_then(|d| {
225 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
225 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
226 .to_py_object(py))
226 .to_py_object(py))
227 })
227 })
228 .or_else(|_| {
228 .or_else(|_| {
229 Err(PyErr::new::<exc::OSError, _>(
229 Err(PyErr::new::<exc::OSError, _>(
230 py,
230 py,
231 "Dirstate error".to_string(),
231 "Dirstate error".to_string(),
232 ))
232 ))
233 })
233 })
234 }
234 }
235
235
236 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
236 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
237 let p1 = extract_node_id(py, &p1)?;
237 let p1 = extract_node_id(py, &p1)?;
238 let p2 = extract_node_id(py, &p2)?;
238 let p2 = extract_node_id(py, &p2)?;
239
239
240 self.borrow_mut(py)?
240 self.borrow_mut(py)?
241 .set_parents(&DirstateParents { p1, p2 });
241 .set_parents(&DirstateParents { p1, p2 });
242 Ok(py.None())
242 Ok(py.None())
243 }
243 }
244
244
245 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
245 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
246 match self.borrow_mut(py)?
246 match self.borrow_mut(py)?
247 .read(st.extract::<PyBytes>(py)?.data(py))
247 .read(st.extract::<PyBytes>(py)?.data(py))
248 {
248 {
249 Ok(Some(parents)) => Ok(Some(
249 Ok(Some(parents)) => Ok(Some(
250 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
250 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
251 .to_py_object(py)
251 .to_py_object(py)
252 .into_object(),
252 .into_object(),
253 )),
253 )),
254 Ok(None) => Ok(Some(py.None())),
254 Ok(None) => Ok(Some(py.None())),
255 Err(_) => Err(PyErr::new::<exc::OSError, _>(
255 Err(_) => Err(PyErr::new::<exc::OSError, _>(
256 py,
256 py,
257 "Dirstate error".to_string(),
257 "Dirstate error".to_string(),
258 )),
258 )),
259 }
259 }
260 }
260 }
261 def write(
261 def write(
262 &self,
262 &self,
263 p1: PyObject,
263 p1: PyObject,
264 p2: PyObject,
264 p2: PyObject,
265 now: PyObject
265 now: PyObject
266 ) -> PyResult<PyBytes> {
266 ) -> PyResult<PyBytes> {
267 let now = Duration::new(now.extract(py)?, 0);
267 let now = Duration::new(now.extract(py)?, 0);
268 let parents = DirstateParents {
268 let parents = DirstateParents {
269 p1: extract_node_id(py, &p1)?,
269 p1: extract_node_id(py, &p1)?,
270 p2: extract_node_id(py, &p2)?,
270 p2: extract_node_id(py, &p2)?,
271 };
271 };
272
272
273 match self.borrow_mut(py)?.pack(parents, now) {
273 match self.borrow_mut(py)?.pack(parents, now) {
274 Ok(packed) => Ok(PyBytes::new(py, &packed)),
274 Ok(packed) => Ok(PyBytes::new(py, &packed)),
275 Err(_) => Err(PyErr::new::<exc::OSError, _>(
275 Err(_) => Err(PyErr::new::<exc::OSError, _>(
276 py,
276 py,
277 "Dirstate error".to_string(),
277 "Dirstate error".to_string(),
278 )),
278 )),
279 }
279 }
280 }
280 }
281
281
282 def filefoldmapasdict(&self) -> PyResult<PyDict> {
282 def filefoldmapasdict(&self) -> PyResult<PyDict> {
283 let dict = PyDict::new(py);
283 let dict = PyDict::new(py);
284 for (key, value) in self.borrow_mut(py)?.build_file_fold_map().iter() {
284 for (key, value) in self.borrow_mut(py)?.build_file_fold_map().iter() {
285 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
285 dict.set_item(py, key.as_ref().to_vec(), value.as_ref().to_vec())?;
286 }
286 }
287 Ok(dict)
287 Ok(dict)
288 }
288 }
289
289
290 def __len__(&self) -> PyResult<usize> {
290 def __len__(&self) -> PyResult<usize> {
291 Ok(self.inner(py).borrow().len())
291 Ok(self.inner(py).borrow().len())
292 }
292 }
293
293
294 def __contains__(&self, key: PyObject) -> PyResult<bool> {
294 def __contains__(&self, key: PyObject) -> PyResult<bool> {
295 let key = key.extract::<PyBytes>(py)?;
295 let key = key.extract::<PyBytes>(py)?;
296 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
296 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
297 }
297 }
298
298
299 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
299 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
300 let key = key.extract::<PyBytes>(py)?;
300 let key = key.extract::<PyBytes>(py)?;
301 let key = HgPath::new(key.data(py));
301 let key = HgPath::new(key.data(py));
302 match self.inner(py).borrow().get(key) {
302 match self.inner(py).borrow().get(key) {
303 Some(entry) => {
303 Some(entry) => {
304 // Explicitly go through u8 first, then cast to
304 // Explicitly go through u8 first, then cast to
305 // platform-specific `c_char`.
305 // platform-specific `c_char`.
306 let state: u8 = entry.state.into();
306 let state: u8 = entry.state.into();
307 Ok(decapsule_make_dirstate_tuple(py)?(
307 Ok(decapsule_make_dirstate_tuple(py)?(
308 state as c_char,
308 state as c_char,
309 entry.mode,
309 entry.mode,
310 entry.size,
310 entry.size,
311 entry.mtime,
311 entry.mtime,
312 ))
312 ))
313 },
313 },
314 None => Err(PyErr::new::<exc::KeyError, _>(
314 None => Err(PyErr::new::<exc::KeyError, _>(
315 py,
315 py,
316 String::from_utf8_lossy(key.as_bytes()),
316 String::from_utf8_lossy(key.as_bytes()),
317 )),
317 )),
318 }
318 }
319 }
319 }
320
320
321 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
321 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
322 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
322 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
323 DirstateMapKeysIterator::from_inner(
323 DirstateMapKeysIterator::from_inner(
324 py,
324 py,
325 leak_handle,
325 leak_handle,
326 leaked_ref.iter(),
326 leaked_ref.iter(),
327 )
327 )
328 }
328 }
329
329
330 def items(&self) -> PyResult<DirstateMapItemsIterator> {
330 def items(&self) -> PyResult<DirstateMapItemsIterator> {
331 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
331 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
332 DirstateMapItemsIterator::from_inner(
332 DirstateMapItemsIterator::from_inner(
333 py,
333 py,
334 leak_handle,
334 leak_handle,
335 leaked_ref.iter(),
335 leaked_ref.iter(),
336 )
336 )
337 }
337 }
338
338
339 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
339 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
340 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
340 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
341 DirstateMapKeysIterator::from_inner(
341 DirstateMapKeysIterator::from_inner(
342 py,
342 py,
343 leak_handle,
343 leak_handle,
344 leaked_ref.iter(),
344 leaked_ref.iter(),
345 )
345 )
346 }
346 }
347
347
348 def getdirs(&self) -> PyResult<Dirs> {
348 def getdirs(&self) -> PyResult<Dirs> {
349 // TODO don't copy, share the reference
349 // TODO don't copy, share the reference
350 self.borrow_mut(py)?.set_dirs();
350 self.borrow_mut(py)?.set_dirs();
351 Dirs::from_inner(
351 Dirs::from_inner(
352 py,
352 py,
353 DirsMultiset::from_dirstate(
353 DirsMultiset::from_dirstate(
354 &self.inner(py).borrow(),
354 &self.inner(py).borrow(),
355 Some(EntryState::Removed),
355 Some(EntryState::Removed),
356 ),
356 ),
357 )
357 )
358 }
358 }
359 def getalldirs(&self) -> PyResult<Dirs> {
359 def getalldirs(&self) -> PyResult<Dirs> {
360 // TODO don't copy, share the reference
360 // TODO don't copy, share the reference
361 self.borrow_mut(py)?.set_all_dirs();
361 self.borrow_mut(py)?.set_all_dirs();
362 Dirs::from_inner(
362 Dirs::from_inner(
363 py,
363 py,
364 DirsMultiset::from_dirstate(
364 DirsMultiset::from_dirstate(
365 &self.inner(py).borrow(),
365 &self.inner(py).borrow(),
366 None,
366 None,
367 ),
367 ),
368 )
368 )
369 }
369 }
370
370
371 // TODO all copymap* methods, see docstring above
371 // TODO all copymap* methods, see docstring above
372 def copymapcopy(&self) -> PyResult<PyDict> {
372 def copymapcopy(&self) -> PyResult<PyDict> {
373 let dict = PyDict::new(py);
373 let dict = PyDict::new(py);
374 for (key, value) in self.inner(py).borrow().copy_map.iter() {
374 for (key, value) in self.inner(py).borrow().copy_map.iter() {
375 dict.set_item(
375 dict.set_item(
376 py,
376 py,
377 PyBytes::new(py, key.as_ref()),
377 PyBytes::new(py, key.as_ref()),
378 PyBytes::new(py, value.as_ref()),
378 PyBytes::new(py, value.as_ref()),
379 )?;
379 )?;
380 }
380 }
381 Ok(dict)
381 Ok(dict)
382 }
382 }
383
383
384 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
384 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
385 let key = key.extract::<PyBytes>(py)?;
385 let key = key.extract::<PyBytes>(py)?;
386 match self.inner(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
386 match self.inner(py).borrow().copy_map.get(HgPath::new(key.data(py))) {
387 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
387 Some(copy) => Ok(PyBytes::new(py, copy.as_ref())),
388 None => Err(PyErr::new::<exc::KeyError, _>(
388 None => Err(PyErr::new::<exc::KeyError, _>(
389 py,
389 py,
390 String::from_utf8_lossy(key.data(py)),
390 String::from_utf8_lossy(key.data(py)),
391 )),
391 )),
392 }
392 }
393 }
393 }
394 def copymap(&self) -> PyResult<CopyMap> {
394 def copymap(&self) -> PyResult<CopyMap> {
395 CopyMap::from_inner(py, self.clone_ref(py))
395 CopyMap::from_inner(py, self.clone_ref(py))
396 }
396 }
397
397
398 def copymaplen(&self) -> PyResult<usize> {
398 def copymaplen(&self) -> PyResult<usize> {
399 Ok(self.inner(py).borrow().copy_map.len())
399 Ok(self.inner(py).borrow().copy_map.len())
400 }
400 }
401 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
401 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
402 let key = key.extract::<PyBytes>(py)?;
402 let key = key.extract::<PyBytes>(py)?;
403 Ok(self
403 Ok(self
404 .inner(py)
404 .inner(py)
405 .borrow()
405 .borrow()
406 .copy_map
406 .copy_map
407 .contains_key(HgPath::new(key.data(py))))
407 .contains_key(HgPath::new(key.data(py))))
408 }
408 }
409 def copymapget(
409 def copymapget(
410 &self,
410 &self,
411 key: PyObject,
411 key: PyObject,
412 default: Option<PyObject>
412 default: Option<PyObject>
413 ) -> PyResult<Option<PyObject>> {
413 ) -> PyResult<Option<PyObject>> {
414 let key = key.extract::<PyBytes>(py)?;
414 let key = key.extract::<PyBytes>(py)?;
415 match self
415 match self
416 .inner(py)
416 .inner(py)
417 .borrow()
417 .borrow()
418 .copy_map
418 .copy_map
419 .get(HgPath::new(key.data(py)))
419 .get(HgPath::new(key.data(py)))
420 {
420 {
421 Some(copy) => Ok(Some(
421 Some(copy) => Ok(Some(
422 PyBytes::new(py, copy.as_ref()).into_object(),
422 PyBytes::new(py, copy.as_ref()).into_object(),
423 )),
423 )),
424 None => Ok(default),
424 None => Ok(default),
425 }
425 }
426 }
426 }
427 def copymapsetitem(
427 def copymapsetitem(
428 &self,
428 &self,
429 key: PyObject,
429 key: PyObject,
430 value: PyObject
430 value: PyObject
431 ) -> PyResult<PyObject> {
431 ) -> PyResult<PyObject> {
432 let key = key.extract::<PyBytes>(py)?;
432 let key = key.extract::<PyBytes>(py)?;
433 let value = value.extract::<PyBytes>(py)?;
433 let value = value.extract::<PyBytes>(py)?;
434 self.borrow_mut(py)?.copy_map.insert(
434 self.borrow_mut(py)?.copy_map.insert(
435 HgPathBuf::from_bytes(key.data(py)),
435 HgPathBuf::from_bytes(key.data(py)),
436 HgPathBuf::from_bytes(value.data(py)),
436 HgPathBuf::from_bytes(value.data(py)),
437 );
437 );
438 Ok(py.None())
438 Ok(py.None())
439 }
439 }
440 def copymappop(
440 def copymappop(
441 &self,
441 &self,
442 key: PyObject,
442 key: PyObject,
443 default: Option<PyObject>
443 default: Option<PyObject>
444 ) -> PyResult<Option<PyObject>> {
444 ) -> PyResult<Option<PyObject>> {
445 let key = key.extract::<PyBytes>(py)?;
445 let key = key.extract::<PyBytes>(py)?;
446 match self
446 match self
447 .borrow_mut(py)?
447 .borrow_mut(py)?
448 .copy_map
448 .copy_map
449 .remove(HgPath::new(key.data(py)))
449 .remove(HgPath::new(key.data(py)))
450 {
450 {
451 Some(_) => Ok(None),
451 Some(_) => Ok(None),
452 None => Ok(default),
452 None => Ok(default),
453 }
453 }
454 }
454 }
455
455
456 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
456 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
457 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
457 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
458 CopyMapKeysIterator::from_inner(
458 CopyMapKeysIterator::from_inner(
459 py,
459 py,
460 leak_handle,
460 leak_handle,
461 leaked_ref.copy_map.iter(),
461 leaked_ref.copy_map.iter(),
462 )
462 )
463 }
463 }
464
464
465 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
465 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
466 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
466 let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
467 CopyMapItemsIterator::from_inner(
467 CopyMapItemsIterator::from_inner(
468 py,
468 py,
469 leak_handle,
469 leak_handle,
470 leaked_ref.copy_map.iter(),
470 leaked_ref.copy_map.iter(),
471 )
471 )
472 }
472 }
473
473
474 });
474 });
475
475
476 impl DirstateMap {
476 impl DirstateMap {
477 fn translate_key(
477 fn translate_key(
478 py: Python,
478 py: Python,
479 res: (&HgPathBuf, &DirstateEntry),
479 res: (&HgPathBuf, &DirstateEntry),
480 ) -> PyResult<Option<PyBytes>> {
480 ) -> PyResult<Option<PyBytes>> {
481 Ok(Some(PyBytes::new(py, res.0.as_ref())))
481 Ok(Some(PyBytes::new(py, res.0.as_ref())))
482 }
482 }
483 fn translate_key_value(
483 fn translate_key_value(
484 py: Python,
484 py: Python,
485 res: (&HgPathBuf, &DirstateEntry),
485 res: (&HgPathBuf, &DirstateEntry),
486 ) -> PyResult<Option<(PyBytes, PyObject)>> {
486 ) -> PyResult<Option<(PyBytes, PyObject)>> {
487 let (f, entry) = res;
487 let (f, entry) = res;
488
488
489 // Explicitly go through u8 first, then cast to
489 // Explicitly go through u8 first, then cast to
490 // platform-specific `c_char`.
490 // platform-specific `c_char`.
491 let state: u8 = entry.state.into();
491 let state: u8 = entry.state.into();
492 Ok(Some((
492 Ok(Some((
493 PyBytes::new(py, f.as_ref()),
493 PyBytes::new(py, f.as_ref()),
494 decapsule_make_dirstate_tuple(py)?(
494 decapsule_make_dirstate_tuple(py)?(
495 state as c_char,
495 state as c_char,
496 entry.mode,
496 entry.mode,
497 entry.size,
497 entry.size,
498 entry.mtime,
498 entry.mtime,
499 ),
499 ),
500 )))
500 )))
501 }
501 }
502 }
502 }
503
503
504 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
504 py_shared_ref!(DirstateMap, RustDirstateMap, inner);
505
505
506 py_shared_iterator!(
506 py_shared_iterator!(
507 DirstateMapKeysIterator,
507 DirstateMapKeysIterator,
508 DirstateMapLeakedRef,
508 PyLeakedRef,
509 StateMapIter<'static>,
509 StateMapIter<'static>,
510 DirstateMap::translate_key,
510 DirstateMap::translate_key,
511 Option<PyBytes>
511 Option<PyBytes>
512 );
512 );
513
513
514 py_shared_iterator!(
514 py_shared_iterator!(
515 DirstateMapItemsIterator,
515 DirstateMapItemsIterator,
516 DirstateMapLeakedRef,
516 PyLeakedRef,
517 StateMapIter<'static>,
517 StateMapIter<'static>,
518 DirstateMap::translate_key_value,
518 DirstateMap::translate_key_value,
519 Option<(PyBytes, PyObject)>
519 Option<(PyBytes, PyObject)>
520 );
520 );
521
521
522 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
522 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
523 let bytes = obj.extract::<PyBytes>(py)?;
523 let bytes = obj.extract::<PyBytes>(py)?;
524 match bytes.data(py).try_into() {
524 match bytes.data(py).try_into() {
525 Ok(s) => Ok(s),
525 Ok(s) => Ok(s),
526 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
526 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
527 }
527 }
528 }
528 }
@@ -1,430 +1,426 b''
1 // ref_sharing.rs
1 // ref_sharing.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to
6 // of this software and associated documentation files (the "Software"), to
7 // deal in the Software without restriction, including without limitation the
7 // deal in the Software without restriction, including without limitation the
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 // sell copies of the Software, and to permit persons to whom the Software is
9 // sell copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
10 // furnished to do so, subject to the following conditions:
11 //
11 //
12 // The above copyright notice and this permission notice shall be included in
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
13 // all copies or substantial portions of the Software.
14 //
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 // IN THE SOFTWARE.
21 // IN THE SOFTWARE.
22
22
23 //! Macros for use in the `hg-cpython` bridge library.
23 //! Macros for use in the `hg-cpython` bridge library.
24
24
25 use crate::exceptions::AlreadyBorrowed;
25 use crate::exceptions::AlreadyBorrowed;
26 use cpython::{PyResult, Python};
26 use cpython::{PyClone, PyObject, PyResult, Python};
27 use std::cell::{Cell, Ref, RefCell, RefMut};
27 use std::cell::{Cell, Ref, RefCell, RefMut};
28
28
29 /// Manages the shared state between Python and Rust
29 /// Manages the shared state between Python and Rust
30 #[derive(Debug, Default)]
30 #[derive(Debug, Default)]
31 pub struct PySharedState {
31 pub struct PySharedState {
32 leak_count: Cell<usize>,
32 leak_count: Cell<usize>,
33 mutably_borrowed: Cell<bool>,
33 mutably_borrowed: Cell<bool>,
34 }
34 }
35
35
36 // &PySharedState can be Send because any access to inner cells is
36 // &PySharedState can be Send because any access to inner cells is
37 // synchronized by the GIL.
37 // synchronized by the GIL.
38 unsafe impl Sync for PySharedState {}
38 unsafe impl Sync for PySharedState {}
39
39
40 impl PySharedState {
40 impl PySharedState {
41 pub fn borrow_mut<'a, T>(
41 pub fn borrow_mut<'a, T>(
42 &'a self,
42 &'a self,
43 py: Python<'a>,
43 py: Python<'a>,
44 pyrefmut: RefMut<'a, T>,
44 pyrefmut: RefMut<'a, T>,
45 ) -> PyResult<PyRefMut<'a, T>> {
45 ) -> PyResult<PyRefMut<'a, T>> {
46 if self.mutably_borrowed.get() {
46 if self.mutably_borrowed.get() {
47 return Err(AlreadyBorrowed::new(
47 return Err(AlreadyBorrowed::new(
48 py,
48 py,
49 "Cannot borrow mutably while there exists another \
49 "Cannot borrow mutably while there exists another \
50 mutable reference in a Python object",
50 mutable reference in a Python object",
51 ));
51 ));
52 }
52 }
53 match self.leak_count.get() {
53 match self.leak_count.get() {
54 0 => {
54 0 => {
55 self.mutably_borrowed.replace(true);
55 self.mutably_borrowed.replace(true);
56 Ok(PyRefMut::new(py, pyrefmut, self))
56 Ok(PyRefMut::new(py, pyrefmut, self))
57 }
57 }
58 // TODO
58 // TODO
59 // For now, this works differently than Python references
59 // For now, this works differently than Python references
60 // in the case of iterators.
60 // in the case of iterators.
61 // Python does not complain when the data an iterator
61 // Python does not complain when the data an iterator
62 // points to is modified if the iterator is never used
62 // points to is modified if the iterator is never used
63 // afterwards.
63 // afterwards.
64 // Here, we are stricter than this by refusing to give a
64 // Here, we are stricter than this by refusing to give a
65 // mutable reference if it is already borrowed.
65 // mutable reference if it is already borrowed.
66 // While the additional safety might be argued for, it
66 // While the additional safety might be argued for, it
67 // breaks valid programming patterns in Python and we need
67 // breaks valid programming patterns in Python and we need
68 // to fix this issue down the line.
68 // to fix this issue down the line.
69 _ => Err(AlreadyBorrowed::new(
69 _ => Err(AlreadyBorrowed::new(
70 py,
70 py,
71 "Cannot borrow mutably while there are \
71 "Cannot borrow mutably while there are \
72 immutable references in Python objects",
72 immutable references in Python objects",
73 )),
73 )),
74 }
74 }
75 }
75 }
76
76
77 /// Return a reference to the wrapped data and its state with an
77 /// Return a reference to the wrapped data and its state with an
78 /// artificial static lifetime.
78 /// artificial static lifetime.
79 /// We need to be protected by the GIL for thread-safety.
79 /// We need to be protected by the GIL for thread-safety.
80 ///
80 ///
81 /// # Safety
81 /// # Safety
82 ///
82 ///
83 /// This is highly unsafe since the lifetime of the given data can be
83 /// This is highly unsafe since the lifetime of the given data can be
84 /// extended. Do not call this function directly.
84 /// extended. Do not call this function directly.
85 pub unsafe fn leak_immutable<T>(
85 pub unsafe fn leak_immutable<T>(
86 &self,
86 &self,
87 py: Python,
87 py: Python,
88 data: &PySharedRefCell<T>,
88 data: &PySharedRefCell<T>,
89 ) -> PyResult<(&'static T, &'static PySharedState)> {
89 ) -> PyResult<(&'static T, &'static PySharedState)> {
90 if self.mutably_borrowed.get() {
90 if self.mutably_borrowed.get() {
91 return Err(AlreadyBorrowed::new(
91 return Err(AlreadyBorrowed::new(
92 py,
92 py,
93 "Cannot borrow immutably while there is a \
93 "Cannot borrow immutably while there is a \
94 mutable reference in Python objects",
94 mutable reference in Python objects",
95 ));
95 ));
96 }
96 }
97 // TODO: it's weird that self is data.py_shared_state. Maybe we
97 // TODO: it's weird that self is data.py_shared_state. Maybe we
98 // can move stuff to PySharedRefCell?
98 // can move stuff to PySharedRefCell?
99 let ptr = data.as_ptr();
99 let ptr = data.as_ptr();
100 let state_ptr: *const PySharedState = &data.py_shared_state;
100 let state_ptr: *const PySharedState = &data.py_shared_state;
101 self.leak_count.replace(self.leak_count.get() + 1);
101 self.leak_count.replace(self.leak_count.get() + 1);
102 Ok((&*ptr, &*state_ptr))
102 Ok((&*ptr, &*state_ptr))
103 }
103 }
104
104
105 /// # Safety
105 /// # Safety
106 ///
106 ///
107 /// It's unsafe to update the reference count without knowing the
107 /// It's unsafe to update the reference count without knowing the
108 /// reference is deleted. Do not call this function directly.
108 /// reference is deleted. Do not call this function directly.
109 pub unsafe fn decrease_leak_count(&self, _py: Python, mutable: bool) {
109 pub unsafe fn decrease_leak_count(&self, _py: Python, mutable: bool) {
110 if mutable {
110 if mutable {
111 assert_eq!(self.leak_count.get(), 0);
111 assert_eq!(self.leak_count.get(), 0);
112 assert!(self.mutably_borrowed.get());
112 assert!(self.mutably_borrowed.get());
113 self.mutably_borrowed.replace(false);
113 self.mutably_borrowed.replace(false);
114 } else {
114 } else {
115 let count = self.leak_count.get();
115 let count = self.leak_count.get();
116 assert!(count > 0);
116 assert!(count > 0);
117 self.leak_count.replace(count - 1);
117 self.leak_count.replace(count - 1);
118 }
118 }
119 }
119 }
120 }
120 }
121
121
122 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
122 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
123 ///
123 ///
124 /// Only immutable operation is allowed through this interface.
124 /// Only immutable operation is allowed through this interface.
125 #[derive(Debug)]
125 #[derive(Debug)]
126 pub struct PySharedRefCell<T> {
126 pub struct PySharedRefCell<T> {
127 inner: RefCell<T>,
127 inner: RefCell<T>,
128 pub py_shared_state: PySharedState, // TODO: remove pub
128 pub py_shared_state: PySharedState, // TODO: remove pub
129 }
129 }
130
130
131 impl<T> PySharedRefCell<T> {
131 impl<T> PySharedRefCell<T> {
132 pub fn new(value: T) -> PySharedRefCell<T> {
132 pub fn new(value: T) -> PySharedRefCell<T> {
133 Self {
133 Self {
134 inner: RefCell::new(value),
134 inner: RefCell::new(value),
135 py_shared_state: PySharedState::default(),
135 py_shared_state: PySharedState::default(),
136 }
136 }
137 }
137 }
138
138
139 pub fn borrow(&self) -> Ref<T> {
139 pub fn borrow(&self) -> Ref<T> {
140 // py_shared_state isn't involved since
140 // py_shared_state isn't involved since
141 // - inner.borrow() would fail if self is mutably borrowed,
141 // - inner.borrow() would fail if self is mutably borrowed,
142 // - and inner.borrow_mut() would fail while self is borrowed.
142 // - and inner.borrow_mut() would fail while self is borrowed.
143 self.inner.borrow()
143 self.inner.borrow()
144 }
144 }
145
145
146 pub fn as_ptr(&self) -> *mut T {
146 pub fn as_ptr(&self) -> *mut T {
147 self.inner.as_ptr()
147 self.inner.as_ptr()
148 }
148 }
149
149
150 // TODO: maybe this should be named as try_borrow_mut(), and use
150 // TODO: maybe this should be named as try_borrow_mut(), and use
151 // inner.try_borrow_mut(). The current implementation panics if
151 // inner.try_borrow_mut(). The current implementation panics if
152 // self.inner has been borrowed, but returns error if py_shared_state
152 // self.inner has been borrowed, but returns error if py_shared_state
153 // refuses to borrow.
153 // refuses to borrow.
154 pub fn borrow_mut<'a>(
154 pub fn borrow_mut<'a>(
155 &'a self,
155 &'a self,
156 py: Python<'a>,
156 py: Python<'a>,
157 ) -> PyResult<PyRefMut<'a, T>> {
157 ) -> PyResult<PyRefMut<'a, T>> {
158 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
158 self.py_shared_state.borrow_mut(py, self.inner.borrow_mut())
159 }
159 }
160 }
160 }
161
161
162 /// Holds a mutable reference to data shared between Python and Rust.
162 /// Holds a mutable reference to data shared between Python and Rust.
163 pub struct PyRefMut<'a, T> {
163 pub struct PyRefMut<'a, T> {
164 inner: RefMut<'a, T>,
164 inner: RefMut<'a, T>,
165 py_shared_state: &'a PySharedState,
165 py_shared_state: &'a PySharedState,
166 }
166 }
167
167
168 impl<'a, T> PyRefMut<'a, T> {
168 impl<'a, T> PyRefMut<'a, T> {
169 // Must be constructed by PySharedState after checking its leak_count.
169 // Must be constructed by PySharedState after checking its leak_count.
170 // Otherwise, drop() would incorrectly update the state.
170 // Otherwise, drop() would incorrectly update the state.
171 fn new(
171 fn new(
172 _py: Python<'a>,
172 _py: Python<'a>,
173 inner: RefMut<'a, T>,
173 inner: RefMut<'a, T>,
174 py_shared_state: &'a PySharedState,
174 py_shared_state: &'a PySharedState,
175 ) -> Self {
175 ) -> Self {
176 Self {
176 Self {
177 inner,
177 inner,
178 py_shared_state,
178 py_shared_state,
179 }
179 }
180 }
180 }
181 }
181 }
182
182
183 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
183 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
184 type Target = RefMut<'a, T>;
184 type Target = RefMut<'a, T>;
185
185
186 fn deref(&self) -> &Self::Target {
186 fn deref(&self) -> &Self::Target {
187 &self.inner
187 &self.inner
188 }
188 }
189 }
189 }
190 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
190 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
191 fn deref_mut(&mut self) -> &mut Self::Target {
191 fn deref_mut(&mut self) -> &mut Self::Target {
192 &mut self.inner
192 &mut self.inner
193 }
193 }
194 }
194 }
195
195
196 impl<'a, T> Drop for PyRefMut<'a, T> {
196 impl<'a, T> Drop for PyRefMut<'a, T> {
197 fn drop(&mut self) {
197 fn drop(&mut self) {
198 let gil = Python::acquire_gil();
198 let gil = Python::acquire_gil();
199 let py = gil.python();
199 let py = gil.python();
200 unsafe {
200 unsafe {
201 self.py_shared_state.decrease_leak_count(py, true);
201 self.py_shared_state.decrease_leak_count(py, true);
202 }
202 }
203 }
203 }
204 }
204 }
205
205
206 /// Allows a `py_class!` generated struct to share references to one of its
206 /// Allows a `py_class!` generated struct to share references to one of its
207 /// data members with Python.
207 /// data members with Python.
208 ///
208 ///
209 /// # Warning
209 /// # Warning
210 ///
210 ///
211 /// TODO allow Python container types: for now, integration with the garbage
211 /// TODO allow Python container types: for now, integration with the garbage
212 /// collector does not extend to Rust structs holding references to Python
212 /// collector does not extend to Rust structs holding references to Python
213 /// objects. Should the need surface, `__traverse__` and `__clear__` will
213 /// objects. Should the need surface, `__traverse__` and `__clear__` will
214 /// need to be written as per the `rust-cpython` docs on GC integration.
214 /// need to be written as per the `rust-cpython` docs on GC integration.
215 ///
215 ///
216 /// # Parameters
216 /// # Parameters
217 ///
217 ///
218 /// * `$name` is the same identifier used in for `py_class!` macro call.
218 /// * `$name` is the same identifier used in for `py_class!` macro call.
219 /// * `$inner_struct` is the identifier of the underlying Rust struct
219 /// * `$inner_struct` is the identifier of the underlying Rust struct
220 /// * `$data_member` is the identifier of the data member of `$inner_struct`
220 /// * `$data_member` is the identifier of the data member of `$inner_struct`
221 /// that will be shared.
221 /// that will be shared.
222 /// * `$leaked` is the identifier to give to the struct that will manage
223 /// references to `$name`, to be used for example in other macros like
224 /// `py_shared_iterator`.
225 ///
222 ///
226 /// # Example
223 /// # Example
227 ///
224 ///
228 /// ```
225 /// ```
229 /// struct MyStruct {
226 /// struct MyStruct {
230 /// inner: Vec<u32>;
227 /// inner: Vec<u32>;
231 /// }
228 /// }
232 ///
229 ///
233 /// py_class!(pub class MyType |py| {
230 /// py_class!(pub class MyType |py| {
234 /// data inner: PySharedRefCell<MyStruct>;
231 /// data inner: PySharedRefCell<MyStruct>;
235 /// });
232 /// });
236 ///
233 ///
237 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
234 /// py_shared_ref!(MyType, MyStruct, inner);
238 /// ```
235 /// ```
239 macro_rules! py_shared_ref {
236 macro_rules! py_shared_ref {
240 (
237 (
241 $name: ident,
238 $name: ident,
242 $inner_struct: ident,
239 $inner_struct: ident,
243 $data_member: ident,
240 $data_member: ident
244 $leaked: ident,
245 ) => {
241 ) => {
246 impl $name {
242 impl $name {
247 // TODO: remove this function in favor of inner(py).borrow_mut()
243 // TODO: remove this function in favor of inner(py).borrow_mut()
248 fn borrow_mut<'a>(
244 fn borrow_mut<'a>(
249 &'a self,
245 &'a self,
250 py: Python<'a>,
246 py: Python<'a>,
251 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
247 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
252 {
248 {
253 // assert $data_member type
249 // assert $data_member type
254 use crate::ref_sharing::PySharedRefCell;
250 use crate::ref_sharing::PySharedRefCell;
255 let data: &PySharedRefCell<_> = self.$data_member(py);
251 let data: &PySharedRefCell<_> = self.$data_member(py);
256 data.borrow_mut(py)
252 data.borrow_mut(py)
257 }
253 }
258
254
259 /// Returns a leaked reference and its management object.
255 /// Returns a leaked reference and its management object.
260 ///
256 ///
261 /// # Safety
257 /// # Safety
262 ///
258 ///
263 /// It's up to you to make sure that the management object lives
259 /// It's up to you to make sure that the management object lives
264 /// longer than the leaked reference. Otherwise, you'll get a
260 /// longer than the leaked reference. Otherwise, you'll get a
265 /// dangling reference.
261 /// dangling reference.
266 unsafe fn leak_immutable<'a>(
262 unsafe fn leak_immutable<'a>(
267 &'a self,
263 &'a self,
268 py: Python<'a>,
264 py: Python<'a>,
269 ) -> PyResult<($leaked, &'static $inner_struct)> {
265 ) -> PyResult<(PyLeakedRef, &'static $inner_struct)> {
266 use cpython::PythonObject;
270 // assert $data_member type
267 // assert $data_member type
271 use crate::ref_sharing::PySharedRefCell;
268 use crate::ref_sharing::PySharedRefCell;
272 let data: &PySharedRefCell<_> = self.$data_member(py);
269 let data: &PySharedRefCell<_> = self.$data_member(py);
273 let (static_ref, static_state_ref) =
270 let (static_ref, static_state_ref) =
274 data.py_shared_state.leak_immutable(py, data)?;
271 data.py_shared_state.leak_immutable(py, data)?;
275 let leak_handle = $leaked::new(py, self, static_state_ref);
272 let leak_handle =
273 PyLeakedRef::new(py, self.as_object(), static_state_ref);
276 Ok((leak_handle, static_ref))
274 Ok((leak_handle, static_ref))
277 }
275 }
278 }
276 }
277 };
278 }
279
279
280 /// Manage immutable references to `$name` leaked into Python
280 /// Manage immutable references to `PyObject` leaked into Python iterators.
281 /// iterators.
281 ///
282 ///
282 /// In truth, this does not represent leaked references themselves;
283 /// In truth, this does not represent leaked references themselves;
283 /// it is instead useful alongside them to manage them.
284 /// it is instead useful alongside them to manage them.
284 pub struct PyLeakedRef {
285 pub struct $leaked {
285 _inner: PyObject,
286 _inner: $name,
286 py_shared_state: &'static PySharedState,
287 py_shared_state: &'static crate::ref_sharing::PySharedState,
287 }
288 }
289
288
290 impl $leaked {
289 impl PyLeakedRef {
291 /// # Safety
290 /// # Safety
292 ///
291 ///
293 /// The `py_shared_state` must be owned by the `inner` Python
292 /// The `py_shared_state` must be owned by the `inner` Python object.
294 /// object.
293 // Marked as unsafe so client code wouldn't construct PyLeakedRef
295 // Marked as unsafe so client code wouldn't construct $leaked
294 // struct by mistake. Its drop() is unsafe.
296 // struct by mistake. Its drop() is unsafe.
295 pub unsafe fn new(
297 unsafe fn new(
296 py: Python,
298 py: Python,
297 inner: &PyObject,
299 inner: &$name,
298 py_shared_state: &'static PySharedState,
300 py_shared_state: &'static crate::ref_sharing::PySharedState,
299 ) -> Self {
301 ) -> Self {
300 Self {
302 Self {
301 _inner: inner.clone_ref(py),
303 _inner: inner.clone_ref(py),
302 py_shared_state,
304 py_shared_state,
305 }
306 }
307 }
303 }
304 }
305 }
308
306
309 impl Drop for $leaked {
307 impl Drop for PyLeakedRef {
310 fn drop(&mut self) {
308 fn drop(&mut self) {
311 // py_shared_state should be alive since we do have
309 // py_shared_state should be alive since we do have
312 // a Python reference to the owner object. Taking GIL makes
310 // a Python reference to the owner object. Taking GIL makes
313 // sure that the state is only accessed by this thread.
311 // sure that the state is only accessed by this thread.
314 let gil = Python::acquire_gil();
312 let gil = Python::acquire_gil();
315 let py = gil.python();
313 let py = gil.python();
316 unsafe {
314 unsafe {
317 self.py_shared_state.decrease_leak_count(py, false);
315 self.py_shared_state.decrease_leak_count(py, false);
318 }
319 }
320 }
316 }
321 };
317 }
322 }
318 }
323
319
324 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
320 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
325 ///
321 ///
326 /// TODO: this is a bit awkward to use, and a better (more complicated)
322 /// TODO: this is a bit awkward to use, and a better (more complicated)
327 /// procedural macro would simplify the interface a lot.
323 /// procedural macro would simplify the interface a lot.
328 ///
324 ///
329 /// # Parameters
325 /// # Parameters
330 ///
326 ///
331 /// * `$name` is the identifier to give to the resulting Rust struct.
327 /// * `$name` is the identifier to give to the resulting Rust struct.
332 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
328 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
333 /// * `$iterator_type` is the type of the Rust iterator.
329 /// * `$iterator_type` is the type of the Rust iterator.
334 /// * `$success_func` is a function for processing the Rust `(key, value)`
330 /// * `$success_func` is a function for processing the Rust `(key, value)`
335 /// tuple on iteration success, turning it into something Python understands.
331 /// tuple on iteration success, turning it into something Python understands.
336 /// * `$success_func` is the return type of `$success_func`
332 /// * `$success_func` is the return type of `$success_func`
337 ///
333 ///
338 /// # Example
334 /// # Example
339 ///
335 ///
340 /// ```
336 /// ```
341 /// struct MyStruct {
337 /// struct MyStruct {
342 /// inner: HashMap<Vec<u8>, Vec<u8>>;
338 /// inner: HashMap<Vec<u8>, Vec<u8>>;
343 /// }
339 /// }
344 ///
340 ///
345 /// py_class!(pub class MyType |py| {
341 /// py_class!(pub class MyType |py| {
346 /// data inner: PySharedRefCell<MyStruct>;
342 /// data inner: PySharedRefCell<MyStruct>;
347 ///
343 ///
348 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
344 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
349 /// let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
345 /// let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
350 /// MyTypeItemsIterator::from_inner(
346 /// MyTypeItemsIterator::from_inner(
351 /// py,
347 /// py,
352 /// leak_handle,
348 /// leak_handle,
353 /// leaked_ref.iter(),
349 /// leaked_ref.iter(),
354 /// )
350 /// )
355 /// }
351 /// }
356 /// });
352 /// });
357 ///
353 ///
358 /// impl MyType {
354 /// impl MyType {
359 /// fn translate_key_value(
355 /// fn translate_key_value(
360 /// py: Python,
356 /// py: Python,
361 /// res: (&Vec<u8>, &Vec<u8>),
357 /// res: (&Vec<u8>, &Vec<u8>),
362 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
358 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
363 /// let (f, entry) = res;
359 /// let (f, entry) = res;
364 /// Ok(Some((
360 /// Ok(Some((
365 /// PyBytes::new(py, f),
361 /// PyBytes::new(py, f),
366 /// PyBytes::new(py, entry),
362 /// PyBytes::new(py, entry),
367 /// )))
363 /// )))
368 /// }
364 /// }
369 /// }
365 /// }
370 ///
366 ///
371 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
367 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
372 ///
368 ///
373 /// py_shared_iterator!(
369 /// py_shared_iterator!(
374 /// MyTypeItemsIterator,
370 /// MyTypeItemsIterator,
375 /// MyTypeLeakedRef,
371 /// PyLeakedRef,
376 /// HashMap<'static, Vec<u8>, Vec<u8>>,
372 /// HashMap<'static, Vec<u8>, Vec<u8>>,
377 /// MyType::translate_key_value,
373 /// MyType::translate_key_value,
378 /// Option<(PyBytes, PyBytes)>
374 /// Option<(PyBytes, PyBytes)>
379 /// );
375 /// );
380 /// ```
376 /// ```
381 macro_rules! py_shared_iterator {
377 macro_rules! py_shared_iterator {
382 (
378 (
383 $name: ident,
379 $name: ident,
384 $leaked: ident,
380 $leaked: ty,
385 $iterator_type: ty,
381 $iterator_type: ty,
386 $success_func: expr,
382 $success_func: expr,
387 $success_type: ty
383 $success_type: ty
388 ) => {
384 ) => {
389 py_class!(pub class $name |py| {
385 py_class!(pub class $name |py| {
390 data inner: RefCell<Option<$leaked>>;
386 data inner: RefCell<Option<$leaked>>;
391 data it: RefCell<$iterator_type>;
387 data it: RefCell<$iterator_type>;
392
388
393 def __next__(&self) -> PyResult<$success_type> {
389 def __next__(&self) -> PyResult<$success_type> {
394 let mut inner_opt = self.inner(py).borrow_mut();
390 let mut inner_opt = self.inner(py).borrow_mut();
395 if inner_opt.is_some() {
391 if inner_opt.is_some() {
396 match self.it(py).borrow_mut().next() {
392 match self.it(py).borrow_mut().next() {
397 None => {
393 None => {
398 // replace Some(inner) by None, drop $leaked
394 // replace Some(inner) by None, drop $leaked
399 inner_opt.take();
395 inner_opt.take();
400 Ok(None)
396 Ok(None)
401 }
397 }
402 Some(res) => {
398 Some(res) => {
403 $success_func(py, res)
399 $success_func(py, res)
404 }
400 }
405 }
401 }
406 } else {
402 } else {
407 Ok(None)
403 Ok(None)
408 }
404 }
409 }
405 }
410
406
411 def __iter__(&self) -> PyResult<Self> {
407 def __iter__(&self) -> PyResult<Self> {
412 Ok(self.clone_ref(py))
408 Ok(self.clone_ref(py))
413 }
409 }
414 });
410 });
415
411
416 impl $name {
412 impl $name {
417 pub fn from_inner(
413 pub fn from_inner(
418 py: Python,
414 py: Python,
419 leaked: $leaked,
415 leaked: $leaked,
420 it: $iterator_type
416 it: $iterator_type
421 ) -> PyResult<Self> {
417 ) -> PyResult<Self> {
422 Self::create_instance(
418 Self::create_instance(
423 py,
419 py,
424 RefCell::new(Some(leaked)),
420 RefCell::new(Some(leaked)),
425 RefCell::new(it)
421 RefCell::new(it)
426 )
422 )
427 }
423 }
428 }
424 }
429 };
425 };
430 }
426 }
General Comments 0
You need to be logged in to leave comments. Login now