##// END OF EJS Templates
rust-dirstate: specify concrete return type of DirsMultiset::iter()...
Yuya Nishihara -
r43155:a03a2946 default
parent child Browse files
Show More
@@ -1,319 +1,322 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 crate::{
12 12 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
13 13 };
14 use std::collections::hash_map::Entry;
14 use std::collections::hash_map::{self, Entry};
15 15 use std::collections::HashMap;
16 16
17 // could be encapsulated if we care API stability more seriously
18 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, Vec<u8>, u32>;
19
17 20 #[derive(PartialEq, Debug)]
18 21 pub struct DirsMultiset {
19 22 inner: HashMap<Vec<u8>, u32>,
20 23 }
21 24
22 25 impl DirsMultiset {
23 26 /// Initializes the multiset from a dirstate.
24 27 ///
25 28 /// If `skip_state` is provided, skips dirstate entries with equal state.
26 29 pub fn from_dirstate(
27 30 vec: &HashMap<Vec<u8>, DirstateEntry>,
28 31 skip_state: Option<EntryState>,
29 32 ) -> Self {
30 33 let mut multiset = DirsMultiset {
31 34 inner: HashMap::new(),
32 35 };
33 36
34 37 for (filename, DirstateEntry { state, .. }) in vec {
35 38 // This `if` is optimized out of the loop
36 39 if let Some(skip) = skip_state {
37 40 if skip != *state {
38 41 multiset.add_path(filename);
39 42 }
40 43 } else {
41 44 multiset.add_path(filename);
42 45 }
43 46 }
44 47
45 48 multiset
46 49 }
47 50
48 51 /// Initializes the multiset from a manifest.
49 52 pub fn from_manifest(vec: &Vec<Vec<u8>>) -> Self {
50 53 let mut multiset = DirsMultiset {
51 54 inner: HashMap::new(),
52 55 };
53 56
54 57 for filename in vec {
55 58 multiset.add_path(filename);
56 59 }
57 60
58 61 multiset
59 62 }
60 63
61 64 /// Increases the count of deepest directory contained in the path.
62 65 ///
63 66 /// If the directory is not yet in the map, adds its parents.
64 67 pub fn add_path(&mut self, path: &[u8]) {
65 68 for subpath in files::find_dirs(path) {
66 69 if let Some(val) = self.inner.get_mut(subpath) {
67 70 *val += 1;
68 71 break;
69 72 }
70 73 self.inner.insert(subpath.to_owned(), 1);
71 74 }
72 75 }
73 76
74 77 /// Decreases the count of deepest directory contained in the path.
75 78 ///
76 79 /// If it is the only reference, decreases all parents until one is
77 80 /// removed.
78 81 /// If the directory is not in the map, something horrible has happened.
79 82 pub fn delete_path(
80 83 &mut self,
81 84 path: &[u8],
82 85 ) -> Result<(), DirstateMapError> {
83 86 for subpath in files::find_dirs(path) {
84 87 match self.inner.entry(subpath.to_owned()) {
85 88 Entry::Occupied(mut entry) => {
86 89 let val = entry.get().clone();
87 90 if val > 1 {
88 91 entry.insert(val - 1);
89 92 break;
90 93 }
91 94 entry.remove();
92 95 }
93 96 Entry::Vacant(_) => {
94 97 return Err(DirstateMapError::PathNotFound(
95 98 path.to_owned(),
96 99 ))
97 100 }
98 101 };
99 102 }
100 103
101 104 Ok(())
102 105 }
103 106
104 107 pub fn contains(&self, key: &[u8]) -> bool {
105 108 self.inner.contains_key(key)
106 109 }
107 110
108 pub fn iter(&self) -> impl Iterator<Item = &Vec<u8>> {
111 pub fn iter(&self) -> DirsMultisetIter {
109 112 self.inner.keys()
110 113 }
111 114
112 115 pub fn len(&self) -> usize {
113 116 self.inner.len()
114 117 }
115 118 }
116 119
117 120 #[cfg(test)]
118 121 mod tests {
119 122 use super::*;
120 123 use std::collections::HashMap;
121 124
122 125 #[test]
123 126 fn test_delete_path_path_not_found() {
124 127 let mut map = DirsMultiset::from_manifest(&vec![]);
125 128 let path = b"doesnotexist/";
126 129 assert_eq!(
127 130 Err(DirstateMapError::PathNotFound(path.to_vec())),
128 131 map.delete_path(path)
129 132 );
130 133 }
131 134
132 135 #[test]
133 136 fn test_delete_path_empty_path() {
134 137 let mut map = DirsMultiset::from_manifest(&vec![vec![]]);
135 138 let path = b"";
136 139 assert_eq!(Ok(()), map.delete_path(path));
137 140 assert_eq!(
138 141 Err(DirstateMapError::PathNotFound(path.to_vec())),
139 142 map.delete_path(path)
140 143 );
141 144 }
142 145
143 146 #[test]
144 147 fn test_delete_path_successful() {
145 148 let mut map = DirsMultiset {
146 149 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
147 150 .iter()
148 151 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
149 152 .collect(),
150 153 };
151 154
152 155 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
153 156 assert_eq!(Ok(()), map.delete_path(b"a/b/"));
154 157 assert_eq!(
155 158 Err(DirstateMapError::PathNotFound(b"a/b/".to_vec())),
156 159 map.delete_path(b"a/b/")
157 160 );
158 161
159 162 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
160 163 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
161 164 eprintln!("{:?}", map);
162 165 assert_eq!(Ok(()), map.delete_path(b"a/"));
163 166 eprintln!("{:?}", map);
164 167
165 168 assert_eq!(Ok(()), map.delete_path(b"a/c/"));
166 169 assert_eq!(
167 170 Err(DirstateMapError::PathNotFound(b"a/c/".to_vec())),
168 171 map.delete_path(b"a/c/")
169 172 );
170 173 }
171 174
172 175 #[test]
173 176 fn test_add_path_empty_path() {
174 177 let mut map = DirsMultiset::from_manifest(&vec![]);
175 178 let path = b"";
176 179 map.add_path(path);
177 180
178 181 assert_eq!(1, map.len());
179 182 }
180 183
181 184 #[test]
182 185 fn test_add_path_successful() {
183 186 let mut map = DirsMultiset::from_manifest(&vec![]);
184 187
185 188 map.add_path(b"a/");
186 189 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
187 190 assert_eq!(1, *map.inner.get(&Vec::new()).unwrap());
188 191 assert_eq!(2, map.len());
189 192
190 193 // Non directory should be ignored
191 194 map.add_path(b"a");
192 195 assert_eq!(1, *map.inner.get(&b"a".to_vec()).unwrap());
193 196 assert_eq!(2, map.len());
194 197
195 198 // Non directory will still add its base
196 199 map.add_path(b"a/b");
197 200 assert_eq!(2, *map.inner.get(&b"a".to_vec()).unwrap());
198 201 assert_eq!(2, map.len());
199 202
200 203 // Duplicate path works
201 204 map.add_path(b"a/");
202 205 assert_eq!(3, *map.inner.get(&b"a".to_vec()).unwrap());
203 206
204 207 // Nested dir adds to its base
205 208 map.add_path(b"a/b/");
206 209 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
207 210 assert_eq!(1, *map.inner.get(&b"a/b".to_vec()).unwrap());
208 211
209 212 // but not its base's base, because it already existed
210 213 map.add_path(b"a/b/c/");
211 214 assert_eq!(4, *map.inner.get(&b"a".to_vec()).unwrap());
212 215 assert_eq!(2, *map.inner.get(&b"a/b".to_vec()).unwrap());
213 216
214 217 map.add_path(b"a/c/");
215 218 assert_eq!(1, *map.inner.get(&b"a/c".to_vec()).unwrap());
216 219
217 220 let expected = DirsMultiset {
218 221 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
219 222 .iter()
220 223 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
221 224 .collect(),
222 225 };
223 226 assert_eq!(map, expected);
224 227 }
225 228
226 229 #[test]
227 230 fn test_dirsmultiset_new_empty() {
228 231 let new = DirsMultiset::from_manifest(&vec![]);
229 232 let expected = DirsMultiset {
230 233 inner: HashMap::new(),
231 234 };
232 235 assert_eq!(expected, new);
233 236
234 237 let new = DirsMultiset::from_dirstate(&HashMap::new(), None);
235 238 let expected = DirsMultiset {
236 239 inner: HashMap::new(),
237 240 };
238 241 assert_eq!(expected, new);
239 242 }
240 243
241 244 #[test]
242 245 fn test_dirsmultiset_new_no_skip() {
243 246 let input_vec = ["a/", "b/", "a/c", "a/d/"]
244 247 .iter()
245 248 .map(|e| e.as_bytes().to_vec())
246 249 .collect();
247 250 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
248 251 .iter()
249 252 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
250 253 .collect();
251 254
252 255 let new = DirsMultiset::from_manifest(&input_vec);
253 256 let expected = DirsMultiset {
254 257 inner: expected_inner,
255 258 };
256 259 assert_eq!(expected, new);
257 260
258 261 let input_map = ["a/", "b/", "a/c", "a/d/"]
259 262 .iter()
260 263 .map(|f| {
261 264 (
262 265 f.as_bytes().to_vec(),
263 266 DirstateEntry {
264 267 state: EntryState::Normal,
265 268 mode: 0,
266 269 mtime: 0,
267 270 size: 0,
268 271 },
269 272 )
270 273 })
271 274 .collect();
272 275 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
273 276 .iter()
274 277 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
275 278 .collect();
276 279
277 280 let new = DirsMultiset::from_dirstate(&input_map, None);
278 281 let expected = DirsMultiset {
279 282 inner: expected_inner,
280 283 };
281 284 assert_eq!(expected, new);
282 285 }
283 286
284 287 #[test]
285 288 fn test_dirsmultiset_new_skip() {
286 289 let input_map = [
287 290 ("a/", EntryState::Normal),
288 291 ("a/b/", EntryState::Normal),
289 292 ("a/c", EntryState::Removed),
290 293 ("a/d/", EntryState::Merged),
291 294 ]
292 295 .iter()
293 296 .map(|(f, state)| {
294 297 (
295 298 f.as_bytes().to_vec(),
296 299 DirstateEntry {
297 300 state: *state,
298 301 mode: 0,
299 302 mtime: 0,
300 303 size: 0,
301 304 },
302 305 )
303 306 })
304 307 .collect();
305 308
306 309 // "a" incremented with "a/c" and "a/d/"
307 310 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
308 311 .iter()
309 312 .map(|(k, v)| (k.as_bytes().to_vec(), *v))
310 313 .collect();
311 314
312 315 let new =
313 316 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal));
314 317 let expected = DirsMultiset {
315 318 inner: expected_inner,
316 319 };
317 320 assert_eq!(expected, new);
318 321 }
319 322 }
@@ -1,148 +1,148 b''
1 1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5 mod ancestors;
6 6 pub mod dagops;
7 7 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
8 8 mod dirstate;
9 9 pub mod discovery;
10 10 pub mod testing; // unconditionally built, for use from integration tests
11 11 pub use dirstate::{
12 dirs_multiset::DirsMultiset,
12 dirs_multiset::{DirsMultiset, DirsMultisetIter},
13 13 dirstate_map::DirstateMap,
14 14 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
15 15 CopyMap, DirstateEntry, DirstateParents, EntryState, StateMap,
16 16 };
17 17 mod filepatterns;
18 18 pub mod utils;
19 19
20 20 pub use filepatterns::{
21 21 build_single_regex, read_pattern_file, PatternSyntax, PatternTuple,
22 22 };
23 23
24 24 /// Mercurial revision numbers
25 25 ///
26 26 /// As noted in revlog.c, revision numbers are actually encoded in
27 27 /// 4 bytes, and are liberally converted to ints, whence the i32
28 28 pub type Revision = i32;
29 29
30 30 /// Marker expressing the absence of a parent
31 31 ///
32 32 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
33 33 /// to be smaller that all existing revisions.
34 34 pub const NULL_REVISION: Revision = -1;
35 35
36 36 /// Same as `mercurial.node.wdirrev`
37 37 ///
38 38 /// This is also equal to `i32::max_value()`, but it's better to spell
39 39 /// it out explicitely, same as in `mercurial.node`
40 40 pub const WORKING_DIRECTORY_REVISION: Revision = 0x7fffffff;
41 41
42 42 /// The simplest expression of what we need of Mercurial DAGs.
43 43 pub trait Graph {
44 44 /// Return the two parents of the given `Revision`.
45 45 ///
46 46 /// Each of the parents can be independently `NULL_REVISION`
47 47 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError>;
48 48 }
49 49
50 50 pub type LineNumber = usize;
51 51
52 52 #[derive(Clone, Debug, PartialEq)]
53 53 pub enum GraphError {
54 54 ParentOutOfRange(Revision),
55 55 WorkingDirectoryUnsupported,
56 56 }
57 57
58 58 #[derive(Clone, Debug, PartialEq)]
59 59 pub enum DirstateParseError {
60 60 TooLittleData,
61 61 Overflow,
62 62 CorruptedEntry(String),
63 63 Damaged,
64 64 }
65 65
66 66 impl From<std::io::Error> for DirstateParseError {
67 67 fn from(e: std::io::Error) -> Self {
68 68 DirstateParseError::CorruptedEntry(e.to_string())
69 69 }
70 70 }
71 71
72 72 impl ToString for DirstateParseError {
73 73 fn to_string(&self) -> String {
74 74 use crate::DirstateParseError::*;
75 75 match self {
76 76 TooLittleData => "Too little data for dirstate.".to_string(),
77 77 Overflow => "Overflow in dirstate.".to_string(),
78 78 CorruptedEntry(e) => format!("Corrupted entry: {:?}.", e),
79 79 Damaged => "Dirstate appears to be damaged.".to_string(),
80 80 }
81 81 }
82 82 }
83 83
84 84 #[derive(Debug, PartialEq)]
85 85 pub enum DirstatePackError {
86 86 CorruptedEntry(String),
87 87 CorruptedParent,
88 88 BadSize(usize, usize),
89 89 }
90 90
91 91 impl From<std::io::Error> for DirstatePackError {
92 92 fn from(e: std::io::Error) -> Self {
93 93 DirstatePackError::CorruptedEntry(e.to_string())
94 94 }
95 95 }
96 96 #[derive(Debug, PartialEq)]
97 97 pub enum DirstateMapError {
98 98 PathNotFound(Vec<u8>),
99 99 EmptyPath,
100 100 }
101 101
102 102 pub enum DirstateError {
103 103 Parse(DirstateParseError),
104 104 Pack(DirstatePackError),
105 105 Map(DirstateMapError),
106 106 IO(std::io::Error),
107 107 }
108 108
109 109 impl From<DirstateParseError> for DirstateError {
110 110 fn from(e: DirstateParseError) -> Self {
111 111 DirstateError::Parse(e)
112 112 }
113 113 }
114 114
115 115 impl From<DirstatePackError> for DirstateError {
116 116 fn from(e: DirstatePackError) -> Self {
117 117 DirstateError::Pack(e)
118 118 }
119 119 }
120 120
121 121 #[derive(Debug)]
122 122 pub enum PatternError {
123 123 UnsupportedSyntax(String),
124 124 }
125 125
126 126 #[derive(Debug)]
127 127 pub enum PatternFileError {
128 128 IO(std::io::Error),
129 129 Pattern(PatternError, LineNumber),
130 130 }
131 131
132 132 impl From<std::io::Error> for PatternFileError {
133 133 fn from(e: std::io::Error) -> Self {
134 134 PatternFileError::IO(e)
135 135 }
136 136 }
137 137
138 138 impl From<DirstateMapError> for DirstateError {
139 139 fn from(e: DirstateMapError) -> Self {
140 140 DirstateError::Map(e)
141 141 }
142 142 }
143 143
144 144 impl From<std::io::Error> for DirstateError {
145 145 fn from(e: std::io::Error) -> Self {
146 146 DirstateError::IO(e)
147 147 }
148 148 }
General Comments 0
You need to be logged in to leave comments. Login now