Skip to content

Commit

Permalink
feat: use gix-object::Find trait
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Nov 5, 2023
1 parent 36f70dc commit a81514f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 77 deletions.
42 changes: 16 additions & 26 deletions gix-worktree-state/src/checkout/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,22 @@ use std::{
};

use bstr::{BStr, BString};
use gix_hash::oid;
use gix_worktree::Stack;

use crate::{checkout, checkout::entry};

mod reduce {
use std::marker::PhantomData;

use crate::checkout;

pub struct Reduce<'entry, E> {
pub struct Reduce<'entry> {
pub aggregate: super::Outcome<'entry>,
pub marker: PhantomData<E>,
}

impl<'entry, E> gix_features::parallel::Reduce for Reduce<'entry, E>
where
E: std::error::Error + Send + Sync + 'static,
{
type Input = Result<super::Outcome<'entry>, checkout::Error<E>>;
impl<'entry> gix_features::parallel::Reduce for Reduce<'entry> {
type Input = Result<super::Outcome<'entry>, checkout::Error>;
type FeedProduce = ();
type Output = super::Outcome<'entry>;
type Error = checkout::Error<E>;
type Error = checkout::Error;

fn feed(&mut self, item: Self::Input) -> Result<Self::FeedProduce, Self::Error> {
let item = item?;
Expand Down Expand Up @@ -78,7 +71,7 @@ pub struct Outcome<'a> {

#[derive(Clone)]
pub struct Context<Find: Clone> {
pub find: Find,
pub objects: Find,
pub path_cache: Stack,
pub filters: gix_filter::Pipeline,
pub buf: Vec<u8>,
Expand Down Expand Up @@ -106,16 +99,15 @@ impl From<&checkout::Options> for Options {
}
}

pub fn process<'entry, Find, E>(
pub fn process<'entry, Find>(
entries_with_paths: impl Iterator<Item = (&'entry mut gix_index::Entry, &'entry BStr)>,
files: &AtomicUsize,
bytes: &AtomicUsize,
delayed_filter_results: &mut Vec<DelayedFilteredStream<'entry>>,
ctx: &mut Context<Find>,
) -> Result<Outcome<'entry>, checkout::Error<E>>
) -> Result<Outcome<'entry>, checkout::Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
E: std::error::Error + Send + Sync + 'static,
Find: gix_object::Find + Clone,
{
let mut delayed_symlinks = Vec::new();
let mut collisions = Vec::new();
Expand Down Expand Up @@ -162,16 +154,15 @@ where
})
}

pub fn process_delayed_filter_results<Find, E>(
pub fn process_delayed_filter_results<Find>(
mut delayed_filter_results: Vec<DelayedFilteredStream<'_>>,
files: &AtomicUsize,
bytes: &AtomicUsize,
out: &mut Outcome<'_>,
ctx: &mut Context<Find>,
) -> Result<(), checkout::Error<E>>
) -> Result<(), checkout::Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
E: std::error::Error + Send + Sync + 'static,
Find: gix_object::Find + Clone,
{
let Options {
destination_is_initially_empty,
Expand Down Expand Up @@ -288,30 +279,29 @@ where
}
}

