Add support for plugins.
This commit is contained in:
@@ -4,34 +4,36 @@
|
|||||||
# Kong is a command-line parser for Go
|
# Kong is a command-line parser for Go
|
||||||
[](http://godoc.org/github.com/alecthomas/kong) [](https://circleci.com/gh/alecthomas/kong) [](https://goreportcard.com/report/github.com/alecthomas/kong) [](https://gophers.slack.com/messages/CN9DS8YF3)
|
[](http://godoc.org/github.com/alecthomas/kong) [](https://circleci.com/gh/alecthomas/kong) [](https://goreportcard.com/report/github.com/alecthomas/kong) [](https://gophers.slack.com/messages/CN9DS8YF3)
|
||||||
|
|
||||||
[TOC levels=2-3 numbered]: # "#### Table of Contents"
|
<!-- TOC depthFrom:2 updateOnSave:true withLinks:true -->
|
||||||
|
|
||||||
#### Table of Contents
|
- [Introduction](#introduction)
|
||||||
1. [Introduction](#introduction)
|
- [Help](#help)
|
||||||
1. [Help](#help)
|
- [Command handling](#command-handling)
|
||||||
1. [Command handling](#command-handling)
|
- [Switch on the command string](#switch-on-the-command-string)
|
||||||
1. [Switch on the command string](#switch-on-the-command-string)
|
- [Attach a `Run(...) error` method to each command](#attach-a-run-error-method-to-each-command)
|
||||||
1. [Attach a `Run(...) error` method to each command](#attach-a-run-error-method-to-each-command)
|
- [Hooks: BeforeResolve(), BeforeApply(), AfterApply() and the Bind() option](#hooks-beforeresolve-beforeapply-afterapply-and-the-bind-option)
|
||||||
1. [Hooks: BeforeResolve(), BeforeApply(), AfterApply() and the Bind() option](#hooks-beforeresolve-beforeapply-afterapply-and-the-bind-option)
|
- [Flags](#flags)
|
||||||
1. [Flags](#flags)
|
- [Commands and sub-commands](#commands-and-sub-commands)
|
||||||
1. [Commands and sub-commands](#commands-and-sub-commands)
|
- [Branching positional arguments](#branching-positional-arguments)
|
||||||
1. [Branching positional arguments](#branching-positional-arguments)
|
- [Terminating positional arguments](#terminating-positional-arguments)
|
||||||
1. [Terminating positional arguments](#terminating-positional-arguments)
|
- [Slices](#slices)
|
||||||
1. [Slices](#slices)
|
- [Maps](#maps)
|
||||||
1. [Maps](#maps)
|
- [Custom named decoders](#custom-named-decoders)
|
||||||
1. [Custom named decoders](#custom-named-decoders)
|
- [Supported field types](#supported-field-types)
|
||||||
1. [Custom decoders (mappers)](#custom-decoders-mappers)
|
- [Custom decoders (mappers)](#custom-decoders-mappers)
|
||||||
1. [Supported tags](#supported-tags)
|
- [Supported tags](#supported-tags)
|
||||||
1. [Variable interpolation](#variable-interpolation)
|
- [Plugins](#plugins)
|
||||||
1. [Modifying Kong's behaviour](#modifying-kongs-behaviour)
|
- [Variable interpolation](#variable-interpolation)
|
||||||
1. [`Name(help)` and `Description(help)` - set the application name description](#namehelp-and-descriptionhelp---set-the-application-name-description)
|
- [Modifying Kong's behaviour](#modifying-kongs-behaviour)
|
||||||
1. [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
|
- [`Name(help)` and `Description(help)` - set the application name description](#namehelp-and-descriptionhelp---set-the-application-name-description)
|
||||||
1. [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
|
- [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
|
||||||
1. [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
|
- [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
|
||||||
1. [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
|
- [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
|
||||||
1. [`Bind(...)` - bind values for callback hooks and Run() methods](#bind---bind-values-for-callback-hooks-and-run-methods)
|
- [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
|
||||||
1. [Other options](#other-options)
|
- [`Bind(...)` - bind values for callback hooks and Run() methods](#bind---bind-values-for-callback-hooks-and-run-methods)
|
||||||
|
- [Other options](#other-options)
|
||||||
|
|
||||||
|
<!-- /TOC -->
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
@@ -437,6 +439,26 @@ Tag | Description
|
|||||||
`embed` | If present, this field's children will be embedded in the parent. Useful for composition.
|
`embed` | If present, this field's children will be embedded in the parent. Useful for composition.
|
||||||
`-` | Ignore the field. Useful for adding non-CLI fields to a configuration struct.
|
`-` | Ignore the field. Useful for adding non-CLI fields to a configuration struct.
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
Kong CLI's can be extended by embedding the `kong.Plugin` type and populating it with pointers to Kong annotated structs. For example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var pluginOne struct {
|
||||||
|
PluginOneFlag string
|
||||||
|
}
|
||||||
|
var pluginTwo struct {
|
||||||
|
PluginTwoFlag string
|
||||||
|
}
|
||||||
|
var cli struct {
|
||||||
|
BaseFlag string
|
||||||
|
kong.Plugins
|
||||||
|
}
|
||||||
|
cli.Plugins = kong.Plugins{&pluginOne, &pluginTwo}
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally if an interface type is embedded, it can also be populated with a Kong annotated struct.
|
||||||
|
|
||||||
## Variable interpolation
|
## Variable interpolation
|
||||||
|
|
||||||
Kong supports limited variable interpolation into help strings, enum lists and
|
Kong supports limited variable interpolation into help strings, enum lists and
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Plugins are dynamically embedded command-line structures.
|
||||||
|
//
|
||||||
|
// Each element in the Plugins list *must* be a pointer to a structure.
|
||||||
|
type Plugins []interface{}
|
||||||
|
|
||||||
func build(k *Kong, ast interface{}) (app *Application, err error) {
|
func build(k *Kong, ast interface{}) (app *Application, err error) {
|
||||||
defer catch(&err)
|
defer catch(&err)
|
||||||
v := reflect.ValueOf(ast)
|
v := reflect.ValueOf(ast)
|
||||||
@@ -50,28 +55,34 @@ func flattenedFields(v reflect.Value) (out []flattenedField) {
|
|||||||
if tag.Ignored {
|
if tag.Ignored {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ft.Anonymous || tag.Embed {
|
if !ft.Anonymous && !tag.Embed {
|
||||||
if fv.Kind() == reflect.Interface {
|
if fv.CanSet() {
|
||||||
fv = fv.Elem()
|
out = append(out, flattenedField{field: ft, value: fv, tag: tag})
|
||||||
}
|
}
|
||||||
sub := flattenedFields(fv)
|
|
||||||
for _, subf := range sub {
|
|
||||||
// Assign parent if it's not already set.
|
|
||||||
if subf.tag.Group == "" {
|
|
||||||
subf.tag.Group = tag.Group
|
|
||||||
}
|
|
||||||
// Accumulate prefixes.
|
|
||||||
subf.tag.Prefix = tag.Prefix + subf.tag.Prefix
|
|
||||||
// Combine parent vars.
|
|
||||||
subf.tag.Vars = tag.Vars.CloneWith(subf.tag.Vars)
|
|
||||||
}
|
|
||||||
out = append(out, sub...)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !fv.CanSet() {
|
|
||||||
|
// Embedded type.
|
||||||
|
if fv.Kind() == reflect.Interface {
|
||||||
|
fv = fv.Elem()
|
||||||
|
} else if fv.Type() == reflect.TypeOf(Plugins{}) {
|
||||||
|
for i := 0; i < fv.Len(); i++ {
|
||||||
|
out = append(out, flattenedFields(fv.Index(i).Elem())...)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out = append(out, flattenedField{field: ft, value: fv, tag: tag})
|
sub := flattenedFields(fv)
|
||||||
|
for _, subf := range sub {
|
||||||
|
// Assign parent if it's not already set.
|
||||||
|
if subf.tag.Group == "" {
|
||||||
|
subf.tag.Group = tag.Group
|
||||||
|
}
|
||||||
|
// Accumulate prefixes.
|
||||||
|
subf.tag.Prefix = tag.Prefix + subf.tag.Prefix
|
||||||
|
// Combine parent vars.
|
||||||
|
subf.tag.Vars = tag.Vars.CloneWith(subf.tag.Vars)
|
||||||
|
}
|
||||||
|
out = append(out, sub...)
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -866,3 +866,24 @@ func TestLoneHpyhen(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "-", cli.Flag)
|
require.Equal(t, "-", cli.Flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlugins(t *testing.T) {
|
||||||
|
var pluginOne struct {
|
||||||
|
One string
|
||||||
|
}
|
||||||
|
var pluginTwo struct {
|
||||||
|
Two string
|
||||||
|
}
|
||||||
|
var cli struct {
|
||||||
|
Base string
|
||||||
|
kong.Plugins
|
||||||
|
}
|
||||||
|
cli.Plugins = kong.Plugins{&pluginOne, &pluginTwo}
|
||||||
|
|
||||||
|
p := mustNew(t, &cli)
|
||||||
|
_, err := p.Parse([]string{"--base=base", "--one=one", "--two=two"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "base", cli.Base)
|
||||||
|
require.Equal(t, "one", pluginOne.One)
|
||||||
|
require.Equal(t, "two", pluginTwo.Two)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user