Respect download limits enforced by remote server, update ffsend-api
See issue timvisee/ffsend#17
This commit is contained in:
parent
b59650f25d
commit
888b10afd6
7 changed files with 145 additions and 77 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -377,10 +377,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -582,7 +584,7 @@ dependencies = [
|
|||
"derive_builder 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffsend-api 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffsend-api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"open 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -603,7 +605,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ffsend-api"
|
||||
version = "0.3.0"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1240,7 +1242,7 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2248,7 +2250,7 @@ dependencies = [
|
|||
"checksum crossterm_utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d26f24386ea91f9c55a85531dd3ee3673e4c82729e64567928665aca3a47c741"
|
||||
"checksum crossterm_winapi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "71dc52848cd3c5e06d5d0e193c0d64ce93b71502e124723a33bf615ef7089610"
|
||||
"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||
"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04"
|
||||
"checksum csv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f0782c7154d8dd08f4adeb5aa22ab178c10281915f7da68d10bb646f03aaee73"
|
||||
"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65"
|
||||
"checksum darling 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "49fc76d30c96cc0bdc8b966968e6535d900f3e42c56204d355192a670d989c6e"
|
||||
"checksum darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9158d690bc62a3a57c3e45b85e4d50de2008b39345592c64efd79345c7e24be0"
|
||||
|
@ -2269,7 +2271,7 @@ dependencies = [
|
|||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
"checksum ffsend-api 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca01e082c12a10fc477f7bb7ea4e264508c0c5d2a8f7bad85862dd403477ab57"
|
||||
"checksum ffsend-api 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1ce8879c0aae328782352fc937abf2bfafe9b2570735a555fa45caac8dcb91f"
|
||||
"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646"
|
||||
"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
|
|
|
@ -97,7 +97,7 @@ colored = "1.7"
|
|||
derive_builder = "0.7"
|
||||
directories = "1.0"
|
||||
failure = "0.1"
|
||||
ffsend-api = { version = "0.3.0", default-features = false }
|
||||
ffsend-api = { version = "0.3.2", default-features = false }
|
||||
fs2 = "0.4"
|
||||
lazy_static = "1.0"
|
||||
open = "1"
|
||||
|
|
|
@ -44,7 +44,7 @@ impl<'a> Params<'a> {
|
|||
|
||||
// Build the parameters data object
|
||||
let data = ParamsDataBuilder::default()
|
||||
.download_limit(matcher_params.download_limit())
|
||||
.download_limit(matcher_params.download_limit().map(|d| d as u8))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -68,47 +68,55 @@ impl<'a> Upload<'a> {
|
|||
|
||||
// TODO: ensure the file exists and is accessible
|
||||
|
||||
// Determine the max file size
|
||||
// TODO: set false parameter to authentication state
|
||||
let max_size = upload_size_max(api_version, false);
|
||||
// We do not authenticate for now
|
||||
let auth = false;
|
||||
|
||||
// Get the file size to warn about large files
|
||||
if let Ok(size) = File::open(&path)
|
||||
.and_then(|f| f.metadata())
|
||||
.map(|m| m.len())
|
||||
// TODO: extract this into external function
|
||||
{
|
||||
if size > max_size && !matcher_main.force() {
|
||||
// The file is too large, show an error and quit
|
||||
quit_error_msg(
|
||||
format!(
|
||||
"the file size is {}, bigger than the maximum allowed of {}",
|
||||
format_bytes(size),
|
||||
format_bytes(max_size),
|
||||
),
|
||||
ErrorHintsBuilder::default()
|
||||
.force(true)
|
||||
.verbose(false)
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
} else if size > UPLOAD_SIZE_MAX_RECOMMENDED && !matcher_main.force() {
|
||||
// The file is larger than the recommended maximum, warn
|
||||
eprintln!(
|
||||
"The file size is {}, bigger than the recommended maximum of {}",
|
||||
format_bytes(size),
|
||||
format_bytes(UPLOAD_SIZE_MAX_RECOMMENDED),
|
||||
);
|
||||
// Determine the max file size
|
||||
// TODO: set false parameter to authentication state
|
||||
let max_size = upload_size_max(api_version, auth);
|
||||
|
||||
// Prompt the user to continue, quit if the user answered no
|
||||
if !prompt_yes("Continue uploading?", Some(true), &matcher_main) {
|
||||
println!("Upload cancelled");
|
||||
quit();
|
||||
// Get the file size to warn about large files
|
||||
if let Ok(size) = File::open(&path)
|
||||
.and_then(|f| f.metadata())
|
||||
.map(|m| m.len())
|
||||
{
|
||||
if size > max_size && !matcher_main.force() {
|
||||
// The file is too large, show an error and quit
|
||||
quit_error_msg(
|
||||
format!(
|
||||
"the file size is {}, bigger than the maximum allowed of {}",
|
||||
format_bytes(size),
|
||||
format_bytes(max_size),
|
||||
),
|
||||
ErrorHintsBuilder::default()
|
||||
.force(true)
|
||||
.verbose(false)
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
} else if size > UPLOAD_SIZE_MAX_RECOMMENDED && !matcher_main.force() {
|
||||
// The file is larger than the recommended maximum, warn
|
||||
eprintln!(
|
||||
"The file size is {}, bigger than the recommended maximum of {}",
|
||||
format_bytes(size),
|
||||
format_bytes(UPLOAD_SIZE_MAX_RECOMMENDED),
|
||||
);
|
||||
|
||||
// Prompt the user to continue, quit if the user answered no
|
||||
if !prompt_yes("Continue uploading?", Some(true), &matcher_main) {
|
||||
println!("Upload cancelled");
|
||||
quit();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print_error_msg("failed to check the file size, ignoring");
|
||||
}
|
||||
} else {
|
||||
print_error_msg("failed to check the file size, ignoring");
|
||||
}
|
||||
|
||||
// TODO: assert max expiry time for file
|
||||
|
||||
// Create a reqwest client capable for uploading files
|
||||
let transfer_client = client_config.client(true);
|
||||
|
||||
|
@ -119,7 +127,11 @@ impl<'a> Upload<'a> {
|
|||
let params = {
|
||||
// Build the parameters data object
|
||||
let params = ParamsDataBuilder::default()
|
||||
.download_limit(matcher_upload.download_limit())
|
||||
.download_limit(
|
||||
matcher_upload
|
||||
.download_limit(&matcher_main, api_version, auth)
|
||||
.map(|d| d as u8),
|
||||
)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,15 +1,63 @@
|
|||
use clap::{Arg, ArgMatches};
|
||||
use ffsend_api::action::params::{
|
||||
PARAMS_DOWNLOAD_MAX as DOWNLOAD_MAX, PARAMS_DOWNLOAD_MIN as DOWNLOAD_MIN,
|
||||
};
|
||||
use ffsend_api::api::Version as ApiVersion;
|
||||
use ffsend_api::config::downloads_max;
|
||||
|
||||
use super::{CmdArg, CmdArgFlag, CmdArgOption};
|
||||
use crate::cmd::matcher::MainMatcher;
|
||||
use crate::util::{highlight, prompt_yes};
|
||||
|
||||
use crate::util::{quit_error_msg, ErrorHintsBuilder};
|
||||
use crate::util::{quit, ErrorHintsBuilder};
|
||||
|
||||
/// The download limit argument.
|
||||
pub struct ArgDownloadLimit {}
|
||||
|
||||
impl ArgDownloadLimit {
|
||||
pub fn value_checked<'a>(
|
||||
matches: &ArgMatches<'a>,
|
||||
main_matcher: &MainMatcher,
|
||||
api_version: ApiVersion,
|
||||
auth: bool,
|
||||
) -> Option<usize> {
|
||||
// Get the download value
|
||||
let mut downloads = Self::value(matches)?;
|
||||
|
||||
// Get number of allowed downloads, return if allowed
|
||||
let allowed = downloads_max(api_version, auth);
|
||||
if allowed.contains(&downloads) {
|
||||
return Some(downloads);
|
||||
}
|
||||
|
||||
// Prompt the user the specified downloads limit is invalid
|
||||
let allowed_str = allowed
|
||||
.iter()
|
||||
.map(|value| format!("{}", value))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
eprintln!("The downloads limit must be one of: {}", allowed_str,);
|
||||
if auth {
|
||||
eprintln!("Use '{}' to force", highlight("--force"));
|
||||
} else {
|
||||
eprintln!(
|
||||
"Use '{}' to force, authenticate for higher limits",
|
||||
highlight("--force")
|
||||
);
|
||||
}
|
||||
|
||||
// Ask to use closest limit, quit if user cancelled
|
||||
let closest = closest(allowed, downloads);
|
||||
if !prompt_yes(
|
||||
&format!("Would you like to limit downloads to {} instead?", closest),
|
||||
None,
|
||||
main_matcher,
|
||||
) {
|
||||
quit();
|
||||
}
|
||||
downloads = closest;
|
||||
|
||||
Some(downloads)
|
||||
}
|
||||
}
|
||||
|
||||
impl CmdArg for ArgDownloadLimit {
|
||||
fn name() -> &'static str {
|
||||
"download-limit"
|
||||
|
@ -29,30 +77,26 @@ impl CmdArg for ArgDownloadLimit {
|
|||
impl CmdArgFlag for ArgDownloadLimit {}
|
||||
|
||||
impl<'a> CmdArgOption<'a> for ArgDownloadLimit {
|
||||
type Value = Option<u8>;
|
||||
type Value = Option<usize>;
|
||||
|
||||
fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
|
||||
// TODO: do not unwrap, report an error
|
||||
Self::value_raw(matches)
|
||||
.map(|d| d.parse::<u8>().expect("invalid download limit"))
|
||||
.and_then(|d| {
|
||||
// Check the download limit bounds
|
||||
// TODO: somehow allow to force a different number here
|
||||
if d < DOWNLOAD_MIN || d > DOWNLOAD_MAX {
|
||||
quit_error_msg(
|
||||
format!(
|
||||
"invalid download limit, must be between {} and {}",
|
||||
DOWNLOAD_MIN, DOWNLOAD_MAX,
|
||||
),
|
||||
ErrorHintsBuilder::default()
|
||||
.force(false)
|
||||
.verbose(false)
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
Some(d)
|
||||
})
|
||||
Self::value_raw(matches).map(|d| d.parse::<usize>().expect("invalid download limit"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the closest value to `current` in the given `values` range.
|
||||
fn closest(values: &[usize], current: usize) -> usize {
|
||||
// Own the values, sort and reverse, start with biggest first
|
||||
let mut values = values.to_vec();
|
||||
values.sort_unstable();
|
||||
|
||||
// Find the closest value, return it
|
||||
*values
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|value| (value, (current as i64 - *value as i64).abs()))
|
||||
.min_by_key(|value| value.1)
|
||||
.expect("failed to find closest value, none given")
|
||||
.0
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ impl<'a: 'b, 'b> ParamsMatcher<'a> {
|
|||
}
|
||||
|
||||
/// Get the download limit.
|
||||
pub fn download_limit(&'a self) -> Option<u8> {
|
||||
pub fn download_limit(&'a self) -> Option<usize> {
|
||||
ArgDownloadLimit::value(self.matches)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use clap::ArgMatches;
|
||||
use ffsend_api::action::params::PARAMS_DEFAULT_DOWNLOAD as DOWNLOAD_DEFAULT;
|
||||
use ffsend_api::api::Version as ApiVersion;
|
||||
use ffsend_api::url::Url;
|
||||
|
||||
use super::Matcher;
|
||||
use crate::cmd::arg::{
|
||||
ArgDownloadLimit, ArgGenPassphrase, ArgHost, ArgPassword, CmdArgFlag, CmdArgOption,
|
||||
use crate::cmd::{
|
||||
arg::{ArgDownloadLimit, ArgGenPassphrase, ArgHost, ArgPassword, CmdArgFlag, CmdArgOption},
|
||||
matcher::MainMatcher,
|
||||
};
|
||||
use crate::util::{bin_name, env_var_present, quit_error_msg, ErrorHintsBuilder};
|
||||
|
||||
|
@ -73,13 +75,21 @@ impl<'a: 'b, 'b> UploadMatcher<'a> {
|
|||
}
|
||||
|
||||
/// Get the download limit.
|
||||
///
|
||||
/// If the download limit was the default, `None` is returned to not
|
||||
/// explicitly set it.
|
||||
pub fn download_limit(&'a self) -> Option<u8> {
|
||||
ArgDownloadLimit::value(self.matches).and_then(|d| match d {
|
||||
DOWNLOAD_DEFAULT => None,
|
||||
d => Some(d),
|
||||
})
|
||||
pub fn download_limit(
|
||||
&'a self,
|
||||
main_matcher: &MainMatcher,
|
||||
api_version: ApiVersion,
|
||||
auth: bool,
|
||||
) -> Option<usize> {
|
||||
ArgDownloadLimit::value_checked(self.matches, main_matcher, api_version, auth).and_then(
|
||||
|d| match d {
|
||||
d if d == DOWNLOAD_DEFAULT as usize => None,
|
||||
d => Some(d),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Check whether to archive the file to upload.
|
||||
|
|
Loading…
Add table
Reference in a new issue