merging no longer mutates the env

g-counter v0.0.5
Jordan Orelli 4 years ago
parent 41d93e23ee
commit e43149ab98

@ -1,8 +1,11 @@
package tea
import (
"bytes"
"fmt"
"reflect"
"sort"
"strings"
)
type env struct {
@ -10,6 +13,31 @@ type env struct {
parent *env
}
func (e *env) String() string {
var buf bytes.Buffer
e.pretty(&buf)
return buf.String()
}
func (e *env) pretty(buf *bytes.Buffer) {
if e.parent != nil {
e.parent.pretty(buf)
buf.WriteRune(' ')
}
var parts []string
for k, v := range e.data {
if s, ok := v.(string); ok {
parts = append(parts, fmt.Sprintf("[%s=%q]", k, s))
} else {
parts = append(parts, fmt.Sprintf("[%s=%v]", k, v))
}
}
sort.Strings(parts)
fmt.Fprintf(buf, "{%s}", strings.Join(parts, ", "))
}
func mkenv(test Test) *env {
var e *env
return e.save(test)
@ -110,6 +138,16 @@ func (e *env) match(dest Test) (*env, error) {
foundWithCorrectValue = make(map[string]bool)
)
keep := func(v *env) {
if leaf == nil {
leaf = &env{data: v.data}
last = leaf
} else {
next := &env{data: v.data, parent: last}
last = next
}
}
for e := e; e != nil; e = e.parent {
present := make([]reflect.StructField, 0, len(required))
@ -131,6 +169,8 @@ func (e *env) match(dest Test) (*env, error) {
// check that the values in the env match the values that were
// asked for.
matched := make(map[string]interface{})
wrongVal := make(map[string]bool)
for _, f := range required {
fv := destV.FieldByName(f.Name)
if fv.Interface() == e.data[f.Name] {
@ -138,32 +178,25 @@ func (e *env) match(dest Test) (*env, error) {
matched[f.Name] = e.data[f.Name]
} else {
foundWithWrongValue[f.Name] = true
wrongVal[f.Name] = true
}
}
if len(wrongVal) > 0 {
continue
}
// all required match conditions are met
if len(matched) == len(required) {
if leaf == nil {
// if this is the first matched layer, it is the leaf of the
// resultant env.
leaf = e
last = leaf
} else {
// otherwise we keep this layer, since it matched our match
// requirements. Another layer already did, but there may
// be other things in the layer we want to keep.
last.parent = e
last = e
}
keep(e)
}
} else {
// the required fields do not exist in the layer, so this layer
// does not conflict with the match requirement.
if leaf != nil {
// since we have a leaf node, we have found a matching layer,
// and since this layer does not conflict, we keep it.
last.parent = e
last = e
// since we have a leaf node, we have already found a matching
// layer, and since this layer does not conflict, we keep it.
keep(e)
}
}
}

@ -280,6 +280,7 @@ func TestMatch(t *testing.T) {
ID: 3,
})
t.Logf("before bob loaded %v", e)
bob := request{Role: "player", Name: "bob"}
if err := e.load(&bob); err != nil {
t.Errorf("failed to load bob: %s", err)
@ -288,6 +289,7 @@ func TestMatch(t *testing.T) {
t.Errorf("expected bob to have ID 3, has %d instead", bob.ID)
}
}
t.Logf("after bob loaded %v", e)
alice := request{Role: "player", Name: "alice"}
if err := e.load(&alice); err != nil {
@ -297,6 +299,7 @@ func TestMatch(t *testing.T) {
t.Errorf("expected alice to have ID 2, has %d instead", alice.ID)
}
}
t.Logf("after alice loaded %v", e)
host := request{Role: "host"}
if err := e.load(&host); err != nil {
@ -308,7 +311,7 @@ func TestMatch(t *testing.T) {
}
})
t.Run("layer-skipping matches", func(t *testing.T) {
t.Run("junk-filtering matches", func(t *testing.T) {
type connect struct {
Passing
Role string `tea:"save"`
@ -324,14 +327,21 @@ func TestMatch(t *testing.T) {
body string
}
type junk struct {
Passing
Fart string `tea:"save"`
}
e := mkenv(connect{
Role: "host",
ID: 1,
})
e = e.save(junk{Fart: "first-junk"})
e = e.save(request{
Role: "host",
body: "one",
})
e = e.save(connect{
Role: "player",
Name: "alice",
@ -343,6 +353,7 @@ func TestMatch(t *testing.T) {
Name: "alice",
ID: 2,
})
e = e.save(Pass)
e = e.save(connect{
Role: "player",
Name: "bob",
@ -353,8 +364,12 @@ func TestMatch(t *testing.T) {
Role: "player",
body: "one",
})
e = e.save(junk{Fart: "second-junk"})
bob := request{Role: "player", Name: "bob"}
alice := request{Role: "player", Name: "alice"}
host := request{Role: "host"}
if err := e.load(&bob); err != nil {
t.Errorf("failed to load bob: %s", err)
} else {
@ -363,8 +378,8 @@ func TestMatch(t *testing.T) {
}
}
alice := request{Role: "player", Name: "alice"}
if err := e.load(&alice); err != nil {
t.Log(e)
t.Errorf("failed to load alice: %s", err)
} else {
if alice.ID != 2 {
@ -372,7 +387,6 @@ func TestMatch(t *testing.T) {
}
}
host := request{Role: "host"}
if err := e.load(&host); err != nil {
t.Errorf("failed to load host: %s", err)
} else {

Loading…
Cancel
Save