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

ability to treat all remaining arguments as free arguments #1

Closed
mllken opened this issue Jul 17, 2021 · 5 comments
Closed

ability to treat all remaining arguments as free arguments #1

mllken opened this issue Jul 17, 2021 · 5 comments

Comments

@mllken
Copy link

mllken commented Jul 17, 2021

Hi, really nice library.

I would like to stop parsing options when I encounter a "free" argument (a Arg::Value) and treat all subsequent arguments as "free" arguments, regardless if they contain option flags. Ideally, I would be able to call something like parser.finish() (fn finish(self) -> Vec<OsString>) to get the remaining arguments. I didn't see an obvious way to do it with the current API.

@blyxxyz
Copy link
Owner

blyxxyz commented Jul 17, 2021

I wrote up a whole reply before realizing that there is a way to do this, though it's arguably abusing the API:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use lexopt::prelude::*;
    let mut parser = lexopt::Parser::from_env();
    while let Some(arg) = parser.next()? {
        match arg {
            Short(ch) => println!("Got short flag -{}", ch),
            Long(opt) => println!("Got long flag --{}", opt),
            Value(val) => {
                println!("Got free arg {:?}", val);
                while let Ok(val) = parser.value() {
                    println!("Got free arg {:?}", val);
                }
            }
        }
    }
    Ok(())
}

or more cryptically but maybe more usefully:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use lexopt::prelude::*;
    let mut parser = lexopt::Parser::from_env();
    let mut free = Vec::new();
    while let Some(arg) = parser.next()? {
        match arg {
            Short(ch) => println!("Got short flag -{}", ch),
            Long(opt) => println!("Got long flag --{}", opt),
            Value(val) => {
                free.push(val);
                free.extend(std::iter::from_fn(|| parser.value().ok()));
            }
        }
    }
    println!("Got free arguments {:?}", free);
    Ok(())
}

.value() will treat all new arguments as free-standing, and since you call it after getting an Arg::Value from .next() there's no risk you're looking at actual option-arguments as from --opt=value.

I don't think this should get its own method but it's worth documenting somewhere. Not sure where. In examples/, with a link from Parser::value's docs? Or in the top-level module documentation?

@mllken
Copy link
Author

mllken commented Jul 17, 2021

That's awesome news. The current documentation for .value() does make it sound like it's strictly for getting a value after seeing an option, but it indeed has the ability to be more generic. It also advises against using it as a way to collect positional arguments. It might be worth rewording as:

"""
pub fn value(&mut self) -> Result<OsString, Error>

Get the next value from the command line.

This function should normally be called right after seeing an option that expects a value. Positional arguments should be collected using next(), but this function can also be used to collect all remaining positional arguments in cases where there is a need to stop processing options.

A value is collected even if it looks like an option (i.e., starts with -).
"""

@mllken
Copy link
Author

mllken commented Jul 17, 2021

Also, a quick API suggestion that might make .value() cleaner would be for the signature to be:

pub fn value(&mut self) -> Option<OsString>

With None indicating an end of command line, similar to how Iterators use None to indicate they are finished returning values.

@blyxxyz
Copy link
Owner

blyxxyz commented Jul 17, 2021

It might be worth rewording as

Yes, I think I'll do something like that.

The wrinkle is that it's only 100% correct to do that right after seeing an Arg::Value. So I think I'll link to an example with a more elaborate explanation.

pub fn value(&mut self) -> Option<OsString>

This would be a cleaner signature, but it would make it harder to use in the common case. Right now you can just write .value()? and automatically turn a missing value into a proper error that even includes the relevant flag.

Some day it might be possible to have a return type of Result<OsString, Error::MissingValue>. I think that would be a good compromise to show at a glance that there's only one error condition.

@blyxxyz
Copy link
Owner

blyxxyz commented Jul 17, 2021

I wrote up an example and linked to it from Parser::value()'s documentation in 612c944, it'll be on docs.rs with the next release.

Thank you for asking about this! I had wondered about it before.

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

2 participants