gnostr_asyncgit/
commit_files.rs

1use std::sync::{
2    atomic::{AtomicUsize, Ordering},
3    Arc, Mutex,
4};
5
6use crossbeam_channel::Sender;
7
8use crate::{
9    error::Result,
10    sync::{self, commit_files::OldNew, CommitId, RepoPath},
11    AsyncGitNotification, StatusItem,
12};
13
14type ResultType = Vec<StatusItem>;
15struct Request<R, A>(R, A);
16
17///
18#[derive(Debug, Copy, Clone, PartialEq, Eq)]
19pub struct CommitFilesParams {
20    ///
21    pub id: CommitId,
22    ///
23    pub other: Option<CommitId>,
24}
25
26impl From<CommitId> for CommitFilesParams {
27    fn from(id: CommitId) -> Self {
28        Self { id, other: None }
29    }
30}
31
32impl From<(CommitId, CommitId)> for CommitFilesParams {
33    fn from((id, other): (CommitId, CommitId)) -> Self {
34        Self {
35            id,
36            other: Some(other),
37        }
38    }
39}
40
41impl From<OldNew<CommitId>> for CommitFilesParams {
42    fn from(old_new: OldNew<CommitId>) -> Self {
43        Self {
44            id: old_new.new,
45            other: Some(old_new.old),
46        }
47    }
48}
49
50///
51pub struct AsyncCommitFiles {
52    current: Arc<Mutex<Option<Request<CommitFilesParams, ResultType>>>>,
53    sender: Sender<AsyncGitNotification>,
54    pending: Arc<AtomicUsize>,
55    repo: RepoPath,
56}
57
58impl AsyncCommitFiles {
59    ///
60    pub fn new(repo: RepoPath, sender: &Sender<AsyncGitNotification>) -> Self {
61        Self {
62            repo,
63            current: Arc::new(Mutex::new(None)),
64            sender: sender.clone(),
65            pending: Arc::new(AtomicUsize::new(0)),
66        }
67    }
68
69    ///
70    pub fn current(&mut self) -> Result<Option<(CommitFilesParams, ResultType)>> {
71        let c = self.current.lock()?;
72
73        c.as_ref()
74            .map_or(Ok(None), |c| Ok(Some((c.0, c.1.clone()))))
75    }
76
77    ///
78    pub fn is_pending(&self) -> bool {
79        self.pending.load(Ordering::Relaxed) > 0
80    }
81
82    ///
83    pub fn fetch(&mut self, params: CommitFilesParams) -> Result<()> {
84        if self.is_pending() {
85            return Ok(());
86        }
87
88        log::trace!("request: {:?}", params);
89
90        {
91            let current = self.current.lock()?;
92            if let Some(c) = &*current {
93                if c.0 == params {
94                    return Ok(());
95                }
96            }
97        }
98
99        let arc_current = Arc::clone(&self.current);
100        let sender = self.sender.clone();
101        let arc_pending = Arc::clone(&self.pending);
102        let repo = self.repo.clone();
103
104        self.pending.fetch_add(1, Ordering::Relaxed);
105
106        rayon_core::spawn(move || {
107            Self::fetch_helper(&repo, params, &arc_current).expect("failed to fetch");
108
109            arc_pending.fetch_sub(1, Ordering::Relaxed);
110
111            sender
112                .send(AsyncGitNotification::CommitFiles)
113                .expect("error sending");
114        });
115
116        Ok(())
117    }
118
119    fn fetch_helper(
120        repo_path: &RepoPath,
121        params: CommitFilesParams,
122        arc_current: &Arc<Mutex<Option<Request<CommitFilesParams, ResultType>>>>,
123    ) -> Result<()> {
124        let res = sync::get_commit_files(repo_path, params.id, params.other)?;
125
126        log::trace!("get_commit_files: {:?} ({})", params, res.len());
127
128        {
129            let mut current = arc_current.lock()?;
130            *current = Some(Request(params, res));
131        }
132
133        Ok(())
134    }
135}