diff --git a/convert/convert.go b/convert/convert.go new file mode 100644 index 0000000..91a7d91 --- /dev/null +++ b/convert/convert.go @@ -0,0 +1,762 @@ +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) +} diff --git a/convert/value.go b/convert/value.go new file mode 100644 index 0000000..2117c07 --- /dev/null +++ b/convert/value.go @@ -0,0 +1,506 @@ +package convert + +import ( + "reflect" + + . "gitea.zaclys.com/bvaudour/gob/option" +) + +// Kind est un ensemble de reflect.Kind. +// Il permet de faciliter la comparaison de types similaires. +type Kind []reflect.Kind + +func (k Kind) Or(in ...Kind) (out Kind) { + out = append(out, k...) + + for _, e := range in { + out = append(out, e...) + } + + return +} + +var ( + Invalid = Kind{reflect.Invalid} + Bool = Kind{reflect.Bool} + Array = Kind{reflect.Array} + Chan = Kind{reflect.Chan} + Func = Kind{reflect.Func} + Interface = Kind{reflect.Interface} + Map = Kind{reflect.Map} + Ptr = Kind{reflect.Ptr} + Slice = Kind{reflect.Slice} + String = Kind{reflect.String} + Struct = Kind{reflect.Struct} + Byte = Kind{reflect.Uint8} + Rune = Kind{reflect.Int32} + Int = Kind{reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64} + Uint = Kind{reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr} + Integer = Int.Or(Uint) + Float = Kind{reflect.Float32, reflect.Float64} + Complex = Kind{reflect.Complex64, reflect.Complex128} + Real = Integer.Or(Float) + Number = Real.Or(Complex) + Char = Kind{reflect.Uint8, reflect.Int32} + Nillable = Kind{reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface} +) + +var ( + empty struct{} + valueOfEmpty = ValueOf(empty) + typeOfEmpty = TypeOf(empty) +) + +// Kinder est une interface permettant de déterminer une sorte de type. +// Elle est implémentée par reflect.Type et reflect.Value. +type Kinder interface { + Kind() reflect.Kind +} + +// Type fournit des méthodes supplémentaires à reflect.Type dont elle hérite. +type Type struct { + reflect.Type +} + +// Value fournit des méthodes supplémentaires à reflect.Value dont elle hérite. +type Value struct { + reflect.Value +} + +// MapIter est similaire à reflect.MapIter mais pour travailler sur Value. +type MapIter struct { + *reflect.MapIter +} + +func (it MapIter) Key() Value { + return Value{it.MapIter.Key()} +} + +func (it MapIter) Value() Value { + return Value{it.MapIter.Value()} +} + +// SliceIter fournit un itérateur de Value de type Slice. +type SliceIter struct { + sl Value + i int +} + +func (it *SliceIter) Reset() { + it.i = -1 +} + +func (it *SliceIter) Next() bool { + if it.i < it.sl.Len()-1 { + it.i++ + return true + } + return false +} + +func (it *SliceIter) Index() int { + return it.i +} + +func (it *SliceIter) Value() Value { + return it.sl.Index(it.i) +} + +func is(e Kinder, k Kind) (out bool) { + ke := e.Kind() + for _, ki := range k { + if ke == ki { + return true + } + } + + return false +} + +// ValueOf retourne une valeur à partir d’une valeur concrète donnée. +func ValueOf(e any) Value { + return Value{reflect.ValueOf(e)} +} + +// PointerOf retourne une valeur contenant un pointeur du type donné. +// Par exemple, si t représente un type int, la valeur retournée +// embarque une valeur de type *int, initialisée. Cet exemple est équivalent +// à ValueOf(new(int)). +func PointerOf(t Type) Value { + return Value{reflect.New(t.Type)} +} + +// AddressableOf retourne une valeur de type donnée et qui est adressable, +// donc modifiable. C’est équivalent à PointerOf(t).Elem() +func AddressableOf(t Type) Value { + return PointerOf(t).Elem() +} + +// PointerTo retourne un type représentant un pointeur du type donné. +// Par exemple, si t est un type représentant int, le type retourné +// représente *int. +func PointerTo(t Type) Type { + return Type{reflect.PointerTo(t.Type)} +} + +// SliceOf retourne un slice initialisé de type t, longueur l et capacité c. +func SliceOf(t Type, l, c int) Value { + return Value{reflect.MakeSlice(t.Type, l, c)} +} + +// MapOf retourne une map initialisée du type donné. +func MapOf(t Type) Value { + return Value{reflect.MakeMap(t.Type)} +} + +// ZeroOf retourne la valeur zéro du type donné. +func ZeroOf(t Type) Value { + return Value{reflect.Zero(t.Type)} +} + +// SliceTypeOf retourne un type représentation une slice du type donné. +// Par exemple, si t est un type représentant int, le type retourné représente []int. +func SliceTypeOf(t Type) Type { + return Type{reflect.SliceOf(t.Type)} +} + +// MapTypeOf retourne un type représentation une map des types clé et valeur donnés. +// Par exemple, si k est un type représentant un string, et v, un type représentant un int, +// le type retourné représente map[string]int. +func MapTypeOf(k Type, v Type) Type { + return Type{reflect.MapOf(k.Type, v.Type)} +} + +// Copy copie le contenu de dest vers src. +// dest et src doivent être de même type et être des slices ou des arrays. +func Copy(src, dest Value) int { + return reflect.Copy(src.Value, dest.Value) +} + +// Append ajoute les valeurs de x au slice s et retourne le slice résultant. +func Append(s Value, x ...Value) Value { + x2 := make([]reflect.Value, len(x)) + for i, e := range x { + x2[i] = e.Value + } + return Value{reflect.Append(s.Value, x2...)} +} + +// AppendSlice ajoute le slice t au slice s et retourne le slice résultant. +func AppendSlice(s Value, t Value) Value { + return Value{reflect.AppendSlice(s.Value, t.Value)} +} + +// Is vérifie si la valeur a un type appartenant à l’ensemble de sortes de types donné. +func (v Value) Is(k Kind) bool { + return is(v, k) +} + +// Slice retourne une portion de slice. C’est en quelque sorte l’équivalent de v[i:j] +func (v Value) Slice(i, j int) Value { + return Value{v.Value.Slice(i, j)} +} + +// Type retourne le type de la valeur. +func (v Value) Type() Type { + return Type{v.Value.Type()} +} + +// DeepType retourne le type profond de la valeur. +// Généralement, cela est équivalent à v.Type(), sauf dans le cas ou la valeur +// est générée de façon interne, par exemple un élément d’une map de type map[any]any +// qui, bien qu’initialisée est considérée comme une interface mais pourrait contenir +// en vraie une string. +func (v Value) DeepType() Type { + return TypeOf(v.Interface()) +} + +// TypeElem retourne le type d’élément du type de la valeur. +// Par exemple, si v représente un []int, son élément sera de type int. +func (v Value) TypeElem() Type { + return v.Type().Elem() +} + +// IsSet retourne vrai si la valeur a un type de la forme map[T]struct{} +func (v Value) IsSet() bool { + return v.Type().IsSet() +} + +// IsZero retourne vrai si la valeur est le zéro de son type. +// Contrairement à la méthode équivalente de reflect.Value, elle ne panique jamais. +func (v Value) IsZero() bool { + return !v.Is(Invalid) && v.Value.IsZero() +} + +// IsNil retourne vrai si la valeur est nulle. +// Contrairement à la méthode équivalente de reflect.Value, elle ne panique jamais. +func (v Value) IsNil() bool { + return v.Is(Invalid) || (v.Is(Nillable) && v.Value.IsNil()) +} + +// Zero retourne le zéro du type de la valeur. +func (v Value) Zero() Value { + if v.IsZero() { + return v + } + return v.Type().Zero() +} + +// Pointer retourne un pointeur vers la valeur. +// Si la valeur n’est pas adressable, elle retourne +// un pointeur initialisé vers une valeur du type de la valeur. +func (v Value) Pointer() Value { + if v.CanAddr() { + return Value{v.Addr()} + } + return v.Type().Pointer() +} + +// Elem retourne l’élément pointé par la valeur. +// Tout comme reflect.Value, elle panique si v n’est pas une interface ou un pointeur. +func (v Value) Elem() Value { + return Value{v.Value.Elem()} +} + +// Set modifie la valeur par la valeur donnée. +func (v Value) Set(e Value) { + v.Value.Set(e.Value) +} + +// SetVal est équivalent à Set mais avec une valeur concrète. +func (v Value) SetVal(e any) { + v.Set(ValueOf(e)) +} + +// SetElem modifie la valeur de la variable pointée par la valeur. +func (v Value) SetElem(e Value) { + v.Elem().Set(e) +} + +// SetElemVal est équivalent à SetElem mais avec une valeur concrète. +func (v Value) SetElemVal(e any) { + v.Elem().SetVal(e) +} + +// Convert retourne la valeur convertie au type donné. +// La valeur n’est pas modifiée. +func (v Value) Convert(t Type) Value { + return Value{v.Value.Convert(t.Type)} +} + +func deepValue(v reflect.Value, deep []bool) Value { + if len(deep) > 0 && deep[0] { + return ValueOf(v.Interface()) + } + return Value{v} +} + +// Index retourne la ième valeur d’un tableau. Si deep est fourni +// et est vrai, il force le retypage profond. +// Par exemple, si v représente []any{5}, v.Index(0) retourne +// la valeur 5 de type interface, mais v.Index(0, true) retourne +// la valeur 5 de type int. +func (v Value) Index(i int, deep ...bool) Value { + return deepValue(v.Value.Index(i), deep) +} + +// SliceRange retourne un itérateur de slice. +func (v Value) SliceRange() *SliceIter { + if !v.Is(Slice) { + panic("not a slice") + } + return &SliceIter{ + sl: v, + i: -1, + } +} + +// AddToSet ajoute une valeur dans le set de valeurs représenté par v. +// v doit donc être du type de la forme map[T]struct{} +func (v Value) AddToSet(e Value) { v.SetMapIndex(e, valueOfEmpty) } + +// Keys retourne les clés de la valeur. La valeur doit être une map. +func (v Value) Keys() []Value { + keys := v.MapKeys() + out := make([]Value, len(keys)) + for i, k := range keys { + out[i].Value = k + } + return out +} + +// MapIndex retourne la kème valeur d’une map. Si deep est fourni +// et est vrai, il force le retypage profond. +// Par exemple, si v représente map[string]any{"cinq": 5}, v.MapIndex(ValueOf("cinq")) retourne +// la valeur 5 de type interface, mais v.MapIndex(ValueOf("cinq"), true) retourne +// la valeur 5 de type int. +func (v Value) MapIndex(k Value, deep ...bool) Value { + return deepValue(v.Value.MapIndex(k.Value), deep) +} + +// MapRange retourne un itérateur de map. +func (v Value) MapRange() MapIter { + return MapIter{v.Value.MapRange()} +} + +// Equal retourne vrai si les valeurs sont égales. +func (v1 Value) Equal(v2 Value) bool { + return v1.Value.Equal(v2.Value) +} + +// MapIndexIfExists agit comme MapIndex mais indique aussi si la valeur existe. +func (v Value) MapIndexIfExists(k Value) (out Option[Value]) { + r := v.MapRange() + for r.Next() { + if exists := k.Equal(r.Key()); exists { + out = Some(r.Value()) + break + } + } + + return +} + +// SetMapIndex associe value à la clé key dans la map. +func (v Value) SetMapIndex(key, value Value) { + if vv, ok := v.MapIndexIfExists(key).Get(); ok { + vv.Set(value) + } +} + +// DelMapIndex supprime la clé de la map. +func (v Value) DelMapIndex(key Value) { + v.Value.SetMapIndex(key.Value, ZeroOf(v.TypeElem()).Value) +} + +// SetIndex modifie la valeur de l’index du slice par la valeur fournie. +func (v Value) SetIndex(i int, value Value) { + v.Index(i).Set(value) +} + +// FieldByName retourne la valeur du champ nommé n. v doit être une structure. +// Si deep est vrai, force le typage interne. +func (v Value) FieldByName(n string, deep ...bool) Value { + return deepValue(v.Value.FieldByName(n), deep) +} + +// Field retourne la valeur du iè champ. v doit être une structure. +// Si deep est vrai, force le typage interne. +func (v Value) Field(i int, deep ...bool) Value { + return deepValue(v.Value.Field(i), deep) +} + +// Fields retourne la liste des champs. v doit être une structure. +func (v Value) Fields() []reflect.StructField { + return v.Type().Fields() +} + +// TypeOf retourne le type de la valeur donnée. +func TypeOf(e any) Type { + return Type{reflect.TypeOf(e)} +} + +// Is vérifie si la valeur a un type appartenant à l’ensemble de sortes de types donné. +func (t Type) Is(k Kind) bool { + return is(t, k) +} + +// IsSet retourne vrai si la valeur a un type de la forme map[T]struct{} +func (t Type) IsSet() bool { + return t.Is(Map) && typeOfEmpty.AssignableTo(t.Elem()) && t.Elem().AssignableTo(typeOfEmpty) +} + +// Elem retourne l’élément pointé par la valeur. +// Tout comme reflect.Type, elle panique si t n’est pas un pointeur, un array, un slice, une map ou un chan. +func (t Type) Elem() Type { + return Type{t.Type.Elem()} +} + +// Key retourne le type de la clé. Elle panique si t n’est pas une map. +func (t Type) Key() Type { + return Type{t.Type.Key()} +} + +// AssignableTo se comporte comme la méthode équivalente de reflect.Type. +func (t Type) AssignableTo(e Type) bool { + return t.Type.AssignableTo(e.Type) +} + +// ComvertibleTo se comporte comme la méthode équivalente de reflect.Type. +func (t Type) ConvertibleTo(e Type) bool { + return t.Type.ConvertibleTo(e.Type) +} + +// EquivalentTo vérifie la convertibilité dans les deux sens. +// Par exemple un int64 est équivalent à un int mais pas une interface. +func (t Type) EquivalentTo(e Type) bool { + return t.ConvertibleTo(e) && e.ConvertibleTo(t) +} + +// Zero retourne la valeur zéro du type. +func (t Type) Zero() Value { + return ZeroOf(t) +} + +// Pointer retourne un pointeur initialisé vers une valeur du type. +func (t Type) Pointer() Value { + return PointerOf(t) +} + +// Fields retourne la liste des champs. t doit être une structure. +func (t Type) Fields() []reflect.StructField { + l := t.NumField() + fields := make([]reflect.StructField, 0, l) + for i := 0; i < l; i++ { + f := t.Field(i) + if f.PkgPath == "" { + fields = append(fields, f) + } + } + return fields +} + +// MapFields retourne la liste des champs indexés par leurs noms. t doit être une structure. +func (t Type) MapFields() map[string]reflect.StructField { + l := t.NumField() + fields := make(map[string]reflect.StructField) + for i := 0; i < l; i++ { + f := t.Field(i) + if f.PkgPath == "" { + fields[f.Name] = f + } + } + return fields +} + +// Is vérifie si la valeur e a un type appartenant à l’ensemble de sortes de types donné. +func Is(e any, k Kind) bool { + return TypeOf(e).Is(k) +} + +func IsZero(e any) bool { return ValueOf(e).IsZero() } +func IsNil(e any) bool { return ValueOf(e).IsNil() } + +func IsInvalid(e any) bool { return Is(e, Invalid) } +func IsBool(e any) bool { return Is(e, Bool) } +func IsArray(e any) bool { return Is(e, Array) } +func IsChan(e any) bool { return Is(e, Chan) } +func IsFunc(e any) bool { return Is(e, Func) } +func IsInterface(e any) bool { return Is(e, Interface) } +func IsMap(e any) bool { return Is(e, Map) } +func IsSet(e any) bool { return TypeOf(e).IsSet() } +func IsPointer(e any) bool { return Is(e, Ptr) } +func IsSlice(e any) bool { return Is(e, Slice) } +func IsString(e any) bool { return Is(e, String) } +func IsStruct(e any) bool { return Is(e, Struct) } +func IsByte(e any) bool { return Is(e, Byte) } +func IsRune(e any) bool { return Is(e, Rune) } +func IsInt(e any) bool { return Is(e, Int) } +func IsUint(e any) bool { return Is(e, Uint) } +func IsInteger(e any) bool { return Is(e, Integer) } +func IsFloat(e any) bool { return Is(e, Float) } +func IsComplex(e any) bool { return Is(e, Complex) } +func IsReal(e any) bool { return Is(e, Real) } +func IsNumber(e any) bool { return Is(e, Number) } +func IsChar(e any) bool { return Is(e, Char) } +func IsNillable(e any) bool { return Is(e, Nillable) } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..551d1b3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitea.zaclys.com/bvaudour/gob + +go 1.21.1