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