1
0
mirror of https://github.com/MontFerret/ferret.git synced 2024-12-16 11:37:36 +02:00
ferret/pkg/runtime/values/object.go

194 lines
3.2 KiB
Go
Raw Normal View History

2018-09-18 22:42:38 +02:00
package values
import (
2018-10-05 21:17:22 +02:00
"encoding/binary"
2018-09-18 22:42:38 +02:00
"encoding/json"
2018-10-05 21:17:22 +02:00
"hash/fnv"
"sort"
"github.com/MontFerret/ferret/pkg/runtime/core"
2018-09-18 22:42:38 +02:00
)
type (
ObjectPredicate = func(value core.Value, key string) bool
ObjectProperty struct {
2018-10-05 21:17:22 +02:00
key string
2018-09-18 22:42:38 +02:00
value core.Value
}
Object struct {
value map[string]core.Value
}
)
func NewObjectProperty(name string, value core.Value) *ObjectProperty {
return &ObjectProperty{name, value}
}
func NewObject() *Object {
return &Object{make(map[string]core.Value)}
}
func NewObjectWith(props ...*ObjectProperty) *Object {
obj := NewObject()
for _, prop := range props {
2018-10-05 21:17:22 +02:00
obj.value[prop.key] = prop.value
2018-09-18 22:42:38 +02:00
}
return obj
}
func (t *Object) MarshalJSON() ([]byte, error) {
return json.Marshal(t.value)
}
func (t *Object) Type() core.Type {
return core.ObjectType
}
func (t *Object) String() string {
marshaled, err := t.MarshalJSON()
if err != nil {
return "{}"
}
return string(marshaled)
}
func (t *Object) Compare(other core.Value) int {
switch other.Type() {
case core.ObjectType:
arr := other.(*Object)
if t.Length() == 0 && arr.Length() == 0 {
return 0
}
var res = 1
for _, val := range t.value {
arr.ForEach(func(otherVal core.Value, key string) bool {
res = val.Compare(otherVal)
return res != -1
})
}
return res
default:
return 1
}
}
func (t *Object) Unwrap() interface{} {
obj := make(map[string]interface{})
for key, val := range t.value {
obj[key] = val.Unwrap()
}
return obj
}
2018-10-05 21:17:22 +02:00
func (t *Object) Hash() uint64 {
h := fnv.New64a()
2018-09-18 22:42:38 +02:00
2018-10-05 21:17:22 +02:00
h.Write([]byte(t.Type().String()))
h.Write([]byte(":"))
h.Write([]byte("{"))
keys := make([]string, 0, len(t.value))
for key := range t.value {
keys = append(keys, key)
2018-09-18 22:42:38 +02:00
}
2018-10-05 21:17:22 +02:00
// order does not really matter
// but it will give us a consistent hash sum
sort.Strings(keys)
endIndex := len(keys) - 1
2018-09-18 22:42:38 +02:00
2018-10-05 21:17:22 +02:00
for idx, key := range keys {
h.Write([]byte(key))
h.Write([]byte(":"))
2018-09-18 22:42:38 +02:00
2018-10-05 21:17:22 +02:00
el := t.value[key]
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, el.Hash())
h.Write(bytes)
if idx != endIndex {
h.Write([]byte(","))
}
2018-09-18 22:42:38 +02:00
}
2018-10-05 21:17:22 +02:00
h.Write([]byte("}"))
return h.Sum64()
2018-09-18 22:42:38 +02:00
}
2018-09-27 17:53:26 +02:00
func (t *Object) Clone() core.Value {
c := NewObject()
for k, v := range t.value {
c.Set(NewString(k), v)
}
return c
}
2018-09-18 22:42:38 +02:00
func (t *Object) Length() Int {
return Int(len(t.value))
}
func (t *Object) Keys() []string {
keys := make([]string, 0, len(t.value))
for k := range t.value {
keys = append(keys, k)
}
return keys
}
func (t *Object) ForEach(predicate ObjectPredicate) {
for key, val := range t.value {
if predicate(val, key) == false {
break
}
}
}
func (t *Object) Get(key String) (core.Value, Boolean) {
2018-09-18 22:42:38 +02:00
val, found := t.value[string(key)]
if found {
return val, NewBoolean(found)
2018-09-18 22:42:38 +02:00
}
return None, NewBoolean(found)
2018-09-18 22:42:38 +02:00
}
func (t *Object) GetIn(path []core.Value) (core.Value, error) {
return GetIn(t, path)
}
func (t *Object) Set(key String, value core.Value) {
if core.IsNil(value) == false {
t.value[string(key)] = value
} else {
t.value[string(key)] = None
}
}
2018-09-27 06:26:56 +02:00
func (t *Object) Remove(key String) {
delete(t.value, string(key))
}
2018-09-18 22:42:38 +02:00
func (t *Object) SetIn(path []core.Value, value core.Value) error {
return SetIn(t, path, value)
}