Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactive prompts #1471

Closed
bedax opened this issue May 13, 2019 · 6 comments
Closed

Interactive prompts #1471

bedax opened this issue May 13, 2019 · 6 comments

Comments

@bedax
Copy link

bedax commented May 13, 2019

Hello, I have a small command line program which I always forget the options for and have to use --help for every time. After looking at darcs recently it occurred to me that it would be nice to get a series of prompts when clap can't parse the input.

For example, let's say I'm writing a simple log program, with the following usage:

USAGE:
   log [OPTIONS] <message>


FLAGS:
   -o, --overwrite
      Overwrite the output file before writing


OPTIONS:
   -p, --path <path>
      The path to the logs file, defaults to `~/.logs`
      
   -l, --level <level>
      The record's log level


ARGS:
   <message> 
      The message to log

If I type log with no arguments, it would be good to be given something like the following:

USAGE: log [OPTIONS] <message>

[y/N] Overwrite the output file before writing
> y

[optional] The path to the log file, defaults to `~/.logs` 
> ~/.some-other-file

[optional] The record's log level 
> info

[required] The message to log 
> something happened

I realise this whole feature isn't necessarily something you want baked into the core, perhaps an external crate, perhaps a feature. Either way, I'm happy to write this, but I'm not sure how best to tap into the parser.

I think it would be useful if clap::App had a get_matches_from_fns function with a signature like this

struct CommandDescription<'a> {
   name: &'a str,
   help: &'a str
}

struct ArgumentDescription<'a> {
   short: &'a str,
   long: &'a str,
   help: &'a str
}

impl clap::App {
   fn get_matches_from_fns<T: Into<OsString> + Clone>
                          (command_getter: impl Fn(CommandDescription) -> T,
                           flag_getter: impl Fn(ArgumentDescription) -> bool,
                           option_getter: impl Fn(ArgumentDescription) -> Option<T>,
                           argument_getter: impl Fn(ArgumentDescription) -> T) 
                           -> ArgMatches ...
}

Then those getters would in this case prompt the user for the input

@danieleades
Copy link
Contributor

this would be amazing.

If this isn't desired for the core library, are there any hooks that would allow one to implement outside the library?

i see that some work was already done in #235. Looks like this was mostly complete, but there some questions about it being a blocking API.

I can imagine some use cases where this would be a problem, but I can imagine a lot more where it's a non-issue.

  • small command line programs which do one thing and then exit
  • long-running programs that need to block on certain parameters until they can run anyway
  • Command line clients to other programs

non-blocking APIs smells like a complete rewrite of the library to me (everything async, blocking api which wraps the async behind a feature flag).

@danieleades
Copy link
Contributor

danieleades commented Jan 11, 2020

could this be resolved by simply adding a hook to set a default using a closure?

something like

Arg::default_with<F, S>(mut self, fun: F) -> Self
    where
    F: FnOnce -> S,
    S: AsRef<str>,

then passing your prompt function into this method.

I'm not too familiar with this codebase, does this sound like a reasonable solution? @kbknapp

@danieleades
Copy link
Contributor

danieleades commented Jan 11, 2020

i guess default_with wouldn't be compatible with default_value, so the default value field would become an enum, containing either the closure, or the value.

for symmetry, you'd have to do something similar for the other corresponding methods

value method closure method
default_value default_with
default_value_os default_with_os
default_values defaults_with

How are conflicting arguments handled elsewhere in Clap? should it be an enum, such that if you use both methods, the second one clobbers the first? or should they be separate fields with a debug_assert to check they're not both set?

@danieleades
Copy link
Contributor

you would also want a default_with_if- so you could have patterns like

arg.default_with_if("interactive", prompt_user)

@pksunkara
Copy link
Member

I have worked on https://github.com/termapps/enquirer this week. Either a hook fn or a matches fn, this library can easily provide them. IMO, the prompts shouldn't be in the core. Hooks? yes but not prompts.

@CreepySkeleton
Copy link
Contributor

Closing in favor of #1634 , they are about the same thing anyway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants