devnote-dev / devnote-dev/cling

Broken option parsing in MainCommand when adding SubCommands

未关闭
#21 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
bug
主要语言
Crystal
星标
33
派生
3
PR 合并指标
30 天内没有已合并 PR

描述

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
```

贡献指南

这个仓库没有索引到贡献指南

调研方向

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.

由索引模型根据 Issue 内容生成。

评估

技术栈
crystal
领域
cli
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
描述清楚
新手友好度
48/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。