Module collection (3)
This commit is contained in:
		
							parent
							
								
									53d7e6c217
								
							
						
					
					
						commit
						6637db7be4
					
				
					 3 changed files with 668 additions and 0 deletions
				
			
		
							
								
								
									
										106
									
								
								collection/json/convert.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								collection/json/convert.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
package json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	j "encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	. "gitea.zaclys.com/bvaudour/gob/option"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// JsonDecoder permet de parser une entrée brute en un objet Json ou un tableau de Json.
 | 
			
		||||
type JsonDecoder struct {
 | 
			
		||||
	d *j.Decoder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewJsonDecoder retourne un décodeur pour la donnée d’entrée spécifiée.
 | 
			
		||||
func NewJsonDecoder(r io.Reader) *JsonDecoder { return &JsonDecoder{d: j.NewDecoder(r)} }
 | 
			
		||||
 | 
			
		||||
// Decode retourne un objet Json décodé.
 | 
			
		||||
func (dec *JsonDecoder) Decode() Result[Json] {
 | 
			
		||||
	o := make(Json)
 | 
			
		||||
	if err := dec.d.Decode(&o); err != nil {
 | 
			
		||||
		return Err[Json](err)
 | 
			
		||||
	}
 | 
			
		||||
	return Ok(o)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeSlice retourne un tableau de Json décodé.
 | 
			
		||||
func (dec *JsonDecoder) DecodeSlice() Result[Slice] {
 | 
			
		||||
	var sl Slice
 | 
			
		||||
	if err := dec.d.Decode(&sl); err != nil {
 | 
			
		||||
		return Err[Slice](err)
 | 
			
		||||
	}
 | 
			
		||||
	return Ok(sl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode décode une entrée brute et la fusionne avec l’objet Json.
 | 
			
		||||
func (o Json) Decode(r io.Reader) Result[Json] {
 | 
			
		||||
	dec := NewJsonDecoder(r)
 | 
			
		||||
	result := dec.Decode()
 | 
			
		||||
 | 
			
		||||
	if o2, ok := result.Ok(); ok {
 | 
			
		||||
		o = o.Fusion(o2)
 | 
			
		||||
		return Ok(o)
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode décode une entrée brute et la fusionne avec le tableau de Json.
 | 
			
		||||
func (sl *Slice) Decode(r io.Reader) Result[Slice] {
 | 
			
		||||
	dec := NewJsonDecoder(r)
 | 
			
		||||
	result := dec.DecodeSlice()
 | 
			
		||||
 | 
			
		||||
	if sl2, ok := result.Ok(); ok {
 | 
			
		||||
		return Ok(sl.Add(sl2...))
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JsonEncoder permet de convertir un objet Json ou un tableau de Json au format brut.
 | 
			
		||||
type JsonEncoder struct {
 | 
			
		||||
	e *j.Encoder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewJsonEncoder fournit un encodeur pour la sortie fournie.
 | 
			
		||||
func NewJsonEncoder(w io.Writer) *JsonEncoder { return &JsonEncoder{e: j.NewEncoder(w)} }
 | 
			
		||||
 | 
			
		||||
// Encode encode un objet Json.
 | 
			
		||||
func (enc *JsonEncoder) Encode(o Json) error { return enc.e.Encode(o) }
 | 
			
		||||
 | 
			
		||||
// EncodeSlice encode un tableau Json.
 | 
			
		||||
func (enc *JsonEncoder) EncodeSlice(sl Slice) error { return enc.e.Encode(sl) }
 | 
			
		||||
 | 
			
		||||
// SetEscapeHTML configure l’encodeur pour que les caractère HTML soient échappés si on est vrai.
 | 
			
		||||
// Ainsi, les caractères &, <, et > sont transformés respectivement en \u0026, \u003c et \u003e
 | 
			
		||||
// Cela permet d’éviter des problèmes de sécurité lorsque du html est embarqué dans du Json.
 | 
			
		||||
func (enc *JsonEncoder) SetEscapeHTML(on bool) { enc.e.SetEscapeHTML(on) }
 | 
			
		||||
 | 
			
		||||
// SetIndent définit le préfixe et l’indentation du Json à la sortie.
 | 
			
		||||
// Cela permet de rendre un Json dans un format lisible (ie. non minifié).
 | 
			
		||||
func (enc *JsonEncoder) SetIndent(prefix, indent string) { enc.e.SetIndent(prefix, indent) }
 | 
			
		||||
 | 
			
		||||
// Encode minifie le Json dans la sortie donnée.
 | 
			
		||||
func (o Json) Encode(w io.Writer) error {
 | 
			
		||||
	enc := NewJsonEncoder(w)
 | 
			
		||||
	return enc.Encode(o)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeHuman encode le Json dans un format humainement lisible (indentation de deux espaces).
 | 
			
		||||
func (o Json) EncodeHuman(w io.Writer) error {
 | 
			
		||||
	enc := NewJsonEncoder(w)
 | 
			
		||||
	enc.SetIndent("", "  ")
 | 
			
		||||
	return enc.Encode(o)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encode minifie le tableau de Json dans la sortie donnée.
 | 
			
		||||
func (sl Slice) Encode(w io.Writer) error {
 | 
			
		||||
	enc := NewJsonEncoder(w)
 | 
			
		||||
	return enc.EncodeSlice(sl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeHuman encode le tableau de Json dans un format humainement lisible (indentation de deux espaces).
 | 
			
		||||
func (sl Slice) EncodeHuman(w io.Writer) error {
 | 
			
		||||
	enc := NewJsonEncoder(w)
 | 
			
		||||
	enc.SetIndent("", "  ")
 | 
			
		||||
	return enc.EncodeSlice(sl)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										548
									
								
								collection/json/json.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										548
									
								
								collection/json/json.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,548 @@
 | 
			
		|||
package json
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"gitea.zaclys.com/bvaudour/gob/collection"
 | 
			
		||||
	"gitea.zaclys.com/bvaudour/gob/convert"
 | 
			
		||||
	. "gitea.zaclys.com/bvaudour/gob/option"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrFailedToParse = errors.New("failed to parse")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func isSlice(c convert.Value) bool {
 | 
			
		||||
	return c.Is(convert.Slice)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isMap(c convert.Value) bool {
 | 
			
		||||
	return c.Is(convert.Map) && c.Type().Key().Is(convert.String)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isAssignableIn(v1, v2 convert.Value) bool {
 | 
			
		||||
	return v1.Type().AssignableTo(v2.TypeElem())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func searchSlice(c convert.Value, k string) (out Option[convert.Value]) {
 | 
			
		||||
	i, err := strconv.Atoi(k)
 | 
			
		||||
	if err == nil && i >= 0 && i < c.Len() {
 | 
			
		||||
		out = Some(c.Index(i))
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func searchMap(c convert.Value, k string) (out Option[convert.Value]) {
 | 
			
		||||
	return c.MapIndexIfExists(convert.ValueOf(k))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func search(c convert.Value, k string) (v Option[convert.Value]) {
 | 
			
		||||
	if isSlice(c) {
 | 
			
		||||
		v = searchSlice(c, k)
 | 
			
		||||
	} else if isMap(c) {
 | 
			
		||||
		v = searchMap(c, k)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func searchAll(c any, keys []string) (v Option[any]) {
 | 
			
		||||
	vv := convert.ValueOf(c)
 | 
			
		||||
	var ok bool
 | 
			
		||||
	for _, k := range keys {
 | 
			
		||||
		if vv, ok = search(vv, k).Get(); !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return Some(vv.Interface())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setSlice(c convert.Value, v any, k string) (out Option[convert.Value]) {
 | 
			
		||||
	i, err := strconv.Atoi(k)
 | 
			
		||||
	vv := convert.ValueOf(v)
 | 
			
		||||
	l := c.Len()
 | 
			
		||||
	if err != nil && i >= 0 && i <= l && isAssignableIn(vv, c) {
 | 
			
		||||
		if i < l {
 | 
			
		||||
			c.SetIndex(i, vv)
 | 
			
		||||
			out = Some(c)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = Some(convert.Append(c, vv))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setMap(c convert.Value, v any, k string) (out Option[convert.Value]) {
 | 
			
		||||
	vv := convert.ValueOf(v)
 | 
			
		||||
	if isAssignableIn(vv, c) {
 | 
			
		||||
		c.SetMapIndex(convert.ValueOf(k), vv)
 | 
			
		||||
		out = Some(c)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func set(c convert.Value, v any, k string) (out Option[convert.Value]) {
 | 
			
		||||
	if isSlice(c) {
 | 
			
		||||
		return setSlice(c, v, k)
 | 
			
		||||
	} else if isMap(c) {
 | 
			
		||||
		return setMap(c, v, k)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setAll(c, v any, keys []string) (ok bool) {
 | 
			
		||||
	l := len(keys)
 | 
			
		||||
	k1, k2 := keys[l-2], keys[l-1]
 | 
			
		||||
	cc := convert.ValueOf(c)
 | 
			
		||||
	for _, k := range keys[:l-2] {
 | 
			
		||||
		if cc, ok = search(cc, k).Get(); !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var ccl convert.Value
 | 
			
		||||
	if ccl, ok = search(cc, k1).Get(); ok {
 | 
			
		||||
		if ccl, ok = set(ccl, v, k2).Get(); ok {
 | 
			
		||||
			set(cc, ccl.Interface(), k1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func delSlice(c convert.Value, k string) (out Option[convert.Value]) {
 | 
			
		||||
	i, err := strconv.Atoi(k)
 | 
			
		||||
	l := c.Len()
 | 
			
		||||
	if err != nil && i >= 0 && i < l {
 | 
			
		||||
		v := convert.SliceOf(c.Type(), l-1, l-1)
 | 
			
		||||
		convert.Copy(v.Slice(0, i), c.Slice(0, i))
 | 
			
		||||
		convert.Copy(v.Slice(i, l-1), c.Slice(i+1, l))
 | 
			
		||||
		return Some(v)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func delMap(c convert.Value, k string) (out Option[convert.Value]) {
 | 
			
		||||
	kk := convert.ValueOf(k)
 | 
			
		||||
	if ok := c.MapIndexIfExists(kk).IsDefined(); ok {
 | 
			
		||||
		c.DelMapIndex(kk)
 | 
			
		||||
		out = Some(c)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func del(c convert.Value, k string) (out Option[convert.Value]) {
 | 
			
		||||
	if isSlice(c) {
 | 
			
		||||
		return delSlice(c, k)
 | 
			
		||||
	} else if isMap(c) {
 | 
			
		||||
		return delMap(c, k)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func delAll(c any, keys []string) (ok bool) {
 | 
			
		||||
	l := len(keys)
 | 
			
		||||
	k1, k2 := keys[l-2], keys[l-1]
 | 
			
		||||
	cc := convert.ValueOf(c)
 | 
			
		||||
	for _, k := range keys[:l-2] {
 | 
			
		||||
		if cc, ok = search(cc, k).Get(); !ok {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var ccl convert.Value
 | 
			
		||||
	if ccl, ok = search(cc, k1).Get(); ok {
 | 
			
		||||
		if ccl, ok = del(ccl, k2).Get(); ok {
 | 
			
		||||
			set(cc, ccl.Interface(), k1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func flat(v convert.Value, prefix ...string) (keys [][]string, values []any) {
 | 
			
		||||
	if isSlice(v) {
 | 
			
		||||
		it, l := v.SliceRange(), len(prefix)
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			k := make([]string, l+1)
 | 
			
		||||
			copy(k[:l], prefix)
 | 
			
		||||
			k[l] = strconv.Itoa(it.Index())
 | 
			
		||||
			kk, vv := flat(it.Value(), k...)
 | 
			
		||||
			collection.Add(&keys, kk...)
 | 
			
		||||
			collection.Add(&values, vv...)
 | 
			
		||||
		}
 | 
			
		||||
	} else if isMap(v) {
 | 
			
		||||
		it, l := v.MapRange(), len(prefix)
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			k := make([]string, l+1)
 | 
			
		||||
			copy(k[:l], prefix)
 | 
			
		||||
			k[l] = it.Key().String()
 | 
			
		||||
			kk, vv := flat(it.Value(), k...)
 | 
			
		||||
			collection.Add(&keys, kk...)
 | 
			
		||||
			collection.Add(&values, vv...)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		collection.Add(&keys, prefix)
 | 
			
		||||
		collection.Add(&values, v.Interface())
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createRec(o Json, keys []string) Json {
 | 
			
		||||
	e := o
 | 
			
		||||
	for _, k := range keys {
 | 
			
		||||
		v, ok := o[k]
 | 
			
		||||
		if ok {
 | 
			
		||||
			if m, ok := v.(Json); ok {
 | 
			
		||||
				e = m
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		m := make(Json)
 | 
			
		||||
		e[k] = m
 | 
			
		||||
		e = m
 | 
			
		||||
	}
 | 
			
		||||
	return o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unflat(v convert.Value, split SplitFunc) (out convert.Value) {
 | 
			
		||||
	if isSlice(v) {
 | 
			
		||||
		it := v.SliceRange()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			i, e := it.Index(), unflat(it.Value(), split)
 | 
			
		||||
			v.SetIndex(i, e)
 | 
			
		||||
		}
 | 
			
		||||
		return v
 | 
			
		||||
	} else if isMap(v) {
 | 
			
		||||
		result := make(Json)
 | 
			
		||||
		it := v.MapRange()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			k, e := it.Key().String(), unflat(it.Value(), split)
 | 
			
		||||
			spl := SplitKey(k, split)
 | 
			
		||||
			if keys, ok := spl.Ok(); ok {
 | 
			
		||||
				result = createRec(result, keys)
 | 
			
		||||
				result.SetRecursive(e.Interface(), keys...)
 | 
			
		||||
			} else {
 | 
			
		||||
				result[k] = e.Interface()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return convert.ValueOf(result)
 | 
			
		||||
	}
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sSlice(c convert.Value) convert.Value {
 | 
			
		||||
	it := c.SliceRange()
 | 
			
		||||
	for it.Next() {
 | 
			
		||||
		i, v := it.Index(), slicify(it.Value())
 | 
			
		||||
		c.SetIndex(i, v)
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func canSlice(c convert.Value) (out Option[map[int]string]) {
 | 
			
		||||
	if !isMap(c) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var keys []int
 | 
			
		||||
	skeys := make(map[int]string)
 | 
			
		||||
	it := c.MapRange()
 | 
			
		||||
	for it.Next() {
 | 
			
		||||
		k := it.Key().String()
 | 
			
		||||
		i, err := strconv.Atoi(k)
 | 
			
		||||
		if err != nil || i < 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		collection.Add(&keys, i)
 | 
			
		||||
		skeys[i] = k
 | 
			
		||||
	}
 | 
			
		||||
	sort.Ints(keys)
 | 
			
		||||
	for i, j := range keys {
 | 
			
		||||
		if i != j {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return Some(skeys)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sMap(c convert.Value) convert.Value {
 | 
			
		||||
	if keys, ok := canSlice(c).Get(); ok {
 | 
			
		||||
		sl := convert.SliceOf(convert.SliceTypeOf(c.TypeElem()), len(keys), len(keys))
 | 
			
		||||
		it := sl.SliceRange()
 | 
			
		||||
		for it.Next() {
 | 
			
		||||
			i := it.Index()
 | 
			
		||||
			k := keys[i]
 | 
			
		||||
			v := slicify(c.MapIndex(convert.ValueOf(k)))
 | 
			
		||||
			sl.SetIndex(i, v)
 | 
			
		||||
		}
 | 
			
		||||
		return sl
 | 
			
		||||
	}
 | 
			
		||||
	it := c.MapRange()
 | 
			
		||||
	for it.Next() {
 | 
			
		||||
		k, v := it.Key(), slicify(it.Value())
 | 
			
		||||
		c.SetMapIndex(k, v)
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func slicify(v convert.Value) convert.Value {
 | 
			
		||||
	if isSlice(v) {
 | 
			
		||||
		return sSlice(v)
 | 
			
		||||
	} else if isMap(v) {
 | 
			
		||||
		return sMap(v)
 | 
			
		||||
	}
 | 
			
		||||
	return v
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Json représente un simple objet JSON.
 | 
			
		||||
type Json map[string]any
 | 
			
		||||
 | 
			
		||||
// SplitFunc est une fonction qui découpe une clé en sous-clés.
 | 
			
		||||
//
 | 
			
		||||
// Paramètres :
 | 
			
		||||
// - key : la clé à découper
 | 
			
		||||
//
 | 
			
		||||
// Sortie :
 | 
			
		||||
// - current : la sous-clé parsée
 | 
			
		||||
// - next : la partie de clé qui reste à parser
 | 
			
		||||
// - err : une erreur retournée lors du parsage ou io.EOF si le parsage est terminé
 | 
			
		||||
type SplitFunc func(key string) (current, next string, err error)
 | 
			
		||||
 | 
			
		||||
// MergeFunc est la transformée inverse de SplitFunc.
 | 
			
		||||
type MergeFunc func(keys []string) (key string)
 | 
			
		||||
 | 
			
		||||
// SplitDot découpe une clé qui est sous la forme a.b.c…
 | 
			
		||||
func SplitDot(key string) (current, next string, err error) {
 | 
			
		||||
	i := strings.Index(key, ".")
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		current, err = key, io.EOF
 | 
			
		||||
	} else {
 | 
			
		||||
		current, next = key[:i], key[i+1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SplitBracket découpe une clé qui est sous la forme a[b][c]…
 | 
			
		||||
func SplitBracket(key string) (current, next string, err error) {
 | 
			
		||||
	i0 := strings.Index(key, "[")
 | 
			
		||||
	i1 := strings.Index(key, "]")
 | 
			
		||||
	if i0 < 0 {
 | 
			
		||||
		if i1 >= 0 {
 | 
			
		||||
			err = ErrFailedToParse
 | 
			
		||||
		} else {
 | 
			
		||||
			current, err = key, io.EOF
 | 
			
		||||
		}
 | 
			
		||||
	} else if i0 >= i1 || (len(key) > i1+1 && key[i1+1] != '[') {
 | 
			
		||||
		err = ErrFailedToParse
 | 
			
		||||
	} else {
 | 
			
		||||
		current, next = key[:i0], key[i0+1:i1]+key[i1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SplitKey découpe la clé selon la fonction de découpage fournie.
 | 
			
		||||
func SplitKey(key string, split SplitFunc) (out Result[[]string]) {
 | 
			
		||||
	current, next := key, ""
 | 
			
		||||
	var err error
 | 
			
		||||
	var keys []string
 | 
			
		||||
	for current, next, err = split(current); err == nil; current, next, err = split(current) {
 | 
			
		||||
		collection.Add(&keys, current)
 | 
			
		||||
		current = next
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err == io.EOF {
 | 
			
		||||
		return Ok(keys)
 | 
			
		||||
	}
 | 
			
		||||
	return Err[[]string](err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MergeDot crée une clé sous la forme a.b.c…
 | 
			
		||||
func MergeDot(keys []string) string {
 | 
			
		||||
	return strings.Join(keys, ".")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MergeBracket crée une clé sous la forme a[b][c]…
 | 
			
		||||
func MergeBracket(keys []string) (key string) {
 | 
			
		||||
	if len(keys) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	key, keys = keys[0], keys[1:]
 | 
			
		||||
	for _, k := range keys {
 | 
			
		||||
		key += "[" + k + "]"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Json) get(keys ...string) (v Option[any]) {
 | 
			
		||||
	switch len(keys) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		v = Some[any](o)
 | 
			
		||||
	case 1:
 | 
			
		||||
		v = Some[any](o[keys[0]])
 | 
			
		||||
	default:
 | 
			
		||||
		v = searchAll(o, keys)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get retourne la valeur de la clé.
 | 
			
		||||
// Si une fonction de découpage est fournie, la clé est recherchée de façon récursive après découpage.
 | 
			
		||||
func (o Json) Get(key string, split ...SplitFunc) (value Option[any]) {
 | 
			
		||||
	if len(split) == 0 {
 | 
			
		||||
		return o.get(key)
 | 
			
		||||
	}
 | 
			
		||||
	if keys, ok := SplitKey(key, split[0]).Ok(); ok {
 | 
			
		||||
		return o.get(keys...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetRecursive retourne la valeur de la clé (de façon récursive).
 | 
			
		||||
func (o Json) GetRecursive(keys ...string) (value Option[any]) {
 | 
			
		||||
	return o.get(keys...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse injecte la valeur associée à la clé dans out, si possible,
 | 
			
		||||
// et retourne vrai si l’injection a été possible.
 | 
			
		||||
// out doit être un pointeur.
 | 
			
		||||
// Si split est fourni la clé est découpée.
 | 
			
		||||
func (o Json) Parse(out any, key string, split ...SplitFunc) (ok bool) {
 | 
			
		||||
	var v any
 | 
			
		||||
	if v, ok = o.Get(key, split...).Get(); ok {
 | 
			
		||||
		ok = convert.Convert(out, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseRecurive agit comme Parse, récursivement.
 | 
			
		||||
func (o Json) ParseRecursive(out any, keys ...string) (ok bool) {
 | 
			
		||||
	var v any
 | 
			
		||||
	if v, ok = o.GetRecursive(keys...).Get(); ok {
 | 
			
		||||
		ok = convert.Convert(out, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SafeParse agit comme Parse mais la valeur trouvée doit être compatible avec le type de out.
 | 
			
		||||
func (o Json) SafeParse(out any, key string, split ...SplitFunc) (ok bool) {
 | 
			
		||||
	var v any
 | 
			
		||||
	if v, ok = o.Get(key, split...).Get(); ok {
 | 
			
		||||
		ok = convert.Convert(out, v, true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SafeParseRecursive agit comme SafeParse, récursivement.
 | 
			
		||||
func (o Json) SafeParseRecursive(out any, keys ...string) (ok bool) {
 | 
			
		||||
	var v any
 | 
			
		||||
	if v, ok = o.GetRecursive(keys...).Get(); ok {
 | 
			
		||||
		ok = convert.Convert(out, v, true)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Json) set(value any, keys ...string) (ok bool) {
 | 
			
		||||
	l := len(keys)
 | 
			
		||||
	switch l {
 | 
			
		||||
	case 0:
 | 
			
		||||
	case 1:
 | 
			
		||||
		o[keys[0]], ok = value, true
 | 
			
		||||
	default:
 | 
			
		||||
		ok = setAll(o, value, keys)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set insère la valeur dans l’objet et retourne vrai si la valeur a été insérée.
 | 
			
		||||
// Si la fonction de découpage est fournie, la clé est découpée.
 | 
			
		||||
func (o Json) Set(value any, key string, split ...SplitFunc) (ok bool) {
 | 
			
		||||
	if len(split) == 0 {
 | 
			
		||||
		return o.set(value, key)
 | 
			
		||||
	}
 | 
			
		||||
	var keys []string
 | 
			
		||||
	if keys, ok = SplitKey(key, split[0]).Ok(); ok {
 | 
			
		||||
		return o.set(value, keys...)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRecursive insère la valeur de façon récursive dans l’objet et retourne vrai si elle existe.
 | 
			
		||||
func (o Json) SetRecursive(value any, keys ...string) (ok bool) {
 | 
			
		||||
	return o.set(value, keys...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o Json) del(keys ...string) (ok bool) {
 | 
			
		||||
	l := len(keys)
 | 
			
		||||
	switch l {
 | 
			
		||||
	case 0:
 | 
			
		||||
	case 1:
 | 
			
		||||
		if ok = o.Get(keys[0]).IsDefined(); ok {
 | 
			
		||||
			delete(o, keys[0])
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		ok = delAll(o, keys)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Si la fonction de découpage est fournie, la clé est découpée.
 | 
			
		||||
func (o Json) Del(key string, split ...SplitFunc) (ok bool) {
 | 
			
		||||
	if len(split) == 0 {
 | 
			
		||||
		return o.del(key)
 | 
			
		||||
	}
 | 
			
		||||
	var keys []string
 | 
			
		||||
	if keys, ok = SplitKey(key, split[0]).Ok(); ok {
 | 
			
		||||
		return o.del(keys...)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DelRecursive supprimer la clé de façon récursive dans l’objet et retourne vrai si elle existe.
 | 
			
		||||
func (o Json) DelRecursive(keys ...string) (ok bool) {
 | 
			
		||||
	return o.del(keys...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clone effectue une copie profonde de l’objet.
 | 
			
		||||
func (o Json) Clone(deep ...bool) Json {
 | 
			
		||||
	c := make(Json)
 | 
			
		||||
	for k, v := range o {
 | 
			
		||||
		c.Set(convert.Clone(v, deep...), k)
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fusion ajoute toutes les entrées de o2 dans o1 et retourne o1
 | 
			
		||||
func (o1 Json) Fusion(o2 Json) Json {
 | 
			
		||||
	for k, v := range o2 {
 | 
			
		||||
		o1[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	return o1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Flat aplatit la structure du Json.
 | 
			
		||||
func (o Json) Flat(merge MergeFunc) (out Json) {
 | 
			
		||||
	keys, values := flat(convert.ValueOf(o))
 | 
			
		||||
	out = make(Json)
 | 
			
		||||
	for i, k := range keys {
 | 
			
		||||
		out[merge(k)] = values[i]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unflat est l’opération inverse de Flat.
 | 
			
		||||
func (o Json) Unflat(split SplitFunc) (out Json) {
 | 
			
		||||
	vo := unflat(convert.ValueOf(o), split)
 | 
			
		||||
	out = vo.Interface().(Json)
 | 
			
		||||
 | 
			
		||||
	for k, v := range out {
 | 
			
		||||
		out[k] = slicify(convert.ValueOf(v))
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								collection/json/json_slice.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								collection/json/json_slice.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
package json
 | 
			
		||||
 | 
			
		||||
// Slice est un tableau d’objets JSON.
 | 
			
		||||
type Slice []Json
 | 
			
		||||
 | 
			
		||||
func (sl *Slice) Add(objects ...Json) Slice {
 | 
			
		||||
	*sl = append(*sl, objects...)
 | 
			
		||||
	return *sl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sl *Slice) Insert(objects ...Json) Slice {
 | 
			
		||||
	*sl = append(objects, (*sl)...)
 | 
			
		||||
	return *sl
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue