Create debug command, pretty print formatted tables
This commit is contained in:
parent
db1087c9e9
commit
7425c6c405
13 changed files with 192 additions and 24 deletions
|
@ -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
cli/src/action/debug.rs
Normal file
68
cli/src/action/debug.rs
Normal file
|
@ -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,7 +1,5 @@
|
|||
extern crate prettytable;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use self::prettytable::{
|
||||
use prettytable::{
|
||||
cell::Cell,
|
||||
format::FormatBuilder,
|
||||
row::Row,
|
||||
|
|
|
@ -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,3 +1,4 @@
|
|||
pub mod debug;
|
||||
pub mod delete;
|
||||
pub mod download;
|
||||
pub mod exists;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
cli/src/cmd/matcher/debug.rs
Normal file
22
cli/src/cmd/matcher/debug.rs
Normal file
|
@ -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,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
cli/src/cmd/subcmd/debug.rs
Normal file
14
cli/src/cmd/subcmd/debug.rs
Normal file
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue