Skip to content

Commit

Permalink
cli+github: show download progress
Browse files Browse the repository at this point in the history
The spinner implementation has been improved to show
the remaining and total amount of bytes to download

Signed-off-by: Orhun Parmaksız <[email protected]>
  • Loading branch information
orhun committed May 9, 2022
1 parent 70200e8 commit 3fa30db
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 12 deletions.
23 changes: 18 additions & 5 deletions src/cli/handlers/download.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::cmp;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

use crate::cli::get_env;
Expand Down Expand Up @@ -107,11 +109,22 @@ impl DownloadHandler {
) -> Result<(), HandlerError> {
let spinner = Spinner::download(&selected_asset.name, output_path);
spinner.start();
let mut stream =
let (mut stream, content_length) =
github::download_asset(client, selected_asset).map_err(Self::download_error)?;
spinner.set_max_progress(content_length);
let mut destination = Self::create_file(output_path)?;
std::io::copy(&mut stream, &mut destination)
.map_err(|x| Self::copy_err(&selected_asset.name, output_path, x))?;
let mut downloaded = 0;
let mut buffer = [0; 1024];
while let Ok(bytes_read) = stream.read(&mut buffer) {
if bytes_read == 0 {
break;
}
destination
.write(&buffer[..bytes_read])
.map_err(|x| Self::write_err(&selected_asset.name, output_path, x))?;
downloaded = cmp::min(downloaded + bytes_read as u64, content_length);
spinner.update_progress(downloaded);
}
spinner.stop();
Ok(())
}
Expand Down Expand Up @@ -151,9 +164,9 @@ impl DownloadHandler {
})
}

pub fn copy_err(asset_name: &str, output_path: &Path, error: std::io::Error) -> HandlerError {
pub fn write_err(asset_name: &str, output_path: &Path, error: std::io::Error) -> HandlerError {
HandlerError::new(format!(
"Error copying {} to {}: {}",
"Error saving {} to {}: {}",
asset_name,
output_path.display(),
error
Expand Down
16 changes: 13 additions & 3 deletions src/cli/spinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ pub struct Spinner {
}

impl Spinner {
pub fn new(message: String, end_message: String) -> Self {
pub fn new(message: String, end_message: String, template: &str) -> Self {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::default_spinner()
.tick_strings(TICKS)
.template("{spinner:.blue} {msg}"),
.template(template),
);
pb.set_message(message);
Self { pb, end_message }
Expand All @@ -38,24 +38,34 @@ impl Spinner {
println!("{}", message);
}

pub fn set_max_progress(&self, progress: u64) {
self.pb.set_length(progress);
}

pub fn update_progress(&self, progress: u64) {
self.pb.set_position(progress);
}

pub fn download(download_asset: &str, output_path: &Path) -> Spinner {
Spinner::new(
format!("Downloading {}", Color::new(download_asset).bold()),
format!(
"Saved to: {}",
Color::new(&format!("{}", output_path.display())).bold()
),
"{msg}\n{percent}% [{wide_bar}] {bytes}/{total_bytes} ({eta})",
)
}

pub fn install() -> Spinner {
Spinner::new(
"Installing".into(),
format!("{}", Color::new("Installation completed!").green()),
"{spinner:.blue} {msg}",
)
}

pub fn no_messages() -> Spinner {
Spinner::new(String::new(), String::new())
Spinner::new(String::new(), String::new(), "{spinner:.blue} {msg}")
}
}
4 changes: 4 additions & 0 deletions src/github/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::fmt::Formatter;
pub enum GithubError {
Http(Box<ureq::Error>),
JsonDeserialization(std::io::Error),
InvalidContentLength,
RepositoryOrReleaseNotFound,
RateLimitExceeded,
Unauthorized,
Expand All @@ -28,6 +29,9 @@ impl std::fmt::Display for GithubError {
GithubError::JsonDeserialization(e) => {
f.write_str(&format!("Error deserializing response: {}", e))
}
GithubError::InvalidContentLength => {
f.write_str("Content-Length header is missing or invalid")
}
GithubError::RepositoryOrReleaseNotFound => {
f.write_str("Repository or release not found")
}
Expand Down
12 changes: 8 additions & 4 deletions src/github/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,15 @@ fn deserialize(response: ureq::Response) -> Result<Release, GithubError> {
pub fn download_asset(
client: &GithubClient,
asset: &Asset,
) -> Result<impl Read + Send, GithubError> {
client
) -> Result<(impl Read + Send, u64), GithubError> {
let response = client
.get(&asset.download_url)
.set("Accept", "application/octet-stream")
.call()
.map_err(GithubError::from)
.map(|response| response.into_reader())
.map_err(GithubError::from)?;
let content_length = response
.header("Content-Length")
.and_then(|v| v.parse().ok())
.ok_or(GithubError::InvalidContentLength)?;
Ok((response.into_reader(), content_length))
}

0 comments on commit 3fa30db

Please sign in to comment.