/* * Copyright (C) 2017 eschao * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package config import ( "encoding" "fmt" "reflect" "strconv" "strings" "time" ) func setValueWithBool(value reflect.Value, boolStr string) error { boolValue, err := strconv.ParseBool(boolStr) if err != nil { if txt, ok := value.Interface().(encoding.TextUnmarshaler); ok { if err = txt.UnmarshalText([]byte(boolStr)); err == nil { return nil } } } if err != nil { return fmt.Errorf("parse bool: %w", err) } value.SetBool(boolValue) return nil } func setValueWithDuration(value reflect.Value, str string) error { d, err := time.ParseDuration(str) if err != nil { return fmt.Errorf("parse duration: %w", err) } value.Set(reflect.ValueOf(d)) return nil } func setValueWithFloatX(value reflect.Value, floatStr string, bitSize int) error { floatValue, err := strconv.ParseFloat(floatStr, bitSize) if err != nil { return fmt.Errorf("parse float: %w", err) } value.SetFloat(floatValue) return nil } func setValueWithIntX(value reflect.Value, intStr string, bitSize int) error { intValue, err := strconv.ParseInt(intStr, 10, bitSize) if err != nil { if txt, ok := value.Interface().(encoding.TextUnmarshaler); ok { if err = txt.UnmarshalText([]byte(intStr)); err == nil { return nil } } } if err != nil { return fmt.Errorf("parse int: %w", err) } value.SetInt(intValue) return nil } func setValueWithUintX(value reflect.Value, uintStr string, bitSize int) error { uintValue, err := strconv.ParseUint(uintStr, 10, bitSize) if err != nil { if txt, ok := value.Interface().(encoding.TextUnmarshaler); ok { if err = txt.UnmarshalText([]byte(uintStr)); err == nil { return nil } } } if err != nil { return fmt.Errorf("parse uint: %w", err) } value.SetUint(uintValue) return nil } func setValueWithSlice(value reflect.Value, slice string, sep string) error { data := strings.Split(slice, sep) size := len(data) if size > 0 { slice := reflect.MakeSlice(value.Type(), size, size) for index := range size { ele := slice.Index(index) str := data[index] if ok, err := setDurationValue(ele, str); ok { return err } if setUnmarshalTextValue(ele, str) { continue } var err error kind := ele.Kind() switch kind { case reflect.Bool: err = setValueWithBool(ele, str) case reflect.String: ele.SetString(str) case reflect.Uint8: err = setValueWithUintX(ele, str, Uint8Size) case reflect.Uint16: err = setValueWithUintX(ele, str, Uint16Size) case reflect.Uint, reflect.Uint32: err = setValueWithUintX(ele, str, Uint32Size) case reflect.Uint64: err = setValueWithUintX(ele, str, Uint64Size) case reflect.Int8: err = setValueWithIntX(ele, str, Int8Size) case reflect.Int16: err = setValueWithIntX(ele, str, Int16Size) case reflect.Int, reflect.Int32: err = setValueWithIntX(ele, str, Int32Size) case reflect.Int64: err = setValueWithIntX(ele, str, Int64Size) case reflect.Float32: err = setValueWithFloatX(ele, str, Float32Size) case reflect.Float64: err = setValueWithFloatX(ele, str, Float64Size) default: return fmt.Errorf("%w: %s", errUnsupportedType, kind.String()) } if err != nil { return err } } value.Set(slice) } return nil } func setUnmarshalTextValue(value reflect.Value, str string) bool { unmarshalerType := reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() valuePtr := reflect.New(value.Type()) if valuePtr.Type().Implements(unmarshalerType) { if decoder, ok := valuePtr.Interface().(encoding.TextUnmarshaler); ok { if err := decoder.UnmarshalText([]byte(str)); err != nil { return false } value.Set(valuePtr.Elem()) return true } } return false } func setDurationValue(value reflect.Value, str string) (bool, error) { valueTypePkgPath := value.Type().PkgPath() valueTypeName := value.Type().Name() if valueTypePkgPath == "time" && valueTypeName == "Duration" { return true, setValueWithDuration(value, str) } return false, nil }