<commit_before>// Copyright 2021 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package executor

import (
	"fmt"
	"hash"
	"hash/fnv"
	"testing"

	"github.com/pingcap/tidb/parser/mysql"
	"github.com/pingcap/tidb/types"
	"github.com/pingcap/tidb/types/json"
	"github.com/pingcap/tidb/util/chunk"
	"github.com/pingcap/tidb/util/memory"
	"github.com/pingcap/tidb/util/mock"
	"github.com/stretchr/testify/require"
)

func initBuildChunk(numRows int) (*chunk.Chunk, []*types.FieldType) {
	numCols := 6
	colTypes := make([]*types.FieldType, 0, numCols)
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeVarchar})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeVarchar})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeNewDecimal})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeJSON})

	oldChk := chunk.NewChunkWithCapacity(colTypes, numRows)
	for i := 0; i < numRows; i++ {
		str := fmt.Sprintf("%d.12345", i)
		oldChk.AppendNull(0)
		oldChk.AppendInt64(1, int64(i))
		oldChk.AppendString(2, str)
		oldChk.AppendString(3, str)
		oldChk.AppendMyDecimal(4, types.NewDecFromStringForTest(str))
		oldChk.AppendJSON(5, json.CreateBinary(str))
	}
	return oldChk, colTypes
}

func initProbeChunk(numRows int) (*chunk.Chunk, []*types.FieldType) {
	numCols := 3
	colTypes := make([]*types.FieldType, 0, numCols)
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeVarchar})

	oldChk := chunk.NewChunkWithCapacity(colTypes, numRows)
	for i := 0; i < numRows; i++ {
		str := fmt.Sprintf("%d.12345", i)
		oldChk.AppendNull(0)
		oldChk.AppendInt64(1, int64(i))
		oldChk.AppendString(2, str)
	}
	return oldChk, colTypes
}

type hashCollision struct {
	count int
}

func (h *hashCollision) Sum64() uint64 {
	h.count++
	return 0
}
func (h hashCollision) Write(p []byte) (n int, err error) { return len(p), nil }
func (h hashCollision) Reset()                            {}
func (h hashCollision) Sum(b []byte) []byte               { panic("not implemented") }
func (h hashCollision) Size() int                         { panic("not implemented") }
func (h hashCollision) BlockSize() int                    { panic("not implemented") }

func TestHashRowContainer(t *testing.T) {
	hashFunc := fnv.New64
	rowContainer, copiedRC := testHashRowContainer(t, hashFunc, false)
	require.Equal(t, int64(0), rowContainer.stat.probeCollision)
	// On windows time.Now() is imprecise, the elapse time may equal 0
	require.True(t, rowContainer.stat.buildTableElapse >= 0)
	require.Equal(t, rowContainer.stat.probeCollision, copiedRC.stat.probeCollision)
	require.Equal(t, rowContainer.stat.buildTableElapse, copiedRC.stat.buildTableElapse)

	rowContainer, copiedRC = testHashRowContainer(t, hashFunc, true)
	require.Equal(t, int64(0), rowContainer.stat.probeCollision)
	require.True(t, rowContainer.stat.buildTableElapse >= 0)
	require.Equal(t, rowContainer.stat.probeCollision, copiedRC.stat.probeCollision)
	require.Equal(t, rowContainer.stat.buildTableElapse, copiedRC.stat.buildTableElapse)

	h := &hashCollision{count: 0}
	hashFuncCollision := func() hash.Hash64 {
		return h
	}
	rowContainer, copiedRC = testHashRowContainer(t, hashFuncCollision, false)
	require.True(t, h.count > 0)
	require.True(t, rowContainer.stat.probeCollision > int64(0))
	require.True(t, rowContainer.stat.buildTableElapse >= 0)
	require.Equal(t, rowContainer.stat.probeCollision, copiedRC.stat.probeCollision)
	require.Equal(t, rowContainer.stat.buildTableElapse, copiedRC.stat.buildTableElapse)
}

func testHashRowContainer(t *testing.T, hashFunc func() hash.Hash64, spill bool) (originRC, copiedRC *hashRowContainer) {
	sctx := mock.NewContext()
	var err error
	numRows := 10

	chk0, colTypes := initBuildChunk(numRows)
	chk1, _ := initBuildChunk(numRows)

	hCtx := &hashContext{
		allTypes:  colTypes,
		keyColIdx: []int{1, 2},
	}
	hCtx.hasNull = make([]bool, numRows)
	for i := 0; i < numRows; i++ {
		hCtx.hashVals = append(hCtx.hashVals, hashFunc())
	}
	rowContainer := newHashRowContainer(sctx, 0, hCtx, hCtx.allTypes)
	copiedRC = rowContainer.ShallowCopy()
	tracker := rowContainer.GetMemTracker()
	tracker.SetLabel(memory.LabelForBuildSideResult)
	if spill {
		tracker.SetBytesLimit(1)
		rowContainer.rowContainer.ActionSpillForTest().Action(tracker)
	}
	err = rowContainer.PutChunk(chk0, nil)
	require.NoError(t, err)
	err = rowContainer.PutChunk(chk1, nil)
	require.NoError(t, err)
	rowContainer.ActionSpill().(*chunk.SpillDiskAction).WaitForTest()
	require.Equal(t, spill, rowContainer.alreadySpilledSafeForTest())
	require.Equal(t, spill, rowContainer.GetMemTracker().BytesConsumed() == 0)
	require.Equal(t, !spill, rowContainer.GetMemTracker().BytesConsumed() > 0)
	if rowContainer.alreadySpilledSafeForTest() {
		require.NotNil(t, rowContainer.GetDiskTracker())
		require.True(t, rowContainer.GetDiskTracker().BytesConsumed() > 0)
	}

	probeChk, probeColType := initProbeChunk(2)
	probeRow := probeChk.GetRow(1)
	probeCtx := &hashContext{
		allTypes:  probeColType,
		keyColIdx: []int{1, 2},
	}
	probeCtx.hasNull = make([]bool, 1)
	probeCtx.hashVals = append(hCtx.hashVals, hashFunc())
	matched, _, err := rowContainer.GetMatchedRowsAndPtrs(hCtx.hashVals[1].Sum64(), probeRow, probeCtx)
	require.NoError(t, err)
	require.Equal(t, 2, len(matched))
	require.Equal(t, chk0.GetRow(1).GetDatumRow(colTypes), matched[0].GetDatumRow(colTypes))
	require.Equal(t, chk1.GetRow(1).GetDatumRow(colTypes), matched[1].GetDatumRow(colTypes))
	return rowContainer, copiedRC
}
<commit_msg>executor: fix test fail in TestHashRowContainer (#33895)<commit_after>// Copyright 2021 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package executor

import (
	"fmt"
	"hash"
	"hash/fnv"
	"testing"

	"github.com/pingcap/tidb/parser/mysql"
	"github.com/pingcap/tidb/types"
	"github.com/pingcap/tidb/types/json"
	"github.com/pingcap/tidb/util/chunk"
	"github.com/pingcap/tidb/util/memory"
	"github.com/pingcap/tidb/util/mock"
	"github.com/stretchr/testify/require"
)

func initBuildChunk(numRows int) (*chunk.Chunk, []*types.FieldType) {
	numCols := 6
	colTypes := make([]*types.FieldType, 0, numCols)
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeVarchar})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeVarchar})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeNewDecimal})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeJSON})

	oldChk := chunk.NewChunkWithCapacity(colTypes, numRows)
	for i := 0; i < numRows; i++ {
		str := fmt.Sprintf("%d.12345", i)
		oldChk.AppendNull(0)
		oldChk.AppendInt64(1, int64(i))
		oldChk.AppendString(2, str)
		oldChk.AppendString(3, str)
		oldChk.AppendMyDecimal(4, types.NewDecFromStringForTest(str))
		oldChk.AppendJSON(5, json.CreateBinary(str))
	}
	return oldChk, colTypes
}

func initProbeChunk(numRows int) (*chunk.Chunk, []*types.FieldType) {
	numCols := 3
	colTypes := make([]*types.FieldType, 0, numCols)
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeLonglong})
	colTypes = append(colTypes, &types.FieldType{Tp: mysql.TypeVarchar})

	oldChk := chunk.NewChunkWithCapacity(colTypes, numRows)
	for i := 0; i < numRows; i++ {
		str := fmt.Sprintf("%d.12345", i)
		oldChk.AppendNull(0)
		oldChk.AppendInt64(1, int64(i))
		oldChk.AppendString(2, str)
	}
	return oldChk, colTypes
}

type hashCollision struct {
	count int
}

func (h *hashCollision) Sum64() uint64 {
	h.count++
	return 0
}
func (h hashCollision) Write(p []byte) (n int, err error) { return len(p), nil }
func (h hashCollision) Reset()                            {}
func (h hashCollision) Sum(b []byte) []byte               { panic("not implemented") }
func (h hashCollision) Size() int                         { panic("not implemented") }
func (h hashCollision) BlockSize() int                    { panic("not implemented") }

func TestHashRowContainer(t *testing.T) {
	hashFunc := fnv.New64
	rowContainer, copiedRC := testHashRowContainer(t, hashFunc, false)
	require.Equal(t, int64(0), rowContainer.stat.probeCollision)
	// On windows time.Now() is imprecise, the elapse time may equal 0
	require.True(t, rowContainer.stat.buildTableElapse >= 0)
	require.Equal(t, rowContainer.stat.probeCollision, copiedRC.stat.probeCollision)
	require.Equal(t, rowContainer.stat.buildTableElapse, copiedRC.stat.buildTableElapse)

	rowContainer, copiedRC = testHashRowContainer(t, hashFunc, true)
	require.Equal(t, int64(0), rowContainer.stat.probeCollision)
	require.True(t, rowContainer.stat.buildTableElapse >= 0)
	require.Equal(t, rowContainer.stat.probeCollision, copiedRC.stat.probeCollision)
	require.Equal(t, rowContainer.stat.buildTableElapse, copiedRC.stat.buildTableElapse)

	h := &hashCollision{count: 0}
	hashFuncCollision := func() hash.Hash64 {
		return h
	}
	rowContainer, copiedRC = testHashRowContainer(t, hashFuncCollision, false)
	require.True(t, h.count > 0)
	require.True(t, rowContainer.stat.probeCollision > int64(0))
	require.True(t, rowContainer.stat.buildTableElapse >= 0)
	require.Equal(t, rowContainer.stat.probeCollision, copiedRC.stat.probeCollision)
	require.Equal(t, rowContainer.stat.buildTableElapse, copiedRC.stat.buildTableElapse)
}

