Skip to content

Commit

Permalink
Merge pull request #73 from crumblingstatue/download_progress_take_3
Browse files Browse the repository at this point in the history
Download progress take 3
  • Loading branch information
Diggsey committed Feb 24, 2016
2 parents 88fe1e1 + e3d969f commit 3a69c14
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 27 deletions.
20 changes: 16 additions & 4 deletions rust-install/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ pub enum Notification<'a> {
CopyingDirectory(&'a Path, &'a Path),
RemovingDirectory(&'a str, &'a Path),
DownloadingFile(&'a hyper::Url, &'a Path),
/// Received the Content-Length of the to-be downloaded data.
DownloadContentLengthReceived(u64),
/// Received some data.
DownloadDataReceived(usize),
/// Download has finished.
DownloadFinished,
NoCanonicalPath(&'a Path),
}

Expand Down Expand Up @@ -133,9 +139,12 @@ impl<'a> Notification<'a> {
use self::Notification::*;
match *self {
CreatingDirectory(_, _) | RemovingDirectory(_, _) => NotificationLevel::Verbose,
LinkingDirectory(_, _) | CopyingDirectory(_, _) | DownloadingFile(_, _) => {
NotificationLevel::Normal
}
LinkingDirectory(_, _) |
CopyingDirectory(_, _) |
DownloadingFile(_, _) |
DownloadContentLengthReceived(_) |
DownloadDataReceived(_) |
DownloadFinished => NotificationLevel::Normal,
NoCanonicalPath(_) => NotificationLevel::Warn,
}
}
Expand All @@ -154,6 +163,9 @@ impl<'a> Display for Notification<'a> {
write!(f, "removing {} directory: '{}'", name, path.display())
}
DownloadingFile(url, _) => write!(f, "downloading file from: '{}'", url),
DownloadContentLengthReceived(len) => write!(f, "download size is: '{}'", len),
DownloadDataReceived(len) => write!(f, "received some data of size {}", len),
DownloadFinished => write!(f, "download finished"),
NoCanonicalPath(path) => write!(f, "could not canonicalize path: '{}'", path.display()),
}
}
Expand Down Expand Up @@ -409,7 +421,7 @@ pub fn download_file(url: hyper::Url,
notify_handler: NotifyHandler)
-> Result<()> {
notify_handler.call(Notification::DownloadingFile(&url, path));
raw::download_file(url.clone(), path, hasher).map_err(|e| {
raw::download_file(url.clone(), path, hasher, notify_handler).map_err(|e| {
Error::DownloadingFile {
url: url,
path: PathBuf::from(path),
Expand Down
13 changes: 12 additions & 1 deletion rust-install/src/utils/raw.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use utils::NotifyHandler;

use std::fs;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -169,8 +170,12 @@ impl fmt::Display for DownloadError {

pub fn download_file<P: AsRef<Path>>(url: hyper::Url,
path: P,
mut hasher: Option<&mut Hasher>)
mut hasher: Option<&mut Hasher>,
notify_handler: NotifyHandler)
-> DownloadResult<()> {
use hyper::header::ContentLength;
use utils::Notification;

let client = Client::new();

let mut res = try!(client.get(url).send().map_err(DownloadError::Network));
Expand All @@ -183,6 +188,10 @@ pub fn download_file<P: AsRef<Path>>(url: hyper::Url,

let mut file = try!(fs::File::create(path).map_err(DownloadError::File));

if let Some(len) = res.headers.get::<ContentLength>().cloned() {
notify_handler.call(Notification::DownloadContentLengthReceived(len.0));
}

loop {
let bytes_read = try!(io::Read::read(&mut res, &mut buffer)
.map_err(hyper::Error::Io)
Expand All @@ -195,8 +204,10 @@ pub fn download_file<P: AsRef<Path>>(url: hyper::Url,
}
try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read])
.map_err(DownloadError::File));
notify_handler.call(Notification::DownloadDataReceived(bytes_read));
} else {
try!(file.sync_data().map_err(DownloadError::File));
notify_handler.call(Notification::DownloadFinished);
return Ok(());
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ impl Cfg {
// Set up the multirust home directory
let multirust_dir = env::var_os("MULTIRUST_HOME")
.and_then(utils::if_not_empty)
.map_or_else(|| data_dir.join(".multirust"),
PathBuf::from);
.map_or_else(|| data_dir.join(".multirust"), PathBuf::from);

try!(utils::ensure_dir_exists("home", &multirust_dir, ntfy!(&notify_handler)));

Expand Down Expand Up @@ -87,8 +86,7 @@ impl Cfg {
let dist_root_url = env::var("MULTIRUST_DIST_ROOT")
.ok()
.and_then(utils::if_not_empty)
.map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT),
Cow::Owned);
.map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT), Cow::Owned);

Ok(Cfg {
multirust_dir: multirust_dir,
Expand Down
4 changes: 3 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ impl<'a> Display for Notification<'a> {
to_ver)
}
MetadataUpgradeNotNeeded(ver) => {
write!(f, "nothing to upgrade: metadata version is already '{}'", ver)
write!(f,
"nothing to upgrade: metadata version is already '{}'",
ver)
}
WritingMetadataVersion(ver) => write!(f, "writing metadata version: '{}'", ver),
ReadMetadataVersion(ver) => write!(f, "read metadata version: '{}'", ver),
Expand Down
110 changes: 93 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,27 +75,100 @@ fn info_fmt(args: fmt::Arguments) {
}

fn set_globals(m: Option<&ArgMatches>) -> Result<Cfg> {
use std::rc::Rc;
use std::cell::{Cell, RefCell};

struct DownloadDisplayer {
content_len: Cell<Option<u64>>,
total_downloaded: Cell<usize>,
term: RefCell<Box<term::StdoutTerminal>>,
}

/// Human readable representation of data size in bytes
struct HumanReadable<T>(T);

impl<T: Into<f64> + Clone> fmt::Display for HumanReadable<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
const KIB: f64 = 1024.0;
const MIB: f64 = 1048576.0;
let size: f64 = self.0.clone().into();

if size >= MIB {
write!(f, "{:.2} MiB", size / MIB)
} else if size >= KIB {
write!(f, "{:.2} KiB", size / KIB)
} else {
write!(f, "{} B", size)
}
}
}

let download_displayer = Rc::new(DownloadDisplayer {
content_len: Cell::new(None),
total_downloaded: Cell::new(0),
term: RefCell::new(term::stdout().expect("Failed to open terminal.")),
});

// Base config
let verbose = m.map_or(false, |m| m.is_present("verbose"));
Cfg::from_env(shared_ntfy!(move |n: Notification| {
use multirust::notify::NotificationLevel::*;
match n.level() {
Verbose => {
if verbose {
println!("{}", n);
}
use rust_install::Notification as In;
use rust_install::utils::Notification as Un;

match n {
Notification::Install(In::Utils(Un::DownloadContentLengthReceived(len))) => {
let dd = download_displayer.clone();
dd.content_len.set(Some(len));
dd.total_downloaded.set(0);
}
Normal => {
println!("{}", n);
}
Info => {
info!("{}", n);
Notification::Install(In::Utils(Un::DownloadDataReceived(len))) => {
let dd = download_displayer.clone();
let mut t = dd.term.borrow_mut();
dd.total_downloaded.set(dd.total_downloaded.get() + len);
let total_downloaded = dd.total_downloaded.get();
let total_h = HumanReadable(total_downloaded as f64);

match dd.content_len.get() {
Some(content_len) => {
let percent = (total_downloaded as f64 / content_len as f64) * 100.;
let content_len_h = HumanReadable(content_len as f64);
let _ = write!(t, "{} / {} ({:.2}%)", total_h, content_len_h, percent);
}
None => {
let _ = write!(t, "{}", total_h);
}
}
// delete_line() doesn't seem to clear the line properly.
// Instead, let's just print some whitespace to clear it.
let _ = write!(t, " ");
let _ = t.flush();
let _ = t.carriage_return();
}
Warn => {
warn!("{}", n);
Notification::Install(In::Utils(Un::DownloadFinished)) => {
let dd = download_displayer.clone();
let _ = writeln!(dd.term.borrow_mut(), "");
}
Error => {
err!("{}", n);
n => {
match n.level() {
Verbose => {
if verbose {
println!("{}", n);
}
}
Normal => {
println!("{}", n);
}
Info => {
info!("{}", n);
}
Warn => {
warn!("{}", n);
}
Error => {
err!("{}", n);
}
}
}
}
}))
Expand Down Expand Up @@ -227,8 +300,11 @@ fn run_multirust() -> Result<()> {
let cfg = try!(set_globals(Some(&app_matches)));

match app_matches.subcommand_name() {
Some("upgrade-data") | Some("delete-data") | Some("install") |
Some("uninstall") | None => {} // Don't need consistent metadata
Some("upgrade-data") |
Some("delete-data") |
Some("install") |
Some("uninstall") |
None => {} // Don't need consistent metadata
Some(_) => {
try!(cfg.check_metadata_version());
}
Expand Down Expand Up @@ -709,7 +785,7 @@ fn ask(question: &str) -> Option<bool> {
}

fn delete_data(cfg: &Cfg, m: &ArgMatches) -> Result<()> {
if !m.is_present("no-prompt") &&
if !m.is_present("no-prompt") &&
!ask("This will delete all toolchains, overrides, aliases, and other multirust data \
associated with this user. Continue?")
.unwrap_or(false) {
Expand Down

0 comments on commit 3a69c14

Please sign in to comment.