2019-06-17 09:48:05 +00:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2019-08-03 01:58:23 +00:00
|
|
|
"github.com/containous/traefik/v2/pkg/types"
|
2019-06-17 09:48:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const defaultPtrValue = "false"
|
|
|
|
|
|
|
|
// FlatOpts holds options used when encoding to Flat.
|
|
|
|
type FlatOpts struct {
|
|
|
|
Case string // "lower" or "upper", defaults to "lower".
|
|
|
|
Separator string
|
|
|
|
SkipRoot bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flat is a configuration item representation.
|
|
|
|
type Flat struct {
|
|
|
|
Name string
|
|
|
|
Description string
|
|
|
|
Default string
|
|
|
|
}
|
|
|
|
|
|
|
|
// EncodeToFlat encodes a node to a Flat representation.
|
|
|
|
// Even though the given node argument should have already been augmented with metadata such as kind,
|
|
|
|
// the element (and its type information) is still needed to treat remaining edge cases.
|
|
|
|
func EncodeToFlat(element interface{}, node *Node, opts FlatOpts) ([]Flat, error) {
|
|
|
|
if element == nil || node == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if node.Kind == 0 {
|
|
|
|
return nil, fmt.Errorf("missing node type: %s", node.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := reflect.ValueOf(element)
|
|
|
|
if elem.Kind() == reflect.Struct {
|
|
|
|
return nil, fmt.Errorf("structs are not supported, use pointer instead")
|
|
|
|
}
|
|
|
|
|
|
|
|
encoder := encoderToFlat{FlatOpts: opts}
|
|
|
|
|
|
|
|
var entries []Flat
|
|
|
|
if encoder.SkipRoot {
|
|
|
|
for _, child := range node.Children {
|
|
|
|
field := encoder.getField(elem.Elem(), child)
|
|
|
|
entries = append(entries, encoder.createFlat(field, child.Name, child)...)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
entries = encoder.createFlat(elem, strings.ToLower(node.Name), node)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(entries, func(i, j int) bool { return entries[i].Name < entries[j].Name })
|
|
|
|
|
|
|
|
return entries, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type encoderToFlat struct {
|
|
|
|
FlatOpts
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e encoderToFlat) createFlat(field reflect.Value, name string, node *Node) []Flat {
|
|
|
|
var entries []Flat
|
|
|
|
if node.Kind != reflect.Map && node.Description != "-" {
|
|
|
|
if !(node.Kind == reflect.Ptr && len(node.Children) > 0) ||
|
|
|
|
(node.Kind == reflect.Ptr && node.Tag.Get("label") == TagLabelAllowEmpty) {
|
|
|
|
if node.Name[0] != '[' {
|
|
|
|
entries = append(entries, Flat{
|
|
|
|
Name: e.getName(name),
|
|
|
|
Description: node.Description,
|
|
|
|
Default: e.getNodeValue(e.getField(field, node), node),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, child := range node.Children {
|
|
|
|
if node.Kind == reflect.Map {
|
|
|
|
fChild := e.getField(field, child)
|
|
|
|
|
|
|
|
var v string
|
|
|
|
if child.Kind == reflect.Struct {
|
|
|
|
v = defaultPtrValue
|
|
|
|
} else {
|
|
|
|
v = e.getNodeValue(fChild, child)
|
|
|
|
}
|
|
|
|
|
|
|
|
if node.Description != "-" {
|
|
|
|
entries = append(entries, Flat{
|
|
|
|
Name: e.getName(name, child.Name),
|
|
|
|
Description: node.Description,
|
|
|
|
Default: v,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if child.Kind == reflect.Struct || child.Kind == reflect.Ptr {
|
|
|
|
for _, ch := range child.Children {
|
|
|
|
f := e.getField(fChild, ch)
|
|
|
|
n := e.getName(name, child.Name, ch.Name)
|
|
|
|
entries = append(entries, e.createFlat(f, n, ch)...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
f := e.getField(field, child)
|
|
|
|
n := e.getName(name, child.Name)
|
|
|
|
entries = append(entries, e.createFlat(f, n, child)...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return entries
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e encoderToFlat) getField(field reflect.Value, node *Node) reflect.Value {
|
|
|
|
switch field.Kind() {
|
|
|
|
case reflect.Struct:
|
|
|
|
return field.FieldByName(node.FieldName)
|
|
|
|
case reflect.Ptr:
|
|
|
|
if field.Elem().Kind() == reflect.Struct {
|
|
|
|
return field.Elem().FieldByName(node.FieldName)
|
|
|
|
}
|
|
|
|
return field.Elem()
|
|
|
|
case reflect.Map:
|
|
|
|
return field.MapIndex(reflect.ValueOf(node.FieldName))
|
|
|
|
default:
|
|
|
|
return field
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e encoderToFlat) getNodeValue(field reflect.Value, node *Node) string {
|
|
|
|
if node.Kind == reflect.Ptr && len(node.Children) > 0 {
|
|
|
|
return defaultPtrValue
|
|
|
|
}
|
|
|
|
|
|
|
|
if field.Kind() == reflect.Int64 {
|
|
|
|
i, _ := strconv.ParseInt(node.Value, 10, 64)
|
|
|
|
|
|
|
|
switch field.Type() {
|
|
|
|
case reflect.TypeOf(types.Duration(time.Second)):
|
|
|
|
return strconv.Itoa(int(i) / int(time.Second))
|
|
|
|
case reflect.TypeOf(time.Second):
|
|
|
|
return time.Duration(i).String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e encoderToFlat) getName(names ...string) string {
|
|
|
|
var name string
|
|
|
|
if names[len(names)-1][0] == '[' {
|
|
|
|
name = strings.Join(names, "")
|
|
|
|
} else {
|
|
|
|
name = strings.Join(names, e.Separator)
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.EqualFold(e.Case, "upper") {
|
|
|
|
return strings.ToUpper(name)
|
|
|
|
}
|
|
|
|
return strings.ToLower(name)
|
|
|
|
}
|