diff --git a/internal/autocert/provider_test/ovh_test.go b/internal/autocert/provider_test/ovh_test.go index b147ce96..f29b61a9 100644 --- a/internal/autocert/provider_test/ovh_test.go +++ b/internal/autocert/provider_test/ovh_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/go-acme/lego/v4/providers/dns/ovh" + "github.com/goccy/go-yaml" U "github.com/yusing/go-proxy/internal/utils" . "github.com/yusing/go-proxy/internal/utils/testing" - "gopkg.in/yaml.v3" ) // type Config struct { @@ -44,7 +44,7 @@ oauth2_config: } testYaml = testYaml[1:] // remove first \n opt := make(map[string]any) - ExpectNoError(t, yaml.Unmarshal([]byte(testYaml), opt)) + ExpectNoError(t, yaml.Unmarshal([]byte(testYaml), &opt)) ExpectNoError(t, U.Deserialize(opt, cfg)) ExpectEqual(t, cfg, cfgExpected) } diff --git a/internal/net/gphttp/middleware/middleware_builder.go b/internal/net/gphttp/middleware/middleware_builder.go index c7be5962..7d5fee84 100644 --- a/internal/net/gphttp/middleware/middleware_builder.go +++ b/internal/net/gphttp/middleware/middleware_builder.go @@ -6,8 +6,8 @@ import ( "path" "sort" + "github.com/goccy/go-yaml" "github.com/yusing/go-proxy/internal/gperr" - "gopkg.in/yaml.v3" ) var ErrMissingMiddlewareUse = gperr.New("missing middleware 'use' field") @@ -43,8 +43,8 @@ func BuildMiddlewaresFromYAML(source string, data []byte, eb *gperr.Builder) map func compileMiddlewares(middlewaresMap map[string]OptionsRaw) ([]*Middleware, gperr.Error) { middlewares := make([]*Middleware, 0, len(middlewaresMap)) - errs := gperr.NewBuilder("middlewares compile error") - invalidOpts := gperr.NewBuilder("options compile error") + errs := gperr.NewBuilder() + invalidOpts := gperr.NewBuilder() for name, opts := range middlewaresMap { m, err := Get(name) @@ -55,7 +55,7 @@ func compileMiddlewares(middlewaresMap map[string]OptionsRaw) ([]*Middleware, gp m, err = m.New(opts) if err != nil { - invalidOpts.Add(err.Subject(name)) + invalidOpts.Add(err.Subject("middlewares." + name)) continue } middlewares = append(middlewares, m) diff --git a/internal/route/provider/docker.go b/internal/route/provider/docker.go index e08cfc8e..0483bbbe 100755 --- a/internal/route/provider/docker.go +++ b/internal/route/provider/docker.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/docker/docker/client" + "github.com/goccy/go-yaml" "github.com/rs/zerolog" "github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/docker" @@ -14,7 +15,6 @@ import ( U "github.com/yusing/go-proxy/internal/utils" "github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/watcher" - "gopkg.in/yaml.v3" ) type DockerProvider struct { diff --git a/internal/route/provider/docker_labels_test.go b/internal/route/provider/docker_labels_test.go index 1c275c11..9d0354f7 100644 --- a/internal/route/provider/docker_labels_test.go +++ b/internal/route/provider/docker_labels_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/docker/docker/api/types/container" + "github.com/goccy/go-yaml" "github.com/yusing/go-proxy/internal/docker" . "github.com/yusing/go-proxy/internal/utils/testing" - "gopkg.in/yaml.v3" _ "embed" ) diff --git a/internal/utils/functional/map.go b/internal/utils/functional/map.go index 1b7a169e..c98c9481 100644 --- a/internal/utils/functional/map.go +++ b/internal/utils/functional/map.go @@ -3,8 +3,8 @@ package functional import ( "sync" + "github.com/goccy/go-yaml" "github.com/puzpuzpuz/xsync/v3" - "gopkg.in/yaml.v3" ) type Map[KT comparable, VT any] struct { @@ -97,7 +97,7 @@ func (m Map[KT, VT]) String() string { m.RangeAll(func(k KT, v VT) { tmp[k] = v }) - data, err := yaml.Marshal(tmp) + data, err := yaml.Marshal(&tmp) if err != nil { return err.Error() } diff --git a/internal/utils/serialization.go b/internal/utils/serialization.go index cebcc737..cf6e7715 100644 --- a/internal/utils/serialization.go +++ b/internal/utils/serialization.go @@ -11,10 +11,10 @@ import ( "time" "github.com/go-playground/validator/v10" + "github.com/goccy/go-yaml" "github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/utils/functional" "github.com/yusing/go-proxy/internal/utils/strutils" - "gopkg.in/yaml.v3" ) type SerializedObject = map[string]any @@ -90,7 +90,7 @@ func extractFields(t reflect.Type) (all, anonymous []reflect.StructField) { } func ValidateWithFieldTags(s any) gperr.Error { - errs := gperr.NewBuilder("validate error") + errs := gperr.NewBuilder() err := validate.Struct(s) var valErrs validator.ValidationErrors if errors.As(err, &valErrs) { @@ -99,9 +99,12 @@ func ValidateWithFieldTags(s any) gperr.Error { if e.Param() != "" { detail += ":" + e.Param() } + if detail != "required" { + detail = "require " + strconv.Quote(detail) + } errs.Add(ErrValidationError. Subject(e.Namespace()). - Withf("require %q", detail)) + Withf(detail)) } } return errs.Error() @@ -181,6 +184,10 @@ func dive(dst reflect.Value) (v reflect.Value, t reflect.Type, err gperr.Error) // // The function returns an error if the target value is not a struct or a map[string]any, or if there is an error during deserialization. func Deserialize(src SerializedObject, dst any) (err gperr.Error) { + return deserialize(src, dst, true) +} + +func deserialize(src SerializedObject, dst any, checkValidateTag bool) (err gperr.Error) { dstV := reflect.ValueOf(dst) dstT := dstV.Type() @@ -209,7 +216,7 @@ func Deserialize(src SerializedObject, dst any) (err gperr.Error) { // convert target fields to lower no-snake // then check if the field of data is in the target - errs := gperr.NewBuilder("deserialize error") + errs := gperr.NewBuilder() switch dstV.Kind() { case reflect.Struct, reflect.Interface: @@ -247,15 +254,15 @@ func Deserialize(src SerializedObject, dst any) (err gperr.Error) { } for k, v := range src { if field, ok := mapping[strutils.ToLowerNoSnake(k)]; ok { - err := Convert(reflect.ValueOf(v), field) + err := Convert(reflect.ValueOf(v), field, !hasValidateTag) if err != nil { - errs.Add(err.Subject(k)) + errs.Add(err) } } else { errs.Add(ErrUnknownField.Subject(k).Withf(strutils.DoYouMean(NearestField(k, mapping)))) } } - if hasValidateTag { + if hasValidateTag && checkValidateTag { errs.Add(ValidateWithFieldTags(dstV.Interface())) } if err := ValidateWithCustomValidator(dstV); err != nil { @@ -266,7 +273,7 @@ func Deserialize(src SerializedObject, dst any) (err gperr.Error) { for k, v := range src { mapVT := dstT.Elem() tmp := New(mapVT).Elem() - err := Convert(reflect.ValueOf(v), tmp) + err := Convert(reflect.ValueOf(v), tmp, true) if err != nil { errs.Add(err.Subject(k)) continue @@ -302,7 +309,7 @@ func isIntFloat(t reflect.Kind) bool { // // Returns: // - error: the error occurred during conversion, or nil if no error occurred. -func Convert(src reflect.Value, dst reflect.Value) gperr.Error { +func Convert(src reflect.Value, dst reflect.Value, checkValidateTag bool) gperr.Error { if !dst.IsValid() { return gperr.Errorf("convert: dst is %w", ErrNilValue) } @@ -371,7 +378,7 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error { if !ok { return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String()) } - return Deserialize(obj, dst.Addr().Interface()) + return deserialize(obj, dst.Addr().Interface(), checkValidateTag) case srcKind == reflect.Slice: if src.Len() == 0 { return nil @@ -384,7 +391,7 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error { i := 0 for j, v := range src.Seq2() { tmp := New(dstT.Elem()).Elem() - err := Convert(v, tmp) + err := Convert(v, tmp, checkValidateTag) if err != nil { sliceErrs.Add(err.Subjectf("[%d]", j)) continue @@ -464,7 +471,7 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe dst.Set(reflect.MakeSlice(dst.Type(), len(values), len(values))) errs := gperr.NewBuilder("invalid slice values") for i, v := range values { - err := Convert(reflect.ValueOf(v), dst.Index(i)) + err := Convert(reflect.ValueOf(v), dst.Index(i), true) if err != nil { errs.Add(err.Subjectf("[%d]", i)) } @@ -490,12 +497,12 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe default: return false, nil } - return true, Convert(reflect.ValueOf(tmp), dst) + return true, Convert(reflect.ValueOf(tmp), dst, true) } func DeserializeYAML[T any](data []byte, target *T) gperr.Error { m := make(map[string]any) - if err := yaml.Unmarshal(data, m); err != nil { + if err := yaml.Unmarshal(data, &m); err != nil { return gperr.Wrap(err) } return Deserialize(m, target) @@ -503,7 +510,7 @@ func DeserializeYAML[T any](data []byte, target *T) gperr.Error { func DeserializeYAMLMap[V any](data []byte) (_ functional.Map[string, V], err gperr.Error) { m := make(map[string]any) - if err = gperr.Wrap(yaml.Unmarshal(data, m)); err != nil { + if err = gperr.Wrap(yaml.Unmarshal(data, &m)); err != nil { return } m2 := make(map[string]V, len(m)) diff --git a/internal/utils/serialization_test.go b/internal/utils/serialization_test.go index 4f91748b..df08211e 100644 --- a/internal/utils/serialization_test.go +++ b/internal/utils/serialization_test.go @@ -5,8 +5,8 @@ import ( "strconv" "testing" + "github.com/goccy/go-yaml" . "github.com/yusing/go-proxy/internal/utils/testing" - "gopkg.in/yaml.v3" ) func TestDeserialize(t *testing.T) {