763 lines
15 KiB
Go
763 lines
15 KiB
Go
|
package convert
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"sort"
|
|||
|
"strconv"
|
|||
|
"unicode/utf8"
|
|||
|
|
|||
|
. "gitea.zaclys.com/bvaudour/gob/option"
|
|||
|
)
|
|||
|
|
|||
|
type BoolType interface {
|
|||
|
~bool
|
|||
|
}
|
|||
|
|
|||
|
type CharType interface {
|
|||
|
~byte | ~rune
|
|||
|
}
|
|||
|
|
|||
|
type StringType interface {
|
|||
|
~string
|
|||
|
}
|
|||
|
|
|||
|
type IntType interface {
|
|||
|
~int | ~int8 | ~int16 | ~int32 | ~int64
|
|||
|
}
|
|||
|
|
|||
|
type UintType interface {
|
|||
|
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
|
|||
|
}
|
|||
|
|
|||
|
type FloatType interface {
|
|||
|
~float32 | ~float64
|
|||
|
}
|
|||
|
|
|||
|
type ComplexType interface {
|
|||
|
~complex64 | ~complex128
|
|||
|
}
|
|||
|
|
|||
|
type IntegerType interface {
|
|||
|
IntType | UintType
|
|||
|
}
|
|||
|
|
|||
|
type RealType interface {
|
|||
|
IntegerType | FloatType
|
|||
|
}
|
|||
|
|
|||
|
type NumberType interface {
|
|||
|
RealType | ComplexType
|
|||
|
}
|
|||
|
|
|||
|
type convFunc = func(Value, Value) bool
|
|||
|
|
|||
|
func isConvertible(t1, t2 Type) bool {
|
|||
|
return t1.AssignableTo(t2) || t1.EquivalentTo(t2)
|
|||
|
}
|
|||
|
|
|||
|
func getConvFunc(t Type) (f Option[convFunc]) {
|
|||
|
switch {
|
|||
|
case t.Is(Bool):
|
|||
|
f = Some(fromBool)
|
|||
|
case t.Is(Char):
|
|||
|
f = Some(fromChar)
|
|||
|
case t.Is(Int):
|
|||
|
f = Some(fromInt)
|
|||
|
case t.Is(Uint):
|
|||
|
f = Some(fromUint)
|
|||
|
case t.Is(Float):
|
|||
|
f = Some(fromFloat)
|
|||
|
case t.Is(Complex):
|
|||
|
f = Some(fromComplex)
|
|||
|
case t.Is(String):
|
|||
|
f = Some(fromString)
|
|||
|
case t.Is(Slice.Or(Array)):
|
|||
|
f = Some(fromSlice)
|
|||
|
case t.IsSet():
|
|||
|
f = Some(fromSet)
|
|||
|
case t.Is(Map):
|
|||
|
f = Some(fromMap)
|
|||
|
case t.Is(Struct):
|
|||
|
f = Some(fromStruct)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func safeConv(src, dest Value) bool {
|
|||
|
ts, td := src.Type(), dest.Type()
|
|||
|
if ts.AssignableTo(td) {
|
|||
|
dest.Set(src)
|
|||
|
return true
|
|||
|
} else if ts.EquivalentTo(td) {
|
|||
|
dest.Set(src.Convert(td))
|
|||
|
return true
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
func unsafeConv(src, dest Value) bool {
|
|||
|
f, ok := getConvFunc(src.Type()).Get()
|
|||
|
return ok && f(src, dest)
|
|||
|
}
|
|||
|
|
|||
|
func conv(src, dest Value, safe ...bool) bool {
|
|||
|
if safeConv(src, dest) {
|
|||
|
return true
|
|||
|
} else if len(safe) == 0 || !safe[0] {
|
|||
|
return unsafeConv(src, dest)
|
|||
|
} else if src.Is(Slice.Or(Array)) && dest.Is(Slice.Or(Array)) {
|
|||
|
return safeConvSlice(src, dest)
|
|||
|
} else if src.Is(Map) && dest.Is(Map) {
|
|||
|
return safeConvMap(src, dest)
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
func safeConvSlice(src, dest Value) (ok bool) {
|
|||
|
l, t := src.Len(), dest.Type()
|
|||
|
isArray := t.Is(Array)
|
|||
|
var v Value
|
|||
|
if isArray {
|
|||
|
if t.Len() != l {
|
|||
|
return
|
|||
|
}
|
|||
|
v = SliceOf(SliceTypeOf(t.Elem()), l, src.Cap())
|
|||
|
} else {
|
|||
|
v = SliceOf(t, l, src.Cap())
|
|||
|
}
|
|||
|
ok = true
|
|||
|
for i := 0; i < l; i++ {
|
|||
|
e1, e2 := src.Index(i), v.Index(i)
|
|||
|
if ok = Convert(e1.Interface(), e2.Pointer().Interface(), true); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
if isArray {
|
|||
|
v = v.Convert(PointerTo(t)).Elem()
|
|||
|
}
|
|||
|
dest.Set(v)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func safeConvMap(src, dest Value) (ok bool) {
|
|||
|
t := dest.Type()
|
|||
|
tk, tv := t.Key(), t.Elem()
|
|||
|
v := MapOf(t)
|
|||
|
keys := src.Keys()
|
|||
|
ok = true
|
|||
|
for _, k1 := range keys {
|
|||
|
k2 := PointerOf(tk)
|
|||
|
if ok = Convert(k1.Interface(), k2.Interface(), true); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
e1, e2 := src.MapIndex(k1), PointerOf(tv)
|
|||
|
if ok = Convert(e1.Interface(), e2.Interface(), true); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
v.SetMapIndex(k2.Elem(), e2.Elem())
|
|||
|
}
|
|||
|
dest.Set(v)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func toSlice(src, dest Value) (ok bool) {
|
|||
|
t := dest.Type()
|
|||
|
r := AddressableOf(t.Elem())
|
|||
|
if ok = conv(src, r); ok {
|
|||
|
sl := SliceOf(t, 1, 1)
|
|||
|
sl.Index(0).Set(r)
|
|||
|
dest.Set(sl)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func toArray(src, dest Value) (ok bool) {
|
|||
|
t := dest.Type()
|
|||
|
tsl := SliceTypeOf(t.Elem())
|
|||
|
sl := AddressableOf(tsl)
|
|||
|
if ok = toSlice(src, sl); ok {
|
|||
|
dest.Set(sl.Convert(t))
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func toSet(src, dest Value) (ok bool) {
|
|||
|
t := dest.Type()
|
|||
|
r := AddressableOf(t.Key())
|
|||
|
if ok = conv(src, r); ok {
|
|||
|
m := MapOf(t)
|
|||
|
m.AddToSet(r)
|
|||
|
dest.Set(m)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromBool(src, dest Value) (ok bool) {
|
|||
|
e := src.Bool()
|
|||
|
var r any
|
|||
|
switch {
|
|||
|
case dest.Is(Bool):
|
|||
|
r, ok = e, true
|
|||
|
case dest.Is(Number):
|
|||
|
r, ok = 0, true
|
|||
|
if e {
|
|||
|
r = 1
|
|||
|
}
|
|||
|
case dest.Is(String):
|
|||
|
r, ok = strconv.FormatBool(e), true
|
|||
|
case dest.Is(Slice):
|
|||
|
return toSlice(src, dest)
|
|||
|
case dest.Is(Array):
|
|||
|
return toArray(src, dest)
|
|||
|
case dest.IsSet():
|
|||
|
return toSet(src, dest)
|
|||
|
}
|
|||
|
if ok {
|
|||
|
v := ValueOf(r).Convert(dest.Type())
|
|||
|
dest.Set(v)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromChar(src, dest Value) (ok bool) {
|
|||
|
e := src.Int()
|
|||
|
var r any
|
|||
|
switch {
|
|||
|
case dest.Is(Bool):
|
|||
|
r, ok = e != 0, true
|
|||
|
case dest.Is(Char):
|
|||
|
r, ok = e, true
|
|||
|
case dest.Is(Number):
|
|||
|
r, ok = e-int64('0'), true
|
|||
|
case dest.Is(String):
|
|||
|
r, ok = fmt.Sprintf("%c", e), true
|
|||
|
case dest.Is(Slice):
|
|||
|
return toSlice(src, dest)
|
|||
|
case dest.Is(Array):
|
|||
|
return toArray(src, dest)
|
|||
|
case dest.IsSet():
|
|||
|
return toSet(src, dest)
|
|||
|
}
|
|||
|
if ok {
|
|||
|
v := ValueOf(r).Convert(dest.Type())
|
|||
|
dest.Set(v)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromString(src, dest Value) (ok bool) {
|
|||
|
e := src.String()
|
|||
|
var r any
|
|||
|
var err error
|
|||
|
switch {
|
|||
|
case dest.Is(Bool):
|
|||
|
r, err = strconv.ParseBool(e)
|
|||
|
ok = err == nil
|
|||
|
case dest.Is(Char):
|
|||
|
if ok = utf8.RuneCountInString(e) == 1; ok {
|
|||
|
r, _ = utf8.DecodeRuneInString(e)
|
|||
|
}
|
|||
|
case dest.Is(Int):
|
|||
|
r, err = strconv.ParseInt(e, 0, 64)
|
|||
|
ok = err == nil
|
|||
|
case dest.Is(Uint):
|
|||
|
r, err = strconv.ParseUint(e, 0, 64)
|
|||
|
ok = err == nil
|
|||
|
case dest.Is(Float):
|
|||
|
r, err = strconv.ParseFloat(e, 64)
|
|||
|
ok = err == nil
|
|||
|
case dest.Is(Complex):
|
|||
|
r, err = strconv.ParseComplex(e, 128)
|
|||
|
ok = err == nil
|
|||
|
case dest.Is(Slice):
|
|||
|
return toSlice(src, dest)
|
|||
|
case dest.Is(Array):
|
|||
|
return toArray(src, dest)
|
|||
|
case dest.IsSet():
|
|||
|
return toSet(src, dest)
|
|||
|
}
|
|||
|
if ok {
|
|||
|
v := ValueOf(r).Convert(dest.Type())
|
|||
|
dest.Set(v)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromNumber[T RealType](src, dest Value, e T, f func(T) any) (ok bool) {
|
|||
|
var r any
|
|||
|
switch {
|
|||
|
case dest.Is(Bool):
|
|||
|
r, ok = e != 0, true
|
|||
|
case dest.Is(Char):
|
|||
|
r, ok = rune(int64(e))+'0', true
|
|||
|
|
|||
|
case dest.Is(Number):
|
|||
|
r, ok = e, true
|
|||
|
case dest.Is(String):
|
|||
|
r, ok = f(e), true
|
|||
|
case dest.Is(Slice):
|
|||
|
return toSlice(src, dest)
|
|||
|
case dest.Is(Array):
|
|||
|
return toArray(src, dest)
|
|||
|
case dest.IsSet():
|
|||
|
return toSet(src, dest)
|
|||
|
}
|
|||
|
if ok {
|
|||
|
v := ValueOf(r).Convert(dest.Type())
|
|||
|
dest.Set(v)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromInt(src, dest Value) (ok bool) {
|
|||
|
e := src.Int()
|
|||
|
return fromNumber(
|
|||
|
src,
|
|||
|
dest,
|
|||
|
e,
|
|||
|
func(n int64) any { return strconv.FormatInt(n, 10) },
|
|||
|
)
|
|||
|
}
|
|||
|
|
|||
|
func fromUint(src, dest Value) (ok bool) {
|
|||
|
e := src.Uint()
|
|||
|
return fromNumber(
|
|||
|
src,
|
|||
|
dest,
|
|||
|
e,
|
|||
|
func(n uint64) any { return strconv.FormatUint(n, 10) },
|
|||
|
)
|
|||
|
}
|
|||
|
|
|||
|
func fromFloat(src, dest Value) (ok bool) {
|
|||
|
e := src.Float()
|
|||
|
return fromNumber(
|
|||
|
src,
|
|||
|
dest,
|
|||
|
e,
|
|||
|
func(n float64) any { return strconv.FormatFloat(n, 'f', -1, 64) },
|
|||
|
)
|
|||
|
}
|
|||
|
|
|||
|
func fromComplex(src, dest Value) (ok bool) {
|
|||
|
e := src.Complex()
|
|||
|
var r any
|
|||
|
switch {
|
|||
|
case dest.Is(Bool):
|
|||
|
r, ok = e != 0, true
|
|||
|
case dest.Is(Char):
|
|||
|
r, ok = rune(real(e))+'0', true
|
|||
|
case dest.Is(Real):
|
|||
|
r, ok = real(e), true
|
|||
|
case dest.Is(Complex):
|
|||
|
r, ok = e, true
|
|||
|
case dest.Is(String):
|
|||
|
r, ok = strconv.FormatComplex(e, 'f', -1, 64), true
|
|||
|
case dest.Is(Slice):
|
|||
|
return toSlice(src, dest)
|
|||
|
case dest.Is(Array):
|
|||
|
return toArray(src, dest)
|
|||
|
case dest.IsSet():
|
|||
|
return toSet(src, dest)
|
|||
|
}
|
|||
|
if ok {
|
|||
|
v := ValueOf(r).Convert(dest.Type())
|
|||
|
dest.Set(v)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromSlice(src, dest Value) (ok bool) {
|
|||
|
t, l := dest.Type(), src.Len()
|
|||
|
var r Value
|
|||
|
switch {
|
|||
|
case dest.Is(Slice):
|
|||
|
r, ok = SliceOf(t, l, src.Cap()), true
|
|||
|
for i := 0; i < l; i++ {
|
|||
|
if ok = conv(src.Index(i, true), r.Index(i)); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
case dest.Is(Array):
|
|||
|
if ok = l == t.Len(); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
tsl := SliceTypeOf(t.Elem())
|
|||
|
r = SliceOf(tsl, l, src.Cap())
|
|||
|
if ok = fromSlice(src, r); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
r = r.Convert(PointerTo(t)).Elem()
|
|||
|
case dest.IsSet():
|
|||
|
tk := t.Key()
|
|||
|
r = MapOf(t)
|
|||
|
for i := 0; i < l; i++ {
|
|||
|
e1, e2 := src.Index(i, true), AddressableOf(tk)
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
r.AddToSet(e2)
|
|||
|
}
|
|||
|
case dest.Is(Map):
|
|||
|
t := dest.Type()
|
|||
|
tk, tv := t.Key(), t.Elem()
|
|||
|
r = MapOf(t)
|
|||
|
for i := 0; i < l; i++ {
|
|||
|
e1, e2 := src.Index(i, true), AddressableOf(tv)
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
k := AddressableOf(tk)
|
|||
|
if ok = conv(ValueOf(i), k); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
r.SetMapIndex(k, e2)
|
|||
|
}
|
|||
|
default:
|
|||
|
if ok = l == 1; ok {
|
|||
|
return conv(src.Index(0, true), dest)
|
|||
|
}
|
|||
|
}
|
|||
|
if ok {
|
|||
|
dest.Set(r)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromSet(src, dest Value) (ok bool) {
|
|||
|
t, keys := dest.Type(), src.Keys()
|
|||
|
var r Value
|
|||
|
switch {
|
|||
|
case dest.Is(Slice):
|
|||
|
r, ok = SliceOf(t, len(keys), len(keys)), true
|
|||
|
for i, k := range keys {
|
|||
|
if ok = conv(src.MapIndex(k, true), r.Index(i)); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
case dest.Is(Array):
|
|||
|
if ok = len(keys) == t.Len(); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
tsl := SliceTypeOf(t.Elem())
|
|||
|
r = SliceOf(tsl, len(keys), len(keys))
|
|||
|
if ok = fromSet(src, r); ok {
|
|||
|
r = r.Convert(PointerTo(t)).Elem()
|
|||
|
}
|
|||
|
case dest.IsSet():
|
|||
|
tk := t.Key()
|
|||
|
r, ok = MapOf(t), true
|
|||
|
for _, k := range keys {
|
|||
|
e1, e2 := src.MapIndex(k, true), AddressableOf(tk)
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
r.AddToSet(e2)
|
|||
|
}
|
|||
|
default:
|
|||
|
if ok = len(keys) == 1; ok {
|
|||
|
ok = conv(src.MapIndex(keys[0], true), dest)
|
|||
|
}
|
|||
|
}
|
|||
|
if ok {
|
|||
|
dest.Set(r)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromMap(src, dest Value) (ok bool) {
|
|||
|
t, keys := dest.Type(), src.Keys()
|
|||
|
var r Value
|
|||
|
switch {
|
|||
|
case dest.Is(Slice):
|
|||
|
idx := make([]int, len(keys))
|
|||
|
ki := make(map[Value]int)
|
|||
|
for i, k := range keys {
|
|||
|
var e int
|
|||
|
if ok = conv(ValueOf(k.Interface()), ValueOf(&e).Elem()); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
ki[k], idx[i] = e, e
|
|||
|
}
|
|||
|
sort.Ints(idx)
|
|||
|
for i, j := range idx {
|
|||
|
if ok = i == j; !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
r, ok = SliceOf(t, len(keys), len(keys)), true
|
|||
|
for _, k := range keys {
|
|||
|
if ok = conv(src.MapIndex(k, true), r.Index(ki[k])); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
case dest.Is(Array):
|
|||
|
if ok = t.Len() != len(keys); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
tsl := SliceTypeOf(t.Elem())
|
|||
|
r = AddressableOf(tsl)
|
|||
|
if ok = fromMap(src, r); ok {
|
|||
|
r = r.Convert(PointerTo(t)).Elem()
|
|||
|
}
|
|||
|
case dest.Is(Map):
|
|||
|
tk, tv := t.Key(), t.Elem()
|
|||
|
r, ok = MapOf(t), true
|
|||
|
for _, k := range keys {
|
|||
|
e1, e2 := src.MapIndex(k, true), AddressableOf(tv)
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
kd := AddressableOf(tk)
|
|||
|
if ok = conv(ValueOf(k.Interface()), kd); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
r.SetMapIndex(kd, e2)
|
|||
|
}
|
|||
|
case dest.Is(Struct):
|
|||
|
r, ok = AddressableOf(t), true
|
|||
|
for _, k := range keys {
|
|||
|
var fs string
|
|||
|
if !Convert(k.Interface(), &fs) {
|
|||
|
continue
|
|||
|
}
|
|||
|
if f, exists := t.FieldByName(fs); !exists || f.PkgPath != "" {
|
|||
|
continue
|
|||
|
}
|
|||
|
e1, e2 := src.MapIndex(k, true), r.FieldByName(fs)
|
|||
|
if !e1.Is(Ptr) && e2.Is(Ptr) {
|
|||
|
e2.Set(PointerOf(e2.TypeElem()))
|
|||
|
e2 = e2.Elem()
|
|||
|
}
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if ok {
|
|||
|
dest.Set(r)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func fromStruct(src, dest Value) (ok bool) {
|
|||
|
ts, t := src.Type(), dest.Type()
|
|||
|
fields := ts.Fields()
|
|||
|
var r Value
|
|||
|
switch {
|
|||
|
case dest.Is(Struct):
|
|||
|
r, ok = AddressableOf(t), true
|
|||
|
for _, f := range fields {
|
|||
|
if _, exists := t.FieldByName(f.Name); !exists {
|
|||
|
continue
|
|||
|
}
|
|||
|
e1, e2 := src.FieldByName(f.Name, true), r.FieldByName(f.Name)
|
|||
|
if !e1.Is(Ptr) && e2.Is(Ptr) {
|
|||
|
e2.Set(PointerOf(e2.TypeElem()))
|
|||
|
e2 = e2.Elem()
|
|||
|
} else if e1.Is(Ptr) && !e2.Is(Ptr) {
|
|||
|
if e1.IsNil() {
|
|||
|
e1 = ZeroOf(e1.Type().Elem())
|
|||
|
} else {
|
|||
|
e1 = e1.Elem()
|
|||
|
}
|
|||
|
}
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
case dest.Is(Map):
|
|||
|
r, ok = MapOf(t), true
|
|||
|
tk, tv := t.Key(), t.Elem()
|
|||
|
for _, f := range fields {
|
|||
|
e1, e2 := src.FieldByName(f.Name, true), AddressableOf(tv)
|
|||
|
if e1.Is(Ptr) && !e2.Is(Ptr) {
|
|||
|
if e1.IsNil() {
|
|||
|
e1 = ZeroOf(e1.Type().Elem())
|
|||
|
} else {
|
|||
|
e1 = e1.Elem()
|
|||
|
}
|
|||
|
}
|
|||
|
if ok = conv(e1, e2); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
k := AddressableOf(tk)
|
|||
|
if ok = conv(ValueOf(f.Name), k); !ok {
|
|||
|
return
|
|||
|
}
|
|||
|
r.SetMapIndex(k, e2)
|
|||
|
}
|
|||
|
}
|
|||
|
if ok {
|
|||
|
dest.Set(r)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// Convert convertit la valeur du paramètre source vers
|
|||
|
// le paramètre destination et retourne true si l’opération
|
|||
|
// s’est effectuée avec succès.
|
|||
|
// La destination doit être un pointeur, afin de pouvoir modifier
|
|||
|
// sa valeur.
|
|||
|
// Si le paramètre optionnel safe est fourni et vaut true,
|
|||
|
// la destination n’est modifiée que si son type est équivalent
|
|||
|
// à celui de la source (par exemple int vers int64, mais
|
|||
|
// pas int vers string).
|
|||
|
func Convert(src, dest any, safe ...bool) bool {
|
|||
|
vs, vd := ValueOf(src), ValueOf(dest)
|
|||
|
if !vd.Is(Ptr) || vd.IsNil() {
|
|||
|
return false
|
|||
|
}
|
|||
|
return conv(vs, vd.Elem(), safe...)
|
|||
|
}
|
|||
|
|
|||
|
func setDefault[T any](def ...T) (out T) {
|
|||
|
if len(def) > 0 {
|
|||
|
out = def[0]
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func toSingle[T BoolType | StringType | NumberType](src any, def ...T) (dest T) {
|
|||
|
if ok := Convert(src, &dest); !ok {
|
|||
|
dest = setDefault(def...)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func ToBool[T BoolType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToChar[T CharType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToInt[T IntType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToUint[T UintType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToFloat[T FloatType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToComplex[T ComplexType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToString[T StringType](src any, def ...T) T {
|
|||
|
return toSingle(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
func ToSlice[T any](src any, def ...T) (dest []T) {
|
|||
|
if ok := Convert(src, &dest); !ok {
|
|||
|
dest = def
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func ToMap[K comparable, V any](src any, def ...map[K]V) (dest map[K]V) {
|
|||
|
if ok := Convert(src, &dest); !ok {
|
|||
|
if len(def) > 0 {
|
|||
|
dest = def[0]
|
|||
|
} else {
|
|||
|
dest = make(map[K]V)
|
|||
|
}
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func ToSet[K comparable](src any, def ...map[K]struct{}) map[K]struct{} {
|
|||
|
return ToMap(src, def...)
|
|||
|
}
|
|||
|
|
|||
|
// SetZero réinitialise la variable pointée
|
|||
|
// par le paramètre d’entrée à sa valeur initiale
|
|||
|
func SetZero(dst any) bool {
|
|||
|
vd := ValueOf(dst)
|
|||
|
if !vd.Is(Ptr) {
|
|||
|
return false
|
|||
|
}
|
|||
|
z := ZeroOf(vd.TypeElem())
|
|||
|
vd.SetElem(z)
|
|||
|
return true
|
|||
|
}
|
|||
|
|
|||
|
func clone(v Value, deep ...bool) Value {
|
|||
|
t := v.Type()
|
|||
|
var c Value
|
|||
|
switch {
|
|||
|
case v.IsNil():
|
|||
|
c = v
|
|||
|
case v.Is(Ptr):
|
|||
|
if len(deep) == 0 || !deep[0] {
|
|||
|
return v
|
|||
|
}
|
|||
|
vc := clone(v.Elem(), deep...)
|
|||
|
c = PointerOf(t.Elem())
|
|||
|
c.SetElem(vc)
|
|||
|
case v.Is(Slice):
|
|||
|
l := v.Len()
|
|||
|
c := SliceOf(t, l, v.Cap())
|
|||
|
for i := 0; i < l; i++ {
|
|||
|
s, d := v.Index(i), c.Index(i)
|
|||
|
d.Set(clone(s, deep...))
|
|||
|
}
|
|||
|
case v.Is(Array):
|
|||
|
l := v.Len()
|
|||
|
tsl := SliceTypeOf(t.Elem())
|
|||
|
c := SliceOf(tsl, l, v.Cap())
|
|||
|
for i := 0; i < l; i++ {
|
|||
|
s, d := v.Index(i), c.Index(i)
|
|||
|
d.Set(clone(s, deep...))
|
|||
|
}
|
|||
|
c = c.Convert(PointerTo(t)).Elem()
|
|||
|
case v.Is(Map):
|
|||
|
c = MapOf(t)
|
|||
|
for _, k := range v.Keys() {
|
|||
|
s := v.MapIndex(k)
|
|||
|
c.SetMapIndex(clone(k, deep...), clone(s, deep...))
|
|||
|
}
|
|||
|
case v.Is(Struct):
|
|||
|
c := ValueOf(t)
|
|||
|
n := t.NumField()
|
|||
|
for i := 0; i < n; i++ {
|
|||
|
f := t.Field(i)
|
|||
|
if f.PkgPath != "" {
|
|||
|
continue
|
|||
|
}
|
|||
|
s, d := v.Field(i), c.Field(i)
|
|||
|
d.Set(clone(s, deep...))
|
|||
|
}
|
|||
|
default:
|
|||
|
c = PointerOf(t)
|
|||
|
Convert(v.Interface(), c.Interface())
|
|||
|
c = c.Elem()
|
|||
|
}
|
|||
|
return c
|
|||
|
}
|
|||
|
|
|||
|
// CloneInterface est l’équivalent rapide de Clone
|
|||
|
// si la valeur de sortie n’a pas besoin d’être typée.
|
|||
|
func CloneInterface(e any, deep ...bool) any {
|
|||
|
c := clone(ValueOf(e), deep...)
|
|||
|
return c.Interface()
|
|||
|
}
|
|||
|
|
|||
|
// Clone retourne une copie de l’élément fournit en paramètre.
|
|||
|
// Si deep est fourni et vaut true, le clonage s’effectue récursivement.
|
|||
|
func Clone[T any](e T, deep ...bool) T {
|
|||
|
c := clone(ValueOf(e), deep...)
|
|||
|
if c.IsNil() {
|
|||
|
return e
|
|||
|
}
|
|||
|
var out T
|
|||
|
v := ValueOf(&out)
|
|||
|
v.SetElem(c)
|
|||
|
return v.Interface().(T)
|
|||
|
}
|