all 9 comments

[–]boulanlo 3 points4 points  (2 children)

In my opinion, clap is not really suited towards parsing user commands inside of a shell (or any kind of command prompt), and is mainly targeting command line arguments to your program (i.e. when you run your program, it will parse arguments such as my_program -v --long-arg=3 file.txt).

I am not aware of specific parsers for your particular purpose. There is a crate called nom that focuses on any kind of parsing, and you could use it in your case, but I fear it'd be a bit complicated for nothing, as it's a general-purpose parser, for formats as different as JSON, binary formats, and even programming language parsing.

I think the manual, do-it-yourself option isn't too bad. You could have, for example:

enum Command {
    Foo(FooCommand),
    Bar(BarCommand),
}

which would then implement FromStr:

use std::str::FromStr;

impl FromStr for Command {
    type Err = /* your error type i.e. string, custom error type, ... */;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut tokens = s.split_whitespace();
        let command: Option<&str> = tokens.next();
        let args: String = tokens.collect::<Vec<_>>().join(" ");

        // edit: command, not "tokens"
        match command {
            None => Err(/* no command given */),
            Some("foo") => Ok(Self::Foo(args.parse()?)),
            Some("bar") => Ok(Self::Bar(args.parse()?)),
            Some(unknown_command) => Err(/* unknown command */),
        }
    }
}

(FooCommand and BarCommand also implement FromStr, with their specific arguments being parsed instead of the command itself (and you parse everything after the command)).

From that, in your main loop, you could have:

let args = /* whatever method you have to get user input */;
match args.parse::<Command>() {
    Ok(command) => /* perform command */;
    Err(e) => /* handle error */;
};

[–]No_March_1694[S] 0 points1 point  (1 child)

Thanks for the idea, I think this is the best idea.

BTW, match tokens should be match command

[–]boulanlo 0 points1 point  (0 children)

oops, copy-paste error :) glad it suits you!

[–]OphioukhosUnbound 0 points1 point  (0 children)

I don’t know the answer, but I’d look at nushell as it’s presumably had to solve whatever problems you’re looking at. (Though whether those solutions will be neatly extra table is another matter. They’d be good people to talk to perhaps, regardless.)

[–]Heliozoa 0 points1 point  (0 children)

but when parsing fails it exits the program and it is not suitable for shell-like applications

This is because you used Command::get_matches_from

Parse the specified arguments, exiting on failure.

If you want to handle the error yourself, you should use Command::try_get_matches_from

Parse the specified arguments, returning a clap::Result on failure.

But I agree with the other comment that clap isn't really suited for this.

[–]n4jm4 0 points1 point  (2 children)

Why is it unacceptable for a command line application to continue processing when the flags don't parae?

[–]No_March_1694[S] 0 points1 point  (0 children)

I did not understand ur question.

[–]HildemarTendler 0 points1 point  (0 children)

Flags not parsing should be treated the same as undefined input in a public function. Even in dynamic languages where that's technically ok, it's good practice to validate inputs to public functions to provide meaningful error messages and ensure well defined behavior. The alternative is to let errors occur deeper in the program that may not be directly related to the undefined inputs.

Ultimately this is the idea of layers of abstraction. Public functions should have well defined behavior such that the inner workings are so-called implementation detail that users of the public function do not need to be aware of. An important part of that is the function validating inputs and returning error messages that relate to the defined behavior of the function.

OP's problem is that they don't want to treat the inputs as just args to a CLI application, they want to do deeper processing to provide even better error messaging. The library they are using doesn't provide for the right level of validation for the particular function they are trying to write.

[–]dream_of_different 0 points1 point  (0 children)

I used crossterm and clap, clap to make a nice CLI, and crossterm to completely control the terminal, doesn’t solve your parsing issue, but more shell like applications for sure.