-
Notifications
You must be signed in to change notification settings - Fork 0
Iterating over ArgMatches
#90
Comments
Sorry for the wait, I've been on vacation with my kids this past week :) Do you mean iterating over only positional values, or over all args that have been used? Also, do you envision this iterating over non-matched args as well? If you'd like to take a stab at implementing I'd be happy to mentor you! This is how I'd attack it:
For the implementation once we have a good design I can give you some pointers as to where the changes you'll want to make are located / any particulars associated with those changes. Once all your new tests pass, I'd run a full test suite If you need to see the debug output while you're building testing a single function use |
First of all, thanks for taking the time to address this! I hope you enjoyed your vacation. The API I had in mind wouldn't be constrained only to positional arguments, but to all the arguments that have been used. Currently I mean adding an API to access the order in which options are given. Ex.:
Let's say my command's behavior is specific to the order in which the options
This example is just off the top of my head and I am sure it could be improved. Please correct me if I talked nonsense at any point because I am not very familiar with the API. |
To make sure it's not an The only other form I've seen is for things like include/exclude logic where there aren't really hard conflicts/overrides at the parsing level, but further up the execution stack there are. I've only seen this between a handlful of args at the most, and hence iterating over the indices isn't an issue. This is of course not to say my list is exhaustive, I'm sure there are other use cases out there! This actually wouldn't be a huge change for clap to implement this, but it would introduce an additional dep (such as The change you'd make is using For the iterator itself, it'd fine to use the |
Another more lengthy option (but arguably more correct) is instead of returning an Iterator of some custom data type tuple, returning the |
My application has a custom
However there are many other use cases. Tasks execution order in a Gulp-like application, apply filters in a specific order in GNU find, ecc. |
That's a pity. I thought it would be as simple as adding a method to |
Right now, clap uses a So it basically would be just a cfg statement which adds the method on the matches struct and uses an indexmap instead of hashmap. And I'd create a struct to act as the iterator, and impl Iterator for it. Something like (details left out for brevity): #[cfg(not(feature = "indexmap"))]
struct ArgMatches {
args: HashMap
}
#[cfg(feature = "indexmap")]
struct ArgMatches {
args: IndexMap
}
impl ArgMatches {
#[cfg(feature = "indexmap")]
fn iter(&self) -> MatchIter {
MatchIter { iter: self.args.iter() }
}
}
#[cfg(feature = "indexmap")]
struct MatchIter {
iter: // whatever IndexMap::iter returns
}
#[cfg(feature = "indexmap")]
impl Iterator for MatchIter {
type Item = // whatever args.iter().next() returns
fn next(&mut self) -> Self::Item {
self.iter().next()
}
} So it's a pretty straight forward change. |
Thanks for the tips, I'll start working on a prototype then. 👍 |
I feel hesitant adding an |
You can take a look at the prototype here. There is some more work to do, specifically:
Please let me know how you like the API and if you want any changes. Congrats on the codebase BTW, I know this is a small change but still, it is a pleasure to dive in. |
My current policy is public, but undocumented internals are effectively private and should not be relied on. Sure, I can't stop anyone from using them directly, but there's the caveat that if I change them without warning it's considered a breaking change. Hence the Most of the time public but undocumented is because it needs to be public because other mods (or eventually crates) need to touch it, but those mods (or crates) are maintained by myself or other clap members and thus it's essentially just a private API. |
Looks like a good start! It's a small nit, but let's change the feature name to Let me know if you have any other questions 👍 |
I'll create a PR for discussing further details, sounds good? |
Yep, I'd actually prefer it that way because it's easier to comment and review 😄 👍 |
I also need this functionality |
Hi all, I'm also looking to use something like this. I'd like to process my args in a functional fashion. The PR, however, seems to have died a death. Given that 3.0 is on the horizon how much of a bad idea is it for me to just use PS, I don't need ordering. |
@bradwood Not as bad as you'd think: 2.x branch is frozen. I'd be really surprised if we change something regarding publicity of those fields because there is plenty of code out there relying on those, ugh. So, if you aren't planning on switching to 3.0 and you're OK with the fact that every time you use private API god kills a kitten, you can do that, yes. Beware: those will be truly private in 3.0. I suspect it won't make it into 3.0. The main question is: what are you going to iterate through? You can't have |
i am trying to call And, as regards the kittens, I'm cool with that! :) |
clap has |
I think I can see a certain use case for this where people want to do dependency injection pattern for evaluating their args. |
For a while I was floating around the idea of adding filter/map capabilities to arguments themselves, in a similar manner to the current But others also wanted more a "handler" type functionality (which is something some python libraries offer). This can be done already by (hackily) using the validator functionality. Currently, the mapping of values can be done pretty easily after the parsing in user code manually, but not during parsing. I'm not 100% sure where I fall on the thinking of this one though, as is the added complexity and public API space worth the small benefit of doing something at parse time instead of manually afterwards? Also perhaps more importantly, have users thought through what happens if a particular map/filter/handler whatever we'd call it has side affects, but parsing ultimately fails? Maybe that's just a, "hey probably best you don't have side affects, but if you do be aware it can have XYZ consequences." I mean you can already (mis)use the validators for handlers, so I'd be less inclined to include that functionality. |
Resolved by derive (and partially by I'm not sure I get your "handlers" idea, I imagine it's something akin clap-rs/clap#1185:
Some thoughts:
In nutshell: I think this kind of tool is too vague and too powerful for a command line parsing library to provide. I don't think clap should be trying to accommodate every single CLI in existence, the number of well-supported designs is quite enough already. |
FWIW, I'm not needing these |
@bradwood Out of morbid curiosity, what is the core problem you're trying to solve? Why do you think I suspect you're just "doing it wrong". |
Exactly. I'm describing a problem/issue that existed prior to derive macros. The "handlers" was just a common theme that popped up every so often, especially when people were moving from Python based CLIs which offer a "when this argument is used, run this function." Although that's a totally different way to build/design a CLI and thus I don't think it's really applicable to how clap runs.
100% agree. I'm just providing some historic context 😉 @bradwood I agree with @CreepySkeleton it sounds like you may be approaching the problem from a particular design angle and we may be seeing it as an XY problem. Some of the principal issues with a feature mapping over arguments (and their values) is:
Those final three bullets are some of what making this feature a general clap feature difficult. If you don't have any of those requirements and are truly only wanting blind side affects, I'd question if there may be a better way to represent the CLI. If you can give some examples of what you're trying to do, we may be able to either point you in a direction or give you a way to accomplish that task with existing features. |
Heh. Given that I'm only 3 weeks in and coming from Python, I'm almost certainly doing it wrong! 😄 Here's the situation:
Instead, more as a academic exercise than anything else, I'm trying to figure out if there's a more elegant way of doing in in a functional style... something like: // NOTE: args.args is a public, but _undocumented_ field in clap.rs
args.args.iter()
.map(
|(k,v)| println!("{} {:?}",*k ,v) //replace this with a mechanism that I can use to call the appropriate builder method
).count(); // I'm using this to drive the function pipeline, purely for the side effects, rather than for the resulting value (I accept that this is probably not idiomatic) What the aforementioned mechanism is, I'm not quite sure (yet)... but I was hoping to figure out a way to look up the other API's builder method that I need to call using the name of the clap argument passed... I wait with bated breath for you to give me some beautiful idiomatic pattern that'll make this into anything other than 30 if statements! Thanks! |
Yours is not a super common case. Essentially, your CLI is a facade for a builder API? A few things about what you mention stood out to me (emphasis mine):
Unless you have a way of mapping those non 1:1 arguments, you'll end up with errors. So dealing with those errors is the fallible requirement I mentioned above.
Although it's not the 2 line map you're hoping for, the most idiomatic way to deal with this type of situation is via the clap derive macros. Your CLI would evaluate to some type, lets call it |
Although it's a more advanced topic, and probably defeats the purpose of what you're trying to do, you could combine your own Custom Derive macro, and do something like |
I'm still reeling from everything you just wrote below the horizontal rule 2 comments above... 🤔. I'm going to have to do a whole lot of research on this, which is no bad thing... Thanks for your help (and patience) here! As a final question, if I may, where would I look for examples of Clap and derive macros. Is this documented somewhere? I'm digging through the docs but not finding much on this topic (but I might not be looking in the right place). |
The clap derive macros are the new big feature of clap 3.x which is still in beta and thus not formally released yet. So the documentation is still lacking. The source is under the If you'd prefer, you can use For info on writing Custom Derive macros:
|
Friday Mar 09, 2018 at 12:04 GMT
Originally opened as clap-rs/clap#1206
Hi, I am using Clap in a little side project and I would like to suggest a minor feature. I am ready to implement it myself and send a PR if needed, I just need a few pointers. The feature at hand is an ordered iterator over
ArgMatches
: as far as my understanding goes, Clap doesn't allow to freely iterate over the parsed arguments. What if my application APIs are dependent upon the order of the arguments? GNUfind
comes to my mind. Callingindex_of
for every argument can be expensive if you have a lot of them.If this feature were to be implemented, what would the return type and name of the method be?
The text was updated successfully, but these errors were encountered: