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 package tea
import ( import (
"bytes"
"fmt" "fmt"
"reflect" "reflect"
"sort"
"strings"
) )
type env struct { type env struct {
@ -10,6 +13,31 @@ type env struct {
parent *env 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 { func mkenv(test Test) *env {
var e *env var e *env
return e.save(test) return e.save(test)
@ -110,6 +138,16 @@ func (e *env) match(dest Test) (*env, error) {
foundWithCorrectValue = make(map[string]bool) 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 { for e := e; e != nil; e = e.parent {
present := make([]reflect.StructField, 0, len(required)) 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 // check that the values in the env match the values that were
// asked for. // asked for.
matched := make(map[string]interface{}) matched := make(map[string]interface{})
wrongVal := make(map[string]bool)
for _, f := range required { for _, f := range required {
fv := destV.FieldByName(f.Name) fv := destV.FieldByName(f.Name)
if fv.Interface() == e.data[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] matched[f.Name] = e.data[f.Name]
} else { } else {
foundWithWrongValue[f.Name] = true foundWithWrongValue[f.Name] = true
wrongVal[f.Name] = true
} }
} }
if len(wrongVal) > 0 {
continue
}
// all required match conditions are met // all required match conditions are met
if len(matched) == len(required) { if len(matched) == len(required) {
if leaf == nil { keep(e)
// 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
}
} }
} else { } else {
// the required fields do not exist in the layer, so this layer // the required fields do not exist in the layer, so this layer
// does not conflict with the match requirement. // does not conflict with the match requirement.
if leaf != nil { if leaf != nil {
// since we have a leaf node, we have found a matching layer, // since we have a leaf node, we have already found a matching
// and since this layer does not conflict, we keep it. // layer, and since this layer does not conflict, we keep it.
last.parent = e keep(e)
last = e
} }
} }
} }

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

Loading…
Cancel
Save