func testHashRowContainer(t *testing.T, hashFunc func() hash.Hash64, spill bool) (originRC, copiedRC *hashRowContainer) {
	sctx := mock.NewContext()
	var err error
	numRows := 10

	chk0, colTypes := initBuildChunk(numRows)
	chk1, _ := initBuildChunk(numRows)

	hCtx := &hashContext{
		allTypes:  colTypes[1:3],
		keyColIdx: []int{1, 2},
	}
	hCtx.hasNull = make([]bool, numRows)
	for i := 0; i < numRows; i++ {
		hCtx.hashVals = append(hCtx.hashVals, hashFunc())
	}
	rowContainer := newHashRowContainer(sctx, 0, hCtx, colTypes)
	copiedRC = rowContainer.ShallowCopy()
	tracker := rowContainer.GetMemTracker()
	tracker.SetLabel(memory.LabelForBuildSideResult)
	if spill {
		tracker.SetBytesLimit(1)
		rowContainer.rowContainer.ActionSpillForTest().Action(tracker)
	}
	err = rowContainer.PutChunk(chk0, nil)
	require.NoError(t, err)
	err = rowContainer.PutChunk(chk1, nil)
	require.NoError(t, err)
	rowContainer.ActionSpill().(*chunk.SpillDiskAction).WaitForTest()
	require.Equal(t, spill, rowContainer.alreadySpilledSafeForTest())
	require.Equal(t, spill, rowContainer.GetMemTracker().BytesConsumed() == 0)
	require.Equal(t, !spill, rowContainer.GetMemTracker().BytesConsumed() > 0)
	if rowContainer.alreadySpilledSafeForTest() {
		require.NotNil(t, rowContainer.GetDiskTracker())
		require.True(t, rowContainer.GetDiskTracker().BytesConsumed() > 0)
	}

	probeChk, probeColType := initProbeChunk(2)
	probeRow := probeChk.GetRow(1)
	probeCtx := &hashContext{
		allTypes:  probeColType[1:3],
		keyColIdx: []int{1, 2},
	}
	probeCtx.hasNull = make([]bool, 1)
	probeCtx.hashVals = append(hCtx.hashVals, hashFunc())
	matched, _, err := rowContainer.GetMatchedRowsAndPtrs(hCtx.hashVals[1].Sum64(), probeRow, probeCtx)
	require.NoError(t, err)
	require.Equal(t, 2, len(matched))
	require.Equal(t, chk0.GetRow(1).GetDatumRow(colTypes), matched[0].GetDatumRow(colTypes))
	require.Equal(t, chk1.GetRow(1).GetDatumRow(colTypes), matched[1].GetDatumRow(colTypes))
	return rowContainer, copiedRC
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fuzzer

import (
	"reflect"
	"strings"

	"github.com/google/gofuzz"

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
)

// Funcs returns the fuzzer functions for the apiextensions apis.
func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
	return []interface{}{
		func(obj *apiextensions.CustomResourceDefinitionSpec, c fuzz.Continue) {
			c.FuzzNoCustom(obj)

			// match our defaulter
			if len(obj.Scope) == 0 {
				obj.Scope = apiextensions.NamespaceScoped
			}
			if len(obj.Names.Singular) == 0 {
				obj.Names.Singular = strings.ToLower(obj.Names.Kind)
			}
			if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
				obj.Names.ListKind = obj.Names.Kind + "List"
			}
		},
		func(obj *apiextensions.JSONSchemaProps, c fuzz.Continue) {
			// we cannot use c.FuzzNoCustom because of the interface{} fields. So let's loop with reflection.
			vobj := reflect.ValueOf(obj).Elem()
			tobj := reflect.TypeOf(obj).Elem()
			for i := 0; i < tobj.NumField(); i++ {
				field := tobj.Field(i)
				switch field.Name {
				case "Default", "Enum", "Example", "Ref":
					continue
				default:
					isValue := true
					switch field.Type.Kind() {
					case reflect.Interface, reflect.Map, reflect.Slice, reflect.Ptr:
						isValue = false
					}
					if isValue || c.Intn(5) == 0 {
						c.Fuzz(vobj.Field(i).Addr().Interface())
					}
				}
			}
			if c.RandBool() {
				validJSON := apiextensions.JSON(`{"some": {"json": "test"}, "string": 42}`)
				obj.Default = &validJSON
			}
			if c.RandBool() {
				obj.Enum = []apiextensions.JSON{c.Float64(), c.RandString(), c.RandBool()}
			}
			if c.RandBool() {
				validJSON := apiextensions.JSON(`"foobarbaz"`)
				obj.Example = &validJSON
			}
			if c.RandBool() {
				validRef := "validRef"
				obj.Ref = &validRef
			}
		},
		func(obj *apiextensions.JSONSchemaPropsOrBool, c fuzz.Continue) {
			if c.RandBool() {
				obj.Schema = &apiextensions.JSONSchemaProps{}
				c.Fuzz(obj.Schema)
			} else {
				obj.Allows = c.RandBool()
			}
		},
		func(obj *apiextensions.JSONSchemaPropsOrArray, c fuzz.Continue) {
			// disallow both Schema and JSONSchemas to be nil.
			if c.RandBool() {
				obj.Schema = &apiextensions.JSONSchemaProps{}
				c.Fuzz(obj.Schema)
			} else {
				obj.JSONSchemas = make([]apiextensions.JSONSchemaProps, c.Intn(3)+1)
				for i := range obj.JSONSchemas {
					c.Fuzz(&obj.JSONSchemas[i])
				}
			}
		},
		func(obj *apiextensions.JSONSchemaPropsOrStringArray, c fuzz.Continue) {
			if c.RandBool() {
				obj.Schema = &apiextensions.JSONSchemaProps{}
				c.Fuzz(obj.Schema)
			} else {
				c.Fuzz(&obj.Property)
			}
		},
	}
}
<commit_msg>apiextensions: fix object keys in fuzzer to exclude escape characters<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fuzzer

import (
	"reflect"
	"strings"

	"github.com/google/gofuzz"

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
)

// Funcs returns the fuzzer functions for the apiextensions apis.
func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
	return []interface{}{
		func(obj *apiextensions.CustomResourceDefinitionSpec, c fuzz.Continue) {
			c.FuzzNoCustom(obj)

			// match our defaulter
			if len(obj.Scope) == 0 {
				obj.Scope = apiextensions.NamespaceScoped
			}
			if len(obj.Names.Singular) == 0 {
				obj.Names.Singular = strings.ToLower(obj.Names.Kind)
			}
			if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
				obj.Names.ListKind = obj.Names.Kind + "List"
			}
		},
		func(obj *apiextensions.JSONSchemaProps, c fuzz.Continue) {
			// we cannot use c.FuzzNoCustom because of the interface{} fields. So let's loop with reflection.
			vobj := reflect.ValueOf(obj).Elem()
			tobj := reflect.TypeOf(obj).Elem()
			for i := 0; i < tobj.NumField(); i++ {
				field := tobj.Field(i)
				switch field.Name {
				case "Default", "Enum", "Example", "Ref":
					continue
				default:
					isValue := true
					switch field.Type.Kind() {
					case reflect.Interface, reflect.Map, reflect.Slice, reflect.Ptr:
						isValue = false
					}
					if isValue || c.Intn(10) == 0 {
						c.Fuzz(vobj.Field(i).Addr().Interface())

						// JSON keys must not contain escape char with our JSON codec (jsoniter)
						// TODO: remove this when/if we moved from jsoniter.ConfigFastest to ConfigCompatibleWithStandardLibrary
						if field.Type.Kind() == reflect.Map {
							keys := append([]reflect.Value(nil), vobj.Field(i).MapKeys()...)
							for _, k := range keys {
								stripped := toJSONString(k.String())
								if stripped == k.String() {
									continue
								}
								// set new key
								vobj.Field(i).SetMapIndex(reflect.ValueOf(stripped), vobj.Field(i).MapIndex(k))
								// remove old
								vobj.Field(i).SetMapIndex(k, reflect.Value{})
							}
						}
					}
				}
			}
			if c.RandBool() {
				validJSON := apiextensions.JSON(`{"some": {"json": "test"}, "string": 42}`)
				obj.Default = &validJSON
			}
			if c.RandBool() {
				obj.Enum = []apiextensions.JSON{c.Float64(), c.RandString(), c.RandBool()}
			}
			if c.RandBool() {
				validJSON := apiextensions.JSON(`"foobarbaz"`)
				obj.Example = &validJSON
			}
			if c.RandBool() {
				validRef := "validRef"
				obj.Ref = &validRef
			}
		},
		func(obj *apiextensions.JSONSchemaPropsOrBool, c fuzz.Continue) {
			if c.RandBool() {
				obj.Schema = &apiextensions.JSONSchemaProps{}
				c.Fuzz(obj.Schema)
			} else {
				obj.Allows = c.RandBool()
			}
		},
		func(obj *apiextensions.JSONSchemaPropsOrArray, c fuzz.Continue) {
			// disallow both Schema and JSONSchemas to be nil.
			if c.RandBool() {
				obj.Schema = &apiextensions.JSONSchemaProps{}
				c.Fuzz(obj.Schema)
			} else {
				obj.JSONSchemas = make([]apiextensions.JSONSchemaProps, c.Intn(3)+1)
				for i := range obj.JSONSchemas {
					c.Fuzz(&obj.JSONSchemas[i])
				}
			}
		},
		func(obj *apiextensions.JSONSchemaPropsOrStringArray, c fuzz.Continue) {
			if c.RandBool() {
				obj.Schema = &apiextensions.JSONSchemaProps{}
				c.Fuzz(obj.Schema)
			} else {
				c.Fuzz(&obj.Property)
			}
		},
	}
}

func toJSONString(s string) string {
	return strings.Map(func(r rune) rune {
		// replace chars which are not supported in keys by jsoniter.ConfigFastest
		if r == '\\' || r == '"' {
			return 'x'
		}
		return r
	}, s)
}
<|endoftext|>


<commit_before>package luar

import (
	"container/list"
	"reflect"

	"github.com/yuin/gopher-lua"
)

const (
	cacheKey = "github.com/layeh/gopher-luar"
	tagName  = "luar"
)

type mtCache struct {
	regular, types map[reflect.Type]*lua.LTable
}

func newMTCache() *mtCache {
	return &mtCache{
		regular: make(map[reflect.Type]*lua.LTable),
		types:   make(map[reflect.Type]*lua.LTable),
	}
}

func getMTCache(L *lua.LState) *mtCache {
	registry, ok := L.Get(lua.RegistryIndex).(*lua.LTable)
	if !ok {
		panic("gopher-luar: corrupt lua registry")
	}
	lCache, ok := registry.RawGetString(cacheKey).(*lua.LUserData)
	if !ok {
		lCache = L.NewUserData()
		lCache.Value = newMTCache()
		registry.RawSetString(cacheKey, lCache)
	}
	cache, ok := lCache.Value.(*mtCache)
	if !ok {
		panic("gopher-luar: corrupt luar metatable cache")
	}
	return cache
}

func addMethods(L *lua.LState, vtype reflect.Type, tbl *lua.LTable) {
	for i := 0; i < vtype.NumMethod(); i++ {
		method := vtype.Method(i)
		if method.PkgPath != "" {
			continue
		}
		fn := New(L, method.Func.Interface())
		tbl.RawSetString(method.Name, fn)
		tbl.RawSetString(getUnexportedName(method.Name), fn)
	}
}

func addFields(L *lua.LState, vtype reflect.Type, tbl *lua.LTable) {
	type element struct {
		Type  reflect.Type
		Index []int
	}

	queue := list.New()
	queue.PushFront(element{
		Type: vtype,
	})

	for queue.Len() > 0 {
		e := queue.Back()
		elem := e.Value.(element)
		vtype := elem.Type
	fields:
		for i := 0; i < vtype.NumField(); i++ {
			field := vtype.Field(i)
			if field.PkgPath != "" && !field.Anonymous {
				continue
			}
			var names []string
			tag := field.Tag.Get(tagName)
			if tag == "-" {
				continue
			}
			if tag != "" {
				names = []string{
					tag,
				}
			} else {
				names = []string{
					field.Name,
					getUnexportedName(field.Name),
				}
			}
			for _, key := range names {
				if tbl.RawGetString(key) != lua.LNil {
					continue fields
				}
			}
			index := make([]int, len(elem.Index)+1)
			copy(index, elem.Index)
			index[len(elem.Index)] = i

			ud := L.NewUserData()
			ud.Value = index
			for _, key := range names {
				tbl.RawSetString(key, ud)
			}
			if field.Anonymous {
				t := field.Type
				if field.Type.Kind() != reflect.Struct {
					if field.Type.Kind() != reflect.Ptr || field.Type.Elem().Kind() != reflect.Struct {
						continue
					}
					t = field.Type.Elem()
				}
				index := make([]int, len(elem.Index)+1)
				copy(index, elem.Index)
				index[len(elem.Index)] = i
				queue.PushFront(element{
					Type:  t,
					Index: index,
				})
			}
		}

		queue.Remove(e)
	}
}

