##// END OF EJS Templates
rust: run rfmt on all hg-core/hg-cpython code...
Raphaël Gomès -
r42760:d26e4a43 default
parent child Browse files
Show More
@@ -1,355 +1,357 b''
1 1 // dirs_multiset.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 //! A multiset of directory names.
9 9 //!
10 10 //! Used to counts the references to directories in a manifest or dirstate.
11 11 use std::collections::hash_map::Entry;
12 12 use std::collections::HashMap;
13 13 use std::ops::Deref;
14 14 use {DirsIterable, DirstateEntry, DirstateMapError};
15 15
16 16 #[derive(PartialEq, Debug)]
17 17 pub struct DirsMultiset {
18 18 inner: HashMap<Vec<u8>, u32>,
19 19 }
20 20
21 21 impl Deref for DirsMultiset {
22 22 type Target = HashMap<Vec<u8>, u32>;
23 23
24 24 fn deref(&self) -> &Self::Target {
25 25 &self.inner
26 26 }
27 27 }
28 28
29 29 impl DirsMultiset {
30 30 /// Initializes the multiset from a dirstate or a manifest.
31 31 ///
32 32 /// If `skip_state` is provided, skips dirstate entries with equal state.
33 33 pub fn new(iterable: DirsIterable, skip_state: Option<i8>) -> Self {
34 34 let mut multiset = DirsMultiset {
35 35 inner: HashMap::new(),
36 36 };
37 37
38 38 match iterable {
39 39 DirsIterable::Dirstate(vec) => {
40 40 for (ref filename, DirstateEntry { state, .. }) in vec {
41 41 // This `if` is optimized out of the loop
42 42 if let Some(skip) = skip_state {
43 43 if skip != state {
44 44 multiset.add_path(filename);
45 45 }
46 46 } else {
47 47 multiset.add_path(filename);
48 48 }
49 49 }
50 50 }
51 51 DirsIterable::Manifest(vec) => {
52 52 for ref filename in vec {
53 53 multiset.add_path(filename);
54 54 }
55 55 }
56 56 }
57 57
58 58 multiset
59 59 }
60 60
61 61 /// Returns the slice up to the next directory name from right to left,
62 62 /// without trailing slash
63 63 fn find_dir(path: &[u8]) -> &[u8] {
64 64 let mut path = path;
65 65 loop {
66 66 if let Some(new_pos) = path.len().checked_sub(1) {
67 67 if path[new_pos] == b'/' {
68 68 break &path[..new_pos];
69 69 }
70 70 path = &path[..new_pos];
71 71 } else {
72 72 break &[];
73 73 }
74 74 }
75 75 }
76 76
77 77 /// Increases the count of deepest directory contained in the path.
78 78 ///
79 79 /// If the directory is not yet in the map, adds its parents.
80 80 pub fn add_path(&mut self, path: &[u8]) {
81 81 let mut pos = path.len();
82 82
83 83 loop {
84 84 let subpath = Self::find_dir(&path[..pos]);
85 85 if let Some(val) = self.inner.get_mut(subpath) {
86 86 *val += 1;
87 87 break;
88 88 }
89 89 self.inner.insert(subpath.to_owned(), 1);
90 90
91 91 pos = subpath.len();
92 92 if pos == 0 {
93 93 break;
94 94 }
95 95 }
96 96 }
97 97
98 98 /// Decreases the count of deepest directory contained in the path.
99 99 ///
100 100 /// If it is the only reference, decreases all parents until one is
101 101 /// removed.
102 102 /// If the directory is not in the map, something horrible has happened.
103 103 pub fn delete_path(
104 104 &mut self,
105 105 path: &[u8],
106 106 ) -> Result<(), DirstateMapError> {
107 107 let mut pos = path.len();
108 108
109 109 loop {
110 110 let subpath = Self::find_dir(&path[..pos]);
111 111 match self.inner.entry(subpath.to_owned()) {
112 112 Entry::Occupied(mut entry) => {
113 113 let val = entry.get().clone();
114 114 if val > 1 {
115 115 entry.insert(val - 1);
116 116 break;
117 117 }
118 118 entry.remove();
119 119 }
120 120 Entry::Vacant(_) => {
121 return Err(DirstateMapError::PathNotFound(path.to_owned()))
121 return Err(DirstateMapError::PathNotFound(
122 path.to_owned(),
123 ))
122 124 }
123 125 };
124 126
125 127 pos = subpath.len();
126 128 if pos == 0 {
127 129 break;
128 130 }
129 131 }
130 132
131 133 Ok(())
132 134 }
133 135 }
134 136
135 137 #[cfg(test)]
136 138 mod tests {
137 139 use super::*;
138 140
139 141 #[test]
140 142 fn test_delete_path_path_not_found() {
141 143 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
142 144 let path = b"doesnotexist/";
143 145 assert_eq!(
144 146 Err(DirstateMapError::PathNotFound(path.to_vec())),
145 147 map.delete_path(path)
146 148 );
147 149 }
148 150
149 151 #[test]
150 152 fn test_delete_path_empty_path() {
151 153 let mut map =
152 154 DirsMultiset::new(DirsIterable::Manifest(vec![vec![]]), None);
153 155 let path = b"";
154 156 assert_eq!(Ok(()), map.delete_path(path));
155 157 assert_eq!(
156 158 Err(DirstateMapError::PathNotFound(path.to_vec())),
157 159 map.delete_path(path)
158 160 );
159 161 }
160 162
161 163 #[test]
162 164 fn test_delete_path_successful() {
163 165 let mut map = DirsMultiset {
164 166 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
165 167 .iter()
166 168 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
167 169 .collect(),
168 170 };
169 171
170 172 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
171 173 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
172 174 assert_eq!(
173 175 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
174 176 map.delete_path(b"a/b/")
175 177 );
176 178
177 179 assert_eq!(2, *map.get(&b"a".to_vec()).unwrap());
178 180 assert_eq!(1, *map.get(&b"a/c".to_vec()).unwrap());
179 181 eprintln!("{:?}", map);
180 182 assert_eq!(Ok(()), map.delete_path(b"a/"));
181 183 eprintln!("{:?}", map);
182 184
183 185 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
184 186 assert_eq!(
185 187 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
186 188 map.delete_path(b"a/c/")
187 189 );
188 190 }
189 191
190 192 #[test]
191 193 fn test_add_path_empty_path() {
192 194 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
193 195 let path = b"";
194 196 map.add_path(path);
195 197
196 198 assert_eq!(1, map.len());
197 199 }
198 200
199 201 #[test]
200 202 fn test_add_path_successful() {
201 203 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
202 204
203 205 map.add_path(b"a/");
204 206 assert_eq!(1, *map.get(&b"a".to_vec()).unwrap());
205 207 assert_eq!(1, *map.get(&Vec::new()).unwrap());
206 208 assert_eq!(2, map.len());
207 209
208 210 // Non directory should be ignored
209 211 map.add_path(b"a");
210 212 assert_eq!(1, *map.get(&b"a".to_vec()).unwrap());
211 213 assert_eq!(2, map.len());
212 214
213 215 // Non directory will still add its base
214 216 map.add_path(b"a/b");
215 217 assert_eq!(2, *map.get(&b"a".to_vec()).unwrap());
216 218 assert_eq!(2, map.len());
217 219
218 220 // Duplicate path works
219 221 map.add_path(b"a/");
220 222 assert_eq!(3, *map.get(&b"a".to_vec()).unwrap());
221 223
222 224 // Nested dir adds to its base
223 225 map.add_path(b"a/b/");
224 226 assert_eq!(4, *map.get(&b"a".to_vec()).unwrap());
225 227 assert_eq!(1, *map.get(&b"a/b".to_vec()).unwrap());
226 228
227 229 // but not its base's base, because it already existed
228 230 map.add_path(b"a/b/c/");
229 231 assert_eq!(4, *map.get(&b"a".to_vec()).unwrap());
230 232 assert_eq!(2, *map.get(&b"a/b".to_vec()).unwrap());
231 233
232 234 map.add_path(b"a/c/");
233 235 assert_eq!(1, *map.get(&b"a/c".to_vec()).unwrap());
234 236
235 237 let expected = DirsMultiset {
236 238 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
237 239 .iter()
238 240 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
239 241 .collect(),
240 242 };
241 243 assert_eq!(map, expected);
242 244 }
243 245
244 246 #[test]
245 247 fn test_dirsmultiset_new_empty() {
246 248 use DirsIterable::{Dirstate, Manifest};
247 249
248 250 let new = DirsMultiset::new(Manifest(vec![]), None);
249 251 let expected = DirsMultiset {
250 252 inner: HashMap::new(),
251 253 };
252 254 assert_eq!(expected, new);
253 255
254 256 let new = DirsMultiset::new(Dirstate(vec![]), None);
255 257 let expected = DirsMultiset {
256 258 inner: HashMap::new(),
257 259 };
258 260 assert_eq!(expected, new);
259 261 }
260 262
261 263 #[test]
262 264 fn test_dirsmultiset_new_no_skip() {
263 265 use DirsIterable::{Dirstate, Manifest};
264 266
265 267 let input_vec = ["a/", "b/", "a/c", "a/d/"]
266 268 .iter()
267 269 .map(|e| e.as_bytes().to_vec())
268 270 .collect();
269 271 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
270 272 .iter()
271 273 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
272 274 .collect();
273 275
274 276 let new = DirsMultiset::new(Manifest(input_vec), None);
275 277 let expected = DirsMultiset {
276 278 inner: expected_inner,
277 279 };
278 280 assert_eq!(expected, new);
279 281
280 282 let input_map = ["a/", "b/", "a/c", "a/d/"]
281 283 .iter()
282 284 .map(|f| {
283 285 (
284 286 f.as_bytes().to_vec(),
285 287 DirstateEntry {
286 288 state: 0,
287 289 mode: 0,
288 290 mtime: 0,
289 291 size: 0,
290 292 },
291 293 )
292 294 })
293 295 .collect();
294 296 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
295 297 .iter()
296 298 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
297 299 .collect();
298 300
299 301 let new = DirsMultiset::new(Dirstate(input_map), None);
300 302 let expected = DirsMultiset {
301 303 inner: expected_inner,
302 304 };
303 305 assert_eq!(expected, new);
304 306 }
305 307
306 308 #[test]
307 309 fn test_dirsmultiset_new_skip() {
308 310 use DirsIterable::{Dirstate, Manifest};
309 311
310 312 let input_vec = ["a/", "b/", "a/c", "a/d/"]
311 313 .iter()
312 314 .map(|e| e.as_bytes().to_vec())
313 315 .collect();
314 316 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
315 317 .iter()
316 318 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
317 319 .collect();
318 320
319 321 let new = DirsMultiset::new(Manifest(input_vec), Some('n' as i8));
320 322 let expected = DirsMultiset {
321 323 inner: expected_inner,
322 324 };
323 325 // Skip does not affect a manifest
324 326 assert_eq!(expected, new);
325 327
326 328 let input_map =
327 329 [("a/", 'n'), ("a/b/", 'n'), ("a/c", 'r'), ("a/d/", 'm')]
328 330 .iter()
329 331 .map(|(f, state)| {
330 332 (
331 333 f.as_bytes().to_vec(),
332 334 DirstateEntry {
333 335 state: *state as i8,
334 336 mode: 0,
335 337 mtime: 0,
336 338 size: 0,
337 339 },
338 340 )
339 341 })
340 342 .collect();
341 343
342 344 // "a" incremented with "a/c" and "a/d/"
343 345 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
344 346 .iter()
345 347 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
346 348 .collect();
347 349
348 350 let new = DirsMultiset::new(Dirstate(input_map), Some('n' as i8));
349 351 let expected = DirsMultiset {
350 352 inner: expected_inner,
351 353 };
352 354 assert_eq!(expected, new);
353 355 }
354 356
355 357 }
@@ -1,53 +1,53 b''
1 1 // dagops.rs
2 2 //
3 3 // Copyright 2019 Georges Racinet <georges.racinet@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::dagops` module provided by the
9 9 //! `hg-core` package.
10 10 //!
11 11 //! From Python, this will be seen as `mercurial.rustext.dagop`
12 use crate::conversion::{py_set, rev_pyiter_collect};
12 13 use cindex::Index;
13 14 use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
14 use crate::conversion::{py_set, rev_pyiter_collect};
15 15 use exceptions::GraphError;
16 16 use hg::dagops;
17 17 use hg::Revision;
18 18 use std::collections::HashSet;
19 19
20 20 /// Using the the `index`, return heads out of any Python iterable of Revisions
21 21 ///
22 22 /// This is the Rust counterpart for `mercurial.dagop.headrevs`
23 23 pub fn headrevs(
24 24 py: Python,
25 25 index: PyObject,
26 26 revs: PyObject,
27 27 ) -> PyResult<PyObject> {
28 28 let mut as_set: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
29 29 dagops::retain_heads(&Index::new(py, index)?, &mut as_set)
30 30 .map_err(|e| GraphError::pynew(py, e))?;
31 31 py_set(py, &as_set)
32 32 }
33 33
34 34 /// Create the module, with `__package__` given from parent
35 35 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
36 36 let dotted_name = &format!("{}.dagop", package);
37 37 let m = PyModule::new(py, dotted_name)?;
38 38 m.add(py, "__package__", package)?;
39 39 m.add(py, "__doc__", "DAG operations - Rust implementation")?;
40 40 m.add(
41 41 py,
42 42 "headrevs",
43 43 py_fn!(py, headrevs(index: PyObject, revs: PyObject)),
44 44 )?;
45 45
46 46 let sys = PyModule::import(py, "sys")?;
47 47 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
48 48 sys_modules.set_item(py, dotted_name, &m)?;
49 49 // Example C code (see pyexpat.c and import.c) will "give away the
50 50 // reference", but we won't because it will be consumed once the
51 51 // Rust PyObject is dropped.
52 52 Ok(m)
53 53 }
@@ -1,72 +1,67 b''
1 1 // ancestors.rs
2 2 //
3 3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
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 Rust errors
9 9 //!
10 10 //! [`GraphError`] exposes `hg::GraphError` as a subclass of `ValueError`
11 11 //! but some variants of `hg::GraphError` can be converted directly to other
12 12 //! existing Python exceptions if appropriate.
13 13 //!
14 14 //! [`GraphError`]: struct.GraphError.html
15 use cpython::exc::{ValueError, RuntimeError};
16 use cpython::{PyErr, Python, exc};
15 use cpython::exc::{RuntimeError, ValueError};
16 use cpython::{exc, PyErr, Python};
17 17 use hg;
18 18
19 19 py_exception!(rustext, GraphError, ValueError);
20 20
21 21 impl GraphError {
22 22 pub fn pynew(py: Python, inner: hg::GraphError) -> PyErr {
23 23 match inner {
24 24 hg::GraphError::ParentOutOfRange(r) => {
25 25 GraphError::new(py, ("ParentOutOfRange", r))
26 26 }
27 27 hg::GraphError::WorkingDirectoryUnsupported => {
28 28 match py
29 29 .import("mercurial.error")
30 30 .and_then(|m| m.get(py, "WdirUnsupported"))
31 31 {
32 32 Err(e) => e,
33 33 Ok(cls) => PyErr::from_instance(py, cls),
34 34 }
35 35 }
36 36 }
37 37 }
38 38 }
39 39
40 40 py_exception!(rustext, PatternError, RuntimeError);
41 41 py_exception!(rustext, PatternFileError, RuntimeError);
42 42
43 43 impl PatternError {
44 44 pub fn pynew(py: Python, inner: hg::PatternError) -> PyErr {
45 45 match inner {
46 46 hg::PatternError::UnsupportedSyntax(m) => {
47 47 PatternError::new(py, ("PatternError", m))
48 48 }
49 49 }
50 50 }
51 51 }
52 52
53
54 53 impl PatternFileError {
55 54 pub fn pynew(py: Python, inner: hg::PatternFileError) -> PyErr {
56 55 match inner {
57 56 hg::PatternFileError::IO(e) => {
58 let value = (
59 e.raw_os_error().unwrap_or(2),
60 e.to_string()
61 );
57 let value = (e.raw_os_error().unwrap_or(2), e.to_string());
62 58 PyErr::new::<exc::IOError, _>(py, value)
63 59 }
64 hg::PatternFileError::Pattern(e, l) => {
65 match e {
66 hg::PatternError::UnsupportedSyntax(m) =>
60 hg::PatternFileError::Pattern(e, l) => match e {
61 hg::PatternError::UnsupportedSyntax(m) => {
67 62 PatternFileError::new(py, ("PatternFileError", m, l))
68 63 }
64 },
65 }
69 66 }
70 67 }
71 }
72 }
@@ -1,53 +1,65 b''
1 1 // lib.rs
2 2 //
3 3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
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 //! Python bindings of `hg-core` objects using the `cpython` crate.
9 9 //! Once compiled, the resulting single shared library object can be placed in
10 10 //! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
11 11 //! It holds several modules, so that from the point of view of Python,
12 12 //! it behaves as the `cext` package.
13 13 //!
14 14 //! Example:
15 15 //!
16 16 //! ```text
17 17 //! >>> from mercurial.rustext import ancestor
18 18 //! >>> ancestor.__doc__
19 19 //! 'Generic DAG ancestor algorithms - Rust implementation'
20 20 //! ```
21 21
22 22 #[macro_use]
23 23 extern crate cpython;
24 24 extern crate hg;
25 25 extern crate libc;
26 26
27 27 pub mod ancestors;
28 28 mod cindex;
29 29 mod conversion;
30 30 pub mod dagops;
31 pub mod dirstate;
31 32 pub mod discovery;
32 33 pub mod exceptions;
33 pub mod dirstate;
34 34 pub mod filepatterns;
35 35
36 36 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
37 37 m.add(
38 38 py,
39 39 "__doc__",
40 40 "Mercurial core concepts - Rust implementation",
41 41 )?;
42 42
43 43 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
44 44 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
45 45 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
46 46 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
47 47 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
48 m.add(py, "filepatterns", filepatterns::init_module(py, &dotted_name)?)?;
48 m.add(
49 py,
50 "filepatterns",
51 filepatterns::init_module(py, &dotted_name)?,
52 )?;
49 53 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
50 m.add(py, "PatternFileError", py.get_type::<exceptions::PatternFileError>())?;
51 m.add(py, "PatternError", py.get_type::<exceptions::PatternError>())?;
54 m.add(
55 py,
56 "PatternFileError",
57 py.get_type::<exceptions::PatternFileError>(),
58 )?;
59 m.add(
60 py,
61 "PatternError",
62 py.get_type::<exceptions::PatternError>(),
63 )?;
52 64 Ok(())
53 65 });
General Comments 0
You need to be logged in to leave comments. Login now