Przeglądaj źródła

fu: add resource type, use new fud events, improve `get` output

epiphany 1 rok temu
rodzic
commit
513bf571e4
2 zmienionych plików z 119 dodań i 42 usunięć
  1. 76 42
      bin/fud/fu/src/main.rs
  2. 43 0
      bin/fud/fu/src/util.rs

+ 76 - 42
bin/fud/fu/src/main.rs

@@ -25,7 +25,7 @@ use std::{
     io::{stdout, Write},
     io::{stdout, Write},
     sync::Arc,
     sync::Arc,
 };
 };
-use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
+use termcolor::{ColorSpec, StandardStream, WriteColor};
 use url::Url;
 use url::Url;
 
 
 use darkfi::{
 use darkfi::{
@@ -40,6 +40,9 @@ use darkfi::{
     Error, Result,
     Error, Result,
 };
 };
 
 
+mod util;
+use crate::util::{status_to_colorspec, type_to_colorspec};
+
 #[derive(Parser)]
 #[derive(Parser)]
 #[clap(name = "fu", about = cli_desc!(), version)]
 #[clap(name = "fu", about = cli_desc!(), version)]
 #[clap(arg_required_else_help(true))]
 #[clap(arg_required_else_help(true))]
@@ -140,27 +143,41 @@ impl Fu {
         );
         );
 
 
         let progress_bar_width = 20;
         let progress_bar_width = 20;
+        let mut started = false;
+        let mut tstdout = StandardStream::stdout(ColorChoice::Auto);
 
 
-        let print_progress_bar = |info: &HashMap<String, JsonValue>| {
+        let mut print_progress_bar = |info: &HashMap<String, JsonValue>| {
+            started = true;
             let resource =
             let resource =
                 info.get("resource").unwrap().get::<HashMap<String, JsonValue>>().unwrap();
                 info.get("resource").unwrap().get::<HashMap<String, JsonValue>>().unwrap();
             let chunks_downloaded =
             let chunks_downloaded =
                 *resource.get("chunks_downloaded").unwrap().get::<f64>().unwrap() as usize;
                 *resource.get("chunks_downloaded").unwrap().get::<f64>().unwrap() as usize;
             let chunks_total =
             let chunks_total =
                 *resource.get("chunks_total").unwrap().get::<f64>().unwrap() as usize;
                 *resource.get("chunks_total").unwrap().get::<f64>().unwrap() as usize;
-            let status = match resource.get("status").unwrap().get::<String>().unwrap().as_str() {
-                "seeding" => "done",
-                s => s,
-            };
-            let completed = if chunks_total > 0 {
-                (chunks_downloaded as f64 / chunks_total as f64 * progress_bar_width as f64)
-                    as usize
-            } else {
-                0
+            let status = resource.get("status").unwrap().get::<String>().unwrap();
+            let percent = match chunks_total {
+                0 => 0f64,
+                _ => chunks_downloaded as f64 / chunks_total as f64,
             };
             };
+            let completed = (percent * progress_bar_width as f64) as usize;
             let remaining = progress_bar_width - completed;
             let remaining = progress_bar_width - completed;
             let bar = "=".repeat(completed) + &" ".repeat(remaining);
             let bar = "=".repeat(completed) + &" ".repeat(remaining);
-            print!("\x1B[2K\r[{}] {}/{} chunks | {}", bar, chunks_downloaded, chunks_total, status);
+            print!(
+                "\x1B[2K\r[{}] {:.1}% | {}/{} chunks | ",
+                bar,
+                percent * 100.0,
+                chunks_downloaded,
+                chunks_total
+            );
+            tstdout.set_color(&status_to_colorspec(status)).unwrap();
+            print!(
+                "{}",
+                match status.as_str() {
+                    "seeding" => "done",
+                    s => s,
+                }
+            );
+            tstdout.reset().unwrap();
             stdout().flush().unwrap();
             stdout().flush().unwrap();
         };
         };
 
 
