Improve download_progress example

- Use `reqwest` with `Response::chunk` to notify progress.
- Turn example state into an enum
This commit is contained in:
Héctor Ramón Jiménez 2020-03-23 20:34:16 +01:00
parent fff333f89b
commit 30c7db3f25
3 changed files with 123 additions and 120 deletions

View file

@ -1,7 +1,7 @@
use iced_futures::futures;
// Just a little utility function
pub fn file<T: ToString>(url: T) -> iced::Subscription<DownloadMessage> {
pub fn file<T: ToString>(url: T) -> iced::Subscription<Progress> {
iced::Subscription::from_recipe(Downloader {
url: url.to_string(),
})
@ -16,7 +16,7 @@ impl<H, I> iced_native::subscription::Recipe<H, I> for Downloader
where
H: std::hash::Hasher,
{
type Output = DownloadMessage;
type Output = Progress;
fn hash(&self, state: &mut H) {
use std::hash::Hash;
@ -27,73 +27,68 @@ where
self: Box<Self>,
_input: futures::stream::BoxStream<'static, I>,
) -> futures::stream::BoxStream<'static, Self::Output> {
use isahc::prelude::*;
Box::pin(futures::stream::unfold(
DownloadState::Ready(self.url),
State::Ready(self.url),
|state| async move {
match state {
DownloadState::Ready(url) => {
let resp = Request::get(&url)
.metrics(true)
.body(())
.unwrap()
.send_async()
.await
.unwrap();
let metrics = resp.metrics().unwrap().clone();
// If you actually want to download:
/*let file = async_std::fs::File::create("download.bin")
.await
.unwrap();*/
State::Ready(url) => {
let response = reqwest::get(&url).await;
async_std::task::spawn(async_std::io::copy(
resp.into_body(),
async_std::io::sink(), //file
));
Some((
DownloadMessage::DownloadStarted,
DownloadState::Downloading(metrics),
))
}
DownloadState::Downloading(metrics) => {
async_std::task::sleep(
std::time::Duration::from_millis(100),
)
.await;
let percentage = metrics.download_progress().0 * 100
/ metrics.download_progress().1;
if percentage == 100 {
Some((
DownloadMessage::Done,
DownloadState::Finished,
))
} else {
Some((
DownloadMessage::Downloading(percentage),
DownloadState::Downloading(metrics),
))
match response {
Ok(response) => Some((
Progress::Started,
State::Downloading {
total: response.content_length().unwrap(),
downloaded: 0,
response,
},
)),
Err(_) => None,
}
}
DownloadState::Finished => None,
State::Downloading {
mut response,
total,
downloaded,
} => match response.chunk().await {
Ok(Some(chunk)) => {
let downloaded = downloaded + chunk.len() as u64;
let percentage =
(downloaded as f32 / total as f32) * 100.0;
Some((
Progress::Advanced(percentage),
State::Downloading {
response,
total,
downloaded,
},
))
}
Ok(None) => Some((Progress::Finished, State::Finished)),
Err(_) => None,
},
State::Finished => None,
}
},
))
}
}
#[derive(Debug)]
pub enum DownloadMessage {
DownloadStarted,
Downloading(u64),
Done,
}
pub enum DownloadState {
Ready(String),
Downloading(isahc::Metrics),
#[derive(Debug, Clone)]
pub enum Progress {
Started,
Advanced(f32),
Finished,
}
pub enum State {
Ready(String),
Downloading {
response: reqwest::Response,
total: u64,
downloaded: u64,
},
Finished,
}