• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ChrisRega / lazy_async_promise / febe5d1b9e42cda3c60426e30cb38e971572fa4d-PR-12

pending completion
febe5d1b9e42cda3c60426e30cb38e971572fa4d-PR-12

Pull #12

github

GitHub
Merge bd96c2924 into 50df9778c
Pull Request #12: 0.5.0 dev branch

230 of 230 new or added lines in 5 files covered. (100.0%)

566 of 619 relevant lines covered (91.44%)

3.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.62
/src/immediatevalueprogress.rs
1
use crate::{DirectCacheAccess, Progress};
2
use crate::{ImmediateValuePromise, ImmediateValueState};
3
use std::borrow::Cow;
4
use std::time::Instant;
5
use tokio::sync::mpsc::Receiver;
6
use tokio::sync::mpsc::Sender;
7

8
/// A status update struct containing the issue-date, progress and a message
9
/// You can use any struct that can be transferred via tokio mpsc channels.
10
#[derive(Debug)]
×
11
pub struct Status<M> {
12
    /// Time when this status was created
13
    pub time: Instant,
14
    /// Current progress
15
    pub progress: Progress,
16
    /// Message
17
    pub message: M,
18
}
19

20
impl<M> Status<M> {
21
    /// Create a new status message with `now` as timestamp
22
    pub fn new(progress: Progress, message: M) -> Self {
2✔
23
        Self {
2✔
24
            progress,
2✔
25
            message,
2✔
26
            time: Instant::now(),
2✔
27
        }
2✔
28
    }
2✔
29
}
30

31
/// This [`Status`] typedef allows to use both: `&'static str` and `String` in a message
32
pub type StringStatus = Status<Cow<'static, str>>;
33

34
impl StringStatus {
35
    /// create a [`StringStatus`] from a `&'static str`
36
    pub fn from_str(progress: Progress, static_message: &'static str) -> Self {
×
37
        StringStatus {
×
38
            message: Cow::Borrowed(static_message),
×
39
            time: Instant::now(),
×
40
            progress,
×
41
        }
×
42
    }
×
43
    /// create a [`StringStatus`] from a `String`
44
    pub fn from_string(progress: Progress, message: String) -> Self {
×
45
        StringStatus {
×
46
            message: Cow::Owned(message),
×
47
            time: Instant::now(),
×
48
            progress,
×
49
        }
×
50
    }
×
51
}
52

53
/// # A progress and status enabling wrapper for [`ImmediateValuePromise`]
54
/// This struct allows to use the [`Progress`] type and any kind of status message
55
/// You can use this to set a computation progress and optionally attach any kind of status message.
56
/// Assume your action runs  for an extended period of time and you want to inform the user about the state:
57
///```rust, no_run
58
///use std::borrow::Cow;
59
///use std::time::Duration;
60
///use lazy_async_promise::{ImmediateValueState, ImmediateValuePromise, Progress, ProgressTrackedImValProm, StringStatus};
61
///let mut oneshot_progress = ProgressTrackedImValProm::new( |s| { ImmediateValuePromise::new(
62
///  async move {
63
///  //send some initial status
64
///    s.send(StringStatus::new(
65
///      Progress::from_percent(0.0),
66
///      "Initializing".into(),
67
///    )).await.unwrap();
68
///    // do some long running operation
69
///    for i in 0..100 {
70
///      tokio::time::sleep(Duration::from_millis(50)).await;
71
///      s.send(StringStatus::new(
72
///        Progress::from_percent(i as f64),
73
///        Cow::Borrowed("In progress"))).await.unwrap();
74
///    }
75
///    Ok(34)
76
///  })}, 2000);
77
///  assert!(matches!(
78
///    oneshot_progress.poll_state(),
79
///    ImmediateValueState::Updating));
80
///   //waiting and polling will yield "In progress" now :)
81
/// ```
82
///
83
pub struct ProgressTrackedImValProm<T: Send, M> {
84
    promise: ImmediateValuePromise<T>,
85
    status: Vec<Status<M>>,
86
    receiver: Receiver<Status<M>>,
87
}
88

89
impl<T: Send + 'static, M> ProgressTrackedImValProm<T, M> {
90
    /// create a new Progress tracked immediate value promise.
