Show More
@@ -1,341 +1,341 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 crate::{ |
|
11 | use crate::{ | |
12 | dirstate::EntryState, utils::files, DirsIterable, DirstateEntry, |
|
12 | dirstate::EntryState, utils::files, DirsIterable, DirstateEntry, | |
13 | DirstateMapError, |
|
13 | DirstateMapError, | |
14 | }; |
|
14 | }; | |
15 |
use std::collections::hash_map:: |
|
15 | use std::collections::hash_map::Entry; | |
16 | use std::collections::HashMap; |
|
16 | use std::collections::HashMap; | |
17 |
|
17 | |||
18 | #[derive(PartialEq, Debug)] |
|
18 | #[derive(PartialEq, Debug)] | |
19 | pub struct DirsMultiset { |
|
19 | pub struct DirsMultiset { | |
20 | inner: HashMap<Vec<u8>, u32>, |
|
20 | inner: HashMap<Vec<u8>, u32>, | |
21 | } |
|
21 | } | |
22 |
|
22 | |||
23 | impl DirsMultiset { |
|
23 | impl DirsMultiset { | |
24 | /// Initializes the multiset from a dirstate or a manifest. |
|
24 | /// Initializes the multiset from a dirstate or a manifest. | |
25 | /// |
|
25 | /// | |
26 | /// If `skip_state` is provided, skips dirstate entries with equal state. |
|
26 | /// If `skip_state` is provided, skips dirstate entries with equal state. | |
27 | pub fn new( |
|
27 | pub fn new( | |
28 | iterable: DirsIterable, |
|
28 | iterable: DirsIterable, | |
29 | skip_state: Option<EntryState>, |
|
29 | skip_state: Option<EntryState>, | |
30 | ) -> Self { |
|
30 | ) -> Self { | |
31 | let mut multiset = DirsMultiset { |
|
31 | let mut multiset = DirsMultiset { | |
32 | inner: HashMap::new(), |
|
32 | inner: HashMap::new(), | |
33 | }; |
|
33 | }; | |
34 |
|
34 | |||
35 | match iterable { |
|
35 | match iterable { | |
36 | DirsIterable::Dirstate(vec) => { |
|
36 | DirsIterable::Dirstate(vec) => { | |
37 | for (filename, DirstateEntry { state, .. }) in vec { |
|
37 | for (filename, DirstateEntry { state, .. }) in vec { | |
38 | // This `if` is optimized out of the loop |
|
38 | // This `if` is optimized out of the loop | |
39 | if let Some(skip) = skip_state { |
|
39 | if let Some(skip) = skip_state { | |
40 | if skip != *state { |
|
40 | if skip != *state { | |
41 | multiset.add_path(filename); |
|
41 | multiset.add_path(filename); | |
42 | } |
|
42 | } | |
43 | } else { |
|
43 | } else { | |
44 | multiset.add_path(filename); |
|
44 | multiset.add_path(filename); | |
45 | } |
|
45 | } | |
46 | } |
|
46 | } | |
47 | } |
|
47 | } | |
48 | DirsIterable::Manifest(vec) => { |
|
48 | DirsIterable::Manifest(vec) => { | |
49 | for filename in vec { |
|
49 | for filename in vec { | |
50 | multiset.add_path(filename); |
|
50 | multiset.add_path(filename); | |
51 | } |
|
51 | } | |
52 | } |
|
52 | } | |
53 | } |
|
53 | } | |
54 |
|
54 | |||
55 | multiset |
|
55 | multiset | |
56 | } |
|
56 | } | |
57 |
|
57 | |||
58 | /// Increases the count of deepest directory contained in the path. |
|
58 | /// Increases the count of deepest directory contained in the path. | |
59 | /// |
|
59 | /// | |
60 | /// If the directory is not yet in the map, adds its parents. |
|
60 | /// If the directory is not yet in the map, adds its parents. | |
61 | pub fn add_path(&mut self, path: &[u8]) { |
|
61 | pub fn add_path(&mut self, path: &[u8]) { | |
62 | for subpath in files::find_dirs(path) { |
|
62 | for subpath in files::find_dirs(path) { | |
63 | if let Some(val) = self.inner.get_mut(subpath) { |
|
63 | if let Some(val) = self.inner.get_mut(subpath) { | |
64 | *val += 1; |
|
64 | *val += 1; | |
65 | break; |
|
65 | break; | |
66 | } |
|
66 | } | |
67 | self.inner.insert(subpath.to_owned(), 1); |
|
67 | self.inner.insert(subpath.to_owned(), 1); | |
68 | } |
|
68 | } | |
69 | } |
|
69 | } | |
70 |
|
70 | |||
71 | /// Decreases the count of deepest directory contained in the path. |
|
71 | /// Decreases the count of deepest directory contained in the path. | |
72 | /// |
|
72 | /// | |
73 | /// If it is the only reference, decreases all parents until one is |
|
73 | /// If it is the only reference, decreases all parents until one is | |
74 | /// removed. |
|
74 | /// removed. | |
75 | /// If the directory is not in the map, something horrible has happened. |
|
75 | /// If the directory is not in the map, something horrible has happened. | |
76 | pub fn delete_path( |
|
76 | pub fn delete_path( | |
77 | &mut self, |
|
77 | &mut self, | |
78 | path: &[u8], |
|
78 | path: &[u8], | |
79 | ) -> Result<(), DirstateMapError> { |
|
79 | ) -> Result<(), DirstateMapError> { | |
80 | for subpath in files::find_dirs(path) { |
|
80 | for subpath in files::find_dirs(path) { | |
81 | match self.inner.entry(subpath.to_owned()) { |
|
81 | match self.inner.entry(subpath.to_owned()) { | |
82 | Entry::Occupied(mut entry) => { |
|
82 | Entry::Occupied(mut entry) => { | |
83 | let val = entry.get().clone(); |
|
83 | let val = entry.get().clone(); | |
84 | if val > 1 { |
|
84 | if val > 1 { | |
85 | entry.insert(val - 1); |
|
85 | entry.insert(val - 1); | |
86 | break; |
|
86 | break; | |
87 | } |
|
87 | } | |
88 | entry.remove(); |
|
88 | entry.remove(); | |
89 | } |
|
89 | } | |
90 | Entry::Vacant(_) => { |
|
90 | Entry::Vacant(_) => { | |
91 | return Err(DirstateMapError::PathNotFound( |
|
91 | return Err(DirstateMapError::PathNotFound( | |
92 | path.to_owned(), |
|
92 | path.to_owned(), | |
93 | )) |
|
93 | )) | |
94 | } |
|
94 | } | |
95 | }; |
|
95 | }; | |
96 | } |
|
96 | } | |
97 |
|
97 | |||
98 | Ok(()) |
|
98 | Ok(()) | |
99 | } |
|
99 | } | |
100 |
|
100 | |||
101 |
pub fn contains |
|
101 | pub fn contains(&self, key: &[u8]) -> bool { | |
102 | self.inner.contains_key(key) |
|
102 | self.inner.contains_key(key) | |
103 | } |
|
103 | } | |
104 |
|
104 | |||
105 |
pub fn iter(&self) -> Iter<Vec<u8> |
|
105 | pub fn iter(&self) -> impl Iterator<Item = &Vec<u8>> { | |
106 |
self.inner. |
|
106 | self.inner.keys() | |
107 | } |
|
107 | } | |
108 |
|
108 | |||
109 | pub fn len(&self) -> usize { |
|
109 | pub fn len(&self) -> usize { | |
110 | self.inner.len() |
|
110 | self.inner.len() | |
111 | } |
|
111 | } | |
112 | } |
|
112 | } | |
113 |
|
113 | |||
114 | #[cfg(test)] |
|
114 | #[cfg(test)] | |
115 | mod tests { |
|
115 | mod tests { | |
116 | use super::*; |
|
116 | use super::*; | |
117 | use std::collections::HashMap; |
|
117 | use std::collections::HashMap; | |
118 |
|
118 | |||
119 | #[test] |
|
119 | #[test] | |
120 | fn test_delete_path_path_not_found() { |
|
120 | fn test_delete_path_path_not_found() { | |
121 | let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None); |
|
121 | let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None); | |
122 | let path = b"doesnotexist/"; |
|
122 | let path = b"doesnotexist/"; | |
123 | assert_eq!( |
|
123 | assert_eq!( | |
124 | Err(DirstateMapError::PathNotFound(path.to_vec())), |
|
124 | Err(DirstateMapError::PathNotFound(path.to_vec())), | |
125 | map.delete_path(path) |
|
125 | map.delete_path(path) | |
126 | ); |
|
126 | ); | |
127 | } |
|
127 | } | |
128 |
|
128 | |||
129 | #[test] |
|
129 | #[test] | |
130 | fn test_delete_path_empty_path() { |
|
130 | fn test_delete_path_empty_path() { | |
131 | let mut map = |
|
131 | let mut map = | |
132 | DirsMultiset::new(DirsIterable::Manifest(&vec![vec![]]), None); |
|
132 | DirsMultiset::new(DirsIterable::Manifest(&vec![vec![]]), None); | |
133 | let path = b""; |
|
133 | let path = b""; | |
134 | assert_eq!(Ok(()), map.delete_path(path)); |
|
134 | assert_eq!(Ok(()), map.delete_path(path)); | |
135 | assert_eq!( |
|
135 | assert_eq!( | |
136 | Err(DirstateMapError::PathNotFound(path.to_vec())), |
|
136 | Err(DirstateMapError::PathNotFound(path.to_vec())), | |
137 | map.delete_path(path) |
|
137 | map.delete_path(path) | |
138 | ); |
|
138 | ); | |
139 | } |
|
139 | } | |
140 |
|
140 | |||
141 | #[test] |
|
141 | #[test] | |
142 | fn test_delete_path_successful() { |
|
142 | fn test_delete_path_successful() { | |
143 | let mut map = DirsMultiset { |
|
143 | let mut map = DirsMultiset { | |
144 | inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)] |
|
144 | inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)] | |
145 | .iter() |
|
145 | .iter() | |
146 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) |
|
146 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) | |
147 | .collect(), |
|
147 | .collect(), | |
148 | }; |
|
148 | }; | |
149 |
|
149 | |||
150 | assert_eq!(Ok(()), map.delete_path(b"a/b/")); |
|
150 | assert_eq!(Ok(()), map.delete_path(b"a/b/")); | |
151 | assert_eq!(Ok(()), map.delete_path(b"a/b/")); |
|
151 | assert_eq!(Ok(()), map.delete_path(b"a/b/")); | |
152 | assert_eq!( |
|
152 | assert_eq!( | |
153 | Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())), |
|
153 | Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())), | |
154 | map.delete_path(b"a/b/") |
|
154 | map.delete_path(b"a/b/") | |
155 | ); |
|
155 | ); | |
156 |
|
156 | |||
157 | assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
157 | assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap()); | |
158 | assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap()); |
|
158 | assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap()); | |
159 | eprintln!("{:?}", map); |
|
159 | eprintln!("{:?}", map); | |
160 | assert_eq!(Ok(()), map.delete_path(b"a/")); |
|
160 | assert_eq!(Ok(()), map.delete_path(b"a/")); | |
161 | eprintln!("{:?}", map); |
|
161 | eprintln!("{:?}", map); | |
162 |
|
162 | |||
163 | assert_eq!(Ok(()), map.delete_path(b"a/c/")); |
|
163 | assert_eq!(Ok(()), map.delete_path(b"a/c/")); | |
164 | assert_eq!( |
|
164 | assert_eq!( | |
165 | Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())), |
|
165 | Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())), | |
166 | map.delete_path(b"a/c/") |
|
166 | map.delete_path(b"a/c/") | |
167 | ); |
|
167 | ); | |
168 | } |
|
168 | } | |
169 |
|
169 | |||
170 | #[test] |
|
170 | #[test] | |
171 | fn test_add_path_empty_path() { |
|
171 | fn test_add_path_empty_path() { | |
172 | let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None); |
|
172 | let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None); | |
173 | let path = b""; |
|
173 | let path = b""; | |
174 | map.add_path(path); |
|
174 | map.add_path(path); | |
175 |
|
175 | |||
176 | assert_eq!(1, map.len()); |
|
176 | assert_eq!(1, map.len()); | |
177 | } |
|
177 | } | |
178 |
|
178 | |||
179 | #[test] |
|
179 | #[test] | |
180 | fn test_add_path_successful() { |
|
180 | fn test_add_path_successful() { | |
181 | let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None); |
|
181 | let mut map = DirsMultiset::new(DirsIterable::Manifest(&vec![]), None); | |
182 |
|
182 | |||
183 | map.add_path(b"a/"); |
|
183 | map.add_path(b"a/"); | |
184 | assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
184 | assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap()); | |
185 | assert_eq!(1, *map.inner.get(&Vec::new()).unwrap()); |
|
185 | assert_eq!(1, *map.inner.get(&Vec::new()).unwrap()); | |
186 | assert_eq!(2, map.len()); |
|
186 | assert_eq!(2, map.len()); | |
187 |
|
187 | |||
188 | // Non directory should be ignored |
|
188 | // Non directory should be ignored | |
189 | map.add_path(b"a"); |
|
189 | map.add_path(b"a"); | |
190 | assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
190 | assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap()); | |
191 | assert_eq!(2, map.len()); |
|
191 | assert_eq!(2, map.len()); | |
192 |
|
192 | |||
193 | // Non directory will still add its base |
|
193 | // Non directory will still add its base | |
194 | map.add_path(b"a/b"); |
|
194 | map.add_path(b"a/b"); | |
195 | assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
195 | assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap()); | |
196 | assert_eq!(2, map.len()); |
|
196 | assert_eq!(2, map.len()); | |
197 |
|
197 | |||
198 | // Duplicate path works |
|
198 | // Duplicate path works | |
199 | map.add_path(b"a/"); |
|
199 | map.add_path(b"a/"); | |
200 | assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
200 | assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap()); | |
201 |
|
201 | |||
202 | // Nested dir adds to its base |
|
202 | // Nested dir adds to its base | |
203 | map.add_path(b"a/b/"); |
|
203 | map.add_path(b"a/b/"); | |
204 | assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
204 | assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap()); | |
205 | assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap()); |
|
205 | assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap()); | |
206 |
|
206 | |||
207 | // but not its base's base, because it already existed |
|
207 | // but not its base's base, because it already existed | |
208 | map.add_path(b"a/b/c/"); |
|
208 | map.add_path(b"a/b/c/"); | |
209 | assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap()); |
|
209 | assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap()); | |
210 | assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap()); |
|
210 | assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap()); | |
211 |
|
211 | |||
212 | map.add_path(b"a/c/"); |
|
212 | map.add_path(b"a/c/"); | |
213 | assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap()); |
|
213 | assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap()); | |
214 |
|
214 | |||
215 | let expected = DirsMultiset { |
|
215 | let expected = DirsMultiset { | |
216 | inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)] |
|
216 | inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)] | |
217 | .iter() |
|
217 | .iter() | |
218 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) |
|
218 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) | |
219 | .collect(), |
|
219 | .collect(), | |
220 | }; |
|
220 | }; | |
221 | assert_eq!(map, expected); |
|
221 | assert_eq!(map, expected); | |
222 | } |
|
222 | } | |
223 |
|
223 | |||
224 | #[test] |
|
224 | #[test] | |
225 | fn test_dirsmultiset_new_empty() { |
|
225 | fn test_dirsmultiset_new_empty() { | |
226 | use DirsIterable::{Dirstate, Manifest}; |
|
226 | use DirsIterable::{Dirstate, Manifest}; | |
227 |
|
227 | |||
228 | let new = DirsMultiset::new(Manifest(&vec![]), None); |
|
228 | let new = DirsMultiset::new(Manifest(&vec![]), None); | |
229 | let expected = DirsMultiset { |
|
229 | let expected = DirsMultiset { | |
230 | inner: HashMap::new(), |
|
230 | inner: HashMap::new(), | |
231 | }; |
|
231 | }; | |
232 | assert_eq!(expected, new); |
|
232 | assert_eq!(expected, new); | |
233 |
|
233 | |||
234 | let new = DirsMultiset::new(Dirstate(&HashMap::new()), None); |
|
234 | let new = DirsMultiset::new(Dirstate(&HashMap::new()), None); | |
235 | let expected = DirsMultiset { |
|
235 | let expected = DirsMultiset { | |
236 | inner: HashMap::new(), |
|
236 | inner: HashMap::new(), | |
237 | }; |
|
237 | }; | |
238 | assert_eq!(expected, new); |
|
238 | assert_eq!(expected, new); | |
239 | } |
|
239 | } | |
240 |
|
240 | |||
241 | #[test] |
|
241 | #[test] | |
242 | fn test_dirsmultiset_new_no_skip() { |
|
242 | fn test_dirsmultiset_new_no_skip() { | |
243 | use DirsIterable::{Dirstate, Manifest}; |
|
243 | use DirsIterable::{Dirstate, Manifest}; | |
244 |
|
244 | |||
245 | let input_vec = ["a/", "b/", "a/c", "a/d/"] |
|
245 | let input_vec = ["a/", "b/", "a/c", "a/d/"] | |
246 | .iter() |
|
246 | .iter() | |
247 | .map(|e| e.as_bytes().to_vec()) |
|
247 | .map(|e| e.as_bytes().to_vec()) | |
248 | .collect(); |
|
248 | .collect(); | |
249 | let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] |
|
249 | let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] | |
250 | .iter() |
|
250 | .iter() | |
251 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) |
|
251 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) | |
252 | .collect(); |
|
252 | .collect(); | |
253 |
|
253 | |||
254 | let new = DirsMultiset::new(Manifest(&input_vec), None); |
|
254 | let new = DirsMultiset::new(Manifest(&input_vec), None); | |
255 | let expected = DirsMultiset { |
|
255 | let expected = DirsMultiset { | |
256 | inner: expected_inner, |
|
256 | inner: expected_inner, | |
257 | }; |
|
257 | }; | |
258 | assert_eq!(expected, new); |
|
258 | assert_eq!(expected, new); | |
259 |
|
259 | |||
260 | let input_map = ["a/", "b/", "a/c", "a/d/"] |
|
260 | let input_map = ["a/", "b/", "a/c", "a/d/"] | |
261 | .iter() |
|
261 | .iter() | |
262 | .map(|f| { |
|
262 | .map(|f| { | |
263 | ( |
|
263 | ( | |
264 | f.as_bytes().to_vec(), |
|
264 | f.as_bytes().to_vec(), | |
265 | DirstateEntry { |
|
265 | DirstateEntry { | |
266 | state: EntryState::Normal, |
|
266 | state: EntryState::Normal, | |
267 | mode: 0, |
|
267 | mode: 0, | |
268 | mtime: 0, |
|
268 | mtime: 0, | |
269 | size: 0, |
|
269 | size: 0, | |
270 | }, |
|
270 | }, | |
271 | ) |
|
271 | ) | |
272 | }) |
|
272 | }) | |
273 | .collect(); |
|
273 | .collect(); | |
274 | let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] |
|
274 | let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] | |
275 | .iter() |
|
275 | .iter() | |
276 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) |
|
276 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) | |
277 | .collect(); |
|
277 | .collect(); | |
278 |
|
278 | |||
279 | let new = DirsMultiset::new(Dirstate(&input_map), None); |
|
279 | let new = DirsMultiset::new(Dirstate(&input_map), None); | |
280 | let expected = DirsMultiset { |
|
280 | let expected = DirsMultiset { | |
281 | inner: expected_inner, |
|
281 | inner: expected_inner, | |
282 | }; |
|
282 | }; | |
283 | assert_eq!(expected, new); |
|
283 | assert_eq!(expected, new); | |
284 | } |
|
284 | } | |
285 |
|
285 | |||
286 | #[test] |
|
286 | #[test] | |
287 | fn test_dirsmultiset_new_skip() { |
|
287 | fn test_dirsmultiset_new_skip() { | |
288 | use DirsIterable::{Dirstate, Manifest}; |
|
288 | use DirsIterable::{Dirstate, Manifest}; | |
289 |
|
289 | |||
290 | let input_vec = ["a/", "b/", "a/c", "a/d/"] |
|
290 | let input_vec = ["a/", "b/", "a/c", "a/d/"] | |
291 | .iter() |
|
291 | .iter() | |
292 | .map(|e| e.as_bytes().to_vec()) |
|
292 | .map(|e| e.as_bytes().to_vec()) | |
293 | .collect(); |
|
293 | .collect(); | |
294 | let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] |
|
294 | let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)] | |
295 | .iter() |
|
295 | .iter() | |
296 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) |
|
296 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) | |
297 | .collect(); |
|
297 | .collect(); | |
298 |
|
298 | |||
299 | let new = |
|
299 | let new = | |
300 | DirsMultiset::new(Manifest(&input_vec), Some(EntryState::Normal)); |
|
300 | DirsMultiset::new(Manifest(&input_vec), Some(EntryState::Normal)); | |
301 | let expected = DirsMultiset { |
|
301 | let expected = DirsMultiset { | |
302 | inner: expected_inner, |
|
302 | inner: expected_inner, | |
303 | }; |
|
303 | }; | |
304 | // Skip does not affect a manifest |
|
304 | // Skip does not affect a manifest | |
305 | assert_eq!(expected, new); |
|
305 | assert_eq!(expected, new); | |
306 |
|
306 | |||
307 | let input_map = [ |
|
307 | let input_map = [ | |
308 | ("a/", EntryState::Normal), |
|
308 | ("a/", EntryState::Normal), | |
309 | ("a/b/", EntryState::Normal), |
|
309 | ("a/b/", EntryState::Normal), | |
310 | ("a/c", EntryState::Removed), |
|
310 | ("a/c", EntryState::Removed), | |
311 | ("a/d/", EntryState::Merged), |
|
311 | ("a/d/", EntryState::Merged), | |
312 | ] |
|
312 | ] | |
313 | .iter() |
|
313 | .iter() | |
314 | .map(|(f, state)| { |
|
314 | .map(|(f, state)| { | |
315 | ( |
|
315 | ( | |
316 | f.as_bytes().to_vec(), |
|
316 | f.as_bytes().to_vec(), | |
317 | DirstateEntry { |
|
317 | DirstateEntry { | |
318 | state: *state, |
|
318 | state: *state, | |
319 | mode: 0, |
|
319 | mode: 0, | |
320 | mtime: 0, |
|
320 | mtime: 0, | |
321 | size: 0, |
|
321 | size: 0, | |
322 | }, |
|
322 | }, | |
323 | ) |
|
323 | ) | |
324 | }) |
|
324 | }) | |
325 | .collect(); |
|
325 | .collect(); | |
326 |
|
326 | |||
327 | // "a" incremented with "a/c" and "a/d/" |
|
327 | // "a" incremented with "a/c" and "a/d/" | |
328 | let expected_inner = [("", 1), ("a", 2), ("a/d", 1)] |
|
328 | let expected_inner = [("", 1), ("a", 2), ("a/d", 1)] | |
329 | .iter() |
|
329 | .iter() | |
330 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) |
|
330 | .map(|(k, v)| (k.as_bytes().to_vec(), *v)) | |
331 | .collect(); |
|
331 | .collect(); | |
332 |
|
332 | |||
333 | let new = |
|
333 | let new = | |
334 | DirsMultiset::new(Dirstate(&input_map), Some(EntryState::Normal)); |
|
334 | DirsMultiset::new(Dirstate(&input_map), Some(EntryState::Normal)); | |
335 | let expected = DirsMultiset { |
|
335 | let expected = DirsMultiset { | |
336 | inner: expected_inner, |
|
336 | inner: expected_inner, | |
337 | }; |
|
337 | }; | |
338 | assert_eq!(expected, new); |
|
338 | assert_eq!(expected, new); | |
339 | } |
|
339 | } | |
340 |
|
340 | |||
341 | } |
|
341 | } |
@@ -1,118 +1,113 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 | //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the |
|
8 | //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the | |
9 | //! `hg-core` package. |
|
9 | //! `hg-core` package. | |
10 |
|
10 | |||
11 | use std::cell::RefCell; |
|
11 | use std::cell::RefCell; | |
12 |
|
12 | |||
13 | use cpython::{ |
|
13 | use cpython::{ | |
14 | exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyObject, PyResult, |
|
14 | exc, ObjectProtocol, PyBytes, PyDict, PyErr, PyObject, PyResult, | |
15 | ToPyObject, |
|
15 | PythonObject, ToPyObject, | |
16 | }; |
|
16 | }; | |
17 |
|
17 | |||
18 | use crate::dirstate::extract_dirstate; |
|
18 | use crate::dirstate::extract_dirstate; | |
19 | use hg::{ |
|
19 | use hg::{ | |
20 | DirsIterable, DirsMultiset, DirstateMapError, DirstateParseError, |
|
20 | DirsIterable, DirsMultiset, DirstateMapError, DirstateParseError, | |
21 | EntryState, |
|
21 | EntryState, | |
22 | }; |
|
22 | }; | |
23 | use std::convert::TryInto; |
|
23 | use std::convert::TryInto; | |
24 |
|
24 | |||
25 | py_class!(pub class Dirs |py| { |
|
25 | py_class!(pub class Dirs |py| { | |
26 | data dirs_map: RefCell<DirsMultiset>; |
|
26 | data dirs_map: RefCell<DirsMultiset>; | |
27 |
|
27 | |||
28 | // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes |
|
28 | // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes | |
29 | // a `list`) |
|
29 | // a `list`) | |
30 | def __new__( |
|
30 | def __new__( | |
31 | _cls, |
|
31 | _cls, | |
32 | map: PyObject, |
|
32 | map: PyObject, | |
33 | skip: Option<PyObject> = None |
|
33 | skip: Option<PyObject> = None | |
34 | ) -> PyResult<Self> { |
|
34 | ) -> PyResult<Self> { | |
35 | let mut skip_state: Option<EntryState> = None; |
|
35 | let mut skip_state: Option<EntryState> = None; | |
36 | if let Some(skip) = skip { |
|
36 | if let Some(skip) = skip { | |
37 | skip_state = Some( |
|
37 | skip_state = Some( | |
38 | skip.extract::<PyBytes>(py)?.data(py)[0] |
|
38 | skip.extract::<PyBytes>(py)?.data(py)[0] | |
39 | .try_into() |
|
39 | .try_into() | |
40 | .map_err(|e: DirstateParseError| { |
|
40 | .map_err(|e: DirstateParseError| { | |
41 | PyErr::new::<exc::ValueError, _>(py, e.to_string()) |
|
41 | PyErr::new::<exc::ValueError, _>(py, e.to_string()) | |
42 | })?, |
|
42 | })?, | |
43 | ); |
|
43 | ); | |
44 | } |
|
44 | } | |
45 | let inner = if let Ok(map) = map.cast_as::<PyDict>(py) { |
|
45 | let inner = if let Ok(map) = map.cast_as::<PyDict>(py) { | |
46 | let dirstate = extract_dirstate(py, &map)?; |
|
46 | let dirstate = extract_dirstate(py, &map)?; | |
47 | DirsMultiset::new( |
|
47 | DirsMultiset::new( | |
48 | DirsIterable::Dirstate(&dirstate), |
|
48 | DirsIterable::Dirstate(&dirstate), | |
49 | skip_state, |
|
49 | skip_state, | |
50 | ) |
|
50 | ) | |
51 | } else { |
|
51 | } else { | |
52 | let map: Result<Vec<Vec<u8>>, PyErr> = map |
|
52 | let map: Result<Vec<Vec<u8>>, PyErr> = map | |
53 | .iter(py)? |
|
53 | .iter(py)? | |
54 | .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned())) |
|
54 | .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned())) | |
55 | .collect(); |
|
55 | .collect(); | |
56 | DirsMultiset::new( |
|
56 | DirsMultiset::new( | |
57 | DirsIterable::Manifest(&map?), |
|
57 | DirsIterable::Manifest(&map?), | |
58 | skip_state, |
|
58 | skip_state, | |
59 | ) |
|
59 | ) | |
60 | }; |
|
60 | }; | |
61 |
|
61 | |||
62 | Self::create_instance(py, RefCell::new(inner)) |
|
62 | Self::create_instance(py, RefCell::new(inner)) | |
63 | } |
|
63 | } | |
64 |
|
64 | |||
65 | def addpath(&self, path: PyObject) -> PyResult<PyObject> { |
|
65 | def addpath(&self, path: PyObject) -> PyResult<PyObject> { | |
66 | self.dirs_map(py).borrow_mut().add_path( |
|
66 | self.dirs_map(py).borrow_mut().add_path( | |
67 | path.extract::<PyBytes>(py)?.data(py), |
|
67 | path.extract::<PyBytes>(py)?.data(py), | |
68 | ); |
|
68 | ); | |
69 | Ok(py.None()) |
|
69 | Ok(py.None()) | |
70 | } |
|
70 | } | |
71 |
|
71 | |||
72 | def delpath(&self, path: PyObject) -> PyResult<PyObject> { |
|
72 | def delpath(&self, path: PyObject) -> PyResult<PyObject> { | |
73 | self.dirs_map(py).borrow_mut().delete_path( |
|
73 | self.dirs_map(py).borrow_mut().delete_path( | |
74 | path.extract::<PyBytes>(py)?.data(py), |
|
74 | path.extract::<PyBytes>(py)?.data(py), | |
75 | ) |
|
75 | ) | |
76 | .and(Ok(py.None())) |
|
76 | .and(Ok(py.None())) | |
77 | .or_else(|e| { |
|
77 | .or_else(|e| { | |
78 | match e { |
|
78 | match e { | |
79 | DirstateMapError::PathNotFound(_p) => { |
|
79 | DirstateMapError::PathNotFound(_p) => { | |
80 | Err(PyErr::new::<exc::ValueError, _>( |
|
80 | Err(PyErr::new::<exc::ValueError, _>( | |
81 | py, |
|
81 | py, | |
82 | "expected a value, found none".to_string(), |
|
82 | "expected a value, found none".to_string(), | |
83 | )) |
|
83 | )) | |
84 | } |
|
84 | } | |
85 | DirstateMapError::EmptyPath => { |
|
85 | DirstateMapError::EmptyPath => { | |
86 | Ok(py.None()) |
|
86 | Ok(py.None()) | |
87 | } |
|
87 | } | |
88 | } |
|
88 | } | |
89 | }) |
|
89 | }) | |
90 | } |
|
90 | } | |
91 |
|
91 | |||
92 | // This is really inefficient on top of being ugly, but it's an easy way |
|
92 | // This is really inefficient on top of being ugly, but it's an easy way | |
93 | // of having it work to continue working on the rest of the module |
|
93 | // of having it work to continue working on the rest of the module | |
94 | // hopefully bypassing Python entirely pretty soon. |
|
94 | // hopefully bypassing Python entirely pretty soon. | |
95 | def __iter__(&self) -> PyResult<PyObject> { |
|
95 | def __iter__(&self) -> PyResult<PyObject> { | |
96 | let dict = PyDict::new(py); |
|
96 | let dirs = self.dirs_map(py).borrow(); | |
97 |
|
97 | let dirs: Vec<_> = dirs | ||
98 | for (key, value) in self.dirs_map(py).borrow().iter() { |
|
98 | .iter() | |
99 | dict.set_item( |
|
99 | .map(|d| PyBytes::new(py, d)) | |
100 | py, |
|
100 | .collect(); | |
101 | PyBytes::new(py, &key[..]), |
|
101 | dirs.to_py_object(py) | |
102 |
|
|
102 | .into_object() | |
103 |
) |
|
103 | .iter(py) | |
104 | } |
|
104 | .map(|o| o.into_object()) | |
105 |
|
||||
106 | let locals = PyDict::new(py); |
|
|||
107 | locals.set_item(py, "obj", dict)?; |
|
|||
108 |
|
||||
109 | py.eval("iter(obj)", None, Some(&locals)) |
|
|||
110 | } |
|
105 | } | |
111 |
|
106 | |||
112 | def __contains__(&self, item: PyObject) -> PyResult<bool> { |
|
107 | def __contains__(&self, item: PyObject) -> PyResult<bool> { | |
113 | Ok(self |
|
108 | Ok(self | |
114 | .dirs_map(py) |
|
109 | .dirs_map(py) | |
115 | .borrow() |
|
110 | .borrow() | |
116 |
.contains |
|
111 | .contains(item.extract::<PyBytes>(py)?.data(py).as_ref())) | |
117 | } |
|
112 | } | |
118 | }); |
|
113 | }); |
General Comments 0
You need to be logged in to leave comments.
Login now