##// END OF EJS Templates
rust: Add a Timestamp struct instead of abusing Duration...
Simon Sapin -
r47871:5d62243c default
parent child Browse files
Show More
@@ -1,505 +1,505
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 use crate::dirstate::parsers::Timestamp;
8 9 use crate::errors::HgError;
9 10 use crate::revlog::node::NULL_NODE;
10 11 use crate::{
11 12 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
12 13 pack_dirstate, parse_dirstate,
13 14 utils::{
14 15 files::normalize_case,
15 16 hg_path::{HgPath, HgPathBuf},
16 17 },
17 18 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
18 19 DirstateParents, FastHashMap, StateMap,
19 20 };
20 21 use micro_timer::timed;
21 22 use std::collections::HashSet;
22 23 use std::convert::TryInto;
23 24 use std::iter::FromIterator;
24 25 use std::ops::Deref;
25 use std::time::Duration;
26 26
27 27 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
28 28
29 29 const MTIME_UNSET: i32 = -1;
30 30
31 31 #[derive(Default)]
32 32 pub struct DirstateMap {
33 33 state_map: StateMap,
34 34 pub copy_map: CopyMap,
35 35 file_fold_map: Option<FileFoldMap>,
36 36 pub dirs: Option<DirsMultiset>,
37 37 pub all_dirs: Option<DirsMultiset>,
38 38 non_normal_set: Option<HashSet<HgPathBuf>>,
39 39 other_parent_set: Option<HashSet<HgPathBuf>>,
40 40 parents: Option<DirstateParents>,
41 41 dirty_parents: bool,
42 42 }
43 43
44 44 /// Should only really be used in python interface code, for clarity
45 45 impl Deref for DirstateMap {
46 46 type Target = StateMap;
47 47
48 48 fn deref(&self) -> &Self::Target {
49 49 &self.state_map
50 50 }
51 51 }
52 52
53 53 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
54 54 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
55 55 iter: I,
56 56 ) -> Self {
57 57 Self {
58 58 state_map: iter.into_iter().collect(),
59 59 ..Self::default()
60 60 }
61 61 }
62 62 }
63 63
64 64 impl DirstateMap {
65 65 pub fn new() -> Self {
66 66 Self::default()
67 67 }
68 68
69 69 pub fn clear(&mut self) {
70 70 self.state_map = StateMap::default();
71 71 self.copy_map.clear();
72 72 self.file_fold_map = None;
73 73 self.non_normal_set = None;
74 74 self.other_parent_set = None;
75 75 self.set_parents(&DirstateParents {
76 76 p1: NULL_NODE,
77 77 p2: NULL_NODE,
78 78 })
79 79 }
80 80
81 81 /// Add a tracked file to the dirstate
82 82 pub fn add_file(
83 83 &mut self,
84 84 filename: &HgPath,
85 85 old_state: EntryState,
86 86 entry: DirstateEntry,
87 87 ) -> Result<(), DirstateMapError> {
88 88 if old_state == EntryState::Unknown || old_state == EntryState::Removed
89 89 {
90 90 if let Some(ref mut dirs) = self.dirs {
91 91 dirs.add_path(filename)?;
92 92 }
93 93 }
94 94 if old_state == EntryState::Unknown {
95 95 if let Some(ref mut all_dirs) = self.all_dirs {
96 96 all_dirs.add_path(filename)?;
97 97 }
98 98 }
99 99 self.state_map.insert(filename.to_owned(), entry.to_owned());
100 100
101 101 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
102 102 self.get_non_normal_other_parent_entries()
103 103 .0
104 104 .insert(filename.to_owned());
105 105 }
106 106
107 107 if entry.size == SIZE_FROM_OTHER_PARENT {
108 108 self.get_non_normal_other_parent_entries()
109 109 .1
110 110 .insert(filename.to_owned());
111 111 }
112 112 Ok(())
113 113 }
114 114
115 115 /// Mark a file as removed in the dirstate.
116 116 ///
117 117 /// The `size` parameter is used to store sentinel values that indicate
118 118 /// the file's previous state. In the future, we should refactor this
119 119 /// to be more explicit about what that state is.
120 120 pub fn remove_file(
121 121 &mut self,
122 122 filename: &HgPath,
123 123 old_state: EntryState,
124 124 size: i32,
125 125 ) -> Result<(), DirstateMapError> {
126 126 if old_state != EntryState::Unknown && old_state != EntryState::Removed
127 127 {
128 128 if let Some(ref mut dirs) = self.dirs {
129 129 dirs.delete_path(filename)?;
130 130 }
131 131 }
132 132 if old_state == EntryState::Unknown {
133 133 if let Some(ref mut all_dirs) = self.all_dirs {
134 134 all_dirs.add_path(filename)?;
135 135 }
136 136 }
137 137
138 138 if let Some(ref mut file_fold_map) = self.file_fold_map {
139 139 file_fold_map.remove(&normalize_case(filename));
140 140 }
141 141 self.state_map.insert(
142 142 filename.to_owned(),
143 143 DirstateEntry {
144 144 state: EntryState::Removed,
145 145 mode: 0,
146 146 size,
147 147 mtime: 0,
148 148 },
149 149 );
150 150 self.get_non_normal_other_parent_entries()
151 151 .0
152 152 .insert(filename.to_owned());
153 153 Ok(())
154 154 }
155 155
156 156 /// Remove a file from the dirstate.
157 157 /// Returns `true` if the file was previously recorded.
158 158 pub fn drop_file(
159 159 &mut self,
160 160 filename: &HgPath,
161 161 old_state: EntryState,
162 162 ) -> Result<bool, DirstateMapError> {
163 163 let exists = self.state_map.remove(filename).is_some();
164 164
165 165 if exists {
166 166 if old_state != EntryState::Removed {
167 167 if let Some(ref mut dirs) = self.dirs {
168 168 dirs.delete_path(filename)?;
169 169 }
170 170 }
171 171 if let Some(ref mut all_dirs) = self.all_dirs {
172 172 all_dirs.delete_path(filename)?;
173 173 }
174 174 }
175 175 if let Some(ref mut file_fold_map) = self.file_fold_map {
176 176 file_fold_map.remove(&normalize_case(filename));
177 177 }
178 178 self.get_non_normal_other_parent_entries()
179 179 .0
180 180 .remove(filename);
181 181
182 182 Ok(exists)
183 183 }
184 184
185 185 pub fn clear_ambiguous_times(
186 186 &mut self,
187 187 filenames: Vec<HgPathBuf>,
188 188 now: i32,
189 189 ) {
190 190 for filename in filenames {
191 191 let mut changed = false;
192 192 if let Some(entry) = self.state_map.get_mut(&filename) {
193 193 if entry.state == EntryState::Normal && entry.mtime == now {
194 194 changed = true;
195 195 *entry = DirstateEntry {
196 196 mtime: MTIME_UNSET,
197 197 ..*entry
198 198 };
199 199 }
200 200 }
201 201 if changed {
202 202 self.get_non_normal_other_parent_entries()
203 203 .0
204 204 .insert(filename.to_owned());
205 205 }
206 206 }
207 207 }
208 208
209 209 pub fn non_normal_entries_remove(
210 210 &mut self,
211 211 key: impl AsRef<HgPath>,
212 212 ) -> bool {
213 213 self.get_non_normal_other_parent_entries()
214 214 .0
215 215 .remove(key.as_ref())
216 216 }
217 217 pub fn non_normal_entries_union(
218 218 &mut self,
219 219 other: HashSet<HgPathBuf>,
220 220 ) -> Vec<HgPathBuf> {
221 221 self.get_non_normal_other_parent_entries()
222 222 .0
223 223 .union(&other)
224 224 .map(ToOwned::to_owned)
225 225 .collect()
226 226 }
227 227
228 228 pub fn get_non_normal_other_parent_entries(
229 229 &mut self,
230 230 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
231 231 self.set_non_normal_other_parent_entries(false);
232 232 (
233 233 self.non_normal_set.as_mut().unwrap(),
234 234 self.other_parent_set.as_mut().unwrap(),
235 235 )
236 236 }
237 237
238 238 /// Useful to get immutable references to those sets in contexts where
239 239 /// you only have an immutable reference to the `DirstateMap`, like when
240 240 /// sharing references with Python.
241 241 ///
242 242 /// TODO, get rid of this along with the other "setter/getter" stuff when
243 243 /// a nice typestate plan is defined.
244 244 ///
245 245 /// # Panics
246 246 ///
247 247 /// Will panic if either set is `None`.
248 248 pub fn get_non_normal_other_parent_entries_panic(
249 249 &self,
250 250 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
251 251 (
252 252 self.non_normal_set.as_ref().unwrap(),
253 253 self.other_parent_set.as_ref().unwrap(),
254 254 )
255 255 }
256 256
257 257 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
258 258 if !force
259 259 && self.non_normal_set.is_some()
260 260 && self.other_parent_set.is_some()
261 261 {
262 262 return;
263 263 }
264 264 let mut non_normal = HashSet::new();
265 265 let mut other_parent = HashSet::new();
266 266
267 267 for (
268 268 filename,
269 269 DirstateEntry {
270 270 state, size, mtime, ..
271 271 },
272 272 ) in self.state_map.iter()
273 273 {
274 274 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
275 275 non_normal.insert(filename.to_owned());
276 276 }
277 277 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
278 278 {
279 279 other_parent.insert(filename.to_owned());
280 280 }
281 281 }
282 282 self.non_normal_set = Some(non_normal);
283 283 self.other_parent_set = Some(other_parent);
284 284 }
285 285
286 286 /// Both of these setters and their uses appear to be the simplest way to
287 287 /// emulate a Python lazy property, but it is ugly and unidiomatic.
288 288 /// TODO One day, rewriting this struct using the typestate might be a
289 289 /// good idea.
290 290 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
291 291 if self.all_dirs.is_none() {
292 292 self.all_dirs = Some(DirsMultiset::from_dirstate(
293 293 self.state_map.iter(),
294 294 None,
295 295 )?);
296 296 }
297 297 Ok(())
298 298 }
299 299
300 300 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
301 301 if self.dirs.is_none() {
302 302 self.dirs = Some(DirsMultiset::from_dirstate(
303 303 &self.state_map,
304 304 Some(EntryState::Removed),
305 305 )?);
306 306 }
307 307 Ok(())
308 308 }
309 309
310 310 pub fn has_tracked_dir(
311 311 &mut self,
312 312 directory: &HgPath,
313 313 ) -> Result<bool, DirstateMapError> {
314 314 self.set_dirs()?;
315 315 Ok(self.dirs.as_ref().unwrap().contains(directory))
316 316 }
317 317
318 318 pub fn has_dir(
319 319 &mut self,
320 320 directory: &HgPath,
321 321 ) -> Result<bool, DirstateMapError> {
322 322 self.set_all_dirs()?;
323 323 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
324 324 }
325 325
326 326 pub fn parents(
327 327 &mut self,
328 328 file_contents: &[u8],
329 329 ) -> Result<&DirstateParents, DirstateError> {
330 330 if let Some(ref parents) = self.parents {
331 331 return Ok(parents);
332 332 }
333 333 let parents;
334 334 if file_contents.len() == PARENT_SIZE * 2 {
335 335 parents = DirstateParents {
336 336 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
337 337 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
338 338 .try_into()
339 339 .unwrap(),
340 340 };
341 341 } else if file_contents.is_empty() {
342 342 parents = DirstateParents {
343 343 p1: NULL_NODE,
344 344 p2: NULL_NODE,
345 345 };
346 346 } else {
347 347 return Err(
348 348 HgError::corrupted("Dirstate appears to be damaged").into()
349 349 );
350 350 }
351 351
352 352 self.parents = Some(parents);
353 353 Ok(self.parents.as_ref().unwrap())
354 354 }
355 355
356 356 pub fn set_parents(&mut self, parents: &DirstateParents) {
357 357 self.parents = Some(parents.clone());
358 358 self.dirty_parents = true;
359 359 }
360 360
361 361 #[timed]
362 362 pub fn read<'a>(
363 363 &mut self,
364 364 file_contents: &'a [u8],
365 365 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
366 366 if file_contents.is_empty() {
367 367 return Ok(None);
368 368 }
369 369
370 370 let (parents, entries, copies) = parse_dirstate(file_contents)?;
371 371 self.state_map.extend(
372 372 entries
373 373 .into_iter()
374 374 .map(|(path, entry)| (path.to_owned(), entry)),
375 375 );
376 376 self.copy_map.extend(
377 377 copies
378 378 .into_iter()
379 379 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
380 380 );
381 381
382 382 if !self.dirty_parents {
383 383 self.set_parents(&parents);
384 384 }
385 385
386 386 Ok(Some(parents))
387 387 }
388 388
389 389 pub fn pack(
390 390 &mut self,
391 391 parents: DirstateParents,
392 now: Duration,
392 now: Timestamp,
393 393 ) -> Result<Vec<u8>, DirstateError> {
394 394 let packed =
395 395 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
396 396
397 397 self.dirty_parents = false;
398 398
399 399 self.set_non_normal_other_parent_entries(true);
400 400 Ok(packed)
401 401 }
402 402 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
403 403 if let Some(ref file_fold_map) = self.file_fold_map {
404 404 return file_fold_map;
405 405 }
406 406 let mut new_file_fold_map = FileFoldMap::default();
407 407
408 408 for (filename, DirstateEntry { state, .. }) in self.state_map.iter() {
409 409 if *state != EntryState::Removed {
410 410 new_file_fold_map
411 411 .insert(normalize_case(&filename), filename.to_owned());
412 412 }
413 413 }
414 414 self.file_fold_map = Some(new_file_fold_map);
415 415 self.file_fold_map.as_ref().unwrap()
416 416 }
417 417 }
418 418
419 419 #[cfg(test)]
420 420 mod tests {
421 421 use super::*;
422 422
423 423 #[test]
424 424 fn test_dirs_multiset() {
425 425 let mut map = DirstateMap::new();
426 426 assert!(map.dirs.is_none());
427 427 assert!(map.all_dirs.is_none());
428 428
429 429 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
430 430 assert!(map.all_dirs.is_some());
431 431 assert!(map.dirs.is_none());
432 432
433 433 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
434 434 assert!(map.dirs.is_some());
435 435 }
436 436
437 437 #[test]
438 438 fn test_add_file() {
439 439 let mut map = DirstateMap::new();
440 440
441 441 assert_eq!(0, map.len());
442 442
443 443 map.add_file(
444 444 HgPath::new(b"meh"),
445 445 EntryState::Normal,
446 446 DirstateEntry {
447 447 state: EntryState::Normal,
448 448 mode: 1337,
449 449 mtime: 1337,
450 450 size: 1337,
451 451 },
452 452 )
453 453 .unwrap();
454 454
455 455 assert_eq!(1, map.len());
456 456 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
457 457 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
458 458 }
459 459
460 460 #[test]
461 461 fn test_non_normal_other_parent_entries() {
462 462 let mut map: DirstateMap = [
463 463 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
464 464 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
465 465 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
466 466 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
467 467 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
468 468 (b"f6", (EntryState::Added, 1337, 1337, -1)),
469 469 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
470 470 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
471 471 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
472 472 (b"fa", (EntryState::Added, 1337, -2, 1337)),
473 473 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
474 474 ]
475 475 .iter()
476 476 .map(|(fname, (state, mode, size, mtime))| {
477 477 (
478 478 HgPathBuf::from_bytes(fname.as_ref()),
479 479 DirstateEntry {
480 480 state: *state,
481 481 mode: *mode,
482 482 size: *size,
483 483 mtime: *mtime,
484 484 },
485 485 )
486 486 })
487 487 .collect();
488 488
489 489 let mut non_normal = [
490 490 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
491 491 ]
492 492 .iter()
493 493 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
494 494 .collect();
495 495
496 496 let mut other_parent = HashSet::new();
497 497 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
498 498 let entries = map.get_non_normal_other_parent_entries();
499 499
500 500 assert_eq!(
501 501 (&mut non_normal, &mut other_parent),
502 502 (entries.0, entries.1)
503 503 );
504 504 }
505 505 }
@@ -1,445 +1,446
1 1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
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
6 6 use crate::errors::HgError;
7 7 use crate::utils::hg_path::HgPath;
8 8 use crate::{
9 9 dirstate::{CopyMap, EntryState, RawEntry, StateMap},
10 10 DirstateEntry, DirstateParents,
11 11 };
12 12 use byteorder::{BigEndian, WriteBytesExt};
13 13 use bytes_cast::BytesCast;
14 14 use micro_timer::timed;
15 15 use std::convert::{TryFrom, TryInto};
16 use std::time::Duration;
17 16
18 17 /// Parents are stored in the dirstate as byte hashes.
19 18 pub const PARENT_SIZE: usize = 20;
20 19 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
21 20 const MIN_ENTRY_SIZE: usize = 17;
22 21
23 22 type ParseResult<'a> = (
24 23 &'a DirstateParents,
25 24 Vec<(&'a HgPath, DirstateEntry)>,
26 25 Vec<(&'a HgPath, &'a HgPath)>,
27 26 );
28 27
29 28 pub fn parse_dirstate_parents(
30 29 contents: &[u8],
31 30 ) -> Result<&DirstateParents, HgError> {
32 31 let (parents, _rest) = DirstateParents::from_bytes(contents)
33 32 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
34 33 Ok(parents)
35 34 }
36 35
37 36 #[timed]
38 37 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
39 38 let mut copies = Vec::new();
40 39 let mut entries = Vec::new();
41 40 let parents =
42 41 parse_dirstate_entries(contents, |path, entry, copy_source| {
43 42 if let Some(source) = copy_source {
44 43 copies.push((path, source));
45 44 }
46 45 entries.push((path, *entry));
47 46 })?;
48 47 Ok((parents, entries, copies))
49 48 }
50 49
51 50 pub fn parse_dirstate_entries<'a>(
52 51 mut contents: &'a [u8],
53 52 mut each_entry: impl FnMut(&'a HgPath, &DirstateEntry, Option<&'a HgPath>),
54 53 ) -> Result<&'a DirstateParents, HgError> {
55 54 let (parents, rest) = DirstateParents::from_bytes(contents)
56 55 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
57 56 contents = rest;
58 57 while !contents.is_empty() {
59 58 let (raw_entry, rest) = RawEntry::from_bytes(contents)
60 59 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
61 60
62 61 let entry = DirstateEntry {
63 62 state: EntryState::try_from(raw_entry.state)?,
64 63 mode: raw_entry.mode.get(),
65 64 mtime: raw_entry.mtime.get(),
66 65 size: raw_entry.size.get(),
67 66 };
68 67 let (paths, rest) =
69 68 u8::slice_from_bytes(rest, raw_entry.length.get() as usize)
70 69 .map_err(|_| HgError::corrupted("Overflow in dirstate."))?;
71 70
72 71 // `paths` is either a single path, or two paths separated by a NULL
73 72 // byte
74 73 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
75 74 let path = HgPath::new(
76 75 iter.next().expect("splitn always yields at least one item"),
77 76 );
78 77 let copy_source = iter.next().map(HgPath::new);
79 78 each_entry(path, &entry, copy_source);
80 79
81 80 contents = rest;
82 81 }
83 82 Ok(parents)
84 83 }
85 84
86 /// `now` is the duration in seconds since the Unix epoch
85 /// Seconds since the Unix epoch
86 pub struct Timestamp(pub u64);
87
87 88 pub fn pack_dirstate(
88 89 state_map: &mut StateMap,
89 90 copy_map: &CopyMap,
90 91 parents: DirstateParents,
91 now: Duration,
92 now: Timestamp,
92 93 ) -> Result<Vec<u8>, HgError> {
93 94 // TODO move away from i32 before 2038.
94 let now: i32 = now.as_secs().try_into().expect("time overflow");
95 let now: i32 = now.0.try_into().expect("time overflow");
95 96
96 97 let expected_size: usize = state_map
97 98 .iter()
98 99 .map(|(filename, _)| {
99 100 let mut length = MIN_ENTRY_SIZE + filename.len();
100 101 if let Some(copy) = copy_map.get(filename) {
101 102 length += copy.len() + 1;
102 103 }
103 104 length
104 105 })
105 106 .sum();
106 107 let expected_size = expected_size + PARENT_SIZE * 2;
107 108
108 109 let mut packed = Vec::with_capacity(expected_size);
109 110
110 111 packed.extend(parents.p1.as_bytes());
111 112 packed.extend(parents.p2.as_bytes());
112 113
113 114 for (filename, entry) in state_map.iter_mut() {
114 115 let new_filename = filename.to_owned();
115 116 let mut new_mtime: i32 = entry.mtime;
116 117 if entry.state == EntryState::Normal && entry.mtime == now {
117 118 // The file was last modified "simultaneously" with the current
118 119 // write to dirstate (i.e. within the same second for file-
119 120 // systems with a granularity of 1 sec). This commonly happens
120 121 // for at least a couple of files on 'update'.
121 122 // The user could change the file without changing its size
122 123 // within the same second. Invalidate the file's mtime in
123 124 // dirstate, forcing future 'status' calls to compare the
124 125 // contents of the file if the size is the same. This prevents
125 126 // mistakenly treating such files as clean.
126 127 new_mtime = -1;
127 128 *entry = DirstateEntry {
128 129 mtime: new_mtime,
129 130 ..*entry
130 131 };
131 132 }
132 133 let mut new_filename = new_filename.into_vec();
133 134 if let Some(copy) = copy_map.get(filename) {
134 135 new_filename.push(b'\0');
135 136 new_filename.extend(copy.bytes());
136 137 }
137 138
138 139 // Unwrapping because `impl std::io::Write for Vec<u8>` never errors
139 140 packed.write_u8(entry.state.into()).unwrap();
140 141 packed.write_i32::<BigEndian>(entry.mode).unwrap();
141 142 packed.write_i32::<BigEndian>(entry.size).unwrap();
142 143 packed.write_i32::<BigEndian>(new_mtime).unwrap();
143 144 packed
144 145 .write_i32::<BigEndian>(new_filename.len() as i32)
145 146 .unwrap();
146 147 packed.extend(new_filename)
147 148 }
148 149
149 150 if packed.len() != expected_size {
150 151 return Err(HgError::CorruptedRepository(format!(
151 152 "bad dirstate size: {} != {}",
152 153 expected_size,
153 154 packed.len()
154 155 )));
155 156 }
156 157
157 158 Ok(packed)
158 159 }
159 160
160 161 #[cfg(test)]
161 162 mod tests {
162 163 use super::*;
163 164 use crate::{utils::hg_path::HgPathBuf, FastHashMap};
164 165 use pretty_assertions::assert_eq;
165 166
166 167 #[test]
167 168 fn test_pack_dirstate_empty() {
168 169 let mut state_map = StateMap::default();
169 170 let copymap = FastHashMap::default();
170 171 let parents = DirstateParents {
171 172 p1: b"12345678910111213141".into(),
172 173 p2: b"00000000000000000000".into(),
173 174 };
174 let now = Duration::new(15000000, 0);
175 let now = Timestamp(15000000);
175 176 let expected = b"1234567891011121314100000000000000000000".to_vec();
176 177
177 178 assert_eq!(
178 179 expected,
179 180 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
180 181 );
181 182
182 183 assert!(state_map.is_empty())
183 184 }
184 185 #[test]
185 186 fn test_pack_dirstate_one_entry() {
186 187 let expected_state_map: StateMap = [(
187 188 HgPathBuf::from_bytes(b"f1"),
188 189 DirstateEntry {
189 190 state: EntryState::Normal,
190 191 mode: 0o644,
191 192 size: 0,
192 193 mtime: 791231220,
193 194 },
194 195 )]
195 196 .iter()
196 197 .cloned()
197 198 .collect();
198 199 let mut state_map = expected_state_map.clone();
199 200
200 201 let copymap = FastHashMap::default();
201 202 let parents = DirstateParents {
202 203 p1: b"12345678910111213141".into(),
203 204 p2: b"00000000000000000000".into(),
204 205 };
205 let now = Duration::new(15000000, 0);
206 let now = Timestamp(15000000);
206 207 let expected = [
207 208 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
208 209 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
209 210 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
210 211 41, 58, 244, 0, 0, 0, 2, 102, 49,
211 212 ]
212 213 .to_vec();
213 214
214 215 assert_eq!(
215 216 expected,
216 217 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
217 218 );
218 219
219 220 assert_eq!(expected_state_map, state_map);
220 221 }
221 222 #[test]
222 223 fn test_pack_dirstate_one_entry_with_copy() {
223 224 let expected_state_map: StateMap = [(
224 225 HgPathBuf::from_bytes(b"f1"),
225 226 DirstateEntry {
226 227 state: EntryState::Normal,
227 228 mode: 0o644,
228 229 size: 0,
229 230 mtime: 791231220,
230 231 },
231 232 )]
232 233 .iter()
233 234 .cloned()
234 235 .collect();
235 236 let mut state_map = expected_state_map.clone();
236 237 let mut copymap = FastHashMap::default();
237 238 copymap.insert(
238 239 HgPathBuf::from_bytes(b"f1"),
239 240 HgPathBuf::from_bytes(b"copyname"),
240 241 );
241 242 let parents = DirstateParents {
242 243 p1: b"12345678910111213141".into(),
243 244 p2: b"00000000000000000000".into(),
244 245 };
245 let now = Duration::new(15000000, 0);
246 let now = Timestamp(15000000);
246 247 let expected = [
247 248 49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
248 249 51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
249 250 48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
250 251 41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
251 252 109, 101,
252 253 ]
253 254 .to_vec();
254 255
255 256 assert_eq!(
256 257 expected,
257 258 pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
258 259 );
259 260 assert_eq!(expected_state_map, state_map);
260 261 }
261 262
262 263 #[test]
263 264 fn test_parse_pack_one_entry_with_copy() {
264 265 let mut state_map: StateMap = [(
265 266 HgPathBuf::from_bytes(b"f1"),
266 267 DirstateEntry {
267 268 state: EntryState::Normal,
268 269 mode: 0o644,
269 270 size: 0,
270 271 mtime: 791231220,
271 272 },
272 273 )]
273 274 .iter()
274 275 .cloned()
275 276 .collect();
276 277 let mut copymap = FastHashMap::default();
277 278 copymap.insert(
278 279 HgPathBuf::from_bytes(b"f1"),
279 280 HgPathBuf::from_bytes(b"copyname"),
280 281 );
281 282 let parents = DirstateParents {
282 283 p1: b"12345678910111213141".into(),
283 284 p2: b"00000000000000000000".into(),
284 285 };
285 let now = Duration::new(15000000, 0);
286 let now = Timestamp(15000000);
286 287 let result =
287 288 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
288 289 .unwrap();
289 290
290 291 let (new_parents, entries, copies) =
291 292 parse_dirstate(result.as_slice()).unwrap();
292 293 let new_state_map: StateMap = entries
293 294 .into_iter()
294 295 .map(|(path, entry)| (path.to_owned(), entry))
295 296 .collect();
296 297 let new_copy_map: CopyMap = copies
297 298 .into_iter()
298 299 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
299 300 .collect();
300 301
301 302 assert_eq!(
302 303 (&parents, state_map, copymap),
303 304 (new_parents, new_state_map, new_copy_map)
304 305 )
305 306 }
306 307
307 308 #[test]
308 309 fn test_parse_pack_multiple_entries_with_copy() {
309 310 let mut state_map: StateMap = [
310 311 (
311 312 HgPathBuf::from_bytes(b"f1"),
312 313 DirstateEntry {
313 314 state: EntryState::Normal,
314 315 mode: 0o644,
315 316 size: 0,
316 317 mtime: 791231220,
317 318 },
318 319 ),
319 320 (
320 321 HgPathBuf::from_bytes(b"f2"),
321 322 DirstateEntry {
322 323 state: EntryState::Merged,
323 324 mode: 0o777,
324 325 size: 1000,
325 326 mtime: 791231220,
326 327 },
327 328 ),
328 329 (
329 330 HgPathBuf::from_bytes(b"f3"),
330 331 DirstateEntry {
331 332 state: EntryState::Removed,
332 333 mode: 0o644,
333 334 size: 234553,
334 335 mtime: 791231220,
335 336 },
336 337 ),
337 338 (
338 339 HgPathBuf::from_bytes(b"f4\xF6"),
339 340 DirstateEntry {
340 341 state: EntryState::Added,
341 342 mode: 0o644,
342 343 size: -1,
343 344 mtime: -1,
344 345 },
345 346 ),
346 347 ]
347 348 .iter()
348 349 .cloned()
349 350 .collect();
350 351 let mut copymap = FastHashMap::default();
351 352 copymap.insert(
352 353 HgPathBuf::from_bytes(b"f1"),
353 354 HgPathBuf::from_bytes(b"copyname"),
354 355 );
355 356 copymap.insert(
356 357 HgPathBuf::from_bytes(b"f4\xF6"),
357 358 HgPathBuf::from_bytes(b"copyname2"),
358 359 );
359 360 let parents = DirstateParents {
360 361 p1: b"12345678910111213141".into(),
361 362 p2: b"00000000000000000000".into(),
362 363 };
363 let now = Duration::new(15000000, 0);
364 let now = Timestamp(15000000);
364 365 let result =
365 366 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
366 367 .unwrap();
367 368
368 369 let (new_parents, entries, copies) =
369 370 parse_dirstate(result.as_slice()).unwrap();
370 371 let new_state_map: StateMap = entries
371 372 .into_iter()
372 373 .map(|(path, entry)| (path.to_owned(), entry))
373 374 .collect();
374 375 let new_copy_map: CopyMap = copies
375 376 .into_iter()
376 377 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
377 378 .collect();
378 379
379 380 assert_eq!(
380 381 (&parents, state_map, copymap),
381 382 (new_parents, new_state_map, new_copy_map)
382 383 )
383 384 }
384 385
385 386 #[test]
386 387 /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
387 388 fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
388 389 let mut state_map: StateMap = [(
389 390 HgPathBuf::from_bytes(b"f1"),
390 391 DirstateEntry {
391 392 state: EntryState::Normal,
392 393 mode: 0o644,
393 394 size: 0,
394 395 mtime: 15000000,
395 396 },
396 397 )]
397 398 .iter()
398 399 .cloned()
399 400 .collect();
400 401 let mut copymap = FastHashMap::default();
401 402 copymap.insert(
402 403 HgPathBuf::from_bytes(b"f1"),
403 404 HgPathBuf::from_bytes(b"copyname"),
404 405 );
405 406 let parents = DirstateParents {
406 407 p1: b"12345678910111213141".into(),
407 408 p2: b"00000000000000000000".into(),
408 409 };
409 let now = Duration::new(15000000, 0);
410 let now = Timestamp(15000000);
410 411 let result =
411 412 pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
412 413 .unwrap();
413 414
414 415 let (new_parents, entries, copies) =
415 416 parse_dirstate(result.as_slice()).unwrap();
416 417 let new_state_map: StateMap = entries
417 418 .into_iter()
418 419 .map(|(path, entry)| (path.to_owned(), entry))
419 420 .collect();
420 421 let new_copy_map: CopyMap = copies
421 422 .into_iter()
422 423 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
423 424 .collect();
424 425
425 426 assert_eq!(
426 427 (
427 428 &parents,
428 429 [(
429 430 HgPathBuf::from_bytes(b"f1"),
430 431 DirstateEntry {
431 432 state: EntryState::Normal,
432 433 mode: 0o644,
433 434 size: 0,
434 435 mtime: -1
435 436 }
436 437 )]
437 438 .iter()
438 439 .cloned()
439 440 .collect::<StateMap>(),
440 441 copymap,
441 442 ),
442 443 (new_parents, new_state_map, new_copy_map)
443 444 )
444 445 }
445 446 }
@@ -1,418 +1,418
1 1 use std::collections::BTreeMap;
2 2 use std::path::PathBuf;
3 use std::time::Duration;
4 3
5 4 use super::path_with_basename::WithBasename;
6 5 use crate::dirstate::parsers::parse_dirstate_entries;
7 6 use crate::dirstate::parsers::parse_dirstate_parents;
7 use crate::dirstate::parsers::Timestamp;
8 8
9 9 use crate::matchers::Matcher;
10 10 use crate::revlog::node::NULL_NODE;
11 11 use crate::utils::hg_path::{HgPath, HgPathBuf};
12 12 use crate::CopyMapIter;
13 13 use crate::DirstateEntry;
14 14 use crate::DirstateError;
15 15 use crate::DirstateMapError;
16 16 use crate::DirstateParents;
17 17 use crate::DirstateStatus;
18 18 use crate::EntryState;
19 19 use crate::FastHashMap;
20 20 use crate::HgPathCow;
21 21 use crate::PatternFileWarning;
22 22 use crate::StateMapIter;
23 23 use crate::StatusError;
24 24 use crate::StatusOptions;
25 25
26 26 pub struct DirstateMap {
27 27 parents: Option<DirstateParents>,
28 28 dirty_parents: bool,
29 29 root: ChildNodes,
30 30 }
31 31
32 32 /// Using a plain `HgPathBuf` of the full path from the repository root as a
33 33 /// map key would also work: all paths in a given map have the same parent
34 34 /// path, so comparing full paths gives the same result as comparing base
35 35 /// names. However `BTreeMap` would waste time always re-comparing the same
36 36 /// string prefix.
37 37 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
38 38
39 39 #[derive(Default)]
40 40 struct Node {
41 41 entry: Option<DirstateEntry>,
42 42 copy_source: Option<HgPathBuf>,
43 43 children: ChildNodes,
44 44 }
45 45
46 46 /// `(full_path, entry, copy_source)`
47 47 type NodeDataMut<'a> = (
48 48 &'a WithBasename<HgPathBuf>,
49 49 &'a mut Option<DirstateEntry>,
50 50 &'a mut Option<HgPathBuf>,
51 51 );
52 52
53 53 impl DirstateMap {
54 54 pub fn new() -> Self {
55 55 Self {
56 56 parents: None,
57 57 dirty_parents: false,
58 58 root: ChildNodes::new(),
59 59 }
60 60 }
61 61
62 62 fn get_node(&self, path: &HgPath) -> Option<&Node> {
63 63 let mut children = &self.root;
64 64 let mut components = path.components();
65 65 let mut component =
66 66 components.next().expect("expected at least one components");
67 67 loop {
68 68 let child = children.get(component)?;
69 69 if let Some(next_component) = components.next() {
70 70 component = next_component;
71 71 children = &child.children;
72 72 } else {
73 73 return Some(child);
74 74 }
75 75 }
76 76 }
77 77
78 78 fn get_or_insert_node(&mut self, path: &HgPath) -> &mut Node {
79 79 let mut child_nodes = &mut self.root;
80 80 let mut inclusive_ancestor_paths =
81 81 WithBasename::inclusive_ancestors_of(path);
82 82 let mut ancestor_path = inclusive_ancestor_paths
83 83 .next()
84 84 .expect("expected at least one inclusive ancestor");
85 85 loop {
86 86 // TODO: can we avoid double lookup in all cases without allocating
87 87 // an owned key in cases where the map already contains that key?
88 88 let child_node =
89 89 if child_nodes.contains_key(ancestor_path.base_name()) {
90 90 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
91 91 } else {
92 92 // This is always a vacant entry, using `.entry()` lets us
93 93 // return a `&mut Node` of the newly-inserted node without
94 94 // yet another lookup. `BTreeMap::insert` doesn’t do this.
95 95 child_nodes.entry(ancestor_path.to_owned()).or_default()
96 96 };
97 97 if let Some(next) = inclusive_ancestor_paths.next() {
98 98 ancestor_path = next;
99 99 child_nodes = &mut child_node.children;
100 100 } else {
101 101 return child_node;
102 102 }
103 103 }
104 104 }
105 105
106 106 fn iter_nodes<'a>(
107 107 &'a self,
108 108 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
109 109 {
110 110 // Depth first tree traversal.
111 111 //
112 112 // If we could afford internal iteration and recursion,
113 113 // this would look like:
114 114 //
115 115 // ```
116 116 // fn traverse_children(
117 117 // children: &ChildNodes,
118 118 // each: &mut impl FnMut(&Node),
119 119 // ) {
120 120 // for child in children.values() {
121 121 // traverse_children(&child.children, each);
122 122 // each(child);
123 123 // }
124 124 // }
125 125 // ```
126 126 //
127 127 // However we want an external iterator and therefore can’t use the
128 128 // call stack. Use an explicit stack instead:
129 129 let mut stack = Vec::new();
130 130 let mut iter = self.root.iter();
131 131 std::iter::from_fn(move || {
132 132 while let Some((key, child_node)) = iter.next() {
133 133 // Pseudo-recursion
134 134 let new_iter = child_node.children.iter();
135 135 let old_iter = std::mem::replace(&mut iter, new_iter);
136 136 stack.push((key, child_node, old_iter));
137 137 }
138 138 // Found the end of a `children.iter()` iterator.
139 139 if let Some((key, child_node, next_iter)) = stack.pop() {
140 140 // "Return" from pseudo-recursion by restoring state from the
141 141 // explicit stack
142 142 iter = next_iter;
143 143
144 144 Some((key, child_node))
145 145 } else {
146 146 // Reached the bottom of the stack, we’re done
147 147 None
148 148 }
149 149 })
150 150 }
151 151
152 152 /// Mutable iterator for the `(entry, copy source)` of each node.
153 153 ///
154 154 /// It would not be safe to yield mutable references to nodes themeselves
155 155 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
156 156 /// reachable from their ancestor nodes, potentially creating multiple
157 157 /// `&mut` references to a given node.
158 158 fn iter_node_data_mut<'a>(
159 159 &'a mut self,
160 160 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
161 161 // Explict stack for pseudo-recursion, see `iter_nodes` above.
162 162 let mut stack = Vec::new();
163 163 let mut iter = self.root.iter_mut();
164 164 std::iter::from_fn(move || {
165 165 while let Some((key, child_node)) = iter.next() {
166 166 // Pseudo-recursion
167 167 let data =
168 168 (key, &mut child_node.entry, &mut child_node.copy_source);
169 169 let new_iter = child_node.children.iter_mut();
170 170 let old_iter = std::mem::replace(&mut iter, new_iter);
171 171 stack.push((data, old_iter));
172 172 }
173 173 // Found the end of a `children.values_mut()` iterator.
174 174 if let Some((data, next_iter)) = stack.pop() {
175 175 // "Return" from pseudo-recursion by restoring state from the
176 176 // explicit stack
177 177 iter = next_iter;
178 178
179 179 Some(data)
180 180 } else {
181 181 // Reached the bottom of the stack, we’re done
182 182 None
183 183 }
184 184 })
185 185 }
186 186 }
187 187
188 188 impl super::dispatch::DirstateMapMethods for DirstateMap {
189 189 fn clear(&mut self) {
190 190 self.set_parents(&DirstateParents {
191 191 p1: NULL_NODE,
192 192 p2: NULL_NODE,
193 193 });
194 194 self.root.clear()
195 195 }
196 196
197 197 fn add_file(
198 198 &mut self,
199 199 _filename: &HgPath,
200 200 _old_state: EntryState,
201 201 _entry: DirstateEntry,
202 202 ) -> Result<(), DirstateMapError> {
203 203 todo!()
204 204 }
205 205
206 206 fn remove_file(
207 207 &mut self,
208 208 _filename: &HgPath,
209 209 _old_state: EntryState,
210 210 _size: i32,
211 211 ) -> Result<(), DirstateMapError> {
212 212 todo!()
213 213 }
214 214
215 215 fn drop_file(
216 216 &mut self,
217 217 _filename: &HgPath,
218 218 _old_state: EntryState,
219 219 ) -> Result<bool, DirstateMapError> {
220 220 todo!()
221 221 }
222 222
223 223 fn clear_ambiguous_times(
224 224 &mut self,
225 225 _filenames: Vec<HgPathBuf>,
226 226 _now: i32,
227 227 ) {
228 228 todo!()
229 229 }
230 230
231 231 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
232 232 todo!()
233 233 }
234 234
235 235 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
236 236 todo!()
237 237 }
238 238
239 239 fn non_normal_or_other_parent_paths(
240 240 &mut self,
241 241 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
242 242 todo!()
243 243 }
244 244
245 245 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
246 246 todo!()
247 247 }
248 248
249 249 fn iter_non_normal_paths(
250 250 &mut self,
251 251 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
252 252 todo!()
253 253 }
254 254
255 255 fn iter_non_normal_paths_panic(
256 256 &self,
257 257 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
258 258 todo!()
259 259 }
260 260
261 261 fn iter_other_parent_paths(
262 262 &mut self,
263 263 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
264 264 todo!()
265 265 }
266 266
267 267 fn has_tracked_dir(
268 268 &mut self,
269 269 _directory: &HgPath,
270 270 ) -> Result<bool, DirstateMapError> {
271 271 todo!()
272 272 }
273 273
274 274 fn has_dir(
275 275 &mut self,
276 276 _directory: &HgPath,
277 277 ) -> Result<bool, DirstateMapError> {
278 278 todo!()
279 279 }
280 280
281 281 fn parents(
282 282 &mut self,
283 283 file_contents: &[u8],
284 284 ) -> Result<&DirstateParents, DirstateError> {
285 285 if self.parents.is_none() {
286 286 let parents = if !file_contents.is_empty() {
287 287 parse_dirstate_parents(file_contents)?.clone()
288 288 } else {
289 289 DirstateParents {
290 290 p1: NULL_NODE,
291 291 p2: NULL_NODE,
292 292 }
293 293 };
294 294 self.parents = Some(parents);
295 295 }
296 296 Ok(self.parents.as_ref().unwrap())
297 297 }
298 298
299 299 fn set_parents(&mut self, parents: &DirstateParents) {
300 300 self.parents = Some(parents.clone());
301 301 self.dirty_parents = true;
302 302 }
303 303
304 304 fn read<'a>(
305 305 &mut self,
306 306 file_contents: &'a [u8],
307 307 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
308 308 if file_contents.is_empty() {
309 309 return Ok(None);
310 310 }
311 311
312 312 let parents = parse_dirstate_entries(
313 313 file_contents,
314 314 |path, entry, copy_source| {
315 315 let node = self.get_or_insert_node(path);
316 316 node.entry = Some(*entry);
317 317 node.copy_source = copy_source.map(HgPath::to_owned);
318 318 },
319 319 )?;
320 320
321 321 if !self.dirty_parents {
322 322 self.set_parents(parents);
323 323 }
324 324
325 325 Ok(Some(parents))
326 326 }
327 327
328 328 fn pack(
329 329 &mut self,
330 330 _parents: DirstateParents,
331 _now: Duration,
331 _now: Timestamp,
332 332 ) -> Result<Vec<u8>, DirstateError> {
333 333 let _ = self.iter_node_data_mut();
334 334 todo!()
335 335 }
336 336
337 337 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
338 338 todo!()
339 339 }
340 340
341 341 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
342 342 todo!()
343 343 }
344 344
345 345 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
346 346 todo!()
347 347 }
348 348
349 349 fn status<'a>(
350 350 &'a self,
351 351 _matcher: &'a (dyn Matcher + Sync),
352 352 _root_dir: PathBuf,
353 353 _ignore_files: Vec<PathBuf>,
354 354 _options: StatusOptions,
355 355 ) -> Result<
356 356 (
357 357 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
358 358 Vec<PatternFileWarning>,
359 359 ),
360 360 StatusError,
361 361 > {
362 362 todo!()
363 363 }
364 364
365 365 fn copy_map_len(&self) -> usize {
366 366 todo!()
367 367 }
368 368
369 369 fn copy_map_iter(&self) -> CopyMapIter<'_> {
370 370 Box::new(self.iter_nodes().filter_map(|(path, node)| {
371 371 node.copy_source
372 372 .as_ref()
373 373 .map(|copy_source| (path.full_path(), copy_source))
374 374 }))
375 375 }
376 376
377 377 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
378 378 if let Some(node) = self.get_node(key) {
379 379 node.copy_source.is_some()
380 380 } else {
381 381 false
382 382 }
383 383 }
384 384
385 385 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
386 386 self.get_node(key)?.copy_source.as_ref()
387 387 }
388 388
389 389 fn copy_map_remove(&mut self, _key: &HgPath) -> Option<HgPathBuf> {
390 390 todo!()
391 391 }
392 392
393 393 fn copy_map_insert(
394 394 &mut self,
395 395 _key: HgPathBuf,
396 396 _value: HgPathBuf,
397 397 ) -> Option<HgPathBuf> {
398 398 todo!()
399 399 }
400 400
401 401 fn len(&self) -> usize {
402 402 todo!()
403 403 }
404 404
405 405 fn contains_key(&self, key: &HgPath) -> bool {
406 406 self.get(key).is_some()
407 407 }
408 408
409 409 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
410 410 self.get_node(key)?.entry.as_ref()
411 411 }
412 412
413 413 fn iter(&self) -> StateMapIter<'_> {
414 414 Box::new(self.iter_nodes().filter_map(|(path, node)| {
415 415 node.entry.as_ref().map(|entry| (path.full_path(), entry))
416 416 }))
417 417 }
418 418 }
@@ -1,333 +1,333
1 1 use std::path::PathBuf;
2 use std::time::Duration;
3 2
3 use crate::dirstate::parsers::Timestamp;
4 4 use crate::matchers::Matcher;
5 5 use crate::utils::hg_path::{HgPath, HgPathBuf};
6 6 use crate::CopyMapIter;
7 7 use crate::DirstateEntry;
8 8 use crate::DirstateError;
9 9 use crate::DirstateMap;
10 10 use crate::DirstateMapError;
11 11 use crate::DirstateParents;
12 12 use crate::DirstateStatus;
13 13 use crate::EntryState;
14 14 use crate::FastHashMap;
15 15 use crate::HgPathCow;
16 16 use crate::PatternFileWarning;
17 17 use crate::StateMapIter;
18 18 use crate::StatusError;
19 19 use crate::StatusOptions;
20 20
21 21 pub trait DirstateMapMethods {
22 22 fn clear(&mut self);
23 23
24 24 fn add_file(
25 25 &mut self,
26 26 filename: &HgPath,
27 27 old_state: EntryState,
28 28 entry: DirstateEntry,
29 29 ) -> Result<(), DirstateMapError>;
30 30
31 31 fn remove_file(
32 32 &mut self,
33 33 filename: &HgPath,
34 34 old_state: EntryState,
35 35 size: i32,
36 36 ) -> Result<(), DirstateMapError>;
37 37
38 38 fn drop_file(
39 39 &mut self,
40 40 filename: &HgPath,
41 41 old_state: EntryState,
42 42 ) -> Result<bool, DirstateMapError>;
43 43
44 44 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32);
45 45
46 46 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool;
47 47
48 48 fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool;
49 49
50 50 fn non_normal_or_other_parent_paths(
51 51 &mut self,
52 52 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_>;
53 53
54 54 fn set_non_normal_other_parent_entries(&mut self, force: bool);
55 55
56 56 fn iter_non_normal_paths(
57 57 &mut self,
58 58 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
59 59
60 60 fn iter_non_normal_paths_panic(
61 61 &self,
62 62 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
63 63
64 64 fn iter_other_parent_paths(
65 65 &mut self,
66 66 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_>;
67 67
68 68 fn has_tracked_dir(
69 69 &mut self,
70 70 directory: &HgPath,
71 71 ) -> Result<bool, DirstateMapError>;
72 72
73 73 fn has_dir(
74 74 &mut self,
75 75 directory: &HgPath,
76 76 ) -> Result<bool, DirstateMapError>;
77 77
78 78 fn parents(
79 79 &mut self,
80 80 file_contents: &[u8],
81 81 ) -> Result<&DirstateParents, DirstateError>;
82 82
83 83 fn set_parents(&mut self, parents: &DirstateParents);
84 84
85 85 fn read<'a>(
86 86 &mut self,
87 87 file_contents: &'a [u8],
88 88 ) -> Result<Option<&'a DirstateParents>, DirstateError>;
89 89
90 90 fn pack(
91 91 &mut self,
92 92 parents: DirstateParents,
93 now: Duration,
93 now: Timestamp,
94 94 ) -> Result<Vec<u8>, DirstateError>;
95 95
96 96 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf>;
97 97
98 98 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError>;
99 99
100 100 fn set_dirs(&mut self) -> Result<(), DirstateMapError>;
101 101
102 102 fn status<'a>(
103 103 &'a self,
104 104 matcher: &'a (dyn Matcher + Sync),
105 105 root_dir: PathBuf,
106 106 ignore_files: Vec<PathBuf>,
107 107 options: StatusOptions,
108 108 ) -> Result<
109 109 (
110 110 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
111 111 Vec<PatternFileWarning>,
112 112 ),
113 113 StatusError,
114 114 >;
115 115
116 116 fn copy_map_len(&self) -> usize;
117 117
118 118 fn copy_map_iter(&self) -> CopyMapIter<'_>;
119 119
120 120 fn copy_map_contains_key(&self, key: &HgPath) -> bool;
121 121
122 122 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf>;
123 123
124 124 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf>;
125 125
126 126 fn copy_map_insert(
127 127 &mut self,
128 128 key: HgPathBuf,
129 129 value: HgPathBuf,
130 130 ) -> Option<HgPathBuf>;
131 131
132 132 fn len(&self) -> usize;
133 133
134 134 fn contains_key(&self, key: &HgPath) -> bool;
135 135
136 136 fn get(&self, key: &HgPath) -> Option<&DirstateEntry>;
137 137
138 138 fn iter(&self) -> StateMapIter<'_>;
139 139 }
140 140
141 141 impl DirstateMapMethods for DirstateMap {
142 142 fn clear(&mut self) {
143 143 self.clear()
144 144 }
145 145
146 146 fn add_file(
147 147 &mut self,
148 148 filename: &HgPath,
149 149 old_state: EntryState,
150 150 entry: DirstateEntry,
151 151 ) -> Result<(), DirstateMapError> {
152 152 self.add_file(filename, old_state, entry)
153 153 }
154 154
155 155 fn remove_file(
156 156 &mut self,
157 157 filename: &HgPath,
158 158 old_state: EntryState,
159 159 size: i32,
160 160 ) -> Result<(), DirstateMapError> {
161 161 self.remove_file(filename, old_state, size)
162 162 }
163 163
164 164 fn drop_file(
165 165 &mut self,
166 166 filename: &HgPath,
167 167 old_state: EntryState,
168 168 ) -> Result<bool, DirstateMapError> {
169 169 self.drop_file(filename, old_state)
170 170 }
171 171
172 172 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
173 173 self.clear_ambiguous_times(filenames, now)
174 174 }
175 175
176 176 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
177 177 let (non_normal, _other_parent) =
178 178 self.get_non_normal_other_parent_entries();
179 179 non_normal.contains(key)
180 180 }
181 181
182 182 fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool {
183 183 self.non_normal_entries_remove(key)
184 184 }
185 185
186 186 fn non_normal_or_other_parent_paths(
187 187 &mut self,
188 188 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
189 189 let (non_normal, other_parent) =
190 190 self.get_non_normal_other_parent_entries();
191 191 Box::new(non_normal.union(other_parent))
192 192 }
193 193
194 194 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
195 195 self.set_non_normal_other_parent_entries(force)
196 196 }
197 197
198 198 fn iter_non_normal_paths(
199 199 &mut self,
200 200 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
201 201 let (non_normal, _other_parent) =
202 202 self.get_non_normal_other_parent_entries();
203 203 Box::new(non_normal.iter())
204 204 }
205 205
206 206 fn iter_non_normal_paths_panic(
207 207 &self,
208 208 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
209 209 let (non_normal, _other_parent) =
210 210 self.get_non_normal_other_parent_entries_panic();
211 211 Box::new(non_normal.iter())
212 212 }
213 213
214 214 fn iter_other_parent_paths(
215 215 &mut self,
216 216 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
217 217 let (_non_normal, other_parent) =
218 218 self.get_non_normal_other_parent_entries();
219 219 Box::new(other_parent.iter())
220 220 }
221 221
222 222 fn has_tracked_dir(
223 223 &mut self,
224 224 directory: &HgPath,
225 225 ) -> Result<bool, DirstateMapError> {
226 226 self.has_tracked_dir(directory)
227 227 }
228 228
229 229 fn has_dir(
230 230 &mut self,
231 231 directory: &HgPath,
232 232 ) -> Result<bool, DirstateMapError> {
233 233 self.has_dir(directory)
234 234 }
235 235
236 236 fn parents(
237 237 &mut self,
238 238 file_contents: &[u8],
239 239 ) -> Result<&DirstateParents, DirstateError> {
240 240 self.parents(file_contents)
241 241 }
242 242
243 243 fn set_parents(&mut self, parents: &DirstateParents) {
244 244 self.set_parents(parents)
245 245 }
246 246
247 247 fn read<'a>(
248 248 &mut self,
249 249 file_contents: &'a [u8],
250 250 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
251 251 self.read(file_contents)
252 252 }
253 253
254 254 fn pack(
255 255 &mut self,
256 256 parents: DirstateParents,
257 now: Duration,
257 now: Timestamp,
258 258 ) -> Result<Vec<u8>, DirstateError> {
259 259 self.pack(parents, now)
260 260 }
261 261
262 262 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
263 263 self.build_file_fold_map()
264 264 }
265 265
266 266 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
267 267 self.set_all_dirs()
268 268 }
269 269
270 270 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
271 271 self.set_dirs()
272 272 }
273 273
274 274 fn status<'a>(
275 275 &'a self,
276 276 matcher: &'a (dyn Matcher + Sync),
277 277 root_dir: PathBuf,
278 278 ignore_files: Vec<PathBuf>,
279 279 options: StatusOptions,
280 280 ) -> Result<
281 281 (
282 282 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
283 283 Vec<PatternFileWarning>,
284 284 ),
285 285 StatusError,
286 286 > {
287 287 crate::status(self, matcher, root_dir, ignore_files, options)
288 288 }
289 289
290 290 fn copy_map_len(&self) -> usize {
291 291 self.copy_map.len()
292 292 }
293 293
294 294 fn copy_map_iter(&self) -> CopyMapIter<'_> {
295 295 Box::new(self.copy_map.iter())
296 296 }
297 297
298 298 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
299 299 self.copy_map.contains_key(key)
300 300 }
301 301
302 302 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
303 303 self.copy_map.get(key)
304 304 }
305 305
306 306 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
307 307 self.copy_map.remove(key)
308 308 }
309 309
310 310 fn copy_map_insert(
311 311 &mut self,
312 312 key: HgPathBuf,
313 313 value: HgPathBuf,
314 314 ) -> Option<HgPathBuf> {
315 315 self.copy_map.insert(key, value)
316 316 }
317 317
318 318 fn len(&self) -> usize {
319 319 (&**self).len()
320 320 }
321 321
322 322 fn contains_key(&self, key: &HgPath) -> bool {
323 323 (&**self).contains_key(key)
324 324 }
325 325
326 326 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
327 327 (&**self).get(key)
328 328 }
329 329
330 330 fn iter(&self) -> StateMapIter<'_> {
331 331 Box::new((&**self).iter())
332 332 }
333 333 }
@@ -1,123 +1,123
1 1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 2 // and Mercurial contributors
3 3 //
4 4 // This software may be used and distributed according to the terms of the
5 5 // GNU General Public License version 2 or any later version.
6 6
7 7 mod ancestors;
8 8 pub mod dagops;
9 9 pub mod errors;
10 10 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
11 mod dirstate;
11 pub mod dirstate;
12 12 pub mod dirstate_tree;
13 13 pub mod discovery;
14 14 pub mod requirements;
15 15 pub mod testing; // unconditionally built, for use from integration tests
16 16 pub use dirstate::{
17 17 dirs_multiset::{DirsMultiset, DirsMultisetIter},
18 18 dirstate_map::DirstateMap,
19 19 parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
20 20 status::{
21 21 status, BadMatch, BadType, DirstateStatus, HgPathCow, StatusError,
22 22 StatusOptions,
23 23 },
24 24 CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
25 25 StateMap, StateMapIter,
26 26 };
27 27 pub mod copy_tracing;
28 28 mod filepatterns;
29 29 pub mod matchers;
30 30 pub mod repo;
31 31 pub mod revlog;
32 32 pub use revlog::*;
33 33 pub mod config;
34 34 pub mod logging;
35 35 pub mod operations;
36 36 pub mod revset;
37 37 pub mod utils;
38 38
39 39 use crate::utils::hg_path::{HgPathBuf, HgPathError};
40 40 pub use filepatterns::{
41 41 parse_pattern_syntax, read_pattern_file, IgnorePattern,
42 42 PatternFileWarning, PatternSyntax,
43 43 };
44 44 use std::collections::HashMap;
45 45 use std::fmt;
46 46 use twox_hash::RandomXxHashBuilder64;
47 47
48 48 /// This is a contract between the `micro-timer` crate and us, to expose
49 49 /// the `log` crate as `crate::log`.
50 50 use log;
51 51
52 52 pub type LineNumber = usize;
53 53
54 54 /// Rust's default hasher is too slow because it tries to prevent collision
55 55 /// attacks. We are not concerned about those: if an ill-minded person has
56 56 /// write access to your repository, you have other issues.
57 57 pub type FastHashMap<K, V> = HashMap<K, V, RandomXxHashBuilder64>;
58 58
59 59 #[derive(Debug, PartialEq)]
60 60 pub enum DirstateMapError {
61 61 PathNotFound(HgPathBuf),
62 62 EmptyPath,
63 63 InvalidPath(HgPathError),
64 64 }
65 65
66 66 impl fmt::Display for DirstateMapError {
67 67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 68 match self {
69 69 DirstateMapError::PathNotFound(_) => {
70 70 f.write_str("expected a value, found none")
71 71 }
72 72 DirstateMapError::EmptyPath => {
73 73 f.write_str("Overflow in dirstate.")
74 74 }
75 75 DirstateMapError::InvalidPath(path_error) => path_error.fmt(f),
76 76 }
77 77 }
78 78 }
79 79
80 80 #[derive(Debug, derive_more::From)]
81 81 pub enum DirstateError {
82 82 Map(DirstateMapError),
83 83 Common(errors::HgError),
84 84 }
85 85
86 86 #[derive(Debug, derive_more::From)]
87 87 pub enum PatternError {
88 88 #[from]
89 89 Path(HgPathError),
90 90 UnsupportedSyntax(String),
91 91 UnsupportedSyntaxInFile(String, String, usize),
92 92 TooLong(usize),
93 93 #[from]
94 94 IO(std::io::Error),
95 95 /// Needed a pattern that can be turned into a regex but got one that
96 96 /// can't. This should only happen through programmer error.
97 97 NonRegexPattern(IgnorePattern),
98 98 }
99 99
100 100 impl fmt::Display for PatternError {
101 101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 102 match self {
103 103 PatternError::UnsupportedSyntax(syntax) => {
104 104 write!(f, "Unsupported syntax {}", syntax)
105 105 }
106 106 PatternError::UnsupportedSyntaxInFile(syntax, file_path, line) => {
107 107 write!(
108 108 f,
109 109 "{}:{}: unsupported syntax {}",
110 110 file_path, line, syntax
111 111 )
112 112 }
113 113 PatternError::TooLong(size) => {
114 114 write!(f, "matcher pattern is too long ({} bytes)", size)
115 115 }
116 116 PatternError::IO(error) => error.fmt(f),
117 117 PatternError::Path(error) => error.fmt(f),
118 118 PatternError::NonRegexPattern(pattern) => {
119 119 write!(f, "'{:?}' cannot be turned into a regex", pattern)
120 120 }
121 121 }
122 122 }
123 123 }
@@ -1,571 +1,571
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 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::{Ref, RefCell};
12 12 use std::convert::TryInto;
13 use std::time::Duration;
14 13
15 14 use cpython::{
16 15 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList,
17 16 PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject,
18 17 ToPyObject, UnsafePyLeaked,
19 18 };
20 19
21 20 use crate::{
22 21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 22 dirstate::non_normal_entries::{
24 23 NonNormalEntries, NonNormalEntriesIterator,
25 24 },
26 25 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
27 26 parsers::dirstate_parents_to_pytuple,
28 27 };
29 28 use hg::{
29 dirstate::parsers::Timestamp,
30 30 dirstate_tree::dispatch::DirstateMapMethods,
31 31 errors::HgError,
32 32 revlog::Node,
33 33 utils::hg_path::{HgPath, HgPathBuf},
34 34 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
35 35 DirstateMapError, DirstateParents, EntryState, StateMapIter,
36 36 };
37 37
38 38 // TODO
39 39 // This object needs to share references to multiple members of its Rust
40 40 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
41 41 // Right now `CopyMap` is done, but it needs to have an explicit reference
42 42 // to `RustDirstateMap` which itself needs to have an encapsulation for
43 43 // every method in `CopyMap` (copymapcopy, etc.).
44 44 // This is ugly and hard to maintain.
45 45 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
46 46 // `py_class!` is already implemented and does not mention
47 47 // `RustDirstateMap`, rightfully so.
48 48 // All attributes also have to have a separate refcount data attribute for
49 49 // leaks, with all methods that go along for reference sharing.
50 50 py_class!(pub class DirstateMap |py| {
51 51 @shared data inner: Box<dyn DirstateMapMethods + Send>;
52 52
53 53 def __new__(_cls, use_dirstate_tree: bool) -> PyResult<Self> {
54 54 let inner = if use_dirstate_tree {
55 55 Box::new(hg::dirstate_tree::dirstate_map::DirstateMap::new()) as _
56 56 } else {
57 57 Box::new(RustDirstateMap::default()) as _
58 58 };
59 59 Self::create_instance(py, inner)
60 60 }
61 61
62 62 def clear(&self) -> PyResult<PyObject> {
63 63 self.inner(py).borrow_mut().clear();
64 64 Ok(py.None())
65 65 }
66 66
67 67 def get(
68 68 &self,
69 69 key: PyObject,
70 70 default: Option<PyObject> = None
71 71 ) -> PyResult<Option<PyObject>> {
72 72 let key = key.extract::<PyBytes>(py)?;
73 73 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
74 74 Some(entry) => {
75 75 Ok(Some(make_dirstate_tuple(py, entry)?))
76 76 },
77 77 None => Ok(default)
78 78 }
79 79 }
80 80
81 81 def addfile(
82 82 &self,
83 83 f: PyObject,
84 84 oldstate: PyObject,
85 85 state: PyObject,
86 86 mode: PyObject,
87 87 size: PyObject,
88 88 mtime: PyObject
89 89 ) -> PyResult<PyObject> {
90 90 self.inner(py).borrow_mut().add_file(
91 91 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
92 92 oldstate.extract::<PyBytes>(py)?.data(py)[0]
93 93 .try_into()
94 94 .map_err(|e: HgError| {
95 95 PyErr::new::<exc::ValueError, _>(py, e.to_string())
96 96 })?,
97 97 DirstateEntry {
98 98 state: state.extract::<PyBytes>(py)?.data(py)[0]
99 99 .try_into()
100 100 .map_err(|e: HgError| {
101 101 PyErr::new::<exc::ValueError, _>(py, e.to_string())
102 102 })?,
103 103 mode: mode.extract(py)?,
104 104 size: size.extract(py)?,
105 105 mtime: mtime.extract(py)?,
106 106 },
107 107 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
108 108 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
109 109 })
110 110 }
111 111
112 112 def removefile(
113 113 &self,
114 114 f: PyObject,
115 115 oldstate: PyObject,
116 116 size: PyObject
117 117 ) -> PyResult<PyObject> {
118 118 self.inner(py).borrow_mut()
119 119 .remove_file(
120 120 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
121 121 oldstate.extract::<PyBytes>(py)?.data(py)[0]
122 122 .try_into()
123 123 .map_err(|e: HgError| {
124 124 PyErr::new::<exc::ValueError, _>(py, e.to_string())
125 125 })?,
126 126 size.extract(py)?,
127 127 )
128 128 .or_else(|_| {
129 129 Err(PyErr::new::<exc::OSError, _>(
130 130 py,
131 131 "Dirstate error".to_string(),
132 132 ))
133 133 })?;
134 134 Ok(py.None())
135 135 }
136 136
137 137 def dropfile(
138 138 &self,
139 139 f: PyObject,
140 140 oldstate: PyObject
141 141 ) -> PyResult<PyBool> {
142 142 self.inner(py).borrow_mut()
143 143 .drop_file(
144 144 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
145 145 oldstate.extract::<PyBytes>(py)?.data(py)[0]
146 146 .try_into()
147 147 .map_err(|e: HgError| {
148 148 PyErr::new::<exc::ValueError, _>(py, e.to_string())
149 149 })?,
150 150 )
151 151 .and_then(|b| Ok(b.to_py_object(py)))
152 152 .or_else(|e| {
153 153 Err(PyErr::new::<exc::OSError, _>(
154 154 py,
155 155 format!("Dirstate error: {}", e.to_string()),
156 156 ))
157 157 })
158 158 }
159 159
160 160 def clearambiguoustimes(
161 161 &self,
162 162 files: PyObject,
163 163 now: PyObject
164 164 ) -> PyResult<PyObject> {
165 165 let files: PyResult<Vec<HgPathBuf>> = files
166 166 .iter(py)?
167 167 .map(|filename| {
168 168 Ok(HgPathBuf::from_bytes(
169 169 filename?.extract::<PyBytes>(py)?.data(py),
170 170 ))
171 171 })
172 172 .collect();
173 173 self.inner(py).borrow_mut()
174 174 .clear_ambiguous_times(files?, now.extract(py)?);
175 175 Ok(py.None())
176 176 }
177 177
178 178 def other_parent_entries(&self) -> PyResult<PyObject> {
179 179 let mut inner_shared = self.inner(py).borrow_mut();
180 180 let set = PySet::empty(py)?;
181 181 for path in inner_shared.iter_other_parent_paths() {
182 182 set.add(py, PyBytes::new(py, path.as_bytes()))?;
183 183 }
184 184 Ok(set.into_object())
185 185 }
186 186
187 187 def non_normal_entries(&self) -> PyResult<NonNormalEntries> {
188 188 NonNormalEntries::from_inner(py, self.clone_ref(py))
189 189 }
190 190
191 191 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
192 192 let key = key.extract::<PyBytes>(py)?;
193 193 Ok(self
194 194 .inner(py)
195 195 .borrow_mut()
196 196 .non_normal_entries_contains(HgPath::new(key.data(py))))
197 197 }
198 198
199 199 def non_normal_entries_display(&self) -> PyResult<PyString> {
200 200 Ok(
201 201 PyString::new(
202 202 py,
203 203 &format!(
204 204 "NonNormalEntries: {}",
205 205 hg::utils::join_display(
206 206 self
207 207 .inner(py)
208 208 .borrow_mut()
209 209 .iter_non_normal_paths(),
210 210 ", "
211 211 )
212 212 )
213 213 )
214 214 )
215 215 }
216 216
217 217 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
218 218 let key = key.extract::<PyBytes>(py)?;
219 219 self
220 220 .inner(py)
221 221 .borrow_mut()
222 222 .non_normal_entries_remove(HgPath::new(key.data(py)));
223 223 Ok(py.None())
224 224 }
225 225
226 226 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
227 227 let mut inner = self.inner(py).borrow_mut();
228 228
229 229 let ret = PyList::new(py, &[]);
230 230 for filename in inner.non_normal_or_other_parent_paths() {
231 231 let as_pystring = PyBytes::new(py, filename.as_bytes());
232 232 ret.append(py, as_pystring.into_object());
233 233 }
234 234 Ok(ret)
235 235 }
236 236
237 237 def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
238 238 // Make sure the sets are defined before we no longer have a mutable
239 239 // reference to the dmap.
240 240 self.inner(py)
241 241 .borrow_mut()
242 242 .set_non_normal_other_parent_entries(false);
243 243
244 244 let leaked_ref = self.inner(py).leak_immutable();
245 245
246 246 NonNormalEntriesIterator::from_inner(py, unsafe {
247 247 leaked_ref.map(py, |o| {
248 248 o.iter_non_normal_paths_panic()
249 249 })
250 250 })
251 251 }
252 252
253 253 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
254 254 let d = d.extract::<PyBytes>(py)?;
255 255 Ok(self.inner(py).borrow_mut()
256 256 .has_tracked_dir(HgPath::new(d.data(py)))
257 257 .map_err(|e| {
258 258 PyErr::new::<exc::ValueError, _>(py, e.to_string())
259 259 })?
260 260 .to_py_object(py))
261 261 }
262 262
263 263 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
264 264 let d = d.extract::<PyBytes>(py)?;
265 265 Ok(self.inner(py).borrow_mut()
266 266 .has_dir(HgPath::new(d.data(py)))
267 267 .map_err(|e| {
268 268 PyErr::new::<exc::ValueError, _>(py, e.to_string())
269 269 })?
270 270 .to_py_object(py))
271 271 }
272 272
273 273 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
274 274 self.inner(py).borrow_mut()
275 275 .parents(st.extract::<PyBytes>(py)?.data(py))
276 276 .map(|parents| dirstate_parents_to_pytuple(py, parents))
277 277 .or_else(|_| {
278 278 Err(PyErr::new::<exc::OSError, _>(
279 279 py,
280 280 "Dirstate error".to_string(),
281 281 ))
282 282 })
283 283 }
284 284
285 285 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
286 286 let p1 = extract_node_id(py, &p1)?;
287 287 let p2 = extract_node_id(py, &p2)?;
288 288
289 289 self.inner(py).borrow_mut()
290 290 .set_parents(&DirstateParents { p1, p2 });
291 291 Ok(py.None())
292 292 }
293 293
294 294 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
295 295 match self.inner(py).borrow_mut()
296 296 .read(st.extract::<PyBytes>(py)?.data(py))
297 297 {
298 298 Ok(Some(parents)) => Ok(Some(
299 299 dirstate_parents_to_pytuple(py, parents)
300 300 .into_object()
301 301 )),
302 302 Ok(None) => Ok(Some(py.None())),
303 303 Err(_) => Err(PyErr::new::<exc::OSError, _>(
304 304 py,
305 305 "Dirstate error".to_string(),
306 306 )),
307 307 }
308 308 }
309 309 def write(
310 310 &self,
311 311 p1: PyObject,
312 312 p2: PyObject,
313 313 now: PyObject
314 314 ) -> PyResult<PyBytes> {
315 let now = Duration::new(now.extract(py)?, 0);
315 let now = Timestamp(now.extract(py)?);
316 316 let parents = DirstateParents {
317 317 p1: extract_node_id(py, &p1)?,
318 318 p2: extract_node_id(py, &p2)?,
319 319 };
320 320
321 321 match self.inner(py).borrow_mut().pack(parents, now) {
322 322 Ok(packed) => Ok(PyBytes::new(py, &packed)),
323 323 Err(_) => Err(PyErr::new::<exc::OSError, _>(
324 324 py,
325 325 "Dirstate error".to_string(),
326 326 )),
327 327 }
328 328 }
329 329
330 330 def filefoldmapasdict(&self) -> PyResult<PyDict> {
331 331 let dict = PyDict::new(py);
332 332 for (key, value) in
333 333 self.inner(py).borrow_mut().build_file_fold_map().iter()
334 334 {
335 335 dict.set_item(
336 336 py,
337 337 PyBytes::new(py, key.as_bytes()).into_object(),
338 338 PyBytes::new(py, value.as_bytes()).into_object(),
339 339 )?;
340 340 }
341 341 Ok(dict)
342 342 }
343 343
344 344 def __len__(&self) -> PyResult<usize> {
345 345 Ok(self.inner(py).borrow().len())
346 346 }
347 347
348 348 def __contains__(&self, key: PyObject) -> PyResult<bool> {
349 349 let key = key.extract::<PyBytes>(py)?;
350 350 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
351 351 }
352 352
353 353 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
354 354 let key = key.extract::<PyBytes>(py)?;
355 355 let key = HgPath::new(key.data(py));
356 356 match self.inner(py).borrow().get(key) {
357 357 Some(entry) => {
358 358 Ok(make_dirstate_tuple(py, entry)?)
359 359 },
360 360 None => Err(PyErr::new::<exc::KeyError, _>(
361 361 py,
362 362 String::from_utf8_lossy(key.as_bytes()),
363 363 )),
364 364 }
365 365 }
366 366
367 367 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
368 368 let leaked_ref = self.inner(py).leak_immutable();
369 369 DirstateMapKeysIterator::from_inner(
370 370 py,
371 371 unsafe { leaked_ref.map(py, |o| o.iter()) },
372 372 )
373 373 }
374 374
375 375 def items(&self) -> PyResult<DirstateMapItemsIterator> {
376 376 let leaked_ref = self.inner(py).leak_immutable();
377 377 DirstateMapItemsIterator::from_inner(
378 378 py,
379 379 unsafe { leaked_ref.map(py, |o| o.iter()) },
380 380 )
381 381 }
382 382
383 383 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
384 384 let leaked_ref = self.inner(py).leak_immutable();
385 385 DirstateMapKeysIterator::from_inner(
386 386 py,
387 387 unsafe { leaked_ref.map(py, |o| o.iter()) },
388 388 )
389 389 }
390 390
391 391 def getdirs(&self) -> PyResult<Dirs> {
392 392 // TODO don't copy, share the reference
393 393 self.inner(py).borrow_mut().set_dirs()
394 394 .map_err(|e| {
395 395 PyErr::new::<exc::ValueError, _>(py, e.to_string())
396 396 })?;
397 397 Dirs::from_inner(
398 398 py,
399 399 DirsMultiset::from_dirstate(
400 400 self.inner(py).borrow().iter(),
401 401 Some(EntryState::Removed),
402 402 )
403 403 .map_err(|e| {
404 404 PyErr::new::<exc::ValueError, _>(py, e.to_string())
405 405 })?,
406 406 )
407 407 }
408 408 def getalldirs(&self) -> PyResult<Dirs> {
409 409 // TODO don't copy, share the reference
410 410 self.inner(py).borrow_mut().set_all_dirs()
411 411 .map_err(|e| {
412 412 PyErr::new::<exc::ValueError, _>(py, e.to_string())
413 413 })?;
414 414 Dirs::from_inner(
415 415 py,
416 416 DirsMultiset::from_dirstate(
417 417 self.inner(py).borrow().iter(),
418 418 None,
419 419 ).map_err(|e| {
420 420 PyErr::new::<exc::ValueError, _>(py, e.to_string())
421 421 })?,
422 422 )
423 423 }
424 424
425 425 // TODO all copymap* methods, see docstring above
426 426 def copymapcopy(&self) -> PyResult<PyDict> {
427 427 let dict = PyDict::new(py);
428 428 for (key, value) in self.inner(py).borrow().copy_map_iter() {
429 429 dict.set_item(
430 430 py,
431 431 PyBytes::new(py, key.as_bytes()),
432 432 PyBytes::new(py, value.as_bytes()),
433 433 )?;
434 434 }
435 435 Ok(dict)
436 436 }
437 437
438 438 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
439 439 let key = key.extract::<PyBytes>(py)?;
440 440 match self.inner(py).borrow().copy_map_get(HgPath::new(key.data(py))) {
441 441 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
442 442 None => Err(PyErr::new::<exc::KeyError, _>(
443 443 py,
444 444 String::from_utf8_lossy(key.data(py)),
445 445 )),
446 446 }
447 447 }
448 448 def copymap(&self) -> PyResult<CopyMap> {
449 449 CopyMap::from_inner(py, self.clone_ref(py))
450 450 }
451 451
452 452 def copymaplen(&self) -> PyResult<usize> {
453 453 Ok(self.inner(py).borrow().copy_map_len())
454 454 }
455 455 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
456 456 let key = key.extract::<PyBytes>(py)?;
457 457 Ok(self
458 458 .inner(py)
459 459 .borrow()
460 460 .copy_map_contains_key(HgPath::new(key.data(py))))
461 461 }
462 462 def copymapget(
463 463 &self,
464 464 key: PyObject,
465 465 default: Option<PyObject>
466 466 ) -> PyResult<Option<PyObject>> {
467 467 let key = key.extract::<PyBytes>(py)?;
468 468 match self
469 469 .inner(py)
470 470 .borrow()
471 471 .copy_map_get(HgPath::new(key.data(py)))
472 472 {
473 473 Some(copy) => Ok(Some(
474 474 PyBytes::new(py, copy.as_bytes()).into_object(),
475 475 )),
476 476 None => Ok(default),
477 477 }
478 478 }
479 479 def copymapsetitem(
480 480 &self,
481 481 key: PyObject,
482 482 value: PyObject
483 483 ) -> PyResult<PyObject> {
484 484 let key = key.extract::<PyBytes>(py)?;
485 485 let value = value.extract::<PyBytes>(py)?;
486 486 self.inner(py).borrow_mut().copy_map_insert(
487 487 HgPathBuf::from_bytes(key.data(py)),
488 488 HgPathBuf::from_bytes(value.data(py)),
489 489 );
490 490 Ok(py.None())
491 491 }
492 492 def copymappop(
493 493 &self,
494 494 key: PyObject,
495 495 default: Option<PyObject>
496 496 ) -> PyResult<Option<PyObject>> {
497 497 let key = key.extract::<PyBytes>(py)?;
498 498 match self
499 499 .inner(py)
500 500 .borrow_mut()
501 501 .copy_map_remove(HgPath::new(key.data(py)))
502 502 {
503 503 Some(_) => Ok(None),
504 504 None => Ok(default),
505 505 }
506 506 }
507 507
508 508 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
509 509 let leaked_ref = self.inner(py).leak_immutable();
510 510 CopyMapKeysIterator::from_inner(
511 511 py,
512 512 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
513 513 )
514 514 }
515 515
516 516 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
517 517 let leaked_ref = self.inner(py).leak_immutable();
518 518 CopyMapItemsIterator::from_inner(
519 519 py,
520 520 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
521 521 )
522 522 }
523 523
524 524 });
525 525
526 526 impl DirstateMap {
527 527 pub fn get_inner<'a>(
528 528 &'a self,
529 529 py: Python<'a>,
530 530 ) -> Ref<'a, Box<dyn DirstateMapMethods + Send>> {
531 531 self.inner(py).borrow()
532 532 }
533 533 fn translate_key(
534 534 py: Python,
535 535 res: (&HgPathBuf, &DirstateEntry),
536 536 ) -> PyResult<Option<PyBytes>> {
537 537 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
538 538 }
539 539 fn translate_key_value(
540 540 py: Python,
541 541 res: (&HgPathBuf, &DirstateEntry),
542 542 ) -> PyResult<Option<(PyBytes, PyObject)>> {
543 543 let (f, entry) = res;
544 544 Ok(Some((
545 545 PyBytes::new(py, f.as_bytes()),
546 546 make_dirstate_tuple(py, &entry)?,
547 547 )))
548 548 }
549 549 }
550 550
551 551 py_shared_iterator!(
552 552 DirstateMapKeysIterator,
553 553 UnsafePyLeaked<StateMapIter<'static>>,
554 554 DirstateMap::translate_key,
555 555 Option<PyBytes>
556 556 );
557 557
558 558 py_shared_iterator!(
559 559 DirstateMapItemsIterator,
560 560 UnsafePyLeaked<StateMapIter<'static>>,
561 561 DirstateMap::translate_key_value,
562 562 Option<(PyBytes, PyObject)>
563 563 );
564 564
565 565 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
566 566 let bytes = obj.extract::<PyBytes>(py)?;
567 567 match bytes.data(py).try_into() {
568 568 Ok(s) => Ok(s),
569 569 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
570 570 }
571 571 }
@@ -1,163 +1,163
1 1 // parsers.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 //! Bindings for the `hg::dirstate::parsers` module provided by the
9 9 //! `hg-core` package.
10 10 //!
11 11 //! From Python, this will be seen as `mercurial.rustext.parsers`
12 12 use cpython::{
13 13 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
14 14 PythonObject, ToPyObject,
15 15 };
16 16 use hg::{
17 pack_dirstate, parse_dirstate, utils::hg_path::HgPathBuf, DirstateEntry,
18 DirstateParents, FastHashMap, PARENT_SIZE,
17 dirstate::parsers::Timestamp, pack_dirstate, parse_dirstate,
18 utils::hg_path::HgPathBuf, DirstateEntry, DirstateParents, FastHashMap,
19 PARENT_SIZE,
19 20 };
20 21 use std::convert::TryInto;
21 22
22 23 use crate::dirstate::{extract_dirstate, make_dirstate_tuple};
23 use std::time::Duration;
24 24
25 25 fn parse_dirstate_wrapper(
26 26 py: Python,
27 27 dmap: PyDict,
28 28 copymap: PyDict,
29 29 st: PyBytes,
30 30 ) -> PyResult<PyTuple> {
31 31 match parse_dirstate(st.data(py)) {
32 32 Ok((parents, entries, copies)) => {
33 33 let dirstate_map: FastHashMap<HgPathBuf, DirstateEntry> = entries
34 34 .into_iter()
35 35 .map(|(path, entry)| (path.to_owned(), entry))
36 36 .collect();
37 37 let copy_map: FastHashMap<HgPathBuf, HgPathBuf> = copies
38 38 .into_iter()
39 39 .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
40 40 .collect();
41 41
42 42 for (filename, entry) in &dirstate_map {
43 43 dmap.set_item(
44 44 py,
45 45 PyBytes::new(py, filename.as_bytes()),
46 46 make_dirstate_tuple(py, entry)?,
47 47 )?;
48 48 }
49 49 for (path, copy_path) in copy_map {
50 50 copymap.set_item(
51 51 py,
52 52 PyBytes::new(py, path.as_bytes()),
53 53 PyBytes::new(py, copy_path.as_bytes()),
54 54 )?;
55 55 }
56 56 Ok(dirstate_parents_to_pytuple(py, parents))
57 57 }
58 58 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
59 59 }
60 60 }
61 61
62 62 fn pack_dirstate_wrapper(
63 63 py: Python,
64 64 dmap: PyDict,
65 65 copymap: PyDict,
66 66 pl: PyTuple,
67 67 now: PyInt,
68 68 ) -> PyResult<PyBytes> {
69 69 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
70 70 let p1: &[u8] = p1.data(py);
71 71 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
72 72 let p2: &[u8] = p2.data(py);
73 73
74 74 let mut dirstate_map = extract_dirstate(py, &dmap)?;
75 75
76 76 let copies: Result<FastHashMap<HgPathBuf, HgPathBuf>, PyErr> = copymap
77 77 .items(py)
78 78 .iter()
79 79 .map(|(key, value)| {
80 80 Ok((
81 81 HgPathBuf::from_bytes(key.extract::<PyBytes>(py)?.data(py)),
82 82 HgPathBuf::from_bytes(value.extract::<PyBytes>(py)?.data(py)),
83 83 ))
84 84 })
85 85 .collect();
86 86
87 87 if p1.len() != PARENT_SIZE || p2.len() != PARENT_SIZE {
88 88 return Err(PyErr::new::<exc::ValueError, _>(
89 89 py,
90 90 "expected a 20-byte hash".to_string(),
91 91 ));
92 92 }
93 93
94 94 match pack_dirstate(
95 95 &mut dirstate_map,
96 96 &copies?,
97 97 DirstateParents {
98 98 p1: p1.try_into().unwrap(),
99 99 p2: p2.try_into().unwrap(),
100 100 },
101 Duration::from_secs(now.as_object().extract::<u64>(py)?),
101 Timestamp(now.as_object().extract::<u64>(py)?),
102 102 ) {
103 103 Ok(packed) => {
104 104 for (filename, entry) in dirstate_map.iter() {
105 105 dmap.set_item(
106 106 py,
107 107 PyBytes::new(py, filename.as_bytes()),
108 108 make_dirstate_tuple(py, &entry)?,
109 109 )?;
110 110 }
111 111 Ok(PyBytes::new(py, &packed))
112 112 }
113 113 Err(error) => {
114 114 Err(PyErr::new::<exc::ValueError, _>(py, error.to_string()))
115 115 }
116 116 }
117 117 }
118 118
119 119 /// Create the module, with `__package__` given from parent
120 120 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
121 121 let dotted_name = &format!("{}.parsers", package);
122 122 let m = PyModule::new(py, dotted_name)?;
123 123
124 124 m.add(py, "__package__", package)?;
125 125 m.add(py, "__doc__", "Parsers - Rust implementation")?;
126 126
127 127 m.add(
128 128 py,
129 129 "parse_dirstate",
130 130 py_fn!(
131 131 py,
132 132 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
133 133 ),
134 134 )?;
135 135 m.add(
136 136 py,
137 137 "pack_dirstate",
138 138 py_fn!(
139 139 py,
140 140 pack_dirstate_wrapper(
141 141 dmap: PyDict,
142 142 copymap: PyDict,
143 143 pl: PyTuple,
144 144 now: PyInt
145 145 )
146 146 ),
147 147 )?;
148 148
149 149 let sys = PyModule::import(py, "sys")?;
150 150 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
151 151 sys_modules.set_item(py, dotted_name, &m)?;
152 152
153 153 Ok(m)
154 154 }
155 155
156 156 pub(crate) fn dirstate_parents_to_pytuple(
157 157 py: Python,
158 158 parents: &DirstateParents,
159 159 ) -> PyTuple {
160 160 let p1 = PyBytes::new(py, parents.p1.as_bytes());
161 161 let p2 = PyBytes::new(py, parents.p2.as_bytes());
162 162 (p1, p2).to_py_object(py)
163 163 }
General Comments 0
You need to be logged in to leave comments. Login now