##// END OF EJS Templates
rust-dirstate: remove too abstracted way of getting &[u8]
Yuya Nishihara -
r43063:8d2d5dfa default
parent child Browse files
Show More
@@ -1,435 +1,429 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},
10 10 pack_dirstate, parse_dirstate,
11 11 utils::copy_into_array,
12 12 CopyMap, DirsIterable, DirsMultiset, DirstateEntry, DirstateError,
13 13 DirstateMapError, DirstateParents, DirstateParseError, StateMap,
14 14 };
15 15 use core::borrow::Borrow;
16 16 use std::collections::{HashMap, HashSet};
17 17 use std::iter::FromIterator;
18 18 use std::ops::Deref;
19 19 use std::time::Duration;
20 20
21 21 pub type FileFoldMap = HashMap<Vec<u8>, Vec<u8>>;
22 22
23 23 const NULL_REVISION: [u8; 20] = [
24 24 b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0',
25 25 b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0',
26 26 ];
27 27 const MTIME_UNSET: i32 = -1;
28 28 const SIZE_DIRTY: i32 = -2;
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<Vec<u8>>,
38 38 other_parent_set: HashSet<Vec<u8>>,
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<(Vec<u8>, DirstateEntry)> for DirstateMap {
53 53 fn from_iter<I: IntoIterator<Item = (Vec<u8>, 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_REVISION,
76 76 p2: NULL_REVISION,
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: &[u8],
84 84 old_state: EntryState,
85 85 entry: DirstateEntry,
86 86 ) {
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_DIRTY {
105 105 self.other_parent_set.insert(filename.to_owned());
106 106 }
107 107 }
108 108
109 109 /// Mark a file as removed in the dirstate.
110 110 ///
111 111 /// The `size` parameter is used to store sentinel values that indicate
112 112 /// the file's previous state. In the future, we should refactor this
113 113 /// to be more explicit about what that state is.
114 114 pub fn remove_file(
115 115 &mut self,
116 116 filename: &[u8],
117 117 old_state: EntryState,
118 118 size: i32,
119 119 ) -> Result<(), DirstateMapError> {
120 120 if old_state != EntryState::Unknown && old_state != EntryState::Removed
121 121 {
122 122 if let Some(ref mut dirs) = self.dirs {
123 123 dirs.delete_path(filename)?;
124 124 }
125 125 }
126 126 if old_state == EntryState::Unknown {
127 127 if let Some(ref mut all_dirs) = self.all_dirs {
128 128 all_dirs.add_path(filename);
129 129 }
130 130 }
131 131
132 132 if let Some(ref mut file_fold_map) = self.file_fold_map {
133 file_fold_map
134 .remove::<Vec<u8>>(filename.to_ascii_uppercase().as_ref());
133 file_fold_map.remove(&filename.to_ascii_uppercase());
135 134 }
136 135 self.state_map.insert(
137 136 filename.to_owned(),
138 137 DirstateEntry {
139 138 state: EntryState::Removed,
140 139 mode: 0,
141 140 size,
142 141 mtime: 0,
143 142 },
144 143 );
145 144 self.non_normal_set.insert(filename.to_owned());
146 145 Ok(())
147 146 }
148 147
149 148 /// Remove a file from the dirstate.
150 149 /// Returns `true` if the file was previously recorded.
151 150 pub fn drop_file(
152 151 &mut self,
153 152 filename: &[u8],
154 153 old_state: EntryState,
155 154 ) -> Result<bool, DirstateMapError> {
156 let exists = self
157 .state_map
158 .remove::<Vec<u8>>(filename.to_owned().as_ref())
159 .is_some();
155 let exists = self.state_map.remove(filename).is_some();
160 156
161 157 if exists {
162 158 if old_state != EntryState::Removed {
163 159 if let Some(ref mut dirs) = self.dirs {
164 160 dirs.delete_path(filename)?;
165 161 }
166 162 }
167 163 if let Some(ref mut all_dirs) = self.all_dirs {
168 164 all_dirs.delete_path(filename)?;
169 165 }
170 166 }
171 167 if let Some(ref mut file_fold_map) = self.file_fold_map {
172 file_fold_map
173 .remove::<Vec<u8>>(filename.to_ascii_uppercase().as_ref());
168 file_fold_map.remove(&filename.to_ascii_uppercase());
174 169 }
175 self.non_normal_set
176 .remove::<Vec<u8>>(filename.to_owned().as_ref());
170 self.non_normal_set.remove(filename);
177 171
178 172 Ok(exists)
179 173 }
180 174
181 175 pub fn clear_ambiguous_times(
182 176 &mut self,
183 177 filenames: Vec<Vec<u8>>,
184 178 now: i32,
185 179 ) {
186 180 for filename in filenames {
187 181 let mut changed = false;
188 182 self.state_map
189 183 .entry(filename.to_owned())
190 184 .and_modify(|entry| {
191 185 if entry.state == EntryState::Normal && entry.mtime == now
192 186 {
193 187 changed = true;
194 188 *entry = DirstateEntry {
195 189 mtime: MTIME_UNSET,
196 190 ..*entry
197 191 };
198 192 }
199 193 });
200 194 if changed {
201 195 self.non_normal_set.insert(filename.to_owned());
202 196 }
203 197 }
204 198 }
205 199
206 200 pub fn non_normal_other_parent_entries(
207 201 &self,
208 202 ) -> (HashSet<Vec<u8>>, HashSet<Vec<u8>>) {
209 203 let mut non_normal = HashSet::new();
210 204 let mut other_parent = HashSet::new();
211 205
212 206 for (
213 207 filename,
214 208 DirstateEntry {
215 209 state, size, mtime, ..
216 210 },
217 211 ) in self.state_map.iter()
218 212 {
219 213 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
220 214 non_normal.insert(filename.to_owned());
221 215 }
222 216 if *state == EntryState::Normal && *size == SIZE_DIRTY {
223 217 other_parent.insert(filename.to_owned());
224 218 }
225 219 }
226 220
227 221 (non_normal, other_parent)
228 222 }
229 223
230 224 /// Both of these setters and their uses appear to be the simplest way to
231 225 /// emulate a Python lazy property, but it is ugly and unidiomatic.
232 226 /// TODO One day, rewriting this struct using the typestate might be a
233 227 /// good idea.
234 228 pub fn set_all_dirs(&mut self) {
235 229 if self.all_dirs.is_none() {
236 230 self.all_dirs = Some(DirsMultiset::new(
237 231 DirsIterable::Dirstate(&self.state_map),
238 232 None,
239 233 ));
240 234 }
241 235 }
242 236
243 237 pub fn set_dirs(&mut self) {
244 238 if self.dirs.is_none() {
245 239 self.dirs = Some(DirsMultiset::new(
246 240 DirsIterable::Dirstate(&self.state_map),
247 241 Some(EntryState::Removed),
248 242 ));
249 243 }
250 244 }
251 245
252 246 pub fn has_tracked_dir(&mut self, directory: &[u8]) -> bool {
253 247 self.set_dirs();
254 self.dirs.as_ref().unwrap().contains(directory.as_ref())
248 self.dirs.as_ref().unwrap().contains(directory)
255 249 }
256 250
257 251 pub fn has_dir(&mut self, directory: &[u8]) -> bool {
258 252 self.set_all_dirs();
259 self.all_dirs.as_ref().unwrap().contains(directory.as_ref())
253 self.all_dirs.as_ref().unwrap().contains(directory)
260 254 }
261 255
262 256 pub fn parents(
263 257 &mut self,
264 258 file_contents: &[u8],
265 259 ) -> Result<DirstateParents, DirstateError> {
266 260 if let Some(ref parents) = self.parents {
267 261 return Ok(parents.clone());
268 262 }
269 263 let parents;
270 264 if file_contents.len() == 40 {
271 265 parents = DirstateParents {
272 266 p1: copy_into_array(&file_contents[..PARENT_SIZE]),
273 267 p2: copy_into_array(
274 268 &file_contents[PARENT_SIZE..PARENT_SIZE * 2],
275 269 ),
276 270 };
277 271 } else if file_contents.is_empty() {
278 272 parents = DirstateParents {
279 273 p1: NULL_REVISION,
280 274 p2: NULL_REVISION,
281 275 };
282 276 } else {
283 277 return Err(DirstateError::Parse(DirstateParseError::Damaged));
284 278 }
285 279
286 280 self.parents = Some(parents.to_owned());
287 281 Ok(parents.clone())
288 282 }
289 283
290 284 pub fn set_parents(&mut self, parents: DirstateParents) {
291 285 self.parents = Some(parents.clone());
292 286 self.dirty_parents = true;
293 287 }
294 288
295 289 pub fn read(
296 290 &mut self,
297 291 file_contents: &[u8],
298 292 ) -> Result<Option<DirstateParents>, DirstateError> {
299 293 if file_contents.is_empty() {
300 294 return Ok(None);
301 295 }
302 296
303 297 let parents = parse_dirstate(
304 298 &mut self.state_map,
305 299 &mut self.copy_map,
306 300 file_contents,
307 301 )?;
308 302
309 303 if !self.dirty_parents {
310 304 self.set_parents(parents.to_owned());
311 305 }
312 306
313 307 Ok(Some(parents))
314 308 }
315 309
316 310 pub fn pack(
317 311 &mut self,
318 312 parents: DirstateParents,
319 313 now: Duration,
320 314 ) -> Result<Vec<u8>, DirstateError> {
321 315 let packed =
322 316 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
323 317
324 318 self.dirty_parents = false;
325 319
326 320 let result = self.non_normal_other_parent_entries();
327 321 self.non_normal_set = result.0;
328 322 self.other_parent_set = result.1;
329 323 Ok(packed)
330 324 }
331 325
332 326 pub fn build_file_fold_map(&mut self) -> FileFoldMap {
333 327 if let Some(ref file_fold_map) = self.file_fold_map {
334 328 return file_fold_map.to_owned();
335 329 }
336 330 let mut new_file_fold_map = FileFoldMap::new();
337 331 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
338 332 {
339 333 if *state == EntryState::Removed {
340 334 new_file_fold_map.insert(
341 335 filename.to_ascii_uppercase().to_owned(),
342 336 filename.to_owned(),
343 337 );
344 338 }
345 339 }
346 340 self.file_fold_map = Some(new_file_fold_map);
347 341 self.file_fold_map.to_owned().unwrap()
348 342 }
349 343 }
350 344
351 345 #[cfg(test)]
352 346 mod tests {
353 347 use super::*;
354 348
355 349 #[test]
356 350 fn test_dirs_multiset() {
357 351 let mut map = DirstateMap::new();
358 352 assert!(map.dirs.is_none());
359 353 assert!(map.all_dirs.is_none());
360 354
361 355 assert_eq!(false, map.has_dir(b"nope"));
362 356 assert!(map.all_dirs.is_some());
363 357 assert!(map.dirs.is_none());
364 358
365 359 assert_eq!(false, map.has_tracked_dir(b"nope"));
366 360 assert!(map.dirs.is_some());
367 361 }
368 362
369 363 #[test]
370 364 fn test_add_file() {
371 365 let mut map = DirstateMap::new();
372 366
373 367 assert_eq!(0, map.len());
374 368
375 369 map.add_file(
376 370 b"meh",
377 371 EntryState::Normal,
378 372 DirstateEntry {
379 373 state: EntryState::Normal,
380 374 mode: 1337,
381 375 mtime: 1337,
382 376 size: 1337,
383 377 },
384 378 );
385 379
386 380 assert_eq!(1, map.len());
387 381 assert_eq!(0, map.non_normal_set.len());
388 382 assert_eq!(0, map.other_parent_set.len());
389 383 }
390 384
391 385 #[test]
392 386 fn test_non_normal_other_parent_entries() {
393 387 let map: DirstateMap = [
394 388 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
395 389 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
396 390 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
397 391 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
398 392 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
399 393 (b"f6", (EntryState::Added, 1337, 1337, -1)),
400 394 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
401 395 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
402 396 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
403 397 (b"fa", (EntryState::Added, 1337, -2, 1337)),
404 398 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
405 399 ]
406 400 .iter()
407 401 .map(|(fname, (state, mode, size, mtime))| {
408 402 (
409 403 fname.to_vec(),
410 404 DirstateEntry {
411 405 state: *state,
412 406 mode: *mode,
413 407 size: *size,
414 408 mtime: *mtime,
415 409 },
416 410 )
417 411 })
418 412 .collect();
419 413
420 414 let non_normal = [
421 415 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
422 416 ]
423 417 .iter()
424 418 .map(|x| x.to_vec())
425 419 .collect();
426 420
427 421 let mut other_parent = HashSet::new();
428 422 other_parent.insert(b"f4".to_vec());
429 423
430 424 assert_eq!(
431 425 (non_normal, other_parent),
432 426 map.non_normal_other_parent_entries()
433 427 );
434 428 }
435 429 }
General Comments 0
You need to be logged in to leave comments. Login now