func getMetatable(L *lua.LState, value reflect.Value) *lua.LTable {
	cache := getMTCache(L)

	vtype := value.Type()
	if vtype.Kind() == reflect.Ptr {
		vtype = vtype.Elem()
	}
	if v := cache.regular[vtype]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__tostring", L.NewFunction(allTostring))
	mt.RawSetString("__metatable", L.NewTable())

	ptrMethods := L.NewTable()
	methods := L.NewTable()

	switch vtype.Kind() {
	case reflect.Array:
		mt.RawSetString("__index", L.NewFunction(arrayIndex))
		mt.RawSetString("__newindex", L.NewFunction(arrayNewIndex))
		mt.RawSetString("__len", L.NewFunction(arrayLen))
		mt.RawSetString("__eq", L.NewFunction(arrayEq))
	case reflect.Chan:
		methods.RawSetString("send", L.NewFunction(chanSend))
		methods.RawSetString("receive", L.NewFunction(chanReceive))
		methods.RawSetString("close", L.NewFunction(chanClose))

		mt.RawSetString("__index", L.NewFunction(chanIndex))
		mt.RawSetString("__len", L.NewFunction(chanLen))
		mt.RawSetString("__eq", L.NewFunction(chanEq))
	case reflect.Map:
		mt.RawSetString("__index", L.NewFunction(mapIndex))
		mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
		mt.RawSetString("__len", L.NewFunction(mapLen))
		mt.RawSetString("__call", L.NewFunction(mapCall))
		mt.RawSetString("__eq", L.NewFunction(mapEq))
	case reflect.Struct:
		fields := L.NewTable()
		addFields(L, vtype, fields)
		mt.RawSetString("fields", fields)

		mt.RawSetString("__index", L.NewFunction(structIndex))
		mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		mt.RawSetString("__eq", L.NewFunction(ptrEq)) // TODO: allow non-pointer structs to be compared
	case reflect.Slice:
		methods.RawSetString("capacity", L.NewFunction(sliceCapacity))
		methods.RawSetString("append", L.NewFunction(sliceAppend))

		mt.RawSetString("__index", L.NewFunction(sliceIndex))
		mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
		mt.RawSetString("__len", L.NewFunction(sliceLen))
		mt.RawSetString("__eq", L.NewFunction(sliceEq))
	default:
		mt.RawSetString("__index", L.NewFunction(ptrIndex))
		mt.RawSetString("__eq", L.NewFunction(ptrEq))
	}

	mt.RawSetString("__pow", L.NewFunction(ptrPow))
	mt.RawSetString("__unm", L.NewFunction(ptrUnm))

	addMethods(L, reflect.PtrTo(vtype), ptrMethods)
	mt.RawSetString("ptr_methods", ptrMethods)

	addMethods(L, vtype, methods)
	mt.RawSetString("methods", methods)

	cache.regular[vtype] = mt
	return mt
}

func getTypeMetatable(L *lua.LState, t reflect.Type) *lua.LTable {
	cache := getMTCache(L)

	if v := cache.types[t]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__call", L.NewFunction(typeCall))
	mt.RawSetString("__eq", L.NewFunction(typeEq))

	cache.types[t] = mt
	return mt
}
<commit_msg>remove unnecessary New call<commit_after>package luar

import (
	"container/list"
	"reflect"

	"github.com/yuin/gopher-lua"
)

const (
	cacheKey = "github.com/layeh/gopher-luar"
	tagName  = "luar"
)

type mtCache struct {
	regular, types map[reflect.Type]*lua.LTable
}

func newMTCache() *mtCache {
	return &mtCache{
		regular: make(map[reflect.Type]*lua.LTable),
		types:   make(map[reflect.Type]*lua.LTable),
	}
}

func getMTCache(L *lua.LState) *mtCache {
	registry, ok := L.Get(lua.RegistryIndex).(*lua.LTable)
	if !ok {
		panic("gopher-luar: corrupt lua registry")
	}
	lCache, ok := registry.RawGetString(cacheKey).(*lua.LUserData)
	if !ok {
		lCache = L.NewUserData()
		lCache.Value = newMTCache()
		registry.RawSetString(cacheKey, lCache)
	}
	cache, ok := lCache.Value.(*mtCache)
	if !ok {
		panic("gopher-luar: corrupt luar metatable cache")
	}
	return cache
}

func addMethods(L *lua.LState, vtype reflect.Type, tbl *lua.LTable) {
	for i := 0; i < vtype.NumMethod(); i++ {
		method := vtype.Method(i)
		if method.PkgPath != "" {
			continue
		}
		fn := funcWrapper(L, method.Func)
		tbl.RawSetString(method.Name, fn)
		tbl.RawSetString(getUnexportedName(method.Name), fn)
	}
}

func addFields(L *lua.LState, vtype reflect.Type, tbl *lua.LTable) {
	type element struct {
		Type  reflect.Type
		Index []int
	}

	queue := list.New()
	queue.PushFront(element{
		Type: vtype,
	})

	for queue.Len() > 0 {
		e := queue.Back()
		elem := e.Value.(element)
		vtype := elem.Type
	fields:
		for i := 0; i < vtype.NumField(); i++ {
			field := vtype.Field(i)
			if field.PkgPath != "" && !field.Anonymous {
				continue
			}
			var names []string
			tag := field.Tag.Get(tagName)
			if tag == "-" {
				continue
			}
			if tag != "" {
				names = []string{
					tag,
				}
			} else {
				names = []string{
					field.Name,
					getUnexportedName(field.Name),
				}
			}
			for _, key := range names {
				if tbl.RawGetString(key) != lua.LNil {
					continue fields
				}
			}
			index := make([]int, len(elem.Index)+1)
			copy(index, elem.Index)
			index[len(elem.Index)] = i

			ud := L.NewUserData()
			ud.Value = index
			for _, key := range names {
				tbl.RawSetString(key, ud)
			}
			if field.Anonymous {
				t := field.Type
				if field.Type.Kind() != reflect.Struct {
					if field.Type.Kind() != reflect.Ptr || field.Type.Elem().Kind() != reflect.Struct {
						continue
					}
					t = field.Type.Elem()
				}
				index := make([]int, len(elem.Index)+1)
				copy(index, elem.Index)
				index[len(elem.Index)] = i
				queue.PushFront(element{
					Type:  t,
					Index: index,
				})
			}
		}

		queue.Remove(e)
	}
}

func getMetatable(L *lua.LState, value reflect.Value) *lua.LTable {
	cache := getMTCache(L)

	vtype := value.Type()
	if vtype.Kind() == reflect.Ptr {
		vtype = vtype.Elem()
	}
	if v := cache.regular[vtype]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__tostring", L.NewFunction(allTostring))
	mt.RawSetString("__metatable", L.NewTable())

	ptrMethods := L.NewTable()
	methods := L.NewTable()

	switch vtype.Kind() {
	case reflect.Array:
		mt.RawSetString("__index", L.NewFunction(arrayIndex))
		mt.RawSetString("__newindex", L.NewFunction(arrayNewIndex))
		mt.RawSetString("__len", L.NewFunction(arrayLen))
		mt.RawSetString("__eq", L.NewFunction(arrayEq))
	case reflect.Chan:
		methods.RawSetString("send", L.NewFunction(chanSend))
		methods.RawSetString("receive", L.NewFunction(chanReceive))
		methods.RawSetString("close", L.NewFunction(chanClose))

		mt.RawSetString("__index", L.NewFunction(chanIndex))
		mt.RawSetString("__len", L.NewFunction(chanLen))
		mt.RawSetString("__eq", L.NewFunction(chanEq))
	case reflect.Map:
		mt.RawSetString("__index", L.NewFunction(mapIndex))
		mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
		mt.RawSetString("__len", L.NewFunction(mapLen))
		mt.RawSetString("__call", L.NewFunction(mapCall))
		mt.RawSetString("__eq", L.NewFunction(mapEq))
	case reflect.Struct:
		fields := L.NewTable()
		addFields(L, vtype, fields)
		mt.RawSetString("fields", fields)

		mt.RawSetString("__index", L.NewFunction(structIndex))
		mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		mt.RawSetString("__eq", L.NewFunction(ptrEq)) // TODO: allow non-pointer structs to be compared
	case reflect.Slice:
		methods.RawSetString("capacity", L.NewFunction(sliceCapacity))
		methods.RawSetString("append", L.NewFunction(sliceAppend))

		mt.RawSetString("__index", L.NewFunction(sliceIndex))
		mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
		mt.RawSetString("__len", L.NewFunction(sliceLen))
		mt.RawSetString("__eq", L.NewFunction(sliceEq))
	default:
		mt.RawSetString("__index", L.NewFunction(ptrIndex))
		mt.RawSetString("__eq", L.NewFunction(ptrEq))
	}

	mt.RawSetString("__pow", L.NewFunction(ptrPow))
	mt.RawSetString("__unm", L.NewFunction(ptrUnm))

	addMethods(L, reflect.PtrTo(vtype), ptrMethods)
	mt.RawSetString("ptr_methods", ptrMethods)

	addMethods(L, vtype, methods)
	mt.RawSetString("methods", methods)

	cache.regular[vtype] = mt
	return mt
}

func getTypeMetatable(L *lua.LState, t reflect.Type) *lua.LTable {
	cache := getMTCache(L)

	if v := cache.types[t]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__call", L.NewFunction(typeCall))
	mt.RawSetString("__eq", L.NewFunction(typeEq))

	cache.types[t] = mt
	return mt
}
<|endoftext|>


<commit_before>package luar

import (
	"container/list"
	"reflect"
	"sync"

	"github.com/yuin/gopher-lua"
)

const cacheKey = "github.com/layeh/gopher-luar"

var mu sync.Mutex

type mtCache struct {
	regular, types map[reflect.Type]lua.LValue
}

func newMTCache() *mtCache {
	return &mtCache{
		regular: make(map[reflect.Type]lua.LValue),
		types:   make(map[reflect.Type]lua.LValue),
	}
}

func getMTCache(L *lua.LState) *mtCache {
	registry, ok := L.Get(lua.RegistryIndex).(*lua.LTable)
	if !ok {
		panic("gopher-luar: corrupt lua registry")
	}
	lCache, ok := registry.RawGetString(cacheKey).(*lua.LUserData)
	if !ok {
		lCache = L.NewUserData()
		lCache.Value = newMTCache()
		registry.RawSetString(cacheKey, lCache)
	}
	cache, ok := lCache.Value.(*mtCache)
	if !ok {
		panic("gopher-luar: corrupt luar metatable cache")
	}
	return cache
}

func addMethods(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	vtype := value.Type()
	for i := 0; i < vtype.NumMethod(); i++ {
		method := vtype.Method(i)
		if method.PkgPath != "" {
			continue
		}
		fn := New(L, method.Func.Interface())
		tbl.RawSetString(method.Name, fn)
		tbl.RawSetString(getUnexportedName(method.Name), fn)
	}
}

func addFields(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	type element struct {
		Type  reflect.Type
		Index []int
	}

	queue := list.New()
	vtype := value.Type()
	queue.PushFront(element{
		Type: vtype,
	})

	for queue.Len() > 0 {
		e := queue.Back()
		elem := e.Value.(element)
		vtype := elem.Type
		if vtype.Kind() == reflect.Ptr {
			vtype = vtype.Elem()
		}
	fields:
		for i := 0; i < vtype.NumField(); i++ {
			field := vtype.Field(i)
			if field.PkgPath != "" {
				continue
			}
			var names []string
			tag := field.Tag.Get("luar")
			if tag == "-" {
				continue
			}
			if tag != "" {
				names = []string{
					tag,
				}
			} else {
				names = []string{
					field.Name,
					getUnexportedName(field.Name),
				}
			}
			for _, key := range names {
				if tbl.RawGetString(key) != lua.LNil {
					continue fields
				}
			}
			index := make([]int, len(elem.Index)+1)
			copy(index, elem.Index)
			index[len(elem.Index)] = i

			ud := L.NewUserData()
			ud.Value = index
			for _, key := range names {
				tbl.RawSetString(key, ud)
			}
			if field.Anonymous {
				index := make([]int, len(elem.Index)+1)
				copy(index, elem.Index)
				index[len(elem.Index)] = i
				queue.PushFront(element{
					Type:  field.Type,
					Index: index,
				})
			}
		}

		queue.Remove(e)
	}
}

