cli: upload-pack events

Introduce an `UploadPack` type for keeping track of relevant data from
upload-pack events and displaying progress to the terminal.

It keeps track of the number of remotes that are being uploaded to as well as
the throughput of transmitted data.

The `Progress` events are logged rather than being displayed since they don't
provide any useful details to the user.

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
Fintan Halpenny
2024-05-17 21:05:43 +02:00
committed by cloudhead
parent 3fe6bbe1cf
commit 740106d759
6 changed files with 129 additions and 14 deletions
+11 -3
View File
@@ -395,6 +395,8 @@ fn sync(
spinner.message("Syncing..");
let mut replicas = HashSet::new();
// Start upload pack as None and set it if we encounter an event
let mut upload_pack = term::upload_pack::UploadPack::new();
for e in events {
match e {
@@ -413,21 +415,27 @@ fn sync(
remote,
progress,
})) if rid == rid_ => {
spinner.message(format!("Uploading {rid} to {remote} {progress}"));
log::debug!("Upload progress for {remote}: {progress}");
}
Ok(Event::UploadPack(UploadPack::PackProgress {
rid: rid_,
remote,
transmitted,
})) if rid == rid_ => spinner.message(upload_pack.transmitted(remote, transmitted)),
Ok(Event::UploadPack(UploadPack::Done {
rid: rid_,
remote,
status,
})) if rid == rid_ => {
spinner.message(format!("Upload done for {rid} to {remote}: {status}"));
log::debug!("Upload done for {rid} to {remote} with status: {status}");
spinner.message(upload_pack.done(&remote));
}
Ok(Event::UploadPack(UploadPack::Error {
rid: rid_,
remote,
err,
})) if rid == rid_ => {
spinner.message(format!("Upload error for {rid} to {remote}: {err}"));
term::warning(format!("Upload error for {rid} to {remote}: {err}"));
}
Ok(_) => {
// Some other irrelevant event received.
+1
View File
@@ -8,6 +8,7 @@ pub mod highlight;
pub mod issue;
pub mod json;
pub mod patch;
pub mod upload_pack;
use std::ffi::OsString;
use std::process;
+26
View File
@@ -82,6 +82,22 @@ pub fn timestamp(time: impl Into<LocalTime>) -> Paint<String> {
Paint::new(fmt.convert(duration.into()))
}
pub fn bytes(size: usize) -> Paint<String> {
const KB: usize = 1024;
const MB: usize = 1024usize.pow(2);
const GB: usize = 1024usize.pow(3);
let size = if size < KB {
format!("{size} B")
} else if size < MB {
format!("{} KiB", size / KB)
} else if size < GB {
format!("{} MiB", size / MB)
} else {
format!("{} GiB", size / GB)
};
Paint::new(size)
}
/// Format a ref update.
pub fn ref_update(update: &RefUpdate) -> Paint<&'static str> {
match update {
@@ -358,4 +374,14 @@ mod test {
let res = strip_comments(test);
assert_eq!(exp, res);
}
#[test]
fn test_bytes() {
assert_eq!(bytes(1023).to_string(), "1023 B");
assert_eq!(bytes(1024).to_string(), "1 KiB");
assert_eq!(bytes(1024 * 9).to_string(), "9 KiB");
assert_eq!(bytes(1024usize.pow(2)).to_string(), "1 MiB");
assert_eq!(bytes(1024usize.pow(2) * 56).to_string(), "56 MiB");
assert_eq!(bytes(1024usize.pow(3) * 1024).to_string(), "1024 GiB");
}
}
+50
View File
@@ -0,0 +1,50 @@
use std::collections::BTreeSet;
use std::time::Instant;
use crate::terminal::format;
use radicle::node::NodeId;
/// Keeps track of upload-pack progress for displaying to the terminal.
pub struct UploadPack {
/// Keep track of which remotes are being uploaded to, removing any that
/// have completed.
remotes: BTreeSet<NodeId>,
/// Keep track of how long we've been transmitting for to calculate
/// throughput.
timer: Instant,
}
impl Default for UploadPack {
fn default() -> Self {
Self::new()
}
}
impl UploadPack {
/// Construct an empty set of spinners.
pub fn new() -> Self {
Self {
remotes: BTreeSet::new(),
timer: Instant::now(),
}
}
/// Display the number of peers, the total transmitted bytes, and the
/// throughput.
pub fn transmitted(&mut self, remote: NodeId, transmitted: usize) -> String {
self.remotes.insert(remote);
let throughput = transmitted as f64 / self.timer.elapsed().as_secs_f64();
let throughput = format::bytes(throughput.floor() as usize);
let n = self.remotes.len();
let transmitted = format::bytes(transmitted);
format!("Uploading to {n} peer(s) ({transmitted} | {throughput:.2}/s)")
}
/// Display which remote has completed upload-pack and how many are
/// remaining.
pub fn done(&mut self, remote: &NodeId) -> String {
self.remotes.remove(remote);
let n = self.remotes.len();
format!("Uploaded to {remote}, {n} peer(s) remaining..")
}
}