Mercurial > hg
comparison rust/hg-core/src/dirstate_tree/dirstate_map.rs @ 47335:ed1583a845d2
dirstate-v2: Make more APIs fallible, returning Result
When parsing becomes lazy, parse error will potentially happen in more places.
This propagates such errors to callers.
Differential Revision: https://phab.mercurial-scm.org/D10749
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Wed, 19 May 2021 13:15:00 +0200 |
parents | 18b3060fe598 |
children | 8d0260d0dbc9 |
comparison
equal
deleted
inserted
replaced
47334:18b3060fe598 | 47335:ed1583a845d2 |
---|---|
3 use std::borrow::Cow; | 3 use std::borrow::Cow; |
4 use std::convert::TryInto; | 4 use std::convert::TryInto; |
5 use std::path::PathBuf; | 5 use std::path::PathBuf; |
6 | 6 |
7 use super::on_disk; | 7 use super::on_disk; |
8 use super::on_disk::DirstateV2ParseError; | |
8 use super::path_with_basename::WithBasename; | 9 use super::path_with_basename::WithBasename; |
9 use crate::dirstate::parsers::pack_entry; | 10 use crate::dirstate::parsers::pack_entry; |
10 use crate::dirstate::parsers::packed_entry_size; | 11 use crate::dirstate::parsers::packed_entry_size; |
11 use crate::dirstate::parsers::parse_dirstate_entries; | 12 use crate::dirstate::parsers::parse_dirstate_entries; |
12 use crate::dirstate::parsers::Timestamp; | 13 use crate::dirstate::parsers::Timestamp; |
13 use crate::matchers::Matcher; | 14 use crate::matchers::Matcher; |
14 use crate::utils::hg_path::{HgPath, HgPathBuf}; | 15 use crate::utils::hg_path::{HgPath, HgPathBuf}; |
15 use crate::CopyMapIter; | 16 use crate::CopyMapIter; |
16 use crate::DirstateEntry; | 17 use crate::DirstateEntry; |
17 use crate::DirstateError; | 18 use crate::DirstateError; |
18 use crate::DirstateMapError; | |
19 use crate::DirstateParents; | 19 use crate::DirstateParents; |
20 use crate::DirstateStatus; | 20 use crate::DirstateStatus; |
21 use crate::EntryState; | 21 use crate::EntryState; |
22 use crate::FastHashMap; | 22 use crate::FastHashMap; |
23 use crate::PatternFileWarning; | 23 use crate::PatternFileWarning; |
79 } | 79 } |
80 } | 80 } |
81 | 81 |
82 pub(super) fn make_mut( | 82 pub(super) fn make_mut( |
83 &mut self, | 83 &mut self, |
84 ) -> &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>> { | 84 ) -> Result< |
85 match self { | 85 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>, |
86 ChildNodes::InMemory(nodes) => nodes, | 86 DirstateV2ParseError, |
87 > { | |
88 match self { | |
89 ChildNodes::InMemory(nodes) => Ok(nodes), | |
87 } | 90 } |
88 } | 91 } |
89 } | 92 } |
90 | 93 |
91 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> { | 94 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> { |
92 pub(super) fn get( | 95 pub(super) fn get( |
93 &self, | 96 &self, |
94 base_name: &HgPath, | 97 base_name: &HgPath, |
95 ) -> Option<NodeRef<'tree, 'on_disk>> { | 98 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> { |
96 match self { | 99 match self { |
97 ChildNodesRef::InMemory(nodes) => nodes | 100 ChildNodesRef::InMemory(nodes) => Ok(nodes |
98 .get_key_value(base_name) | 101 .get_key_value(base_name) |
99 .map(|(k, v)| NodeRef::InMemory(k, v)), | 102 .map(|(k, v)| NodeRef::InMemory(k, v))), |
100 } | 103 } |
101 } | 104 } |
102 | 105 |
103 /// Iterate in undefined order | 106 /// Iterate in undefined order |
104 pub(super) fn iter( | 107 pub(super) fn iter( |
129 ChildNodesRef::InMemory(nodes) => { | 132 ChildNodesRef::InMemory(nodes) => { |
130 let mut vec: Vec<_> = nodes | 133 let mut vec: Vec<_> = nodes |
131 .iter() | 134 .iter() |
132 .map(|(k, v)| NodeRef::InMemory(k, v)) | 135 .map(|(k, v)| NodeRef::InMemory(k, v)) |
133 .collect(); | 136 .collect(); |
137 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath { | |
138 match node { | |
139 NodeRef::InMemory(path, _node) => path.base_name(), | |
140 } | |
141 } | |
134 // `sort_unstable_by_key` doesn’t allow keys borrowing from the | 142 // `sort_unstable_by_key` doesn’t allow keys borrowing from the |
135 // value: https://github.com/rust-lang/rust/issues/34162 | 143 // value: https://github.com/rust-lang/rust/issues/34162 |
136 vec.sort_unstable_by(|a, b| a.base_name().cmp(b.base_name())); | 144 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b))); |
137 vec | 145 vec |
138 } | 146 } |
139 } | 147 } |
140 } | 148 } |
141 } | 149 } |
142 | 150 |
143 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> { | 151 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> { |
144 pub(super) fn full_path(&self) -> &'tree HgPath { | 152 pub(super) fn full_path( |
145 match self { | 153 &self, |
146 NodeRef::InMemory(path, _node) => path.full_path(), | 154 ) -> Result<&'tree HgPath, DirstateV2ParseError> { |
155 match self { | |
156 NodeRef::InMemory(path, _node) => Ok(path.full_path()), | |
147 } | 157 } |
148 } | 158 } |
149 | 159 |
150 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree | 160 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree |
151 pub(super) fn full_path_cow(&self) -> Cow<'on_disk, HgPath> { | 161 pub(super) fn full_path_cow( |
152 match self { | 162 &self, |
153 NodeRef::InMemory(path, _node) => path.full_path().clone(), | 163 ) -> Result<Cow<'on_disk, HgPath>, DirstateV2ParseError> { |
154 } | 164 match self { |
155 } | 165 NodeRef::InMemory(path, _node) => Ok(path.full_path().clone()), |
156 | 166 } |
157 pub(super) fn base_name(&self) -> &'tree HgPath { | 167 } |
158 match self { | 168 |
159 NodeRef::InMemory(path, _node) => path.base_name(), | 169 pub(super) fn base_name( |
160 } | 170 &self, |
161 } | 171 ) -> Result<&'tree HgPath, DirstateV2ParseError> { |
162 | 172 match self { |
163 pub(super) fn children(&self) -> ChildNodesRef<'tree, 'on_disk> { | 173 NodeRef::InMemory(path, _node) => Ok(path.base_name()), |
164 match self { | 174 } |
165 NodeRef::InMemory(_path, node) => node.children.as_ref(), | 175 } |
166 } | 176 |
167 } | 177 pub(super) fn children( |
168 | 178 &self, |
169 pub(super) fn copy_source(&self) -> Option<&'tree HgPath> { | 179 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> { |
180 match self { | |
181 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()), | |
182 } | |
183 } | |
184 | |
185 pub(super) fn has_copy_source(&self) -> bool { | |
186 match self { | |
187 NodeRef::InMemory(_path, node) => node.copy_source.is_some(), | |
188 } | |
189 } | |
190 | |
191 pub(super) fn copy_source( | |
192 &self, | |
193 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> { | |
170 match self { | 194 match self { |
171 NodeRef::InMemory(_path, node) => { | 195 NodeRef::InMemory(_path, node) => { |
172 node.copy_source.as_ref().map(|s| &**s) | 196 Ok(node.copy_source.as_ref().map(|s| &**s)) |
173 } | 197 } |
174 } | 198 } |
175 } | 199 } |
176 | 200 |
177 pub(super) fn has_entry(&self) -> bool { | 201 pub(super) fn has_entry(&self) -> bool { |
178 match self { | 202 match self { |
179 NodeRef::InMemory(_path, node) => node.entry.is_some(), | 203 NodeRef::InMemory(_path, node) => node.entry.is_some(), |
180 } | 204 } |
181 } | 205 } |
182 | 206 |
183 pub(super) fn entry(&self) -> Option<DirstateEntry> { | 207 pub(super) fn entry( |
184 match self { | 208 &self, |
185 NodeRef::InMemory(_path, node) => node.entry, | 209 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { |
186 } | 210 match self { |
187 } | 211 NodeRef::InMemory(_path, node) => Ok(node.entry), |
188 pub(super) fn state(&self) -> Option<EntryState> { | 212 } |
213 } | |
214 | |
215 pub(super) fn state( | |
216 &self, | |
217 ) -> Result<Option<EntryState>, DirstateV2ParseError> { | |
189 match self { | 218 match self { |
190 NodeRef::InMemory(_path, node) => { | 219 NodeRef::InMemory(_path, node) => { |
191 node.entry.as_ref().map(|entry| entry.state) | 220 Ok(node.entry.as_ref().map(|entry| entry.state)) |
192 } | 221 } |
193 } | 222 } |
194 } | 223 } |
195 | 224 |
196 pub(super) fn tracked_descendants_count(&self) -> u32 { | 225 pub(super) fn tracked_descendants_count(&self) -> u32 { |
251 |ancestor| { | 280 |ancestor| { |
252 if tracked { | 281 if tracked { |
253 ancestor.tracked_descendants_count += 1 | 282 ancestor.tracked_descendants_count += 1 |
254 } | 283 } |
255 }, | 284 }, |
256 ); | 285 )?; |
257 assert!( | 286 assert!( |
258 node.entry.is_none(), | 287 node.entry.is_none(), |
259 "duplicate dirstate entry in read" | 288 "duplicate dirstate entry in read" |
260 ); | 289 ); |
261 assert!( | 290 assert!( |
266 node.copy_source = copy_source.map(Cow::Borrowed); | 295 node.copy_source = copy_source.map(Cow::Borrowed); |
267 map.nodes_with_entry_count += 1; | 296 map.nodes_with_entry_count += 1; |
268 if copy_source.is_some() { | 297 if copy_source.is_some() { |
269 map.nodes_with_copy_source_count += 1 | 298 map.nodes_with_copy_source_count += 1 |
270 } | 299 } |
300 Ok(()) | |
271 }, | 301 }, |
272 )?; | 302 )?; |
273 let parents = Some(parents.clone()); | 303 let parents = Some(parents.clone()); |
274 | 304 |
275 Ok((map, parents)) | 305 Ok((map, parents)) |
276 } | 306 } |
277 | 307 |
278 fn get_node<'tree>( | 308 fn get_node<'tree>( |
279 &'tree self, | 309 &'tree self, |
280 path: &HgPath, | 310 path: &HgPath, |
281 ) -> Option<NodeRef<'tree, 'on_disk>> { | 311 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> { |
282 let mut children = self.root.as_ref(); | 312 let mut children = self.root.as_ref(); |
283 let mut components = path.components(); | 313 let mut components = path.components(); |
284 let mut component = | 314 let mut component = |
285 components.next().expect("expected at least one components"); | 315 components.next().expect("expected at least one components"); |
286 loop { | 316 loop { |
287 let child = children.get(component)?; | 317 if let Some(child) = children.get(component)? { |
288 if let Some(next_component) = components.next() { | 318 if let Some(next_component) = components.next() { |
289 component = next_component; | 319 component = next_component; |
290 children = child.children(); | 320 children = child.children()?; |
321 } else { | |
322 return Ok(Some(child)); | |
323 } | |
291 } else { | 324 } else { |
292 return Some(child); | 325 return Ok(None); |
293 } | 326 } |
294 } | 327 } |
295 } | 328 } |
296 | 329 |
297 /// Returns a mutable reference to the node at `path` if it exists | 330 /// Returns a mutable reference to the node at `path` if it exists |
299 /// This takes `root` instead of `&mut self` so that callers can mutate | 332 /// This takes `root` instead of `&mut self` so that callers can mutate |
300 /// other fields while the returned borrow is still valid | 333 /// other fields while the returned borrow is still valid |
301 fn get_node_mut<'tree>( | 334 fn get_node_mut<'tree>( |
302 root: &'tree mut ChildNodes<'on_disk>, | 335 root: &'tree mut ChildNodes<'on_disk>, |
303 path: &HgPath, | 336 path: &HgPath, |
304 ) -> Option<&'tree mut Node<'on_disk>> { | 337 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> { |
305 let mut children = root; | 338 let mut children = root; |
306 let mut components = path.components(); | 339 let mut components = path.components(); |
307 let mut component = | 340 let mut component = |
308 components.next().expect("expected at least one components"); | 341 components.next().expect("expected at least one components"); |
309 loop { | 342 loop { |
310 let child = children.make_mut().get_mut(component)?; | 343 if let Some(child) = children.make_mut()?.get_mut(component) { |
311 if let Some(next_component) = components.next() { | 344 if let Some(next_component) = components.next() { |
312 component = next_component; | 345 component = next_component; |
313 children = &mut child.children; | 346 children = &mut child.children; |
347 } else { | |
348 return Ok(Some(child)); | |
349 } | |
314 } else { | 350 } else { |
315 return Some(child); | 351 return Ok(None); |
316 } | 352 } |
317 } | 353 } |
318 } | 354 } |
319 | 355 |
320 fn get_or_insert_node<'tree, 'path>( | 356 fn get_or_insert_node<'tree, 'path>( |
322 path: &'path HgPath, | 358 path: &'path HgPath, |
323 to_cow: impl Fn( | 359 to_cow: impl Fn( |
324 WithBasename<&'path HgPath>, | 360 WithBasename<&'path HgPath>, |
325 ) -> WithBasename<Cow<'on_disk, HgPath>>, | 361 ) -> WithBasename<Cow<'on_disk, HgPath>>, |
326 mut each_ancestor: impl FnMut(&mut Node), | 362 mut each_ancestor: impl FnMut(&mut Node), |
327 ) -> &'tree mut Node<'on_disk> { | 363 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> { |
328 let mut child_nodes = root; | 364 let mut child_nodes = root; |
329 let mut inclusive_ancestor_paths = | 365 let mut inclusive_ancestor_paths = |
330 WithBasename::inclusive_ancestors_of(path); | 366 WithBasename::inclusive_ancestors_of(path); |
331 let mut ancestor_path = inclusive_ancestor_paths | 367 let mut ancestor_path = inclusive_ancestor_paths |
332 .next() | 368 .next() |
334 loop { | 370 loop { |
335 // TODO: can we avoid allocating an owned key in cases where the | 371 // TODO: can we avoid allocating an owned key in cases where the |
336 // map already contains that key, without introducing double | 372 // map already contains that key, without introducing double |
337 // lookup? | 373 // lookup? |
338 let child_node = child_nodes | 374 let child_node = child_nodes |
339 .make_mut() | 375 .make_mut()? |
340 .entry(to_cow(ancestor_path)) | 376 .entry(to_cow(ancestor_path)) |
341 .or_default(); | 377 .or_default(); |
342 if let Some(next) = inclusive_ancestor_paths.next() { | 378 if let Some(next) = inclusive_ancestor_paths.next() { |
343 each_ancestor(child_node); | 379 each_ancestor(child_node); |
344 ancestor_path = next; | 380 ancestor_path = next; |
345 child_nodes = &mut child_node.children; | 381 child_nodes = &mut child_node.children; |
346 } else { | 382 } else { |
347 return child_node; | 383 return Ok(child_node); |
348 } | 384 } |
349 } | 385 } |
350 } | 386 } |
351 | 387 |
352 fn add_or_remove_file( | 388 fn add_or_remove_file( |
353 &mut self, | 389 &mut self, |
354 path: &HgPath, | 390 path: &HgPath, |
355 old_state: EntryState, | 391 old_state: EntryState, |
356 new_entry: DirstateEntry, | 392 new_entry: DirstateEntry, |
357 ) { | 393 ) -> Result<(), DirstateV2ParseError> { |
358 let tracked_count_increment = | 394 let tracked_count_increment = |
359 match (old_state.is_tracked(), new_entry.state.is_tracked()) { | 395 match (old_state.is_tracked(), new_entry.state.is_tracked()) { |
360 (false, true) => 1, | 396 (false, true) => 1, |
361 (true, false) => -1, | 397 (true, false) => -1, |
362 _ => 0, | 398 _ => 0, |
374 1 => ancestor.tracked_descendants_count += 1, | 410 1 => ancestor.tracked_descendants_count += 1, |
375 -1 => ancestor.tracked_descendants_count -= 1, | 411 -1 => ancestor.tracked_descendants_count -= 1, |
376 _ => {} | 412 _ => {} |
377 } | 413 } |
378 }, | 414 }, |
379 ); | 415 )?; |
380 if node.entry.is_none() { | 416 if node.entry.is_none() { |
381 self.nodes_with_entry_count += 1 | 417 self.nodes_with_entry_count += 1 |
382 } | 418 } |
383 node.entry = Some(new_entry) | 419 node.entry = Some(new_entry); |
420 Ok(()) | |
384 } | 421 } |
385 | 422 |
386 fn iter_nodes<'tree>( | 423 fn iter_nodes<'tree>( |
387 &'tree self, | 424 &'tree self, |
388 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> + 'tree { | 425 ) -> impl Iterator< |
426 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>, | |
427 > + 'tree { | |
389 // Depth first tree traversal. | 428 // Depth first tree traversal. |
390 // | 429 // |
391 // If we could afford internal iteration and recursion, | 430 // If we could afford internal iteration and recursion, |
392 // this would look like: | 431 // this would look like: |
393 // | 432 // |
407 // call stack. Use an explicit stack instead: | 446 // call stack. Use an explicit stack instead: |
408 let mut stack = Vec::new(); | 447 let mut stack = Vec::new(); |
409 let mut iter = self.root.as_ref().iter(); | 448 let mut iter = self.root.as_ref().iter(); |
410 std::iter::from_fn(move || { | 449 std::iter::from_fn(move || { |
411 while let Some(child_node) = iter.next() { | 450 while let Some(child_node) = iter.next() { |
451 let children = match child_node.children() { | |
452 Ok(children) => children, | |
453 Err(error) => return Some(Err(error)), | |
454 }; | |
412 // Pseudo-recursion | 455 // Pseudo-recursion |
413 let new_iter = child_node.children().iter(); | 456 let new_iter = children.iter(); |
414 let old_iter = std::mem::replace(&mut iter, new_iter); | 457 let old_iter = std::mem::replace(&mut iter, new_iter); |
415 stack.push((child_node, old_iter)); | 458 stack.push((child_node, old_iter)); |
416 } | 459 } |
417 // Found the end of a `children.iter()` iterator. | 460 // Found the end of a `children.iter()` iterator. |
418 if let Some((child_node, next_iter)) = stack.pop() { | 461 if let Some((child_node, next_iter)) = stack.pop() { |
419 // "Return" from pseudo-recursion by restoring state from the | 462 // "Return" from pseudo-recursion by restoring state from the |
420 // explicit stack | 463 // explicit stack |
421 iter = next_iter; | 464 iter = next_iter; |
422 | 465 |
423 Some(child_node) | 466 Some(Ok(child_node)) |
424 } else { | 467 } else { |
425 // Reached the bottom of the stack, we’re done | 468 // Reached the bottom of the stack, we’re done |
426 None | 469 None |
427 } | 470 } |
428 }) | 471 }) |
429 } | 472 } |
430 | 473 |
431 fn clear_known_ambiguous_mtimes(&mut self, paths: &[impl AsRef<HgPath>]) { | 474 fn clear_known_ambiguous_mtimes( |
475 &mut self, | |
476 paths: &[impl AsRef<HgPath>], | |
477 ) -> Result<(), DirstateV2ParseError> { | |
432 for path in paths { | 478 for path in paths { |
433 if let Some(node) = | 479 if let Some(node) = |
434 Self::get_node_mut(&mut self.root, path.as_ref()) | 480 Self::get_node_mut(&mut self.root, path.as_ref())? |
435 { | 481 { |
436 if let Some(entry) = node.entry.as_mut() { | 482 if let Some(entry) = node.entry.as_mut() { |
437 entry.clear_mtime(); | 483 entry.clear_mtime(); |
438 } | 484 } |
439 } | 485 } |
440 } | 486 } |
441 } | 487 Ok(()) |
488 } | |
489 | |
490 /// Return a faillilble iterator of full paths of nodes that have an | |
491 /// `entry` for which the given `predicate` returns true. | |
492 /// | |
493 /// Fallibility means that each iterator item is a `Result`, which may | |
494 /// indicate a parse error of the on-disk dirstate-v2 format. Such errors | |
495 /// should only happen if Mercurial is buggy or a repository is corrupted. | |
496 fn filter_full_paths<'tree>( | |
497 &'tree self, | |
498 predicate: impl Fn(&DirstateEntry) -> bool + 'tree, | |
499 ) -> impl Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + 'tree | |
500 { | |
501 filter_map_results(self.iter_nodes(), move |node| { | |
502 if let Some(entry) = node.entry()? { | |
503 if predicate(&entry) { | |
504 return Ok(Some(node.full_path()?)); | |
505 } | |
506 } | |
507 Ok(None) | |
508 }) | |
509 } | |
510 } | |
511 | |
512 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s. | |
513 /// | |
514 /// The callback is only called for incoming `Ok` values. Errors are passed | |
515 /// through as-is. In order to let it use the `?` operator the callback is | |
516 /// expected to return a `Result` of `Option`, instead of an `Option` of | |
517 /// `Result`. | |
518 fn filter_map_results<'a, I, F, A, B, E>( | |
519 iter: I, | |
520 f: F, | |
521 ) -> impl Iterator<Item = Result<B, E>> + 'a | |
522 where | |
523 I: Iterator<Item = Result<A, E>> + 'a, | |
524 F: Fn(A) -> Result<Option<B>, E> + 'a, | |
525 { | |
526 iter.filter_map(move |result| match result { | |
527 Ok(node) => f(node).transpose(), | |
528 Err(e) => Some(Err(e)), | |
529 }) | |
442 } | 530 } |
443 | 531 |
444 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> { | 532 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> { |
445 fn clear(&mut self) { | 533 fn clear(&mut self) { |
446 self.root = Default::default(); | 534 self.root = Default::default(); |
451 fn add_file( | 539 fn add_file( |
452 &mut self, | 540 &mut self, |
453 filename: &HgPath, | 541 filename: &HgPath, |
454 old_state: EntryState, | 542 old_state: EntryState, |
455 entry: DirstateEntry, | 543 entry: DirstateEntry, |
456 ) -> Result<(), DirstateMapError> { | 544 ) -> Result<(), DirstateError> { |
457 self.add_or_remove_file(filename, old_state, entry); | 545 Ok(self.add_or_remove_file(filename, old_state, entry)?) |
458 Ok(()) | |
459 } | 546 } |
460 | 547 |
461 fn remove_file( | 548 fn remove_file( |
462 &mut self, | 549 &mut self, |
463 filename: &HgPath, | 550 filename: &HgPath, |
464 old_state: EntryState, | 551 old_state: EntryState, |
465 size: i32, | 552 size: i32, |
466 ) -> Result<(), DirstateMapError> { | 553 ) -> Result<(), DirstateError> { |
467 let entry = DirstateEntry { | 554 let entry = DirstateEntry { |
468 state: EntryState::Removed, | 555 state: EntryState::Removed, |
469 mode: 0, | 556 mode: 0, |
470 size, | 557 size, |
471 mtime: 0, | 558 mtime: 0, |
472 }; | 559 }; |
473 self.add_or_remove_file(filename, old_state, entry); | 560 Ok(self.add_or_remove_file(filename, old_state, entry)?) |
474 Ok(()) | |
475 } | 561 } |
476 | 562 |
477 fn drop_file( | 563 fn drop_file( |
478 &mut self, | 564 &mut self, |
479 filename: &HgPath, | 565 filename: &HgPath, |
480 old_state: EntryState, | 566 old_state: EntryState, |
481 ) -> Result<bool, DirstateMapError> { | 567 ) -> Result<bool, DirstateError> { |
482 struct Dropped { | 568 struct Dropped { |
483 was_tracked: bool, | 569 was_tracked: bool, |
484 had_entry: bool, | 570 had_entry: bool, |
485 had_copy_source: bool, | 571 had_copy_source: bool, |
486 } | 572 } |
487 fn recur(nodes: &mut ChildNodes, path: &HgPath) -> Option<Dropped> { | 573 fn recur( |
574 nodes: &mut ChildNodes, | |
575 path: &HgPath, | |
576 ) -> Result<Option<Dropped>, DirstateV2ParseError> { | |
488 let (first_path_component, rest_of_path) = | 577 let (first_path_component, rest_of_path) = |
489 path.split_first_component(); | 578 path.split_first_component(); |
490 let node = nodes.make_mut().get_mut(first_path_component)?; | 579 let node = if let Some(node) = |
580 nodes.make_mut()?.get_mut(first_path_component) | |
581 { | |
582 node | |
583 } else { | |
584 return Ok(None); | |
585 }; | |
491 let dropped; | 586 let dropped; |
492 if let Some(rest) = rest_of_path { | 587 if let Some(rest) = rest_of_path { |
493 dropped = recur(&mut node.children, rest)?; | 588 if let Some(d) = recur(&mut node.children, rest)? { |
494 if dropped.was_tracked { | 589 dropped = d; |
495 node.tracked_descendants_count -= 1; | 590 if dropped.was_tracked { |
591 node.tracked_descendants_count -= 1; | |
592 } | |
593 } else { | |
594 return Ok(None); | |
496 } | 595 } |
497 } else { | 596 } else { |
498 dropped = Dropped { | 597 dropped = Dropped { |
499 was_tracked: node | 598 was_tracked: node |
500 .entry | 599 .entry |
508 // parent nodes, remove a node if it just became empty. | 607 // parent nodes, remove a node if it just became empty. |
509 if node.entry.is_none() | 608 if node.entry.is_none() |
510 && node.copy_source.is_none() | 609 && node.copy_source.is_none() |
511 && node.children.is_empty() | 610 && node.children.is_empty() |
512 { | 611 { |
513 nodes.make_mut().remove(first_path_component); | 612 nodes.make_mut()?.remove(first_path_component); |
514 } | 613 } |
515 Some(dropped) | 614 Ok(Some(dropped)) |
516 } | 615 } |
517 | 616 |
518 if let Some(dropped) = recur(&mut self.root, filename) { | 617 if let Some(dropped) = recur(&mut self.root, filename)? { |
519 if dropped.had_entry { | 618 if dropped.had_entry { |
520 self.nodes_with_entry_count -= 1 | 619 self.nodes_with_entry_count -= 1 |
521 } | 620 } |
522 if dropped.had_copy_source { | 621 if dropped.had_copy_source { |
523 self.nodes_with_copy_source_count -= 1 | 622 self.nodes_with_copy_source_count -= 1 |
527 debug_assert!(!old_state.is_tracked()); | 626 debug_assert!(!old_state.is_tracked()); |
528 Ok(false) | 627 Ok(false) |
529 } | 628 } |
530 } | 629 } |
531 | 630 |
532 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) { | 631 fn clear_ambiguous_times( |
632 &mut self, | |
633 filenames: Vec<HgPathBuf>, | |
634 now: i32, | |
635 ) -> Result<(), DirstateV2ParseError> { | |
533 for filename in filenames { | 636 for filename in filenames { |
534 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) { | 637 if let Some(node) = Self::get_node_mut(&mut self.root, &filename)? |
638 { | |
535 if let Some(entry) = node.entry.as_mut() { | 639 if let Some(entry) = node.entry.as_mut() { |
536 entry.clear_ambiguous_mtime(now); | 640 entry.clear_ambiguous_mtime(now); |
537 } | 641 } |
538 } | 642 } |
539 } | 643 } |
540 } | 644 Ok(()) |
541 | 645 } |
542 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool { | 646 |
543 self.get_node(key) | 647 fn non_normal_entries_contains( |
544 .and_then(|node| node.entry()) | 648 &mut self, |
545 .map_or(false, |entry| entry.is_non_normal()) | 649 key: &HgPath, |
650 ) -> Result<bool, DirstateV2ParseError> { | |
651 Ok(if let Some(node) = self.get_node(key)? { | |
652 node.entry()?.map_or(false, |entry| entry.is_non_normal()) | |
653 } else { | |
654 false | |
655 }) | |
546 } | 656 } |
547 | 657 |
548 fn non_normal_entries_remove(&mut self, _key: &HgPath) { | 658 fn non_normal_entries_remove(&mut self, _key: &HgPath) { |
549 // Do nothing, this `DirstateMap` does not have a separate "non normal | 659 // Do nothing, this `DirstateMap` does not have a separate "non normal |
550 // entries" set that need to be kept up to date | 660 // entries" set that need to be kept up to date |
551 } | 661 } |
552 | 662 |
553 fn non_normal_or_other_parent_paths( | 663 fn non_normal_or_other_parent_paths( |
554 &mut self, | 664 &mut self, |
555 ) -> Box<dyn Iterator<Item = &HgPath> + '_> { | 665 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_> |
556 Box::new(self.iter_nodes().filter_map(|node| { | 666 { |
557 node.entry() | 667 Box::new(self.filter_full_paths(|entry| { |
558 .filter(|entry| { | 668 entry.is_non_normal() || entry.is_from_other_parent() |
559 entry.is_non_normal() || entry.is_from_other_parent() | |
560 }) | |
561 .map(|_| node.full_path()) | |
562 })) | 669 })) |
563 } | 670 } |
564 | 671 |
565 fn set_non_normal_other_parent_entries(&mut self, _force: bool) { | 672 fn set_non_normal_other_parent_entries(&mut self, _force: bool) { |
566 // Do nothing, this `DirstateMap` does not have a separate "non normal | 673 // Do nothing, this `DirstateMap` does not have a separate "non normal |
567 // entries" and "from other parent" sets that need to be recomputed | 674 // entries" and "from other parent" sets that need to be recomputed |
568 } | 675 } |
569 | 676 |
570 fn iter_non_normal_paths( | 677 fn iter_non_normal_paths( |
571 &mut self, | 678 &mut self, |
572 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> { | 679 ) -> Box< |
680 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, | |
681 > { | |
573 self.iter_non_normal_paths_panic() | 682 self.iter_non_normal_paths_panic() |
574 } | 683 } |
575 | 684 |
576 fn iter_non_normal_paths_panic( | 685 fn iter_non_normal_paths_panic( |
577 &self, | 686 &self, |
578 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> { | 687 ) -> Box< |
579 Box::new(self.iter_nodes().filter_map(|node| { | 688 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, |
580 node.entry() | 689 > { |
581 .filter(|entry| entry.is_non_normal()) | 690 Box::new(self.filter_full_paths(|entry| entry.is_non_normal())) |
582 .map(|_| node.full_path()) | |
583 })) | |
584 } | 691 } |
585 | 692 |
586 fn iter_other_parent_paths( | 693 fn iter_other_parent_paths( |
587 &mut self, | 694 &mut self, |
588 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> { | 695 ) -> Box< |
589 Box::new(self.iter_nodes().filter_map(|node| { | 696 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, |
590 node.entry() | 697 > { |
591 .filter(|entry| entry.is_from_other_parent()) | 698 Box::new(self.filter_full_paths(|entry| entry.is_from_other_parent())) |
592 .map(|_| node.full_path()) | |
593 })) | |
594 } | 699 } |
595 | 700 |
596 fn has_tracked_dir( | 701 fn has_tracked_dir( |
597 &mut self, | 702 &mut self, |
598 directory: &HgPath, | 703 directory: &HgPath, |
599 ) -> Result<bool, DirstateMapError> { | 704 ) -> Result<bool, DirstateError> { |
600 if let Some(node) = self.get_node(directory) { | 705 if let Some(node) = self.get_node(directory)? { |
601 // A node without a `DirstateEntry` was created to hold child | 706 // A node without a `DirstateEntry` was created to hold child |
602 // nodes, and is therefore a directory. | 707 // nodes, and is therefore a directory. |
603 Ok(!node.has_entry() && node.tracked_descendants_count() > 0) | 708 Ok(!node.has_entry() && node.tracked_descendants_count() > 0) |
604 } else { | 709 } else { |
605 Ok(false) | 710 Ok(false) |
606 } | 711 } |
607 } | 712 } |
608 | 713 |
609 fn has_dir( | 714 fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> { |
610 &mut self, | 715 if let Some(node) = self.get_node(directory)? { |
611 directory: &HgPath, | |
612 ) -> Result<bool, DirstateMapError> { | |
613 if let Some(node) = self.get_node(directory) { | |
614 // A node without a `DirstateEntry` was created to hold child | 716 // A node without a `DirstateEntry` was created to hold child |
615 // nodes, and is therefore a directory. | 717 // nodes, and is therefore a directory. |
616 Ok(!node.has_entry()) | 718 Ok(!node.has_entry()) |
617 } else { | 719 } else { |
618 Ok(false) | 720 Ok(false) |
629 let mut ambiguous_mtimes = Vec::new(); | 731 let mut ambiguous_mtimes = Vec::new(); |
630 // Optizimation (to be measured?): pre-compute size to avoid `Vec` | 732 // Optizimation (to be measured?): pre-compute size to avoid `Vec` |
631 // reallocations | 733 // reallocations |
632 let mut size = parents.as_bytes().len(); | 734 let mut size = parents.as_bytes().len(); |
633 for node in self.iter_nodes() { | 735 for node in self.iter_nodes() { |
634 if let Some(entry) = node.entry() { | 736 let node = node?; |
737 if let Some(entry) = node.entry()? { | |
635 size += | 738 size += |
636 packed_entry_size(node.full_path(), node.copy_source()); | 739 packed_entry_size(node.full_path()?, node.copy_source()?); |
637 if entry.mtime_is_ambiguous(now) { | 740 if entry.mtime_is_ambiguous(now) { |
638 ambiguous_mtimes.push(node.full_path_cow()) | 741 ambiguous_mtimes.push(node.full_path_cow()?) |
639 } | 742 } |
640 } | 743 } |
641 } | 744 } |
642 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes); | 745 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes)?; |
643 | 746 |
644 let mut packed = Vec::with_capacity(size); | 747 let mut packed = Vec::with_capacity(size); |
645 packed.extend(parents.as_bytes()); | 748 packed.extend(parents.as_bytes()); |
646 | 749 |
647 for node in self.iter_nodes() { | 750 for node in self.iter_nodes() { |
648 if let Some(entry) = node.entry() { | 751 let node = node?; |
752 if let Some(entry) = node.entry()? { | |
649 pack_entry( | 753 pack_entry( |
650 node.full_path(), | 754 node.full_path()?, |
651 &entry, | 755 &entry, |
652 node.copy_source(), | 756 node.copy_source()?, |
653 &mut packed, | 757 &mut packed, |
654 ); | 758 ); |
655 } | 759 } |
656 } | 760 } |
657 Ok(packed) | 761 Ok(packed) |
665 ) -> Result<Vec<u8>, DirstateError> { | 769 ) -> Result<Vec<u8>, DirstateError> { |
666 // TODO: how do we want to handle this in 2038? | 770 // TODO: how do we want to handle this in 2038? |
667 let now: i32 = now.0.try_into().expect("time overflow"); | 771 let now: i32 = now.0.try_into().expect("time overflow"); |
668 let mut paths = Vec::new(); | 772 let mut paths = Vec::new(); |
669 for node in self.iter_nodes() { | 773 for node in self.iter_nodes() { |
670 if let Some(entry) = node.entry() { | 774 let node = node?; |
775 if let Some(entry) = node.entry()? { | |
671 if entry.mtime_is_ambiguous(now) { | 776 if entry.mtime_is_ambiguous(now) { |
672 paths.push(node.full_path_cow()) | 777 paths.push(node.full_path_cow()?) |
673 } | 778 } |
674 } | 779 } |
675 } | 780 } |
676 // Borrow of `self` ends here since we collect cloned paths | 781 // Borrow of `self` ends here since we collect cloned paths |
677 | 782 |
678 self.clear_known_ambiguous_mtimes(&paths); | 783 self.clear_known_ambiguous_mtimes(&paths)?; |
679 | 784 |
680 on_disk::write(self, parents) | 785 on_disk::write(self, parents) |
681 } | 786 } |
682 | 787 |
683 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> { | 788 fn set_all_dirs(&mut self) -> Result<(), DirstateError> { |
684 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that | 789 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that |
685 // needs to be recomputed | 790 // needs to be recomputed |
686 Ok(()) | 791 Ok(()) |
687 } | 792 } |
688 | 793 |
689 fn set_dirs(&mut self) -> Result<(), DirstateMapError> { | 794 fn set_dirs(&mut self) -> Result<(), DirstateError> { |
690 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs | 795 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs |
691 // to be recomputed | 796 // to be recomputed |
692 Ok(()) | 797 Ok(()) |
693 } | 798 } |
694 | 799 |
706 fn copy_map_len(&self) -> usize { | 811 fn copy_map_len(&self) -> usize { |
707 self.nodes_with_copy_source_count as usize | 812 self.nodes_with_copy_source_count as usize |
708 } | 813 } |
709 | 814 |
710 fn copy_map_iter(&self) -> CopyMapIter<'_> { | 815 fn copy_map_iter(&self) -> CopyMapIter<'_> { |
711 Box::new(self.iter_nodes().filter_map(|node| { | 816 Box::new(filter_map_results(self.iter_nodes(), |node| { |
712 node.copy_source() | 817 Ok(if let Some(source) = node.copy_source()? { |
713 .map(|copy_source| (node.full_path(), copy_source)) | 818 Some((node.full_path()?, source)) |
819 } else { | |
820 None | |
821 }) | |
714 })) | 822 })) |
715 } | 823 } |
716 | 824 |
717 fn copy_map_contains_key(&self, key: &HgPath) -> bool { | 825 fn copy_map_contains_key( |
718 if let Some(node) = self.get_node(key) { | 826 &self, |
719 node.copy_source().is_some() | 827 key: &HgPath, |
828 ) -> Result<bool, DirstateV2ParseError> { | |
829 Ok(if let Some(node) = self.get_node(key)? { | |
830 node.has_copy_source() | |
720 } else { | 831 } else { |
721 false | 832 false |
722 } | 833 }) |
723 } | 834 } |
724 | 835 |
725 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> { | 836 fn copy_map_get( |
726 self.get_node(key)?.copy_source() | 837 &self, |
727 } | 838 key: &HgPath, |
728 | 839 ) -> Result<Option<&HgPath>, DirstateV2ParseError> { |
729 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> { | 840 if let Some(node) = self.get_node(key)? { |
841 if let Some(source) = node.copy_source()? { | |
842 return Ok(Some(source)); | |
843 } | |
844 } | |
845 Ok(None) | |
846 } | |
847 | |
848 fn copy_map_remove( | |
849 &mut self, | |
850 key: &HgPath, | |
851 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | |
730 let count = &mut self.nodes_with_copy_source_count; | 852 let count = &mut self.nodes_with_copy_source_count; |
731 Self::get_node_mut(&mut self.root, key).and_then(|node| { | 853 Ok(Self::get_node_mut(&mut self.root, key)?.and_then(|node| { |
732 if node.copy_source.is_some() { | 854 if node.copy_source.is_some() { |
733 *count -= 1 | 855 *count -= 1 |
734 } | 856 } |
735 node.copy_source.take().map(Cow::into_owned) | 857 node.copy_source.take().map(Cow::into_owned) |
736 }) | 858 })) |
737 } | 859 } |
738 | 860 |
739 fn copy_map_insert( | 861 fn copy_map_insert( |
740 &mut self, | 862 &mut self, |
741 key: HgPathBuf, | 863 key: HgPathBuf, |
742 value: HgPathBuf, | 864 value: HgPathBuf, |
743 ) -> Option<HgPathBuf> { | 865 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { |
744 let node = Self::get_or_insert_node( | 866 let node = Self::get_or_insert_node( |
745 &mut self.root, | 867 &mut self.root, |
746 &key, | 868 &key, |
747 WithBasename::to_cow_owned, | 869 WithBasename::to_cow_owned, |
748 |_ancestor| {}, | 870 |_ancestor| {}, |
749 ); | 871 )?; |
750 if node.copy_source.is_none() { | 872 if node.copy_source.is_none() { |
751 self.nodes_with_copy_source_count += 1 | 873 self.nodes_with_copy_source_count += 1 |
752 } | 874 } |
753 node.copy_source.replace(value.into()).map(Cow::into_owned) | 875 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned)) |
754 } | 876 } |
755 | 877 |
756 fn len(&self) -> usize { | 878 fn len(&self) -> usize { |
757 self.nodes_with_entry_count as usize | 879 self.nodes_with_entry_count as usize |
758 } | 880 } |
759 | 881 |
760 fn contains_key(&self, key: &HgPath) -> bool { | 882 fn contains_key( |
761 self.get(key).is_some() | 883 &self, |
762 } | 884 key: &HgPath, |
763 | 885 ) -> Result<bool, DirstateV2ParseError> { |
764 fn get(&self, key: &HgPath) -> Option<DirstateEntry> { | 886 Ok(self.get(key)?.is_some()) |
765 self.get_node(key)?.entry() | 887 } |
888 | |
889 fn get( | |
890 &self, | |
891 key: &HgPath, | |
892 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { | |
893 Ok(if let Some(node) = self.get_node(key)? { | |
894 node.entry()? | |
895 } else { | |
896 None | |
897 }) | |
766 } | 898 } |
767 | 899 |
768 fn iter(&self) -> StateMapIter<'_> { | 900 fn iter(&self) -> StateMapIter<'_> { |
769 Box::new(self.iter_nodes().filter_map(|node| { | 901 Box::new(filter_map_results(self.iter_nodes(), |node| { |
770 node.entry().map(|entry| (node.full_path(), entry)) | 902 Ok(if let Some(entry) = node.entry()? { |
903 Some((node.full_path()?, entry)) | |
904 } else { | |
905 None | |
906 }) | |
771 })) | 907 })) |
772 } | 908 } |
773 } | 909 } |