91
    pub fn new(
1✔
92
        creator: impl FnOnce(Sender<Status<M>>) -> ImmediateValuePromise<T>,
1✔
93
        buffer: usize,
1✔
94
    ) -> Self {
1✔
95
        let (sender, receiver) = tokio::sync::mpsc::channel(buffer);
1✔
96
        ProgressTrackedImValProm {
1✔
97
            receiver,
1✔
98
            status: Vec::new(),
1✔
99
            promise: creator(sender),
1✔
100
        }
1✔
101
    }
1✔
102

103
    /// Slice of all recorded [`Status`] changes
104
    pub fn status_history(&self) -> &[Status<M>] {
×
105
        &self.status
×
106
    }
×
107

108
    /// Get the last [`Status`] if there is any
109
    pub fn last_status(&self) -> Option<&Status<M>> {
×
110
        self.status.last()
×
111
    }
×
112

113
    /// Is our future already finished?
114
    pub fn finished(&self) -> bool {
×
115
        self.promise.get_value().is_some()
×
116
    }
×
117

118
    /// Poll the state and process the messages
119
    pub fn poll_state(&mut self) -> &ImmediateValueState<T> {
3✔
120
        while let Ok(msg) = self.receiver.try_recv() {
5✔
121
            self.status.push(msg);
2✔
122
        }
2✔
123
        self.promise.poll_state()
3✔
124
    }
3✔
125

126
    /// Get the current progress
127
    pub fn get_progress(&self) -> Progress {
2✔
128
        self.status
2✔
129
            .last()
2✔
130
            .map(|p| p.progress)
2✔
131
            .unwrap_or(Progress::default())
2✔
132
    }
2✔
133
}
134

135
impl<T: Send + 'static, M> DirectCacheAccess<T> for ProgressTrackedImValProm<T, M> {
136
    fn get_value_mut(&mut self) -> Option<&mut T> {
×
137
        self.promise.get_value_mut()
×
138
    }
×
139
    fn get_value(&self) -> Option<&T> {
×
140
        self.promise.get_value()
×
141
    }
×
142
    fn take_value(&mut self) -> Option<T> {
×
143
        self.promise.take_value()
×
144
    }
×
145
}
146
#[cfg(test)]
147
mod test {
148
    use super::*;
149
    use crate::ImmediateValuePromise;
150
    use std::time::Duration;
151
    #[tokio::test]
1✔
152
    async fn basic_usage_cycle() {
1✔
153
        let mut oneshot_progress = ProgressTrackedImValProm::new(
1✔
154
            |s| {
1✔
155
                ImmediateValuePromise::new(async move {
1✔
156
                    s.send(StringStatus::new(
1✔
157
                        Progress::from_percent(0.0),
1✔
158
                        Cow::Borrowed("Initializing"),
1✔
159
                    ))
1✔
160
                    .await
×
161
                    .unwrap();
1✔
162
                    tokio::time::sleep(Duration::from_millis(50)).await;
1✔
163
                    s.send(StringStatus::new(
1✔
164
                        Progress::from_percent(100.0),
1✔
165
                        Cow::Borrowed("Done"),
1✔
166
                    ))
1✔
167
                    .await
×
168
                    .unwrap();
1✔
169
                    Ok(34)
1✔
170
                })
1✔
171
            },
1✔
172
            2000,
1✔
173
        );
1✔
174
        assert!(matches!(
1✔
175
            oneshot_progress.poll_state(),
1✔
176
            ImmediateValueState::Updating
177
        ));
178
        assert_eq!(*oneshot_progress.get_progress(), 0.0);
1✔
179
        tokio::time::sleep(Duration::from_millis(100)).await;
1✔
180
        let _ = oneshot_progress.poll_state();
1✔
181
        assert_eq!(*oneshot_progress.get_progress(), 1.0);
1✔
182
        let result = oneshot_progress.poll_state();
1✔
183

184
        if let ImmediateValueState::Success(val) = result {
1✔
185
            assert_eq!(*val, 34);
1✔
186
            return;
1✔
187
        }
×
188

×
189
        unreachable!();
×
190
    }
191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc