You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Working on a tool with a handful of subcommands, I find myself wanting to handle common work in the root command, and then pass some state to the subcommand. I’m not sure what might be possible, but something like this is what I’d like to be able to do:
% raise3d --addr 192.168.1.23 monitor --notify
@mainstructRootCommand:AsyncParsableCommand<Raise3DAPI>{staticvarconfiguration=CommandConfiguration(commandName:"raise3d",
abstract:"A utility to interact witih Raise3D printers.",
subcommands:[Monitor.self])@Option(help:"The printer’s local address.")varaddr:String@Option(help:"The printer’s password.")varpassword:String?mutatingfunc run()asyncthrows->Raise3DAPI{letpassword=self.password ??{/* prompt user for password */}()letapi=Raise3DAPI(host:self.options.addr, password:self.options.password)tryawait api.login()return api
}}structMonitor:AsyncParsableCommand<Raise3DAPI>{staticvarconfiguration=CommandConfiguration(commandName:"monitor",
abstract:"Monitor the printer and optionally notify of errors.")@Option(help:"Notify if error.")varnotify:Bool= false
mutatingfunc run(state inAPI:Raise3DAPI)asyncthrows{
while (true){letjobInfo=tryawait inAPI.getJobInfo()
if self.notify && jobInfo.status ==.error {// Send notification}Task.sleep(for:.seconds(1))}}}classRaise3DAPI{// … }
This lets subcommands be a little cleaner, because they don't have to redundantly declare the global options, and all the common work is done in the parent command. The desired state is generic.
The text was updated successfully, but these errors were encountered:
If you nest Monitor inside RootCommand, then you can add a property annotated with @OptionGroup in the former to access the options of the latter:
@mainstructRootCommand:AsyncParsableCommand<Raise3DAPI>{...structMonitor:AsyncParsableCommand<Raise3DAPI>{...@OptionGroupvarroot:RootCommandmutatingfunc run()asyncthrows// Access `root.addr` and `root.password`...}}
But you can't just make run in RootCommand return a value that you inject into the subcommand: Only one run command is invoked and that's the one of the subcommand.
Maybe you can make Raise3DAPI conform to ParsableArguments such that you can inject that value directly as an @OptionGroup (docs).
A similar problem I'm having, although not related to passing state, is that if my Root command is a container for subcommands, I don't have a chance to execute any code at startup. For example this doesn't let me setup a logging system since the only code that runs is from the subcommand.
I can revert to using main.swift and manually running the main method on the command (although you need to dance a bit with async context in order to call the proper overload), but it would be nice if there was a way to run setup code and then let the specific subcommand run. There maybe some way but I haven't found it 🤔
Working on a tool with a handful of subcommands, I find myself wanting to handle common work in the root command, and then pass some state to the subcommand. I’m not sure what might be possible, but something like this is what I’d like to be able to do:
This lets subcommands be a little cleaner, because they don't have to redundantly declare the global options, and all the common work is done in the parent command. The desired state is generic.
The text was updated successfully, but these errors were encountered: