traefik/pkg/provider/label/internal/element_nodes.go
Ludovic Fernandez f1b085fa36 Move code to pkg
2019-03-15 09:42:03 +01:00

164 lines
4 KiB
Go

package internal
import (
"fmt"
"reflect"
"strconv"
"strings"
)
// EncodeToNode Converts an element to a node.
// element -> nodes
func EncodeToNode(element interface{}) (*Node, error) {
rValue := reflect.ValueOf(element)
node := &Node{Name: "traefik"}
err := setNodeValue(node, rValue)
if err != nil {
return nil, err
}
return node, nil
}
func setNodeValue(node *Node, rValue reflect.Value) error {
switch rValue.Kind() {
case reflect.String:
node.Value = rValue.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
node.Value = strconv.FormatInt(rValue.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
node.Value = strconv.FormatUint(rValue.Uint(), 10)
case reflect.Float32, reflect.Float64:
node.Value = strconv.FormatFloat(rValue.Float(), 'f', 6, 64)
case reflect.Bool:
node.Value = strconv.FormatBool(rValue.Bool())
case reflect.Struct:
return setStructValue(node, rValue)
case reflect.Ptr:
return setNodeValue(node, rValue.Elem())
case reflect.Map:
return setMapValue(node, rValue)
case reflect.Slice:
return setSliceValue(node, rValue)
default:
// noop
}
return nil
}
func setStructValue(node *Node, rValue reflect.Value) error {
rType := rValue.Type()
for i := 0; i < rValue.NumField(); i++ {
field := rType.Field(i)
fieldValue := rValue.Field(i)
if !isExported(field) {
continue
}
if field.Tag.Get(TagLabel) == "-" {
continue
}
if err := isSupportedType(field); err != nil {
return err
}
if isSkippedField(field, fieldValue) {
continue
}
nodeName := field.Name
if field.Type.Kind() == reflect.Slice && len(field.Tag.Get(TagLabelSliceAsStruct)) != 0 {
nodeName = field.Tag.Get(TagLabelSliceAsStruct)
}
child := &Node{Name: nodeName, FieldName: field.Name}
if err := setNodeValue(child, fieldValue); err != nil {
return err
}
if field.Type.Kind() == reflect.Ptr && len(child.Children) == 0 {
if field.Tag.Get(TagLabel) != "allowEmpty" {
continue
}
child.Value = "true"
}
node.Children = append(node.Children, child)
}
return nil
}
func setMapValue(node *Node, rValue reflect.Value) error {
for _, key := range rValue.MapKeys() {
child := &Node{Name: key.String(), FieldName: key.String()}
node.Children = append(node.Children, child)
if err := setNodeValue(child, rValue.MapIndex(key)); err != nil {
return err
}
}
return nil
}
func setSliceValue(node *Node, rValue reflect.Value) error {
// label-slice-as-struct
if rValue.Type().Elem().Kind() == reflect.Struct && !strings.EqualFold(node.Name, node.FieldName) {
if rValue.Len() > 1 {
return fmt.Errorf("node %s has too many slice entries: %d", node.Name, rValue.Len())
}
if err := setNodeValue(node, rValue.Index(0)); err != nil {
return err
}
}
var values []string
for i := 0; i < rValue.Len(); i++ {
eValue := rValue.Index(i)
switch eValue.Kind() {
case reflect.String:
values = append(values, eValue.String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
values = append(values, strconv.FormatInt(eValue.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
values = append(values, strconv.FormatUint(eValue.Uint(), 10))
case reflect.Float32, reflect.Float64:
values = append(values, strconv.FormatFloat(eValue.Float(), 'f', 6, 64))
case reflect.Bool:
values = append(values, strconv.FormatBool(eValue.Bool()))
default:
// noop
}
}
node.Value = strings.Join(values, ", ")
return nil
}
func isSkippedField(field reflect.StructField, fieldValue reflect.Value) bool {
if field.Type.Kind() == reflect.String && fieldValue.Len() == 0 {
return true
}
if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct && fieldValue.IsNil() {
return true
}
if (field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Map) &&
(fieldValue.IsNil() || fieldValue.Len() == 0) {
return true
}
return false
}