From 5058a89152c7f34450a14237c1f565f182c86b18 Mon Sep 17 00:00:00 2001 From: Rayquaza Date: Thu, 20 Nov 2025 16:09:49 +0800 Subject: [PATCH 1/3] feat: error report on invalid default value --- defaults.go | 103 +++++++++++++++++++++++++++++++---------------- defaults_test.go | 84 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 35 deletions(-) diff --git a/defaults.go b/defaults.go index f453928..d16812d 100644 --- a/defaults.go +++ b/defaults.go @@ -4,6 +4,7 @@ import ( "encoding" "encoding/json" "errors" + "fmt" "reflect" "strconv" "time" @@ -34,7 +35,7 @@ func Set(ptr interface{}) error { for i := 0; i < t.NumField(); i++ { if defaultVal := t.Field(i).Tag.Get(fieldName); defaultVal != "-" { - if err := setField(v.Field(i), defaultVal); err != nil { + if err := setField(v.Field(i), t.Field(i).Name, defaultVal); err != nil { return err } } @@ -51,7 +52,11 @@ func MustSet(ptr interface{}) { } } -func setField(field reflect.Value, defaultVal string) error { +func setField(field reflect.Value, currFieldName, defaultVal string) error { + wrapErr := func(err error) error { + return fmt.Errorf("error on set Field:[%s], DefaultValue:[%s] Error:[%v]", currFieldName, defaultVal, err) + } + if !field.CanSet() { return nil } @@ -68,63 +73,91 @@ func setField(field reflect.Value, defaultVal string) error { switch field.Kind() { case reflect.Bool: - if val, err := strconv.ParseBool(defaultVal); err == nil { - field.Set(reflect.ValueOf(val).Convert(field.Type())) + val, err := strconv.ParseBool(defaultVal) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(val).Convert(field.Type())) case reflect.Int: - if val, err := strconv.ParseInt(defaultVal, 0, strconv.IntSize); err == nil { - field.Set(reflect.ValueOf(int(val)).Convert(field.Type())) + val, err := strconv.ParseInt(defaultVal, 0, strconv.IntSize) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(int(val)).Convert(field.Type())) case reflect.Int8: - if val, err := strconv.ParseInt(defaultVal, 0, 8); err == nil { - field.Set(reflect.ValueOf(int8(val)).Convert(field.Type())) + val, err := strconv.ParseInt(defaultVal, 0, 8) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(int8(val)).Convert(field.Type())) case reflect.Int16: - if val, err := strconv.ParseInt(defaultVal, 0, 16); err == nil { - field.Set(reflect.ValueOf(int16(val)).Convert(field.Type())) + val, err := strconv.ParseInt(defaultVal, 0, 16) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(int16(val)).Convert(field.Type())) case reflect.Int32: - if val, err := strconv.ParseInt(defaultVal, 0, 32); err == nil { - field.Set(reflect.ValueOf(int32(val)).Convert(field.Type())) + val, err := strconv.ParseInt(defaultVal, 0, 32) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(int32(val)).Convert(field.Type())) case reflect.Int64: if val, err := time.ParseDuration(defaultVal); err == nil { field.Set(reflect.ValueOf(val).Convert(field.Type())) } else if val, err := strconv.ParseInt(defaultVal, 0, 64); err == nil { field.Set(reflect.ValueOf(val).Convert(field.Type())) + } else { + return wrapErr(err) } case reflect.Uint: - if val, err := strconv.ParseUint(defaultVal, 0, strconv.IntSize); err == nil { - field.Set(reflect.ValueOf(uint(val)).Convert(field.Type())) + val, err := strconv.ParseUint(defaultVal, 0, strconv.IntSize) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(uint(val)).Convert(field.Type())) case reflect.Uint8: - if val, err := strconv.ParseUint(defaultVal, 0, 8); err == nil { - field.Set(reflect.ValueOf(uint8(val)).Convert(field.Type())) + val, err := strconv.ParseUint(defaultVal, 0, 8) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(uint8(val)).Convert(field.Type())) case reflect.Uint16: - if val, err := strconv.ParseUint(defaultVal, 0, 16); err == nil { - field.Set(reflect.ValueOf(uint16(val)).Convert(field.Type())) + val, err := strconv.ParseUint(defaultVal, 0, 16) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(uint16(val)).Convert(field.Type())) case reflect.Uint32: - if val, err := strconv.ParseUint(defaultVal, 0, 32); err == nil { - field.Set(reflect.ValueOf(uint32(val)).Convert(field.Type())) + val, err := strconv.ParseUint(defaultVal, 0, 32) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(uint32(val)).Convert(field.Type())) case reflect.Uint64: - if val, err := strconv.ParseUint(defaultVal, 0, 64); err == nil { - field.Set(reflect.ValueOf(val).Convert(field.Type())) + val, err := strconv.ParseUint(defaultVal, 0, 64) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(val).Convert(field.Type())) case reflect.Uintptr: - if val, err := strconv.ParseUint(defaultVal, 0, strconv.IntSize); err == nil { - field.Set(reflect.ValueOf(uintptr(val)).Convert(field.Type())) + val, err := strconv.ParseUint(defaultVal, 0, strconv.IntSize) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(uintptr(val)).Convert(field.Type())) case reflect.Float32: - if val, err := strconv.ParseFloat(defaultVal, 32); err == nil { - field.Set(reflect.ValueOf(float32(val)).Convert(field.Type())) + val, err := strconv.ParseFloat(defaultVal, 32) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(float32(val)).Convert(field.Type())) case reflect.Float64: - if val, err := strconv.ParseFloat(defaultVal, 64); err == nil { - field.Set(reflect.ValueOf(val).Convert(field.Type())) + val, err := strconv.ParseFloat(defaultVal, 64) + if err != nil { + return wrapErr(err) } + field.Set(reflect.ValueOf(val).Convert(field.Type())) case reflect.String: field.Set(reflect.ValueOf(defaultVal).Convert(field.Type())) @@ -133,7 +166,7 @@ func setField(field reflect.Value, defaultVal string) error { ref.Elem().Set(reflect.MakeSlice(field.Type(), 0, 0)) if defaultVal != "" && defaultVal != "[]" { if err := json.Unmarshal([]byte(defaultVal), ref.Interface()); err != nil { - return err + return wrapErr(err) } } field.Set(ref.Elem().Convert(field.Type())) @@ -142,14 +175,14 @@ func setField(field reflect.Value, defaultVal string) error { ref.Elem().Set(reflect.MakeMap(field.Type())) if defaultVal != "" && defaultVal != "{}" { if err := json.Unmarshal([]byte(defaultVal), ref.Interface()); err != nil { - return err + return wrapErr(err) } } field.Set(ref.Elem().Convert(field.Type())) case reflect.Struct: if defaultVal != "" && defaultVal != "{}" { if err := json.Unmarshal([]byte(defaultVal), field.Addr().Interface()); err != nil { - return err + return wrapErr(err) } } case reflect.Ptr: @@ -160,7 +193,7 @@ func setField(field reflect.Value, defaultVal string) error { switch field.Kind() { case reflect.Ptr: if isInitial || field.Elem().Kind() == reflect.Struct { - setField(field.Elem(), defaultVal) + setField(field.Elem(), currFieldName, defaultVal) callSetter(field.Interface()) } case reflect.Struct: @@ -169,7 +202,7 @@ func setField(field reflect.Value, defaultVal string) error { } case reflect.Slice: for j := 0; j < field.Len(); j++ { - if err := setField(field.Index(j), ""); err != nil { + if err := setField(field.Index(j), currFieldName, ""); err != nil { return err } } @@ -181,14 +214,14 @@ func setField(field reflect.Value, defaultVal string) error { case reflect.Ptr: switch v.Elem().Kind() { case reflect.Struct, reflect.Slice, reflect.Map: - if err := setField(v.Elem(), ""); err != nil { + if err := setField(v.Elem(), currFieldName, ""); err != nil { return err } } case reflect.Struct, reflect.Slice, reflect.Map: ref := reflect.New(v.Type()) ref.Elem().Set(v) - if err := setField(ref.Elem(), ""); err != nil { + if err := setField(ref.Elem(), currFieldName, ""); err != nil { return err } field.SetMapIndex(e, ref.Elem().Convert(v.Type())) diff --git a/defaults_test.go b/defaults_test.go index b0dc0e7..1654072 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -769,3 +769,87 @@ func TestDefaultsSetter(t *testing.T) { t.Errorf("expected 1 for MainInt, got %d", main.MainInt) } } + +func expectErrorForDefaultSet(t *testing.T, cfg interface{}) { + if err := Set(cfg); err == nil { + t.Errorf("expected error, got nil") + } +} + +type ErrBoolDefault struct { + ErrBool bool `default:"ture"` +} +type ErrIntDefault struct { + ErrInt int `default:"abc"` +} + +type ErrInt8Default struct { + ErrInt8 int8 `default:"abc"` +} +type ErrInt16Default struct { + ErrInt16 int16 `default:"abc"` +} + +type ErrInt32Default struct { + ErrInt32 int32 `default:"abc"` +} + +type ErrInt64Default struct { + ErrInt64 int64 `default:"abc"` +} + +type ErrUintDefault struct { + ErrUint uint `default:"abc"` +} +type ErrUint8Default struct { + ErrUint8 uint8 `default:"abc"` +} +type ErrUint16Default struct { + ErrUint16 uint16 `default:"abc"` +} +type ErrUint32Default struct { + ErrUint32 uint32 `default:"abc"` +} +type ErrUint64Default struct { + ErrUint64 uint64 `default:"abc"` +} +type ErrUintptrDefault struct { + ErrUintptr uintptr `default:"abc"` +} +type ErrFloat32Default struct { + ErrFloat32 float32 `default:"abc"` +} +type ErrFloat64Default struct { + ErrFloat64 float64 `default:"abc"` +} + +func TestBasicTypeErrorConfig(t *testing.T) { + errBool := &ErrBoolDefault{} + expectErrorForDefaultSet(t, errBool) + errInt := &ErrIntDefault{} + expectErrorForDefaultSet(t, errInt) + errInt8 := &ErrInt8Default{} + expectErrorForDefaultSet(t, errInt8) + errInt16 := &ErrInt16Default{} + expectErrorForDefaultSet(t, errInt16) + errInt32 := &ErrInt32Default{} + expectErrorForDefaultSet(t, errInt32) + errInt64 := &ErrInt64Default{} + expectErrorForDefaultSet(t, errInt64) + errUint := &ErrUintDefault{} + expectErrorForDefaultSet(t, errUint) + errUint8 := &ErrUint8Default{} + expectErrorForDefaultSet(t, errUint8) + errUint16 := &ErrUint16Default{} + expectErrorForDefaultSet(t, errUint16) + errUint32 := &ErrUint32Default{} + expectErrorForDefaultSet(t, errUint32) + errUint64 := &ErrUint64Default{} + expectErrorForDefaultSet(t, errUint64) + errUintptr := &ErrUintptrDefault{} + expectErrorForDefaultSet(t, errUintptr) + errFloat32 := &ErrFloat32Default{} + expectErrorForDefaultSet(t, errFloat32) + errFloat64 := &ErrFloat64Default{} + expectErrorForDefaultSet(t, errFloat64) +} From df4441bf6bed81455482272aa46cf25921e483ba Mon Sep 17 00:00:00 2001 From: Rayquaza Date: Thu, 20 Nov 2025 16:10:24 +0800 Subject: [PATCH 2/3] feat: error report on sub field error --- defaults.go | 5 ++++- defaults_test.go | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/defaults.go b/defaults.go index d16812d..595736b 100644 --- a/defaults.go +++ b/defaults.go @@ -193,7 +193,10 @@ func setField(field reflect.Value, currFieldName, defaultVal string) error { switch field.Kind() { case reflect.Ptr: if isInitial || field.Elem().Kind() == reflect.Struct { - setField(field.Elem(), currFieldName, defaultVal) + err := setField(field.Elem(), currFieldName, defaultVal) + if err != nil { + return err + } callSetter(field.Interface()) } case reflect.Struct: diff --git a/defaults_test.go b/defaults_test.go index 1654072..7f45cb4 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -853,3 +853,19 @@ func TestBasicTypeErrorConfig(t *testing.T) { errFloat64 := &ErrFloat64Default{} expectErrorForDefaultSet(t, errFloat64) } + +type SubErrorDefault struct { + ErrSlice []string `default:"[1,2,3]"` + NormalInt int `default:"1"` +} + +type ParentErrorDefault struct { + NormalInt int `default:"1"` + ErrChild *SubErrorDefault `default:"{}"` + NormalBool bool `default:"true"` +} + +func TestSubFieldError(t *testing.T) { + cfg := &ParentErrorDefault{} + expectErrorForDefaultSet(t, cfg) +} From 6c20eed7f229bd476b2c5b8619370ae08a7a1aae Mon Sep 17 00:00:00 2001 From: Rayquaza Date: Thu, 20 Nov 2025 17:56:45 +0800 Subject: [PATCH 3/3] feat: typo --- defaults_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/defaults_test.go b/defaults_test.go index 7f45cb4..152e777 100644 --- a/defaults_test.go +++ b/defaults_test.go @@ -779,6 +779,7 @@ func expectErrorForDefaultSet(t *testing.T, cfg interface{}) { type ErrBoolDefault struct { ErrBool bool `default:"ture"` } + type ErrIntDefault struct { ErrInt int `default:"abc"` } @@ -786,6 +787,7 @@ type ErrIntDefault struct { type ErrInt8Default struct { ErrInt8 int8 `default:"abc"` } + type ErrInt16Default struct { ErrInt16 int16 `default:"abc"` } @@ -801,24 +803,31 @@ type ErrInt64Default struct { type ErrUintDefault struct { ErrUint uint `default:"abc"` } + type ErrUint8Default struct { ErrUint8 uint8 `default:"abc"` } + type ErrUint16Default struct { ErrUint16 uint16 `default:"abc"` } + type ErrUint32Default struct { ErrUint32 uint32 `default:"abc"` } + type ErrUint64Default struct { ErrUint64 uint64 `default:"abc"` } + type ErrUintptrDefault struct { ErrUintptr uintptr `default:"abc"` } + type ErrFloat32Default struct { ErrFloat32 float32 `default:"abc"` } + type ErrFloat64Default struct { ErrFloat64 float64 `default:"abc"` }