507 lines
14 KiB
Go
507 lines
14 KiB
Go
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) }
|