chore: replace gopkg.in/yaml.v3 with goccy/go-yaml

This commit is contained in:
yusing 2025-04-25 08:36:54 +08:00
parent 59490dcac0
commit 2f33ee02d9
7 changed files with 33 additions and 26 deletions

View file

@ -4,9 +4,9 @@ import (
"testing" "testing"
"github.com/go-acme/lego/v4/providers/dns/ovh" "github.com/go-acme/lego/v4/providers/dns/ovh"
"github.com/goccy/go-yaml"
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
"gopkg.in/yaml.v3"
) )
// type Config struct { // type Config struct {
@ -44,7 +44,7 @@ oauth2_config:
} }
testYaml = testYaml[1:] // remove first \n testYaml = testYaml[1:] // remove first \n
opt := make(map[string]any) 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)) ExpectNoError(t, U.Deserialize(opt, cfg))
ExpectEqual(t, cfg, cfgExpected) ExpectEqual(t, cfg, cfgExpected)
} }

View file

@ -6,8 +6,8 @@ import (
"path" "path"
"sort" "sort"
"github.com/goccy/go-yaml"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"gopkg.in/yaml.v3"
) )
var ErrMissingMiddlewareUse = gperr.New("missing middleware 'use' field") 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) { func compileMiddlewares(middlewaresMap map[string]OptionsRaw) ([]*Middleware, gperr.Error) {
middlewares := make([]*Middleware, 0, len(middlewaresMap)) middlewares := make([]*Middleware, 0, len(middlewaresMap))
errs := gperr.NewBuilder("middlewares compile error") errs := gperr.NewBuilder()
invalidOpts := gperr.NewBuilder("options compile error") invalidOpts := gperr.NewBuilder()
for name, opts := range middlewaresMap { for name, opts := range middlewaresMap {
m, err := Get(name) m, err := Get(name)
@ -55,7 +55,7 @@ func compileMiddlewares(middlewaresMap map[string]OptionsRaw) ([]*Middleware, gp
m, err = m.New(opts) m, err = m.New(opts)
if err != nil { if err != nil {
invalidOpts.Add(err.Subject(name)) invalidOpts.Add(err.Subject("middlewares." + name))
continue continue
} }
middlewares = append(middlewares, m) middlewares = append(middlewares, m)

View file

@ -5,6 +5,7 @@ import (
"strconv" "strconv"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/goccy/go-yaml"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/docker"
@ -14,7 +15,6 @@ import (
U "github.com/yusing/go-proxy/internal/utils" U "github.com/yusing/go-proxy/internal/utils"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
"github.com/yusing/go-proxy/internal/watcher" "github.com/yusing/go-proxy/internal/watcher"
"gopkg.in/yaml.v3"
) )
type DockerProvider struct { type DockerProvider struct {

View file

@ -4,9 +4,9 @@ import (
"testing" "testing"
"github.com/docker/docker/api/types/container" "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/docker"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
"gopkg.in/yaml.v3"
_ "embed" _ "embed"
) )

View file

@ -3,8 +3,8 @@ package functional
import ( import (
"sync" "sync"
"github.com/goccy/go-yaml"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
"gopkg.in/yaml.v3"
) )
type Map[KT comparable, VT any] struct { 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) { m.RangeAll(func(k KT, v VT) {
tmp[k] = v tmp[k] = v
}) })
data, err := yaml.Marshal(tmp) data, err := yaml.Marshal(&tmp)
if err != nil { if err != nil {
return err.Error() return err.Error()
} }

View file

@ -11,10 +11,10 @@ import (
"time" "time"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/goccy/go-yaml"
"github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/gperr"
"github.com/yusing/go-proxy/internal/utils/functional" "github.com/yusing/go-proxy/internal/utils/functional"
"github.com/yusing/go-proxy/internal/utils/strutils" "github.com/yusing/go-proxy/internal/utils/strutils"
"gopkg.in/yaml.v3"
) )
type SerializedObject = map[string]any type SerializedObject = map[string]any
@ -90,7 +90,7 @@ func extractFields(t reflect.Type) (all, anonymous []reflect.StructField) {
} }
func ValidateWithFieldTags(s any) gperr.Error { func ValidateWithFieldTags(s any) gperr.Error {
errs := gperr.NewBuilder("validate error") errs := gperr.NewBuilder()
err := validate.Struct(s) err := validate.Struct(s)
var valErrs validator.ValidationErrors var valErrs validator.ValidationErrors
if errors.As(err, &valErrs) { if errors.As(err, &valErrs) {
@ -99,9 +99,12 @@ func ValidateWithFieldTags(s any) gperr.Error {
if e.Param() != "" { if e.Param() != "" {
detail += ":" + e.Param() detail += ":" + e.Param()
} }
if detail != "required" {
detail = "require " + strconv.Quote(detail)
}
errs.Add(ErrValidationError. errs.Add(ErrValidationError.
Subject(e.Namespace()). Subject(e.Namespace()).
Withf("require %q", detail)) Withf(detail))
} }
} }
return errs.Error() 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. // 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) { 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) dstV := reflect.ValueOf(dst)
dstT := dstV.Type() dstT := dstV.Type()
@ -209,7 +216,7 @@ func Deserialize(src SerializedObject, dst any) (err gperr.Error) {
// convert target fields to lower no-snake // convert target fields to lower no-snake
// then check if the field of data is in the target // then check if the field of data is in the target
errs := gperr.NewBuilder("deserialize error") errs := gperr.NewBuilder()
switch dstV.Kind() { switch dstV.Kind() {
case reflect.Struct, reflect.Interface: case reflect.Struct, reflect.Interface:
@ -247,15 +254,15 @@ func Deserialize(src SerializedObject, dst any) (err gperr.Error) {
} }
for k, v := range src { for k, v := range src {
if field, ok := mapping[strutils.ToLowerNoSnake(k)]; ok { if field, ok := mapping[strutils.ToLowerNoSnake(k)]; ok {
err := Convert(reflect.ValueOf(v), field) err := Convert(reflect.ValueOf(v), field, !hasValidateTag)
if err != nil { if err != nil {
errs.Add(err.Subject(k)) errs.Add(err)
} }
} else { } else {
errs.Add(ErrUnknownField.Subject(k).Withf(strutils.DoYouMean(NearestField(k, mapping)))) errs.Add(ErrUnknownField.Subject(k).Withf(strutils.DoYouMean(NearestField(k, mapping))))
} }
} }
if hasValidateTag { if hasValidateTag && checkValidateTag {
errs.Add(ValidateWithFieldTags(dstV.Interface())) errs.Add(ValidateWithFieldTags(dstV.Interface()))
} }
if err := ValidateWithCustomValidator(dstV); err != nil { if err := ValidateWithCustomValidator(dstV); err != nil {
@ -266,7 +273,7 @@ func Deserialize(src SerializedObject, dst any) (err gperr.Error) {
for k, v := range src { for k, v := range src {
mapVT := dstT.Elem() mapVT := dstT.Elem()
tmp := New(mapVT).Elem() tmp := New(mapVT).Elem()
err := Convert(reflect.ValueOf(v), tmp) err := Convert(reflect.ValueOf(v), tmp, true)
if err != nil { if err != nil {
errs.Add(err.Subject(k)) errs.Add(err.Subject(k))
continue continue
@ -302,7 +309,7 @@ func isIntFloat(t reflect.Kind) bool {
// //
// Returns: // Returns:
// - error: the error occurred during conversion, or nil if no error occurred. // - 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() { if !dst.IsValid() {
return gperr.Errorf("convert: dst is %w", ErrNilValue) return gperr.Errorf("convert: dst is %w", ErrNilValue)
} }
@ -371,7 +378,7 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error {
if !ok { if !ok {
return ErrUnsupportedConversion.Subject(dstT.String() + " to " + srcT.String()) 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: case srcKind == reflect.Slice:
if src.Len() == 0 { if src.Len() == 0 {
return nil return nil
@ -384,7 +391,7 @@ func Convert(src reflect.Value, dst reflect.Value) gperr.Error {
i := 0 i := 0
for j, v := range src.Seq2() { for j, v := range src.Seq2() {
tmp := New(dstT.Elem()).Elem() tmp := New(dstT.Elem()).Elem()
err := Convert(v, tmp) err := Convert(v, tmp, checkValidateTag)
if err != nil { if err != nil {
sliceErrs.Add(err.Subjectf("[%d]", j)) sliceErrs.Add(err.Subjectf("[%d]", j))
continue 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))) dst.Set(reflect.MakeSlice(dst.Type(), len(values), len(values)))
errs := gperr.NewBuilder("invalid slice values") errs := gperr.NewBuilder("invalid slice values")
for i, v := range 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 { if err != nil {
errs.Add(err.Subjectf("[%d]", i)) errs.Add(err.Subjectf("[%d]", i))
} }
@ -490,12 +497,12 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr gpe
default: default:
return false, nil 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 { func DeserializeYAML[T any](data []byte, target *T) gperr.Error {
m := make(map[string]any) 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 gperr.Wrap(err)
} }
return Deserialize(m, target) 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) { func DeserializeYAMLMap[V any](data []byte) (_ functional.Map[string, V], err gperr.Error) {
m := make(map[string]any) 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 return
} }
m2 := make(map[string]V, len(m)) m2 := make(map[string]V, len(m))

View file

@ -5,8 +5,8 @@ import (
"strconv" "strconv"
"testing" "testing"
"github.com/goccy/go-yaml"
. "github.com/yusing/go-proxy/internal/utils/testing" . "github.com/yusing/go-proxy/internal/utils/testing"
"gopkg.in/yaml.v3"
) )
func TestDeserialize(t *testing.T) { func TestDeserialize(t *testing.T) {