use chrono::Duration;
use clap::ArgMatches;
use failure::Fail;
use ffsend_api::action::exists::{Error as ExistsError, Exists as ApiExists};
use ffsend_api::action::info::{Error as InfoError, Info as ApiInfo};
use ffsend_api::action::metadata::Metadata as ApiMetadata;
use ffsend_api::file::remote_file::{FileParseError, RemoteFile};
use prettytable::{format::FormatBuilder, Cell, Row, Table};
use crate::client::create_config;
use crate::cmd::matcher::{info::InfoMatcher, main::MainMatcher, Matcher};
#[cfg(feature = "history")]
use crate::history_tool;
use crate::util::{
ensure_owner_token, ensure_password, format_bytes, format_duration, print_error,
};
pub struct Info<'a> {
cmd_matches: &'a ArgMatches<'a>,
}
impl<'a> Info<'a> {
pub fn new(cmd_matches: &'a ArgMatches<'a>) -> Self {
Self { cmd_matches }
}
pub fn invoke(&self) -> Result<(), Error> {
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_info = InfoMatcher::with(self.cmd_matches).unwrap();
let url = matcher_info.url();
let client_config = create_config(&matcher_main);
let client = client_config.client(false);
let mut file = RemoteFile::parse_url(url, matcher_info.owner())?;
#[cfg(feature = "history")]
history_tool::derive_file_properties(&matcher_main, &mut file);
let has_owner = ensure_owner_token(file.owner_token_mut(), &matcher_main, true);
let exists = ApiExists::new(&file).invoke(&client)?;
if !exists.exists() {
#[cfg(feature = "history")]
history_tool::remove(&matcher_main, &file);
return Err(Error::Expired);
}
let mut password = matcher_info.password();
let has_password = ensure_password(
&mut password,
exists.requires_password(),
&matcher_main,
true,
);
let info = if has_owner {
Some(ApiInfo::new(&file, None).invoke(&client)?)
} else {
None
};
let metadata = if has_password {
ApiMetadata::new(&file, password, false)
.invoke(&client)
.map_err(|err| {
print_error(err.context("failed to fetch file metadata, showing limited info"))
})
.ok()
} else {
None
};
if let Some(info) = &info {
let ttl_millis = info.ttl_millis() as i64;
let ttl = Duration::milliseconds(ttl_millis);
file.set_expire_duration(ttl);
}
#[cfg(feature = "history")]
history_tool::add(&matcher_main, file.clone(), true);
let mut table = Table::new();
table.set_format(FormatBuilder::new().padding(0, 2).build());
table.add_row(Row::new(vec![Cell::new("ID:"), Cell::new(file.id())]));
if let Some(metadata) = &metadata {
table.add_row(Row::new(vec![
Cell::new("Name:"),
Cell::new(metadata.metadata().name()),
]));
let size = metadata.size();
table.add_row(Row::new(vec![
Cell::new("Size:"),
Cell::new(&if size >= 1024 {
format!("{} ({} B)", format_bytes(size), size)
} else {
format_bytes(size)
}),
]));
table.add_row(Row::new(vec![
Cell::new("MIME:"),
Cell::new(metadata.metadata().mime()),
]));
}
if let Some(info) = &info {
table.add_row(Row::new(vec![
Cell::new("Downloads:"),
Cell::new(&format!(
"{} of {}",
info.download_count(),
info.download_limit()
)),
]));
let ttl_millis = info.ttl_millis() as i64;
let ttl = Duration::milliseconds(ttl_millis);
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)
}),
]));
}
table.printstd();
Ok(())
}
}
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "invalid share link")]
InvalidUrl(#[cause] FileParseError),
#[fail(display = "failed to check whether the file exists")]
Exists(#[cause] ExistsError),
#[fail(display = "failed to fetch file info")]
Info(#[cause] InfoError),
#[fail(display = "the file has expired or did never exist")]
Expired,
}
impl From<FileParseError> for Error {
fn from(err: FileParseError) -> Error {
Error::InvalidUrl(err)
}
}
impl From<ExistsError> for Error {
fn from(err: ExistsError) -> Error {
Error::Exists(err)
}
}
impl From<InfoError> for Error {
fn from(err: InfoError) -> Error {
Error::Info(err)
}
}