yuck
yuck copied to clipboard
Nested subcommands
Is it possible to have subcommands within subcommands?
I'd like the ability to do something like remote channel change 5
, or remote channel read
, where channel
is the main subcommand and change
and read
are subcommands that are part of the channel
subcommand.
I've attempted to do it with a yuck file like this:
Usage: remote
Interact with a television.
Usage: remote power on
Power on the television
Usage: remote power off
Power off the television
Usage: remote channel change [CHANNEL]...
Change channel to CHANNEL
Usage: remote channel read
Read current channel
Which generates the following:
$ ./remote --help
Usage: remote [OPTION]... COMMAND
Interact with a television.
COMMAND may be one of:
power Power off the television
channel Read current channel
Options accepted by all commands:
-h, --help display this help and exit
-V, --version output version information and exit
$ ./remote power --help
Usage: remote power off
Power off the television
Common options:
-h, --help display this help and exit
-V, --version output version information and exit
$ ./remote channel --help
Usage: remote channel read
Read current channel
Common options:
-h, --help display this help and exit
-V, --version output version information and exit
As you can see, it seems to only register the last sub-subcommand (e.g., power off
and channel read
) in the yuck file.
Is this functionality already present and I'm doing it incorrectly? If it isn't present, would the current architecture allow for it to be easily added? I would be happy to contribute.
Hey Austin, unfortunately this isn't possible in yuck as it stands. The way it registers the last sub-subcommand is a leaky abstraction over m4 which happens to allow to redefine macros.
As for a possible implementation: yuck lives on a union of common options and structs of subcommands. Adding another nesting this should read a union of unions of structs of sub-subcommands, structs of sub-commands and common options.
I was thinking of providing means to nest different yuck files somehow and in a meaningful way. For instance in the above case you'd have 3 yuck files, remote.yuck
, power yuck
and channel.yuck
each specifying just their immediate subcommands. Then in the C file with main()
you'd have something like
#include "remote.yucc"
#include "power.yucc"
#include "channel.yucc"
int main(int argc, char *argv[])
{
yuck_t remote[1];
yuck_parse(remote, argc, argv);
switch (remote->cmd) {
power_yuck_t power[1];
case REMOTE_CMD_POWER:
yuck_parse(power, argc, argv);
...
}
...
}
I imagine yuck gen
would be called with a --nest
or something to prefix the types with its own name to avoid symbol name clashes.
However, in either case it's currently unclear to me what to do about common options, the term unfolds into global common options and subcommand common options. In your example there's only nesting depth 0 and nesting depth 2 commands, but what if there was a
Usage: remote channel
Display current channel name.
Its options would/should be treated as common options to all remote channel
invocations. Actually that's more of a question than a statement.
Now, for a completely different idea: Observe how yuck gendsl
on your original file actually does see all subsubcommands. Instead of changing the current m4 script (which generates the C output from that), a second m4 script tailored to your use case could be implemented and you simply do a yuck gen --script=MY.m4 remote.yuck
. (Actually using multiple m4 scripts was the plan anyway to support different languages and stuff, I just happen to not use different languages a lot.)
Anyway, tell me what you think.
The tailored m4 script sounds like the best idea for now, or I may keep doing it how I have been which is remote power_on
, remote power_off
, remote channel_change
, etc.
I'd like to contribute to the project for use by others but I don't have any spare time in the foreseeable future.