gnostr_asyncgit/
remote_progress.rs

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