comparison rust/hg-cpython/src/parsers.rs @ 42747:760a7851e9ba

rust-parsers: move parser bindings to their own file and Python module This tidies up the Rust side while simplifying the Python side. Differential Revision: https://phab.mercurial-scm.org/D6627
author Raphaël Gomès <rgomes@octobus.net>
date Wed, 10 Jul 2019 10:16:28 +0200
parents rust/hg-cpython/src/dirstate.rs@b3518b0baa47
children 7cae6bc29ff9
comparison
equal deleted inserted replaced
42746:b3518b0baa47 42747:760a7851e9ba
1 // parsers.rs
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
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.
7
8 //! Bindings for the `hg::dirstate::parsers` module provided by the
9 //! `hg-core` package.
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.parsers`
12 //!
13 use cpython::{
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
15 PythonObject, ToPyObject,
16 };
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
19 DirstatePackError, DirstateParents, DirstateParseError,
20 };
21 use std::collections::HashMap;
22
23 use libc::c_char;
24
25 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate_vec};
26
27 fn parse_dirstate_wrapper(
28 py: Python,
29 dmap: PyDict,
30 copymap: PyDict,
31 st: PyBytes,
32 ) -> PyResult<PyTuple> {
33 match parse_dirstate(st.data(py)) {
34 Ok((parents, dirstate_vec, copies)) => {
35 for (filename, entry) in dirstate_vec {
36 dmap.set_item(
37 py,
38 PyBytes::new(py, &filename[..]),
39 decapsule_make_dirstate_tuple(py)?(
40 entry.state as c_char,
41 entry.mode,
42 entry.size,
43 entry.mtime,
44 ),
45 )?;
46 }
47 for CopyVecEntry { path, copy_path } in copies {
48 copymap.set_item(
49 py,
50 PyBytes::new(py, path),
51 PyBytes::new(py, copy_path),
52 )?;
53 }
54 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
55 .to_py_object(py))
56 }
57 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
58 py,
59 match e {
60 DirstateParseError::TooLittleData => {
61 "too little data for parents".to_string()
62 }
63 DirstateParseError::Overflow => {
64 "overflow in dirstate".to_string()
65 }
66 DirstateParseError::CorruptedEntry(e) => e,
67 },
68 )),
69 }
70 }
71
72 fn pack_dirstate_wrapper(
73 py: Python,
74 dmap: PyDict,
75 copymap: PyDict,
76 pl: PyTuple,
77 now: PyInt,
78 ) -> PyResult<PyBytes> {
79 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
80 let p1: &[u8] = p1.data(py);
81 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
82 let p2: &[u8] = p2.data(py);
83
84 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
85
86 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
87 .items(py)
88 .iter()
89 .map(|(key, value)| {
90 Ok((
91 key.extract::<PyBytes>(py)?.data(py).to_owned(),
92 value.extract::<PyBytes>(py)?.data(py).to_owned(),
93 ))
94 })
95 .collect();
96
97 match pack_dirstate(
98 &dirstate_vec,
99 &copies?,
100 DirstateParents { p1, p2 },
101 now.as_object().extract::<i32>(py)?,
102 ) {
103 Ok((packed, new_dirstate_vec)) => {
104 for (
105 filename,
106 DirstateEntry {
107 state,
108 mode,
109 size,
110 mtime,
111 },
112 ) in new_dirstate_vec
113 {
114 dmap.set_item(
115 py,
116 PyBytes::new(py, &filename[..]),
117 decapsule_make_dirstate_tuple(py)?(
118 state as c_char,
119 mode,
120 size,
121 mtime,
122 ),
123 )?;
124 }
125 Ok(PyBytes::new(py, &packed))
126 }
127 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
128 py,
129 match error {
130 DirstatePackError::CorruptedParent => {
131 "expected a 20-byte hash".to_string()
132 }
133 DirstatePackError::CorruptedEntry(e) => e,
134 DirstatePackError::BadSize(expected, actual) => {
135 format!("bad dirstate size: {} != {}", actual, expected)
136 }
137 },
138 )),
139 }
140 }
141
142 /// Create the module, with `__package__` given from parent
143 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
144 let dotted_name = &format!("{}.parsers", package);
145 let m = PyModule::new(py, dotted_name)?;
146
147 m.add(py, "__package__", package)?;
148 m.add(py, "__doc__", "Parsers - Rust implementation")?;
149
150 m.add(
151 py,
152 "parse_dirstate",
153 py_fn!(
154 py,
155 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
156 ),
157 )?;
158 m.add(
159 py,
160 "pack_dirstate",
161 py_fn!(
162 py,
163 pack_dirstate_wrapper(
164 dmap: PyDict,
165 copymap: PyDict,
166 pl: PyTuple,
167 now: PyInt
168 )
169 ),
170 )?;
171
172 let sys = PyModule::import(py, "sys")?;
173 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
174 sys_modules.set_item(py, dotted_name, &m)?;
175
176 Ok(m)
177 }