v0.5.0-rc4: initial support for ovh, provider generator implementation update, replaced all interface{} to any

This commit is contained in:
yusing
2024-09-17 12:06:58 +08:00
parent 82f06374f7
commit 21fcceb391
21 changed files with 286 additions and 64 deletions

View File

@@ -2,25 +2,25 @@ package functional
import "sync"
func ForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
func ForEachKey[K comparable, V any](obj map[K]V, do func(K)) {
for k := range obj {
do(k)
}
}
func ForEachValue[K comparable, V interface{}](obj map[K]V, do func(V)) {
func ForEachValue[K comparable, V any](obj map[K]V, do func(V)) {
for _, v := range obj {
do(v)
}
}
func ForEachKV[K comparable, V interface{}](obj map[K]V, do func(K, V)) {
func ForEachKV[K comparable, V any](obj map[K]V, do func(K, V)) {
for k, v := range obj {
do(k, v)
}
}
func ParallelForEach[T interface{}](obj []T, do func(T)) {
func ParallelForEach[T any](obj []T, do func(T)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for _, v := range obj {
@@ -32,7 +32,7 @@ func ParallelForEach[T interface{}](obj []T, do func(T)) {
wg.Wait()
}
func ParallelForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
func ParallelForEachKey[K comparable, V any](obj map[K]V, do func(K)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for k := range obj {
@@ -44,7 +44,7 @@ func ParallelForEachKey[K comparable, V interface{}](obj map[K]V, do func(K)) {
wg.Wait()
}
func ParallelForEachValue[K comparable, V interface{}](obj map[K]V, do func(V)) {
func ParallelForEachValue[K comparable, V any](obj map[K]V, do func(V)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for _, v := range obj {
@@ -56,7 +56,7 @@ func ParallelForEachValue[K comparable, V interface{}](obj map[K]V, do func(V))
wg.Wait()
}
func ParallelForEachKV[K comparable, V interface{}](obj map[K]V, do func(K, V)) {
func ParallelForEachKV[K comparable, V any](obj map[K]V, do func(K, V)) {
var wg sync.WaitGroup
wg.Add(len(obj))
for k, v := range obj {

View File

@@ -9,7 +9,7 @@ import (
E "github.com/yusing/go-proxy/error"
)
type Map[KT comparable, VT interface{}] struct {
type Map[KT comparable, VT any] struct {
m map[KT]VT
defVals map[KT]VT
sync.RWMutex
@@ -22,7 +22,7 @@ type Map[KT comparable, VT interface{}] struct {
//
// Return:
// - *Map[KT, VT]: a pointer to the newly created Map.
func NewMap[KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
func NewMap[KT comparable, VT any](dv ...map[KT]VT) *Map[KT, VT] {
return NewMapFrom(make(map[KT]VT), dv...)
}
@@ -36,7 +36,7 @@ func NewMap[KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
//
// Return:
// - *Map[KT, VT]: a pointer to the newly created Map.
func NewMapOf[M Map[KT, VT], KT comparable, VT interface{}](dv ...map[KT]VT) *Map[KT, VT] {
func NewMapOf[M Map[KT, VT], KT comparable, VT any](dv ...map[KT]VT) *Map[KT, VT] {
return NewMapFrom(make(map[KT]VT), dv...)
}
@@ -48,7 +48,7 @@ func NewMapOf[M Map[KT, VT], KT comparable, VT interface{}](dv ...map[KT]VT) *Ma
//
// Return:
// - *Map[KT, VT]: a pointer to the newly created Map.
func NewMapFrom[KT comparable, VT interface{}](from map[KT]VT, dv ...map[KT]VT) *Map[KT, VT] {
func NewMapFrom[KT comparable, VT any](from map[KT]VT, dv ...map[KT]VT) *Map[KT, VT] {
if len(dv) > 0 {
return &Map[KT, VT]{m: from, defVals: dv[0]}
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"reflect"
"strings"
"github.com/santhosh-tekuri/jsonschema"
E "github.com/yusing/go-proxy/error"
@@ -12,7 +13,7 @@ import (
)
func ValidateYaml(schema *jsonschema.Schema, data []byte) E.NestedError {
var i interface{}
var i any
err := yaml.Unmarshal(data, &i)
if err != nil {
@@ -55,7 +56,7 @@ func TryJsonStringify(o any) string {
return string(b)
}
// Serialize converts the given data into a map[string]interface{} representation.
// Serialize converts the given data into a map[string]any representation.
//
// It uses reflection to inspect the data type and handle different kinds of data.
// For a struct, it extracts the fields using the json tag if present, or the field name if not.
@@ -66,9 +67,9 @@ func TryJsonStringify(o any) string {
// - data: The data to be converted into a map.
//
// Returns:
// - result: The resulting map[string]interface{} representation of the data.
// - result: The resulting map[string]any representation of the data.
// - error: An error if the data type is unsupported or if there is an error during conversion.
func Serialize(data interface{}) (SerializedObject, error) {
func Serialize(data any) (SerializedObject, E.NestedError) {
result := make(map[string]any)
// Use reflection to inspect the data type
@@ -76,7 +77,7 @@ func Serialize(data interface{}) (SerializedObject, error) {
// Check if the value is valid
if !value.IsValid() {
return nil, fmt.Errorf("invalid data")
return nil, E.Invalid("data", fmt.Sprintf("type: %T", data))
}
// Dereference pointers if necessary
@@ -107,7 +108,7 @@ func Serialize(data interface{}) (SerializedObject, error) {
} else if field.Anonymous {
// If the field is an embedded struct, add its fields to the result
fieldMap, err := Serialize(value.Field(i).Interface())
if err != nil {
if err.HasError() {
return nil, err
}
for k, v := range fieldMap {
@@ -118,10 +119,72 @@ func Serialize(data interface{}) (SerializedObject, error) {
}
}
default:
return nil, fmt.Errorf("unsupported type: %s", value.Kind())
// return nil, fmt.Errorf("unsupported type: %s", value.Kind())
return nil, E.Unsupported("type", value.Kind())
}
return result, nil
return result, E.Nil()
}
type SerializedObject map[string]any
func Deserialize(src map[string]any, target any) E.NestedError {
// convert data fields to lower no-snake
// convert target fields to lower
// then check if the field of data is in the target
mapping := make(map[string]string)
t := reflect.TypeOf(target).Elem()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
snakeCaseField := strings.ToLower(field.Name)
mapping[snakeCaseField] = field.Name
}
for k, v := range src {
kCleaned := toLowerNoSnake(k)
if fieldName, ok := mapping[kCleaned]; ok {
prop := reflect.ValueOf(target).Elem().FieldByName(fieldName)
propType := prop.Type()
isPtr := prop.Kind() == reflect.Ptr
if prop.CanSet() {
val := reflect.ValueOf(v)
vType := val.Type()
switch {
case isPtr && vType.ConvertibleTo(propType.Elem()):
ptr := reflect.New(propType.Elem())
ptr.Elem().Set(val.Convert(propType.Elem()))
prop.Set(ptr)
case vType.ConvertibleTo(propType):
prop.Set(val.Convert(propType))
case isPtr:
var vSerialized SerializedObject
vSerialized, ok = v.(SerializedObject)
if !ok {
if vType.ConvertibleTo(reflect.TypeFor[SerializedObject]()) {
vSerialized = val.Convert(reflect.TypeFor[SerializedObject]()).Interface().(SerializedObject)
} else {
return E.Failure(fmt.Sprintf("convert %s (%T) to %s", k, v, reflect.TypeFor[SerializedObject]()))
}
}
propNew := reflect.New(propType.Elem())
err := Deserialize(vSerialized, propNew.Interface())
if err.HasError() {
return E.Failure("set field").With(k).With(err)
}
prop.Set(propNew)
default:
return E.Unsupported("field", k).Extraf("type=%s", propType)
}
} else {
return E.Unsupported("field", k).Extraf("type=%s", propType)
}
} else {
return E.Failure("unknown field").With(k)
}
}
return E.Nil()
}
func toLowerNoSnake(s string) string {
return strings.ToLower(strings.ReplaceAll(s, "_", ""))
}
type SerializedObject = map[string]any

View File

@@ -7,9 +7,16 @@ import (
E "github.com/yusing/go-proxy/error"
)
func ExpectErrNil(t *testing.T, err E.NestedError) {
func ExpectNoError(t *testing.T, err error) {
t.Helper()
if err.HasError() {
var noError bool
switch t := err.(type) {
case E.NestedError:
noError = t.NoError()
default:
noError = err == nil
}
if !noError {
t.Errorf("expected err=nil, got %s", err.Error())
}
}