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) }