func getMetatable(L *lua.LState, value reflect.Value) lua.LValue {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	vtype := value.Type()
	if v := cache.regular[vtype]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__metatable", L.NewTable())

	switch vtype.Kind() {
	case reflect.Array:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(arrayIndex))
		mt.RawSetString("__len", L.NewFunction(arrayLen))
		mt.RawSetString("__eq", L.NewFunction(arrayEq))
	case reflect.Chan:
		methods := L.NewTable()
		methods.RawSetString("send", L.NewFunction(chanSend))
		methods.RawSetString("receive", L.NewFunction(chanReceive))
		methods.RawSetString("close", L.NewFunction(chanClose))
		addMethods(L, value, methods)

		mt.RawSetString("__index", methods)
		mt.RawSetString("__len", L.NewFunction(chanLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(chanEq))
	case reflect.Map:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(mapIndex))
		mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
		mt.RawSetString("__len", L.NewFunction(mapLen))
		mt.RawSetString("__call", L.NewFunction(mapCall))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(mapEq))
	case reflect.Ptr:
		ptrMethods := L.NewTable()
		addMethods(L, value, ptrMethods)
		mt.RawSetString("ptr_methods", ptrMethods)
		methods := L.NewTable()
		addMethods(L, value.Elem(), methods)
		mt.RawSetString("methods", methods)
		if value.Elem().Kind() == reflect.Struct {
			fields := L.NewTable()
			addFields(L, value.Elem(), fields)
			mt.RawSetString("fields", fields)
		}

		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__index", L.NewFunction(arrayIndex))
		} else {
			mt.RawSetString("__index", L.NewFunction(ptrIndex))
		}
		switch value.Elem().Kind() {
		case reflect.Array:
			mt.RawSetString("__newindex", L.NewFunction(arrayNewIndex))
		case reflect.Struct:
			mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		}
		mt.RawSetString("__pow", L.NewFunction(ptrPow))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__unm", L.NewFunction(ptrUnm))
		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__len", L.NewFunction(arrayLen))
		}
		mt.RawSetString("__eq", L.NewFunction(ptrEq))
	case reflect.Slice:
		methods := L.NewTable()
		methods.RawSetString("capacity", L.NewFunction(sliceCapacity))
		methods.RawSetString("append", L.NewFunction(sliceAppend))
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(sliceIndex))
		mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
		mt.RawSetString("__len", L.NewFunction(sliceLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(sliceEq))
	case reflect.Struct:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)
		fields := L.NewTable()
		addFields(L, value, fields)
		mt.RawSetString("fields", fields)

		mt.RawSetString("__index", L.NewFunction(structIndex))
		mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
	}

	cache.regular[vtype] = mt
	return mt
}

func getTypeMetatable(L *lua.LState, t reflect.Type) lua.LValue {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	if v := cache.types[t]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__call", L.NewFunction(typeCall))
	mt.RawSetString("__tostring", L.NewFunction(allTostring))
	mt.RawSetString("__eq", L.NewFunction(typeEq))

	cache.types[t] = mt
	return mt
}
<commit_msg>move struct tag name to const<commit_after>package luar

import (
	"container/list"
	"reflect"
	"sync"

	"github.com/yuin/gopher-lua"
)

const (
	cacheKey = "github.com/layeh/gopher-luar"
	tagName  = "luar"
)

var mu sync.Mutex

type mtCache struct {
	regular, types map[reflect.Type]lua.LValue
}

func newMTCache() *mtCache {
	return &mtCache{
		regular: make(map[reflect.Type]lua.LValue),
		types:   make(map[reflect.Type]lua.LValue),
	}
}

func getMTCache(L *lua.LState) *mtCache {
	registry, ok := L.Get(lua.RegistryIndex).(*lua.LTable)
	if !ok {
		panic("gopher-luar: corrupt lua registry")
	}
	lCache, ok := registry.RawGetString(cacheKey).(*lua.LUserData)
	if !ok {
		lCache = L.NewUserData()
		lCache.Value = newMTCache()
		registry.RawSetString(cacheKey, lCache)
	}
	cache, ok := lCache.Value.(*mtCache)
	if !ok {
		panic("gopher-luar: corrupt luar metatable cache")
	}
	return cache
}

func addMethods(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	vtype := value.Type()
	for i := 0; i < vtype.NumMethod(); i++ {
		method := vtype.Method(i)
		if method.PkgPath != "" {
			continue
		}
		fn := New(L, method.Func.Interface())
		tbl.RawSetString(method.Name, fn)
		tbl.RawSetString(getUnexportedName(method.Name), fn)
	}
}

func addFields(L *lua.LState, value reflect.Value, tbl *lua.LTable) {
	type element struct {
		Type  reflect.Type
		Index []int
	}

	queue := list.New()
	vtype := value.Type()
	queue.PushFront(element{
		Type: vtype,
	})

	for queue.Len() > 0 {
		e := queue.Back()
		elem := e.Value.(element)
		vtype := elem.Type
		if vtype.Kind() == reflect.Ptr {
			vtype = vtype.Elem()
		}
	fields:
		for i := 0; i < vtype.NumField(); i++ {
			field := vtype.Field(i)
			if field.PkgPath != "" {
				continue
			}
			var names []string
			tag := field.Tag.Get(tagName)
			if tag == "-" {
				continue
			}
			if tag != "" {
				names = []string{
					tag,
				}
			} else {
				names = []string{
					field.Name,
					getUnexportedName(field.Name),
				}
			}
			for _, key := range names {
				if tbl.RawGetString(key) != lua.LNil {
					continue fields
				}
			}
			index := make([]int, len(elem.Index)+1)
			copy(index, elem.Index)
			index[len(elem.Index)] = i

			ud := L.NewUserData()
			ud.Value = index
			for _, key := range names {
				tbl.RawSetString(key, ud)
			}
			if field.Anonymous {
				index := make([]int, len(elem.Index)+1)
				copy(index, elem.Index)
				index[len(elem.Index)] = i
				queue.PushFront(element{
					Type:  field.Type,
					Index: index,
				})
			}
		}

		queue.Remove(e)
	}
}

func getMetatable(L *lua.LState, value reflect.Value) lua.LValue {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	vtype := value.Type()
	if v := cache.regular[vtype]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__metatable", L.NewTable())

	switch vtype.Kind() {
	case reflect.Array:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(arrayIndex))
		mt.RawSetString("__len", L.NewFunction(arrayLen))
		mt.RawSetString("__eq", L.NewFunction(arrayEq))
	case reflect.Chan:
		methods := L.NewTable()
		methods.RawSetString("send", L.NewFunction(chanSend))
		methods.RawSetString("receive", L.NewFunction(chanReceive))
		methods.RawSetString("close", L.NewFunction(chanClose))
		addMethods(L, value, methods)

		mt.RawSetString("__index", methods)
		mt.RawSetString("__len", L.NewFunction(chanLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(chanEq))
	case reflect.Map:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(mapIndex))
		mt.RawSetString("__newindex", L.NewFunction(mapNewIndex))
		mt.RawSetString("__len", L.NewFunction(mapLen))
		mt.RawSetString("__call", L.NewFunction(mapCall))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(mapEq))
	case reflect.Ptr:
		ptrMethods := L.NewTable()
		addMethods(L, value, ptrMethods)
		mt.RawSetString("ptr_methods", ptrMethods)
		methods := L.NewTable()
		addMethods(L, value.Elem(), methods)
		mt.RawSetString("methods", methods)
		if value.Elem().Kind() == reflect.Struct {
			fields := L.NewTable()
			addFields(L, value.Elem(), fields)
			mt.RawSetString("fields", fields)
		}

		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__index", L.NewFunction(arrayIndex))
		} else {
			mt.RawSetString("__index", L.NewFunction(ptrIndex))
		}
		switch value.Elem().Kind() {
		case reflect.Array:
			mt.RawSetString("__newindex", L.NewFunction(arrayNewIndex))
		case reflect.Struct:
			mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		}
		mt.RawSetString("__pow", L.NewFunction(ptrPow))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__unm", L.NewFunction(ptrUnm))
		if value.Elem().Kind() == reflect.Array {
			mt.RawSetString("__len", L.NewFunction(arrayLen))
		}
		mt.RawSetString("__eq", L.NewFunction(ptrEq))
	case reflect.Slice:
		methods := L.NewTable()
		methods.RawSetString("capacity", L.NewFunction(sliceCapacity))
		methods.RawSetString("append", L.NewFunction(sliceAppend))
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)

		mt.RawSetString("__index", L.NewFunction(sliceIndex))
		mt.RawSetString("__newindex", L.NewFunction(sliceNewIndex))
		mt.RawSetString("__len", L.NewFunction(sliceLen))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
		mt.RawSetString("__eq", L.NewFunction(sliceEq))
	case reflect.Struct:
		methods := L.NewTable()
		addMethods(L, value, methods)
		mt.RawSetString("methods", methods)
		fields := L.NewTable()
		addFields(L, value, fields)
		mt.RawSetString("fields", fields)

		mt.RawSetString("__index", L.NewFunction(structIndex))
		mt.RawSetString("__newindex", L.NewFunction(structNewIndex))
		mt.RawSetString("__tostring", L.NewFunction(allTostring))
	}

	cache.regular[vtype] = mt
	return mt
}

func getTypeMetatable(L *lua.LState, t reflect.Type) lua.LValue {
	mu.Lock()
	defer mu.Unlock()

	cache := getMTCache(L)

	if v := cache.types[t]; v != nil {
		return v
	}

	mt := L.NewTable()
	mt.RawSetString("__call", L.NewFunction(typeCall))
	mt.RawSetString("__tostring", L.NewFunction(allTostring))
	mt.RawSetString("__eq", L.NewFunction(typeEq))

	cache.types[t] = mt
	return mt
}
<|endoftext|>


<commit_before>package singularity

import (
	"fmt"
	"log"
	"runtime/debug"
	"strings"

	"github.com/opentable/go-singularity"
	"github.com/opentable/sous/lib"
	"github.com/pkg/errors"
	"github.com/satori/go.uuid"
)

/*
The imagined use case here is like this:

intendedSet := getFromManifests()
existingSet := getFromSingularity()

dChans := intendedSet.Diff(existingSet)
*/

type (
	deployer struct {
		Client  rectificationClient
		singFac func(string) *singularity.Client
	}

	// rectificationClient abstracts the raw interactions with Singularity.
	rectificationClient interface {
		// Deploy creates a new deploy on a particular requeust
		Deploy(cluster, depID, reqID, dockerImage string, r sous.Resources, e sous.Env, vols sous.Volumes) error

		// PostRequest sends a request to a Singularity cluster to initiate
		PostRequest(cluster, reqID string, instanceCount int, kind sous.ManifestKind, owners sous.OwnerSet) error

		// DeleteRequest instructs Singularity to delete a particular request
		DeleteRequest(cluster, reqID, message string) error
	}

	// DTOMap is shorthand for map[string]interface{}
	dtoMap map[string]interface{}
)

// NewDeployer creates a new Singularity-based sous.Deployer.
func NewDeployer(c rectificationClient) sous.Deployer {
	return &deployer{Client: c}
}

func (r *deployer) RectifyCreates(cc <-chan *sous.Deployable, errs chan<- sous.DiffResolution) {
	for d := range cc {
		result := sous.DiffResolution{DeployID: d.ID()}
		if err := r.RectifySingleCreate(d); err != nil {
			result.Error = sous.WrapResolveError(&sous.CreateError{Deployment: d.Deployment, Err: err})
			result.Desc = "not created"
		} else {
			result.Desc = "created"
		}
		errs <- result
	}
}

func (r *deployer) SetSingularityFactory(fn func(string) *singularity.Client) {
	r.singFac = fn
}

func (r *deployer) buildSingClient(url string) *singularity.Client {
	if r.singFac == nil {
		return singularity.NewClient(url)
	}
	return r.singFac(url)
}

