github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/object.go (about) 1 package values 2 3 import ( 4 "context" 5 "encoding/binary" 6 "hash/fnv" 7 "sort" 8 9 "github.com/wI2L/jettison" 10 11 "github.com/MontFerret/ferret/pkg/runtime/core" 12 "github.com/MontFerret/ferret/pkg/runtime/values/types" 13 ) 14 15 type ( 16 ObjectPredicate = func(value core.Value, key string) bool 17 18 ObjectProperty struct { 19 key string 20 value core.Value 21 } 22 23 Object struct { 24 value map[string]core.Value 25 } 26 ) 27 28 func NewObjectProperty(name string, value core.Value) *ObjectProperty { 29 return &ObjectProperty{name, value} 30 } 31 32 func NewObject() *Object { 33 return &Object{make(map[string]core.Value)} 34 } 35 36 func NewObjectWith(props ...*ObjectProperty) *Object { 37 obj := NewObject() 38 39 for _, prop := range props { 40 obj.value[prop.key] = prop.value 41 } 42 43 return obj 44 } 45 46 func (t *Object) MarshalJSON() ([]byte, error) { 47 return jettison.MarshalOpts(t.value, jettison.NoHTMLEscaping()) 48 } 49 50 func (t *Object) Type() core.Type { 51 return types.Object 52 } 53 54 func (t *Object) String() string { 55 marshaled, err := t.MarshalJSON() 56 57 if err != nil { 58 return "{}" 59 } 60 61 return string(marshaled) 62 } 63 64 // Compare compares the source object with other core.Value 65 // The behavior of the Compare is similar 66 // to the comparison of objects in ArangoDB 67 func (t *Object) Compare(other core.Value) int64 { 68 if other.Type() == t.Type() { 69 other := other.(*Object) 70 71 if t.Length() == 0 && other.Length() == 0 { 72 return 0 73 } 74 75 if t.Length() < other.Length() { 76 return -1 77 } 78 79 if t.Length() > other.Length() { 80 return 1 81 } 82 83 var res int64 84 85 tKeys := make([]string, 0, len(t.value)) 86 87 for k := range t.value { 88 tKeys = append(tKeys, k) 89 } 90 91 sortedT := sort.StringSlice(tKeys) 92 sortedT.Sort() 93 94 otherKeys := make([]string, 0, other.Length()) 95 96 other.ForEach(func(value core.Value, k string) bool { 97 otherKeys = append(otherKeys, k) 98 return true 99 }) 100 101 sortedOther := sort.StringSlice(otherKeys) 102 sortedOther.Sort() 103 104 var tVal, otherVal core.Value 105 var tKey, otherKey string 106 107 for i := 0; i < len(t.value) && res == 0; i++ { 108 tKey, otherKey = sortedT[i], sortedOther[i] 109 110 if tKey == otherKey { 111 tVal, _ = t.Get(NewString(tKey)) 112 otherVal, _ = other.Get(NewString(tKey)) 113 res = tVal.Compare(otherVal) 114 115 continue 116 } 117 118 if tKey < otherKey { 119 res = 1 120 } else { 121 res = -1 122 } 123 124 break 125 } 126 127 return res 128 } 129 130 return types.Compare(types.Object, other.Type()) 131 } 132 133 func (t *Object) Unwrap() interface{} { 134 obj := make(map[string]interface{}) 135 136 for key, val := range t.value { 137 obj[key] = val.Unwrap() 138 } 139 140 return obj 141 } 142 143 func (t *Object) Hash() uint64 { 144 h := fnv.New64a() 145 146 h.Write([]byte(t.Type().String())) 147 h.Write([]byte(":")) 148 h.Write([]byte("{")) 149 150 keys := make([]string, 0, len(t.value)) 151 152 for key := range t.value { 153 keys = append(keys, key) 154 } 155 156 // order does not really matter 157 // but it will give us a consistent hash sum 158 sort.Strings(keys) 159 endIndex := len(keys) - 1 160 161 for idx, key := range keys { 162 h.Write([]byte(key)) 163 h.Write([]byte(":")) 164 165 el := t.value[key] 166 167 bytes := make([]byte, 8) 168 binary.LittleEndian.PutUint64(bytes, el.Hash()) 169 170 h.Write(bytes) 171 172 if idx != endIndex { 173 h.Write([]byte(",")) 174 } 175 } 176 177 h.Write([]byte("}")) 178 179 return h.Sum64() 180 } 181 182 func (t *Object) Copy() core.Value { 183 c := NewObject() 184 185 for k, v := range t.value { 186 c.Set(NewString(k), v) 187 } 188 189 return c 190 } 191 192 func (t *Object) Length() Int { 193 return Int(len(t.value)) 194 } 195 196 func (t *Object) Keys() []String { 197 keys := make([]String, 0, len(t.value)) 198 199 for k := range t.value { 200 keys = append(keys, NewString(k)) 201 } 202 203 return keys 204 } 205 206 func (t *Object) Values() []core.Value { 207 keys := make([]core.Value, 0, len(t.value)) 208 209 for _, v := range t.value { 210 keys = append(keys, v) 211 } 212 213 return keys 214 } 215 216 func (t *Object) ForEach(predicate ObjectPredicate) { 217 for key, val := range t.value { 218 if !predicate(val, key) { 219 break 220 } 221 } 222 } 223 224 func (t *Object) Find(predicate ObjectPredicate) (core.Value, Boolean) { 225 for idx, val := range t.value { 226 if predicate(val, idx) { 227 return val, True 228 } 229 } 230 231 return None, False 232 } 233 234 func (t *Object) Has(key String) Boolean { 235 _, exists := t.value[string(key)] 236 237 return NewBoolean(exists) 238 } 239 240 func (t *Object) MustGet(key String) core.Value { 241 val, _ := t.Get(key) 242 243 return val 244 } 245 246 func (t *Object) MustGetOr(key String, defaultValue core.Value) core.Value { 247 val, found := t.value[string(key)] 248 249 if found { 250 return val 251 } 252 253 return defaultValue 254 } 255 256 func (t *Object) Get(key String) (core.Value, Boolean) { 257 val, found := t.value[string(key)] 258 259 if found { 260 return val, NewBoolean(found) 261 } 262 263 return None, NewBoolean(found) 264 } 265 266 func (t *Object) Set(key String, value core.Value) { 267 if value != nil { 268 t.value[string(key)] = value 269 } else { 270 t.value[string(key)] = None 271 } 272 } 273 274 func (t *Object) Remove(key String) { 275 delete(t.value, string(key)) 276 } 277 278 func (t *Object) Clone() core.Cloneable { 279 cloned := NewObject() 280 281 var value core.Value 282 var keyString String 283 284 for key := range t.value { 285 keyString = NewString(key) 286 value, _ = t.Get(keyString) 287 288 cloneable, ok := value.(core.Cloneable) 289 290 if ok { 291 value = cloneable.Clone() 292 } 293 cloned.Set(keyString, value) 294 } 295 296 return cloned 297 } 298 299 func (t *Object) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) { 300 if len(path) == 0 { 301 return None, nil 302 } 303 304 segmentIdx := 0 305 first, _ := t.Get(ToString(path[segmentIdx])) 306 307 if len(path) == 1 { 308 return first, nil 309 } 310 311 segmentIdx++ 312 313 if first == None || first == nil { 314 return None, core.NewPathError(core.ErrInvalidPath, segmentIdx) 315 } 316 317 getter, ok := first.(core.Getter) 318 319 if !ok { 320 return GetIn(ctx, first, path[segmentIdx:]) 321 } 322 323 return getter.GetIn(ctx, path[segmentIdx:]) 324 }