return Result<> instead of Result<Option<>> for find_entry

Signed-off-by: Han Xu <keepsimple@gmail.com>
This commit is contained in:
Han Xu
2023-01-17 08:42:35 -08:00
parent 016ece6647
commit de418e811e
3 changed files with 63 additions and 76 deletions
+43 -50
View File
@@ -29,7 +29,6 @@ use std::{
use git2::Blob;
use radicle_git_ext::{is_not_found_err, Oid};
use radicle_std_ext::result::ResultExt as _;
use crate::{Repository, Revision};
@@ -43,17 +42,15 @@ pub mod error {
#[error(transparent)]
Git(#[from] git2::Error),
#[error(transparent)]
Entry(#[from] Entry),
#[error(transparent)]
File(#[from] File),
#[error("the path {0} is not valid")]
InvalidPath(PathBuf),
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum Entry {
#[error("{0}: {1}")]
InvalidType(PathBuf, &'static str),
#[error("the entry name was not valid UTF-8")]
Utf8Error,
#[error("the path {0} not found")]
PathNotFound(PathBuf),
}
#[derive(Debug, Error, PartialEq)]
@@ -258,15 +255,18 @@ impl Entry {
pub(crate) fn from_entry(
entry: &git2::TreeEntry,
path: PathBuf,
) -> Result<Option<Self>, error::Entry> {
let name = entry.name().ok_or(error::Entry::Utf8Error)?.to_string();
) -> Result<Self, error::Directory> {
let name = entry.name().ok_or(error::Directory::Utf8Error)?.to_string();
let id = entry.id().into();
Ok(entry.kind().and_then(|kind| match kind {
git2::ObjectType::Tree => Some(Self::Directory(Directory::new(name, path, id))),
git2::ObjectType::Blob => Some(Self::File(File::new(name, path, id))),
_ => None,
}))
match entry.kind() {
Some(git2::ObjectType::Tree) => Ok(Self::Directory(Directory::new(name, path, id))),
Some(git2::ObjectType::Blob) => Ok(Self::File(File::new(name, path, id))),
_ => Err(error::Directory::InvalidType(
path,
"Entry must be a tree or a blob",
)),
}
}
}
@@ -361,7 +361,7 @@ impl Directory {
// empty for the first level.
tree.walk(git2::TreeWalkMode::PreOrder, |_entry_path, entry| {
match Entry::from_entry(entry, path.clone()) {
Ok(Some(entry)) => match entry {
Ok(entry) => match entry {
Entry::File(_) => {
entries.insert(entry.name().clone(), entry);
git2::TreeWalkResult::Ok
@@ -372,7 +372,6 @@ impl Directory {
git2::TreeWalkResult::Skip
},
},
Ok(None) => git2::TreeWalkResult::Skip,
Err(err) => {
error = Some(err);
git2::TreeWalkResult::Abort
@@ -381,72 +380,66 @@ impl Directory {
})?;
match error {
Some(err) => Err(err.into()),
Some(err) => Err(err),
None => Ok(Entries { listing: entries }),
}
}
/// Find the [`Entry`] found at `path`, if it exists.
pub fn find_entry<P>(
&self,
path: &P,
repo: &Repository,
) -> Result<Option<Entry>, error::Directory>
/// Find the [`Entry`] found at a non-empty `path`, if it exists.
pub fn find_entry<P>(&self, path: &P, repo: &Repository) -> Result<Entry, error::Directory>
where
P: AsRef<Path>,
{
// Search the path in git2 tree.
let path = path.as_ref();
let git2_tree = repo.find_tree(self.id)?;
let entry = git2_tree
.get_path(path)
.map(Some)
.or_matches::<git2::Error, _, _>(is_not_found_err, || Ok(None))?;
let entry = git2_tree.get_path(path).map_err(|e| {
if is_not_found_err(&e) {
error::Directory::PathNotFound(path.to_path_buf())
} else {
error::Directory::Git(e)
}
})?;
let parent = path
.parent()
.ok_or_else(|| error::Directory::InvalidPath(path.to_path_buf()))?;
let root_path = self.path().join(parent);
Ok(entry
.and_then(|entry| Entry::from_entry(&entry, root_path.to_path_buf()).transpose())
.transpose()
.unwrap())
Entry::from_entry(&entry, root_path)
}
/// Find the `Oid`, for a [`File`], found at `path`, if it exists.
pub fn find_file<P>(
&self,
path: &P,
repo: &Repository,
) -> Result<Option<File>, error::Directory>
pub fn find_file<P>(&self, path: &P, repo: &Repository) -> Result<File, error::Directory>
where
P: AsRef<Path>,
{
Ok(match self.find_entry(path, repo)? {
Some(Entry::File(file)) => Some(file),
_ => None,
})
match self.find_entry(path, repo)? {
Entry::File(file) => Ok(file),
_ => Err(error::Directory::InvalidType(
path.as_ref().to_path_buf(),
"Entry must be a file",
)),
}
}
/// Find the `Directory` found at `path`, if it exists.
///
/// If `path` is `ROOT_DIR` (i.e. an empty path), returns self.
pub fn find_directory<P>(
&self,
path: &P,
repo: &Repository,
) -> Result<Option<Self>, error::Directory>
pub fn find_directory<P>(&self, path: &P, repo: &Repository) -> Result<Self, error::Directory>
where
P: AsRef<Path>,
{
if path.as_ref() == Path::new(ROOT_DIR) {
return Ok(Some(self.clone()));
return Ok(self.clone());
}
Ok(match self.find_entry(path, repo)? {
Some(Entry::Directory(d)) => Some(d),
_ => None,
})
match self.find_entry(path, repo)? {
Entry::Directory(d) => Ok(d),
_ => Err(error::Directory::InvalidType(
path.as_ref().to_path_buf(),
"Entry must be a directory",
)),
}
}
// TODO(fintan): This is going to be a bit trickier so going to leave it out for
+2 -4
View File
@@ -258,15 +258,13 @@ impl Repository {
path: &P,
) -> Result<Directory, Error> {
let root = self.root_dir(commit)?;
root.find_directory(path, self)?
.ok_or_else(|| Error::PathNotFound(path.as_ref().to_path_buf()))
Ok(root.find_directory(path, self)?)
}
/// Returns a [`File`] for `path` in `commit`.
pub fn file<C: ToCommit, P: AsRef<Path>>(&self, commit: C, path: &P) -> Result<File, Error> {
let root = self.root_dir(commit)?;
root.find_file(path, self)?
.ok_or_else(|| Error::PathNotFound(path.as_ref().to_path_buf()))
Ok(root.find_file(path, self)?)
}
/// Returns a [`Tree`] for `path` in `commit`.
+18 -22
View File
@@ -22,32 +22,37 @@ mod directory {
// find_entry for a file.
let path = Path::new("src/memory.rs");
let entry = root.find_entry(&path, &repo).unwrap();
assert!(matches!(entry, Some(fs::Entry::File(_))));
assert!(matches!(entry, fs::Entry::File(_)));
// find_entry for a directory.
let path = Path::new("this/is/a/really/deeply/nested/directory/tree");
let entry = root.find_entry(&path, &repo).unwrap();
assert!(matches!(entry, Some(fs::Entry::Directory(_))));
assert!(matches!(entry, fs::Entry::Directory(_)));
// find_entry for a non-leaf directory and its relative path.
let path = Path::new("text");
let entry = root.find_entry(&path, &repo).unwrap();
assert!(matches!(entry, Some(fs::Entry::Directory(_))));
if let Some(fs::Entry::Directory(sub_dir)) = entry {
assert!(matches!(entry, fs::Entry::Directory(_)));
if let fs::Entry::Directory(sub_dir) = entry {
let inner_path = Path::new("garden.txt");
let inner_entry = sub_dir.find_entry(&inner_path, &repo).unwrap();
assert!(matches!(inner_entry, Some(fs::Entry::File(_))));
assert!(matches!(inner_entry, fs::Entry::File(_)));
}
// find_entry for non-existing file
let path = Path::new("this/is/a/really/missing_file");
let result = root.find_entry(&path, &repo).unwrap();
assert_eq!(result, None);
let result = root.find_entry(&path, &repo);
assert!(matches!(result, Err(fs::error::Directory::PathNotFound(_))));
// find_entry for absolute path: fail.
let path = Path::new("/src/memory.rs");
let result = root.find_entry(&path, &repo);
assert!(result.is_err());
// find entry for an empty path
let path = Path::new("");
let result = root.find_entry(&path, &repo);
assert!(result.is_err());
}
#[test]
@@ -59,10 +64,7 @@ mod directory {
.unwrap();
// Assert that we can find the memory.rs file!
assert!(root
.find_file(&Path::new("src/memory.rs"), &repo)
.unwrap()
.is_some());
assert!(root.find_file(&Path::new("src/memory.rs"), &repo).is_ok());
let root_contents: Vec<Entry> = root.entries(&repo).unwrap().collect();
assert_eq!(root_contents.len(), 7);
@@ -82,10 +84,7 @@ mod directory {
assert_eq!(root_contents[5].name(), "text");
assert_eq!(root_contents[6].name(), "this");
let src = root
.find_directory(&Path::new("src"), &repo)
.unwrap()
.unwrap();
let src = root.find_directory(&Path::new("src"), &repo).unwrap();
assert_eq!(src.path(), Path::new("src").to_path_buf());
let src_contents: Vec<Entry> = src.entries(&repo).unwrap().collect();
assert_eq!(src_contents.len(), 3);
@@ -109,8 +108,8 @@ mod directory {
let path = Path::new("src");
let entry = root.find_entry(&path, &repo).unwrap();
assert!(matches!(entry, Some(fs::Entry::Directory(_))));
if let Some(fs::Entry::Directory(d)) = entry {
assert!(matches!(entry, fs::Entry::Directory(_)));
if let fs::Entry::Directory(d) = entry {
assert_eq!(16297, d.size(&repo).unwrap());
}
}
@@ -120,7 +119,7 @@ mod directory {
let repo = Repository::open(GIT_PLATINUM).unwrap();
let branch = Branch::local(refname!("dev"));
let root = repo.root_dir(&branch).unwrap();
let dir = root.find_directory(&"this/is", &repo).unwrap().unwrap();
let dir = root.find_directory(&"this/is", &repo).unwrap();
let last_commit = repo.last_commit(&dir.path(), &branch).unwrap().unwrap();
assert_eq!(
last_commit.id.to_string(),
@@ -135,10 +134,7 @@ mod directory {
let root = repo.root_dir(&branch).unwrap();
// Find a file with "\" in its name.
let f = root
.find_file(&"special/faux\\path", &repo)
.unwrap()
.unwrap();
let f = root.find_file(&"special/faux\\path", &repo).unwrap();
let last_commit = repo.last_commit(&f.path(), &branch).unwrap().unwrap();
assert_eq!(
last_commit.id.to_string(),