func (r *deployer) ImageName(d *sous.Deployable) (string, error) {
	if d.BuildArtifact == nil {
		return "", &sous.MissingImageNameError{Cause: fmt.Errorf("Missing BuildArtifact on Deployable")}
	}
	return d.BuildArtifact.Name, nil
}

func rectifyRecover(d interface{}, f string, err *error) {
	if r := recover(); r != nil {
		sous.Log.Warn.Printf("Panic in %s with %# v", f, d)
		sous.Log.Warn.Printf("  %v", r)
		sous.Log.Warn.Print(string(debug.Stack()))
		*err = errors.Errorf("Panicked")
	}
}

func (r *deployer) RectifySingleCreate(d *sous.Deployable) (err error) {
	Log.Debug.Printf("Rectifying creation %q:  \n %# v", d.ID(), d.Deployment)
	defer rectifyRecover(d, "RectifySingleCreate", &err)
	name, err := r.ImageName(d)
	if err != nil {
		return err
	}
	reqID := computeRequestID(d)
	if err = r.Client.PostRequest(d.Cluster.BaseURL, reqID, d.NumInstances, d.Kind, d.Owners); err != nil {
		return err
	}
	return r.Client.Deploy(
		d.Cluster.BaseURL, newDepID(), reqID, name, d.Resources,
		d.Env, d.DeployConfig.Volumes)
}

func (r *deployer) RectifyDeletes(dc <-chan *sous.Deployable, errs chan<- sous.DiffResolution) {
	for d := range dc {
		result := sous.DiffResolution{DeployID: d.ID()}
		if err := r.RectifySingleDelete(d); err != nil {
			result.Error = sous.WrapResolveErroErrorWrapper(&sous.DeleteError{Deployment: d.Deployment, Err: err})
			result.Desc = "not deleted"
		} else {
			result.Desc = "deleted"
		}
		errs <- result
	}
}

func (r *deployer) RectifySingleDelete(d *sous.Deployable) (err error) {
	defer rectifyRecover(d, "RectifySingleDelete", &err)
	requestID := computeRequestID(d)
	// TODO: Alert the owner of this request that there is no manifest for it;
	// they should either delete the request manually, or else add the manifest back.
	sous.Log.Warn.Printf("NOT DELETING REQUEST %q (FOR: %q)", requestID, d.ID())
	return nil
	// The following line deletes requests when it is not commented out.
	//return r.Client.DeleteRequest(d.Cluster.BaseURL, requestID, "deleting request for removed manifest")
}

func (r *deployer) RectifyModifies(
	mc <-chan *sous.DeployablePair, errs chan<- sous.DiffResolution) {
	for pair := range mc {
		result := sous.DiffResolution{DeployID: pair.ID()}
		if err := r.RectifySingleModification(pair); err != nil {
			dp := &sous.DeploymentPair{
				Prior: pair.Prior.Deployment,
				Post:  pair.Post.Deployment,
			}
			log.Printf("%#v", err)
			result.Error = sous.WrapResolveError(&sous.ChangeError{Deployments: dp, Err: err})
			result.Desc = "not updated"
		} else {
			result.Desc = "updated"
		}
		errs <- result
	}
}

func (r *deployer) RectifySingleModification(pair *sous.DeployablePair) (err error) {
	Log.Debug.Printf("Rectifying modified %q: \n  %# v \n    =>  \n  %# v", pair.ID(), pair.Prior.Deployment, pair.Post.Deployment)
	defer rectifyRecover(pair, "RectifySingleModification", &err)
	if r.changesReq(pair) {
		Log.Debug.Printf("Updating Request...")
		if err := r.Client.PostRequest(
			pair.Post.Cluster.BaseURL,
			computeRequestID(pair.Post),
			pair.Post.NumInstances,
			pair.Post.Kind,
			pair.Post.Owners,
		); err != nil {
			return err
		}
	}

	if changesDep(pair) {
		Log.Debug.Printf("Deploying...")
		name, err := r.ImageName(pair.Post)
		if err != nil {
			return err
		}

		if err := r.Client.Deploy(
			pair.Post.Cluster.BaseURL,
			newDepID(),
			computeRequestID(pair.Prior),
			name,
			pair.Post.Resources,
			pair.Post.Env,
			pair.Post.DeployConfig.Volumes,
		); err != nil {
			return err
		}
	}
	return nil
}

func (r deployer) changesReq(pair *sous.DeployablePair) bool {
	return pair.Prior.NumInstances != pair.Post.NumInstances
}

func changesDep(pair *sous.DeployablePair) bool {
	return !(pair.Prior.SourceID.Equal(pair.Post.SourceID) &&
		pair.Prior.Resources.Equal(pair.Post.Resources) &&
		pair.Prior.Env.Equal(pair.Post.Env) &&
		pair.Prior.DeployConfig.Volumes.Equal(pair.Post.DeployConfig.Volumes))
}

func computeRequestID(d *sous.Deployable) string {
	if len(d.RequestID) > 0 {
		return d.RequestID
	}
	return MakeRequestID(d.ID())
}

// MakeRequestID creats a Singularity request ID from a sous.DeployID.
func MakeRequestID(mid sous.DeployID) string {
	sl := strings.Replace(mid.ManifestID.Source.String(), "/", ">", -1)
	return fmt.Sprintf("%s:%s:%s", sl, mid.ManifestID.Flavor, mid.Cluster)
}

// ParseRequestID parses a DeployID from a Singularity request ID created by
// Sous.
func ParseRequestID(id string) (sous.DeployID, error) {
	parts := strings.Split(id, ":")
	if len(parts) != 3 {
		return sous.DeployID{}, fmt.Errorf("request ID %q should contain exactly 2 colons", id)
	}
	if len(parts[0]) == 0 {
		return sous.DeployID{}, fmt.Errorf("request ID %q has an empty SourceLocation", id)
	}
	if len(parts[2]) == 0 {
		return sous.DeployID{}, fmt.Errorf("request ID %q has an empty Cluster name", id)
	}
	parts[0] = strings.Replace(parts[0], ">", "/", -1)
	slParts := strings.Split(parts[0], ",")
	if len(slParts) == 1 {
		slParts = append(slParts, "")
	}

	return sous.DeployID{
		ManifestID: sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: slParts[0],
				Dir:  slParts[1],
			},
			Flavor: parts[1],
		},
		Cluster: parts[2],
	}, nil
}

func newDepID() string {
	return MakeDeployID(uuid.NewV4().String())
}
<commit_msg>singularity: Fix typo.<commit_after>package singularity

import (
	"fmt"
	"log"
	"runtime/debug"
	"strings"

	"github.com/opentable/go-singularity"
	"github.com/opentable/sous/lib"
	"github.com/pkg/errors"
	"github.com/satori/go.uuid"
)

/*
The imagined use case here is like this:

intendedSet := getFromManifests()
existingSet := getFromSingularity()

dChans := intendedSet.Diff(existingSet)
*/

type (
	deployer struct {
		Client  rectificationClient
		singFac func(string) *singularity.Client
	}

	// rectificationClient abstracts the raw interactions with Singularity.
	rectificationClient interface {
		// Deploy creates a new deploy on a particular requeust
		Deploy(cluster, depID, reqID, dockerImage string, r sous.Resources, e sous.Env, vols sous.Volumes) error

		// PostRequest sends a request to a Singularity cluster to initiate
		PostRequest(cluster, reqID string, instanceCount int, kind sous.ManifestKind, owners sous.OwnerSet) error

		// DeleteRequest instructs Singularity to delete a particular request
		DeleteRequest(cluster, reqID, message string) error
	}

	// DTOMap is shorthand for map[string]interface{}
	dtoMap map[string]interface{}
)

// NewDeployer creates a new Singularity-based sous.Deployer.
func NewDeployer(c rectificationClient) sous.Deployer {
	return &deployer{Client: c}
}

func (r *deployer) RectifyCreates(cc <-chan *sous.Deployable, errs chan<- sous.DiffResolution) {
	for d := range cc {
		result := sous.DiffResolution{DeployID: d.ID()}
		if err := r.RectifySingleCreate(d); err != nil {
			result.Error = sous.WrapResolveError(&sous.CreateError{Deployment: d.Deployment, Err: err})
			result.Desc = "not created"
		} else {
			result.Desc = "created"
		}
		errs <- result
	}
}

func (r *deployer) SetSingularityFactory(fn func(string) *singularity.Client) {
	r.singFac = fn
}

func (r *deployer) buildSingClient(url string) *singularity.Client {
	if r.singFac == nil {
		return singularity.NewClient(url)
	}
	return r.singFac(url)
}

func (r *deployer) ImageName(d *sous.Deployable) (string, error) {
	if d.BuildArtifact == nil {
		return "", &sous.MissingImageNameError{Cause: fmt.Errorf("Missing BuildArtifact on Deployable")}
	}
	return d.BuildArtifact.Name, nil
}

func rectifyRecover(d interface{}, f string, err *error) {
	if r := recover(); r != nil {
		sous.Log.Warn.Printf("Panic in %s with %# v", f, d)
		sous.Log.Warn.Printf("  %v", r)
		sous.Log.Warn.Print(string(debug.Stack()))
		*err = errors.Errorf("Panicked")
	}
}

func (r *deployer) RectifySingleCreate(d *sous.Deployable) (err error) {
	Log.Debug.Printf("Rectifying creation %q:  \n %# v", d.ID(), d.Deployment)
	defer rectifyRecover(d, "RectifySingleCreate", &err)
	name, err := r.ImageName(d)
	if err != nil {
		return err
	}
	reqID := computeRequestID(d)
	if err = r.Client.PostRequest(d.Cluster.BaseURL, reqID, d.NumInstances, d.Kind, d.Owners); err != nil {
		return err
	}
	return r.Client.Deploy(
		d.Cluster.BaseURL, newDepID(), reqID, name, d.Resources,
		d.Env, d.DeployConfig.Volumes)
}

func (r *deployer) RectifyDeletes(dc <-chan *sous.Deployable, errs chan<- sous.DiffResolution) {
	for d := range dc {
		result := sous.DiffResolution{DeployID: d.ID()}
		if err := r.RectifySingleDelete(d); err != nil {
			result.Error = sous.WrapResolveError(&sous.DeleteError{Deployment: d.Deployment, Err: err})
			result.Desc = "not deleted"
		} else {
			result.Desc = "deleted"
		}
		errs <- result
	}
}

func (r *deployer) RectifySingleDelete(d *sous.Deployable) (err error) {
	defer rectifyRecover(d, "RectifySingleDelete", &err)
	requestID := computeRequestID(d)
	// TODO: Alert the owner of this request that there is no manifest for it;
	// they should either delete the request manually, or else add the manifest back.
	sous.Log.Warn.Printf("NOT DELETING REQUEST %q (FOR: %q)", requestID, d.ID())
	return nil
	// The following line deletes requests when it is not commented out.
	//return r.Client.DeleteRequest(d.Cluster.BaseURL, requestID, "deleting request for removed manifest")
}

func (r *deployer) RectifyModifies(
	mc <-chan *sous.DeployablePair, errs chan<- sous.DiffResolution) {
	for pair := range mc {
		result := sous.DiffResolution{DeployID: pair.ID()}
		if err := r.RectifySingleModification(pair); err != nil {
			dp := &sous.DeploymentPair{
				Prior: pair.Prior.Deployment,
				Post:  pair.Post.Deployment,
			}
			log.Printf("%#v", err)
			result.Error = sous.WrapResolveError(&sous.ChangeError{Deployments: dp, Err: err})
			result.Desc = "not updated"
		} else {
			result.Desc = "updated"
		}
		errs <- result
	}
}

