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