Skip to main content

asyncgit/
remote_progress.rs

1//!
2
3use crate::{
4	error::Result,
5	progress::ProgressPercent,
6	sync::remotes::push::{AsyncProgress, ProgressNotification},
7	AsyncGitNotification,
8};
9use crossbeam_channel::{Receiver, Sender};
10use git2::PackBuilderStage;
11use std::{
12	sync::{Arc, Mutex},
13	thread::{self, JoinHandle},
14};
15
16/// used for push/pull
17#[derive(Clone, Debug)]
18pub enum RemoteProgressState {
19	///
20	PackingAddingObject,
21	///
22	PackingDeltafiction,
23	///
24	Pushing,
25	/// fetch progress
26	Transfer,
27	/// remote progress done
28	Done,
29}
30
31///
32#[derive(Clone, Debug)]
33pub struct RemoteProgress {
34	///
35	pub state: RemoteProgressState,
36	///
37	pub progress: ProgressPercent,
38}
39
40impl RemoteProgress {
41	///
42	pub fn new(
43		state: RemoteProgressState,
44		current: usize,
45		total: usize,
46	) -> Self {
47		Self {
48			state,
49			progress: ProgressPercent::new(current, total),
50		}
51	}
52
53	///
54	pub const fn get_progress_percent(&self) -> u8 {
55		self.progress.progress
56	}
57
58	pub(crate) fn set_progress<T>(
59		progress: &Arc<Mutex<Option<T>>>,
60		state: Option<T>,
61	) -> Result<()> {
62		let mut progress = progress.lock()?;
63
64		*progress = state;
65
66		Ok(())
67	}
68
69	/// spawn thread to listen to progress notifications coming in from blocking remote git method (fetch/push)
70	pub(crate) fn spawn_receiver_thread<
71		T: 'static + AsyncProgress,
72	>(
73		notification_type: AsyncGitNotification,
74		sender: Sender<AsyncGitNotification>,
75		receiver: Receiver<T>,
76		progress: Arc<Mutex<Option<T>>>,
77	) -> JoinHandle<()> {
78		thread::spawn(move || loop {
79			let incoming = receiver.recv();
80			match incoming {
81				Ok(update) => {
82					Self::set_progress(
83						&progress,
84						Some(update.clone()),
85					)
86					.expect("set progress failed");
87					sender
88						.send(notification_type)
89						.expect("Notification error");
90
91					thread::yield_now();
92
93					if update.is_done() {
94						break;
95					}
96				}
97				Err(e) => {
98					log::error!(
99						"remote progress receiver error: {e}",
100					);
101					break;
102				}
103			}
104		})
105	}
106}
107
108impl From<ProgressNotification> for RemoteProgress {
109	fn from(progress: ProgressNotification) -> Self {
110		match progress {
111			ProgressNotification::Packing {
112				stage,
113				current,
114				total,
115			} => match stage {
116				PackBuilderStage::AddingObjects => Self::new(
117					RemoteProgressState::PackingAddingObject,
118					current,
119					total,
120				),
121				PackBuilderStage::Deltafication => Self::new(
122					RemoteProgressState::PackingDeltafiction,
123					current,
124					total,
125				),
126			},
127			ProgressNotification::PushTransfer {
128				current,
129				total,
130				..
131			} => Self::new(
132				RemoteProgressState::Pushing,
133				current,
134				total,
135			),
136			ProgressNotification::Transfer {
137				objects,
138				total_objects,
139				..
140			} => Self::new(
141				RemoteProgressState::Transfer,
142				objects,
143				total_objects,
144			),
145			_ => Self::new(RemoteProgressState::Done, 1, 1),
146		}
147	}
148}