pub fn checkout_entry_handle_result<'entry, Find, E>(
pub fn checkout_entry_handle_result<'entry, Find>(
entry: &'entry mut gix_index::Entry,
entry_path: &'entry BStr,
errors: &mut Vec<checkout::ErrorRecord>,
collisions: &mut Vec<checkout::Collision>,
files: &AtomicUsize,
bytes: &AtomicUsize,
Context {
find,
objects,
path_cache,
filters,
buf,
options,
}: &mut Context<Find>,
) -> Result<entry::Outcome<'entry>, checkout::Error<E>>
) -> Result<entry::Outcome<'entry>, checkout::Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Clone,
E: std::error::Error + Send + Sync + 'static,
Find: gix_object::Find + Clone,
{
let res = entry::checkout(
entry,
entry_path,
entry::Context {
find,
objects,
path_cache,
filters,
buf,
Expand Down
44 changes: 21 additions & 23 deletions gix-worktree-state/src/checkout/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use std::{

use bstr::BStr;
use gix_filter::{driver::apply::MaybeDelayed, pipeline::convert::ToWorktreeOutcome};
use gix_hash::oid;
use gix_index::{entry::Stat, Entry};
use gix_object::FindExt;
use gix_worktree::Stack;
use io_close::Close;

pub struct Context<'a, Find> {
pub find: &'a mut Find,
pub objects: &'a mut Find,
pub path_cache: &'a mut Stack,
pub filters: &'a mut gix_filter::Pipeline,
pub buf: &'a mut Vec<u8>,
Expand Down Expand Up @@ -53,11 +53,11 @@ impl Outcome<'_> {
}

#[cfg_attr(not(unix), allow(unused_variables))]
pub fn checkout<'entry, Find, E>(
pub fn checkout<'entry, Find>(
entry: &'entry mut Entry,
entry_path: &'entry BStr,
Context {
find,
objects,
filters,
path_cache,
buf,
Expand All @@ -73,25 +73,25 @@ pub fn checkout<'entry, Find, E>(
filter_process_delay,
..
}: crate::checkout::chunk::Options,
) -> Result<Outcome<'entry>, crate::checkout::Error<E>>
) -> Result<Outcome<'entry>, crate::checkout::Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E>,
E: std::error::Error + Send + Sync + 'static,
Find: gix_object::Find,
{
let dest_relative = gix_path::try_from_bstr(entry_path).map_err(|_| crate::checkout::Error::IllformedUtf8 {
path: entry_path.to_owned(),
})?;
let is_dir = Some(entry.mode == gix_index::entry::Mode::COMMIT || entry.mode == gix_index::entry::Mode::DIR);
let path_cache = path_cache.at_path(dest_relative, is_dir, &mut *find)?;
let path_cache = path_cache.at_path(dest_relative, is_dir, &*objects)?;
let dest = path_cache.path();

let object_size = match entry.mode {
gix_index::entry::Mode::FILE | gix_index::entry::Mode::FILE_EXECUTABLE => {
let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
err,
oid: entry.id,
path: dest.to_path_buf(),
})?;
let obj = (&*objects)
.find_blob(&entry.id, buf)
.map_err(|err| crate::checkout::Error::Find {
err,
path: dest.to_path_buf(),
})?;

let filtered = filters.convert_to_worktree(
obj.data,
Expand Down Expand Up @@ -140,11 +140,12 @@ where
num_bytes
}
gix_index::entry::Mode::SYMLINK => {
let obj = find(&entry.id, buf).map_err(|err| crate::checkout::Error::Find {
err,
oid: entry.id,
path: dest.to_path_buf(),
})?;
let obj = (&*objects)
.find_blob(&entry.id, buf)
.map_err(|err| crate::checkout::Error::Find {
err,
path: dest.to_path_buf(),
})?;
let symlink_destination = gix_path::try_from_byte_slice(obj.data)
.map_err(|_| crate::checkout::Error::IllformedUtf8 { path: obj.data.into() })?;

Expand Down Expand Up @@ -269,14 +270,11 @@ pub(crate) fn open_file(

/// Close `file` and store its stats in `entry`, possibly setting `file` executable depending on `set_executable_after_creation`.
#[cfg_attr(windows, allow(unused_variables))]
pub(crate) fn finalize_entry<E>(
pub(crate) fn finalize_entry(
entry: &mut gix_index::Entry,
file: std::fs::File,
set_executable_after_creation: Option<&Path>,
) -> Result<(), crate::checkout::Error<E>>
where
E: std::error::Error + Send + Sync + 'static,
{
) -> Result<(), crate::checkout::Error> {
// For possibly existing, overwritten files, we must change the file mode explicitly.
#[cfg(unix)]
if let Some(path) = set_executable_after_creation {
Expand Down
26 changes: 11 additions & 15 deletions gix-worktree-state/src/checkout/function.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::sync::atomic::AtomicBool;

use gix_features::{interrupt, parallel::in_parallel_with_finalize};
use gix_hash::oid;
use gix_worktree::{stack, Stack};

use crate::checkout::chunk;

/// Checkout the entire `index` into `dir`, and resolve objects found in index entries with `find` to write their content to their
/// Checkout the entire `index` into `dir`, and resolve objects found in index entries with `objects` to write their content to their
/// respective path in `dir`.
/// Use `files` to count each fully checked out file, and count the amount written `bytes`. If `should_interrupt` is `true`, the
/// operation will abort.
Expand All @@ -17,39 +16,37 @@ use crate::checkout::chunk;
/// Note that interruption still produce an `Ok(…)` value, so the caller should look at `should_interrupt` to communicate the outcome.
///
#[allow(clippy::too_many_arguments)]
pub fn checkout<Find, E>(
pub fn checkout<Find>(
index: &mut gix_index::State,
dir: impl Into<std::path::PathBuf>,
find: Find,
objects: Find,
files: &dyn gix_features::progress::Count,
bytes: &dyn gix_features::progress::Count,
should_interrupt: &AtomicBool,
options: crate::checkout::Options,
) -> Result<crate::checkout::Outcome, crate::checkout::Error<E>>
) -> Result<crate::checkout::Outcome, crate::checkout::Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Send + Clone,
E: std::error::Error + Send + Sync + 'static,
Find: gix_object::Find + Send + Clone,
{
let paths = index.take_path_backing();
let res = checkout_inner(index, &paths, dir, find, files, bytes, should_interrupt, options);
let res = checkout_inner(index, &paths, dir, objects, files, bytes, should_interrupt, options);
index.return_path_backing(paths);
res
}

#[allow(clippy::too_many_arguments)]
fn checkout_inner<Find, E>(
fn checkout_inner<Find>(
index: &mut gix_index::State,
paths: &gix_index::PathStorage,
dir: impl Into<std::path::PathBuf>,
find: Find,
objects: Find,
files: &dyn gix_features::progress::Count,
bytes: &dyn gix_features::progress::Count,
should_interrupt: &AtomicBool,
mut options: crate::checkout::Options,
) -> Result<crate::checkout::Outcome, crate::checkout::Error<E>>
) -> Result<crate::checkout::Outcome, crate::checkout::Error>
where
Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<gix_object::BlobRef<'a>, E> + Send + Clone,
E: std::error::Error + Send + Sync + 'static,
Find: gix_object::Find + Send + Clone,
{
let num_files = files.counter();
let num_bytes = bytes.counter();
Expand All @@ -72,7 +69,7 @@ where
paths,
),
filters: options.filters,
find,
objects,
};

let chunk::Outcome {
Expand Down Expand Up @@ -123,7 +120,6 @@ where
},
chunk::Reduce {
aggregate: Default::default(),
marker: Default::default(),
},
)?
};
Expand Down
7 changes: 3 additions & 4 deletions gix-worktree-state/src/checkout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,17 @@ pub struct Options {
/// The error returned by the [checkout()][crate::checkout()] function.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error<E: std::error::Error + Send + Sync + 'static> {
pub enum Error {
#[error("Could not convert path to UTF8: {}", .path)]
IllformedUtf8 { path: BString },
#[error("The clock was off when reading file related metadata after updating a file on disk")]
Time(#[from] std::time::SystemTimeError),
#[error("IO error while writing blob or reading file metadata or changing filetype")]
Io(#[from] std::io::Error),
#[error("object {} for checkout at {} could not be retrieved from object database", .oid.to_hex(), .path.display())]
#[error("object for checkout at {} could not be retrieved from object database", .path.display())]
Find {
#[source]
err: E,
oid: gix_hash::ObjectId,
err: gix_object::find::existing_object::Error,
path: std::path::PathBuf,
},
#[error(transparent)]
Expand Down
40 changes: 31 additions & 9 deletions gix-worktree-state/tests/state/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{

use gix_features::progress;
use gix_object::bstr::ByteSlice;
use gix_object::FindExt;
use gix_object::Data;
use gix_testtools::tempfile::TempDir;
use gix_worktree_state::checkout::Collision;
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -487,7 +487,7 @@ fn checkout_index_in_tmp_dir(
fn checkout_index_in_tmp_dir_opts(
opts: gix_worktree_state::checkout::Options,
name: &str,
mut allow_return_object: impl FnMut(&gix_hash::oid) -> bool + Send + Clone,
allow_return_object: impl FnMut(&gix_hash::oid) -> bool + Send + Clone,
prep_dest: impl Fn(&Path) -> std::io::Result<()>,
) -> crate::Result<(PathBuf, TempDir, gix_index::File, gix_worktree_state::checkout::Outcome)> {
let source_tree = fixture_path(name);
Expand All @@ -497,16 +497,38 @@ fn checkout_index_in_tmp_dir_opts(
let destination = gix_testtools::tempfile::tempdir_in(std::env::current_dir()?)?;
prep_dest(destination.path()).expect("preparation must succeed");

#[derive(Clone)]
struct MaybeFind<Allow: Clone, Find: Clone> {
allow: std::cell::RefCell<Allow>,
objects: Find,
}

impl<Allow, Find> gix_object::Find for MaybeFind<Allow, Find>
where
Allow: FnMut(&gix_hash::oid) -> bool + Send + Clone,
Find: gix_object::Find + Send + Clone,
{
fn try_find<'a>(
&self,
id: &gix_hash::oid,
buf: &'a mut Vec<u8>,
) -> Result<Option<Data<'a>>, gix_object::find::Error> {
if (self.allow.borrow_mut())(id) {
self.objects.try_find(id, buf)
} else {
Ok(None)
}
}
}

let db = MaybeFind {
allow: allow_return_object.into(),
objects: odb,
};
let outcome = gix_worktree_state::checkout(
&mut index,
destination.path(),
move |oid, buf| {
if allow_return_object(oid) {
odb.find_blob(oid, buf)
} else {
Err(gix_object::find::existing_object::Error::NotFound { oid: oid.to_owned() })
}
},
db,
&progress::Discard,
&progress::Discard,
&AtomicBool::default(),
Expand Down

0 comments on commit a81514f

Please sign in to comment.