mirror of
https://seed.radicle.dev/rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt.git
synced 2026-10-25 14:20:31 +08:00
Merge remote-tracking branch 'origin/std-path'
* origin/std-path: radicle-surf: remove file_system::path Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
This commit is contained in:
@@ -58,13 +58,13 @@ fn init_repository_or_exit(path_to_repo: &str) -> git::Repository {
|
||||
|
||||
fn print_diff_summary(diff: &Diff, elapsed_nanos: u128) {
|
||||
diff.created.iter().for_each(|created| {
|
||||
println!("+++ {}", created.path);
|
||||
println!("+++ {:?}", created.path);
|
||||
});
|
||||
diff.deleted.iter().for_each(|deleted| {
|
||||
println!("--- {}", deleted.path);
|
||||
println!("--- {:?}", deleted.path);
|
||||
});
|
||||
diff.modified.iter().for_each(|modified| {
|
||||
println!("mod {}", modified.path);
|
||||
println!("mod {:?}", modified.path);
|
||||
});
|
||||
|
||||
println!(
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
//! Represents a commit.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use git_ref_format::RefString;
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{
|
||||
@@ -26,7 +28,6 @@ use serde::{
|
||||
|
||||
use crate::{
|
||||
diff,
|
||||
file_system,
|
||||
git::{self, glob, Glob, Repository},
|
||||
person::Person,
|
||||
revision::Revision,
|
||||
@@ -245,10 +246,6 @@ pub fn commits(repo: &Repository, maybe_revision: Option<Revision>) -> Result<Co
|
||||
/// An error reported by commit API.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// An error occurred during a file system operation.
|
||||
#[error(transparent)]
|
||||
FileSystem(#[from] file_system::Error),
|
||||
|
||||
/// An error occurred during a git operation.
|
||||
#[error(transparent)]
|
||||
Git(#[from] git::Error),
|
||||
@@ -258,5 +255,5 @@ pub enum Error {
|
||||
|
||||
/// Trying to find a file path which could not be found.
|
||||
#[error("the path '{0}' was not found")]
|
||||
PathNotFound(file_system::Path),
|
||||
PathNotFound(PathBuf),
|
||||
}
|
||||
|
||||
+15
-15
@@ -17,13 +17,13 @@
|
||||
|
||||
#![allow(dead_code, unused_variables, missing_docs)]
|
||||
|
||||
use std::{convert::TryFrom, slice};
|
||||
use std::{convert::TryFrom, path::PathBuf, slice};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{ser, Serialize, Serializer};
|
||||
|
||||
use crate::{
|
||||
file_system::{Directory, Path},
|
||||
file_system::Directory,
|
||||
git::{Error, Repository},
|
||||
};
|
||||
|
||||
@@ -52,14 +52,14 @@ impl Default for Diff {
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CreateFile {
|
||||
pub path: Path,
|
||||
pub path: PathBuf,
|
||||
pub diff: FileDiff,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DeleteFile {
|
||||
pub path: Path,
|
||||
pub path: PathBuf,
|
||||
pub diff: FileDiff,
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ pub struct DeleteFile {
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MoveFile {
|
||||
pub old_path: Path,
|
||||
pub new_path: Path,
|
||||
pub old_path: PathBuf,
|
||||
pub new_path: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -81,8 +81,8 @@ pub struct MoveFile {
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CopyFile {
|
||||
pub old_path: Path,
|
||||
pub new_path: Path,
|
||||
pub old_path: PathBuf,
|
||||
pub new_path: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -104,7 +104,7 @@ pub enum EofNewLine {
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ModifiedFile {
|
||||
pub path: Path,
|
||||
pub path: PathBuf,
|
||||
pub diff: FileDiff,
|
||||
pub eof: Option<EofNewLine>,
|
||||
}
|
||||
@@ -309,7 +309,7 @@ impl Diff {
|
||||
|
||||
pub(crate) fn add_modified_file(
|
||||
&mut self,
|
||||
path: Path,
|
||||
path: PathBuf,
|
||||
hunks: impl Into<Hunks>,
|
||||
eof: Option<EofNewLine>,
|
||||
) {
|
||||
@@ -325,15 +325,15 @@ impl Diff {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn add_moved_file(&mut self, old_path: Path, new_path: Path) {
|
||||
pub(crate) fn add_moved_file(&mut self, old_path: PathBuf, new_path: PathBuf) {
|
||||
self.moved.push(MoveFile { old_path, new_path });
|
||||
}
|
||||
|
||||
pub(crate) fn add_copied_file(&mut self, old_path: Path, new_path: Path) {
|
||||
pub(crate) fn add_copied_file(&mut self, old_path: PathBuf, new_path: PathBuf) {
|
||||
self.copied.push(CopyFile { old_path, new_path });
|
||||
}
|
||||
|
||||
pub(crate) fn add_modified_binary_file(&mut self, path: Path) {
|
||||
pub(crate) fn add_modified_binary_file(&mut self, path: PathBuf) {
|
||||
self.modified.push(ModifiedFile {
|
||||
path,
|
||||
diff: FileDiff::Binary,
|
||||
@@ -341,11 +341,11 @@ impl Diff {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn add_created_file(&mut self, path: Path, diff: FileDiff) {
|
||||
pub(crate) fn add_created_file(&mut self, path: PathBuf, diff: FileDiff) {
|
||||
self.created.push(CreateFile { path, diff });
|
||||
}
|
||||
|
||||
pub(crate) fn add_deleted_file(&mut self, path: Path, diff: FileDiff) {
|
||||
pub(crate) fn add_deleted_file(&mut self, path: PathBuf, diff: FileDiff) {
|
||||
self.deleted.push(DeleteFile { path, diff });
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,12 @@
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::{
|
||||
diff::{self, Diff, EofNewLine, Hunk, Hunks, Line, LineDiff},
|
||||
file_system::Path,
|
||||
};
|
||||
use crate::diff::{self, Diff, EofNewLine, Hunk, Hunks, Line, LineDiff};
|
||||
|
||||
pub mod error {
|
||||
use thiserror::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::file_system::{self, Path};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
@@ -54,8 +51,6 @@ pub mod error {
|
||||
#[error("git delta type is not handled")]
|
||||
DeltaUnhandled(git2::Delta),
|
||||
#[error(transparent)]
|
||||
FileSystem(#[from] file_system::Error),
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
#[error(transparent)]
|
||||
Hunk(#[from] Hunk),
|
||||
@@ -63,7 +58,7 @@ pub mod error {
|
||||
Line(#[from] LineDiff),
|
||||
/// A patch is unavailable.
|
||||
#[error("couldn't retrieve patch for {0}")]
|
||||
PatchUnavailable(Path),
|
||||
PatchUnavailable(PathBuf),
|
||||
/// A The path of a file isn't available.
|
||||
#[error("couldn't retrieve file path")]
|
||||
PathUnavailable,
|
||||
@@ -95,8 +90,10 @@ impl<'a> TryFrom<git2::Diff<'a>> for Diff {
|
||||
match delta.status() {
|
||||
Delta::Added => {
|
||||
let diff_file = delta.new_file();
|
||||
let path = diff_file.path().ok_or(error::Diff::PathUnavailable)?;
|
||||
let path = Path::try_from(path.to_path_buf())?;
|
||||
let path = diff_file
|
||||
.path()
|
||||
.ok_or(error::Diff::PathUnavailable)?
|
||||
.to_path_buf();
|
||||
|
||||
let patch = Patch::from_diff(&git_diff, idx)?;
|
||||
if let Some(patch) = patch {
|
||||
@@ -117,9 +114,10 @@ impl<'a> TryFrom<git2::Diff<'a>> for Diff {
|
||||
},
|
||||
Delta::Deleted => {
|
||||
let diff_file = delta.old_file();
|
||||
let path = diff_file.path().ok_or(error::Diff::PathUnavailable)?;
|
||||
let path = Path::try_from(path.to_path_buf())?;
|
||||
|
||||
let path = diff_file
|
||||
.path()
|
||||
.ok_or(error::Diff::PathUnavailable)?
|
||||
.to_path_buf();
|
||||
let patch = Patch::from_diff(&git_diff, idx)?;
|
||||
if let Some(patch) = patch {
|
||||
diff.add_deleted_file(
|
||||
@@ -139,9 +137,10 @@ impl<'a> TryFrom<git2::Diff<'a>> for Diff {
|
||||
},
|
||||
Delta::Modified => {
|
||||
let diff_file = delta.new_file();
|
||||
let path = diff_file.path().ok_or(error::Diff::PathUnavailable)?;
|
||||
let path = Path::try_from(path.to_path_buf())?;
|
||||
|
||||
let path = diff_file
|
||||
.path()
|
||||
.ok_or(error::Diff::PathUnavailable)?
|
||||
.to_path_buf();
|
||||
let patch = Patch::from_diff(&git_diff, idx)?;
|
||||
|
||||
if let Some(patch) = patch {
|
||||
@@ -200,10 +199,7 @@ impl<'a> TryFrom<git2::Diff<'a>> for Diff {
|
||||
.path()
|
||||
.ok_or(error::Diff::PathUnavailable)?;
|
||||
|
||||
let old_path = Path::try_from(old.to_path_buf())?;
|
||||
let new_path = Path::try_from(new.to_path_buf())?;
|
||||
|
||||
diff.add_moved_file(old_path, new_path);
|
||||
diff.add_moved_file(old.to_path_buf(), new.to_path_buf());
|
||||
},
|
||||
Delta::Copied => {
|
||||
let old = delta
|
||||
@@ -215,10 +211,7 @@ impl<'a> TryFrom<git2::Diff<'a>> for Diff {
|
||||
.path()
|
||||
.ok_or(error::Diff::PathUnavailable)?;
|
||||
|
||||
let old_path = Path::try_from(old.to_path_buf())?;
|
||||
let new_path = Path::try_from(new.to_path_buf())?;
|
||||
|
||||
diff.add_copied_file(old_path, new_path);
|
||||
diff.add_copied_file(old.to_path_buf(), new.to_path_buf());
|
||||
},
|
||||
status => {
|
||||
return Err(error::Diff::DeltaUnhandled(status));
|
||||
|
||||
@@ -112,8 +112,3 @@
|
||||
|
||||
pub mod directory;
|
||||
pub use directory::{Directory, Entries, Entry, File, FileContent};
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
mod path;
|
||||
|
||||
pub use self::path::*;
|
||||
|
||||
@@ -22,18 +22,15 @@
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
convert::{Infallible, Into},
|
||||
path,
|
||||
convert::{Infallible, Into as _},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use git2::Blob;
|
||||
use radicle_git_ext::{is_not_found_err, Oid};
|
||||
use radicle_std_ext::result::ResultExt as _;
|
||||
|
||||
use crate::{
|
||||
file_system::{path::*, Error},
|
||||
git::{Repository, Revision},
|
||||
};
|
||||
use crate::git::{Repository, Revision};
|
||||
|
||||
pub mod error {
|
||||
use thiserror::Error;
|
||||
@@ -52,8 +49,6 @@ pub mod error {
|
||||
pub enum Entry {
|
||||
#[error("the entry name was not valid UTF-8")]
|
||||
Utf8Error,
|
||||
#[error(transparent)]
|
||||
Label(#[from] super::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
@@ -74,11 +69,32 @@ pub mod error {
|
||||
/// [`File::content`].
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct File {
|
||||
pub(crate) name: Label,
|
||||
pub(crate) id: Oid,
|
||||
/// The name of the file.
|
||||
name: String,
|
||||
/// The relative path of the file, not including the `name`,
|
||||
/// in respect to the root of the git repository.
|
||||
prefix: PathBuf,
|
||||
/// The object identifier of the git blob of this file.
|
||||
id: Oid,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Construct a new `File`.
|
||||
///
|
||||
/// The `path` must be the prefix location of the directory, and
|
||||
/// so should not end in `name`.
|
||||
///
|
||||
/// The `id` must point to a git blob.
|
||||
pub(crate) fn new(name: String, prefix: PathBuf, id: Oid) -> Self {
|
||||
debug_assert!(
|
||||
!prefix.ends_with(&name),
|
||||
"prefix = {:?}, name = {}",
|
||||
prefix,
|
||||
name
|
||||
);
|
||||
Self { name, prefix, id }
|
||||
}
|
||||
|
||||
/// The name of this `File`.
|
||||
pub fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
@@ -89,17 +105,18 @@ impl File {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Create a new `File` with the `name` and `id` provided.
|
||||
/// Return the exact path for this `File`, including the `name` of
|
||||
/// the directory itself.
|
||||
///
|
||||
/// The `id` must point to a `git` blob.
|
||||
pub fn new(name: String, id: Oid) -> Self {
|
||||
Self {
|
||||
name: Label {
|
||||
label: name,
|
||||
hidden: false,
|
||||
},
|
||||
id,
|
||||
}
|
||||
/// The path is relative to the git repository root.
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.prefix.join(&self.name)
|
||||
}
|
||||
|
||||
/// Return the [`Path`] where this `File` is located, relative to the
|
||||
/// git repository root.
|
||||
pub fn location(&self) -> &Path {
|
||||
&self.prefix
|
||||
}
|
||||
|
||||
/// Get the [`FileContent`] for this `File`.
|
||||
@@ -140,12 +157,12 @@ impl<'a> FileContent<'a> {
|
||||
|
||||
/// A representations of a [`Directory`]'s entries.
|
||||
pub struct Entries {
|
||||
listing: BTreeMap<Label, Entry>,
|
||||
listing: BTreeMap<String, Entry>,
|
||||
}
|
||||
|
||||
impl Entries {
|
||||
/// Return the [`Label`]s of each [`Entry`].
|
||||
pub fn names(&self) -> impl Iterator<Item = &Label> {
|
||||
/// Return the name of each [`Entry`].
|
||||
pub fn names(&self) -> impl Iterator<Item = &String> {
|
||||
self.listing.keys()
|
||||
}
|
||||
|
||||
@@ -154,8 +171,8 @@ impl Entries {
|
||||
self.listing.values()
|
||||
}
|
||||
|
||||
/// Return each [`Label`] and [`Entry`].
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Label, &Entry)> {
|
||||
/// Return each [`Entry`] and its name.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&String, &Entry)> {
|
||||
self.listing.iter()
|
||||
}
|
||||
}
|
||||
@@ -172,22 +189,22 @@ pub enum Entry {
|
||||
impl Entry {
|
||||
/// Get a label for the `Entriess`, either the name of the [`File`]
|
||||
/// or the name of the [`Directory`].
|
||||
pub fn label(&self) -> &Label {
|
||||
pub fn name(&self) -> &String {
|
||||
match self {
|
||||
Entry::File(file) => &file.name,
|
||||
Entry::Directory(directory) => directory.name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_entry(entry: &git2::TreeEntry) -> Result<Option<Self>, error::Entry> {
|
||||
let name = Label {
|
||||
label: entry.name().ok_or(error::Entry::Utf8Error)?.to_string(),
|
||||
hidden: false,
|
||||
};
|
||||
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();
|
||||
let id = entry.id().into();
|
||||
Ok(entry.kind().and_then(|kind| match kind {
|
||||
git2::ObjectType::Tree => Some(Self::Directory(Directory { name, id })),
|
||||
git2::ObjectType::Blob => Some(Self::File(File { name, id })),
|
||||
git2::ObjectType::Tree => Some(Self::Directory(Directory::new(name, path, id))),
|
||||
git2::ObjectType::Blob => Some(Self::File(File::new(name, path, id))),
|
||||
_ => None,
|
||||
}))
|
||||
}
|
||||
@@ -204,21 +221,63 @@ impl Entry {
|
||||
/// [git-tree]: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Directory {
|
||||
pub(crate) name: Label,
|
||||
pub(crate) id: Oid,
|
||||
/// The name of the directoy.
|
||||
name: String,
|
||||
/// The relative path of the directory, not including the `name`,
|
||||
/// in respect to the root of the git repository.
|
||||
prefix: PathBuf,
|
||||
/// The object identifier of the git tree of this directory.
|
||||
id: Oid,
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
/// Get the name of the current `Directory`.
|
||||
pub fn name(&self) -> &Label {
|
||||
&self.name
|
||||
/// Creates a directory given its `id`.
|
||||
///
|
||||
/// The `name` and `prefix` are both set to be empty.
|
||||
///
|
||||
/// The `id` must point to a `git` tree.
|
||||
pub(crate) fn root(id: Oid) -> Self {
|
||||
Self::new("".to_string(), PathBuf::new(), id)
|
||||
}
|
||||
|
||||
/// Creates a directory given its `name` and `id`.
|
||||
///
|
||||
/// The `path` must be the prefix location of the directory, and
|
||||
/// so should not end in `name`.
|
||||
///
|
||||
/// The `id` must point to a `git` tree.
|
||||
pub fn new(name: Label, id: Oid) -> Self {
|
||||
Self { name, id }
|
||||
pub(crate) fn new(name: String, prefix: PathBuf, id: Oid) -> Self {
|
||||
debug_assert!(
|
||||
name.is_empty() || !prefix.ends_with(&name),
|
||||
"prefix = {:?}, name = {}",
|
||||
prefix,
|
||||
name
|
||||
);
|
||||
Self { name, prefix, id }
|
||||
}
|
||||
|
||||
/// Get the name of the current `Directory`.
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The object identifier of this `File`.
|
||||
pub fn id(&self) -> Oid {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Return the exact path for this `Directory`, including the `name` of the
|
||||
/// directory itself.
|
||||
///
|
||||
/// The path is relative to the git repository root.
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.prefix.join(&self.name)
|
||||
}
|
||||
|
||||
/// Return the [`Path`] where this `Directory` is located, relative to the
|
||||
/// git repository root.
|
||||
pub fn location(&self) -> &Path {
|
||||
&self.prefix
|
||||
}
|
||||
|
||||
/// Return the [`Entries`] for this `Directory`'s `Oid`.
|
||||
@@ -238,15 +297,15 @@ impl Directory {
|
||||
let mut error = None;
|
||||
|
||||
// Walks only the first level of entries.
|
||||
tree.walk(git2::TreeWalkMode::PreOrder, |_s, entry| {
|
||||
match Entry::from_entry(entry) {
|
||||
tree.walk(git2::TreeWalkMode::PreOrder, |path, entry| {
|
||||
match Entry::from_entry(entry, Path::new(path).to_path_buf()) {
|
||||
Ok(Some(entry)) => match entry {
|
||||
Entry::File(_) => {
|
||||
entries.insert(entry.label().clone(), entry);
|
||||
entries.insert(entry.name().clone(), entry);
|
||||
git2::TreeWalkResult::Ok
|
||||
},
|
||||
Entry::Directory(_) => {
|
||||
entries.insert(entry.label().clone(), entry);
|
||||
entries.insert(entry.name().clone(), entry);
|
||||
// Skip nested directories
|
||||
git2::TreeWalkResult::Skip
|
||||
},
|
||||
@@ -266,45 +325,52 @@ impl Directory {
|
||||
}
|
||||
|
||||
/// Find the [`Entry`] found at `path`, if it exists.
|
||||
pub fn find_entry(
|
||||
pub fn find_entry<P>(
|
||||
&self,
|
||||
path: &path::Path,
|
||||
path: &P,
|
||||
repo: &Repository,
|
||||
) -> Result<Option<Entry>, crate::git::Error> {
|
||||
) -> Result<Option<Entry>, crate::git::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
// Search the path in git2 tree.
|
||||
let git2_tree = repo.git2_repo().find_tree(self.id.into())?;
|
||||
let entry = git2_tree
|
||||
.get_path(path)
|
||||
.get_path(path.as_ref())
|
||||
.map(Some)
|
||||
.or_matches::<git2::Error, _, _>(is_not_found_err, || Ok(None))?;
|
||||
|
||||
Ok(entry
|
||||
.and_then(|entry| Entry::from_entry(&entry).transpose())
|
||||
.and_then(|entry| Entry::from_entry(&entry, path.as_ref().to_path_buf()).transpose())
|
||||
.transpose()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
/// Find the `Oid`, for a [`File`], found at `path`, if it exists.
|
||||
pub fn find_file(
|
||||
pub fn find_file<P>(
|
||||
&self,
|
||||
path: Path,
|
||||
path: &P,
|
||||
repo: &Repository,
|
||||
) -> Result<Option<Oid>, crate::git::Error> {
|
||||
let path_buf: std::path::PathBuf = (&path).into();
|
||||
Ok(match self.find_entry(path_buf.as_path(), repo)? {
|
||||
) -> Result<Option<Oid>, crate::git::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(match self.find_entry(path, repo)? {
|
||||
Some(Entry::File(f)) => Some(f.id),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Find the `Directory` found at `path`, if it exists.
|
||||
pub fn find_directory(
|
||||
pub fn find_directory<P>(
|
||||
&self,
|
||||
path: Path,
|
||||
path: P,
|
||||
repo: &Repository,
|
||||
) -> Result<Option<Self>, crate::git::Error> {
|
||||
let path_buf: std::path::PathBuf = (&path).into();
|
||||
Ok(match self.find_entry(path_buf.as_path(), repo)? {
|
||||
) -> Result<Option<Self>, crate::git::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(match self.find_entry(&path, repo)? {
|
||||
Some(Entry::Directory(d)) => Some(d),
|
||||
_ => None,
|
||||
})
|
||||
@@ -313,7 +379,7 @@ impl Directory {
|
||||
// TODO(fintan): This is going to be a bit trickier so going to leave it out for
|
||||
// now
|
||||
#[allow(dead_code)]
|
||||
fn fuzzy_find(_label: Label) -> Vec<Self> {
|
||||
fn fuzzy_find(_label: &Path) -> Vec<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// This file is part of radicle-surf
|
||||
// <https://github.com/radicle-dev/radicle-surf>
|
||||
//
|
||||
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 3 or
|
||||
// later as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Errors that can occur within the file system logic.
|
||||
//!
|
||||
//! These errors occur due to [`Label`](super::path::Label) and
|
||||
//! [`Path`](super::path::Path) parsing when using their respective `TryFrom`
|
||||
//! instances.
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use thiserror::Error;
|
||||
|
||||
pub(crate) const EMPTY_PATH: Error = Error::Path(PathError::Empty);
|
||||
pub(crate) const EMPTY_LABEL: Error = Error::Label(LabelError::Empty);
|
||||
|
||||
/// Build an [`Error::Label(LabelError::InvalidUTF8)`] from an
|
||||
/// [`OsStr`](std::ffi::OsStr)
|
||||
pub(crate) fn label_invalid_utf8(item: &OsStr) -> Error {
|
||||
Error::Label(LabelError::InvalidUTF8 {
|
||||
label: item.to_string_lossy().into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Build an [`Error::Label(LabelError::ContainsSlash)`] from a [`str`]
|
||||
pub(crate) fn label_has_slash(item: &str) -> Error {
|
||||
Error::Label(LabelError::ContainsSlash { label: item.into() })
|
||||
}
|
||||
|
||||
/// Error type for all file system errors that can occur.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// A `LabelError` specific error for parsing a
|
||||
/// [`Path`](super::path::Label).
|
||||
#[error(transparent)]
|
||||
Label(#[from] LabelError),
|
||||
/// A `PathError` specific error for parsing a [`Path`](super::path::Path).
|
||||
#[error(transparent)]
|
||||
Path(#[from] PathError),
|
||||
}
|
||||
|
||||
/// Parse errors for when parsing a string to a [`Path`](super::path::Path).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum PathError {
|
||||
/// An error signifying that a [`Path`](super::path::Path) is empty.
|
||||
#[error("path is empty")]
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Parse errors for when parsing a string to a [`Label`](super::path::Label).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum LabelError {
|
||||
/// An error signifying that a [`Label`](super::path::Label) is contains
|
||||
/// invalid UTF-8.
|
||||
#[error("label '{label}' contains invalid UTF-8")]
|
||||
InvalidUTF8 { label: String },
|
||||
/// An error signifying that a [`Label`](super::path::Label) contains a `/`.
|
||||
#[error("label '{label}' contains a slash")]
|
||||
ContainsSlash { label: String },
|
||||
/// An error signifying that a [`Label`](super::path::Label) is empty.
|
||||
#[error("label is empty")]
|
||||
Empty,
|
||||
}
|
||||
@@ -1,453 +0,0 @@
|
||||
// This file is part of radicle-surf
|
||||
// <https://github.com/radicle-dev/radicle-surf>
|
||||
//
|
||||
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 3 or
|
||||
// later as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use crate::{file_system::error, nonempty::split_last};
|
||||
use nonempty::NonEmpty;
|
||||
use std::{convert::TryFrom, ffi::CString, fmt, ops::Deref, path, str::FromStr};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
pub mod unsound;
|
||||
|
||||
/// `Label` is a special case of a `String` identifier for
|
||||
/// [`Directory`](`crate::file_system::directory::Directory`) and
|
||||
/// [`File`](`crate::file_system::directory::File`) names, and is used in
|
||||
/// [`Path`] as the component parts of a path.
|
||||
///
|
||||
/// A `Label` should not be empty or contain `/`s. It is encouraged to use the
|
||||
/// `TryFrom` instance to create a `Label`.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Label {
|
||||
pub(crate) label: String,
|
||||
pub(crate) hidden: bool,
|
||||
}
|
||||
|
||||
impl Deref for Label {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.label
|
||||
}
|
||||
}
|
||||
|
||||
/// The label for the root directory.
|
||||
pub const ROOT_LABEL: &str = "~";
|
||||
|
||||
impl Label {
|
||||
/// The root label for the root directory, i.e. `"~"`.
|
||||
///
|
||||
/// Prefer creating a root [`Path`], by using
|
||||
/// [`Path::root`](struct.Path.html#method.root).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
///
|
||||
/// let root = Path::root();
|
||||
/// assert_eq!(*root.split_first().0, Label::root());
|
||||
/// ```
|
||||
pub fn root() -> Self {
|
||||
Label {
|
||||
label: ROOT_LABEL.into(),
|
||||
hidden: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the label is equivalent to [`Label::root`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::Label;
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let root = unsound::label::new("~");
|
||||
/// assert!(root.is_root());
|
||||
/// ```
|
||||
pub fn is_root(&self) -> bool {
|
||||
*self == Self::root()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Label {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.label)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Label {
|
||||
type Error = error::Error;
|
||||
|
||||
fn try_from(item: &str) -> Result<Self, Self::Error> {
|
||||
if item.is_empty() {
|
||||
Err(error::EMPTY_LABEL)
|
||||
} else if item.contains('/') {
|
||||
Err(error::label_has_slash(item))
|
||||
} else {
|
||||
Ok(Label {
|
||||
label: item.into(),
|
||||
hidden: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Label {
|
||||
type Err = error::Error;
|
||||
|
||||
fn from_str(item: &str) -> Result<Self, Self::Err> {
|
||||
Label::try_from(item)
|
||||
}
|
||||
}
|
||||
|
||||
/// A non-empty set of [`Label`]s to define a path to a directory or file.
|
||||
///
|
||||
/// `Path` tends to be used for insertion or find operations.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path(pub NonEmpty<Label>);
|
||||
|
||||
impl fmt::Display for Path {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (prefix, suffix) = self.clone().split_last();
|
||||
for p in prefix {
|
||||
if p.is_root() {
|
||||
continue;
|
||||
}
|
||||
write!(f, "{}/", p)?;
|
||||
}
|
||||
write!(f, "{}", suffix)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Path {
|
||||
type Error = error::Error;
|
||||
|
||||
fn try_from(item: &str) -> Result<Self, Self::Error> {
|
||||
let mut path = Vec::new();
|
||||
|
||||
for label in item.trim_end_matches('/').split('/') {
|
||||
let l = Label::try_from(label)?;
|
||||
path.push(l);
|
||||
}
|
||||
|
||||
NonEmpty::from_slice(&path)
|
||||
.ok_or(error::EMPTY_PATH)
|
||||
.map(Path)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Path {
|
||||
type Err = error::Error;
|
||||
|
||||
fn from_str(item: &str) -> Result<Self, Self::Err> {
|
||||
Path::try_from(item)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Path> for Vec<Label> {
|
||||
fn from(path: Path) -> Self {
|
||||
path.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Path> for path::PathBuf {
|
||||
fn from(path: &Path) -> Self {
|
||||
path.iter()
|
||||
.map(|label| label.as_str())
|
||||
.collect::<path::PathBuf>()
|
||||
}
|
||||
}
|
||||
|
||||
impl git2::IntoCString for Path {
|
||||
fn into_c_string(self) -> Result<CString, git2::Error> {
|
||||
if self.is_root() {
|
||||
// the root pathsec is empty
|
||||
"".into_c_string()
|
||||
} else {
|
||||
// build the file path pathsec
|
||||
let path = self.0.tail;
|
||||
let mut pathspec = "".to_string();
|
||||
for p in path.iter() {
|
||||
// If we have a label such as 'faux\path' we need to double escape it for
|
||||
// `git2::DiffOptions::pathspec` to work properly. As far as we're aware this is
|
||||
// the only use of IntoCString for Path.
|
||||
let label = p.label.replace('\\', "\\\\");
|
||||
let _ = write!(pathspec, "{label}/");
|
||||
}
|
||||
let pathspec = pathspec.trim_end_matches('/');
|
||||
pathspec.into_c_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Path {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Create a new `Path` with a single [`Label`].
|
||||
pub fn new(label: Label) -> Path {
|
||||
Path(NonEmpty::new(label))
|
||||
}
|
||||
|
||||
/// The root path is a `Path` made up of the single root label (see:
|
||||
/// [`Label::root`](#method.root).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
///
|
||||
/// let root = Path::root();
|
||||
/// assert_eq!(*root.split_first().0, Label::root());
|
||||
/// ```
|
||||
pub fn root() -> Self {
|
||||
Path(NonEmpty::new(Label::root()))
|
||||
}
|
||||
|
||||
/// Check that this is the root path.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::Path;
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// let root = Path::root();
|
||||
/// let not_root = unsound::path::new("src/lib.rs");
|
||||
///
|
||||
/// assert!(root.is_root());
|
||||
/// assert!(!not_root.is_root());
|
||||
/// ```
|
||||
pub fn is_root(&self) -> bool {
|
||||
*self == Self::root()
|
||||
}
|
||||
|
||||
/// Append two `Path`s together.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::Path;
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// let mut path1 = unsound::path::new("foo/bar");
|
||||
/// let path2 = unsound::path::new("baz/quux");
|
||||
/// path1.append(path2);
|
||||
/// let expected = unsound::path::new("foo/bar/baz/quux");
|
||||
/// assert_eq!(path1, expected);
|
||||
/// ```
|
||||
pub fn append(&mut self, path: Self) {
|
||||
let mut other = path.0.into();
|
||||
self.0.append(&mut other)
|
||||
}
|
||||
|
||||
/// Push a new [`Label`] onto the `Path`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let mut root = Path::root();
|
||||
/// root.push(unsound::label::new("src"));
|
||||
/// root.push(unsound::label::new("lib.rs"));
|
||||
///
|
||||
/// assert_eq!(root, unsound::path::new("~/src/lib.rs"));
|
||||
/// ```
|
||||
pub fn push(&mut self, label: Label) {
|
||||
self.0.push(label)
|
||||
}
|
||||
|
||||
/// Pop the [`Label`] from the end of the tail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let mut root = Path::root();
|
||||
/// root.push(unsound::label::new("src"));
|
||||
/// root.push(unsound::label::new("lib.rs"));
|
||||
///
|
||||
/// assert_eq!(root.pop(), Some(unsound::label::new("lib.rs")));
|
||||
/// ```
|
||||
pub fn pop(&mut self) -> Option<Label> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
/// Iterator over the [`Label`]s in the `Path`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let path = unsound::path::new("~/src/lib.rs");
|
||||
/// let mut path_iter = path.iter();
|
||||
///
|
||||
/// assert_eq!(path_iter.next(), Some(&Label::root()));
|
||||
/// assert_eq!(path_iter.next(), Some(&unsound::label::new("src")));
|
||||
/// assert_eq!(path_iter.next(), Some(&unsound::label::new("lib.rs")));
|
||||
/// ```
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Label> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Get the first [`Label`] in the `Path` and the rest of the [`Label`]s
|
||||
/// after it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let path = unsound::path::new("~/src/lib.rs");
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// path.split_first(),
|
||||
/// (&Label::root(), &[unsound::label::new("src"), unsound::label::new("lib.rs")][..])
|
||||
/// );
|
||||
/// ```
|
||||
pub fn split_first(&self) -> (&Label, &[Label]) {
|
||||
self.0.split_first()
|
||||
}
|
||||
|
||||
/// Get the prefix of the [`Label`]s and the last [`Label`].
|
||||
///
|
||||
/// This is useful when the prefix is a directory path and the last label is
|
||||
/// a file name.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let path = unsound::path::new("~/src/lib.rs");
|
||||
/// assert_eq!(path.split_last(), (vec![Label::root(), unsound::label::new("src")], unsound::label::new("lib.rs")));
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let path = unsound::path::new("foo/bar/baz");
|
||||
/// assert_eq!(
|
||||
/// path.split_last(),
|
||||
/// (vec![unsound::label::new("foo"), unsound::label::new("bar")], unsound::label::new("baz"))
|
||||
/// );
|
||||
/// ```
|
||||
pub fn split_last(self) -> (Vec<Label>, Label) {
|
||||
split_last(self.0)
|
||||
}
|
||||
|
||||
/// Construct a `Path` given at least one [`Label`] followed by 0 or more
|
||||
/// [`Label`]s.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use nonempty::NonEmpty;
|
||||
/// use radicle_surf::file_system::{Path, Label};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let path = Path::from_labels(
|
||||
/// Label::root(),
|
||||
/// &[unsound::label::new("foo"), unsound::label::new("bar"), unsound::label::new("baz.rs")]
|
||||
/// );
|
||||
///
|
||||
/// let mut expected = Path::root();
|
||||
/// expected.push(unsound::label::new("foo"));
|
||||
/// expected.push(unsound::label::new("bar"));
|
||||
/// expected.push(unsound::label::new("baz.rs"));
|
||||
///
|
||||
/// assert_eq!(path, expected);
|
||||
/// let path_vec: Vec<Label> = path.0.into();
|
||||
/// assert_eq!(
|
||||
/// path_vec,
|
||||
/// vec![Label::root(), unsound::label::new("foo"), unsound::label::new("bar"),
|
||||
/// unsound::label::new("baz.rs")]
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_labels(root: Label, labels: &[Label]) -> Path {
|
||||
Path((root, labels.to_vec()).into())
|
||||
}
|
||||
|
||||
/// Construct a `Path` using [`Label::root`](#method.root) as the head of
|
||||
/// the `Path.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use nonempty::NonEmpty;
|
||||
/// use radicle_surf::file_system::{Label, Path};
|
||||
/// use radicle_surf::file_system::unsound;
|
||||
///
|
||||
/// let path = Path::with_root(
|
||||
/// &[unsound::label::new("foo"), unsound::label::new("bar"), unsound::label::new("baz.rs")]
|
||||
/// );
|
||||
///
|
||||
/// let mut expected = Path::root();
|
||||
/// expected.push(unsound::label::new("foo"));
|
||||
/// expected.push(unsound::label::new("bar"));
|
||||
/// expected.push(unsound::label::new("baz.rs"));
|
||||
///
|
||||
/// assert_eq!(path, expected);
|
||||
/// let path_vec: Vec<Label> = path.0.into();
|
||||
/// assert_eq!(
|
||||
/// path_vec,
|
||||
/// vec![Label::root(), unsound::label::new("foo"), unsound::label::new("bar"),
|
||||
/// unsound::label::new("baz.rs")]
|
||||
/// );
|
||||
/// ```
|
||||
pub fn with_root(labels: &[Label]) -> Path {
|
||||
Path::from_labels(Label::root(), labels)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<path::PathBuf> for Path {
|
||||
type Error = error::Error;
|
||||
|
||||
fn try_from(path_buf: path::PathBuf) -> Result<Self, Self::Error> {
|
||||
let mut path = Path::root();
|
||||
for p in path_buf.iter() {
|
||||
let p = p.to_str().ok_or_else(|| error::label_invalid_utf8(p))?;
|
||||
let l = Label::try_from(p)?;
|
||||
path.push(l);
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// This file is part of radicle-surf
|
||||
// <https://github.com/radicle-dev/radicle-surf>
|
||||
//
|
||||
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 3 or
|
||||
// later as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! In Irish slang there exists the term "sound". One is a "sound" person if
|
||||
//! they are nice and you can rely on them. This module is the anithesis of
|
||||
//! being "sound", you might say it is "unsound".
|
||||
//!
|
||||
//! The aim of this module is to make testing easier. During test time, _we
|
||||
//! know_ that a string is going to be non-empty because we are using the
|
||||
//! literal `"sound_label"`. The same for knowing that the form
|
||||
//! `"what/a/sound/bunch"` is a valid path.
|
||||
//!
|
||||
//! On the other hand, if we do not control the data coming in we should use the
|
||||
//! more "sound" method of the [`std::convert::TryFrom`] instance for
|
||||
//! [`crate::file_system::Label`] and [`crate::file_system::Path`]
|
||||
//! to ensure we have valid data to use for further operations.
|
||||
|
||||
pub mod path {
|
||||
//! Unsound creation of [`Path`]s.
|
||||
|
||||
use crate::file_system::path::Path;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// **NB**: Use with caution!
|
||||
///
|
||||
/// Calls `try_from` on the input and expects it to not fail.
|
||||
///
|
||||
/// Used for testing and playground purposes.
|
||||
pub fn new(path: &str) -> Path {
|
||||
Path::try_from(path).expect("unsafe_path: Failed to parse path")
|
||||
}
|
||||
}
|
||||
|
||||
pub mod label {
|
||||
//! Unsound creation of [`Label`]s.
|
||||
|
||||
use crate::file_system::path::Label;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// **NB**: Use with caution!
|
||||
///
|
||||
/// Calls `try_from` on the input and expects it to not fail.
|
||||
///
|
||||
/// Used for testing and playground purposes.
|
||||
pub fn new(path: &str) -> Label {
|
||||
Label::try_from(path).expect("unsafe_path: Failed to parse label")
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
use std::{convert::TryFrom, str};
|
||||
|
||||
use radicle_git_ext::Oid;
|
||||
use git_ext::Oid;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
file_system,
|
||||
git::{Commit, Error, Repository, ToCommit},
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::git::{Commit, Error, Repository, ToCommit};
|
||||
|
||||
/// An iterator that produces the history of commits for a given `head`,
|
||||
/// in the `repo`.
|
||||
@@ -32,7 +33,7 @@ pub struct History<'a> {
|
||||
|
||||
/// Internal implementation, subject to refactoring.
|
||||
enum FilterBy {
|
||||
File { path: file_system::Path },
|
||||
File { path: PathBuf },
|
||||
}
|
||||
|
||||
impl<'a> History<'a> {
|
||||
@@ -61,8 +62,13 @@ impl<'a> History<'a> {
|
||||
///
|
||||
/// Note that it is possible that a filtered History becomes empty,
|
||||
/// even though calling `.head()` still returns the original head.
|
||||
pub fn by_path(mut self, path: file_system::Path) -> Self {
|
||||
self.filter_by = Some(FilterBy::File { path });
|
||||
pub fn by_path<P>(mut self, path: P) -> Self
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
self.filter_by = Some(FilterBy::File {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{collections::BTreeSet, convert::TryFrom, path::PathBuf, str};
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
convert::TryFrom,
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
};
|
||||
|
||||
use git_ref_format::{refspec::QualifiedPattern, Qualified};
|
||||
use radicle_git_ext::Oid;
|
||||
@@ -23,8 +28,7 @@ use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
diff::{self, *},
|
||||
file_system,
|
||||
file_system::{directory::FileContent, Directory, Label},
|
||||
file_system::{directory::FileContent, Directory},
|
||||
git::{
|
||||
commit,
|
||||
glob,
|
||||
@@ -58,9 +62,6 @@ pub enum Error {
|
||||
/// An error that comes from performing a *diff* operations.
|
||||
#[error(transparent)]
|
||||
Diff(#[from] diff::git::error::Diff),
|
||||
/// An error that comes from performing a [`crate::file_system`] operation.
|
||||
#[error(transparent)]
|
||||
FileSystem(#[from] file_system::Error),
|
||||
/// A wrapper around the generic [`git2::Error`].
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
@@ -72,7 +73,7 @@ pub enum Error {
|
||||
NotQualified(String),
|
||||
/// The requested file was not found.
|
||||
#[error("path not found for: {0}")]
|
||||
PathNotFound(file_system::Path),
|
||||
PathNotFound(PathBuf),
|
||||
#[error(transparent)]
|
||||
RefFormat(#[from] git_ref_format::Error),
|
||||
#[error(transparent)]
|
||||
@@ -187,19 +188,16 @@ impl Repository {
|
||||
.map_err(|err| Error::ToCommit(err.into()))?;
|
||||
let git2_commit = self.inner.find_commit((commit.id).into())?;
|
||||
let tree = git2_commit.as_object().peel_to_tree()?;
|
||||
Ok(Directory {
|
||||
name: Label::root(),
|
||||
id: tree.id().into(),
|
||||
})
|
||||
Ok(Directory::root(tree.id().into()))
|
||||
}
|
||||
|
||||
/// Returns the last commit, if exists, for a `path` in the history of
|
||||
/// `rev`.
|
||||
pub fn last_commit<C: ToCommit>(
|
||||
&self,
|
||||
path: file_system::Path,
|
||||
rev: C,
|
||||
) -> Result<Option<Commit>, Error> {
|
||||
pub fn last_commit<P, C>(&self, path: P, rev: C) -> Result<Option<Commit>, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
C: ToCommit,
|
||||
{
|
||||
let history = self.history(rev)?;
|
||||
history.by_path(path).next().transpose()
|
||||
}
|
||||
@@ -239,17 +237,20 @@ impl Repository {
|
||||
}
|
||||
|
||||
/// Retrieves the file with `path` in this commit.
|
||||
pub fn get_commit_file<R: Revision>(
|
||||
&self,
|
||||
rev: &R,
|
||||
path: file_system::Path,
|
||||
) -> Result<FileContent, crate::git::Error> {
|
||||
pub fn get_commit_file<P, R>(&self, rev: &R, path: &P) -> Result<FileContent, crate::git::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
R: Revision,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
let id = self.object_id(rev)?;
|
||||
let commit = self.get_git2_commit(id)?;
|
||||
let tree = commit.tree()?;
|
||||
let entry = tree.get_path(PathBuf::from(&path).as_ref())?;
|
||||
let entry = tree.get_path(path)?;
|
||||
let object = entry.to_object(self.git2_repo())?;
|
||||
let blob = object.into_blob().map_err(|_| Error::PathNotFound(path))?;
|
||||
let blob = object
|
||||
.into_blob()
|
||||
.map_err(|_| Error::PathNotFound(path.to_path_buf()))?;
|
||||
Ok(FileContent::new(blob))
|
||||
}
|
||||
|
||||
@@ -355,16 +356,19 @@ impl Repository {
|
||||
Ok(other == git2_oid || is_descendant)
|
||||
}
|
||||
|
||||
pub(crate) fn diff_commit_and_parents(
|
||||
pub(crate) fn diff_commit_and_parents<P>(
|
||||
&self,
|
||||
path: &file_system::Path,
|
||||
path: &P,
|
||||
commit: &git2::Commit,
|
||||
) -> Result<Option<file_system::Path>, Error> {
|
||||
) -> Result<Option<PathBuf>, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut parents = commit.parents();
|
||||
|
||||
let diff = self.diff_commits(Some(path), parents.next().as_ref(), commit)?;
|
||||
let diff = self.diff_commits(Some(path.as_ref()), parents.next().as_ref(), commit)?;
|
||||
if let Some(_delta) = diff.deltas().next() {
|
||||
Ok(Some(path.clone()))
|
||||
Ok(Some(path.as_ref().to_path_buf()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@@ -372,7 +376,7 @@ impl Repository {
|
||||
|
||||
fn diff_commits(
|
||||
&self,
|
||||
path: Option<&file_system::Path>,
|
||||
path: Option<&Path>,
|
||||
from: Option<&git2::Commit>,
|
||||
to: &git2::Commit,
|
||||
) -> Result<git2::Diff, Error> {
|
||||
|
||||
@@ -99,6 +99,3 @@ pub use revision::Revision;
|
||||
pub mod syntax;
|
||||
#[cfg(feature = "syntax")]
|
||||
pub use syntax::SYNTAX_SET;
|
||||
|
||||
// Private modules
|
||||
mod nonempty;
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// This file is part of radicle-surf
|
||||
// <https://github.com/radicle-dev/radicle-surf>
|
||||
//
|
||||
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 3 or
|
||||
// later as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use nonempty::NonEmpty;
|
||||
|
||||
pub fn split_last<T>(non_empty: NonEmpty<T>) -> (Vec<T>, T)
|
||||
where
|
||||
T: Eq,
|
||||
{
|
||||
let (head, mut tail) = non_empty.into();
|
||||
let last = tail.pop();
|
||||
match last {
|
||||
None => (vec![], head),
|
||||
Some(last) => {
|
||||
tail.insert(0, head);
|
||||
(tail, last)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
//! Common definitions for git objects (blob and tree).
|
||||
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{
|
||||
ser::{SerializeStruct as _, Serializer},
|
||||
@@ -30,11 +32,7 @@ pub use blob::{Blob, BlobContent};
|
||||
pub mod tree;
|
||||
pub use tree::{tree, Tree, TreeEntry};
|
||||
|
||||
use crate::{
|
||||
commit,
|
||||
file_system::{self, directory},
|
||||
git,
|
||||
};
|
||||
use crate::{commit, file_system::directory, git};
|
||||
|
||||
/// Git object types.
|
||||
///
|
||||
@@ -91,15 +89,11 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
Directory(#[from] directory::error::Directory),
|
||||
|
||||
/// An error occurred during a file system operation.
|
||||
#[error(transparent)]
|
||||
FileSystem(#[from] file_system::Error),
|
||||
|
||||
/// An error occurred during a git operation.
|
||||
#[error(transparent)]
|
||||
Git(#[from] git::Error),
|
||||
|
||||
/// Trying to find a file path which could not be found.
|
||||
#[error("the path '{0}' was not found")]
|
||||
PathNotFound(file_system::Path),
|
||||
PathNotFound(PathBuf),
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
//! Represents git object type 'blob', i.e. actual file contents.
|
||||
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
|
||||
|
||||
use std::str::{self, FromStr as _};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{
|
||||
@@ -28,7 +31,6 @@ use serde::{
|
||||
|
||||
use crate::{
|
||||
commit,
|
||||
file_system,
|
||||
git::Repository,
|
||||
object::{Error, Info, ObjectType},
|
||||
revision::Revision,
|
||||
@@ -44,7 +46,7 @@ pub struct Blob {
|
||||
/// Extra info for the file.
|
||||
pub info: Info,
|
||||
/// Absolute path to the object from the root of the repo.
|
||||
pub path: String,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
impl Blob {
|
||||
@@ -114,38 +116,42 @@ impl Serialize for BlobContent {
|
||||
///
|
||||
/// Will return [`Error`] if the project doesn't exist or a surf interaction
|
||||
/// fails.
|
||||
pub fn blob(
|
||||
repo: &Repository,
|
||||
maybe_revision: Option<Revision>,
|
||||
path: &str,
|
||||
) -> Result<Blob, Error> {
|
||||
pub fn blob<P>(repo: &Repository, maybe_revision: Option<Revision>, path: &P) -> Result<Blob, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
make_blob(repo, maybe_revision, path, content)
|
||||
}
|
||||
|
||||
fn make_blob<C>(
|
||||
fn make_blob<P, C>(
|
||||
repo: &Repository,
|
||||
maybe_revision: Option<Revision>,
|
||||
path: &str,
|
||||
path: &P,
|
||||
content: C,
|
||||
) -> Result<Blob, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
C: FnOnce(&[u8]) -> BlobContent,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
let revision = maybe_revision.unwrap();
|
||||
let root = repo.root_dir(&revision)?;
|
||||
let p = file_system::Path::from_str(path)?;
|
||||
|
||||
let file = root
|
||||
.find_file(p.clone(), repo)?
|
||||
.ok_or_else(|| Error::PathNotFound(p.clone()))?;
|
||||
|
||||
let mut commit_path = file_system::Path::root();
|
||||
commit_path.append(p.clone());
|
||||
.find_file(&path, repo)?
|
||||
.ok_or_else(|| Error::PathNotFound(path.to_path_buf()))?;
|
||||
|
||||
let last_commit = repo
|
||||
.last_commit(commit_path, &revision)?
|
||||
.last_commit(path, &revision)?
|
||||
.map(|c| commit::Header::from(&c));
|
||||
let (_rest, last) = p.split_last();
|
||||
// TODO: fuck this
|
||||
let name = path
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_os_string()
|
||||
.into_string()
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let file_content = repo.file_content(file)?;
|
||||
let content = content(file_content.as_bytes());
|
||||
@@ -153,11 +159,11 @@ where
|
||||
Ok(Blob {
|
||||
content,
|
||||
info: Info {
|
||||
name: last.to_string(),
|
||||
name,
|
||||
object_type: ObjectType::Blob,
|
||||
last_commit,
|
||||
},
|
||||
path: path.to_string(),
|
||||
path: path.to_path_buf(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! Represents git object type 'tree', i.e. like directory entries in Unix.
|
||||
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
|
||||
|
||||
use std::str::FromStr as _;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use git_ref_format::refname;
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -29,7 +29,7 @@ use serde::{
|
||||
|
||||
use crate::{
|
||||
commit,
|
||||
file_system::{self, directory},
|
||||
file_system::directory,
|
||||
git::Repository,
|
||||
object::{Error, Info, ObjectType},
|
||||
revision::Revision,
|
||||
@@ -38,7 +38,7 @@ use crate::{
|
||||
/// Result of a directory listing, carries other trees and blobs.
|
||||
pub struct Tree {
|
||||
/// Absolute path to the tree object from the repo root.
|
||||
pub path: String,
|
||||
pub path: PathBuf,
|
||||
/// Entries listed in that tree result.
|
||||
pub entries: Vec<TreeEntry>,
|
||||
/// Extra info for the tree object.
|
||||
@@ -65,7 +65,7 @@ pub struct TreeEntry {
|
||||
/// Extra info for the entry.
|
||||
pub info: Info,
|
||||
/// Absolute path to the object from the root of the repo.
|
||||
pub path: String,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -86,12 +86,15 @@ impl Serialize for TreeEntry {
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return [`Error`] if any of the surf interactions fail.
|
||||
pub fn tree(
|
||||
pub fn tree<P>(
|
||||
repo: &Repository,
|
||||
maybe_revision: Option<Revision>,
|
||||
maybe_prefix: Option<String>,
|
||||
) -> Result<Tree, Error> {
|
||||
let prefix = maybe_prefix.unwrap_or_default();
|
||||
maybe_prefix: Option<&P>,
|
||||
) -> Result<Tree, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let maybe_prefix = maybe_prefix.map(|p| p.as_ref());
|
||||
let rev = match maybe_revision {
|
||||
Some(r) => r,
|
||||
None => Revision::Branch {
|
||||
@@ -100,51 +103,42 @@ pub fn tree(
|
||||
},
|
||||
};
|
||||
|
||||
let path = if prefix == "/" || prefix.is_empty() {
|
||||
file_system::Path::root()
|
||||
} else {
|
||||
file_system::Path::from_str(&prefix)?
|
||||
let prefix_dir = match maybe_prefix {
|
||||
None => repo.root_dir(&rev)?,
|
||||
Some(path) => repo
|
||||
.root_dir(&rev)?
|
||||
.find_directory(path, repo)?
|
||||
.ok_or_else(|| Error::PathNotFound(path.to_path_buf()))?,
|
||||
};
|
||||
|
||||
let root_dir = repo.root_dir(&rev)?;
|
||||
let prefix_dir = if path.is_root() {
|
||||
root_dir
|
||||
} else {
|
||||
root_dir
|
||||
.find_directory(path.clone(), repo)?
|
||||
.ok_or_else(|| Error::PathNotFound(path.clone()))?
|
||||
};
|
||||
let mut entries = prefix_dir
|
||||
.entries(repo)?
|
||||
.entries()
|
||||
.fold(Vec::new(), |mut entries, entry| {
|
||||
let entry_path = match maybe_prefix {
|
||||
Some(path) => {
|
||||
let mut path = path.to_path_buf();
|
||||
path.push(entry.name());
|
||||
path
|
||||
},
|
||||
None => PathBuf::new(),
|
||||
};
|
||||
|
||||
let mut entries =
|
||||
prefix_dir
|
||||
.entries(repo)?
|
||||
.iter()
|
||||
.fold(Vec::new(), |mut entries, (label, entry)| {
|
||||
let entry_path = if path.is_root() {
|
||||
file_system::Path::new(label.clone())
|
||||
} else {
|
||||
let mut p = path.clone();
|
||||
p.push(label.clone());
|
||||
p
|
||||
};
|
||||
let mut commit_path = file_system::Path::root();
|
||||
commit_path.append(entry_path.clone());
|
||||
let info = Info {
|
||||
name: entry.name().clone(),
|
||||
object_type: match entry {
|
||||
directory::Entry::Directory(_) => ObjectType::Tree,
|
||||
directory::Entry::File { .. } => ObjectType::Blob,
|
||||
},
|
||||
last_commit: None,
|
||||
};
|
||||
|
||||
let info = Info {
|
||||
name: label.to_string(),
|
||||
object_type: match entry {
|
||||
directory::Entry::Directory(_) => ObjectType::Tree,
|
||||
directory::Entry::File { .. } => ObjectType::Blob,
|
||||
},
|
||||
last_commit: None,
|
||||
};
|
||||
|
||||
entries.push(TreeEntry {
|
||||
info,
|
||||
path: entry_path.to_string(),
|
||||
});
|
||||
entries
|
||||
entries.push(TreeEntry {
|
||||
info,
|
||||
path: entry_path,
|
||||
});
|
||||
entries
|
||||
});
|
||||
|
||||
// We want to ensure that in the response Tree entries come first. `Ord` being
|
||||
// derived on the enum ensures Variant declaration order.
|
||||
@@ -152,17 +146,15 @@ pub fn tree(
|
||||
// https://doc.rust-lang.org/std/cmp/trait.Ord.html#derivable
|
||||
entries.sort_by(|a, b| a.info.object_type.cmp(&b.info.object_type));
|
||||
|
||||
let last_commit = if path.is_root() {
|
||||
let last_commit = if maybe_prefix.is_none() {
|
||||
let history = repo.history(&rev)?;
|
||||
Some(commit::Header::from(history.head()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let name = if path.is_root() {
|
||||
"".into()
|
||||
} else {
|
||||
let (_first, last) = path.split_last();
|
||||
last.to_string()
|
||||
let name = match maybe_prefix {
|
||||
None => "".into(),
|
||||
Some(path) => path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
};
|
||||
let info = Info {
|
||||
name,
|
||||
@@ -171,7 +163,7 @@ pub fn tree(
|
||||
};
|
||||
|
||||
Ok(Tree {
|
||||
path: prefix,
|
||||
path: maybe_prefix.map_or(PathBuf::new(), |path| path.to_path_buf()),
|
||||
entries,
|
||||
info,
|
||||
})
|
||||
|
||||
@@ -3,31 +3,6 @@
|
||||
|
||||
//! Unit tests for radicle_surf::file_system
|
||||
|
||||
#[cfg(test)]
|
||||
mod path {
|
||||
use radicle_surf::file_system::unsound;
|
||||
|
||||
#[test]
|
||||
fn split_last_root_and_foo() {
|
||||
let path = unsound::path::new("foo");
|
||||
assert_eq!(path.split_last(), (vec![], unsound::label::new("foo")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_last_same_labels() {
|
||||
// An interesting case for when first == last, but doesn't imply a singleton
|
||||
// Path.
|
||||
let path = unsound::path::new("foo/bar/foo");
|
||||
assert_eq!(
|
||||
path.split_last(),
|
||||
(
|
||||
vec![unsound::label::new("foo"), unsound::label::new("bar")],
|
||||
unsound::label::new("foo")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod directory {
|
||||
use git_ref_format::refname;
|
||||
|
||||
+25
-23
@@ -7,7 +7,7 @@
|
||||
use radicle_surf::git::{Author, Commit};
|
||||
use radicle_surf::{
|
||||
diff::*,
|
||||
file_system::{directory, unsound, Path},
|
||||
file_system::directory,
|
||||
git::{Branch, Error, Glob, Oid, Repository},
|
||||
};
|
||||
|
||||
@@ -246,7 +246,10 @@ mod last_commit {
|
||||
use git_ref_format::refname;
|
||||
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn readme_missing_and_memory() {
|
||||
@@ -257,10 +260,7 @@ mod last_commit {
|
||||
|
||||
// memory.rs is commited later so it should not exist here.
|
||||
let memory_last_commit_oid = repo
|
||||
.last_commit(
|
||||
Path::with_root(&[unsound::label::new("src"), unsound::label::new("memory.rs")]),
|
||||
oid,
|
||||
)
|
||||
.last_commit(Path::new("src/memory.rs"), oid)
|
||||
.expect("Failed to get last commit")
|
||||
.map(|commit| commit.id);
|
||||
|
||||
@@ -268,7 +268,7 @@ mod last_commit {
|
||||
|
||||
// README.md exists in this commit.
|
||||
let readme_last_commit = repo
|
||||
.last_commit(Path::with_root(&[unsound::label::new("README.md")]), oid)
|
||||
.last_commit(Path::new("README.md"), oid)
|
||||
.expect("Failed to get last commit")
|
||||
.map(|commit| commit.id);
|
||||
|
||||
@@ -286,7 +286,7 @@ mod last_commit {
|
||||
let expected_commit_id = Oid::from_str("f3a089488f4cfd1a240a9c01b3fcc4c34a4e97b2").unwrap();
|
||||
|
||||
let folder_svelte = repo
|
||||
.last_commit(unsound::path::new("~/examples/Folder.svelte"), oid)
|
||||
.last_commit(Path::new("examples/Folder.svelte"), oid)
|
||||
.expect("Failed to get last commit")
|
||||
.map(|commit| commit.id);
|
||||
|
||||
@@ -305,7 +305,7 @@ mod last_commit {
|
||||
|
||||
let nested_directory_tree_commit_id = repo
|
||||
.last_commit(
|
||||
unsound::path::new("~/this/is/a/really/deeply/nested/directory/tree"),
|
||||
Path::new("this/is/a/really/deeply/nested/directory/tree"),
|
||||
oid,
|
||||
)
|
||||
.expect("Failed to get last commit")
|
||||
@@ -327,13 +327,13 @@ mod last_commit {
|
||||
let expected_commit_id = Oid::from_str("a0dd9122d33dff2a35f564d564db127152c88e02").unwrap();
|
||||
|
||||
let backslash_commit_id = repo
|
||||
.last_commit(unsound::path::new("~/special/faux\\path"), oid)
|
||||
.last_commit(Path::new(r"special/faux\\path"), oid)
|
||||
.expect("Failed to get last commit")
|
||||
.map(|commit| commit.id);
|
||||
assert_eq!(backslash_commit_id, Some(expected_commit_id));
|
||||
|
||||
let ogre_commit_id = repo
|
||||
.last_commit(unsound::path::new("~/special/👹👹👹"), oid)
|
||||
.last_commit(Path::new("special/👹👹👹"), oid)
|
||||
.expect("Failed to get last commit")
|
||||
.map(|commit| commit.id);
|
||||
assert_eq!(ogre_commit_id, Some(expected_commit_id));
|
||||
@@ -345,7 +345,7 @@ mod last_commit {
|
||||
.expect("Could not retrieve ./data/git-platinum as git repository");
|
||||
let rev = Branch::local(refname!("master"));
|
||||
let root_last_commit_id = repo
|
||||
.last_commit(Path::root(), &rev)
|
||||
.last_commit(&PathBuf::new(), &rev)
|
||||
.expect("Failed to get last commit")
|
||||
.map(|commit| commit.id);
|
||||
|
||||
@@ -362,7 +362,7 @@ mod last_commit {
|
||||
let repo = Repository::open(GIT_PLATINUM)
|
||||
.expect("Could not retrieve ./data/git-platinum as git repository");
|
||||
let history = repo.history(&Branch::local(refname!("dev"))).unwrap();
|
||||
let file_commit = history.by_path(unsound::path::new("~/bin/cat")).next();
|
||||
let file_commit = history.by_path(Path::new("bin/cat")).next();
|
||||
assert!(file_commit.is_some());
|
||||
println!("file commit: {:?}", &file_commit);
|
||||
}
|
||||
@@ -373,7 +373,7 @@ mod diff {
|
||||
use super::*;
|
||||
use git_ref_format::refname;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::str::FromStr;
|
||||
use std::{path::Path, str::FromStr};
|
||||
|
||||
#[test]
|
||||
fn test_initial_diff() -> Result<(), Error> {
|
||||
@@ -386,7 +386,7 @@ mod diff {
|
||||
|
||||
let expected_diff = Diff {
|
||||
created: vec![CreateFile {
|
||||
path: Path::with_root(&[unsound::label::new("README.md")]),
|
||||
path: Path::new("README.md").to_path_buf(),
|
||||
diff: FileDiff::Plain {
|
||||
hunks: vec![Hunk {
|
||||
header: Line::from(b"@@ -0,0 +1 @@\n".to_vec()),
|
||||
@@ -434,7 +434,7 @@ mod diff {
|
||||
moved: vec![],
|
||||
copied: vec![],
|
||||
modified: vec![ModifiedFile {
|
||||
path: Path::with_root(&[unsound::label::new("README.md")]),
|
||||
path: Path::new("README.md").to_path_buf(),
|
||||
diff: FileDiff::Plain {
|
||||
hunks: vec![Hunk {
|
||||
header: Line::from(b"@@ -1 +1,2 @@\n".to_vec()),
|
||||
@@ -474,16 +474,16 @@ mod diff {
|
||||
assert_eq!(diff.moved.len(), 1);
|
||||
assert_eq!(diff.modified.len(), 2);
|
||||
for c in diff.created.iter() {
|
||||
println!("created: {}", &c.path);
|
||||
println!("created: {:?}", &c.path);
|
||||
}
|
||||
for d in diff.deleted.iter() {
|
||||
println!("deleted: {}", &d.path);
|
||||
println!("deleted: {:?}", &d.path);
|
||||
}
|
||||
for m in diff.moved.iter() {
|
||||
println!("moved: {} -> {}", &m.old_path, &m.new_path);
|
||||
println!("moved: {:?} -> {:?}", &m.old_path, &m.new_path);
|
||||
}
|
||||
for m in diff.modified.iter() {
|
||||
println!("modified: {}", &m.path);
|
||||
println!("modified: {:?}", &m.path);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -717,6 +717,8 @@ mod reference {
|
||||
}
|
||||
|
||||
mod code_browsing {
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
|
||||
use git_ref_format::refname;
|
||||
@@ -740,7 +742,7 @@ mod code_browsing {
|
||||
repo,
|
||||
(0, 0),
|
||||
&mut |(count, indent_level), entry| {
|
||||
println!("> {}{}", " ".repeat(indent_level * 4), entry.label());
|
||||
println!("> {}{}", " ".repeat(indent_level * 4), entry.name());
|
||||
match entry {
|
||||
directory::Entry::File(_) => Ok((count + 1, indent_level)),
|
||||
directory::Entry::Directory(_) => Ok((count + 1, indent_level + 1)),
|
||||
@@ -775,11 +777,11 @@ mod code_browsing {
|
||||
fn test_file_history() {
|
||||
let repo = Repository::open(GIT_PLATINUM).unwrap();
|
||||
let history = repo.history(&Branch::local(refname!("dev"))).unwrap();
|
||||
let path = unsound::path::new("README.md");
|
||||
let path = Path::new("README.md");
|
||||
let mut file_history = history.by_path(path);
|
||||
let commit = file_history.next().unwrap().unwrap();
|
||||
let file = repo
|
||||
.get_commit_file(&commit.id, unsound::path::new("README.md"))
|
||||
.get_commit_file(&commit.id, &Path::new("README.md"))
|
||||
.unwrap();
|
||||
assert_eq!(file.size(), 67);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,3 @@ mod git;
|
||||
|
||||
#[cfg(test)]
|
||||
mod diff;
|
||||
|
||||
#[cfg(test)]
|
||||
mod file_system;
|
||||
|
||||
Reference in New Issue
Block a user