Bläddra i källkod

Create interactive prompt yes/no method

timvisee 7 år sedan
förälder
incheckning
ff8c644194
2 ändrade filer med 101 tillägg och 17 borttagningar
  1. 18 15
      IDEAS.md
  2. 83 2
      cli/src/util.rs

+ 18 - 15
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

+ 83 - 2
cli/src/util.rs

@@ -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)