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 }