mirror of
https://github.com/hanzo-fi/numscript.git
synced 2026-10-30 20:54:52 +00:00
100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
package interpreter
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sort"
|
|
)
|
|
|
|
// SetAccountMetadataRow is a single piece of account metadata set by the script
|
|
// during execution. Unlike the input metadata (which is opaque and string-valued,
|
|
// since its serialization format isn't always known), the set value's type is
|
|
// known, so it is carried as a typed Value and serialized in the tagged form.
|
|
type SetAccountMetadataRow struct {
|
|
Account string `json:"account"`
|
|
Key string `json:"key"`
|
|
Value Value `json:"value"`
|
|
Scope string `json:"scope,omitempty"`
|
|
}
|
|
|
|
// SetAccountsMetadata is the account metadata produced by the script (the
|
|
// execution result's accountsMeta, and a spec's expect.metadata).
|
|
type SetAccountsMetadata []SetAccountMetadataRow
|
|
|
|
func (r *SetAccountMetadataRow) UnmarshalJSON(data []byte) error {
|
|
var raw struct {
|
|
Account string `json:"account"`
|
|
Key string `json:"key"`
|
|
Value json.RawMessage `json:"value"`
|
|
Scope string `json:"scope"`
|
|
}
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return err
|
|
}
|
|
value, err := ParseTaggedValue(raw.Value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.Account, r.Key, r.Scope, r.Value = raw.Account, raw.Key, raw.Scope, value
|
|
return nil
|
|
}
|
|
|
|
// CompareSetAccountsMetadata reports whether two lists hold the same rows,
|
|
// ignoring order but respecting multiplicity (so [x, x] != [x, y]). Values are
|
|
// compared on their canonical source form.
|
|
func CompareSetAccountsMetadata(a SetAccountsMetadata, b SetAccountsMetadata) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
key := func(r SetAccountMetadataRow) string {
|
|
value := ""
|
|
if r.Value != nil {
|
|
value = r.Value.String()
|
|
}
|
|
return r.Account + "\x00" + r.Key + "\x00" + r.Scope + "\x00" + value
|
|
}
|
|
counts := make(map[string]int, len(a))
|
|
for _, r := range a {
|
|
counts[key(r)]++
|
|
}
|
|
for _, r := range b {
|
|
k := key(r)
|
|
counts[k]--
|
|
if counts[k] < 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// internalSetAccountsMeta is the in-memory store of metadata set during
|
|
// execution, keyed for upserts.
|
|
type internalSetAccountsMeta map[metadataKey]Value
|
|
|
|
func (m internalSetAccountsMeta) Set(account, scope, key string, value Value) {
|
|
m[metadataKey{Account: account, Scope: scope, Key: key}] = value
|
|
}
|
|
|
|
// toRows flattens the set metadata into the external representation, sorted by
|
|
// (account, scope, key) for deterministic output.
|
|
func (m internalSetAccountsMeta) toRows() SetAccountsMetadata {
|
|
rows := make(SetAccountsMetadata, 0, len(m))
|
|
for k, value := range m {
|
|
rows = append(rows, SetAccountMetadataRow{
|
|
Account: k.Account,
|
|
Scope: k.Scope,
|
|
Key: k.Key,
|
|
Value: value,
|
|
})
|
|
}
|
|
sort.Slice(rows, func(i, j int) bool {
|
|
if rows[i].Account != rows[j].Account {
|
|
return rows[i].Account < rows[j].Account
|
|
}
|
|
if rows[i].Scope != rows[j].Scope {
|
|
return rows[i].Scope < rows[j].Scope
|
|
}
|
|
return rows[i].Key < rows[j].Key
|
|
})
|
|
return rows
|
|
}
|