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