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