hf_hub_simple_progress/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#![cfg_attr(feature = "async-closure", feature(async_fn_traits, unboxed_closures))]
#![doc = include_str!("../README.md")]
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};

#[cfg(feature = "sync")]
pub mod sync;

#[cfg(feature = "async-closure")]
pub mod async_closure;

/// The download progress event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProgressEvent {
    /// The resource to download
    pub url: String,

    /// The progress expressed as a value between 0 and 1
    pub percentage: f32,

    /// Time elapsed since the download as being started
    pub elapsed_time: Duration,

    /// Estimated time to complete the download
    pub remaining_time: Duration,
}

/// Store the state of a download
#[derive(Debug, Clone)]
struct DownloadState {
    start_time: Instant,
    len: usize,
    offset: usize,
    url: String,
}

impl DownloadState {
    fn new(len: usize, url: &str) -> DownloadState {
        DownloadState {
            start_time: Instant::now(),
            len,
            offset: 0,
            url: url.to_string(),
        }
    }

    fn update(&mut self, delta: usize) -> Option<ProgressEvent> {
        if delta == 0 {
            return None;
        }

        self.offset += delta;

        let elapsed_time = Instant::now() - self.start_time;

        let progress = self.offset as f32 / self.len as f32;
        let progress_100 = progress * 100.;

        let remaining_percentage = 100. - progress_100;
        let duration_unit = elapsed_time
            / if progress_100 as u32 == 0 {
                1
            } else {
                progress_100 as u32
            };
        let remaining_time = duration_unit * remaining_percentage as u32;

        let event = ProgressEvent {
            url: self.url.clone(),
            percentage: progress,
            elapsed_time,
            remaining_time,
        };
        Some(event)
    }
}

#[cfg(test)]
mod tests {
    use crate::DownloadState;
    use std::thread::sleep;
    use std::time::Duration;

    #[test]
    fn it_works() {
        let mut state = DownloadState::new(10, "https://www.rust-lang.org");

        assert!(state.update(0).is_none());
        sleep(Duration::from_secs(1));

        let mid_update = state.update(5).unwrap();
        assert_eq!(0.5, mid_update.percentage);
        assert!(mid_update.elapsed_time.as_secs_f32() > 1.);
        assert!(mid_update.remaining_time.as_secs_f32() > 1.);

        let end_update = state.update(5).unwrap();
        assert_eq!(1., end_update.percentage);
        assert_eq!(0., end_update.remaining_time.as_secs_f32());
    }
}