gob/convert/value.go

507 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 dune 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. Cest é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 à lensemble de sortes de types donné.
func (v Value) Is(k Kind) bool {
return is(v, k)
}
// Slice retourne une portion de slice. Cest 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 dune map de type map[any]any
// qui, bien quinitialisé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 nest 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 nest 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 nest 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 dun 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 dune 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 lindex 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 à lensemble 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 nest 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 nest 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 à lensemble 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) }