func (r *deployer) RectifySingleModification(pair *sous.DeployablePair) (err error) {
	Log.Debug.Printf("Rectifying modified %q: \n  %# v \n    =>  \n  %# v", pair.ID(), pair.Prior.Deployment, pair.Post.Deployment)
	defer rectifyRecover(pair, "RectifySingleModification", &err)
	if r.changesReq(pair) {
		Log.Debug.Printf("Updating Request...")
		if err := r.Client.PostRequest(
			pair.Post.Cluster.BaseURL,
			computeRequestID(pair.Post),
			pair.Post.NumInstances,
			pair.Post.Kind,
			pair.Post.Owners,
		); err != nil {
			return err
		}
	}

	if changesDep(pair) {
		Log.Debug.Printf("Deploying...")
		name, err := r.ImageName(pair.Post)
		if err != nil {
			return err
		}

		if err := r.Client.Deploy(
			pair.Post.Cluster.BaseURL,
			newDepID(),
			computeRequestID(pair.Prior),
			name,
			pair.Post.Resources,
			pair.Post.Env,
			pair.Post.DeployConfig.Volumes,
		); err != nil {
			return err
		}
	}
	return nil
}

func (r deployer) changesReq(pair *sous.DeployablePair) bool {
	return pair.Prior.NumInstances != pair.Post.NumInstances
}

func changesDep(pair *sous.DeployablePair) bool {
	return !(pair.Prior.SourceID.Equal(pair.Post.SourceID) &&
		pair.Prior.Resources.Equal(pair.Post.Resources) &&
		pair.Prior.Env.Equal(pair.Post.Env) &&
		pair.Prior.DeployConfig.Volumes.Equal(pair.Post.DeployConfig.Volumes))
}

func computeRequestID(d *sous.Deployable) string {
	if len(d.RequestID) > 0 {
		return d.RequestID
	}
	return MakeRequestID(d.ID())
}

// MakeRequestID creats a Singularity request ID from a sous.DeployID.
func MakeRequestID(mid sous.DeployID) string {
	sl := strings.Replace(mid.ManifestID.Source.String(), "/", ">", -1)
	return fmt.Sprintf("%s:%s:%s", sl, mid.ManifestID.Flavor, mid.Cluster)
}

// ParseRequestID parses a DeployID from a Singularity request ID created by
// Sous.
func ParseRequestID(id string) (sous.DeployID, error) {
	parts := strings.Split(id, ":")
	if len(parts) != 3 {
		return sous.DeployID{}, fmt.Errorf("request ID %q should contain exactly 2 colons", id)
	}
	if len(parts[0]) == 0 {
		return sous.DeployID{}, fmt.Errorf("request ID %q has an empty SourceLocation", id)
	}
	if len(parts[2]) == 0 {
		return sous.DeployID{}, fmt.Errorf("request ID %q has an empty Cluster name", id)
	}
	parts[0] = strings.Replace(parts[0], ">", "/", -1)
	slParts := strings.Split(parts[0], ",")
	if len(slParts) == 1 {
		slParts = append(slParts, "")
	}

	return sous.DeployID{
		ManifestID: sous.ManifestID{
			Source: sous.SourceLocation{
				Repo: slParts[0],
				Dir:  slParts[1],
			},
			Flavor: parts[1],
		},
		Cluster: parts[2],
	}, nil
}

func newDepID() string {
	return MakeDeployID(uuid.NewV4().String())
}
<|endoftext|>


<commit_before>package balanced

import (
	"fmt"
	"net/http"
	"time"
)

type CardService struct {
	client *Client
}

type Card struct {
	ExpirationMonth int    `json:"expiration_month"` // Expiration month (e.g., 1 for January); required
	ExpirationYear  int    `json:"expiration_year"`  // Expiration year; required
	Number          string `json:"number"`           // The digits of the credit card number; required
	*Address        `json:"address,omitempty"`
	Customer        string            `json:"customer,omitempty"` // The customer this card is associated to
	Cvv             string            `json:"cvv,omitempty"`      // The 3-4 digit security code for the card
	Name            string            `json:"name,omitempty"`     // The customer's name on card
	AvsPostalMatch  interface{}       `json:"avs_postal_match,omitempty"`
	AvsResult       interface{}       `json:"avs_result,omitempty"`
	AvsStreetMatch  interface{}       `json:"avs_street_match,omitempty"`
	Brand           string            `json:"brand,omitempty"`      // e.g., "MasterCard"
	CvvMatch        string            `json:"cvv_match,omitempty"`  // e.g., "yes"
	CvvResult       string            `json:"cvv_result,omitempty"` // e.g., "Match"
	Fingerprint     string            `json:"fingerprint,omitempty"`
	Meta            map[string]string `json:"meta,omitempty"`
	Href            string            `json:"href,omitempty"` // e.g., "/cards/CC2t9628l4ecJics6T8RuLPf"
	Id              string            `json:"id,omitempty"`
	Links           *CardLinks        `json:"links,omitempty"`
	IsVerified      bool              `json:"is_verified,omitempty"`
	CreatedAt       *time.Time        `json:"created_at,omitempty"`
	UpdatedAt       *time.Time        `json:"updated_at,omitempty"`

}

type CardResponse struct {
	Cards []Card                 `json:"cards"`
	Links *CardResponseLinks     `json:"links"`
	Meta  map[string]interface{} `json:"meta"`
}

type CardResponseLinks struct {
	CardHolds string `json:"cards.card_holds"`
	Customers string `json:"cards.customers"`
	Debits    string `json:"cards.debits"`
}

type CardPage struct {
	Cards []Card
	*PaginationParams
}

