-
Notifications
You must be signed in to change notification settings - Fork 3
Description
First off, I've just started using this shard due to the stdlib OptionParser lacking some considerable customization options and flexibility. Thanks for putting this together.
However, I have run into an issue when adding subcommands. This could totally be just me "holding it wrong", but I was unable to figure out how to do it properly.
Use-Case
Share common options with SubCommands
This is most useful for things like a config file. I'd like to specify this :single type option only in the MainCommand and have all SubCommands simply inherit it.
Allow inherting global flag options
It would very beneficial to have flag options like -v/--verbose or -d/--debug in the global scope of the MainCommand to be inheritable by SubCommands.
Expected Behaviour
Options defined in MainCommand should always be parseable and usable, regardless of whether any SubCommands are added.
Example
Options defined in MainCommand specified in front of the SubCommand.
# my_cli_tool [options] <subcommand>
Options:
-c, --config Config file to use (default: /my/default/path/config.yml)
Actual Behavior
Subcommands with @inherit_options=false
Any non-flag options in MainCommand result in a NilAssertionError at runtime, e.g.:
# my_cli_tool -c config.yml
Unhandled exception: Error while executing command error handler:
Cling::Parser::Result#value cannot be nil (Cling::ExecutionError)
from lib/cling/src/cling/executor.cr:51:9 in 'handle'
from lib/cling/src/cling/command.cr:172:5 in 'execute'
from broken_parsing.cr:41:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:129:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:115:7 in 'main'
from /usr/lib/crystal/crystal/system/unix/main.cr:9:3 in 'main'
from /usr/lib/libc.so.6 in '??'
from /usr/lib/libc.so.6 in '__libc_start_main'
from ./broken_parsing in '_start'
from ???
Caused by: Cling::Parser::Result#value cannot be nil (NilAssertionError)
from lib/cling/src/cling/parser.cr:35:7 in 'value'
from lib/cling/src/cling/executor.cr:46:55 in 'handle'
from lib/cling/src/cling/command.cr:172:5 in 'execute'
from broken_parsing.cr:41:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:129:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:115:7 in 'main'
from /usr/lib/crystal/crystal/system/unix/main.cr:9:3 in 'main'
from /usr/lib/libc.so.6 in '??'
from /usr/lib/libc.so.6 in '__libc_start_main'
from ./broken_parsing in '_start'
from ???
Subcommands with @inherit_options=true
Now the inherited non-flag options can only be used after the SubCommand, e.g.:
# my_cli_tool <subcommand> -c config.yml foobar
Which also renders them completely unusable outside of a SubCommand context.
Trying to specify them like # my_cli_tool -c config.yml <subcommand> foobar results in the same NilAssertionError.
Minimal Example for Reproduction
require "cling"
class SubCommand < Cling::Command
def setup : Nil
@name = "subcommand"
@inherit_options = true
add_argument "name", description: "some name", required: true
end
def run(arguments : Cling::Arguments, options : Cling::Options) : Nil
puts "subcommand run ; name = #{arguments.get("name")}"
end
end
class MainCommand < Cling::Command
def setup : Nil
@summary = @description = "some description"
@name = "foobar"
add_option 'h', "help", description: "Show usage details"
add_option 'c', "config",
description: "Config file to use",
type: :single,
default: "/my/default/path/config.yml"
add_command SubCommand.new
end
def pre_run(arguments : Cling::Arguments, options : Cling::Options) : Nil
if options.has? "help"
puts help_template
exit_program 0
end
end
def run(arguments : Cling::Arguments, options : Cling::Options) : Nil
puts "main run ; config = #{options.get("config")}"
end
end
cli = MainCommand.new
cli.execute ARGV