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 use cpython::{ |
|
13 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python, |
|
14 PythonObject, ToPyObject, |
|
15 }; |
|
16 use hg::{ |
|
17 dirstate::parsers::Timestamp, pack_dirstate, parse_dirstate, |
|
18 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParents, FastHashMap, |
|
19 PARENT_SIZE, |
|
20 }; |
|
21 use std::convert::TryInto; |
|
22 |
|
23 use crate::dirstate::{extract_dirstate, make_dirstate_item}; |
|
24 |
|
25 fn parse_dirstate_wrapper( |
|
26 py: Python, |
|
27 dmap: PyDict, |
|
28 copymap: PyDict, |
|
29 st: PyBytes, |
|
30 ) -> PyResult<PyTuple> { |
|
31 match parse_dirstate(st.data(py)) { |
|
32 Ok((parents, entries, copies)) => { |
|
33 let dirstate_map: FastHashMap<HgPathBuf, DirstateEntry> = entries |
|
34 .into_iter() |
|
35 .map(|(path, entry)| (path.to_owned(), entry)) |
|
36 .collect(); |
|
37 let copy_map: FastHashMap<HgPathBuf, HgPathBuf> = copies |
|
38 .into_iter() |
|
39 .map(|(path, copy)| (path.to_owned(), copy.to_owned())) |
|
40 .collect(); |
|
41 |
|
42 for (filename, entry) in &dirstate_map { |
|
43 dmap.set_item( |
|
44 py, |
|
45 PyBytes::new(py, filename.as_bytes()), |
|
46 make_dirstate_item(py, entry)?, |
|
47 )?; |
|
48 } |
|
49 for (path, copy_path) in copy_map { |
|
50 copymap.set_item( |
|
51 py, |
|
52 PyBytes::new(py, path.as_bytes()), |
|
53 PyBytes::new(py, copy_path.as_bytes()), |
|
54 )?; |
|
55 } |
|
56 Ok(dirstate_parents_to_pytuple(py, parents)) |
|
57 } |
|
58 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())), |
|
59 } |
|
60 } |
|
61 |
|
62 fn pack_dirstate_wrapper( |
|
63 py: Python, |
|
64 dmap: PyDict, |
|
65 copymap: PyDict, |
|
66 pl: PyTuple, |
|
67 now: PyInt, |
|
68 ) -> PyResult<PyBytes> { |
|
69 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?; |
|
70 let p1: &[u8] = p1.data(py); |
|
71 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?; |
|
72 let p2: &[u8] = p2.data(py); |
|
73 |
|
74 let mut dirstate_map = extract_dirstate(py, &dmap)?; |
|
75 |
|
76 let copies: Result<FastHashMap<HgPathBuf, HgPathBuf>, PyErr> = copymap |
|
77 .items(py) |
|
78 .iter() |
|
79 .map(|(key, value)| { |
|
80 Ok(( |
|
81 HgPathBuf::from_bytes(key.extract::<PyBytes>(py)?.data(py)), |
|
82 HgPathBuf::from_bytes(value.extract::<PyBytes>(py)?.data(py)), |
|
83 )) |
|
84 }) |
|
85 .collect(); |
|
86 |
|
87 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE { |
|
88 return Err(PyErr::new::<exc::ValueError, _>( |
|
89 py, |
|
90 "expected a 20-byte hash".to_string(), |
|
91 )); |
|
92 } |
|
93 |
|
94 match pack_dirstate( |
|
95 &mut dirstate_map, |
|
96 &copies?, |
|
97 DirstateParents { |
|
98 p1: p1.try_into().unwrap(), |
|
99 p2: p2.try_into().unwrap(), |
|
100 }, |
|
101 Timestamp(now.as_object().extract::<i64>(py)?), |
|
102 ) { |
|
103 Ok(packed) => { |
|
104 for (filename, entry) in dirstate_map.iter() { |
|
105 dmap.set_item( |
|
106 py, |
|
107 PyBytes::new(py, filename.as_bytes()), |
|
108 make_dirstate_item(py, &entry)?, |
|
109 )?; |
|
110 } |
|
111 Ok(PyBytes::new(py, &packed)) |
|
112 } |
|
113 Err(error) => { |
|
114 Err(PyErr::new::<exc::ValueError, _>(py, error.to_string())) |
|
115 } |
|
116 } |
|
117 } |
|
118 |
|
119 /// Create the module, with `__package__` given from parent |
|
120 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> { |
|
121 let dotted_name = &format!("{}.parsers", package); |
|
122 let m = PyModule::new(py, dotted_name)?; |
|
123 |
|
124 m.add(py, "__package__", package)?; |
|
125 m.add(py, "__doc__", "Parsers - Rust implementation")?; |
|
126 |
|
127 m.add( |
|
128 py, |
|
129 "parse_dirstate", |
|
130 py_fn!( |
|
131 py, |
|
132 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes) |
|
133 ), |
|
134 )?; |
|
135 m.add( |
|
136 py, |
|
137 "pack_dirstate", |
|
138 py_fn!( |
|
139 py, |
|
140 pack_dirstate_wrapper( |
|
141 dmap: PyDict, |
|
142 copymap: PyDict, |
|
143 pl: PyTuple, |
|
144 now: PyInt |
|
145 ) |
|
146 ), |
|
147 )?; |
|
148 |
|
149 let sys = PyModule::import(py, "sys")?; |
|
150 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
|
151 sys_modules.set_item(py, dotted_name, &m)?; |
|
152 |
|
153 Ok(m) |
|
154 } |
|
155 |
|
156 pub(crate) fn dirstate_parents_to_pytuple( |
|
157 py: Python, |
|
158 parents: &DirstateParents, |
|
159 ) -> PyTuple { |
|
160 let p1 = PyBytes::new(py, parents.p1.as_bytes()); |
|
161 let p2 = PyBytes::new(py, parents.p2.as_bytes()); |
|
162 (p1, p2).to_py_object(py) |
|
163 } |
|