github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xdefault/xdefault.go (about)

     1  package xdefault
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/Aoi-hosizora/ahlib/xreflect"
     7  	"math"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  // TODO refactor and improve FillDefaultFields
    14  
    15  // TODO https://github.com/creasty/defaults/blob/master/defaults.go
    16  
    17  // _defaultTag is the "default" tag name which is used in FillDefaultFields.
    18  const _defaultTag = "default"
    19  
    20  var (
    21  	errNilValue     = errors.New("xdefault: nil value")
    22  	errNotStructPtr = errors.New("xdefault: not a pointer of a structure type")
    23  )
    24  
    25  const (
    26  	panicInvalidDefaultType = "xdefault: parsing '%s' as the default value of field '%s' failed: %v"
    27  )
    28  
    29  // FillDefaultFields fills struct fields with "default" tag recursively, returns true if all fields are set or filled, returns error only when given parameter
    30  // is nil or is not a pointer of struct, panics when using mismatched default value type and field type.
    31  //
    32  // Note that:
    33  // 1. values inside arrays, non-bytes-slices, maps, structs will be filled when these kinds of collections or wrappers are not empty.
    34  // 2. pointers, integers, unsigned integers, floating points, booleans, complex numerics, strings, bytes will be filled when they are nil, zero or empty.
    35  // 3. other kinds of values will be ignored for filling, and false will be returned, this means not all fields are filled.
    36  //
    37  // Example:
    38  // 	type Config struct {
    39  // 		Meta *struct {
    40  // 			Port   uint32 `yaml:"port"   default:"12345"`
    41  // 			Debug  bool   `yaml:"debug"  default:"true"`
    42  // 			Secret []byte `yaml:"secret" default:"abc"`
    43  // 			Ranges [2]int `yaml:"ranges" default:"3"`
    44  // 			// ...
    45  // 		}
    46  // 		MySQL *struct {
    47  // 			Host string `yaml:"host" default:"127.0.0.1"`
    48  // 			Port int32  `yaml:"port" default:"3306"`
    49  // 			// ...
    50  // 		}
    51  // 		// ...
    52  // 	}
    53  // 	cfg := &Config{}
    54  // 	// unmarshal cfg...
    55  // 	_, err = FillDefaultFields(cfg)
    56  func FillDefaultFields(value interface{}) (allFilled bool, err error) {
    57  	if value == nil {
    58  		return false, errNilValue
    59  	}
    60  
    61  	val := reflect.ValueOf(value)
    62  	if val.Kind() != reflect.Ptr {
    63  		return false, errNotStructPtr
    64  	}
    65  	val = val.Elem()
    66  	if val.Kind() != reflect.Struct {
    67  		return false, errNotStructPtr
    68  	}
    69  	typ := val.Type()
    70  
    71  	allFilled = false
    72  	for i := 0; i < typ.NumField(); i++ {
    73  		sf := typ.Field(i)
    74  		if sf.IsExported() && sf.Type != nil { // only for exported fields
    75  			allFilled = coreFillField(sf.Type, val.Field(i), sf.Tag, sf.Name, nil) || allFilled
    76  		}
    77  	}
    78  
    79  	return allFilled, nil
    80  }
    81  
    82  // coreFillField is the core implementation of FillDefaultFields, this sets the default value using given reflect.StructTag to given reflect.Value.
    83  func coreFillField(ftyp reflect.Type, fval reflect.Value, ftag reflect.StructTag, fname string, setMapItem func(v reflect.Value)) (filled bool) {
    84  	k := ftyp.Kind()
    85  	switch {
    86  	case k == reflect.Struct && ftyp.NumField() == 0,
    87  		(k == reflect.Array || (k == reflect.Slice && ftyp.Elem().Kind() != reflect.Uint8 /* byte */) || k == reflect.Map) && fval.Len() == 0,
    88  		k == reflect.Invalid || k == reflect.Func || k == reflect.Chan || k == reflect.Interface || k == reflect.UnsafePointer:
    89  		return false // this field is not filled
    90  
    91  	case k == reflect.Struct || k == reflect.Array || (k == reflect.Slice && ftyp.Elem().Kind() != reflect.Uint8 /* byte */) || k == reflect.Map || k == reflect.Ptr:
    92  		// set default value to array / non-bytes-slice / map / pointer / struct
    93  		return fillComplexField(k, ftyp, fval, ftag, fname, setMapItem) // this may call coreFillField function recursively
    94  
    95  	default:
    96  		// set default value to int / uint / float / bool / complex / string / bytes
    97  		defaul, ok := ftag.Lookup(_defaultTag)
    98  		if !ok {
    99  			return false // no "default" tag
   100  		}
   101  		return fillSimpleField(k, ftyp, fval, defaul, fname, setMapItem) // this may call setMapItem function
   102  	}
   103  }
   104  
   105  // fillComplexField is the core implementation of coreFillField for complex field types.
   106  func fillComplexField(k reflect.Kind, ftyp reflect.Type, fval reflect.Value, ftag reflect.StructTag, fname string, setMapItem func(v reflect.Value)) (allFilled bool) {
   107  	switch k {
   108  	case reflect.Map:
   109  		allFilled = false
   110  		for _, key := range fval.MapKeys() {
   111  			key := key
   112  			allFilled = coreFillField(ftyp.Elem(), fval.MapIndex(key), ftag, fmt.Sprintf("(%s)[\"%s\"]", fname, key.String()), func(v reflect.Value) {
   113  				// note: non-reference values (or items), which are got from map by index directly, cannot be addressed and written !!!
   114  				fval.SetMapIndex(key, v)
   115  			}) || allFilled
   116  		}
   117  		return allFilled
   118  
   119  	case reflect.Slice:
   120  		allFilled = false
   121  		for i := 0; i < fval.Len(); i++ {
   122  			allFilled = coreFillField(ftyp.Elem(), fval.Index(i), ftag, fmt.Sprintf("(%s)[%d]", fname, i), nil) || allFilled
   123  			// <<< no need to setMapItem, because slice items are stored in heap
   124  		}
   125  		return allFilled
   126  
   127  	case reflect.Array:
   128  		allFilled = false
   129  		cached := make(map[int]reflect.Value) // array value index to reflect.Value
   130  		for i := 0; i < ftyp.Len(); i++ {
   131  			i := i
   132  			allFilled = coreFillField(ftyp.Elem(), fval.Index(i), ftag, fmt.Sprintf("(%s)[%d]", fname, i), func(v reflect.Value) {
   133  				cached[i] = v // record each value for new array
   134  			}) || allFilled
   135  		}
   136  		if len(cached) > 0 {
   137  			newArray := reflect.New(ftyp).Elem()
   138  			newArray.Set(fval) // note: use typedmemmove (reflect.Value.Set), faster than for-iterate
   139  			for i := 0; i < ftyp.Len(); i++ {
   140  				if newVal, ok := cached[i]; ok {
   141  					newArray.Index(i).Set(newVal)
   142  				}
   143  			}
   144  			setMapItem(newArray) // <<< replace the whole array to map item in all cases
   145  		}
   146  		return allFilled
   147  
   148  	case reflect.Ptr:
   149  		etyp := ftyp.Elem()
   150  		if !fval.IsNil() {
   151  			return coreFillField(etyp, fval.Elem(), ftag, fmt.Sprintf("*(%s)", fname), nil)
   152  		}
   153  		newPtr := reflect.New(etyp)
   154  		filled := coreFillField(etyp, newPtr.Elem(), ftag, fmt.Sprintf("*(%s)", fname), nil)
   155  		if filled {
   156  			if fval.CanSet() {
   157  				fval.Set(newPtr)
   158  			} else {
   159  				setMapItem(newPtr) // <<< replace the whole pointer to map item when wrapped value cannot be set directly
   160  			}
   161  		}
   162  		return filled
   163  
   164  	case reflect.Struct:
   165  		allFilled = false
   166  		cached := make(map[int]reflect.Value) // struct field index to reflect.Value
   167  		for i := 0; i < ftyp.NumField(); i++ {
   168  			i := i
   169  			if sf := ftyp.Field(i); sf.IsExported() && sf.Type != nil { // only for exported fields
   170  				allFilled = coreFillField(sf.Type, fval.Field(i), sf.Tag, fmt.Sprintf("%s.%s", fname, sf.Name), func(v reflect.Value) {
   171  					cached[i] = v // record each field value for new struct
   172  				}) || allFilled
   173  			}
   174  		}
   175  		if len(cached) > 0 {
   176  			newStruct := reflect.New(ftyp).Elem()
   177  			newStruct.Set(fval) // include all exported and unexported fields
   178  			for i := 0; i < ftyp.NumField(); i++ {
   179  				if newVal, ok := cached[i]; ok {
   180  					newStruct.Field(i).Set(newVal)
   181  				}
   182  			}
   183  			setMapItem(newStruct) // <<< replace the whole struct to map item in all cases
   184  		}
   185  		return allFilled
   186  
   187  	default:
   188  		// unreachable
   189  		return false
   190  	}
   191  }
   192  
   193  // fillSimpleField is the core implementation of coreFillField for simple field types.
   194  func fillSimpleField(k reflect.Kind, ftyp reflect.Type, fval reflect.Value, defaul string, fname string, setMapItem func(v reflect.Value)) bool {
   195  	switch {
   196  	case xreflect.IsIntKind(k) && fval.Int() == 0:
   197  		i, err := strconv.ParseInt(defaul, 10, 64)
   198  		if err != nil {
   199  			panic(fmt.Sprintf(panicInvalidDefaultType, defaul, fname, err))
   200  		}
   201  		if fval.CanSet() { // addressable and assignable
   202  			fval.SetInt(i)
   203  		} else if setMapItem != nil { // must be a value of map, followings that call setMapItem are all the same case
   204  			setMapItem(reflect.ValueOf(i).Convert(ftyp))
   205  		}
   206  		return true
   207  
   208  	case xreflect.IsUintKind(k) && fval.Uint() == 0:
   209  		u, err := strconv.ParseUint(defaul, 10, 64)
   210  		if err != nil {
   211  			panic(fmt.Sprintf(panicInvalidDefaultType, defaul, fname, err))
   212  		}
   213  		if fval.CanSet() {
   214  			fval.SetUint(u)
   215  		} else if setMapItem != nil {
   216  			setMapItem(reflect.ValueOf(u).Convert(ftyp))
   217  		}
   218  		return true
   219  
   220  	case xreflect.IsFloatKind(k) && math.Float64bits(fval.Float()) == 0:
   221  		f, err := strconv.ParseFloat(defaul, 64)
   222  		if err != nil {
   223  			panic(fmt.Sprintf(panicInvalidDefaultType, defaul, fname, err))
   224  		}
   225  		if fval.CanSet() {
   226  			fval.SetFloat(f)
   227  		} else if setMapItem != nil {
   228  			setMapItem(reflect.ValueOf(f).Convert(ftyp))
   229  		}
   230  		return true
   231  
   232  	case xreflect.IsComplexKind(k) && math.Float64bits(real(fval.Complex())) == 0 && math.Float64bits(imag(fval.Complex())) == 0:
   233  		c, err := strconv.ParseComplex(defaul, 128)
   234  		if err != nil {
   235  			panic(fmt.Sprintf(panicInvalidDefaultType, defaul, fname, err))
   236  		}
   237  		if fval.CanSet() {
   238  			fval.SetComplex(c)
   239  		} else if setMapItem != nil {
   240  			setMapItem(reflect.ValueOf(c).Convert(ftyp))
   241  		}
   242  		return true
   243  
   244  	case k == reflect.Bool && fval.Bool() == false:
   245  		defaul = strings.ToLower(defaul)
   246  		b := defaul == "1" || defaul == "true" || defaul == "t"
   247  		if fval.CanSet() {
   248  			fval.SetBool(b)
   249  		} else if setMapItem != nil {
   250  			setMapItem(reflect.ValueOf(b))
   251  		}
   252  		return true
   253  
   254  	case k == reflect.String && len(fval.String()) == 0:
   255  		str := defaul
   256  		if fval.CanSet() {
   257  			fval.SetString(str)
   258  		} else if setMapItem != nil {
   259  			setMapItem(reflect.ValueOf(str))
   260  		}
   261  		return true
   262  
   263  	case k == reflect.Slice && ftyp.Elem().Kind() == reflect.Uint8 /* byte */ && fval.Len() == 0:
   264  		bs := []byte(defaul)
   265  		if fval.CanSet() {
   266  			fval.SetBytes(bs)
   267  		} else if setMapItem != nil {
   268  			setMapItem(reflect.ValueOf(bs))
   269  		}
   270  		return true
   271  
   272  	default:
   273  		// unnecessary to fill value to these kinds of fields
   274  		return false
   275  	}
   276  }