devnote-dev / devnote-dev/cling

Broken option parsing in MainCommand when adding SubCommands

Đang mở
#21 1 bình luận 0 reaction 0 người được giao Xem trên GitHub
bug
Ngôn ngữ chính
Crystal
Star
33
Fork
3
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

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

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
crystal
Lĩnh vực
cli
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
48/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.