// Creates a card on the server.
//
// Fraud and Card declinations can be reduced if the following information is
// supplied when tokenizing a card:
// Name (Name on card)
// Cvv
// PostalCode
// CountryCode (Country code, ISO 3166-1 alpha-3)
func (s *CardService) Create(card *Card) (*Card, *http.Response, error) {
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.POST("/cards", nil, card, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &cardResponse.Cards[0], httpResponse, nil
}

func (s *CardService) Delete(cardId string) (bool, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v", cardId)
	httpResponse, err := s.client.DELETE(path, nil, nil, nil)
	if err != nil {
		return false, httpResponse, err
	}
	code := httpResponse.StatusCode
	didDelete := 200 <= code && code < 300
	return didDelete, httpResponse, nil
}

func (s *CardService) Fetch(cardId string) (*Card, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v", cardId)
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.GET(path, nil, nil, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &cardResponse.Cards[0], httpResponse, nil
}

func (s *CardService) List(args ...interface{}) (*CardPage, *http.Response, error) {
	query := paginatedArgsToQuery(args)
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.GET("/cards", query, nil, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &CardPage{
		Cards:            cardResponse.Cards,
		PaginationParams: NewPaginationParams(cardResponse.Meta),
	}, httpResponse, nil
}

func (s *CardService) Update(cardId string, params map[string]interface{}) (*Card, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v", cardId)
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.PUT(path, nil, params, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &cardResponse.Cards[0], httpResponse, nil
}

func (s *CardService) AssociateWithCustomer(cardId, customerId string) (*Card, *http.Response, error) {
	return s.Update(cardId, map[string]interface{}{
		"customer": fmt.Sprintf("/customers/%v", customerId),
	})
}

func (s *CardService) Charge(cardId string, debit *Debit) (*Debit, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v/debits", cardId)
	debitResponse := new(DebitResponse)
	httpResponse, err := s.client.POST(path, nil, debit, debitResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &debitResponse.Debits[0], httpResponse, nil
}

func (s *CardService) Credit(cardId string, credit *Credit) (*Credit, *http.Response, error) {
	if credit.Amount > 250000 {
		return nil, nil, fmt.Errorf("Cannot credit more than $2,500 to a card, but tried crediting %.2f", float32(credit.Amount)/100)
	}
	path := fmt.Sprintf("/cards/%v/credits", cardId)
	creditResponse := new(CreditResponse)
	httpResponse, err := s.client.POST(path, nil, credit, creditResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &creditResponse.Credits[0], httpResponse, nil
}
<commit_msg>Introduce CardLinks type<commit_after>package balanced

import (
	"fmt"
	"net/http"
	"time"
)

type CardService struct {
	client *Client
}

type Card struct {
	ExpirationMonth int    `json:"expiration_month"` // Expiration month (e.g., 1 for January); required
	ExpirationYear  int    `json:"expiration_year"`  // Expiration year; required
	Number          string `json:"number"`           // The digits of the credit card number; required
	*Address        `json:"address,omitempty"`
	Customer        string            `json:"customer,omitempty"` // The customer this card is associated to
	Cvv             string            `json:"cvv,omitempty"`      // The 3-4 digit security code for the card
	Name            string            `json:"name,omitempty"`     // The customer's name on card
	AvsPostalMatch  interface{}       `json:"avs_postal_match,omitempty"`
	AvsResult       interface{}       `json:"avs_result,omitempty"`
	AvsStreetMatch  interface{}       `json:"avs_street_match,omitempty"`
	Brand           string            `json:"brand,omitempty"`      // e.g., "MasterCard"
	CvvMatch        string            `json:"cvv_match,omitempty"`  // e.g., "yes"
	CvvResult       string            `json:"cvv_result,omitempty"` // e.g., "Match"
	Fingerprint     string            `json:"fingerprint,omitempty"`
	Meta            map[string]string `json:"meta,omitempty"`
	Href            string            `json:"href,omitempty"` // e.g., "/cards/CC2t9628l4ecJics6T8RuLPf"
	Id              string            `json:"id,omitempty"`
	Links           *CardLinks        `json:"links,omitempty"`
	IsVerified      bool              `json:"is_verified,omitempty"`
	CreatedAt       *time.Time        `json:"created_at,omitempty"`
	UpdatedAt       *time.Time        `json:"updated_at,omitempty"`
}

type CardLinks struct {
	Customer string `json:"customer"`
}

type CardResponse struct {
	Cards []Card                 `json:"cards"`
	Links *CardResponseLinks     `json:"links"`
	Meta  map[string]interface{} `json:"meta"`
}

type CardResponseLinks struct {
	CardHolds string `json:"cards.card_holds"`
	Customers string `json:"cards.customers"`
	Debits    string `json:"cards.debits"`
}

type CardPage struct {
	Cards []Card
	*PaginationParams
}

// Creates a card on the server.
//
// Fraud and Card declinations can be reduced if the following information is
// supplied when tokenizing a card:
// Name (Name on card)
// Cvv
// PostalCode
// CountryCode (Country code, ISO 3166-1 alpha-3)
func (s *CardService) Create(card *Card) (*Card, *http.Response, error) {
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.POST("/cards", nil, card, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &cardResponse.Cards[0], httpResponse, nil
}

func (s *CardService) Delete(cardId string) (bool, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v", cardId)
	httpResponse, err := s.client.DELETE(path, nil, nil, nil)
	if err != nil {
		return false, httpResponse, err
	}
	code := httpResponse.StatusCode
	didDelete := 200 <= code && code < 300
	return didDelete, httpResponse, nil
}

func (s *CardService) Fetch(cardId string) (*Card, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v", cardId)
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.GET(path, nil, nil, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &cardResponse.Cards[0], httpResponse, nil
}

func (s *CardService) List(args ...interface{}) (*CardPage, *http.Response, error) {
	query := paginatedArgsToQuery(args)
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.GET("/cards", query, nil, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &CardPage{
		Cards:            cardResponse.Cards,
		PaginationParams: NewPaginationParams(cardResponse.Meta),
	}, httpResponse, nil
}

func (s *CardService) Update(cardId string, params map[string]interface{}) (*Card, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v", cardId)
	cardResponse := new(CardResponse)
	httpResponse, err := s.client.PUT(path, nil, params, cardResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &cardResponse.Cards[0], httpResponse, nil
}

func (s *CardService) AssociateWithCustomer(cardId, customerId string) (*Card, *http.Response, error) {
	return s.Update(cardId, map[string]interface{}{
		"customer": fmt.Sprintf("/customers/%v", customerId),
	})
}

func (s *CardService) Charge(cardId string, debit *Debit) (*Debit, *http.Response, error) {
	path := fmt.Sprintf("/cards/%v/debits", cardId)
	debitResponse := new(DebitResponse)
	httpResponse, err := s.client.POST(path, nil, debit, debitResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &debitResponse.Debits[0], httpResponse, nil
}

func (s *CardService) Credit(cardId string, credit *Credit) (*Credit, *http.Response, error) {
	if credit.Amount > 250000 {
		return nil, nil, fmt.Errorf("Cannot credit more than $2,500 to a card, but tried crediting %.2f", float32(credit.Amount)/100)
	}
	path := fmt.Sprintf("/cards/%v/credits", cardId)
	creditResponse := new(CreditResponse)
	httpResponse, err := s.client.POST(path, nil, credit, creditResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &creditResponse.Credits[0], httpResponse, nil
}
<|endoftext|>


<commit_before>package graphql

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/samsarahq/thunder/reactive"
)

const (
	MaxSubscriptions = 200
	MinRerunInterval = 1 * time.Second
)

type JSONSocket interface {
	ReadJSON(value interface{}) error
	WriteJSON(value interface{}) error
	Close() error
}

type MakeCtxFunc func(context.Context) context.Context

type GraphqlLogger interface {
	StartExecution(ctx context.Context, tags map[string]string, initial bool)
	FinishExecution(ctx context.Context, tags map[string]string, delay time.Duration)
	Error(ctx context.Context, err error, tags map[string]string)
}

type conn struct {
	writeMu sync.Mutex
	socket  JSONSocket

	schema  *Schema
	makeCtx MakeCtxFunc
	logger  GraphqlLogger

	url string

	mu            sync.Mutex
	subscriptions map[string]*reactive.Rerunner
}

type inEnvelope struct {
	ID      string          `json:"id"`
	Type    string          `json:"type"`
	Message json.RawMessage `json:"message"`
}

type outEnvelope struct {
	ID      string      `json:"id,omitempty"`
	Type    string      `json:"type"`
	Message interface{} `json:"message,omitempty"`
}

type subscribeMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type mutateMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type SanitizedError interface {
	error
	SanitizedError() string
}

type SafeError struct {
	message string
}

func (e SafeError) Error() string {
	return e.message
}

func (e SafeError) SanitizedError() string {
	return e.message
}

func NewSafeError(format string, a ...interface{}) error {
	return SafeError{message: fmt.Sprintf(format, a...)}
}

func sanitizeError(err error) string {
	if sanitized, ok := err.(SanitizedError); ok {
		return sanitized.SanitizedError()
	}
	return "Internal server error"
}

func isCloseError(err error) bool {
	_, ok := err.(*websocket.CloseError)
	return ok || err == websocket.ErrCloseSent
}

func (c *conn) writeOrClose(id string, typ string, message interface{}) {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()

	if err := c.socket.WriteJSON(outEnvelope{
		ID:      id,
		Type:    typ,
		Message: message,
	}); err != nil {
		if !isCloseError(err) {
			c.socket.Close()
			log.Printf("socket.WriteJSON: %s\n", err)
		}
	}
}

func mustMarshalJson(v interface{}) string {
	bytes, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}
	return string(bytes)
}

func (c *conn) handleSubscribe(id string, subscribe *subscribeMessage) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.subscriptions[id]; ok {
		return NewSafeError("duplicate subscription")
	}

	if len(c.subscriptions)+1 > MaxSubscriptions {
		return NewSafeError("too many subscriptions")
	}

	selectionSet, err := Parse(subscribe.Query, subscribe.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Query, selectionSet); err != nil {
		return err
	}

	var previous interface{}

	e := Executor{}

	initial := true
	tags := map[string]string{"url": c.url, "query": subscribe.Query, "queryVariables": mustMarshalJson(subscribe.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(ctx)

		start := time.Now()
		c.logger.StartExecution(ctx, tags, initial)
		current, err := e.Execute(ctx, c.schema.Query, nil, selectionSet)
		c.logger.FinishExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)

			if extractPathError(err) == context.Canceled {
				return nil, err
			}

			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, diff := Diff(previous, current)
		previous = current
		initial = false

		if diff {
			c.writeOrClose(id, "update", PrepareForMarshal(delta))
		}

		return nil, nil
	}, MinRerunInterval)

	return nil
}

func (c *conn) handleMutate(id string, mutate *mutateMessage) error {
	// TODO: deduplicate code
	c.mu.Lock()
	defer c.mu.Unlock()

	selectionSet, err := Parse(mutate.Query, mutate.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Mutation, selectionSet); err != nil {
		return err
	}

	e := Executor{}

	tags := map[string]string{"url": c.url, "query": mutate.Query, "queryVariables": mustMarshalJson(mutate.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(context.Background())

		start := time.Now()
		c.logger.StartExecution(ctx, tags, true)
		current, err := e.Execute(ctx, c.schema.Mutation, c.schema.Mutation, selectionSet)
		c.logger.FinishExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)

			if extractPathError(err) == context.Canceled {
				return nil, err
			}

			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, _ := Diff(nil, current)
		c.writeOrClose(id, "result", PrepareForMarshal(delta))

		return nil, errors.New("stop")
	}, MinRerunInterval)

	return nil
}

func (c *conn) closeSubscription(id string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if runner, ok := c.subscriptions[id]; ok {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) closeSubscriptions() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for id, runner := range c.subscriptions {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) handle(e *inEnvelope) error {
	switch e.Type {
	case "subscribe":
		var subscribe subscribeMessage
		if err := json.Unmarshal(e.Message, &subscribe); err != nil {
			return err
		}
		return c.handleSubscribe(e.ID, &subscribe)

	case "unsubscribe":
		c.closeSubscription(e.ID)
		return nil

	case "mutate":
		var mutate mutateMessage
		if err := json.Unmarshal(e.Message, &mutate); err != nil {
			return err
		}
		return c.handleMutate(e.ID, &mutate)

	case "echo":
		c.writeOrClose(e.ID, "echo", nil)
		return nil

	case "url":
		var url string
		if err := json.Unmarshal(e.Message, &url); err != nil {
			return err
		}
		c.url = url
		return nil

	default:
		return NewSafeError("unknown message type")
	}
}

type simpleLogger struct {
}

func (s *simpleLogger) StartExecution(ctx context.Context, tags map[string]string, initial bool) {
}
func (s *simpleLogger) FinishExecution(ctx context.Context, tags map[string]string, delay time.Duration) {
}
func (s *simpleLogger) Error(ctx context.Context, err error, tags map[string]string) {
	log.Printf("error:%v\n%s", tags, err)
}

func Handler(schema *Schema) http.Handler {
	upgrader := &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		socket, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Printf("upgrader.Upgrade: %v", err)
			return
		}
		defer socket.Close()

		makeCtx := func(ctx context.Context) context.Context {
			return ctx
		}

		ServeJSONSocket(socket, schema, makeCtx, &simpleLogger{})
	})
}

func ServeJSONSocket(socket JSONSocket, schema *Schema, makeCtx MakeCtxFunc, logger GraphqlLogger) {
	c := &conn{
		socket: socket,

		schema:  schema,
		makeCtx: makeCtx,
		logger:  logger,

		subscriptions: make(map[string]*reactive.Rerunner),
	}

	defer c.closeSubscriptions()

	for {
		var envelope inEnvelope
		if err := c.socket.ReadJSON(&envelope); err != nil {
			if !isCloseError(err) {
				log.Println("socket.ReadJSON:", err)
			}
			return
		}

		if err := c.handle(&envelope); err != nil {
			log.Println("c.handle:", err)
			c.writeOrClose(envelope.ID, "error", sanitizeError(err))
		}
	}
}
<commit_msg>graphql: change MinRerunInterval back to 5 seconds<commit_after>package graphql

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/samsarahq/thunder/reactive"
)

const (
	MaxSubscriptions = 200
	MinRerunInterval = 5 * time.Second
)

type JSONSocket interface {
	ReadJSON(value interface{}) error
	WriteJSON(value interface{}) error
	Close() error
}

type MakeCtxFunc func(context.Context) context.Context

type GraphqlLogger interface {
	StartExecution(ctx context.Context, tags map[string]string, initial bool)
	FinishExecution(ctx context.Context, tags map[string]string, delay time.Duration)
	Error(ctx context.Context, err error, tags map[string]string)
}

type conn struct {
	writeMu sync.Mutex
	socket  JSONSocket

	schema  *Schema
	makeCtx MakeCtxFunc
	logger  GraphqlLogger

	url string

	mu            sync.Mutex
	subscriptions map[string]*reactive.Rerunner
}

type inEnvelope struct {
	ID      string          `json:"id"`
	Type    string          `json:"type"`
	Message json.RawMessage `json:"message"`
}

type outEnvelope struct {
	ID      string      `json:"id,omitempty"`
	Type    string      `json:"type"`
	Message interface{} `json:"message,omitempty"`
}

type subscribeMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type mutateMessage struct {
	Query     string                 `json:"query"`
	Variables map[string]interface{} `json:"variables"`
}

type SanitizedError interface {
	error
	SanitizedError() string
}

type SafeError struct {
	message string
}

func (e SafeError) Error() string {
	return e.message
}

func (e SafeError) SanitizedError() string {
	return e.message
}

func NewSafeError(format string, a ...interface{}) error {
	return SafeError{message: fmt.Sprintf(format, a...)}
}

func sanitizeError(err error) string {
	if sanitized, ok := err.(SanitizedError); ok {
		return sanitized.SanitizedError()
	}
	return "Internal server error"
}

func isCloseError(err error) bool {
	_, ok := err.(*websocket.CloseError)
	return ok || err == websocket.ErrCloseSent
}

func (c *conn) writeOrClose(id string, typ string, message interface{}) {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()

	if err := c.socket.WriteJSON(outEnvelope{
		ID:      id,
		Type:    typ,
		Message: message,
	}); err != nil {
		if !isCloseError(err) {
			c.socket.Close()
			log.Printf("socket.WriteJSON: %s\n", err)
		}
	}
}

func mustMarshalJson(v interface{}) string {
	bytes, err := json.Marshal(v)
	if err != nil {
		panic(err)
	}
	return string(bytes)
}

func (c *conn) handleSubscribe(id string, subscribe *subscribeMessage) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.subscriptions[id]; ok {
		return NewSafeError("duplicate subscription")
	}

	if len(c.subscriptions)+1 > MaxSubscriptions {
		return NewSafeError("too many subscriptions")
	}

	selectionSet, err := Parse(subscribe.Query, subscribe.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Query, selectionSet); err != nil {
		return err
	}

	var previous interface{}

	e := Executor{}

	initial := true
	tags := map[string]string{"url": c.url, "query": subscribe.Query, "queryVariables": mustMarshalJson(subscribe.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(ctx)

		start := time.Now()
		c.logger.StartExecution(ctx, tags, initial)
		current, err := e.Execute(ctx, c.schema.Query, nil, selectionSet)
		c.logger.FinishExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)

			if extractPathError(err) == context.Canceled {
				return nil, err
			}

			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, diff := Diff(previous, current)
		previous = current
		initial = false

		if diff {
			c.writeOrClose(id, "update", PrepareForMarshal(delta))
		}

		return nil, nil
	}, MinRerunInterval)

	return nil
}

func (c *conn) handleMutate(id string, mutate *mutateMessage) error {
	// TODO: deduplicate code
	c.mu.Lock()
	defer c.mu.Unlock()

	selectionSet, err := Parse(mutate.Query, mutate.Variables)
	if err != nil {
		return err
	}
	if err := PrepareQuery(c.schema.Mutation, selectionSet); err != nil {
		return err
	}

	e := Executor{}

	tags := map[string]string{"url": c.url, "query": mutate.Query, "queryVariables": mustMarshalJson(mutate.Variables), "id": id}

	c.subscriptions[id] = reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		ctx = c.makeCtx(context.Background())

		start := time.Now()
		c.logger.StartExecution(ctx, tags, true)
		current, err := e.Execute(ctx, c.schema.Mutation, c.schema.Mutation, selectionSet)
		c.logger.FinishExecution(ctx, tags, time.Since(start))

		if err != nil {
			c.writeOrClose(id, "error", sanitizeError(err))
			go c.closeSubscription(id)

			if extractPathError(err) == context.Canceled {
				return nil, err
			}

			if _, ok := err.(SanitizedError); !ok {
				c.logger.Error(ctx, err, tags)
			}
			return nil, err
		}

		delta, _ := Diff(nil, current)
		c.writeOrClose(id, "result", PrepareForMarshal(delta))

		return nil, errors.New("stop")
	}, MinRerunInterval)

	return nil
}