@@ -187,7 +204,7 @@ impl Fu {
                     }
                     }
                     match params.get("event").unwrap().get::<String>().unwrap().as_str() {
                     match params.get("event").unwrap().get::<String>().unwrap().as_str() {
                         "download_started" |
                         "download_started" |
-                        "file_download_completed" |
+                        "metadata_download_completed" |
                         "chunk_download_completed" => {
                         "chunk_download_completed" => {
                             print_progress_bar(info);
                             print_progress_bar(info);
                         }
                         }
@@ -202,9 +219,9 @@ impl Fu {
                             println!("\nDownload completed:\n{}", file_path);
                             println!("\nDownload completed:\n{}", file_path);
                             return Ok(());
                             return Ok(());
                         }
                         }
-                        "file_not_found" => {
+                        "metadata_not_found" => {
                             println!();
                             println!();
-                            return Err(Error::Custom(format!("Could not find file {}", file_hash)));
+                            return Err(Error::Custom(format!("Could not find {}", file_hash)));
                         }
                         }
                         "chunk_not_found" => {
                         "chunk_not_found" => {
                             // A seeder does not have a chunk we are looking for,
                             // A seeder does not have a chunk we are looking for,
@@ -217,7 +234,9 @@ impl Fu {
                         }
                         }
                         "download_error" => {
                         "download_error" => {
                             // An error that caused the download to be unsuccessful
                             // An error that caused the download to be unsuccessful
-                            println!();
+                            if started {
+                                println!();
+                            }
                             return Err(Error::Custom(
                             return Err(Error::Custom(
                                 info.get("error").unwrap().get::<String>().unwrap().to_string(),
                                 info.get("error").unwrap().get::<String>().unwrap().to_string(),
                             ));
                             ));
@@ -243,11 +262,11 @@ impl Fu {
         let req = JsonRequest::new("put", JsonValue::Array(vec![JsonValue::String(file)]));
         let req = JsonRequest::new("put", JsonValue::Array(vec![JsonValue::String(file)]));
         let rep = self.rpc_client.request(req).await?;
         let rep = self.rpc_client.request(req).await?;
         match rep {
         match rep {
-            JsonValue::String(file_id) => {
-                println!("{}", file_id);
+            JsonValue::String(id) => {
+                println!("{}", id);
                 Ok(())
                 Ok(())
             }
             }
-            _ => Err(Error::ParseFailed("File ID is not a string")),
+            _ => Err(Error::ParseFailed("ID is not a string")),
         }
         }
     }
     }
 
 
@@ -257,19 +276,38 @@ impl Fu {
 
 
         let resources: Vec<JsonValue> = rep.clone().try_into().unwrap();
         let resources: Vec<JsonValue> = rep.clone().try_into().unwrap();
 
 
+        let mut tstdout = StandardStream::stdout(ColorChoice::Auto);
+
         for rs in resources.iter() {
         for rs in resources.iter() {
             let resource = rs.get::<HashMap<String, JsonValue>>().unwrap();
             let resource = rs.get::<HashMap<String, JsonValue>>().unwrap();
-            let path = resource.get("path").unwrap().get::<String>().unwrap().as_str();
+            let path = resource.get("path").unwrap().get::<String>().unwrap();
             let hash = resource.get("hash").unwrap().get::<String>().unwrap().as_str();
             let hash = resource.get("hash").unwrap().get::<String>().unwrap().as_str();
+            let rtype = resource.get("type").unwrap().get::<String>().unwrap();
             let chunks_downloaded =
             let chunks_downloaded =
                 *resource.get("chunks_downloaded").unwrap().get::<f64>().unwrap() as usize;
                 *resource.get("chunks_downloaded").unwrap().get::<f64>().unwrap() as usize;
             let chunks_total =
             let chunks_total =
                 *resource.get("chunks_total").unwrap().get::<f64>().unwrap() as usize;
                 *resource.get("chunks_total").unwrap().get::<f64>().unwrap() as usize;
-            let status = resource.get("status").unwrap().get::<String>().unwrap().as_str();
+            let status = resource.get("status").unwrap().get::<String>().unwrap();
+            tstdout.set_color(ColorSpec::new().set_bold(true)).unwrap();
             println!("{}", path);
             println!("{}", path);
-            println!("\tID: {}", hash);
-            println!("\tStatus: {}", status);
-            println!("\tChunks: {}/{}", chunks_downloaded, chunks_total);
+            tstdout.reset().unwrap();
+            println!("  ID: {}", hash);
+            print!("  Type: ");
+            tstdout.set_color(&type_to_colorspec(rtype)).unwrap();
+            println!("{}", rtype);
+            tstdout.reset().unwrap();
+            print!("  Status: ");
+            tstdout.set_color(&status_to_colorspec(status)).unwrap();
+            println!("{}", status);
+            tstdout.reset().unwrap();
+            println!(
+                "  Chunks: {}/{}",
+                chunks_downloaded,
+                match chunks_total {
+                    0 => "?".to_string(),
+                    _ => chunks_total.to_string(),
+                }
+            );
         }
         }
 
 
         Ok(())
         Ok(())
@@ -389,23 +427,16 @@ impl Fu {
             print!("\x1b[{};1H\x1B[2K", i + 2);
             print!("\x1b[{};1H\x1B[2K", i + 2);
 
 
             let hash = resource.get("hash").unwrap().get::<String>().unwrap();
             let hash = resource.get("hash").unwrap().get::<String>().unwrap();
-            print!("\r{:>44} ", hash,);
+            print!("\r{:>44} ", hash);
+
+            let rtype = resource.get("type").unwrap().get::<String>().unwrap();
+            tstdout.set_color(&type_to_colorspec(rtype)).unwrap();
+            print!("{:>9} ", rtype);
+            tstdout.reset().unwrap();
 
 
             let status = resource.get("status").unwrap().get::<String>().unwrap();
             let status = resource.get("status").unwrap().get::<String>().unwrap();
-            tstdout
-                .set_color(
-                    ColorSpec::new()
-                        .set_fg(match status.as_str() {
-                            "downloading" => Some(Color::Blue),
-                            "seeding" => Some(Color::Green),
-                            "discovering" => Some(Color::Magenta),
-                            "incomplete" => Some(Color::Red),
-                            _ => None,
-                        })
-                        .set_bold(true),
-                )
-                .unwrap();
-            print!("{:>11} ", status,);
+            tstdout.set_color(&status_to_colorspec(status)).unwrap();
+            print!("{:>11} ", status);
             tstdout.reset().unwrap();
             tstdout.reset().unwrap();
 
 
             let chunks_downloaded =
             let chunks_downloaded =
@@ -437,7 +468,10 @@ impl Fu {
             print!("\x1B[2J\x1B[1;1H");
             print!("\x1B[2J\x1B[1;1H");
 
 
             // Print column headers
             // Print column headers
-            println!("\x1b[4m{:>44} {:>11} {:>5} {:>9}\x1b[0m", "Hash", "Status", "%", "Chunks");
+            println!(
+                "\x1b[4m{:>44} {:>9} {:>11} {:>5} {:>9}\x1b[0m",
+                "Hash", "Type", "Status", "%", "Chunks"
+            );
         };
         };
 
 
         print_begin().await;
         print_begin().await;
@@ -458,11 +492,11 @@ impl Fu {
                         params.get("info").unwrap().get::<HashMap<String, JsonValue>>().unwrap();
                         params.get("info").unwrap().get::<HashMap<String, JsonValue>>().unwrap();
                     match params.get("event").unwrap().get::<String>().unwrap().as_str() {
                     match params.get("event").unwrap().get::<String>().unwrap().as_str() {
                         "download_started" |
                         "download_started" |
-                        "file_download_completed" |
+                        "metadata_download_completed" |
                         "chunk_download_completed" |
                         "chunk_download_completed" |
                         "download_completed" |
                         "download_completed" |
                         "missing_chunks" |
                         "missing_chunks" |
-                        "file_not_found" |
+                        "metadata_not_found" |
                         "resource_updated" => {
                         "resource_updated" => {
                             let resource = info
                             let resource = info
                                 .get("resource")
                                 .get("resource")

+ 43 - 0
bin/fud/fu/src/util.rs

@@ -0,0 +1,43 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2025 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use termcolor::{Color, ColorSpec};
+
+pub fn status_to_colorspec(status: &str) -> ColorSpec {
+    ColorSpec::new()
+        .set_fg(match status {
+            "downloading" => Some(Color::Blue),
+            "seeding" => Some(Color::Green),
+            "discovering" => Some(Color::Magenta),
+            "incomplete" => Some(Color::Red),
+            _ => None,
+        })
+        .set_bold(true)
+        .clone()
+}
+
+pub fn type_to_colorspec(rtype: &str) -> ColorSpec {
+    ColorSpec::new()
+        .set_fg(match rtype {
+            "file" => Some(Color::Blue),
+            "directory" => Some(Color::Magenta),
+            _ => None,
+        })
+        .set_bold(true)
+        .clone()
+}