2018-09-18 22:42:38 +02:00
|
|
|
package values
|
|
|
|
|
|
|
|
import (
|
2019-08-21 04:00:15 +02:00
|
|
|
"context"
|
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"
|
2018-10-07 22:55:12 +02:00
|
|
|
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
2019-02-13 19:31:18 +02:00
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
2018-09-18 22:42:38 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
ObjectPredicate = func(value core.Value, key string) bool
|
2019-02-13 19:31:18 +02:00
|
|
|
|
|
|
|
ObjectProperty struct {
|
2018-10-05 21:17:22 +02:00
|
|
|
key string
|
2018-09-18 22:42:38 +02:00
|
|
|
value core.Value
|
|
|
|
}
|
2019-02-13 19:31:18 +02:00
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
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 {
|
2019-02-13 19:31:18 +02:00
|
|
|
return types.Object
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Object) String() string {
|
|
|
|
marshaled, err := t.MarshalJSON()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return "{}"
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(marshaled)
|
|
|
|
}
|
|
|
|
|
2018-10-23 00:24:53 +02:00
|
|
|
// Compare compares the source object with other core.Value
|
|
|
|
// The behavior of the Compare is similar
|
|
|
|
// to the comparison of objects in ArangoDB
|
2019-02-13 19:31:18 +02:00
|
|
|
func (t *Object) Compare(other core.Value) int64 {
|
|
|
|
if other.Type() == t.Type() {
|
2018-10-10 19:04:15 +02:00
|
|
|
other := other.(*Object)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-10 19:04:15 +02:00
|
|
|
if t.Length() == 0 && other.Length() == 0 {
|
2018-09-18 22:42:38 +02:00
|
|
|
return 0
|
|
|
|
}
|
2019-02-13 19:31:18 +02:00
|
|
|
|
2018-10-10 19:04:15 +02:00
|
|
|
if t.Length() < other.Length() {
|
|
|
|
return -1
|
|
|
|
}
|
2019-02-13 19:31:18 +02:00
|
|
|
|
2018-10-10 19:04:15 +02:00
|
|
|
if t.Length() > other.Length() {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2019-02-13 19:31:18 +02:00
|
|
|
var res int64
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2019-03-13 20:51:30 +02:00
|
|
|
tKeys := make([]string, 0, len(t.value))
|
|
|
|
|
|
|
|
for k := range t.value {
|
|
|
|
tKeys = append(tKeys, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
sortedT := sort.StringSlice(tKeys)
|
2018-10-23 00:24:53 +02:00
|
|
|
sortedT.Sort()
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2019-03-13 20:51:30 +02:00
|
|
|
otherKeys := make([]string, 0, other.Length())
|
|
|
|
|
|
|
|
other.ForEach(func(value core.Value, k string) bool {
|
|
|
|
otherKeys = append(otherKeys, k)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
sortedOther := sort.StringSlice(otherKeys)
|
2018-10-23 00:24:53 +02:00
|
|
|
sortedOther.Sort()
|
2018-10-10 19:04:15 +02:00
|
|
|
|
2018-10-23 00:24:53 +02:00
|
|
|
var tVal, otherVal core.Value
|
|
|
|
var tKey, otherKey string
|
|
|
|
|
|
|
|
for i := 0; i < len(t.value) && res == 0; i++ {
|
|
|
|
tKey, otherKey = sortedT[i], sortedOther[i]
|
|
|
|
|
|
|
|
if tKey == otherKey {
|
|
|
|
tVal, _ = t.Get(NewString(tKey))
|
|
|
|
otherVal, _ = other.Get(NewString(tKey))
|
|
|
|
res = tVal.Compare(otherVal)
|
2019-02-13 19:31:18 +02:00
|
|
|
|
2018-10-23 00:24:53 +02:00
|
|
|
continue
|
2018-10-10 19:04:15 +02:00
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-23 00:24:53 +02:00
|
|
|
if tKey < otherKey {
|
|
|
|
res = 1
|
|
|
|
} else {
|
|
|
|
res = -1
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
2019-02-13 19:31:18 +02:00
|
|
|
|
|
|
|
return types.Compare(types.Object, other.Type())
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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-10-12 17:58:08 +02:00
|
|
|
func (t *Object) Copy() core.Value {
|
2018-09-27 17:53:26 +02:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2019-03-13 20:51:30 +02:00
|
|
|
func (t *Object) Keys() []String {
|
|
|
|
keys := make([]String, 0, len(t.value))
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
for k := range t.value {
|
2019-03-13 20:51:30 +02:00
|
|
|
keys = append(keys, NewString(k))
|
|
|
|
}
|
|
|
|
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Object) Values() []core.Value {
|
|
|
|
keys := make([]core.Value, 0, len(t.value))
|
|
|
|
|
|
|
|
for _, v := range t.value {
|
|
|
|
keys = append(keys, v)
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Object) ForEach(predicate ObjectPredicate) {
|
|
|
|
for key, val := range t.value {
|
2019-05-03 23:10:34 +02:00
|
|
|
if !predicate(val, key) {
|
2018-09-18 22:42:38 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-24 00:52:01 +02:00
|
|
|
func (t *Object) MustGet(key String) core.Value {
|
|
|
|
val, _ := t.Get(key)
|
|
|
|
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2018-10-07 22:55:12 +02:00
|
|
|
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 {
|
2018-10-07 22:55:12 +02:00
|
|
|
return val, NewBoolean(found)
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-07 22:55:12 +02:00
|
|
|
return None, NewBoolean(found)
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Object) Set(key String, value core.Value) {
|
2018-10-28 07:45:26 +02:00
|
|
|
if value != nil {
|
2018-09-18 22:42:38 +02:00
|
|
|
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-10-12 17:58:08 +02:00
|
|
|
func (t *Object) Clone() core.Cloneable {
|
|
|
|
cloned := NewObject()
|
|
|
|
|
|
|
|
var value core.Value
|
|
|
|
var keyString String
|
2019-02-13 19:31:18 +02:00
|
|
|
|
2018-10-12 17:58:08 +02:00
|
|
|
for key := range t.value {
|
|
|
|
keyString = NewString(key)
|
|
|
|
value, _ = t.Get(keyString)
|
2019-02-13 19:31:18 +02:00
|
|
|
|
|
|
|
cloneable, ok := value.(core.Cloneable)
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
value = cloneable.Clone()
|
2018-10-12 17:58:08 +02:00
|
|
|
}
|
|
|
|
cloned.Set(keyString, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cloned
|
|
|
|
}
|
2019-08-21 04:00:15 +02:00
|
|
|
|
|
|
|
func (t *Object) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
|
|
|
|
if len(path) == 0 {
|
|
|
|
return None, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if typ := path[0].Type(); typ != types.String {
|
|
|
|
return None, core.TypeError(typ, types.String)
|
|
|
|
}
|
|
|
|
|
|
|
|
first, _ := t.Get(path[0].(String))
|
|
|
|
|
|
|
|
if len(path) == 1 {
|
|
|
|
return first, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
getter, ok := first.(core.Getter)
|
|
|
|
if !ok {
|
|
|
|
return None, core.TypeError(
|
|
|
|
first.Type(),
|
|
|
|
core.NewType("Getter"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return getter.GetIn(ctx, path[1:])
|
|
|
|
}
|