repo_path: make RepoPath::components() return iterator

This allows us to change the backing type from Vec<String> to String.
This commit is contained in:
Yuya Nishihara 2023-11-25 19:09:58 +09:00
parent aba8c640be
commit 974a6870b3
6 changed files with 61 additions and 45 deletions

View file

@ -301,9 +301,10 @@ fn create_parent_dirs(
working_copy_path: &Path,
repo_path: &RepoPath,
) -> Result<bool, CheckoutError> {
let (_, dir_components) = repo_path
.components()
.split_last()
// TODO: make RepoPath::parent() cheap and use it instead
let mut dir_components = repo_path.components();
dir_components
.next_back()
.expect("repo path shouldn't be root");
let mut dir_path = working_copy_path.to_owned();
for c in dir_components {

View file

@ -19,7 +19,7 @@ use std::iter;
use tracing::instrument;
use crate::repo_path::{RepoPath, RepoPathComponentBuf};
use crate::repo_path::{RepoPath, RepoPathComponentBuf, RepoPathComponentsIter};
#[derive(PartialEq, Eq, Debug)]
pub enum Visit {
@ -146,13 +146,13 @@ impl Matcher for PrefixMatcher {
}
fn visit(&self, dir: &RepoPath) -> Visit {
for (sub, tail_components) in self.tree.walk_to(dir) {
for (sub, mut tail_components) in self.tree.walk_to(dir) {
// 'is_file' means the current path matches prefix paths
if sub.is_file {
return Visit::AllRecursively;
}
// 'dir' found, and is an ancestor of prefix paths
if tail_components.is_empty() {
if tail_components.next().is_none() {
return sub.to_visit_sets();
}
}
@ -280,11 +280,11 @@ impl RepoPathTree {
}
fn add(&mut self, dir: &RepoPath) -> &mut RepoPathTree {
dir.components().iter().fold(self, |sub, name| {
dir.components().fold(self, |sub, name| {
// Avoid name.clone() if entry already exists.
if !sub.entries.contains_key(name) {
sub.is_dir = true;
sub.entries.insert(name.clone(), RepoPathTree::new());
sub.entries.insert(name.to_owned(), RepoPathTree::new());
}
sub.entries.get_mut(name).unwrap()
})
@ -300,7 +300,6 @@ impl RepoPathTree {
fn get(&self, dir: &RepoPath) -> Option<&RepoPathTree> {
dir.components()
.iter()
.try_fold(self, |sub, name| sub.entries.get(name))
}
@ -310,17 +309,17 @@ impl RepoPathTree {
.unwrap_or(Visit::Nothing)
}
fn walk_to<'a>(
fn walk_to<'a, 'b: 'a>(
&'a self,
dir: &'a RepoPath,
) -> impl Iterator<Item = (&RepoPathTree, &[RepoPathComponentBuf])> + 'a {
iter::successors(
Some((self, dir.components().as_slice())),
|(sub, components)| {
let (name, tail) = components.split_first()?;
Some((sub.entries.get(name)?, tail))
},
)
dir: &'b RepoPath,
) -> impl Iterator<Item = (&'a RepoPathTree, RepoPathComponentsIter<'b>)> + 'a {
iter::successors(Some((self, dir.components())), |(sub, components)| {
// TODO: Add cheap as_path() method to the components iterator.
// Cloning iterator should be cheap, but looks a bit weird.
let mut components = components.clone();
let name = components.next()?;
Some((sub.entries.get(name)?, components))
})
}
fn to_visit_sets(&self) -> Visit {

View file

@ -30,7 +30,7 @@ use pollster::FutureExt;
use crate::backend::{BackendError, BackendResult, ConflictId, MergedTreeId, TreeId, TreeValue};
use crate::matchers::{EverythingMatcher, Matcher};
use crate::merge::{Merge, MergeBuilder, MergedTreeValue};
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathComponentBuf};
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathComponentsIter};
use crate::store::Store;
use crate::tree::{try_resolve_file_conflict, Tree, TreeMergeError};
use crate::tree_builder::TreeBuilder;
@ -268,11 +268,9 @@ impl MergedTree {
}
}
// TODO: switch to borrowed &RepoPath type or RepoPathComponents iterator
fn sub_tree_recursive(&self, components: &[RepoPathComponentBuf]) -> Option<MergedTree> {
if let Some((first, tail)) = components.split_first() {
tail.iter()
.try_fold(self.sub_tree(first)?, |tree, name| tree.sub_tree(name))
fn sub_tree_recursive(&self, mut components: RepoPathComponentsIter) -> Option<MergedTree> {
if let Some(first) = components.next() {
components.try_fold(self.sub_tree(first)?, |tree, name| tree.sub_tree(name))
} else {
Some(self.clone())
}

View file

@ -111,6 +111,12 @@ impl ToOwned for RepoPathComponent {
}
}
/// Iterator over `RepoPath` components.
pub type RepoPathComponentsIter<'a> = std::iter::Map<
std::slice::Iter<'a, RepoPathComponentBuf>,
fn(&RepoPathComponentBuf) -> &RepoPathComponent,
>;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RepoPath {
components: Vec<RepoPathComponentBuf>,
@ -244,8 +250,8 @@ impl RepoPath {
}
}
pub fn components(&self) -> &Vec<RepoPathComponentBuf> {
&self.components
pub fn components(&self) -> RepoPathComponentsIter<'_> {
self.components.iter().map(AsRef::as_ref)
}
pub fn join(&self, entry: &RepoPathComponent) -> RepoPath {
@ -361,16 +367,30 @@ mod tests {
#[test]
fn test_components() {
assert_eq!(RepoPath::root().components(), &vec![]);
assert!(RepoPath::root().components().next().is_none());
assert_eq!(
repo_path("dir").components(),
&vec![RepoPathComponentBuf::from("dir")]
repo_path("dir").components().collect_vec(),
vec![RepoPathComponent::new("dir")]
);
assert_eq!(
repo_path("dir/subdir").components(),
&vec![
RepoPathComponentBuf::from("dir"),
RepoPathComponentBuf::from("subdir")
repo_path("dir/subdir").components().collect_vec(),
vec![
RepoPathComponent::new("dir"),
RepoPathComponent::new("subdir"),
]
);
// Iterates from back
assert!(RepoPath::root().components().next_back().is_none());
assert_eq!(
repo_path("dir").components().rev().collect_vec(),
vec![RepoPathComponent::new("dir")]
);
assert_eq!(
repo_path("dir/subdir").components().rev().collect_vec(),
vec![
RepoPathComponent::new("subdir"),
RepoPathComponent::new("dir"),
]
);
}

View file

@ -30,7 +30,7 @@ use crate::backend::{
use crate::files::MergeResult;
use crate::matchers::{EverythingMatcher, Matcher};
use crate::merge::{trivial_merge, Merge, MergedTreeValue};
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathComponentBuf};
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathComponentsIter};
use crate::store::Store;
use crate::{backend, files};
@ -159,11 +159,9 @@ impl Tree {
self.store.get_tree(subdir, id).unwrap()
}
// TODO: switch to borrowed &RepoPath type or RepoPathComponents iterator
fn sub_tree_recursive(&self, components: &[RepoPathComponentBuf]) -> Option<Tree> {
if let Some((first, tail)) = components.split_first() {
tail.iter()
.try_fold(self.sub_tree(first)?, |tree, name| tree.sub_tree(name))
fn sub_tree_recursive(&self, mut components: RepoPathComponentsIter) -> Option<Tree> {
if let Some(first) = components.next() {
components.try_fold(self.sub_tree(first)?, |tree, name| tree.sub_tree(name))
} else {
// TODO: It would be nice to be able to return a reference here, but
// then we would have to figure out how to share Tree instances

View file

@ -140,7 +140,7 @@ fn test_from_legacy_tree() {
);
// file1: regular file without conflicts
assert_eq!(
merged_tree.value(&file1_path.components()[0]),
merged_tree.value(file1_path.components().next().unwrap()),
MergedTreeVal::Resolved(Some(&TreeValue::File {
id: file1_id.clone(),
executable: false,
@ -148,7 +148,7 @@ fn test_from_legacy_tree() {
);
// file2: 3-way conflict
assert_eq!(
merged_tree.value(&file2_path.components()[0]),
merged_tree.value(file2_path.components().next().unwrap()),
MergedTreeVal::Conflict(Merge::from_removes_adds(
vec![Some(file_value(&file2_v1_id)), None],
vec![
@ -160,7 +160,7 @@ fn test_from_legacy_tree() {
);
// file3: modify/delete conflict
assert_eq!(
merged_tree.value(&file3_path.components()[0]),
merged_tree.value(file3_path.components().next().unwrap()),
MergedTreeVal::Conflict(Merge::from_removes_adds(
vec![Some(file_value(&file3_v1_id)), None],
vec![Some(file_value(&file3_v2_id)), None, None],
@ -168,7 +168,7 @@ fn test_from_legacy_tree() {
);
// file4: add/add conflict
assert_eq!(
merged_tree.value(&file4_path.components()[0]),
merged_tree.value(file4_path.components().next().unwrap()),
MergedTreeVal::Conflict(Merge::from_removes_adds(
vec![None, None],
vec![
@ -180,7 +180,7 @@ fn test_from_legacy_tree() {
);
// file5: 5-way conflict
assert_eq!(
merged_tree.value(&file5_path.components()[0]),
merged_tree.value(file5_path.components().next().unwrap()),
MergedTreeVal::Conflict(Merge::from_removes_adds(
vec![
Some(file_value(&file5_v1_id)),