0
0
Fork 0
mirror of https://github.com/crazy-max/diun.git synced 2025-05-14 03:12:18 +00:00
crazy-max_diun/vendor/github.com/crazy-max/gonfig/parser/element_fill.go
2024-12-14 22:30:21 +01:00

578 lines
14 KiB
Go

package parser
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
"github.com/crazy-max/gonfig/types"
)
const defaultRawSliceSeparator = ","
type initializer interface {
SetDefaults()
}
// FillerOpts Options for the filler.
type FillerOpts struct {
AllowSliceAsStruct bool
RawSliceSeparator string
}
// Fill populates the fields of the element using the information in node.
func Fill(element interface{}, node *Node, opts FillerOpts) error {
return newFiller(opts).Fill(element, node)
}
type filler struct {
FillerOpts
}
func newFiller(opts FillerOpts) filler {
if opts.RawSliceSeparator == "" {
opts.RawSliceSeparator = defaultRawSliceSeparator
}
return filler{FillerOpts: opts}
}
// Fill populates the fields of the element using the information in node.
func (f filler) Fill(element interface{}, node *Node) error {
if element == nil || node == nil {
return nil
}
if node.Kind == 0 {
return fmt.Errorf("missing node type: %s", node.Name)
}
root := reflect.ValueOf(element)
if root.Kind() == reflect.Struct {
return fmt.Errorf("struct are not supported, use pointer instead")
}
return f.fill(root.Elem(), node)
}
func (f filler) fill(field reflect.Value, node *Node) error {
// related to allow-empty or ignore tag
if node.Disabled {
return nil
}
switch field.Kind() {
case reflect.String:
field.SetString(node.Value)
return nil
case reflect.Bool:
val, err := strconv.ParseBool(node.Value)
if err != nil {
return err
}
field.SetBool(val)
return nil
case reflect.Int8:
return setInt(field, node.Value, 8)
case reflect.Int16:
return setInt(field, node.Value, 16)
case reflect.Int32:
return setInt(field, node.Value, 32)
case reflect.Int64, reflect.Int:
return setInt(field, node.Value, 64)
case reflect.Uint8:
return setUint(field, node.Value, 8)
case reflect.Uint16:
return setUint(field, node.Value, 16)
case reflect.Uint32:
return setUint(field, node.Value, 32)
case reflect.Uint64, reflect.Uint:
return setUint(field, node.Value, 64)
case reflect.Float32:
return setFloat(field, node.Value, 32)
case reflect.Float64:
return setFloat(field, node.Value, 64)
case reflect.Struct:
return f.setStruct(field, node)
case reflect.Pointer:
return f.setPtr(field, node)
case reflect.Map:
return f.setMap(field, node)
case reflect.Slice:
return f.setSlice(field, node)
default:
return nil
}
}
func (f filler) setPtr(field reflect.Value, node *Node) error {
if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
if field.Type().Implements(reflect.TypeOf((*initializer)(nil)).Elem()) {
method := field.MethodByName("SetDefaults")
if method.IsValid() {
method.Call([]reflect.Value{})
}
}
}
return f.fill(field.Elem(), node)
}
func (f filler) setStruct(field reflect.Value, node *Node) error {
for _, child := range node.Children {
fd := field.FieldByName(child.FieldName)
zeroValue := reflect.Value{}
if fd == zeroValue {
return fmt.Errorf("field not found, node: %s (%s)", child.Name, child.FieldName)
}
err := f.fill(fd, child)
if err != nil {
return err
}
}
return nil
}
func (f filler) setSlice(field reflect.Value, node *Node) error {
if field.Type().Elem().Kind() == reflect.Struct ||
field.Type().Elem().Kind() == reflect.Pointer && field.Type().Elem().Elem().Kind() == reflect.Struct {
return f.setSliceStruct(field, node)
}
if len(node.Value) == 0 {
return nil
}
values := strings.Split(node.Value, f.RawSliceSeparator)
if f.RawSliceSeparator != defaultRawSliceSeparator {
if len(values) < 2 {
// TODO(ldez): must be changed to an error.
return makeSlice(field, strings.Split(node.Value, defaultRawSliceSeparator))
}
// TODO(ldez): this is related to raw map and file. Rethink the node parser.
values = values[2:]
}
return makeSlice(field, values)
}
func makeSlice(field reflect.Value, values []string) error {
slice := reflect.MakeSlice(field.Type(), len(values), len(values))
field.Set(slice)
for i := 0; i < len(values); i++ {
value := strings.TrimSpace(values[i])
switch field.Type().Elem().Kind() {
case reflect.String:
field.Index(i).SetString(value)
case reflect.Int:
val, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
field.Index(i).SetInt(val)
case reflect.Int8:
err := setInt(field.Index(i), value, 8)
if err != nil {
return err
}
case reflect.Int16:
err := setInt(field.Index(i), value, 16)
if err != nil {
return err
}
case reflect.Int32:
err := setInt(field.Index(i), value, 32)
if err != nil {
return err
}
case reflect.Int64:
err := setInt(field.Index(i), value, 64)
if err != nil {
return err
}
case reflect.Uint:
val, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
field.Index(i).SetUint(val)
case reflect.Uint8:
err := setUint(field.Index(i), value, 8)
if err != nil {
return err
}
case reflect.Uint16:
err := setUint(field.Index(i), value, 16)
if err != nil {
return err
}
case reflect.Uint32:
err := setUint(field.Index(i), value, 32)
if err != nil {
return err
}
case reflect.Uint64:
err := setUint(field.Index(i), value, 64)
if err != nil {
return err
}
case reflect.Float32:
err := setFloat(field.Index(i), value, 32)
if err != nil {
return err
}
case reflect.Float64:
err := setFloat(field.Index(i), value, 64)
if err != nil {
return err
}
case reflect.Bool:
val, err := strconv.ParseBool(value)
if err != nil {
return err
}
field.Index(i).SetBool(val)
default:
return fmt.Errorf("unsupported type: %s", field.Type().Elem())
}
}
return nil
}
func (f filler) setSliceStruct(field reflect.Value, node *Node) error {
if f.AllowSliceAsStruct && node.Tag.Get(TagLabelSliceAsStruct) != "" {
return f.setSliceAsStruct(field, node)
}
field.Set(reflect.MakeSlice(field.Type(), len(node.Children), len(node.Children)))
for i, child := range node.Children {
// use Ptr to allow "SetDefaults"
value := reflect.New(reflect.PointerTo(field.Type().Elem()))
err := f.setPtr(value, child)
if err != nil {
return err
}
field.Index(i).Set(value.Elem().Elem())
}
return nil
}
func (f filler) setSliceAsStruct(field reflect.Value, node *Node) error {
if len(node.Children) == 0 {
return fmt.Errorf("invalid slice: node %s", node.Name)
}
// use Ptr to allow "SetDefaults"
value := reflect.New(reflect.PointerTo(field.Type().Elem()))
if err := f.setPtr(value, node); err != nil {
return err
}
elem := value.Elem().Elem()
field.Set(reflect.MakeSlice(field.Type(), 1, 1))
field.Index(0).Set(elem)
return nil
}
func (f filler) setMap(field reflect.Value, node *Node) error {
if field.IsNil() {
field.Set(reflect.MakeMap(field.Type()))
}
if field.Type().Elem().Kind() == reflect.Interface {
err := f.fillRawValue(field, node, false)
if err != nil {
return err
}
for _, child := range node.Children {
err = f.fillRawValue(field, child, true)
if err != nil {
return err
}
}
return nil
}
for _, child := range node.Children {
ptrValue := reflect.New(reflect.PointerTo(field.Type().Elem()))
err := f.fill(ptrValue, child)
if err != nil {
return err
}
value := ptrValue.Elem().Elem()
key := reflect.ValueOf(child.Name)
field.SetMapIndex(key, value)
}
return nil
}
func setInt(field reflect.Value, value string, bitSize int) error {
switch field.Type() {
case reflect.TypeOf(types.Duration(0)):
return setDuration(field, value, bitSize, time.Second)
case reflect.TypeOf(time.Duration(0)):
return setDuration(field, value, bitSize, time.Nanosecond)
default:
val, err := strconv.ParseInt(value, 10, bitSize)
if err != nil {
return err
}
field.Set(reflect.ValueOf(val).Convert(field.Type()))
return nil
}
}
func setDuration(field reflect.Value, value string, bitSize int, defaultUnit time.Duration) error {
val, err := strconv.ParseInt(value, 10, bitSize)
if err == nil {
field.Set(reflect.ValueOf(time.Duration(val) * defaultUnit).Convert(field.Type()))
return nil
}
duration, err := time.ParseDuration(value)
if err != nil {
return err
}
field.Set(reflect.ValueOf(duration).Convert(field.Type()))
return nil
}
func setUint(field reflect.Value, value string, bitSize int) error {
val, err := strconv.ParseUint(value, 10, bitSize)
if err != nil {
return err
}
field.Set(reflect.ValueOf(val).Convert(field.Type()))
return nil
}
func setFloat(field reflect.Value, value string, bitSize int) error {
val, err := strconv.ParseFloat(value, bitSize)
if err != nil {
return err
}
field.Set(reflect.ValueOf(val).Convert(field.Type()))
return nil
}
func (f filler) fillRawValue(field reflect.Value, node *Node, subMap bool) error {
m, ok := node.RawValue.(map[string]interface{})
if !ok {
return nil
}
if _, self := m[node.Name]; self || !subMap {
for k, v := range m {
if f.RawSliceSeparator == defaultRawSliceSeparator {
field.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v))
continue
}
// TODO(ldez): all the next section is related to raw map and file. Rethink the node parser.
s, ok := v.(string)
if !ok || len(s) == 0 || !strings.HasPrefix(s, f.RawSliceSeparator) {
rawValue, err := f.cleanRawValue(reflect.ValueOf(v))
if err != nil {
return err
}
field.SetMapIndex(reflect.ValueOf(k), rawValue)
continue
}
// typed slice
value, err := f.fillRawTypedSlice(s)
if err != nil {
return err
}
field.SetMapIndex(reflect.ValueOf(k), value)
}
return nil
}
// In the case of sub-map, fill raw typed slices recursively.
_, err := f.fillRawMapWithTypedSlice(m)
if err != nil {
return err
}
p := map[string]interface{}{node.Name: m}
node.RawValue = p
field.SetMapIndex(reflect.ValueOf(node.Name), reflect.ValueOf(p[node.Name]))
return nil
}
func (f filler) fillRawMapWithTypedSlice(elt interface{}) (reflect.Value, error) {
eltValue := reflect.ValueOf(elt)
switch eltValue.Kind() {
case reflect.String:
if strings.HasPrefix(elt.(string), f.RawSliceSeparator) {
sliceValue, err := f.fillRawTypedSlice(elt.(string))
if err != nil {
return eltValue, err
}
return sliceValue, nil
}
case reflect.Map:
for k, v := range elt.(map[string]interface{}) {
value, err := f.fillRawMapWithTypedSlice(v)
if err != nil {
return eltValue, err
}
eltValue.SetMapIndex(reflect.ValueOf(k), value)
}
}
return eltValue, nil
}
func (f filler) fillRawTypedSlice(s string) (reflect.Value, error) {
raw := strings.Split(s, f.RawSliceSeparator)
rawType, err := strconv.Atoi(raw[1])
if err != nil {
return reflect.Value{}, err
}
kind := reflect.Kind(rawType)
slice := reflect.MakeSlice(reflect.TypeOf([]interface{}{}), len(raw[2:]), len(raw[2:]))
for i := 0; i < len(raw[2:]); i++ {
switch kind {
case reflect.Bool:
val, err := strconv.ParseBool(raw[i+2])
if err != nil {
return reflect.Value{}, fmt.Errorf("parse bool: %s, %w", raw[i+2], err)
}
slice.Index(i).Set(reflect.ValueOf(val))
case reflect.Int:
val, err := strconv.ParseInt(raw[i+2], 10, 64)
if err != nil {
return reflect.Value{}, fmt.Errorf("parse int: %s, %w", raw[i+2], err)
}
slice.Index(i).Set(reflect.ValueOf(val))
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(raw[i+2], 10, int(math.Pow(2, float64(kind-reflect.Int+2))))
if err != nil {
return reflect.Value{}, fmt.Errorf("parse %s: %s, %w", kind, raw[i+2], err)
}
slice.Index(i).Set(reflect.ValueOf(val))
case reflect.Uint:
val, err := strconv.ParseUint(raw[i+2], 10, 64)
if err != nil {
return reflect.Value{}, fmt.Errorf("parse uint: %s, %w", raw[i+2], err)
}
slice.Index(i).Set(reflect.ValueOf(val))
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(raw[i+2], 10, int(math.Pow(2, float64(kind-reflect.Uint+2))))
if err != nil {
return reflect.Value{}, fmt.Errorf("parse uint: %s, %w", raw[i+2], err)
}
slice.Index(i).Set(reflect.ValueOf(val))
case reflect.Float32:
err := setFloat(slice.Index(i), raw[i+2], 32)
if err != nil {
return reflect.Value{}, fmt.Errorf("parse float32: %s, %w", raw[i+2], err)
}
case reflect.Float64:
err := setFloat(slice.Index(i), raw[i+2], 64)
if err != nil {
return reflect.Value{}, fmt.Errorf("parse float64: %s, %w", raw[i+2], err)
}
case reflect.String:
slice.Index(i).Set(reflect.ValueOf(raw[i+2]))
default:
return reflect.Value{}, fmt.Errorf("unsupported kind: %d", kind)
}
}
return slice, nil
}
func (f filler) cleanRawValue(value reflect.Value) (reflect.Value, error) {
switch value.Kind() {
case reflect.Pointer:
rawValue, err := f.cleanRawValue(value.Elem())
if err != nil {
return reflect.Value{}, err
}
value.Elem().Set(rawValue)
case reflect.Map:
keys := value.MapKeys()
for _, key := range keys {
v := value.MapIndex(key)
rawValue, err := f.cleanRawValue(v)
if err != nil {
return reflect.Value{}, err
}
value.SetMapIndex(key, rawValue)
}
case reflect.Slice:
if value.IsZero() {
return value, nil
}
for i := 0; i < value.Len(); i++ {
if !value.Index(i).IsZero() {
rawValue, err := f.cleanRawValue(value.Index(i))
if err != nil {
return reflect.Value{}, err
}
value.Index(i).Set(rawValue)
}
}
case reflect.Interface:
return f.cleanRawValue(value.Elem())
case reflect.String:
if strings.HasPrefix(value.String(), f.RawSliceSeparator) {
return f.fillRawTypedSlice(value.String())
}
}
return value, nil
}