浏览代码

Create debug command, pretty print formatted tables

timvisee 7 年之前
父节点
当前提交
7425c6c405

+ 1 - 1
ROADMAP.md

@@ -3,7 +3,6 @@ The first release used for gathering feedback on the application by selected
 people.
 
 Features:
-- A `defaults` command to list defaults such as the host URL and history file
 - Make use of stdout and stderr consistent
 - Allow file/directory archiving on upload
 - Allow unarchiving on download 
@@ -17,6 +16,7 @@ Features:
 - Gentoo portage package
 - Arch AUR package
 - Windows, macOS and Redox support
+- Allow empty owner token for info command
 - Check and validate all errors, are some too verbose?
 
 # Beta release 0.1 (public)

+ 68 - 0
cli/src/action/debug.rs

@@ -0,0 +1,68 @@
+use chrono::Duration;
+use clap::ArgMatches;
+use ffsend_api::config::{SEND_DEFAULT_EXPIRE_TIME, SEND_DEFAULT_HOST};
+use prettytable::{
+    cell::Cell,
+    format::FormatBuilder,
+    row::Row,
+    Table,
+};
+
+use cmd::matcher::{
+    debug::DebugMatcher,
+    main::MainMatcher,
+    Matcher,
+};
+use error::ActionError;
+#[cfg(feature = "history")]
+use history_tool;
+use util::{ensure_owner_token, format_duration, print_success};
+
+/// A file debug action.
+pub struct Debug<'a> {
+    cmd_matches: &'a ArgMatches<'a>,
+}
+
+impl<'a> Debug<'a> {
+    /// Construct a new debug action.
+    pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
+        Self {
+            cmd_matches,
+        }
+    }
+
+    /// Invoke the debug action.
+    // TODO: create a trait for this method
+    pub fn invoke(&self) -> Result<(), ActionError> {
+        // Create the command matchers
+        let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
+        let matcher_debug = DebugMatcher::with(self.cmd_matches).unwrap();
+
+        // Create a table for all debug information
+        let mut table = Table::new();
+        table.set_format(FormatBuilder::new().padding(0, 2).build());
+
+        // The default host
+        table.add_row(Row::new(vec![
+            Cell::new("host:"),
+            Cell::new(SEND_DEFAULT_HOST),
+        ]));
+
+        // The history file
+        table.add_row(Row::new(vec![
+            Cell::new("history file:"),
+            Cell::new(matcher_main.history().to_str().unwrap_or("?")),
+        ]));
+
+        // The default host
+        table.add_row(Row::new(vec![
+            Cell::new("default expiry:"),
+            Cell::new(&format_duration(Duration::seconds(SEND_DEFAULT_EXPIRE_TIME))),
+        ]));
+
+        // Print the table
+        table.printstd();
+
+        Ok(())
+    }
+}

+ 1 - 3
cli/src/action/history.rs

@@ -1,7 +1,5 @@
-extern crate prettytable;
-
 use clap::ArgMatches;
