diff options
Diffstat (limited to 'src/diff_thread')
-rw-r--r-- | src/diff_thread/client.rs | 31 | ||||
-rw-r--r-- | src/diff_thread/mod.rs | 28 | ||||
-rw-r--r-- | src/diff_thread/server.rs | 39 |
3 files changed, 98 insertions, 0 deletions
diff --git a/src/diff_thread/client.rs b/src/diff_thread/client.rs new file mode 100644 index 0000000..dcc9cfe --- /dev/null +++ b/src/diff_thread/client.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: EUPL-1.2 +// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> + +use super::{server, FileDiff, MakeDiff}; + +use std::sync::mpsc::{sync_channel, SyncSender}; +use std::thread::spawn; + +use git2::Repository; + +pub struct DiffThread<D: MakeDiff> { + sender: SyncSender<(D, SyncSender<Result<FileDiff, git2::Error>>)>, +} + +impl<D: MakeDiff> DiffThread<D> { + pub fn new(repo: Repository) -> Self { + let (sender, receiver) = sync_channel(0); + + spawn(move || server::main(&repo, receiver)); + + Self { sender } + } + + pub fn diff_files(&self, diff_maker: D) -> impl Iterator<Item = Result<FileDiff, git2::Error>> { + let (sender, receiver) = sync_channel(2); + self.sender + .send((diff_maker, sender)) + .expect("sending to diff thread"); + receiver.into_iter() + } +} diff --git a/src/diff_thread/mod.rs b/src/diff_thread/mod.rs new file mode 100644 index 0000000..9416509 --- /dev/null +++ b/src/diff_thread/mod.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: EUPL-1.2 +// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> + +//! libgit2's diffing API gives us a callback whenever a file diff is ready, but +//! that's not very convenient for Rust code, because it can't be used as an +//! iterator. So we run the diff in a thread, and send the diff information we need +//! to the main thread over a channel. When the main thread is processing diff +//! information too slowly, we block the diff thread until the main thread catches +//! up. + +mod server; +mod client; + +pub use client::DiffThread; + +use std::path::PathBuf; + +use git2::{Repository, Diff, Delta}; + +pub trait MakeDiff: Send + 'static { + fn make_diff(self, _: &Repository) -> Result<Diff, git2::Error>; +} + +/// `new_path` isn't included because it's not currently used in git-girf. +pub struct FileDiff { + pub status: Delta, + pub old_path: Option<PathBuf>, +} diff --git a/src/diff_thread/server.rs b/src/diff_thread/server.rs new file mode 100644 index 0000000..d6d43ab --- /dev/null +++ b/src/diff_thread/server.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: EUPL-1.2 +// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> + +use super::{FileDiff, MakeDiff}; + +use std::sync::mpsc::{Receiver, SyncSender}; + +use git2::Repository; + +pub fn main( + repository: &Repository, + receiver: Receiver<(impl MakeDiff, SyncSender<Result<FileDiff, git2::Error>>)>, +) { + for (diff_maker, sender) in receiver { + let diff = match diff_maker.make_diff(&repository) { + Ok(diff) => diff, + Err(e) => { + let _ = sender.send(Err(e)); + return; + } + }; + + if let Err(e) = diff.foreach( + &mut |delta, _| { + let _ = sender.send(Ok(FileDiff { + status: delta.status(), + old_path: delta.old_file().path().map(Into::into), + })); + + true + }, + None, + None, + None, + ) { + let _ = sender.send(Err(e)); + } + } +} |