Support limited variable interpolation.

Kong supports limited variable interpolation into help strings, enum lists and
default values.

Variables are in the form:

    ${<name>}

Variables are set with the `Vars(map[string]string)` option. Undefined
variable references in the grammar will result in an error at construction
time.
This commit is contained in:
Alec Thomas
2018-06-27 21:07:06 +10:00
parent 6408010083
commit 1bb0c0b4b2
9 changed files with 187 additions and 10 deletions
+59 -1
View File
@@ -51,6 +51,7 @@ type Kong struct {
help HelpPrinter
helpOptions HelpOptions
helpFlag *Flag
vars map[string]string
// Set temporarily by Options. These are applied after build().
postBuildOptions []Option
@@ -67,6 +68,7 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
before: map[reflect.Value]HookFunc{},
registry: NewRegistry().RegisterDefaults(),
resolvers: []ResolverFunc{Envars()},
vars: map[string]string{},
}
for _, option := range options {
@@ -88,15 +90,71 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
k.Model.HelpFlag = k.helpFlag
for _, option := range k.postBuildOptions {
if err := option(k); err != nil {
if err = option(k); err != nil {
return nil, err
}
}
k.postBuildOptions = nil
if err = k.interpolate(k.Model.Node); err != nil {
return nil, err
}
return k, nil
}
// Interpolate variables into model.
func (k *Kong) interpolate(node *Node) (err error) {
node.Help, err = interpolate(node.Help, k.vars)
if err != nil {
return fmt.Errorf("help for %s: %s", node.Path(), err)
}
for _, flag := range node.Flags {
if err = k.interpolateValue(flag.Value); err != nil {
return err
}
}
for _, pos := range node.Positional {
if err = k.interpolateValue(pos); err != nil {
return err
}
}
for _, child := range node.Children {
if err = k.interpolate(child); err != nil {
return err
}
}
return nil
}
func (k *Kong) interpolateValue(value *Value) (err error) {
if value.Default, err = interpolate(value.Default, k.vars); err != nil {
return fmt.Errorf("default value for %s: %s", value.Summary(), err)
}
if value.Enum, err = interpolate(value.Enum, k.vars); err != nil {
return fmt.Errorf("enum value for %s: %s", value.Summary(), err)
}
vars := mergeVars(k.vars, map[string]string{
"default": value.Default,
"enum": value.Enum,
})
if value.Help, err = interpolate(value.Help, vars); err != nil {
return fmt.Errorf("help for %s: %s", value.Summary(), err)
}
return nil
}
func mergeVars(base, extra map[string]string) map[string]string {
out := make(map[string]string, len(base)+len(extra))
for k, v := range base {
out[k] = v
}
for k, v := range extra {
out[k] = v
}
return out
}
// Provide additional builtin flags, if any.
func (k *Kong) extraFlags() []*Flag {
if k.noDefaultHelp {