mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 17:49:15 +02:00
Create interactive prompt yes/no method
This commit is contained in:
parent
896c01b6ef
commit
ff8c644194
2 changed files with 101 additions and 17 deletions
33
IDEAS.md
33
IDEAS.md
|
@ -1,36 +1,39 @@
|
|||
# Ideas
|
||||
- allow creating non existent directories with the `-f` flag
|
||||
- only allow file extension renaming on upload with `-f` flag
|
||||
- `-y` flag for assume yes
|
||||
- `-f` flag for forcing (no interact?)
|
||||
# First release
|
||||
- Implement `-f` flag for forcing
|
||||
- Allow creating non existent directories with the `-f` flag
|
||||
- Only allow file extension renaming on upload with `-f` flag
|
||||
- Only allow empty passwords with `-f` flag
|
||||
- Implement `-y` flag for assume yes
|
||||
- Check for file expiry everywhere
|
||||
- Soft limit uploads to 1GB and 2GB
|
||||
- Remember all uploaded files, make files listable
|
||||
- Incognito mode, to not remember files `--incognito`
|
||||
- Automatically get owner token, from file history when setting password
|
||||
- Use clipboard through `xclip` on Linux if available for persistence
|
||||
- Automated releases through CI
|
||||
- Release binaries on GitHub
|
||||
- Ubuntu PPA package
|
||||
|
||||
# Other ideas
|
||||
- Box errors
|
||||
- On download, mention a wrong or missing password with a HTTP 401 response
|
||||
- Automatically get owner token, from file history when setting password
|
||||
- Implement error handling everywhere properly
|
||||
- Quick upload/download without `upload` or `download` subcommands?
|
||||
- Flag to explicitly delete file after download
|
||||
- Allow file deletion by consuming all download slots
|
||||
- Check remote version and heartbeat using `/__version__`
|
||||
- Check whether the file still exists everywhere
|
||||
- API actions contain duplicate code, create centralized functions
|
||||
- Download to a temporary location first
|
||||
- Soft limit uploads to 1GB and 2GB
|
||||
- Allow piping input/output files
|
||||
- Allow file/directory archiving on upload
|
||||
- Allow unarchiving on download
|
||||
- Allow hiding the progress bar, and/or showing simple progress
|
||||
- Remember all uploaded files, make files listable
|
||||
- Incognito mode, to not remember files `--incognito`
|
||||
- Allow hiding the progress bar, and/or showing simple progress (with `-q`)
|
||||
- Document all code components
|
||||
- Dotfile for default properties
|
||||
- Host configuration file for host tags, to easily upload to other hosts
|
||||
- Generate man pages
|
||||
- Automated releases through CI
|
||||
- Release binaries on GitHub
|
||||
- Ubuntu PPA package
|
||||
- Move API URL generator methods out of remote file class
|
||||
- Prompt if a file download password is required
|
||||
- Do not allow empty passwords (must force with `-f`) (as not usable on web)
|
||||
- Must use `-f` to overwrite existing file
|
||||
- Rename host to server?
|
||||
- Read and write files from and to stdin and stdout with `-` as file
|
||||
|
|
|
@ -137,12 +137,11 @@ pub fn ensure_password(
|
|||
/// Prompt the user to enter some value.
|
||||
/// The prompt that is shown should be passed to `msg`,
|
||||
/// excluding the `:` suffix.
|
||||
// TODO: do not prompt if no-interactive
|
||||
pub fn prompt(msg: &str, main_matcher: &MainMatcher) -> String {
|
||||
// Quit with an error if we may not interact
|
||||
if main_matcher.no_interact() {
|
||||
quit_error(format_err!(
|
||||
"Could not prompt for '{}', must be specified in no-interact mode",
|
||||
"Could not prompt for '{}' in no-interact mode, maybe specify it",
|
||||
msg,
|
||||
).compat());
|
||||
}
|
||||
|
@ -163,6 +162,88 @@ pub fn prompt(msg: &str, main_matcher: &MainMatcher) -> String {
|
|||
input.trim().to_owned()
|
||||
}
|
||||
|
||||
/// Prompt the user for a question, allowing a yes or now answer.
|
||||
/// True is returned if yes was answered, false if no.
|
||||
///
|
||||
/// A default may be given, which is chosen if no-interact mode is
|
||||
/// enabled, or if enter was pressed by the user without entering anything.
|
||||
pub fn prompt_yes(
|
||||
msg: &str,
|
||||
def: Option<bool>,
|
||||
main_matcher: &MainMatcher,
|
||||
) -> bool {
|
||||
// Define the available options string
|
||||
let options = format!("[{}/{}]", match def {
|
||||
Some(def) if def => "Y",
|
||||
_ => "y",
|
||||
}, match def {
|
||||
Some(def) if !def => "N",
|
||||
_ => "n",
|
||||
});
|
||||
|
||||
// Assume yes
|
||||
if main_matcher.assume_yes() {
|
||||
eprintln!("{} {}: yes", msg, options);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Autoselect if in no-interact mode
|
||||
if main_matcher.no_interact() {
|
||||
if let Some(def) = def {
|
||||
eprintln!("{} {}: {}", msg, options, if def {
|
||||
"yes"
|
||||
} else {
|
||||
"no"
|
||||
});
|
||||
return def;
|
||||
} else {
|
||||
quit_error(format_err!(
|
||||
"Could not prompt question '{}' in no-interact mode, maybe specify it",
|
||||
msg,
|
||||
).compat());
|
||||
}
|
||||
}
|
||||
|
||||
// Get the user input
|
||||
let answer = prompt(&format!("{} {}", msg, options), main_matcher);
|
||||
|
||||
// Assume the default if the answer is empty
|
||||
if answer.is_empty() && def.is_some() {
|
||||
return def.unwrap();
|
||||
}
|
||||
|
||||
// Derive a boolean and return
|
||||
match derive_bool(&answer) {
|
||||
Some(answer) => answer,
|
||||
None => prompt_yes(msg, def, main_matcher),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to derive true or false (yes or no) from the given input.
|
||||
/// None is returned if no boolean could be derived accurately.
|
||||
fn derive_bool(input: &str) -> Option<bool> {
|
||||
// Process the input
|
||||
let input = input.trim().to_lowercase();
|
||||
|
||||
// Handle short or incomplete answers
|
||||
match input.as_str() {
|
||||
"y" | "ye" | "t" | "1" => return Some(true),
|
||||
"n" | "f" | "0" => return Some(false),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// Handle complete answers with any suffix
|
||||
if input.starts_with("yes") || input.starts_with("true") {
|
||||
return Some(true);
|
||||
}
|
||||
if input.starts_with("no") || input.starts_with("false") {
|
||||
return Some(false);
|
||||
}
|
||||
|
||||
// The answer could not be determined, return none
|
||||
None
|
||||
}
|
||||
|
||||
/// Prompt the user to enter an owner token.
|
||||
pub fn prompt_owner_token(main_matcher: &MainMatcher) -> String {
|
||||
prompt("Owner token", main_matcher)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue