Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/cmd/test_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func makeSpecsFile(
}

specs := specs_format.Specs{
Schema: "https://raw.githubusercontent.com/formancehq/numscript/main/v1.specs.schema.json",
Schema: specs_format.SchemaURL,
Balances: store.StaticStore.Balances,
Vars: vars,
FeatureFlags: featureFlags_,
Expand Down
14 changes: 7 additions & 7 deletions internal/interpreter/__snapshots__/pretty_print_meta_test.snap
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@

[TestPrettyPrintMeta/renders_plain_values - 1]
| Name  | Value  |
| count | 42 |
| greeting | "hello" |
| Name  | Value |
| count | 42 |
| greeting | hello |
---

[TestPrettyPrintMeta/renders_a_scoped_account_value_in_its_source_form - 1]
| Name  | Value  |
| greeting | "hello" |
| owner | scoped(alice, "reserve") |
[TestPrettyPrintMeta/renders_an_account_value_as_its_bare_name - 1]
| Name  | Value |
| greeting | hello |
| owner | alice |
---
4 changes: 2 additions & 2 deletions internal/interpreter/accounts_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
)

func TestCompareSetAccountsMetadata(t *testing.T) {
x := SetAccountMetadataRow{Account: "a", Key: "k", Value: NewMonetaryInt(1)}
y := SetAccountMetadataRow{Account: "a", Key: "k", Value: NewMonetaryInt(2)}
x := SetAccountMetadataRow{Account: "a", Key: "k", Value: "1"}
y := SetAccountMetadataRow{Account: "a", Key: "k", Value: "2"}

t.Run("equal regardless of order", func(t *testing.T) {
require.True(t, CompareSetAccountsMetadata(
Expand Down
24 changes: 16 additions & 8 deletions internal/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ type InterpreterError interface {
parser.Ranged
}

type Metadata = map[string]Value
// Metadata is the external representation of transaction metadata: keys mapped
// to their rendered value (see MetaString). The runtime keeps the typed values in
// programState.TxMeta and renders them when building the ExecutionResult.
type Metadata = map[string]string

type Posting struct {
Source string `json:"source"`
Expand Down Expand Up @@ -225,7 +228,7 @@ func RunProgram(

res := &ExecutionResult{
Postings: st.Postings,
Metadata: st.TxMeta,
Metadata: st.txMetaToRendered(),
AccountsMetadata: st.SetAccountsMeta.toRows(),
}
return res, nil
Expand All @@ -248,6 +251,16 @@ type programState struct {
CurrentBalanceQuery BalanceQuery
}

// txMetaToRendered renders the typed transaction metadata into the external,
// untyped Metadata form.
func (st *programState) txMetaToRendered() Metadata {
meta := make(Metadata, len(st.TxMeta))
for k, v := range st.TxMeta {
meta[k] = MetaString(v)
}
return meta
}

func (st *programState) pushSender(name AccountAddress, monetary MonetaryInt, color String) {
monetaryBi := big.Int(monetary)

Expand Down Expand Up @@ -1057,10 +1070,5 @@ func PrettyPrintPostings(postings []Posting) string {
}

func PrettyPrintMeta(meta Metadata) string {
m := map[string]string{}
for k, v := range meta {
m[k] = v.String()
}

return utils.CsvPrettyMap("Name", "Value", m)
return utils.CsvPrettyMap("Name", "Value", meta)
}
12 changes: 7 additions & 5 deletions internal/interpreter/pretty_print_meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ import (
"github.com/gkampitakis/go-snaps/snaps"
)

// Metadata holds already-rendered values (see MetaString), so PrettyPrintMeta
// only has the table layout left to get right.
func TestPrettyPrintMeta(t *testing.T) {
t.Run("renders plain values", func(t *testing.T) {
meta := Metadata{
"greeting": String("hello"),
"count": NewMonetaryInt(42),
"greeting": MetaString(String("hello")),
"count": MetaString(NewMonetaryInt(42)),
}

snaps.MatchSnapshot(t, PrettyPrintMeta(meta))
})

t.Run("renders a scoped account value in its source form", func(t *testing.T) {
t.Run("renders an account value as its bare name", func(t *testing.T) {
meta := Metadata{
"greeting": String("hello"),
"owner": AccountAddress{Name: "alice", Scope: "reserve"},
"greeting": MetaString(String("hello")),
"owner": MetaString(AccountAddress{Name: "alice"}),
}

snaps.MatchSnapshot(t, PrettyPrintMeta(meta))
Expand Down
41 changes: 9 additions & 32 deletions internal/interpreter/set_accounts_metadata.go
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
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.
// during execution. The value is carried as the rendered text produced by
// MetaString, matching the input metadata: the wire format is untyped, so a
// value's type is not part of what gets stored or asserted.
type SetAccountMetadataRow struct {
Account string `json:"account"`
Key string `json:"key"`
Value Value `json:"value"`
Value string `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.
// ignoring order but respecting multiplicity (so [x, x] != [x, y]).
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
return r.Account + "\x00" + r.Key + "\x00" + r.Scope + "\x00" + r.Value
}
counts := make(map[string]int, len(a))
for _, r := range a {
Expand All @@ -75,15 +51,16 @@ func (m internalSetAccountsMeta) Set(account, scope, key string, value Value) {
}

// toRows flattens the set metadata into the external representation, sorted by
// (account, scope, key) for deterministic output.
// (account, scope, key) for deterministic output. Values are rendered here: the
// runtime keeps them typed, the wire format does not.
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,
Value: MetaString(value),
})
}
sort.Slice(rows, func(i, j int) bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{
"it": "sets the expected meta",
"expect.txMetadata": [
{ "key": "key", "value": { "type": "portion", "numerator": "11", "denominator": "2" } }
{ "key": "key", "value": "11/2" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"it": "-",
"expect.txMetadata": [
{ "key": "k", "value": { "type": "number", "value": "3" } }
{ "key": "k", "value": "3" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"status": "pending"
},
"expect.txMetadata": [
{ "key": "k", "value": { "type": "account", "name": "acc:42:pending:user:001" } }
{ "key": "k", "value": "acc:42:pending:user:001" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{
"it": "-",
"expect.txMetadata": [
{ "key": "amt", "value": { "type": "number", "value": "100" } }
{ "key": "amt", "value": "100" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{
"it": "-",
"expect.txMetadata": [
{ "key": "asset", "value": { "type": "asset", "name": "ABC" } }
{ "key": "asset", "value": "ABC" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/formancehq/numscript/29a351f9aa5ae03d72b85f55981e52dd57e81c07/specs.schema.json",
"$schema": "https://raw.githubusercontent.com/formancehq/numscript/main/v1.specs.schema.json",
"variables": {
"amt": "100"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
{
"it": "sets metadata scoped by the account's scope, distinct from the unscoped entry",
"expect.metadata": [
{ "account": "acc", "key": "k", "value": { "type": "string", "value": "unscoped" } },
{ "account": "acc", "scope": "myscope", "key": "k", "value": { "type": "string", "value": "scoped" } }
{ "account": "acc", "key": "k", "value": "unscoped" },
{ "account": "acc", "scope": "myscope", "key": "k", "value": "scoped" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
{ "account": "acc", "key": "overridden", "value": "1" }
],
"expect.metadata": [
{ "account": "acc", "key": "new", "value": { "type": "number", "value": "2" } },
{ "account": "acc", "key": "overridden", "value": { "type": "number", "value": "100" } }
{ "account": "acc", "key": "new", "value": "2" },
{ "account": "acc", "key": "overridden", "value": "100" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
{
"it": "-",
"expect.metadata": [
{ "account": "acc", "key": "account", "value": { "type": "account", "name": "acc" } },
{ "account": "acc", "key": "asset", "value": { "type": "asset", "name": "COIN" } },
{ "account": "acc", "key": "num", "value": { "type": "number", "value": "42" } },
{ "account": "acc", "key": "portion", "value": { "type": "portion", "numerator": "2", "denominator": "7" } },
{ "account": "acc", "key": "portion-perc", "value": { "type": "portion", "numerator": "1", "denominator": "100" } },
{ "account": "acc", "key": "str", "value": { "type": "string", "value": "abc" } }
{ "account": "acc", "key": "account", "value": "acc" },
{ "account": "acc", "key": "asset", "value": "COIN" },
{ "account": "acc", "key": "num", "value": "42" },
{ "account": "acc", "key": "portion", "value": "2/7" },
{ "account": "acc", "key": "portion-perc", "value": "1/100" },
{ "account": "acc", "key": "str", "value": "abc" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
{
"it": "-",
"expect.txMetadata": [
{ "key": "account", "value": { "type": "account", "name": "acc" } },
{ "key": "asset", "value": { "type": "asset", "name": "COIN" } },
{ "key": "num", "value": { "type": "number", "value": "42" } },
{ "key": "portion", "value": { "type": "portion", "numerator": "3", "denominator": "25" } },
{ "key": "str", "value": { "type": "string", "value": "abc" } }
{ "key": "account", "value": "acc" },
{ "key": "asset", "value": "COIN" },
{ "key": "num", "value": "42" },
{ "key": "portion", "value": "3/25" },
{ "key": "str", "value": "abc" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"it": "-",
"expect.txMetadata": [
{ "key": "k", "value": { "type": "monetary", "asset": "USD/2", "amount": "7" } }
{ "key": "k", "value": "USD/2 7" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"it": "-",
"expect.txMetadata": [
{ "key": "k", "value": { "type": "number", "value": "9" } }
{ "key": "k", "value": "9" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
}
],
"expect.txMetadata": [
{ "key": "description", "value": { "type": "string", "value": "midnight ride" } },
{ "key": "por", "value": { "type": "portion", "numerator": "21", "denominator": "50" } },
{ "key": "ride", "value": { "type": "number", "value": "1" } }
{ "key": "description", "value": "midnight ride" },
{ "key": "por", "value": "21/50" },
{ "key": "ride", "value": "1" }
]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
}
],
"expect.txMetadata": [
{ "key": "description", "value": { "type": "string", "value": "midnight ride" } },
{ "key": "ride", "value": { "type": "number", "value": "1" } }
{ "key": "description", "value": "midnight ride" },
{ "key": "ride", "value": "1" }
]
}
]
Expand Down
Loading
Loading