gnostr_asyncgit/
tags.rs

1use std::{
2    sync::{Arc, Mutex},
3    time::{Duration, Instant},
4};
5
6use crossbeam_channel::Sender;
7use sync::Tags;
8
9use crate::{
10    asyncjob::{AsyncJob, AsyncSingleJob, RunParams},
11    error::Result,
12    hash,
13    sync::{self, RepoPath},
14    AsyncGitNotification,
15};
16
17///
18#[derive(Default, Clone)]
19pub struct TagsResult {
20    hash: u64,
21    tags: Tags,
22}
23
24///
25pub struct AsyncTags {
26    last: Option<(Instant, TagsResult)>,
27    sender: Sender<AsyncGitNotification>,
28    job: AsyncSingleJob<AsyncTagsJob>,
29    repo: RepoPath,
30}
31
32impl AsyncTags {
33    ///
34    pub fn new(repo: RepoPath, sender: &Sender<AsyncGitNotification>) -> Self {
35        Self {
36            repo,
37            last: None,
38            sender: sender.clone(),
39            job: AsyncSingleJob::new(sender.clone()),
40        }
41    }
42
43    /// last fetched result
44    pub fn last(&self) -> Result<Option<Tags>> {
45        Ok(self.last.as_ref().map(|result| result.1.tags.clone()))
46    }
47
48    ///
49    pub fn is_pending(&self) -> bool {
50        self.job.is_pending()
51    }
52
53    ///
54    fn is_outdated(&self, dur: Duration) -> bool {
55        self.last
56            .as_ref()
57            .map_or(true, |(last_time, _)| last_time.elapsed() > dur)
58    }
59
60    ///
61    pub fn request(&mut self, dur: Duration, force: bool) -> Result<()> {
62        log::trace!("request");
63
64        if !force && self.job.is_pending() {
65            return Ok(());
66        }
67
68        let outdated = self.is_outdated(dur);
69
70        if !force && !outdated {
71            return Ok(());
72        }
73
74        let repo = self.repo.clone();
75
76        if outdated {
77            self.job.spawn(AsyncTagsJob::new(
78                self.last.as_ref().map_or(0, |(_, result)| result.hash),
79                repo,
80            ));
81
82            if let Some(job) = self.job.take_last() {
83                if let Some(Ok(result)) = job.result() {
84                    self.last = Some(result);
85                }
86            }
87        } else {
88            self.sender.send(AsyncGitNotification::FinishUnchanged)?;
89        }
90
91        Ok(())
92    }
93}
94
95enum JobState {
96    Request(u64, RepoPath),
97    Response(Result<(Instant, TagsResult)>),
98}
99
100///
101#[derive(Clone, Default)]
102pub struct AsyncTagsJob {
103    state: Arc<Mutex<Option<JobState>>>,
104}
105
106///
107impl AsyncTagsJob {
108    ///
109    pub fn new(last_hash: u64, repo: RepoPath) -> Self {
110        Self {
111            state: Arc::new(Mutex::new(Some(JobState::Request(last_hash, repo)))),
112        }
113    }
114
115    ///
116    pub fn result(&self) -> Option<Result<(Instant, TagsResult)>> {
117        if let Ok(mut state) = self.state.lock() {
118            if let Some(state) = state.take() {
119                return match state {
120                    JobState::Request(_, _) => None,
121                    JobState::Response(result) => Some(result),
122                };
123            }
124        }
125
126        None
127    }
128}
129
130impl AsyncJob for AsyncTagsJob {
131    type Notification = AsyncGitNotification;
132    type Progress = ();
133
134    fn run(
135        &mut self,
136        _params: RunParams<Self::Notification, Self::Progress>,
137    ) -> Result<Self::Notification> {
138        let mut notification = AsyncGitNotification::FinishUnchanged;
139        if let Ok(mut state) = self.state.lock() {
140            *state = state.take().map(|state| match state {
141                JobState::Request(last_hash, repo) => {
142                    let tags = sync::get_tags(&repo);
143
144                    JobState::Response(tags.map(|tags| {
145                        let hash = hash(&tags);
146                        if last_hash != hash {
147                            notification = AsyncGitNotification::Tags;
148                        }
149
150                        (Instant::now(), TagsResult { hash, tags })
151                    }))
152                }
153                JobState::Response(result) => JobState::Response(result),
154            });
155        }
156
157        Ok(notification)
158    }
159}