-use self::prettytable::{
+use prettytable::{
     cell::Cell,
     format::FormatBuilder,
     row::Row,

+ 59 - 15
cli/src/action/info.rs

@@ -15,6 +15,13 @@ use ffsend_api::file::remote_file::{
     RemoteFile,
 };
 use ffsend_api::reqwest::Client;
+use prettytable::{
+    cell::Cell,
+    format::FormatBuilder,
+    row::Row,
+    Table,
+};
+
 
 use cmd::matcher::{
     Matcher,
@@ -101,25 +108,62 @@ impl<'a> Info<'a> {
         #[cfg(feature = "history")]
         history_tool::add(&matcher_main, file.clone(), true);
 
-        // Print all file details
-        println!("ID: {}", file.id());
+        // Create a new table for the information
+        let mut table = Table::new();
+        table.set_format(FormatBuilder::new().padding(0, 2).build());
+
+        // Add the ID
+        table.add_row(Row::new(vec![
+            Cell::new("ID:"),
+            Cell::new(file.id()),
+        ]));
+
+        // Metadata related details
         if let Some(metadata) = metadata {
+            // The file name
+            table.add_row(Row::new(vec![
+                Cell::new("name:"),
+                Cell::new(metadata.metadata().name()),
+            ]));
+
+            // The file size
             let size = metadata.size();
-            println!("Name: {}", metadata.metadata().name());
-            if size >= 1024 {
-                println!("Size: {} ({} B)", format_bytes(size), size);
-            } else {
-                println!("Size: {}", format_bytes(size));
-            }
-            println!("MIME: {}", metadata.metadata().mime());
-        }
-        println!("Downloads: {} of {}", info.download_count(), info.download_limit());
-        if ttl_millis >= 60 * 1000 {
-            println!("Expiry: {} ({}s)", format_duration(&ttl), ttl.num_seconds());
-        } else {
-            println!("Expiry: {}", format_duration(&ttl));
+            table.add_row(Row::new(vec![
+                Cell::new("MIME:"),
+                Cell::new(
+                    &if size >= 1024 {
+                        format!("{} ({} B)", format_bytes(size), size)
+                    } else {
+                        format_bytes(size)
+                    }
+                ),
+            ]));
+
+            // The file MIME
+            table.add_row(Row::new(vec![
+                Cell::new("MIME:"),
+                Cell::new(metadata.metadata().mime()),
+            ]));
         }
 
+        // The download count
+        table.add_row(Row::new(vec![
+            Cell::new("downloads:"),
+            Cell::new(&format!("{} of {}", info.download_count(), info.download_limit())),
+        ]));
+
+        // The time to live
+        table.add_row(Row::new(vec![
+            Cell::new("expiry:"),
+            Cell::new(
+                &if ttl_millis >= 60 * 1000 {
+                    format!("{} ({}s)", format_duration(&ttl), ttl.num_seconds())
+                } else {
+                    format_duration(&ttl)
+                }
+            ),
+        ]));
+
         Ok(())
     }
 }

+ 1 - 0
cli/src/action/mod.rs

@@ -1,3 +1,4 @@
+pub mod debug;
 pub mod delete;
 pub mod download;
 pub mod exists;

+ 1 - 1
cli/src/action/password.rs

@@ -7,8 +7,8 @@ use ffsend_api::file::remote_file::RemoteFile;
 use ffsend_api::reqwest::Client;
 
 use cmd::matcher::{
-    Matcher,
     main::MainMatcher,
+    Matcher,
     password::PasswordMatcher,
 };
 use error::ActionError;

+ 11 - 2
cli/src/cmd/handler.rs

@@ -3,6 +3,7 @@ extern crate directories;
 use clap::{App, AppSettings, Arg, ArgMatches};
 
 use super::matcher::{
+    DebugMatcher,
     DeleteMatcher,
     DownloadMatcher,
     ExistsMatcher,
@@ -15,6 +16,7 @@ use super::matcher::{
 #[cfg(feature = "history")]
 use super::matcher::HistoryMatcher;
 use super::subcmd::{
+    CmdDebug,
     CmdDelete,
     CmdDownload,
     CmdExists,
@@ -73,6 +75,7 @@ impl<'a: 'b, 'b> Handler<'a> {
                 .alias("assume-yes")
                 .global(true)
                 .help("Assume yes for prompts"))
+            .subcommand(CmdDebug::build())
             .subcommand(CmdDelete::build())
             .subcommand(CmdDownload::build().display_order(2))
             .subcommand(CmdExists::build())
@@ -88,8 +91,9 @@ impl<'a: 'b, 'b> Handler<'a> {
                 .short("H")
                 .value_name("FILE")
                 .global(true)
-                .help("History file to use")
-                .default_value(&DEFAULT_HISTORY_FILE))
+                .help("Use the specified history file")
+                .default_value(&DEFAULT_HISTORY_FILE)
+                .hide_default_value(true))
             .arg(Arg::with_name("incognito")
                 .long("incognito")
                 .short("i")
@@ -120,6 +124,11 @@ impl<'a: 'b, 'b> Handler<'a> {
         &self.matches
     }
 
+    /// Get the debug sub command, if matched.
+    pub fn debug(&'a self) -> Option<DebugMatcher> {
+        DebugMatcher::with(&self.matches)
+    }
+
     /// Get the delete sub command, if matched.
     pub fn delete(&'a self) -> Option<DeleteMatcher> {
         DeleteMatcher::with(&self.matches)

+ 22 - 0
cli/src/cmd/matcher/debug.rs

@@ -0,0 +1,22 @@
+use ffsend_api::url::Url;
+
+use clap::ArgMatches;
+
+use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
+use super::Matcher;
+
+/// The debug command matcher.
+pub struct DebugMatcher<'a> {
+    matches: &'a ArgMatches<'a>,
+}
+
+impl<'a> Matcher<'a> for DebugMatcher<'a> {
+    fn with(matches: &'a ArgMatches) -> Option<Self> {
+        matches.subcommand_matches("debug")
+            .map(|matches|
+                 DebugMatcher {
+                     matches,
+                 }
+            )
+    }
+}

+ 2 - 0
cli/src/cmd/matcher/mod.rs

@@ -1,3 +1,4 @@
+pub mod debug;
 pub mod delete;
 pub mod download;
 pub mod exists;
@@ -10,6 +11,7 @@ pub mod password;
 pub mod upload;
 
 // Reexport to matcher module
+pub use self::debug::DebugMatcher;
 pub use self::delete::DeleteMatcher;
 pub use self::download::DownloadMatcher;
 pub use self::exists::ExistsMatcher;

+ 14 - 0
cli/src/cmd/subcmd/debug.rs

@@ -0,0 +1,14 @@
+use clap::{App, SubCommand};
+
+use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
+
+/// The debug command definition.
+pub struct CmdDebug;
+
+impl CmdDebug {
+    pub fn build<'a, 'b>() -> App<'a, 'b> {
+        SubCommand::with_name("debug")
+            .about("View debug information")
+            .visible_alias("dbg")
+    }
+}

+ 2 - 0
cli/src/cmd/subcmd/mod.rs

@@ -1,3 +1,4 @@
+pub mod debug;
 pub mod delete;
 pub mod download;
 pub mod exists;
@@ -9,6 +10,7 @@ pub mod password;
 pub mod upload;
 
 // Reexport to cmd module
+pub use self::debug::CmdDebug;
 pub use self::delete::CmdDelete;
 pub use self::download::CmdDownload;
 pub use self::exists::CmdExists;

+ 8 - 0
cli/src/main.rs

@@ -10,6 +10,7 @@ extern crate ffsend_api;
 #[cfg(feature = "history")]
 #[macro_use]
 extern crate lazy_static;
+extern crate prettytable;
 extern crate rpassword;
 extern crate serde;
 #[cfg(feature = "history")]
@@ -29,6 +30,7 @@ mod host;
 mod progress;
 mod util;
 
+use action::debug::Debug;
 use action::delete::Delete;
 use action::download::Download;
 use action::exists::Exists;
@@ -58,6 +60,12 @@ fn main() {
 /// If no proper action is selected, the program will quit with an error
 /// message.
 fn invoke_action(handler: &Handler) -> Result<(), Error> {
+    // Match the debug command
+    if handler.debug().is_some() {
+        return Debug::new(handler.matches()).invoke()
+            .map_err(|err| err.into());
+    }
+
     // Match the delete command
     if handler.delete().is_some() {
         return Delete::new(handler.matches()).invoke()

+ 2 - 2
cli/src/util.rs

@@ -530,9 +530,9 @@ pub fn format_bytes(bytes: u64) -> String {
 /// - `9m55s`
 /// - `1s`
 /// - `now`
-pub fn format_duration(duration: &Duration) -> String {
+pub fn format_duration(duration: impl Borrow<Duration>) -> String {
     // Get the total number of seconds, return immediately if zero or less
-    let mut secs = duration.num_seconds();
+    let mut secs = duration.borrow().num_seconds();
     if secs <= 0 {
         return "now".into();
     }