devnote-dev / devnote-dev/cling
Broken option parsing in MainCommand when adding SubCommands
- Langage dominant
- Crystal
- Étoiles
- 33
- Forks
- 3
- Métriques de merge des PR
- Aucune PR mergée en 30 j
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]
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 -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 foobar` results in the same `NilAssertionError`.
## Minimal Example for Reproduction
```crystal
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
```
Guide de contribution
Aucun guide de contribution indexé pour ce dépôt
Piste de recherche
Start with the minimal Crystal reproduction and inspect lib/cling/src/cling/parser.cr, lib/cling/src/cling/executor.cr, and lib/cling/src/cling/command.cr. Run the shown commands with @inherit_options set to false and true, then verify that MainCommand options parse before subcommands without a NilAssertionError and remain usable by the command.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- crystal
- Domaine
- cli
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- À l'abandon
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 48/100