##// END OF EJS Templates
rust-warnings: fix warnings in tests...
Raphaël Gomès -
r44342:d8a96ceb default
parent child Browse files
Show More
@@ -1,346 +1,346 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::utils::hg_path::{HgPath, HgPathBuf};
12 12 use crate::{
13 13 dirstate::EntryState, utils::files, DirstateEntry, DirstateMapError,
14 14 FastHashMap,
15 15 };
16 16 use std::collections::hash_map::{self, Entry};
17 17
18 18 // could be encapsulated if we care API stability more seriously
19 19 pub type DirsMultisetIter<'a> = hash_map::Keys<'a, HgPathBuf, u32>;
20 20
21 21 #[derive(PartialEq, Debug)]
22 22 pub struct DirsMultiset {
23 23 inner: FastHashMap<HgPathBuf, u32>,
24 24 }
25 25
26 26 impl DirsMultiset {
27 27 /// Initializes the multiset from a dirstate.
28 28 ///
29 29 /// If `skip_state` is provided, skips dirstate entries with equal state.
30 30 pub fn from_dirstate(
31 31 dirstate: &FastHashMap<HgPathBuf, DirstateEntry>,
32 32 skip_state: Option<EntryState>,
33 33 ) -> Result<Self, DirstateMapError> {
34 34 let mut multiset = DirsMultiset {
35 35 inner: FastHashMap::default(),
36 36 };
37 37
38 38 for (filename, DirstateEntry { state, .. }) in dirstate {
39 39 // This `if` is optimized out of the loop
40 40 if let Some(skip) = skip_state {
41 41 if skip != *state {
42 42 multiset.add_path(filename)?;
43 43 }
44 44 } else {
45 45 multiset.add_path(filename)?;
46 46 }
47 47 }
48 48
49 49 Ok(multiset)
50 50 }
51 51
52 52 /// Initializes the multiset from a manifest.
53 53 pub fn from_manifest(
54 54 manifest: &[impl AsRef<HgPath>],
55 55 ) -> Result<Self, DirstateMapError> {
56 56 let mut multiset = DirsMultiset {
57 57 inner: FastHashMap::default(),
58 58 };
59 59
60 60 for filename in manifest {
61 61 multiset.add_path(filename.as_ref())?;
62 62 }
63 63
64 64 Ok(multiset)
65 65 }
66 66
67 67 /// Increases the count of deepest directory contained in the path.
68 68 ///
69 69 /// If the directory is not yet in the map, adds its parents.
70 70 pub fn add_path(
71 71 &mut self,
72 72 path: impl AsRef<HgPath>,
73 73 ) -> Result<(), DirstateMapError> {
74 74 for subpath in files::find_dirs(path.as_ref()) {
75 75 if subpath.as_bytes().last() == Some(&b'/') {
76 76 // TODO Remove this once PathAuditor is certified
77 77 // as the only entrypoint for path data
78 78 return Err(DirstateMapError::ConsecutiveSlashes);
79 79 }
80 80 if let Some(val) = self.inner.get_mut(subpath) {
81 81 *val += 1;
82 82 break;
83 83 }
84 84 self.inner.insert(subpath.to_owned(), 1);
85 85 }
86 86 Ok(())
87 87 }
88 88
89 89 /// Decreases the count of deepest directory contained in the path.
90 90 ///
91 91 /// If it is the only reference, decreases all parents until one is
92 92 /// removed.
93 93 /// If the directory is not in the map, something horrible has happened.
94 94 pub fn delete_path(
95 95 &mut self,
96 96 path: impl AsRef<HgPath>,
97 97 ) -> Result<(), DirstateMapError> {
98 98 for subpath in files::find_dirs(path.as_ref()) {
99 99 match self.inner.entry(subpath.to_owned()) {
100 100 Entry::Occupied(mut entry) => {
101 101 let val = entry.get().clone();
102 102 if val > 1 {
103 103 entry.insert(val - 1);
104 104 break;
105 105 }
106 106 entry.remove();
107 107 }
108 108 Entry::Vacant(_) => {
109 109 return Err(DirstateMapError::PathNotFound(
110 110 path.as_ref().to_owned(),
111 111 ))
112 112 }
113 113 };
114 114 }
115 115
116 116 Ok(())
117 117 }
118 118
119 119 pub fn contains(&self, key: impl AsRef<HgPath>) -> bool {
120 120 self.inner.contains_key(key.as_ref())
121 121 }
122 122
123 123 pub fn iter(&self) -> DirsMultisetIter {
124 124 self.inner.keys()
125 125 }
126 126
127 127 pub fn len(&self) -> usize {
128 128 self.inner.len()
129 129 }
130 130 }
131 131
132 132 #[cfg(test)]
133 133 mod tests {
134 134 use super::*;
135 135
136 136 #[test]
137 137 fn test_delete_path_path_not_found() {
138 138 let manifest: Vec<HgPathBuf> = vec![];
139 139 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
140 140 let path = HgPathBuf::from_bytes(b"doesnotexist/");
141 141 assert_eq!(
142 142 Err(DirstateMapError::PathNotFound(path.to_owned())),
143 143 map.delete_path(&path)
144 144 );
145 145 }
146 146
147 147 #[test]
148 148 fn test_delete_path_empty_path() {
149 149 let mut map =
150 150 DirsMultiset::from_manifest(&vec![HgPathBuf::new()]).unwrap();
151 151 let path = HgPath::new(b"");
152 152 assert_eq!(Ok(()), map.delete_path(path));
153 153 assert_eq!(
154 154 Err(DirstateMapError::PathNotFound(path.to_owned())),
155 155 map.delete_path(path)
156 156 );
157 157 }
158 158
159 159 #[test]
160 160 fn test_delete_path_successful() {
161 161 let mut map = DirsMultiset {
162 162 inner: [("", 5), ("a", 3), ("a/b", 2), ("a/c", 1)]
163 163 .iter()
164 164 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
165 165 .collect(),
166 166 };
167 167
168 168 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
169 169 eprintln!("{:?}", map);
170 170 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/b/")));
171 171 eprintln!("{:?}", map);
172 172 assert_eq!(
173 173 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
174 174 b"a/b/"
175 175 ))),
176 176 map.delete_path(HgPath::new(b"a/b/"))
177 177 );
178 178
179 179 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
180 180 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
181 181 eprintln!("{:?}", map);
182 182 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/")));
183 183 eprintln!("{:?}", map);
184 184
185 185 assert_eq!(Ok(()), map.delete_path(HgPath::new(b"a/c/")));
186 186 assert_eq!(
187 187 Err(DirstateMapError::PathNotFound(HgPathBuf::from_bytes(
188 188 b"a/c/"
189 189 ))),
190 190 map.delete_path(HgPath::new(b"a/c/"))
191 191 );
192 192 }
193 193
194 194 #[test]
195 195 fn test_add_path_empty_path() {
196 196 let manifest: Vec<HgPathBuf> = vec![];
197 197 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
198 198 let path = HgPath::new(b"");
199 map.add_path(path);
199 map.add_path(path).unwrap();
200 200
201 201 assert_eq!(1, map.len());
202 202 }
203 203
204 204 #[test]
205 205 fn test_add_path_successful() {
206 206 let manifest: Vec<HgPathBuf> = vec![];
207 207 let mut map = DirsMultiset::from_manifest(&manifest).unwrap();
208 208
209 map.add_path(HgPath::new(b"a/"));
209 map.add_path(HgPath::new(b"a/")).unwrap();
210 210 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
211 211 assert_eq!(1, *map.inner.get(HgPath::new(b"")).unwrap());
212 212 assert_eq!(2, map.len());
213 213
214 214 // Non directory should be ignored
215 map.add_path(HgPath::new(b"a"));
215 map.add_path(HgPath::new(b"a")).unwrap();
216 216 assert_eq!(1, *map.inner.get(HgPath::new(b"a")).unwrap());
217 217 assert_eq!(2, map.len());
218 218
219 219 // Non directory will still add its base
220 map.add_path(HgPath::new(b"a/b"));
220 map.add_path(HgPath::new(b"a/b")).unwrap();
221 221 assert_eq!(2, *map.inner.get(HgPath::new(b"a")).unwrap());
222 222 assert_eq!(2, map.len());
223 223
224 224 // Duplicate path works
225 map.add_path(HgPath::new(b"a/"));
225 map.add_path(HgPath::new(b"a/")).unwrap();
226 226 assert_eq!(3, *map.inner.get(HgPath::new(b"a")).unwrap());
227 227
228 228 // Nested dir adds to its base
229 map.add_path(HgPath::new(b"a/b/"));
229 map.add_path(HgPath::new(b"a/b/")).unwrap();
230 230 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
231 231 assert_eq!(1, *map.inner.get(HgPath::new(b"a/b")).unwrap());
232 232
233 233 // but not its base's base, because it already existed
234 map.add_path(HgPath::new(b"a/b/c/"));
234 map.add_path(HgPath::new(b"a/b/c/")).unwrap();
235 235 assert_eq!(4, *map.inner.get(HgPath::new(b"a")).unwrap());
236 236 assert_eq!(2, *map.inner.get(HgPath::new(b"a/b")).unwrap());
237 237
238 map.add_path(HgPath::new(b"a/c/"));
238 map.add_path(HgPath::new(b"a/c/")).unwrap();
239 239 assert_eq!(1, *map.inner.get(HgPath::new(b"a/c")).unwrap());
240 240
241 241 let expected = DirsMultiset {
242 242 inner: [("", 2), ("a", 5), ("a/b", 2), ("a/b/c", 1), ("a/c", 1)]
243 243 .iter()
244 244 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
245 245 .collect(),
246 246 };
247 247 assert_eq!(map, expected);
248 248 }
249 249
250 250 #[test]
251 251 fn test_dirsmultiset_new_empty() {
252 252 let manifest: Vec<HgPathBuf> = vec![];
253 253 let new = DirsMultiset::from_manifest(&manifest).unwrap();
254 254 let expected = DirsMultiset {
255 255 inner: FastHashMap::default(),
256 256 };
257 257 assert_eq!(expected, new);
258 258
259 259 let new = DirsMultiset::from_dirstate(&FastHashMap::default(), None)
260 260 .unwrap();
261 261 let expected = DirsMultiset {
262 262 inner: FastHashMap::default(),
263 263 };
264 264 assert_eq!(expected, new);
265 265 }
266 266
267 267 #[test]
268 268 fn test_dirsmultiset_new_no_skip() {
269 269 let input_vec: Vec<HgPathBuf> = ["a/", "b/", "a/c", "a/d/"]
270 270 .iter()
271 271 .map(|e| HgPathBuf::from_bytes(e.as_bytes()))
272 272 .collect();
273 273 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
274 274 .iter()
275 275 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
276 276 .collect();
277 277
278 278 let new = DirsMultiset::from_manifest(&input_vec).unwrap();
279 279 let expected = DirsMultiset {
280 280 inner: expected_inner,
281 281 };
282 282 assert_eq!(expected, new);
283 283
284 284 let input_map = ["a/", "b/", "a/c", "a/d/"]
285 285 .iter()
286 286 .map(|f| {
287 287 (
288 288 HgPathBuf::from_bytes(f.as_bytes()),
289 289 DirstateEntry {
290 290 state: EntryState::Normal,
291 291 mode: 0,
292 292 mtime: 0,
293 293 size: 0,
294 294 },
295 295 )
296 296 })
297 297 .collect();
298 298 let expected_inner = [("", 2), ("a", 3), ("b", 1), ("a/d", 1)]
299 299 .iter()
300 300 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
301 301 .collect();
302 302
303 303 let new = DirsMultiset::from_dirstate(&input_map, None).unwrap();
304 304 let expected = DirsMultiset {
305 305 inner: expected_inner,
306 306 };
307 307 assert_eq!(expected, new);
308 308 }
309 309
310 310 #[test]
311 311 fn test_dirsmultiset_new_skip() {
312 312 let input_map = [
313 313 ("a/", EntryState::Normal),
314 314 ("a/b/", EntryState::Normal),
315 315 ("a/c", EntryState::Removed),
316 316 ("a/d/", EntryState::Merged),
317 317 ]
318 318 .iter()
319 319 .map(|(f, state)| {
320 320 (
321 321 HgPathBuf::from_bytes(f.as_bytes()),
322 322 DirstateEntry {
323 323 state: *state,
324 324 mode: 0,
325 325 mtime: 0,
326 326 size: 0,
327 327 },
328 328 )
329 329 })
330 330 .collect();
331 331
332 332 // "a" incremented with "a/c" and "a/d/"
333 333 let expected_inner = [("", 1), ("a", 2), ("a/d", 1)]
334 334 .iter()
335 335 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
336 336 .collect();
337 337
338 338 let new =
339 339 DirsMultiset::from_dirstate(&input_map, Some(EntryState::Normal))
340 340 .unwrap();
341 341 let expected = DirsMultiset {
342 342 inner: expected_inner,
343 343 };
344 344 assert_eq!(expected, new);
345 345 }
346 346 }
@@ -1,435 +1,436 b''
1 1 // dirstate_map.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 use crate::{
9 9 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
10 10 pack_dirstate, parse_dirstate,
11 11 utils::{
12 12 files::normalize_case,
13 13 hg_path::{HgPath, HgPathBuf},
14 14 },
15 15 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
16 16 DirstateParents, DirstateParseError, FastHashMap, StateMap,
17 17 };
18 18 use core::borrow::Borrow;
19 19 use std::collections::HashSet;
20 20 use std::convert::TryInto;
21 21 use std::iter::FromIterator;
22 22 use std::ops::Deref;
23 23 use std::time::Duration;
24 24
25 25 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
26 26
27 27 const NULL_ID: [u8; 20] = [0; 20];
28 28 const MTIME_UNSET: i32 = -1;
29 29
30 30 #[derive(Default)]
31 31 pub struct DirstateMap {
32 32 state_map: StateMap,
33 33 pub copy_map: CopyMap,
34 34 file_fold_map: Option<FileFoldMap>,
35 35 pub dirs: Option<DirsMultiset>,
36 36 pub all_dirs: Option<DirsMultiset>,
37 37 non_normal_set: HashSet<HgPathBuf>,
38 38 other_parent_set: HashSet<HgPathBuf>,
39 39 parents: Option<DirstateParents>,
40 40 dirty_parents: bool,
41 41 }
42 42
43 43 /// Should only really be used in python interface code, for clarity
44 44 impl Deref for DirstateMap {
45 45 type Target = StateMap;
46 46
47 47 fn deref(&self) -> &Self::Target {
48 48 &self.state_map
49 49 }
50 50 }
51 51
52 52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 54 iter: I,
55 55 ) -> Self {
56 56 Self {
57 57 state_map: iter.into_iter().collect(),
58 58 ..Self::default()
59 59 }
60 60 }
61 61 }
62 62
63 63 impl DirstateMap {
64 64 pub fn new() -> Self {
65 65 Self::default()
66 66 }
67 67
68 68 pub fn clear(&mut self) {
69 69 self.state_map.clear();
70 70 self.copy_map.clear();
71 71 self.file_fold_map = None;
72 72 self.non_normal_set.clear();
73 73 self.other_parent_set.clear();
74 74 self.set_parents(&DirstateParents {
75 75 p1: NULL_ID,
76 76 p2: NULL_ID,
77 77 })
78 78 }
79 79
80 80 /// Add a tracked file to the dirstate
81 81 pub fn add_file(
82 82 &mut self,
83 83 filename: &HgPath,
84 84 old_state: EntryState,
85 85 entry: DirstateEntry,
86 86 ) -> Result<(), DirstateMapError> {
87 87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 88 {
89 89 if let Some(ref mut dirs) = self.dirs {
90 90 dirs.add_path(filename)?;
91 91 }
92 92 }
93 93 if old_state == EntryState::Unknown {
94 94 if let Some(ref mut all_dirs) = self.all_dirs {
95 95 all_dirs.add_path(filename)?;
96 96 }
97 97 }
98 98 self.state_map.insert(filename.to_owned(), entry.to_owned());
99 99
100 100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 101 self.non_normal_set.insert(filename.to_owned());
102 102 }
103 103
104 104 if entry.size == SIZE_FROM_OTHER_PARENT {
105 105 self.other_parent_set.insert(filename.to_owned());
106 106 }
107 107 Ok(())
108 108 }
109 109
110 110 /// Mark a file as removed in the dirstate.
111 111 ///
112 112 /// The `size` parameter is used to store sentinel values that indicate
113 113 /// the file's previous state. In the future, we should refactor this
114 114 /// to be more explicit about what that state is.
115 115 pub fn remove_file(
116 116 &mut self,
117 117 filename: &HgPath,
118 118 old_state: EntryState,
119 119 size: i32,
120 120 ) -> Result<(), DirstateMapError> {
121 121 if old_state != EntryState::Unknown && old_state != EntryState::Removed
122 122 {
123 123 if let Some(ref mut dirs) = self.dirs {
124 124 dirs.delete_path(filename)?;
125 125 }
126 126 }
127 127 if old_state == EntryState::Unknown {
128 128 if let Some(ref mut all_dirs) = self.all_dirs {
129 129 all_dirs.add_path(filename)?;
130 130 }
131 131 }
132 132
133 133 if let Some(ref mut file_fold_map) = self.file_fold_map {
134 134 file_fold_map.remove(&normalize_case(filename));
135 135 }
136 136 self.state_map.insert(
137 137 filename.to_owned(),
138 138 DirstateEntry {
139 139 state: EntryState::Removed,
140 140 mode: 0,
141 141 size,
142 142 mtime: 0,
143 143 },
144 144 );
145 145 self.non_normal_set.insert(filename.to_owned());
146 146 Ok(())
147 147 }
148 148
149 149 /// Remove a file from the dirstate.
150 150 /// Returns `true` if the file was previously recorded.
151 151 pub fn drop_file(
152 152 &mut self,
153 153 filename: &HgPath,
154 154 old_state: EntryState,
155 155 ) -> Result<bool, DirstateMapError> {
156 156 let exists = self.state_map.remove(filename).is_some();
157 157
158 158 if exists {
159 159 if old_state != EntryState::Removed {
160 160 if let Some(ref mut dirs) = self.dirs {
161 161 dirs.delete_path(filename)?;
162 162 }
163 163 }
164 164 if let Some(ref mut all_dirs) = self.all_dirs {
165 165 all_dirs.delete_path(filename)?;
166 166 }
167 167 }
168 168 if let Some(ref mut file_fold_map) = self.file_fold_map {
169 169 file_fold_map.remove(&normalize_case(filename));
170 170 }
171 171 self.non_normal_set.remove(filename);
172 172
173 173 Ok(exists)
174 174 }
175 175
176 176 pub fn clear_ambiguous_times(
177 177 &mut self,
178 178 filenames: Vec<HgPathBuf>,
179 179 now: i32,
180 180 ) {
181 181 for filename in filenames {
182 182 let mut changed = false;
183 183 self.state_map
184 184 .entry(filename.to_owned())
185 185 .and_modify(|entry| {
186 186 if entry.state == EntryState::Normal && entry.mtime == now
187 187 {
188 188 changed = true;
189 189 *entry = DirstateEntry {
190 190 mtime: MTIME_UNSET,
191 191 ..*entry
192 192 };
193 193 }
194 194 });
195 195 if changed {
196 196 self.non_normal_set.insert(filename.to_owned());
197 197 }
198 198 }
199 199 }
200 200
201 201 pub fn non_normal_other_parent_entries(
202 202 &self,
203 203 ) -> (HashSet<HgPathBuf>, HashSet<HgPathBuf>) {
204 204 let mut non_normal = HashSet::new();
205 205 let mut other_parent = HashSet::new();
206 206
207 207 for (
208 208 filename,
209 209 DirstateEntry {
210 210 state, size, mtime, ..
211 211 },
212 212 ) in self.state_map.iter()
213 213 {
214 214 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
215 215 non_normal.insert(filename.to_owned());
216 216 }
217 217 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
218 218 {
219 219 other_parent.insert(filename.to_owned());
220 220 }
221 221 }
222 222
223 223 (non_normal, other_parent)
224 224 }
225 225
226 226 /// Both of these setters and their uses appear to be the simplest way to
227 227 /// emulate a Python lazy property, but it is ugly and unidiomatic.
228 228 /// TODO One day, rewriting this struct using the typestate might be a
229 229 /// good idea.
230 230 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
231 231 if self.all_dirs.is_none() {
232 232 self.all_dirs =
233 233 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
234 234 }
235 235 Ok(())
236 236 }
237 237
238 238 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
239 239 if self.dirs.is_none() {
240 240 self.dirs = Some(DirsMultiset::from_dirstate(
241 241 &self.state_map,
242 242 Some(EntryState::Removed),
243 243 )?);
244 244 }
245 245 Ok(())
246 246 }
247 247
248 248 pub fn has_tracked_dir(
249 249 &mut self,
250 250 directory: &HgPath,
251 251 ) -> Result<bool, DirstateMapError> {
252 252 self.set_dirs()?;
253 253 Ok(self.dirs.as_ref().unwrap().contains(directory))
254 254 }
255 255
256 256 pub fn has_dir(
257 257 &mut self,
258 258 directory: &HgPath,
259 259 ) -> Result<bool, DirstateMapError> {
260 260 self.set_all_dirs()?;
261 261 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
262 262 }
263 263
264 264 pub fn parents(
265 265 &mut self,
266 266 file_contents: &[u8],
267 267 ) -> Result<&DirstateParents, DirstateError> {
268 268 if let Some(ref parents) = self.parents {
269 269 return Ok(parents);
270 270 }
271 271 let parents;
272 272 if file_contents.len() == PARENT_SIZE * 2 {
273 273 parents = DirstateParents {
274 274 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
275 275 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
276 276 .try_into()
277 277 .unwrap(),
278 278 };
279 279 } else if file_contents.is_empty() {
280 280 parents = DirstateParents {
281 281 p1: NULL_ID,
282 282 p2: NULL_ID,
283 283 };
284 284 } else {
285 285 return Err(DirstateError::Parse(DirstateParseError::Damaged));
286 286 }
287 287
288 288 self.parents = Some(parents);
289 289 Ok(self.parents.as_ref().unwrap())
290 290 }
291 291
292 292 pub fn set_parents(&mut self, parents: &DirstateParents) {
293 293 self.parents = Some(parents.clone());
294 294 self.dirty_parents = true;
295 295 }
296 296
297 297 pub fn read(
298 298 &mut self,
299 299 file_contents: &[u8],
300 300 ) -> Result<Option<DirstateParents>, DirstateError> {
301 301 if file_contents.is_empty() {
302 302 return Ok(None);
303 303 }
304 304
305 305 let parents = parse_dirstate(
306 306 &mut self.state_map,
307 307 &mut self.copy_map,
308 308 file_contents,
309 309 )?;
310 310
311 311 if !self.dirty_parents {
312 312 self.set_parents(&parents);
313 313 }
314 314
315 315 Ok(Some(parents))
316 316 }
317 317
318 318 pub fn pack(
319 319 &mut self,
320 320 parents: DirstateParents,
321 321 now: Duration,
322 322 ) -> Result<Vec<u8>, DirstateError> {
323 323 let packed =
324 324 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
325 325
326 326 self.dirty_parents = false;
327 327
328 328 let result = self.non_normal_other_parent_entries();
329 329 self.non_normal_set = result.0;
330 330 self.other_parent_set = result.1;
331 331 Ok(packed)
332 332 }
333 333
334 334 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
335 335 if let Some(ref file_fold_map) = self.file_fold_map {
336 336 return file_fold_map;
337 337 }
338 338 let mut new_file_fold_map = FileFoldMap::default();
339 339 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
340 340 {
341 341 if *state == EntryState::Removed {
342 342 new_file_fold_map
343 343 .insert(normalize_case(filename), filename.to_owned());
344 344 }
345 345 }
346 346 self.file_fold_map = Some(new_file_fold_map);
347 347 self.file_fold_map.as_ref().unwrap()
348 348 }
349 349 }
350 350
351 351 #[cfg(test)]
352 352 mod tests {
353 353 use super::*;
354 354
355 355 #[test]
356 356 fn test_dirs_multiset() {
357 357 let mut map = DirstateMap::new();
358 358 assert!(map.dirs.is_none());
359 359 assert!(map.all_dirs.is_none());
360 360
361 361 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
362 362 assert!(map.all_dirs.is_some());
363 363 assert!(map.dirs.is_none());
364 364
365 365 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
366 366 assert!(map.dirs.is_some());
367 367 }
368 368
369 369 #[test]
370 370 fn test_add_file() {
371 371 let mut map = DirstateMap::new();
372 372
373 373 assert_eq!(0, map.len());
374 374
375 375 map.add_file(
376 376 HgPath::new(b"meh"),
377 377 EntryState::Normal,
378 378 DirstateEntry {
379 379 state: EntryState::Normal,
380 380 mode: 1337,
381 381 mtime: 1337,
382 382 size: 1337,
383 383 },
384 );
384 )
385 .unwrap();
385 386
386 387 assert_eq!(1, map.len());
387 388 assert_eq!(0, map.non_normal_set.len());
388 389 assert_eq!(0, map.other_parent_set.len());
389 390 }
390 391
391 392 #[test]
392 393 fn test_non_normal_other_parent_entries() {
393 394 let map: DirstateMap = [
394 395 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
395 396 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
396 397 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
397 398 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
398 399 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
399 400 (b"f6", (EntryState::Added, 1337, 1337, -1)),
400 401 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
401 402 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
402 403 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
403 404 (b"fa", (EntryState::Added, 1337, -2, 1337)),
404 405 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
405 406 ]
406 407 .iter()
407 408 .map(|(fname, (state, mode, size, mtime))| {
408 409 (
409 410 HgPathBuf::from_bytes(fname.as_ref()),
410 411 DirstateEntry {
411 412 state: *state,
412 413 mode: *mode,
413 414 size: *size,
414 415 mtime: *mtime,
415 416 },
416 417 )
417 418 })
418 419 .collect();
419 420
420 421 let non_normal = [
421 422 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
422 423 ]
423 424 .iter()
424 425 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
425 426 .collect();
426 427
427 428 let mut other_parent = HashSet::new();
428 429 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
429 430
430 431 assert_eq!(
431 432 (non_normal, other_parent),
432 433 map.non_normal_other_parent_entries()
433 434 );
434 435 }
435 436 }
General Comments 0
You need to be logged in to leave comments. Login now