Skip to content

parseAllAsRoot() for list of parsed commands #797

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

cmcgee1024
Copy link

@cmcgee1024 cmcgee1024 commented Jul 22, 2025

Description

It can be useful to get the list of parseable commands from the root
to the last subcommand in order to trace the path taken through the
command decision tree, and access parent command states from a
subcommand.

Here is an example of two paths through a decision tree encoded
as swift argument parser commands, subcommands and options.

mycmd decision1 --opt1=abc decision2 --opt2=def
mycmd decision1 --opt1=abc decision2A --opt3=ghi

In both leaf commands (decision2 and decision2A) they want to
know two things. First, is the fact that decision1 was chosen. It's
possible that subcommands can be reused at different points in a
decision tree. That choice can affect the behaviour of the
subcommand when it executes.

Second, the subcommands might further alter their behaviour
in the presence of flags/options, such as opt1 in this example.

Option groups are the usual method to propagate options down
to subcommands, but they have some limitations. The options
become present in the subcommands, and often visible with the
online help. This causes bloat in the subcommands if they need
to be aware of parent options. Also, they only apply to options
(or flags). There is no way to identify the parent commands that
were chosen.

Detailed Design

Provide a new parseAllAsRoot() function that operates much in the
same way as parseAsRoot(), except it returns the list of parsed
commands. With such a list, a client can either store the list in a
shared location, or connect the commands together with parent
and child relationships with a custom command protocol.

let cmds = Foo.parseAllAsRoot(CommandLine.arguments)
var last = cmds.first
for cmd in cmd.dropFirst() {
    if let nestedCmd = (cmd as? NestedCommand) {
        nestedCmd.parent = last
    }
    last = cmd
}
guard let last else {
    fatalError("Nothing to run")
}
try last.run()

Documentation Plan

The documentation has been updated with a documentation string for the new
parseAllAsRoot() function on ParseableCommand.

Test Plan

The code paths are very similar to the existing parseAsRoot(), except that the
parsed commands list is preserved in this new function instead of being thrown
away. Existing tests will continue to verify the shared code paths. In order to
verify that the command list is formed as expected, an existing E2E test case
has been enhanced to verify the correctness of the whole list.

Source Impact

There should be no impact to existing client.

Checklist

  • I've added at least one test that validates that my change is working, if appropriate
  • I've followed the code style of the rest of the project
  • I've read the Contribution Guidelines
  • I've updated the documentation if necessary

@cmcgee1024 cmcgee1024 requested a review from rauhul July 22, 2025 15:49
@rauhul rauhul requested a review from natecook1000 July 22, 2025 15:50
@rauhul
Copy link
Contributor

rauhul commented Jul 22, 2025

Could you explain an example usecase?

@cmcgee1024
Copy link
Author

cmcgee1024 commented Jul 22, 2025

Could you explain an example usecase?

Sure, for example a child command might walk up the path in the tree and adjust its behaviour based on decisions made through the choice of the parent commands. Those decisions might also be further qualified with specific flags and options. Cross-cutting concerns can be marked by having certain commands conform to client-specific protocols.

Alternatively, a child command might invoke certain methods in their parent so that they don't need to implement them themselves, such as creating certain common output files. This allows the child to decouple to a degree from the outputs generated by the parent.

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

Successfully merging this pull request may close these issues.

2 participants