use crate::{ error::Result, hash, sync::{self, CommitId}, AsyncNotification, FileDiff, CWD, }; use crossbeam_channel::Sender; use std::{ hash::Hash, sync::{ atomic::{AtomicUsize, Ordering}, Arc, Mutex, }, }; /// #[derive(Hash, Clone, PartialEq)] pub enum DiffType { /// diff in a given commit Commit(CommitId), /// diff against staged file Stage, /// diff against file in workdir WorkDir, } /// #[derive(Hash, Clone, PartialEq)] pub struct DiffParams { /// path to the file to diff pub path: String, /// what kind of diff pub diff_type: DiffType, } struct Request(R, Option); #[derive(Default, Clone)] struct LastResult { params: P, hash: u64, result: R, } /// pub struct AsyncDiff { current: Arc>>, last: Arc>>>, sender: Sender, pending: Arc, } impl AsyncDiff { /// pub fn new(sender: Sender) -> Self { Self { current: Arc::new(Mutex::new(Request(0, None))), last: Arc::new(Mutex::new(None)), sender, pending: Arc::new(AtomicUsize::new(0)), } } /// pub fn last(&mut self) -> Result> { let last = self.last.lock()?; Ok(match last.clone() { Some(res) => Some((res.params, res.result)), None => None, }) } /// pub fn refresh(&mut self) -> Result<()> { if let Ok(Some(param)) = self.get_last_param() { self.clear_current()?; self.request(param)?; } Ok(()) } /// pub fn is_pending(&self) -> bool { self.pending.load(Ordering::Relaxed) > 0 } /// pub fn request( &mut self, params: DiffParams, ) -> Result> { log::trace!("request"); let hash = hash(¶ms); { let mut current = self.current.lock()?; if current.0 == hash { return Ok(current.1.clone()); } current.0 = hash; current.1 = None; } let arc_current = Arc::clone(&self.current); let arc_last = Arc::clone(&self.last); let sender = self.sender.clone(); let arc_pending = Arc::clone(&self.pending); rayon_core::spawn(move || { arc_pending.fetch_add(1, Ordering::Relaxed); let notify = AsyncDiff::get_diff_helper( params, arc_last, arc_current, hash, ) .expect("error getting diff"); arc_pending.fetch_sub(1, Ordering::Relaxed); sender .send(if notify { AsyncNotification::Diff } else { AsyncNotification::FinishUnchanged }) .expect("error sending diff"); }); Ok(None) } fn get_diff_helper( params: DiffParams, arc_last: Arc< Mutex>>, >, arc_current: Arc>>, hash: u64, ) -> Result { let res = match params.diff_type { DiffType::Stage => { sync::diff::get_diff(CWD, params.path.clone(), true)? } DiffType::WorkDir => { sync::diff::get_diff(CWD, params.path.clone(), false)? } DiffType::Commit(id) => sync::diff::get_diff_commit( CWD, id, params.path.clone(), )?, }; let mut notify = false; { let mut current = arc_current.lock()?; if current.0 == hash { current.1 = Some(res.clone()); notify = true; } } { let mut last = arc_last.lock()?; *last = Some(LastResult { result: res, hash, params, }); } Ok(notify) } fn get_last_param(&self) -> Result> { Ok(self.last.lock()?.clone().map(|e| e.params)) } fn clear_current(&mut self) -> Result<()> { let mut current = self.current.lock()?; current.0 = 0; current.1 = None; Ok(()) } }