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

Please support flattening an Args and mutating arguments before merging #5050

Open
2 tasks done
joshtriplett opened this issue Jul 29, 2023 · 5 comments
Open
2 tasks done
Labels
C-enhancement Category: Raise on the bar on expectations

Comments

@joshtriplett
Copy link
Contributor

Please complete the following tasks

Clap Version

4.3.19

Describe your use case

I have a group of four arguments that repeat three different times with different names and descriptions. Right now, I have to manually duplicate the arguments with different names and descriptions, and then I manually transform each group of four back into a struct. I'd like to have a struct deriving Args that contains those four arguments, and flatten three copies of it into a parent struct, calling mut_arg on the child along the way.

Right now, this doesn't work because the arguments all get inserted first, and will conflict, before mut_arg runs.

Also, even if this did work, it'd require calling mut_arg four times, once for each child argument.

Describe the solution you'd like

Based on discussion with @epage: could we implement the combination of flatten and mut_arg in a different way, by first turning the child struct into a full standalone Command, then running the supplied mut_arg on that Command, and then merge the Command into the parent Command?

In addition, could we have a mut_args that runs on all arguments of the child struct? That'd make it easy to systematically modify id, long, and help, for every argument.

Alternatives, if applicable

No response

Additional Context

No response

@epage
Copy link
Member

epage commented Aug 1, 2023

The overall need for flattening was brought up in #3117 but the design was deemed too complicated and rejected.

The thing we overlooked in our discussion over zulip which is brought up in #3117 is that we need unique Arg::id and changing ti behind clap_derive's back will break the FromArgMatches impls because they will be using the original name.

There are other ways that this design could be useful, like with #3221 and #3513.

In #3513, I was concerned about not being able to uniquify the Arg::id and how that might frustrate people more than not doing the feature:

I think this gives me a way to express a thought I was having that I couldn't put into words before. Maintainership requires managing expectations. I worry that implementing this, though it helps some people, will make it harder for us to manage expectations for reusable structs and adapting this combination of features to people's needs.

@joshtriplett
Copy link
Contributor Author

Interesting. When flattening a struct into a parent struct, does the child's impl still need to be used, or could the parent become responsible for filling in the fields merged from the child using its own IDs?

I'm still interested in a design that would allow for flattening multiple copies of the same struct into a parent while using different argument names, IDs, help text, and potentially other changes. I'm not at all attached to any particular implementation path for doing so, though.

@epage
Copy link
Member

epage commented Aug 1, 2023

Interesting. When flattening a struct into a parent struct, does the child's impl still need to be used, or could the parent become responsible for filling in the fields merged from the child using its own IDs?

The parent would need to know the field IDs, their clap type, how to consstruct the struct, etc.

I'm not at all attached to any particular implementation path for doing so, though.

So far I've not been able to come up with a feasible design that can deal with how little each side knows about the other without geting into more complex (and runtime vs compile time) trait interactions.

@joshtriplett
Copy link
Contributor Author

Would it help if flattenable structs had to do a different derive, like derive(ArgsTemplate), and/or a different attribute than flatten?

Would it help if flattenable structs had a const-generic template parameter that augments the ID? (That has usability issues, but it seems better than nothing.)

Would it help if the combination of flatten and modification just always resulted in doing some things at runtime rather than compile time? At least for my use cases, a tiny bit of runtime overhead when parsing arguments would not be a problem.

The parent would need to know the field IDs, their clap type, how to consstruct the struct, etc.

Right. And it can easily get all that at runtime, but not at compile time from a separate derive...

@epage
Copy link
Member

epage commented Aug 1, 2023

Would it help if flattenable structs had to do a different derive, like derive(ArgsTemplate), and/or a different attribute than flatten?

If you mean a different derive + trait, then that is just another way of expressing the same idea of "pass information at runtime". Personally, extra trait and derive doesn't seem like the ideal way because that makes the user experience worse

Would it help if flattenable structs had a const-generic template parameter that augments the ID? (That has usability issues, but it seems better than nothing.)

The derive would need to recognize that it should use this, on both sides. Most likely an attribute. We need to add attributes more related to generics (e.g. wanting to make parsing generic over the help system), just haven't decided on how to express all of that.

Would it help if the combination of flatten and modification just always resulted in doing some things at runtime rather than compile time? At least for my use cases, a tiny bit of runtime overhead when parsing arguments would not be a problem.

My more general concern is that there are a lot of these one off cases for more runtime communication between derives and being hesitant in drawing the line for where it is acceptable to do so and where to say no, so I've just always said no.

Without the extra trait mentioned above, we also have to be careful of compatibility, not just of trait definition changes but codegen changes for interacting with by-hand impls of the traits.

More specifically, whether we get the information from a generic parameter or from a runtime parameter, we need to do string joins to pass into clap which requires the string feature flag. Making a derive feature like this dependent on a feature like that feels like it'd be confusing. I also have a lot of users wanting further push down on build times and binary sizes, so I have to balance the different interests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: Raise on the bar on expectations
Projects
None yet
Development

No branches or pull requests

2 participants