##// END OF EJS Templates
rust: simplify overly complicated expression...
Raphaël Gomès -
r42761:11c025c8 default
parent child Browse files
Show More
@@ -1,335 +1,334 b''
1 1 // dirstate.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate` module provided by the
9 9 //! `hg-core` package.
10 10 //!
11 11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12 12
13 13 use cpython::{
14 14 exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject,
15 15 PyResult, PySequence, PyTuple, Python, PythonObject, ToPyObject,
16 16 };
17 17 use hg::{
18 18 pack_dirstate, parse_dirstate, CopyVecEntry, DirsIterable, DirsMultiset,
19 19 DirstateEntry, DirstateMapError, DirstatePackError, DirstateParents,
20 20 DirstateParseError, DirstateVec,
21 21 };
22 22 use std::collections::HashMap;
23 23 use std::ffi::CStr;
24 24
25 25 #[cfg(feature = "python27")]
26 26 extern crate python27_sys as python_sys;
27 27 #[cfg(feature = "python3")]
28 28 extern crate python3_sys as python_sys;
29 29
30 30 use self::python_sys::PyCapsule_Import;
31 31 use libc::{c_char, c_int};
32 32 use std::cell::RefCell;
33 33 use std::mem::transmute;
34 34
35 35 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
36 36 /// for this type, and raises a Python `Exception` if the check does not pass.
37 37 /// Because this type differs only in name from the regular Python tuple, it
38 38 /// would be a good idea in the near future to remove it entirely to allow
39 39 /// for a pure Python tuple of the same effective structure to be used,
40 40 /// rendering this type and the capsule below useless.
41 41 type MakeDirstateTupleFn = extern "C" fn(
42 42 state: c_char,
43 43 mode: c_int,
44 44 size: c_int,
45 45 mtime: c_int,
46 46 ) -> PyObject;
47 47
48 48 /// This is largely a copy/paste from cindex.rs, pending the merge of a
49 49 /// `py_capsule_fn!` macro in the rust-cpython project:
50 50 /// https://github.com/dgrunwald/rust-cpython/pull/169
51 51 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
52 52 unsafe {
53 53 let caps_name = CStr::from_bytes_with_nul_unchecked(
54 54 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
55 55 );
56 56 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
57 57 if from_caps.is_null() {
58 58 return Err(PyErr::fetch(py));
59 59 }
60 60 Ok(transmute(from_caps))
61 61 }
62 62 }
63 63
64 64 fn parse_dirstate_wrapper(
65 65 py: Python,
66 66 dmap: PyDict,
67 67 copymap: PyDict,
68 68 st: PyBytes,
69 69 ) -> PyResult<PyTuple> {
70 70 match parse_dirstate(st.data(py)) {
71 71 Ok((parents, dirstate_vec, copies)) => {
72 72 for (filename, entry) in dirstate_vec {
73 73 dmap.set_item(
74 74 py,
75 75 PyBytes::new(py, &filename[..]),
76 76 decapsule_make_dirstate_tuple(py)?(
77 77 entry.state as c_char,
78 78 entry.mode,
79 79 entry.size,
80 80 entry.mtime,
81 81 ),
82 82 )?;
83 83 }
84 84 for CopyVecEntry { path, copy_path } in copies {
85 85 copymap.set_item(
86 86 py,
87 87 PyBytes::new(py, path),
88 88 PyBytes::new(py, copy_path),
89 89 )?;
90 90 }
91 91 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
92 92 .to_py_object(py))
93 93 }
94 94 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
95 95 py,
96 96 match e {
97 97 DirstateParseError::TooLittleData => {
98 98 "too little data for parents".to_string()
99 99 }
100 100 DirstateParseError::Overflow => {
101 101 "overflow in dirstate".to_string()
102 102 }
103 103 DirstateParseError::CorruptedEntry(e) => e,
104 104 },
105 105 )),
106 106 }
107 107 }
108 108
109 109 fn extract_dirstate_vec(
110 110 py: Python,
111 111 dmap: &PyDict,
112 112 ) -> Result<DirstateVec, PyErr> {
113 113 dmap.items(py)
114 114 .iter()
115 115 .map(|(filename, stats)| {
116 116 let stats = stats.extract::<PySequence>(py)?;
117 117 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
118 118 let state = state.data(py)[0] as i8;
119 119 let mode = stats.get_item(py, 1)?.extract(py)?;
120 120 let size = stats.get_item(py, 2)?.extract(py)?;
121 121 let mtime = stats.get_item(py, 3)?.extract(py)?;
122 122 let filename = filename.extract::<PyBytes>(py)?;
123 123 let filename = filename.data(py);
124 124 Ok((
125 125 filename.to_owned(),
126 126 DirstateEntry {
127 127 state,
128 128 mode,
129 129 size,
130 130 mtime,
131 131 },
132 132 ))
133 133 })
134 134 .collect()
135 135 }
136 136
137 137 fn pack_dirstate_wrapper(
138 138 py: Python,
139 139 dmap: PyDict,
140 140 copymap: PyDict,
141 141 pl: PyTuple,
142 142 now: PyInt,
143 143 ) -> PyResult<PyBytes> {
144 144 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
145 145 let p1: &[u8] = p1.data(py);
146 146 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
147 147 let p2: &[u8] = p2.data(py);
148 148
149 149 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
150 150
151 151 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
152 152 .items(py)
153 153 .iter()
154 154 .map(|(key, value)| {
155 155 Ok((
156 156 key.extract::<PyBytes>(py)?.data(py).to_owned(),
157 157 value.extract::<PyBytes>(py)?.data(py).to_owned(),
158 158 ))
159 159 })
160 160 .collect();
161 161
162 162 match pack_dirstate(
163 163 &dirstate_vec,
164 164 &copies?,
165 165 DirstateParents { p1, p2 },
166 166 now.as_object().extract::<i32>(py)?,
167 167 ) {
168 168 Ok((packed, new_dirstate_vec)) => {
169 169 for (
170 170 filename,
171 171 DirstateEntry {
172 172 state,
173 173 mode,
174 174 size,
175 175 mtime,
176 176 },
177 177 ) in new_dirstate_vec
178 178 {
179 179 dmap.set_item(
180 180 py,
181 181 PyBytes::new(py, &filename[..]),
182 182 decapsule_make_dirstate_tuple(py)?(
183 183 state as c_char,
184 184 mode,
185 185 size,
186 186 mtime,
187 187 ),
188 188 )?;
189 189 }
190 190 Ok(PyBytes::new(py, &packed))
191 191 }
192 192 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
193 193 py,
194 194 match error {
195 195 DirstatePackError::CorruptedParent => {
196 196 "expected a 20-byte hash".to_string()
197 197 }
198 198 DirstatePackError::CorruptedEntry(e) => e,
199 199 DirstatePackError::BadSize(expected, actual) => {
200 200 format!("bad dirstate size: {} != {}", actual, expected)
201 201 }
202 202 },
203 203 )),
204 204 }
205 205 }
206 206
207 207 py_class!(pub class Dirs |py| {
208 208 data dirs_map: RefCell<DirsMultiset>;
209 209
210 210 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
211 211 // a `list`)
212 212 def __new__(
213 213 _cls,
214 214 map: PyObject,
215 215 skip: Option<PyObject> = None
216 216 ) -> PyResult<Self> {
217 217 let mut skip_state: Option<i8> = None;
218 218 if let Some(skip) = skip {
219 219 skip_state = Some(skip.extract::<PyBytes>(py)?.data(py)[0] as i8);
220 220 }
221 221 let dirs_map;
222 222
223 223 if let Ok(map) = map.cast_as::<PyDict>(py) {
224 224 let dirstate_vec = extract_dirstate_vec(py, &map)?;
225 225 dirs_map = DirsMultiset::new(
226 226 DirsIterable::Dirstate(dirstate_vec),
227 227 skip_state,
228 228 )
229 229 } else {
230 230 let map: Result<Vec<Vec<u8>>, PyErr> = map
231 231 .iter(py)?
232 232 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
233 233 .collect();
234 234 dirs_map = DirsMultiset::new(
235 235 DirsIterable::Manifest(map?),
236 236 skip_state,
237 237 )
238 238 }
239 239
240 240 Self::create_instance(py, RefCell::new(dirs_map))
241 241 }
242 242
243 243 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
244 244 self.dirs_map(py).borrow_mut().add_path(
245 245 path.extract::<PyBytes>(py)?.data(py),
246 246 );
247 247 Ok(py.None())
248 248 }
249 249
250 250 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
251 251 self.dirs_map(py).borrow_mut().delete_path(
252 252 path.extract::<PyBytes>(py)?.data(py),
253 253 )
254 254 .and(Ok(py.None()))
255 255 .or_else(|e| {
256 256 match e {
257 257 DirstateMapError::PathNotFound(_p) => {
258 258 Err(PyErr::new::<exc::ValueError, _>(
259 259 py,
260 260 "expected a value, found none".to_string(),
261 261 ))
262 262 }
263 263 DirstateMapError::EmptyPath => {
264 264 Ok(py.None())
265 265 }
266 266 }
267 267 })
268 268 }
269 269
270 270 // This is really inefficient on top of being ugly, but it's an easy way
271 271 // of having it work to continue working on the rest of the module
272 272 // hopefully bypassing Python entirely pretty soon.
273 273 def __iter__(&self) -> PyResult<PyObject> {
274 274 let dict = PyDict::new(py);
275 275
276 276 for (key, value) in self.dirs_map(py).borrow().iter() {
277 277 dict.set_item(
278 278 py,
279 279 PyBytes::new(py, &key[..]),
280 280 value.to_py_object(py),
281 281 )?;
282 282 }
283 283
284 284 let locals = PyDict::new(py);
285 285 locals.set_item(py, "obj", dict)?;
286 286
287 287 py.eval("iter(obj)", None, Some(&locals))
288 288 }
289 289
290 290 def __contains__(&self, item: PyObject) -> PyResult<bool> {
291 291 Ok(self
292 292 .dirs_map(py)
293 293 .borrow()
294 .get(&item.extract::<PyBytes>(py)?.data(py).to_owned())
295 .is_some())
294 .contains_key(item.extract::<PyBytes>(py)?.data(py).as_ref()))
296 295 }
297 296 });
298 297
299 298 /// Create the module, with `__package__` given from parent
300 299 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
301 300 let dotted_name = &format!("{}.dirstate", package);
302 301 let m = PyModule::new(py, dotted_name)?;
303 302
304 303 m.add(py, "__package__", package)?;
305 304 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
306 305 m.add(
307 306 py,
308 307 "parse_dirstate",
309 308 py_fn!(
310 309 py,
311 310 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
312 311 ),
313 312 )?;
314 313 m.add(
315 314 py,
316 315 "pack_dirstate",
317 316 py_fn!(
318 317 py,
319 318 pack_dirstate_wrapper(
320 319 dmap: PyDict,
321 320 copymap: PyDict,
322 321 pl: PyTuple,
323 322 now: PyInt
324 323 )
325 324 ),
326 325 )?;
327 326
328 327 m.add_class::<Dirs>(py)?;
329 328
330 329 let sys = PyModule::import(py, "sys")?;
331 330 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
332 331 sys_modules.set_item(py, dotted_name, &m)?;
333 332
334 333 Ok(m)
335 334 }
General Comments 0
You need to be logged in to leave comments. Login now