func (c *conn) closeSubscription(id string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if runner, ok := c.subscriptions[id]; ok {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) closeSubscriptions() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for id, runner := range c.subscriptions {
		runner.Stop()
		delete(c.subscriptions, id)
	}
}

func (c *conn) handle(e *inEnvelope) error {
	switch e.Type {
	case "subscribe":
		var subscribe subscribeMessage
		if err := json.Unmarshal(e.Message, &subscribe); err != nil {
			return err
		}
		return c.handleSubscribe(e.ID, &subscribe)

	case "unsubscribe":
		c.closeSubscription(e.ID)
		return nil

	case "mutate":
		var mutate mutateMessage
		if err := json.Unmarshal(e.Message, &mutate); err != nil {
			return err
		}
		return c.handleMutate(e.ID, &mutate)

	case "echo":
		c.writeOrClose(e.ID, "echo", nil)
		return nil

	case "url":
		var url string
		if err := json.Unmarshal(e.Message, &url); err != nil {
			return err
		}
		c.url = url
		return nil

	default:
		return NewSafeError("unknown message type")
	}
}

type simpleLogger struct {
}

func (s *simpleLogger) StartExecution(ctx context.Context, tags map[string]string, initial bool) {
}
func (s *simpleLogger) FinishExecution(ctx context.Context, tags map[string]string, delay time.Duration) {
}
func (s *simpleLogger) Error(ctx context.Context, err error, tags map[string]string) {
	log.Printf("error:%v\n%s", tags, err)
}

func Handler(schema *Schema) http.Handler {
	upgrader := &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		socket, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Printf("upgrader.Upgrade: %v", err)
			return
		}
		defer socket.Close()

		makeCtx := func(ctx context.Context) context.Context {
			return ctx
		}

		ServeJSONSocket(socket, schema, makeCtx, &simpleLogger{})
	})
}

func ServeJSONSocket(socket JSONSocket, schema *Schema, makeCtx MakeCtxFunc, logger GraphqlLogger) {
	c := &conn{
		socket: socket,

		schema:  schema,
		makeCtx: makeCtx,
		logger:  logger,

		subscriptions: make(map[string]*reactive.Rerunner),
	}

	defer c.closeSubscriptions()

	for {
		var envelope inEnvelope
		if err := c.socket.ReadJSON(&envelope); err != nil {
			if !isCloseError(err) {
				log.Println("socket.ReadJSON:", err)
			}
			return
		}

		if err := c.handle(&envelope); err != nil {
			log.Println("c.handle:", err)
			c.writeOrClose(envelope.ID, "error", sanitizeError(err))
		}
	}
}
<|endoftext|>


<commit_before>package main

import "flag"

import (
    "net"
    "fmt"
    "encoding/binary"
    "io"
)

func recv_packet(conn io.Reader) uint32 {
    msg := new(uint32)

    err := binary.Read(conn, binary.BigEndian, msg)

    if err != nil {
        if err.Error() == "EOF" {
            return 99
        }
        fmt.Println("binary.Read failed:", err)
    }

    return *msg
}

type NegBody struct {
    ID uint32
}

func send_neg(conn net.Conn, ID uint32) {
    var Type uint32
    Type = 0

    msg := new(NegBody)
    msg.ID = ID

    err := binary.Write(conn, binary.BigEndian, Type)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
}

func recv_neg(conn io.Reader) uint32 {
    msg := new(NegBody)

    err := binary.Read(conn, binary.BigEndian, msg)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    fmt.Println(msg)
    return msg.ID
}

type InitBody struct {
    Clock uint32
    RAND [16]byte
    Link_key [16]byte
}

func recv_init(conn io.Reader) (clock uint32, RAND, Link_key [16]byte) {
    msg := new(InitBody)

    err := binary.Read(conn, binary.BigEndian, msg)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    fmt.Println(msg)

    clock = msg.Clock
    RAND = msg.RAND
    Link_key = msg.Link_key
    return
}

func send_init(conn net.Conn, clock uint32, RAND, Link_key [16]byte) {
    var Type uint32
    Type = 1

    msg := new(InitBody)

    msg.Clock = clock
    msg.RAND = RAND
    msg.Link_key = Link_key

    err := binary.Write(conn, binary.BigEndian, Type)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
}

type DataBody struct {
    Clock uint32
    Length uint32
    Data []byte
}

func recv_data(conn io.Reader) (clock uint32, Data []byte) {
    msg := new(DataBody)

    err := binary.Read(conn, binary.BigEndian, &msg.Clock)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    err = binary.Read(conn, binary.BigEndian, &msg.Length)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    msg.Data = make([]byte, msg.Length)

    err = binary.Read(conn, binary.BigEndian, msg.Data)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    fmt.Println(msg)

    clock, Data = msg.Clock, msg.Data
    return
}

func send_data(conn net.Conn, clock uint32, Data []byte) {
    var Type uint32
    Type = 2

    msg := new(DataBody)

    msg.Data = Data
    msg.Length = uint32(len(msg.Data))
    msg.Clock = clock

    err := binary.Write(conn, binary.BigEndian, Type)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg.Clock)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg.Length)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg.Data)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
}

func server_main() net.Conn {
    ln, err := net.Listen("tcp", ":8080")

    if err != nil {
        fmt.Println("net.Listen failed:", err)
    }

    conn, err := ln.Accept()

    fmt.Printf("Accepted Connection\n")

    if err != nil {
        fmt.Println("ln.Accept failed:", err)
    }

    return conn

}

func client_main() net.Conn {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println("net.Dial failed:", err)
    }

    return conn

}

func main() {
    isServerPtr := flag.Bool("server", false, "Run in server mode?")

    flag.Parse()

    fmt.Println("Is Server: ", *isServerPtr)

    var conn net.Conn

    if *isServerPtr {
        conn = server_main()
    } else {
        conn = client_main()
    }

    for i := uint32(0); i < 20; i++ {

        fmt.Println(i)

        send_neg(conn, 56)
        send_init(conn, i, [16]byte{}, [16]byte{})
        send_data(conn, i, []byte{100, 200, 120})

        for j := 0; j < 3; j++ {
            packet_type := recv_packet(conn)

            switch packet_type {
                case 0: recv_neg(conn)
                case 1: recv_init(conn)
                case 2: recv_data(conn)
                case 99: break

            }
        }
    }

    conn.Close()

}
<commit_msg>Tidied up comms code a little<commit_after>package main

import "flag"

import (
    "net"
    "fmt"
    "encoding/binary"
    "io"
)

func recv_packet(conn io.Reader) (Type uint32) {
    err := binary.Read(conn, binary.BigEndian, &Type)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    return
}

type NegBody struct {
    ID uint32
}

func recv_neg(conn io.Reader) (ID uint32) {
    var msg NegBody
    err := binary.Read(conn, binary.BigEndian, &msg)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    fmt.Println(msg)
    return msg.ID
}

func send_neg(conn io.Writer, ID uint32) {
    Type := uint32(0)
    var msg NegBody

    msg.ID = ID

    err := binary.Write(conn, binary.BigEndian, Type)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
}

type InitBody struct {
    Clock uint32
    RAND [16]byte
    Link_key [16]byte
}

func recv_init(conn io.Reader) (clock uint32, RAND, Link_key [16]byte) {
    var msg InitBody

    err := binary.Read(conn, binary.BigEndian, &msg)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    fmt.Println(msg)

    clock = msg.Clock
    RAND = msg.RAND
    Link_key = msg.Link_key
    return
}

func send_init(conn io.Writer, clock uint32, RAND, Link_key [16]byte) {
    Type := uint32(1)
    var msg InitBody

    msg.Clock = clock
    msg.RAND = RAND
    msg.Link_key = Link_key

    err := binary.Write(conn, binary.BigEndian, Type)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
}

type DataBody struct {
    Clock uint32
    Length uint32
    Data []byte
}

func recv_data(conn io.Reader) (clock uint32, Data []byte) {
    var msg DataBody

    err := binary.Read(conn, binary.BigEndian, &msg.Clock)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    err = binary.Read(conn, binary.BigEndian, &msg.Length)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    msg.Data = make([]byte, msg.Length)

    err = binary.Read(conn, binary.BigEndian, &msg.Data)

    if err != nil {
        fmt.Println("binary.Read failed:", err)
    }

    fmt.Println(msg)

    clock, Data = msg.Clock, msg.Data
    return
}

func send_data(conn io.Writer, clock uint32, Data []byte) {
    Type := uint32(2)

    var msg DataBody

    msg.Data = Data
    msg.Length = uint32(len(msg.Data))
    msg.Clock = clock

    err := binary.Write(conn, binary.BigEndian, Type)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg.Clock)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg.Length)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }

    err = binary.Write(conn, binary.BigEndian, msg.Data)

    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
}

func server_main() net.Conn {
    ln, err := net.Listen("tcp", ":8080")

    if err != nil {
        fmt.Println("net.Listen failed:", err)
    }

    conn, err := ln.Accept()

    fmt.Printf("Accepted Connection\n")

    if err != nil {
        fmt.Println("ln.Accept failed:", err)
    }

    return conn
}

func client_main() net.Conn {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")

    if err != nil {
        fmt.Println("net.Dial failed:", err)
    }

    return conn
}

func main() {
    isServerPtr := flag.Bool("server", false, "Run in server mode?")
    flag.Parse()
    fmt.Println("Is Server: ", *isServerPtr)

    var conn net.Conn

    if *isServerPtr {
        conn = server_main()
    } else {
        conn = client_main()
    }

    for i := uint32(0); i < 20; i++ {
        fmt.Println("Round: ", i)

        send_neg(conn, 56)
        send_init(conn, i, [16]byte{}, [16]byte{})
        send_data(conn, i, []byte{'f', 'o', 'o'})

        for j := 0; j < 3; j++ {
            packet_type := recv_packet(conn)

            switch packet_type {
                case 0: recv_neg(conn)
                case 1: recv_init(conn)
                case 2: recv_data(conn)
                case 99: break
            }
        }
    }

    conn.Close()
}
<|endoftext|>


<commit_before>package resolv

// RequestFactory creates multiple requests using opts.
type RequestFactory struct {
	opts []RequestOption
}

func NewRequestFactory(opts ...RequestOption) *RequestFactory {
	return &RequestFactory{
		opts: opts,
	}
}

func (f *RequestFactory) FromNames(addr string, type_ uint16, names ...string) []*Request {
	var reqs []*Request

	for _, name := range names {
		r := NewRequest(addr, name, type_, f.opts...)
		reqs = append(reqs, r)
	}

	return reqs
}

func (f *RequestFactory) FromTypes(addr, name string, types ...uint16) []*Request {
	var reqs []*Request

	for _, type_ := range types {
		r := NewRequest(addr, name, type_, f.opts...)
		reqs = append(reqs, r)
	}

	return reqs
}
<commit_msg>Update RequestFactory comments<commit_after>package resolv

// RequestFactory creates multiple requests using custom options.
type RequestFactory struct {
	opts []RequestOption
}

// NewRequestFactory creates a factory using custom options.
func NewRequestFactory(opts ...RequestOption) *RequestFactory {
	return &RequestFactory{
		opts: opts,
	}
}

// FromNames creates multiple requests using different names.
func (f *RequestFactory) FromNames(addr string, type_ uint16, names ...string) []*Request {
	var reqs []*Request

	for _, name := range names {
		r := NewRequest(addr, name, type_, f.opts...)
		reqs = append(reqs, r)
	}

	return reqs
}

// FromTypes creates multiple requests using different types.
func (f *RequestFactory) FromTypes(addr, name string, types ...uint16) []*Request {
	var reqs []*Request

	for _, type_ := range types {
		r := NewRequest(addr, name, type_, f.opts...)
		reqs = append(reqs, r)
	}

	return reqs
}
<|endoftext|>