-
Notifications
You must be signed in to change notification settings - Fork 9
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
How to get remaining arguments #12
Comments
I found a solution though I don't think it's very nice: Value(val) if command.is_none() => {
let cmd = val.to_string_lossy().into_owned();
let mut args = vec![cmd];
while let Some(a) = parser.next()? {
match a {
Long(s) => args.push(format!("--{}", s)),
Short(c) => args.push(format!("-{}", c)),
Value(v) => args.push(v.to_string_lossy().into_owned()),
}
}
command = Some(Command::External(args))
} |
Assuming the last argument was a It's mentioned in a single sentence in the docs but I would like to have a proper API for this. |
Yeah, this works perfectly, and I had missed it from the docs. I would suggest creating a function for it to make it easier to find, eg. |
My current plan is a |
I've implemented I'll make a new release in a few days. |
Looks promising! One related issue is when delegating arguments to a sub-parser, I wonder if you have an ideas: while let Some(arg) = parser.next()? {
match arg {
Long("foo") => {
foo = true;
}
_ => {
// Here we basically want to delegate the rest of the arguments to be parsed to another parser.
// This doesn't quite work because we've already consumed `arg`!
let remaining = parser.raw_args();
let result = sub_parser.parse_args(remaining)?;
sub_options = result;
break;
}
}
} |
If Value(arg) => {
let remaining = std::iter::once(arg).chain(parser.raw_args()?);
... It gets trickier if it doesn't have to be a
I'm going to think about this. I have a few halfbaked ideas. Do you actually need the |
Yes this is exactly the stuff I've been running into. I don't think I can match on The workaround I've been using so far is this: let mut unparsed: Vec<OsString> = Vec::new();
while let Some(arg) = parser.next()? {
match arg {
...
_ => unparsed.push(args::format(arg))
}
}
let sub = lexopt::Parser::from_args(unparsed);
... And the implementation of pub fn format(arg: lexopt::Arg) -> OsString {
match arg {
lexopt::Arg::Long(flag) => format!("--{}", flag).into(),
lexopt::Arg::Short(flag) => format!("-{}", flag).into(),
lexopt::Arg::Value(val) => val,
}
} It would already be helpful to have a |
One option could be to have a way to put back an arg into the parser, eg. arg => {
parser.input(arg); // "un-parse" arg
sub_parser.parse(parser.raw_args());
} Another interesting function might be EDIT: Just saw this comment:
This is something I'm not handling indeed, so it would break at the moment, though I don't use those types of options. |
Does the subparser use lexopt too? Do I understand correctly that the arguments can be interleaved, so that e.g. for the command line A solution I have been pondering is a That would be somewhat similar to your Another solution could be to turn your logic inside-out. In the outermost parser, define a closure like this: let mut foo = false;
let handle = |arg| -> bool {
match arg {
Long("foo") => foo = true,
_ => return false,
}
true
}; Then pass while let Some(arg) = parser.next()? {
match arg {
arg if handle(&arg) => (),
... (The closure indicates that it "consumed" I'm not sure if that's worthwhile as-is. It's kind of complicated and doesn't let you call |
Yes
That's right, at least I support that with my current workaround in some cases. Will think about some of the ideas you proposed, thanks! I've gone through many options parsers over the last few years: |
After some more exploration, I've found that the method above with some tweaks is the cleanest and most flexible. Basically, I have a trait like this, implemented by all parsers: pub trait Args: Sized {
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)>;
} Then when parsing, I do something like this: impl Args for MainOptions {
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
let (sub_opts1, unparsed) = SubOptions1::from_args(args)?;
let (sub_opts2, unparsed) = SubOptions2::from_args(unparsed)?;
let (sub_opts3, unparsed) = SubOptions3::from_args(unparsed)?;
// etc ...
// Now the main option parser working with the remaining options.
let mut parser = lexopt::Parser::from_args(unparsed);
while let Some(arg) = parser.next()? { ... }
// ... The sub-parsers build an unparsed list from ignored args with |
Ah, that's a nice way to think about it! I don't think I'll ever support that properly, because to parse arguments the correct/pedantic way you have to do a single pass with all the information available. An argument But I think you said none of your options take arguments, so you don't suffer from this problem. I've had an Would something like this work? use std::ffi::OsString;
use lexopt::Arg;
enum OwnedArg {
Value(OsString),
Short(char),
Long(String),
}
impl From<Arg<'_>> for OwnedArg {
fn from(arg: Arg<'_>) -> OwnedArg {
match arg {
Arg::Value(value) => OwnedArg::Value(value),
Arg::Short(opt) => OwnedArg::Short(opt),
Arg::Long(opt) => OwnedArg::Long(opt.to_owned()),
}
}
}
fn main() -> anyhow::Result<()> {
let mut args = Vec::new();
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
args.push(OwnedArg::from(arg));
}
let (parsed, _) = MainOptions::from_args(args)?;
Ok(())
}
|
Yes, I think that would work!
|
I'd probably also implement |
What I was getting at is that you only need to use a let mut parser = lexopt::Parser::from_args(args);
while let Some(arg) = parser.next()? {
match arg { Can't you just do this? for arg in args {
match arg { If you're only doing options and positional arguments (and no options with values) then that seems like it would be enough. A fundamental problem with processing multiple times is that you lose the meaning of Running the arguments through a |
Ah I see, but how about optional values and stuff? Eg.
Interesting.. This could be solved if EDIT: Actually doesn't getting a Value(a) => {
unparsed.push("--");
unparsed.push(a);
unparsed.extend(parser.raw_args());
break;
} Or simply split the unparsed flags from the unparsed values.. |
Yep, that's a limitation. I thought you didn't use options like those, but I guess you only meant you didn't spell them
That's how POSIX does it, but most modern parsers (including lexopt) don't do it like that. You can put flags after positional arguments if you want. If you do want the POSIX behavior I wrote up an example here. But I don't think that works for your use case, because the If you want to be able to do That, or ditching the subparsers and combining it all into one big match. Would that be possible/practical? |
Can you remind me what the issue is with The reason I have sub-parsers is simply that some flag sets are shared across tools, and I don't want to have to duplicate the matches and parsing. So it's a question of keeping things in one place. |
The main issue I meant is that the Spellings like (You could decide that these shortcomings are acceptable.)
Ah, I understand now! I don't have a good solution yet but I'll keep thinking about it. |
Ok interesting, these are ok trade-offs for me at the moment, but obviously a more robust solution would be interesting. |
Feel free to open another issue for subparsers. |
To implement external sub-commands, eg.
git foo --bar --baz
, I need to invoke a binary eg.git-foo
in this case with arguments--bar --baz
.I can't figure out though, how to get the remaining arguments as plain strings, and not as
Long/Short/Value
. Any ideas how I might achieve that?The text was updated successfully, but these errors were encountered: