##// END OF EJS Templates
rust-utils: use new find_dirs iterator...
Raphaël Gomès -
r42814:717686c5 default
parent child Browse files
Show More
@@ -1,360 +1,329 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, Iter};
12 12 use std::collections::HashMap;
13 13 use {DirsIterable, DirstateEntry, DirstateMapError};
14 use utils::files;
14 15
15 16 #[derive(PartialEq, Debug)]
16 17 pub struct DirsMultiset {
17 18 inner: HashMap<Vec<u8>, u32>,
18 19 }
19 20
20 21 impl DirsMultiset {
21 22 /// Initializes the multiset from a dirstate or a manifest.
22 23 ///
23 24 /// If `skip_state` is provided, skips dirstate entries with equal state.
24 25 pub fn new(iterable: DirsIterable, skip_state: Option<i8>) -> Self {
25 26 let mut multiset = DirsMultiset {
26 27 inner: HashMap::new(),
27 28 };
28 29
29 30 match iterable {
30 31 DirsIterable::Dirstate(vec) => {
31 32 for (ref filename, DirstateEntry { state, .. }) in vec {
32 33 // This `if` is optimized out of the loop
33 34 if let Some(skip) = skip_state {
34 35 if skip != state {
35 36 multiset.add_path(filename);
36 37 }
37 38 } else {
38 39 multiset.add_path(filename);
39 40 }
40 41 }
41 42 }
42 43 DirsIterable::Manifest(vec) => {
43 44 for ref filename in vec {
44 45 multiset.add_path(filename);
45 46 }
46 47 }
47 48 }
48 49
49 50 multiset
50 51 }
51 52
52 /// Returns the slice up to the next directory name from right to left,
53 /// without trailing slash
54 fn find_dir(path: &[u8]) -> &[u8] {
55 let mut path = path;
56 loop {
57 if let Some(new_pos) = path.len().checked_sub(1) {
58 if path[new_pos] == b'/' {
59 break &path[..new_pos];
60 }
61 path = &path[..new_pos];
62 } else {
63 break &[];
64 }
65 }
66 }
67
68 53 /// Increases the count of deepest directory contained in the path.
69 54 ///
70 55 /// If the directory is not yet in the map, adds its parents.
71 56 pub fn add_path(&mut self, path: &[u8]) {
72 let mut pos = path.len();
73
74 loop {
75 let subpath = Self::find_dir(&path[..pos]);
57 for subpath in files::find_dirs(path) {
76 58 if let Some(val) = self.inner.get_mut(subpath) {
77 59 *val += 1;
78 60 break;
79 61 }
80 62 self.inner.insert(subpath.to_owned(), 1);
81
82 pos = subpath.len();
83 if pos == 0 {
84 break;
85 }
86 63 }
87 64 }
88 65
89 66 /// Decreases the count of deepest directory contained in the path.
90 67 ///
91 68 /// If it is the only reference, decreases all parents until one is
92 69 /// removed.
93 70 /// If the directory is not in the map, something horrible has happened.
94 71 pub fn delete_path(
95 72 &mut self,
96 73 path: &[u8],
97 74 ) -> Result<(), DirstateMapError> {
98 let mut pos = path.len();
99
100 loop {
101 let subpath = Self::find_dir(&path[..pos]);
75 for subpath in files::find_dirs(path) {
102 76 match self.inner.entry(subpath.to_owned()) {
103 77 Entry::Occupied(mut entry) => {
104 78 let val = entry.get().clone();
105 79 if val > 1 {
106 80 entry.insert(val - 1);
107 81 break;
108 82 }
109 83 entry.remove();
110 84 }
111 85 Entry::Vacant(_) => {
112 86 return Err(DirstateMapError::PathNotFound(
113 87 path.to_owned(),
114 88 ))
115 89 }
116 90 };
117
118 pos = subpath.len();
119 if pos == 0 {
120 break;
121 }
122 91 }
123 92
124 93 Ok(())
125 94 }
126 95
127 96 pub fn contains_key(&self, key: &[u8]) -> bool {
128 97 self.inner.contains_key(key)
129 98 }
130 99
131 100 pub fn iter(&self) -> Iter<Vec<u8>, u32> {
132 101 self.inner.iter()
133 102 }
134 103
135 104 pub fn len(&self) -> usize {
136 105 self.inner.len()
137 106 }
138 107 }
139 108
140 109 #[cfg(test)]
141 110 mod tests {
142 111 use super::*;
143 112
144 113 #[test]
145 114 fn test_delete_path_path_not_found() {
146 115 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
147 116 let path = b"doesnotexist/";
148 117 assert_eq!(
149 118 Err(DirstateMapError::PathNotFound(path.to_vec())),
150 119 map.delete_path(path)
151 120 );
152 121 }
153 122
154 123 #[test]
155 124 fn test_delete_path_empty_path() {
156 125 let mut map =
157 126 DirsMultiset::new(DirsIterable::Manifest(vec![vec![]]), None);
158 127 let path = b"";
159 128 assert_eq!(Ok(()), map.delete_path(path));
160 129 assert_eq!(
161 130 Err(DirstateMapError::PathNotFound(path.to_vec())),
162 131 map.delete_path(path)
163 132 );
164 133 }
165 134
166 135 #[test]
167 136 fn test_delete_path_successful() {
168 137 let mut map = DirsMultiset {
169 138 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
170 139 .iter()
171 140 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
172 141 .collect(),
173 142 };
174 143
175 144 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
176 145 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
177 146 assert_eq!(
178 147 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
179 148 map.delete_path(b"a/b/")
180 149 );
181 150
182 151 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
183 152 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
184 153 eprintln!("{:?}", map);
185 154 assert_eq!(Ok(()), map.delete_path(b"a/"));
186 155 eprintln!("{:?}", map);
187 156
188 157 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
189 158 assert_eq!(
190 159 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
191 160 map.delete_path(b"a/c/")
192 161 );
193 162 }
194 163
195 164 #[test]
196 165 fn test_add_path_empty_path() {
197 166 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
198 167 let path = b"";
199 168 map.add_path(path);
200 169
201 170 assert_eq!(1, map.len());
202 171 }
203 172
204 173 #[test]
205 174 fn test_add_path_successful() {
206 175 let mut map = DirsMultiset::new(DirsIterable::Manifest(vec![]), None);
207 176
208 177 map.add_path(b"a/");
209 178 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
210 179 assert_eq!(1, *map.inner.get(&Vec::new()).unwrap());
211 180 assert_eq!(2, map.len());
212 181
213 182 // Non directory should be ignored
214 183 map.add_path(b"a");
215 184 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
216 185 assert_eq!(2, map.len());
217 186
218 187 // Non directory will still add its base
219 188 map.add_path(b"a/b");
220 189 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
221 190 assert_eq!(2, map.len());
222 191
223 192 // Duplicate path works
224 193 map.add_path(b"a/");
225 194 assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap());
226 195
227 196 // Nested dir adds to its base
228 197 map.add_path(b"a/b/");
229 198 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
230 199 assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap());
231 200
232 201 // but not its base's base, because it already existed
233 202 map.add_path(b"a/b/c/");
234 203 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
235 204 assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap());
236 205
237 206 map.add_path(b"a/c/");
238 207 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
239 208
240 209 let expected = DirsMultiset {
241 210 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
242 211 .iter()
243 212 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
244 213 .collect(),
245 214 };
246 215 assert_eq!(map, expected);
247 216 }
248 217
249 218 #[test]
250 219 fn test_dirsmultiset_new_empty() {
251 220 use DirsIterable::{Dirstate, Manifest};
252 221
253 222 let new = DirsMultiset::new(Manifest(vec![]), None);
254 223 let expected = DirsMultiset {
255 224 inner: HashMap::new(),
256 225 };
257 226 assert_eq!(expected, new);
258 227
259 228 let new = DirsMultiset::new(Dirstate(vec![]), None);
260 229 let expected = DirsMultiset {
261 230 inner: HashMap::new(),
262 231 };
263 232 assert_eq!(expected, new);
264 233 }
265 234
266 235 #[test]
267 236 fn test_dirsmultiset_new_no_skip() {
268 237 use DirsIterable::{Dirstate, Manifest};
269 238
270 239 let input_vec = ["a/", "b/", "a/c", "a/d/"]
271 240 .iter()
272 241 .map(|e| e.as_bytes().to_vec())
273 242 .collect();
274 243 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
275 244 .iter()
276 245 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
277 246 .collect();
278 247
279 248 let new = DirsMultiset::new(Manifest(input_vec), None);
280 249 let expected = DirsMultiset {
281 250 inner: expected_inner,
282 251 };
283 252 assert_eq!(expected, new);
284 253
285 254 let input_map = ["a/", "b/", "a/c", "a/d/"]
286 255 .iter()
287 256 .map(|f| {
288 257 (
289 258 f.as_bytes().to_vec(),
290 259 DirstateEntry {
291 260 state: 0,
292 261 mode: 0,
293 262 mtime: 0,
294 263 size: 0,
295 264 },
296 265 )
297 266 })
298 267 .collect();
299 268 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
300 269 .iter()
301 270 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
302 271 .collect();
303 272
304 273 let new = DirsMultiset::new(Dirstate(input_map), None);
305 274 let expected = DirsMultiset {
306 275 inner: expected_inner,
307 276 };
308 277 assert_eq!(expected, new);
309 278 }
310 279
311 280 #[test]
312 281 fn test_dirsmultiset_new_skip() {
313 282 use DirsIterable::{Dirstate, Manifest};
314 283
315 284 let input_vec = ["a/", "b/", "a/c", "a/d/"]
316 285 .iter()
317 286 .map(|e| e.as_bytes().to_vec())
318 287 .collect();
319 288 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
320 289 .iter()
321 290 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
322 291 .collect();
323 292
324 293 let new = DirsMultiset::new(Manifest(input_vec), Some('n' as i8));
325 294 let expected = DirsMultiset {
326 295 inner: expected_inner,
327 296 };
328 297 // Skip does not affect a manifest
329 298 assert_eq!(expected, new);
330 299
331 300 let input_map =
332 301 [("a/", 'n'), ("a/b/", 'n'), ("a/c", 'r'), ("a/d/", 'm')]
333 302 .iter()
334 303 .map(|(f, state)| {
335 304 (
336 305 f.as_bytes().to_vec(),
337 306 DirstateEntry {
338 307 state: *state as i8,
339 308 mode: 0,
340 309 mtime: 0,
341 310 size: 0,
342 311 },
343 312 )
344 313 })
345 314 .collect();
346 315
347 316 // "a" incremented with "a/c" and "a/d/"
348 317 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
349 318 .iter()
350 319 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
351 320 .collect();
352 321
353 322 let new = DirsMultiset::new(Dirstate(input_map), Some('n' as i8));
354 323 let expected = DirsMultiset {
355 324 inner: expected_inner,
356 325 };
357 326 assert_eq!(expected, new);
358 327 }
359 328
360 329 }
General Comments 0
You need to be logged in to leave comments. Login now