gnostr_asyncgit/
status.rs

1use std::{
2	hash::Hash,
3	sync::{
4		Arc, Mutex,
5		atomic::{AtomicUsize, Ordering},
6	},
7	time::{SystemTime, UNIX_EPOCH},
8};
9
10use crossbeam_channel::Sender;
11
12use crate::{
13	AsyncGitNotification, StatusItem,
14	error::Result,
15	hash,
16	sync::{
17		self, RepoPath, ShowUntrackedFilesConfig, status::StatusType,
18	},
19};
20
21fn current_tick() -> u128 {
22	SystemTime::now()
23		.duration_since(UNIX_EPOCH)
24		.expect("time before unix epoch!")
25		.as_millis()
26}
27
28#[derive(Default, Hash, Clone)]
29pub struct Status {
30	pub items: Vec<StatusItem>,
31}
32
33///
34#[derive(Default, Hash, Copy, Clone, PartialEq, Eq)]
35pub struct StatusParams {
36	tick: u128,
37	status_type: StatusType,
38	config: Option<ShowUntrackedFilesConfig>,
39}
40
41impl StatusParams {
42	///
43	pub fn new(
44		status_type: StatusType,
45		config: Option<ShowUntrackedFilesConfig>,
46	) -> Self {
47		Self {
48			tick: current_tick(),
49			status_type,
50			config,
51		}
52	}
53}
54
55struct Request<R, A>(R, Option<A>);
56
57///
58pub struct AsyncStatus {
59	current: Arc<Mutex<Request<u64, Status>>>,
60	last: Arc<Mutex<Status>>,
61	sender: Sender<AsyncGitNotification>,
62	pending: Arc<AtomicUsize>,
63	repo: RepoPath,
64}
65
66impl AsyncStatus {
67	///
68	pub fn new(
69		repo: RepoPath,
70		sender: Sender<AsyncGitNotification>,
71	) -> Self {
72		Self {
73			repo,
74			current: Arc::new(Mutex::new(Request(0, None))),
75			last: Arc::new(Mutex::new(Status::default())),
76			sender,
77			pending: Arc::new(AtomicUsize::new(0)),
78		}
79	}
80
81	///
82	pub fn last(&mut self) -> Result<Status> {
83		let last = self.last.lock()?;
84		Ok(last.clone())
85	}
86
87	///
88	pub fn is_pending(&self) -> bool {
89		self.pending.load(Ordering::Relaxed) > 0
90	}
91
92	///
93	pub fn fetch(
94		&mut self,
95		params: &StatusParams,
96	) -> Result<Option<Status>> {
97		if self.is_pending() {
98			log::trace!("request blocked, still pending");
99			return Ok(None);
100		}
101
102		let hash_request = hash(&params);
103
104		log::trace!(
105			"request: [hash: {}] (type: {:?})",
106			hash_request,
107			params.status_type,
108		);
109
110		{
111			let mut current = self.current.lock()?;
112
113			if current.0 == hash_request {
114				return Ok(current.1.clone());
115			}
116
117			current.0 = hash_request;
118			current.1 = None;
119		}
120
121		let arc_current = Arc::clone(&self.current);
122		let arc_last = Arc::clone(&self.last);
123		let sender = self.sender.clone();
124		let arc_pending = Arc::clone(&self.pending);
125		let status_type = params.status_type;
126		let config = params.config;
127		let repo = self.repo.clone();
128
129		self.pending.fetch_add(1, Ordering::Relaxed);
130
131		rayon_core::spawn(move || {
132			if let Err(e) = Self::fetch_helper(
133				&repo,
134				status_type,
135				config,
136				hash_request,
137				&arc_current,
138				&arc_last,
139			) {
140				log::error!("fetch_helper: {}", e);
141			}
142
143			arc_pending.fetch_sub(1, Ordering::Relaxed);
144
145			sender
146				.send(AsyncGitNotification::Status)
147				.expect("error sending status");
148		});
149
150		Ok(None)
151	}
152
153	fn fetch_helper(
154		repo: &RepoPath,
155		status_type: StatusType,
156		config: Option<ShowUntrackedFilesConfig>,
157		hash_request: u64,
158		arc_current: &Arc<Mutex<Request<u64, Status>>>,
159		arc_last: &Arc<Mutex<Status>>,
160	) -> Result<()> {
161		let res = Self::get_status(repo, status_type, config)?;
162		log::trace!(
163			"status fetched: {} (type: {:?})",
164			hash_request,
165			status_type,
166		);
167
168		{
169			let mut current = arc_current.lock()?;
170			if current.0 == hash_request {
171				current.1 = Some(res.clone());
172			}
173		}
174
175		{
176			let mut last = arc_last.lock()?;
177			*last = res;
178		}
179
180		Ok(())
181	}
182
183	fn get_status(
184		repo: &RepoPath,
185		status_type: StatusType,
186		config: Option<ShowUntrackedFilesConfig>,
187	) -> Result<Status> {
188		Ok(Status {
189			items: sync::status::get_status(
190				repo,
191				status_type,
192				config,
193			)?,
194		})
195	}
196}