<commit_before>// Package new generates micro service templates
package new

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
	"time"

	"github.com/micro/cli"
	tmpl "github.com/micro/micro/internal/template"
	"github.com/micro/micro/internal/usage"
	"github.com/xlab/treeprint"
)

type config struct {
	// foo
	Alias string
	// micro new example -type
	Command string
	// go.micro
	Namespace string
	// api, srv, web, fnc
	Type string
	// go.micro.srv.foo
	FQDN string
	// github.com/micro/foo
	Dir string
	// $GOPATH/src/github.com/micro/foo
	GoDir string
	// $GOPATH
	GoPath string
	// Files
	Files []file
	// Comments
	Comments []string
	// Plugins registry=etcd:broker=nats
	Plugins []string
}

type file struct {
	Path string
	Tmpl string
}

func write(c config, file, tmpl string) error {
	fn := template.FuncMap{
		"title": strings.Title,
	}

	f, err := os.Create(file)
	if err != nil {
		return err
	}
	defer f.Close()

	t, err := template.New("f").Funcs(fn).Parse(tmpl)
	if err != nil {
		return err
	}

	return t.Execute(f, c)
}

func create(c config) error {
	// check if dir exists
	if _, err := os.Stat(c.GoDir); !os.IsNotExist(err) {
		return fmt.Errorf("%s already exists", c.GoDir)
	}

	// create usage report
	u := usage.New("new")
	// a single request/service
	u.Metrics.Count["requests"] = uint64(1)
	u.Metrics.Count["services"] = uint64(1)
	// send report
	go usage.Report(u)

	// just wait
	<-time.After(time.Millisecond * 250)

	fmt.Printf("Creating service %s in %s\n\n", c.FQDN, c.GoDir)

	t := treeprint.New()

	nodes := map[string]treeprint.Tree{}
	nodes[c.GoDir] = t

	// write the files
	for _, file := range c.Files {
		f := filepath.Join(c.GoDir, file.Path)
		dir := filepath.Dir(f)

		b, ok := nodes[dir]
		if !ok {
			d, _ := filepath.Rel(c.GoDir, dir)
			b = t.AddBranch(d)
			nodes[dir] = b
		}

		if _, err := os.Stat(dir); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, 0755); err != nil {
				return err
			}
		}

		p := filepath.Base(f)

		b.AddNode(p)
		if err := write(c, f, file.Tmpl); err != nil {
			return err
		}
	}

	// print tree
	fmt.Println(t.String())

	for _, comment := range c.Comments {
		fmt.Println(comment)
	}

	// just wait
	<-time.After(time.Millisecond * 250)

	return nil
}

func run(ctx *cli.Context) {
	namespace := ctx.String("namespace")
	alias := ctx.String("alias")
	fqdn := ctx.String("fqdn")
	atype := ctx.String("type")
	dir := ctx.Args().First()
	useGoPath := ctx.Bool("gopath")
	useGoModule := os.Getenv("GO111MODULE")
	var plugins []string

	if len(dir) == 0 {
		fmt.Println("specify service name")
		return
	}

	if len(namespace) == 0 {
		fmt.Println("namespace not defined")
		return
	}

	if len(atype) == 0 {
		fmt.Println("type not defined")
		return
	}

	// set the command
	command := fmt.Sprintf("micro new %s", dir)
	if len(namespace) > 0 {
		command += " --namespace=" + namespace
	}
	if len(alias) > 0 {
		command += " --alias=" + alias
	}
	if len(fqdn) > 0 {
		command += " --fqdn=" + fqdn
	}
	if len(atype) > 0 {
		command += " --type=" + atype
	}
	if plugins := ctx.StringSlice("plugin"); len(plugins) > 0 {
		command += " --plugin=" + strings.Join(plugins, ":")
	}

	// check if the path is absolute, we don't want this
	// we want to a relative path so we can install in GOPATH
	if path.IsAbs(dir) {
		fmt.Println("require relative path as service will be installed in GOPATH")
		return
	}

	var goPath string
	var goDir string

	// only set gopath if told to use it
	if useGoPath {
		goPath = os.Getenv("GOPATH")

		// don't know GOPATH, runaway....
		if len(goPath) == 0 {
			fmt.Println("unknown GOPATH")
			return
		}

		// attempt to split path if not windows
		if runtime.GOOS == "windows" {
			goPath = strings.Split(goPath, ";")[0]
		} else {
			goPath = strings.Split(goPath, ":")[0]
		}
		goDir = filepath.Join(goPath, "src", path.Clean(dir))
	} else {
		goDir = path.Clean(dir)
	}

	if len(alias) == 0 {
		// set as last part
		alias = filepath.Base(dir)
		// strip hyphens
		parts := strings.Split(alias, "-")
		alias = parts[0]
	}

	if len(fqdn) == 0 {
		fqdn = strings.Join([]string{namespace, atype, alias}, ".")
	}

	for _, plugin := range ctx.StringSlice("plugin") {
		// registry=etcd:broker=nats
		for _, p := range strings.Split(plugin, ":") {
			// registry=etcd
			parts := strings.Split(p, "=")
			if len(parts) < 2 {
				continue
			}
			plugins = append(plugins, path.Join(parts...))
		}
	}

	var c config

	switch atype {
	case "fnc":
		// create srv config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainFNC},
				{"generate.go", tmpl.GenerateFile},
				{"plugin.go", tmpl.Plugin},
				{"handler/" + alias + ".go", tmpl.HandlerFNC},
				{"subscriber/" + alias + ".go", tmpl.SubscriberFNC},
				{"proto/" + alias + "/" + alias + ".proto", tmpl.ProtoFNC},
				{"Dockerfile", tmpl.DockerFNC},
				{"Makefile", tmpl.Makefile},
				{"README.md", tmpl.ReadmeFNC},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file " + alias + ".proto:\n",
				"cd " + goDir,
				"protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/" + alias + "/" + alias + ".proto\n",
			},
		}
	case "srv":
		// create srv config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainSRV},
				{"generate.go", tmpl.GenerateFile},
				{"plugin.go", tmpl.Plugin},
				{"handler/" + alias + ".go", tmpl.HandlerSRV},
				{"subscriber/" + alias + ".go", tmpl.SubscriberSRV},
				{"proto/" + alias + "/" + alias + ".proto", tmpl.ProtoSRV},
				{"Dockerfile", tmpl.DockerSRV},
				{"Makefile", tmpl.Makefile},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file " + alias + ".proto:\n",
				"cd " + goDir,
				"protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/" + alias + "/" + alias + ".proto\n",
			},
		}
	case "api":
		// create api config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainAPI},
				{"generate.go", tmpl.GenerateFile},
				{"plugin.go", tmpl.Plugin},
				{"client/" + alias + ".go", tmpl.WrapperAPI},
				{"handler/" + alias + ".go", tmpl.HandlerAPI},
				{"proto/" + alias + "/" + alias + ".proto", tmpl.ProtoAPI},
				{"Makefile", tmpl.Makefile},
				{"Dockerfile", tmpl.DockerSRV},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file " + alias + ".proto:\n",
				"cd " + goDir,
				"protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/" + alias + "/" + alias + ".proto\n",
			},
		}
	case "web":
		// create srv config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainWEB},
				{"plugin.go", tmpl.Plugin},
				{"handler/handler.go", tmpl.HandlerWEB},
				{"html/index.html", tmpl.HTMLWEB},
				{"Dockerfile", tmpl.DockerWEB},
				{"Makefile", tmpl.Makefile},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{},
		}
	default:
		fmt.Println("Unknown type", atype)
		return
	}

	// set gomodule
	if useGoModule == "on" || useGoModule == "auto" {
		c.Files = append(c.Files, file{"go.mod", tmpl.Module})
	}

	if err := create(c); err != nil {
		fmt.Println(err)
		return
	}
}

func Commands() []cli.Command {
	return []cli.Command{
		{
			Name:  "new",
			Usage: "Create a service template",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "namespace",
					Usage: "Namespace for the service e.g com.example",
					Value: "go.micro",
				},
				cli.StringFlag{
					Name:  "type",
					Usage: "Type of service e.g api, fnc, srv, web",
					Value: "srv",
				},
				cli.StringFlag{
					Name:  "fqdn",
					Usage: "FQDN of service e.g com.example.srv.service (defaults to namespace.type.alias)",
				},
				cli.StringFlag{
					Name:  "alias",
					Usage: "Alias is the short name used as part of combined name if specified",
				},
				cli.StringSliceFlag{
					Name:  "plugin",
					Usage: "Specify plugins e.g --plugin=registry=etcd:broker=nats or use flag multiple times",
				},
				cli.BoolTFlag{
					Name:  "gopath",
					Usage: "Create the service in the gopath. Defaults to true.",
				},
			},
			Action: func(c *cli.Context) {
				run(c)
			},
		},
	}
}
<commit_msg> If no GOPATH is set, use default path. (#425)<commit_after>// Package new generates micro service templates
package new

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
	"time"
	"go/build"

	"github.com/micro/cli"
	tmpl "github.com/micro/micro/internal/template"
	"github.com/micro/micro/internal/usage"
	"github.com/xlab/treeprint"
)

type config struct {
	// foo
	Alias string
	// micro new example -type
	Command string
	// go.micro
	Namespace string
	// api, srv, web, fnc
	Type string
	// go.micro.srv.foo
	FQDN string
	// github.com/micro/foo
	Dir string
	// $GOPATH/src/github.com/micro/foo
	GoDir string
	// $GOPATH
	GoPath string
	// Files
	Files []file
	// Comments
	Comments []string
	// Plugins registry=etcd:broker=nats
	Plugins []string
}

type file struct {
	Path string
	Tmpl string
}

func write(c config, file, tmpl string) error {
	fn := template.FuncMap{
		"title": strings.Title,
	}

	f, err := os.Create(file)
	if err != nil {
		return err
	}
	defer f.Close()

	t, err := template.New("f").Funcs(fn).Parse(tmpl)
	if err != nil {
		return err
	}

	return t.Execute(f, c)
}

func create(c config) error {
	// check if dir exists
	if _, err := os.Stat(c.GoDir); !os.IsNotExist(err) {
		return fmt.Errorf("%s already exists", c.GoDir)
	}

	// create usage report
	u := usage.New("new")
	// a single request/service
	u.Metrics.Count["requests"] = uint64(1)
	u.Metrics.Count["services"] = uint64(1)
	// send report
	go usage.Report(u)

	// just wait
	<-time.After(time.Millisecond * 250)

	fmt.Printf("Creating service %s in %s\n\n", c.FQDN, c.GoDir)

	t := treeprint.New()

	nodes := map[string]treeprint.Tree{}
	nodes[c.GoDir] = t

	// write the files
	for _, file := range c.Files {
		f := filepath.Join(c.GoDir, file.Path)
		dir := filepath.Dir(f)

		b, ok := nodes[dir]
		if !ok {
			d, _ := filepath.Rel(c.GoDir, dir)
			b = t.AddBranch(d)
			nodes[dir] = b
		}

		if _, err := os.Stat(dir); os.IsNotExist(err) {
			if err := os.MkdirAll(dir, 0755); err != nil {
				return err
			}
		}

		p := filepath.Base(f)

		b.AddNode(p)
		if err := write(c, f, file.Tmpl); err != nil {
			return err
		}
	}

	// print tree
	fmt.Println(t.String())

	for _, comment := range c.Comments {
		fmt.Println(comment)
	}

	// just wait
	<-time.After(time.Millisecond * 250)

	return nil
}

func run(ctx *cli.Context) {
	namespace := ctx.String("namespace")
	alias := ctx.String("alias")
	fqdn := ctx.String("fqdn")
	atype := ctx.String("type")
	dir := ctx.Args().First()
	useGoPath := ctx.Bool("gopath")
	useGoModule := os.Getenv("GO111MODULE")
	var plugins []string

	if len(dir) == 0 {
		fmt.Println("specify service name")
		return
	}

	if len(namespace) == 0 {
		fmt.Println("namespace not defined")
		return
	}

	if len(atype) == 0 {
		fmt.Println("type not defined")
		return
	}

	// set the command
	command := fmt.Sprintf("micro new %s", dir)
	if len(namespace) > 0 {
		command += " --namespace=" + namespace
	}
	if len(alias) > 0 {
		command += " --alias=" + alias
	}
	if len(fqdn) > 0 {
		command += " --fqdn=" + fqdn
	}
	if len(atype) > 0 {
		command += " --type=" + atype
	}
	if plugins := ctx.StringSlice("plugin"); len(plugins) > 0 {
		command += " --plugin=" + strings.Join(plugins, ":")
	}

	// check if the path is absolute, we don't want this
	// we want to a relative path so we can install in GOPATH
	if path.IsAbs(dir) {
		fmt.Println("require relative path as service will be installed in GOPATH")
		return
	}

	var goPath string
	var goDir string

	// only set gopath if told to use it
	if useGoPath {
		goPath = build.Default.GOPATH

		// don't know GOPATH, runaway....
		if len(goPath) == 0 {
			fmt.Println("unknown GOPATH")
			return
		}

		// attempt to split path if not windows
		if runtime.GOOS == "windows" {
			goPath = strings.Split(goPath, ";")[0]
		} else {
			goPath = strings.Split(goPath, ":")[0]
		}
		goDir = filepath.Join(goPath, "src", path.Clean(dir))
	} else {
		goDir = path.Clean(dir)
	}

	if len(alias) == 0 {
		// set as last part
		alias = filepath.Base(dir)
		// strip hyphens
		parts := strings.Split(alias, "-")
		alias = parts[0]
	}

	if len(fqdn) == 0 {
		fqdn = strings.Join([]string{namespace, atype, alias}, ".")
	}

	for _, plugin := range ctx.StringSlice("plugin") {
		// registry=etcd:broker=nats
		for _, p := range strings.Split(plugin, ":") {
			// registry=etcd
			parts := strings.Split(p, "=")
			if len(parts) < 2 {
				continue
			}
			plugins = append(plugins, path.Join(parts...))
		}
	}

	var c config

	switch atype {
	case "fnc":
		// create srv config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainFNC},
				{"generate.go", tmpl.GenerateFile},
				{"plugin.go", tmpl.Plugin},
				{"handler/" + alias + ".go", tmpl.HandlerFNC},
				{"subscriber/" + alias + ".go", tmpl.SubscriberFNC},
				{"proto/" + alias + "/" + alias + ".proto", tmpl.ProtoFNC},
				{"Dockerfile", tmpl.DockerFNC},
				{"Makefile", tmpl.Makefile},
				{"README.md", tmpl.ReadmeFNC},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file " + alias + ".proto:\n",
				"cd " + goDir,
				"protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/" + alias + "/" + alias + ".proto\n",
			},
		}
	case "srv":
		// create srv config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainSRV},
				{"generate.go", tmpl.GenerateFile},
				{"plugin.go", tmpl.Plugin},
				{"handler/" + alias + ".go", tmpl.HandlerSRV},
				{"subscriber/" + alias + ".go", tmpl.SubscriberSRV},
				{"proto/" + alias + "/" + alias + ".proto", tmpl.ProtoSRV},
				{"Dockerfile", tmpl.DockerSRV},
				{"Makefile", tmpl.Makefile},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file " + alias + ".proto:\n",
				"cd " + goDir,
				"protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/" + alias + "/" + alias + ".proto\n",
			},
		}
	case "api":
		// create api config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainAPI},
				{"generate.go", tmpl.GenerateFile},
				{"plugin.go", tmpl.Plugin},
				{"client/" + alias + ".go", tmpl.WrapperAPI},
				{"handler/" + alias + ".go", tmpl.HandlerAPI},
				{"proto/" + alias + "/" + alias + ".proto", tmpl.ProtoAPI},
				{"Makefile", tmpl.Makefile},
				{"Dockerfile", tmpl.DockerSRV},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{
				"\ndownload protobuf for micro:\n",
				"brew install protobuf",
				"go get -u github.com/golang/protobuf/{proto,protoc-gen-go}",
				"go get -u github.com/micro/protoc-gen-micro",
				"\ncompile the proto file " + alias + ".proto:\n",
				"cd " + goDir,
				"protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/" + alias + "/" + alias + ".proto\n",
			},
		}
	case "web":
		// create srv config
		c = config{
			Alias:     alias,
			Command:   command,
			Namespace: namespace,
			Type:      atype,
			FQDN:      fqdn,
			Dir:       dir,
			GoDir:     goDir,
			GoPath:    goPath,
			Plugins:   plugins,
			Files: []file{
				{"main.go", tmpl.MainWEB},
				{"plugin.go", tmpl.Plugin},
				{"handler/handler.go", tmpl.HandlerWEB},
				{"html/index.html", tmpl.HTMLWEB},
				{"Dockerfile", tmpl.DockerWEB},
				{"Makefile", tmpl.Makefile},
				{"README.md", tmpl.Readme},
			},
			Comments: []string{},
		}
	default:
		fmt.Println("Unknown type", atype)
		return
	}

	// set gomodule
	if useGoModule == "on" || useGoModule == "auto" {
		c.Files = append(c.Files, file{"go.mod", tmpl.Module})
	}

	if err := create(c); err != nil {
		fmt.Println(err)
		return
	}
}

func Commands() []cli.Command {
	return []cli.Command{
		{
			Name:  "new",
			Usage: "Create a service template",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "namespace",
					Usage: "Namespace for the service e.g com.example",
					Value: "go.micro",
				},
				cli.StringFlag{
					Name:  "type",
					Usage: "Type of service e.g api, fnc, srv, web",
					Value: "srv",
				},
				cli.StringFlag{
					Name:  "fqdn",
					Usage: "FQDN of service e.g com.example.srv.service (defaults to namespace.type.alias)",
				},
				cli.StringFlag{
					Name:  "alias",
					Usage: "Alias is the short name used as part of combined name if specified",
				},
				cli.StringSliceFlag{
					Name:  "plugin",
					Usage: "Specify plugins e.g --plugin=registry=etcd:broker=nats or use flag multiple times",
				},
				cli.BoolTFlag{
					Name:  "gopath",
					Usage: "Create the service in the gopath. Defaults to true.",
				},
			},
			Action: func(c *cli.Context) {
				run(c)
			},
		},
	}
}
<|endoftext|>


<commit_before>// (C) 2017 Julian Andres Klode <jak@jak-linux.org>
// Licensed under the 2-Clause BSD license, see LICENSE for more information.

package capabilities

import (
	"fmt"
	"go/ast"
	"go/parser"
	"reflect"
	"strings"
	"testing"

	"github.com/julian-klode/lingolang/permission"
)

func recoverErrorOrFail(t *testing.T, message string) {
	if e := recover(); e == nil || !strings.Contains(fmt.Sprint(e), message) {
		t.Fatalf("Unexpected error -- %s -- expected it to contain -- %s --", e, message)
	}
}

func runFuncRecover(t *testing.T, message string, block func()) {
	defer recoverErrorOrFail(t, message)
	block()
}

type tuplePermission []string

func newPermission(input interface{}) permission.Permission {
	switch input := input.(type) {
	case string:
		perm, err := permission.NewParser(input).Parse()
		if err != nil {
			panic(err)
		}
		return perm
	case permission.Permission:
		return input
	case tuplePermission:
		base := newPermission(input[0])
		var others []permission.Permission
		for _, s := range input[1:] {
			o := newPermission(s)
			others = append(others, o)
		}

		return &permission.TuplePermission{base.(permission.BasePermission), others}
	}
	panic("Not reachable")
}

func TestVisitIdent(t *testing.T) {
	st := Store{
		{"x", newPermission("om[]om"), newPermission("om")},
	}
	i := &Interpreter{}
	runFuncRecover(t, "Unknown variable", func() {
		i.VisitExpr(st, ast.NewIdent("a"))
	})
	runFuncRecover(t, "Cannot restrict effective permission", func() {
		i.VisitExpr(st, ast.NewIdent("x"))
	})
}

func TestVisitExpr(t *testing.T) {
	type errorResult string

	testCases := []struct {
		expr         string
		name         string
		lhs          interface{}
		rhs          interface{}
		result       interface{}
		dependencies []string
		lhsAfter     interface{}
		rhsAfter     interface{}
	}{
		// ------------------- Binary expressions ----------------------------
		{"b&&b", "binarySingleIdent", "", "om", "om", []string{}, "", "om"},

		{"a+b", "binaryOk", "or", "or", "om", []string{}, "or", "or"},
		{"a+b", "binaryLhsUnreadable", "ow", "or", errorResult("In a: Required permissions r, but only have ow"), []string{}, "or", "or"},
		{"a+b", "binaryLhsUnreadable", "or", "ow", errorResult("In b: Required permissions r, but only have ow"), []string{}, "or", "or"},
		// ------------------- Indexing ----------------------------
		{"a[b]", "mutableSlice", "om[]om", "om", "om", []string{"a"}, "n[]n", "om"},
		{"a[b]", "mutableArray", "om[_]om", "om", "om", []string{"a"}, "n[_]n", "om"},
		{"a[b]", "mutableMap", "om map[om]om", "om", "om", []string{"a"}, "n map[n]n", "om"},
		// mutable map, non-copyable key: Item was moved into the map, it's gone now.
		{"a[b]", "mutableMapNoCopyKey", "om map[om * om]om", "om * om", "om", []string{"a"}, "n map[n * r]n", "n * r"},
		// a regular writable pointer is copyable. but beware: that's unsafe.
		{"a[b]", "mutableMapCopyablePointerKey", "om map[orw * orw]om", "orw * orw", "om", []string{"a"}, "n map[n * rw]n", "orw * orw"},
		// we pass a mutable key where we only need r/o, the key is consumed.
		{"a[b]", "mutableMapFreeze", "om map[or * or]om", "om * om", "om", []string{"a"}, "n map[n * r]n", "n * r"},
		{"a[b]", "notIndexable", "or", "ov", errorResult("Indexing unknown"), nil, "", ""},
		{"a[b]", "keyNotReadable", "om[] om", "ow", errorResult("Required permission"), nil, "", ""},
		{"a[b]", "indexableNotReadable", "on[] on", "or", errorResult("Required permission"), nil, "", ""},
		{"a[b]", "mutableMapInvalidKey", "or map[om * om]ov", "ov * ov", errorResult("move or copy"), nil, "", ""},
		// ------------------- Star expressions ----------------------------
		{"*b", "mutablePointer", "", "om * om", "om", []string{"b"}, "", "n * r"},
		{"*b", "mutablePointerReadTarget", "", "om * or", "or", []string{"b"}, "", "n * r"},
		{"*b", "readOnlyPointer", "", "or * or", "or", []string{"b"}, "", "n * r"},
		{"*b", "noPointer", "", "or", errorResult("non-pointer"), []string{"b"}, "", "n * r"},
		{scenario{"var b *int", "*b"}, "stareWithTypeInfo", "", "or", errorResult("non-pointer"), []string{"b"}, "", "n * r"},
		// Unary expressions
		{"-b", "mutableNegation", nil, "om", "om", []string{}, nil, "om"},
		{"&b", "mutableNegation", nil, "om", "om * om", []string{"b"}, nil, "n"},
		{"&b", "mutableNegation", nil, "or", "om * or", []string{"b"}, nil, "n"},
		// Parens
		{"(b)", "parenMut", nil, "om", "om", []string{"b"}, nil, "n"},
		{"(b)", "parenPoint", nil, "om * om", "om * om", []string{"b"}, nil, "n * r"},
		// Function calls
		{"a(b)", "callMutableNoCopy", "om func(om * om) or", "om * om", "or", []string{}, "om func(om * om) or", "n * r"},
		{"a(b, b)", "callMutableNoCopy", "om func(om * om, om * om) or", "om * om", errorResult("Cannot copy or move"), []string{}, "om func(om * om, om * om) or", "n * r"},
		{"a(b)", "callMutableNoCopyUnowned", "om func(m * m) or", "om * om", "or", []string{}, "om func(m * m) or", "om * om"},
		{"a(b)", "callMutableNoCopyUnownedToUnownedReadable", "om func(r * r) or", "om * om", "or", []string{}, "om func(r * r) or", "om * om"},
		{"a(b, b)", "callMutableNoCopyUnownedToUnownedReadable", "om func(r * r, r * r) or", "om * om", errorResult("Cannot copy or move"), []string{}, "om func(r * r, r * r) or", "om * om"},
		{"a(b)", "callMutableNoCopyUnownedToOwnedReadable", "om func(or * or) or", "om * om", "or", []string{}, "om func(or * or) or", "or * or"},
		{"a(b, b)", "callMutableNoCopyUnownedToOwnedReadable", "om func(or * or, or * or) or", "om * om", "or", []string{}, "om func(or * or, or * or) or", "or * or"},
		{"a(b)", "callMutableNoCopyUnownedToOwnedReadable", "om func(or * owr) or", "om * om", "or", []string{}, "om func(or * owr) or", "or * or"},
		{"a(b)", "callMutableCopy", "om func(om) or", "om", "or", []string{}, "om func(om) or", "om"},
		{"a(b)", "callMutableNoRet", "om func(om)", "om", tuplePermission{"om"}, []string{}, "om func(om)", "om"},
		{"a(b)", "callMutableNoRet", "om func(om) (ov, oa)", "om", tuplePermission{"om", "ov", "oa"}, []string{}, "om func(om) (ov, oa)", "om"},
		{"a(b)", "callMutableIncompat", "om func(om * om)", "or * or", errorResult("not copy or move"), []string{}, "om func(om)", "or * or"},

		{"a(b)", "callRetValue", "om func(om) n", "om", "n", []string{}, "om func(om) n", "om"},
		{"a(b)", "callNotAFunction", "om", "om", errorResult("non-function"), []string{}, "om", "om"},
		// Basic lit
		{"127", "basicLitInt", nil, nil, "om", []string{}, nil, nil},
		{"127.1", "basicLitFloat", nil, nil, "om", []string{}, nil, nil},
		{"0i", "basicLitImag", nil, nil, "om", []string{}, nil, nil},
		{"'c'", "basicLitChar", nil, nil, "om", []string{}, nil, nil},
		{"\"string\"", "basicLitString", nil, nil, "om", []string{}, nil, nil},
		// Slice
		{"a[:]", "sliceAllArr", "om [_]ov", nil, "om []ov", []string{"a"}, "n [_]n", nil},
		{"a[:]", "sliceAllSlice", "om []ov", nil, "om []ov", []string{"a"}, "n []n", nil},
		{"a[:]", "sliceAllSliceRo", "or []or", nil, "or []or", []string{"a"}, "n []n", nil},
		{"a[:b]", "sliceHigh", "om []ov", "om", "om []ov", []string{"a"}, "n []n", "om"},
		{"a[b:2:3]", "sliceMin", "om []ov", "om", "om []ov", []string{"a"}, "n []n", "om"},
		{"a[1:2:b]", "sliceMax", "om []ov", "om", "om []ov", []string{"a"}, "n []n", "om"},
		{"a[1:2:b]", "sliceInvalid", "om map[ov]ov", "om", errorResult("not sliceable"), []string{"a"}, "n []n", "om"},
		// TODO
		{"T {1}", "compositeLit", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
		{"func() {}", "funcLit", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
		{"a.b", "selector", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
		{"a.(b)", "selector", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
	}

	for _, test := range testCases {

		t.Run(test.name, func(t *testing.T) {
			st := NewStore()
			i := &Interpreter{}

			var lhs *ast.Ident
			var rhs *ast.Ident
			e, _ := parser.ParseExpr(test.expr)
			ast.Inspect(e, func(node ast.Node) bool {
				if ident, ok := node.(*ast.Ident); ok {
					switch ident.Name {
					case "a":
						lhs = ident
					case "b":
						rhs = ident
					}
				}
				return true
			})

			if lhs != nil {
				st = st.Define(lhs.Name, newPermission(test.lhs))
			}
			if rhs != nil {
				st = st.Define(rhs.Name, newPermission(test.rhs))
			}

			if eResult, ok := test.result.(errorResult); ok {
				runFuncRecover(t, string(eResult), func() {
					i.VisitExpr(st, e)
				})
				return
			}

			perm, deps, store := i.VisitExpr(st, e)

			if !reflect.DeepEqual(newPermission(test.result), perm) {
				t.Errorf("Evaluated to %v, expected %v", perm, newPermission(test.result))
			}

			// Check dependencies
			depsAsString := make([]string, len(deps))
			for i := range deps {
				depsAsString[i] = deps[i].id.Name
			}

			if !reflect.DeepEqual(depsAsString, test.dependencies) {
				t.Errorf("Found dependencies %v, expected %v", depsAsString, test.dependencies)
			}

			if lhs != nil && !reflect.DeepEqual(store.GetEffective(lhs.Name), newPermission(test.lhsAfter)) {
				t.Errorf("Found lhs after = %v, expected %v", store.GetEffective(lhs.Name), newPermission(test.lhsAfter))
			}
			if rhs != nil && !reflect.DeepEqual(store.GetEffective(rhs.Name), newPermission(test.rhsAfter)) {
				t.Errorf("Found rhs after = %v, expected %v", store.GetEffective(rhs.Name), newPermission(test.rhsAfter))
			}
		})

	}
}

func TestVisitExpr_bad(t *testing.T) {
	var i Interpreter
	var st Store
	runFuncRecover(t, "bad expr", func() {
		i.VisitExpr(st, &ast.BadExpr{})
	})
}

func TestRelease(t *testing.T) {
	var i Interpreter
	var st Store
	runFuncRecover(t, "not release borrowed variable", func() {
		st = st.Define("a", newPermission("om"))
		i.Release(ast.NewIdent("a"), st, []Borrowed{
			{ast.NewIdent("a"), newPermission("om * om")},
		})
	})
}
<commit_msg>interpreter_test: Improve error handling<commit_after>// (C) 2017 Julian Andres Klode <jak@jak-linux.org>
// Licensed under the 2-Clause BSD license, see LICENSE for more information.

package capabilities

import (
	"fmt"
	"go/ast"
	"go/parser"
	"reflect"
	"strings"
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/julian-klode/lingolang/permission"
)

func recoverErrorOrFail(t *testing.T, message string) {
	if e := recover(); (e == nil && message != "") || (e != nil && message == "") || !strings.Contains(fmt.Sprint(e), message) {
		t.Fatalf("Unexpected error -- %s -- expected it to contain -- %s --", e, message)
	}
}

func runFuncRecover(t *testing.T, message string, block func()) {
	defer recoverErrorOrFail(t, message)
	block()
}

type tuplePermission []string

func newPermission(input interface{}) permission.Permission {
	switch input := input.(type) {
	case string:
		perm, err := permission.NewParser(input).Parse()
		if err != nil {
			panic(err)
		}
		return perm
	case permission.Permission:
		return input
	case tuplePermission:
		base := newPermission(input[0])
		var others []permission.Permission
		for _, s := range input[1:] {
			o := newPermission(s)
			others = append(others, o)
		}

		return &permission.TuplePermission{base.(permission.BasePermission), others}
	}
	panic(fmt.Errorf("Not reachable, unkown object %#v", input))
}

func TestVisitIdent(t *testing.T) {
	st := Store{
		{"x", newPermission("om[]om"), newPermission("om")},
	}
	i := &Interpreter{}
	runFuncRecover(t, "Unknown variable", func() {
		i.VisitExpr(st, ast.NewIdent("a"))
	})
	runFuncRecover(t, "Cannot restrict effective permission", func() {
		i.VisitExpr(st, ast.NewIdent("x"))
	})
}

func TestVisitExpr(t *testing.T) {
	type errorResult string

	testCases := []struct {
		expr         string
		name         string
		lhs          interface{}
		rhs          interface{}
		result       interface{}
		dependencies []string
		lhsAfter     interface{}
		rhsAfter     interface{}
	}{
		// ------------------- Binary expressions ----------------------------
		{"b&&b", "binarySingleIdent", "", "om", "om", []string{}, "", "om"},

		{"a+b", "binaryOk", "or", "or", "om", []string{}, "or", "or"},
		{"a+b", "binaryLhsUnreadable", "ow", "or", errorResult("In a: Required permissions r, but only have ow"), []string{}, "or", "or"},
		{"a+b", "binaryLhsUnreadable", "or", "ow", errorResult("In b: Required permissions r, but only have ow"), []string{}, "or", "or"},
		// ------------------- Indexing ----------------------------
		{"a[b]", "mutableSlice", "om[]om", "om", "om", []string{"a"}, "n[]n", "om"},
		{"a[b]", "mutableArray", "om[_]om", "om", "om", []string{"a"}, "n[_]n", "om"},
		{"a[b]", "mutableMap", "om map[om]om", "om", "om", []string{"a"}, "n map[n]n", "om"},
		// mutable map, non-copyable key: Item was moved into the map, it's gone now.
		{"a[b]", "mutableMapNoCopyKey", "om map[om * om]om", "om * om", "om", []string{"a"}, "n map[n * r]n", "n * r"},
		// a regular writable pointer is copyable. but beware: that's unsafe.
		{"a[b]", "mutableMapCopyablePointerKey", "om map[orw * orw]om", "orw * orw", "om", []string{"a"}, "n map[n * rw]n", "orw * orw"},
		// we pass a mutable key where we only need r/o, the key is consumed.
		{"a[b]", "mutableMapFreeze", "om map[or * or]om", "om * om", "om", []string{"a"}, "n map[n * r]n", "n * r"},
		{"a[b]", "notIndexable", "or", "ov", errorResult("Indexing unknown"), nil, "", ""},
		{"a[b]", "keyNotReadable", "om[] om", "ow", errorResult("Required permission"), nil, "", ""},
		{"a[b]", "indexableNotReadable", "on[] on", "or", errorResult("Required permission"), nil, "", ""},
		{"a[b]", "mutableMapInvalidKey", "or map[om * om]ov", "ov * ov", errorResult("move or copy"), nil, "", ""},
		// ------------------- Star expressions ----------------------------
		{"*b", "mutablePointer", "", "om * om", "om", []string{"b"}, "", "n * r"},
		{"*b", "mutablePointerReadTarget", "", "om * or", "or", []string{"b"}, "", "n * r"},
		{"*b", "readOnlyPointer", "", "or * or", "or", []string{"b"}, "", "n * r"},
		{"*b", "noPointer", "", "or", errorResult("non-pointer"), []string{"b"}, "", "n * r"},
		{scenario{"var b *int", "*b"}, "stareWithTypeInfo", "", "or", errorResult("non-pointer"), []string{"b"}, "", "n * r"},
		// Unary expressions
		{"-b", "mutableNegation", nil, "om", "om", []string{}, nil, "om"},
		{"&b", "mutableNegation", nil, "om", "om * om", []string{"b"}, nil, "n"},
		{"&b", "mutableNegation", nil, "or", "om * or", []string{"b"}, nil, "n"},
		// Parens
		{"(b)", "parenMut", nil, "om", "om", []string{"b"}, nil, "n"},
		{"(b)", "parenPoint", nil, "om * om", "om * om", []string{"b"}, nil, "n * r"},
		// Function calls
		{"a(b)", "callMutableNoCopy", "om func(om * om) or", "om * om", "or", []string{}, "om func(om * om) or", "n * r"},
		{"a(b, b)", "callMutableNoCopy", "om func(om * om, om * om) or", "om * om", errorResult("Cannot copy or move"), []string{}, "om func(om * om, om * om) or", "n * r"},
		{"a(b)", "callMutableNoCopyUnowned", "om func(m * m) or", "om * om", "or", []string{}, "om func(m * m) or", "om * om"},
		{"a(b)", "callMutableNoCopyUnownedToUnownedReadable", "om func(r * r) or", "om * om", "or", []string{}, "om func(r * r) or", "om * om"},
		{"a(b, b)", "callMutableNoCopyUnownedToUnownedReadable", "om func(r * r, r * r) or", "om * om", errorResult("Cannot copy or move"), []string{}, "om func(r * r, r * r) or", "om * om"},
		{"a(b)", "callMutableNoCopyUnownedToOwnedReadable", "om func(or * or) or", "om * om", "or", []string{}, "om func(or * or) or", "or * or"},
		{"a(b, b)", "callMutableNoCopyUnownedToOwnedReadable", "om func(or * or, or * or) or", "om * om", "or", []string{}, "om func(or * or, or * or) or", "or * or"},
		{"a(b)", "callMutableNoCopyUnownedToOwnedReadable", "om func(or * owr) or", "om * om", "or", []string{}, "om func(or * owr) or", "or * or"},
		{"a(b)", "callMutableCopy", "om func(om) or", "om", "or", []string{}, "om func(om) or", "om"},
		{"a(b)", "callMutableNoRet", "om func(om)", "om", tuplePermission{"om"}, []string{}, "om func(om)", "om"},
		{"a(b)", "callMutableNoRet", "om func(om) (ov, oa)", "om", tuplePermission{"om", "ov", "oa"}, []string{}, "om func(om) (ov, oa)", "om"},
		{"a(b)", "callMutableIncompat", "om func(om * om)", "or * or", errorResult("not copy or move"), []string{}, "om func(om)", "or * or"},

		{"a(b)", "callRetValue", "om func(om) n", "om", "n", []string{}, "om func(om) n", "om"},
		{"a(b)", "callNotAFunction", "om", "om", errorResult("non-function"), []string{}, "om", "om"},
		// Basic lit
		{"127", "basicLitInt", nil, nil, "om", []string{}, nil, nil},
		{"127.1", "basicLitFloat", nil, nil, "om", []string{}, nil, nil},
		{"0i", "basicLitImag", nil, nil, "om", []string{}, nil, nil},
		{"'c'", "basicLitChar", nil, nil, "om", []string{}, nil, nil},
		{"\"string\"", "basicLitString", nil, nil, "om", []string{}, nil, nil},
		// Slice
		{"a[:]", "sliceAllArr", "om [_]ov", nil, "om []ov", []string{"a"}, "n [_]n", nil},
		{"a[:]", "sliceAllSlice", "om []ov", nil, "om []ov", []string{"a"}, "n []n", nil},
		{"a[:]", "sliceAllSliceRo", "or []or", nil, "or []or", []string{"a"}, "n []n", nil},
		{"a[:b]", "sliceHigh", "om []ov", "om", "om []ov", []string{"a"}, "n []n", "om"},
		{"a[b:2:3]", "sliceMin", "om []ov", "om", "om []ov", []string{"a"}, "n []n", "om"},
		{"a[1:2:b]", "sliceMax", "om []ov", "om", "om []ov", []string{"a"}, "n []n", "om"},
		{"a[1:2:b]", "sliceInvalid", "om map[ov]ov", "om", errorResult("not sliceable"), []string{"a"}, "n []n", "om"},
		// TODO
		{"T {1}", "compositeLit", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
		{"func() {}", "funcLit", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
		{"a.b", "selector", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
		{"a.(b)", "selector", "om", "om", errorResult("not yet implemented"), nil, nil, nil},
	}

	for _, test := range testCases {

		t.Run(test.name, func(t *testing.T) {
			defer recoverErrorOrFail(t, "")
			st := NewStore()
			i := &Interpreter{}

			var lhs *ast.Ident
			var rhs *ast.Ident
			e, _ := parser.ParseExpr(test.expr)
			ast.Inspect(e, func(node ast.Node) bool {
				if ident, ok := node.(*ast.Ident); ok {
					switch ident.Name {
					case "a":
						lhs = ident
					case "b":
						rhs = ident
					}
				}
				return true
			})

			if lhs != nil && test.lhs != nil {
				st = st.Define(lhs.Name, newPermission(test.lhs))
			}
			if rhs != nil && test.rhs != nil {
				st = st.Define(rhs.Name, newPermission(test.rhs))
			}

			if eResult, ok := test.result.(errorResult); ok {
				runFuncRecover(t, string(eResult), func() {
					i.VisitExpr(st, e)
				})
				return
			}

			perm, deps, store := i.VisitExpr(st, e)

			if !reflect.DeepEqual(newPermission(test.result), perm) {
				t.Errorf("Evaluated to %v, expected %v", perm, newPermission(test.result))
			}

			// Check dependencies
			depsAsString := make([]string, len(deps))
			for i := range deps {
				depsAsString[i] = deps[i].id.Name
			}

			if !reflect.DeepEqual(depsAsString, test.dependencies) {
				t.Errorf("Found dependencies %v, expected %v", depsAsString, test.dependencies)
			}

			if lhs != nil && !reflect.DeepEqual(store.GetEffective(lhs.Name), newPermission(test.lhsAfter)) {
				t.Error(spew.Errorf("Found lhs after = %v, expected %v", store.GetEffective(lhs.Name), newPermission(test.lhsAfter)))
			}
			if rhs != nil && !reflect.DeepEqual(store.GetEffective(rhs.Name), newPermission(test.rhsAfter)) {
				t.Error(spew.Errorf("Found rhs after = %v, expected %v", store.GetEffective(rhs.Name), newPermission(test.rhsAfter)))
			}
		})

	}
}

func TestVisitExpr_bad(t *testing.T) {
	var i Interpreter
	var st Store
	runFuncRecover(t, "bad expr", func() {
		i.VisitExpr(st, &ast.BadExpr{})
	})
}

func TestRelease(t *testing.T) {
	var i Interpreter
	var st Store
	runFuncRecover(t, "not release borrowed variable", func() {
		st = st.Define("a", newPermission("om"))
		i.Release(ast.NewIdent("a"), st, []Borrowed{
			{ast.NewIdent("a"), newPermission("om * om")},
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"text/template"
	"time"
)

var editEntry bool
var ignoreDirty bool

func init() {
	flag.BoolVar(&editEntry, "edit", true, "open the target entry in the editor")
	flag.BoolVar(&ignoreDirty, "ignoredirty", false, "ignore if the git repository is dirty")
}

var newEntryCmd = &Command{
	Name:    "new",
	Usage:   "",
	Summary: "Create a new journal entry",
	Help:    "TODO",
	Run: func(c *Command, args ...string) {
		wd, err := os.Getwd()
		if err != nil {
			log.Fatal(err)
		}

		_, err = newEntry(wd, entryTmpl, nil, MutateInto, c, args...)
		if err != nil {
			log.Fatal(err)
		}
	},
}

//A layout to use as the entry's filename
const filenameLayout = "2006-01-02-1504-MST"

func newEntry(dir string, entryTmpl *template.Template, Now func() time.Time, mutateIntoEditor func(*exec.Cmd) (Process, error), c *Command, args ...string) (j journalEntry, err error) {
	if !ignoreDirty {
		if err := GitIsClean(dir); err != nil {
			return j, err
		}
	}

	// Get the OpenedAt time
	if Now == nil {
		Now = time.Now
	}
	now := Now()

	j = journalEntry{
		Filename: now.Format(filenameLayout),
		OpenedAt: now.Format(time.UnixDate),
	}

	// Scan for active ideas
	filename, err := lastEntryFilename(dir)
	if err != nil && err.Error() != "journal is empty" {
		return j, err
	}

	lastEntry, err := os.OpenFile(path.Join(dir, filename), os.O_RDONLY, 0600)
	if err != nil {
		return j, err
	}

	ideaScanner := NewIdeaScanner(lastEntry)
	for ideaScanner.Scan() {
		i := ideaScanner.Idea()
		if i.Status == IS_Active {
			j.ActiveIdeas = append(j.ActiveIdeas, i)
		}
	}

	b := bytes.NewBuffer(make([]byte, 0, 256))

	// *sigh* can't stop laughing.....
	err = entryTmpl.Execute(b, j)
	if err != nil {
		return j, err
	}

	entryFilepath := path.Join(dir, j.Filename)
	err = ioutil.WriteFile(entryFilepath, b.Bytes(), os.FileMode(0600))
	if err != nil {
		return j, err
	}

	// Open the Editor
	if mutateIntoEditor != nil && editEntry {
		// TODO: enable the editor to configurable
		editorPath, err := exec.LookPath("vim")
		if err != nil {
			return j, err
		}

		editor, err := mutateIntoEditor(exec.Command(editorPath, entryFilepath))
		if err != nil {
			return j, err
		}

		err = editor.Wait()
		if err != nil {
			return j, err
		}
	}

	j.ClosedAt = Now().Format(time.UnixDate)

	// Append the ClosedAt time to the file
	f, err := os.OpenFile(entryFilepath, os.O_RDWR, 0600)
	if err != nil {
		return j, err
	}
	defer f.Close()

	// Goto EOF
	_, err = f.Seek(-1, 2)
	if err != nil {
		return j, err
	}

	lastTwoBytes := make([]byte, 2)

	_, err = f.Read(lastTwoBytes)
	if err != nil {
		return j, err
	}

	fbuf := bufio.NewWriter(f)

	switch {
	case bytes.Equal(lastTwoBytes, []byte("\n\n")):
		fbuf.WriteString(j.ClosedAt + "\n")
	default:
		fbuf.WriteString("\n" + j.ClosedAt + "\n")
	}

	err = fbuf.Flush()
	if err != nil {
		return j, err
	}

	// Parse the commit msg from the journal entry
	data, err := ioutil.ReadFile(entryFilepath)
	if err != nil {
		return j, err
	}

	var commitMsg string

	s := bufio.NewScanner(bytes.NewReader(data))
	for s.Scan() {
		line := s.Text()
		if i := strings.Index(line, "#~"); i == 0 {
			commitMsg = line[3:]
		} else {
			continue
		}
	}

	if commitMsg == "" {
		return j, errors.New("entry is missing an event to use as the commit message")
	}

	// Commit the new journal entry to the git repository
	if err := GitAdd(dir, entryFilepath); err != nil {
		return j, err
	}

	if err := GitCommitAll(dir, commitMsg); err != nil {
		return j, err
	}

	return j, nil
}

type journalEntry struct {
	Filename    string
	OpenedAt    string
	ClosedAt    string
	ActiveIdeas []*Idea
}

var entryTmpl = template.Must(template.New("entry").Parse(
	`{{.OpenedAt}}

#~ Title(will be used as commit message)
TODO Make this some random quote or something stupid
{{range .ActiveIdeas}}## [{{.Status}}] {{.Name}}
{{.Body}}{{end}}`))
<commit_msg>Fixed some layout issues in the tmpl | I found these when actually going to write an entry | They need specifications<commit_after>package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"text/template"
	"time"
)

var editEntry bool
var ignoreDirty bool

func init() {
	flag.BoolVar(&editEntry, "edit", true, "open the target entry in the editor")
	flag.BoolVar(&ignoreDirty, "ignoredirty", false, "ignore if the git repository is dirty")
}

var newEntryCmd = &Command{
	Name:    "new",
	Usage:   "",
	Summary: "Create a new journal entry",
	Help:    "TODO",
	Run: func(c *Command, args ...string) {
		wd, err := os.Getwd()
		if err != nil {
			log.Fatal(err)
		}

		_, err = newEntry(wd, entryTmpl, nil, MutateInto, c, args...)
		if err != nil {
			log.Fatal(err)
		}
	},
}

//A layout to use as the entry's filename
const filenameLayout = "2006-01-02-1504-MST"

func newEntry(dir string, entryTmpl *template.Template, Now func() time.Time, mutateIntoEditor func(*exec.Cmd) (Process, error), c *Command, args ...string) (j journalEntry, err error) {
	if !ignoreDirty {
		if err := GitIsClean(dir); err != nil {
			return j, err
		}
	}

	// Get the OpenedAt time
	if Now == nil {
		Now = time.Now
	}
	now := Now()

	j = journalEntry{
		Filename: now.Format(filenameLayout),
		OpenedAt: now.Format(time.UnixDate),
	}

	// Scan for active ideas
	filename, err := lastEntryFilename(dir)
	if err != nil && err.Error() != "journal is empty" {
		return j, err
	}

	lastEntry, err := os.OpenFile(path.Join(dir, filename), os.O_RDONLY, 0600)
	if err != nil {
		return j, err
	}

	ideaScanner := NewIdeaScanner(lastEntry)
	for ideaScanner.Scan() {
		i := ideaScanner.Idea()
		if i.Status == IS_Active {
			j.ActiveIdeas = append(j.ActiveIdeas, i)
		}
	}

	b := bytes.NewBuffer(make([]byte, 0, 256))

	// *sigh* can't stop laughing.....
	err = entryTmpl.Execute(b, j)
	if err != nil {
		return j, err
	}

	entryFilepath := path.Join(dir, j.Filename)
	err = ioutil.WriteFile(entryFilepath, b.Bytes(), os.FileMode(0600))
	if err != nil {
		return j, err
	}

	// Open the Editor
	if mutateIntoEditor != nil && editEntry {
		// TODO: enable the editor to configurable
		editorPath, err := exec.LookPath("vim")
		if err != nil {
			return j, err
		}

		editor, err := mutateIntoEditor(exec.Command(editorPath, entryFilepath))
		if err != nil {
			return j, err
		}

		err = editor.Wait()
		if err != nil {
			return j, err
		}
	}

	j.ClosedAt = Now().Format(time.UnixDate)

	// Append the ClosedAt time to the file
	f, err := os.OpenFile(entryFilepath, os.O_RDWR, 0600)
	if err != nil {
		return j, err
	}
	defer f.Close()

	// Goto EOF
	_, err = f.Seek(-1, 2)
	if err != nil {
		return j, err
	}

	lastTwoBytes := make([]byte, 2)

	_, err = f.Read(lastTwoBytes)
	if err != nil {
		return j, err
	}

	fbuf := bufio.NewWriter(f)

	switch {
	case bytes.Equal(lastTwoBytes, []byte("\n\n")):
		fbuf.WriteString(j.ClosedAt + "\n")
	default:
		fbuf.WriteString("\n" + j.ClosedAt + "\n")
	}

	err = fbuf.Flush()
	if err != nil {
		return j, err
	}

	// Parse the commit msg from the journal entry
	data, err := ioutil.ReadFile(entryFilepath)
	if err != nil {
		return j, err
	}

	var commitMsg string

	s := bufio.NewScanner(bytes.NewReader(data))
	for s.Scan() {
		line := s.Text()
		if i := strings.Index(line, "#~"); i == 0 {
			commitMsg = line[3:]
		} else {
			continue
		}
	}

	if commitMsg == "" {
		return j, errors.New("entry is missing an event to use as the commit message")
	}

	// Commit the new journal entry to the git repository
	if err := GitAdd(dir, entryFilepath); err != nil {
		return j, err
	}

	if err := GitCommitAll(dir, commitMsg); err != nil {
		return j, err
	}

	return j, nil
}

type journalEntry struct {
	Filename    string
	OpenedAt    string
	ClosedAt    string
	ActiveIdeas []*Idea
}

var entryTmpl = template.Must(template.New("entry").Parse(
	`{{.OpenedAt}}

#~ Title(will be used as commit message)
TODO Make this some random quote or something stupid
{{range .ActiveIdeas}}
## [{{.Status}}] {{.Name}}
{{.Body}}
{{end}}`))
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"runtime"

	"github.com/mattn/go-isatty"

	"github.com/zetamatta/nyagos/frame"
	"github.com/zetamatta/nyagos/functions"
	"github.com/zetamatta/nyagos/history"
	"github.com/zetamatta/nyagos/shell"
)

type ScriptEngineForOptionImpl struct{}

func (this *ScriptEngineForOptionImpl) SetArg(args []string) {}

func (this *ScriptEngineForOptionImpl) RunFile(ctx context.Context,fname string) ([]byte, error) {
	println("Script is not supported.")
	return nil, nil
}

func (this *ScriptEngineForOptionImpl) RunString(code string) error {
	println("Script is not supported.")
	return nil
}

func Main() error {
	sh := shell.New()
	defer sh.Close()

	ctx := context.Background()

	langEngine := func(fname string) ([]byte, error) {
		return nil, nil
	}
	shellEngine := func(fname string) error {
		fd, err := os.Open(fname)
		if err != nil {
			return err
		}
		stream1 := shell.NewCmdStreamFile(fd)
		_, err = sh.Loop(ctx, stream1)
		fd.Close()
		if err == io.EOF {
			return nil
		} else {
			return err
		}
	}

	script, err := frame.OptionParse(sh, &ScriptEngineForOptionImpl{})
	if err != nil {
		return err
	}

	if !isatty.IsTerminal(os.Stdin.Fd()) || script != nil {
		frame.SilentMode = true
	}

	if !frame.OptionNorc {
		if !frame.SilentMode {
			fmt.Printf("Nihongo Yet Another GOing Shell %s-%s by %s\n",
				frame.VersionOrStamp(),
				runtime.GOARCH,
				runtime.Version())
			fmt.Println("(c) 2014-2018 NYAOS.ORG <http://www.nyaos.org>")
		}
		if err := frame.LoadScripts(shellEngine, langEngine); err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
		}
	}

	if script != nil {
		if err := script(ctx); err != nil {
			if err != io.EOF {
				return err
			} else {
				return nil
			}
		}
	}

	var stream1 shell.Stream
	if isatty.IsTerminal(os.Stdin.Fd()) {
		constream := frame.NewCmdStreamConsole(
			func() (int, error) {
				functions.Prompt(
					[]interface{}{frame.Format2Prompt(os.Getenv("PROMPT"))})
				return 0, nil
			})
		stream1 = constream
		frame.DefaultHistory = constream.History
		ctx = context.WithValue(ctx, history.PackageId, constream.History)
	} else {
		stream1 = shell.NewCmdStreamFile(os.Stdin)
	}
	sh.ForEver(ctx, stream1)
	return nil
}
<commit_msg>go fmt ... orz<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"runtime"

	"github.com/mattn/go-isatty"

	"github.com/zetamatta/nyagos/frame"
	"github.com/zetamatta/nyagos/functions"
	"github.com/zetamatta/nyagos/history"
	"github.com/zetamatta/nyagos/shell"
)

type ScriptEngineForOptionImpl struct{}

func (this *ScriptEngineForOptionImpl) SetArg(args []string) {}

func (this *ScriptEngineForOptionImpl) RunFile(ctx context.Context, fname string) ([]byte, error) {
	println("Script is not supported.")
	return nil, nil
}

func (this *ScriptEngineForOptionImpl) RunString(code string) error {
	println("Script is not supported.")
	return nil
}

func Main() error {
	sh := shell.New()
	defer sh.Close()

	ctx := context.Background()

	langEngine := func(fname string) ([]byte, error) {
		return nil, nil
	}
	shellEngine := func(fname string) error {
		fd, err := os.Open(fname)
		if err != nil {
			return err
		}
		stream1 := shell.NewCmdStreamFile(fd)
		_, err = sh.Loop(ctx, stream1)
		fd.Close()
		if err == io.EOF {
			return nil
		} else {
			return err
		}
	}

	script, err := frame.OptionParse(sh, &ScriptEngineForOptionImpl{})
	if err != nil {
		return err
	}

	if !isatty.IsTerminal(os.Stdin.Fd()) || script != nil {
		frame.SilentMode = true
	}

	if !frame.OptionNorc {
		if !frame.SilentMode {
			fmt.Printf("Nihongo Yet Another GOing Shell %s-%s by %s\n",
				frame.VersionOrStamp(),
				runtime.GOARCH,
				runtime.Version())
			fmt.Println("(c) 2014-2018 NYAOS.ORG <http://www.nyaos.org>")
		}
		if err := frame.LoadScripts(shellEngine, langEngine); err != nil {
			fmt.Fprintln(os.Stderr, err.Error())
		}
	}

	if script != nil {
		if err := script(ctx); err != nil {
			if err != io.EOF {
				return err
			} else {
				return nil
			}
		}
	}

	var stream1 shell.Stream
	if isatty.IsTerminal(os.Stdin.Fd()) {
		constream := frame.NewCmdStreamConsole(
			func() (int, error) {
				functions.Prompt(
					[]interface{}{frame.Format2Prompt(os.Getenv("PROMPT"))})
				return 0, nil
			})
		stream1 = constream
		frame.DefaultHistory = constream.History
		ctx = context.WithValue(ctx, history.PackageId, constream.History)
	} else {
		stream1 = shell.NewCmdStreamFile(os.Stdin)
	}
	sh.ForEver(ctx, stream1)
	return nil
}
<|endoftext|>


<commit_before><commit_msg>[Fix]記述順序の修正<commit_after><|endoftext|>


<commit_before>package gopp_test

import (
	"fmt"
	"github.com/skelterjohn/gopp"
	"strings"
	"testing"
)

// tests where we create a grammar and parse a document

const mathgopp = `
# the root is an equation
Eqn => {type=MathEqn} {field=Left} <<Expr>> '=' {field=Right} <<Expr>> '\n'
Expr => {type=MathSum} {field=First} <<Term>> '+' {field=Second} <<Term>>
Expr => <Term>
Term => {type=MathProduct} {field=First} <<Factor>> '*' {field=Second} <<Factor>>
Term => <Factor>
Factor => {type=MathExprFactor} '(' {field=Expr} <<Expr>> ')'
Factor => {type=MathNumberFactor} {field=Number} <number>
number = /(\d+)/
`

type MathEqn struct {
	Left, Right interface{}
}

func (e MathEqn) String() string {
	return fmt.Sprintf("%s=%s", e.Left, e.Right)
}

type MathSum struct {
	First, Second interface{}
}

func (s MathSum) String() string {
	return fmt.Sprintf("%s+%s", s.First, s.Second)
}

type MathProduct struct {
	First, Second interface{}
}

func (p MathProduct) String() string {
	return fmt.Sprintf("%s*%s", p.First, p.Second)
}

type MathExprFactor struct {
	Expr interface{}
}

func (ef MathExprFactor) String() string {
	return fmt.Sprintf("(%s)", ef.Expr)
}

type MathNumberFactor struct {
	Number string
}

func (nf MathNumberFactor) String() string {
	return nf.Number
}

func TestMath(t *testing.T) {
	df, err := gopp.NewDecoderFactory(mathgopp, "Eqn")
	if err != nil {
		t.Error(err)
		return
	}
	df.RegisterType(MathExprFactor{})
	df.RegisterType(MathNumberFactor{})
	df.RegisterType(MathSum{})
	df.RegisterType(MathProduct{})
	dec := df.NewDecoder(strings.NewReader("5+1=6\n"))
	var eqn MathEqn
	err = dec.Decode(&eqn)
	if err != nil {
		t.Error(err)
		return
	}

	expectedEqn := MathEqn{
		Left: MathSum{
			First:  MathNumberFactor{"5"},
			Second: MathNumberFactor{"1"},
		},
		Right: MathNumberFactor{"6"},
	}

	if eqn != expectedEqn {
		t.Errorf("Expected %q, got %q.", expectedEqn, eqn)
	}
}

func TestMathPrecedence(t *testing.T) {
	df, err := gopp.NewDecoderFactory(mathgopp, "Eqn")
	if err != nil {
		t.Error(err)
		return
	}
	df.RegisterType(MathExprFactor{})
	df.RegisterType(MathNumberFactor{})
	df.RegisterType(MathSum{})
	df.RegisterType(MathProduct{})
	dec := df.NewDecoder(strings.NewReader("5+5*2=6*2+3\n"))
	var eqn MathEqn
	err = dec.Decode(&eqn)
	if err != nil {
		t.Error(err)
		return
	}

	expectedEqn := MathEqn{
		Left: MathSum{
			MathNumberFactor{"5"},
			MathProduct{
				MathNumberFactor{"5"},
				MathNumberFactor{"2"},
			},
		},
		Right: MathSum{
			MathProduct{
				MathNumberFactor{"6"},
				MathNumberFactor{"2"},
			},
			MathNumberFactor{"3"},
		},
	}

	if eqn != expectedEqn {
		t.Errorf("Expected %q, got %q.", expectedEqn, eqn)
	}
}
<commit_msg>useful comments in for math grammar<commit_after>package gopp_test

import (
	"fmt"
	"github.com/skelterjohn/gopp"
	"strings"
	"testing"
)

// tests where we create a grammar and parse a document

const mathgopp = `
# The root is an equation, with a left-hand and right-hand side.
Eqn => {type=MathEqn} {field=Left} <<Expr>> '=' {field=Right} <<Expr>> '\n'

# An Expr is either the sum of two terms,
Expr => {type=MathSum} {field=First} <<Term>> '+' {field=Second} <<Term>>
# or just another term.
Expr => <Term>

# A Term is either the product of two factors,
Term => {type=MathProduct} {field=First} <<Factor>> '*' {field=Second} <<Factor>>
# or just another factor.
Term => <Factor>

# A factor is either a parenthesized expression,
Factor => {type=MathExprFactor} '(' {field=Expr} <<Expr>> ')'
# or just a number.
Factor => {type=MathNumberFactor} {field=Number} <number>

# A number is a string of consecutive digits.
number = /(\d+)/
`

type MathEqn struct {
	Left, Right interface{}
}

func (e MathEqn) String() string {
	return fmt.Sprintf("%s=%s", e.Left, e.Right)
}

type MathSum struct {
	First, Second interface{}
}

func (s MathSum) String() string {
	return fmt.Sprintf("%s+%s", s.First, s.Second)
}

type MathProduct struct {
	First, Second interface{}
}

func (p MathProduct) String() string {
	return fmt.Sprintf("%s*%s", p.First, p.Second)
}

type MathExprFactor struct {
	Expr interface{}
}

func (ef MathExprFactor) String() string {
	return fmt.Sprintf("(%s)", ef.Expr)
}

type MathNumberFactor struct {
	Number string
}

func (nf MathNumberFactor) String() string {
	return nf.Number
}

func TestMath(t *testing.T) {
	df, err := gopp.NewDecoderFactory(mathgopp, "Eqn")
	if err != nil {
		t.Error(err)
		return
	}
	df.RegisterType(MathExprFactor{})
	df.RegisterType(MathNumberFactor{})
	df.RegisterType(MathSum{})
	df.RegisterType(MathProduct{})
	dec := df.NewDecoder(strings.NewReader("5+1=6\n"))
	var eqn MathEqn
	err = dec.Decode(&eqn)
	if err != nil {
		t.Error(err)
		return
	}

	expectedEqn := MathEqn{
		Left: MathSum{
			First:  MathNumberFactor{"5"},
			Second: MathNumberFactor{"1"},
		},
		Right: MathNumberFactor{"6"},
	}

	if eqn != expectedEqn {
		t.Errorf("Expected %q, got %q.", expectedEqn, eqn)
	}
}

func TestMathPrecedence(t *testing.T) {
	df, err := gopp.NewDecoderFactory(mathgopp, "Eqn")
	if err != nil {
		t.Error(err)
		return
	}
	df.RegisterType(MathExprFactor{})
	df.RegisterType(MathNumberFactor{})
	df.RegisterType(MathSum{})
	df.RegisterType(MathProduct{})
	dec := df.NewDecoder(strings.NewReader("5+5*2=6*2+3\n"))
	var eqn MathEqn
	err = dec.Decode(&eqn)
	if err != nil {
		t.Error(err)
		return
	}

	expectedEqn := MathEqn{
		Left: MathSum{
			MathNumberFactor{"5"},
			MathProduct{
				MathNumberFactor{"5"},
				MathNumberFactor{"2"},
			},
		},
		Right: MathSum{
			MathProduct{
				MathNumberFactor{"6"},
				MathNumberFactor{"2"},
			},
			MathNumberFactor{"3"},
		},
	}

	if eqn != expectedEqn {
		t.Errorf("Expected %q, got %q.", expectedEqn, eqn)
	}
}
<|endoftext|>


<commit_before>package gopp

import (
	"reflect"
	"strings"
	"testing"
)

var ByHandGrammarREs []TypedRegexp

func init() {
	var err error
	ByHandGrammarREs, err = ByHandGrammar.TokenREs()
	if err != nil {
		panic(err)
	}
}

func TestParseTag(t *testing.T) {
	tokens, err := Tokenize(ByHandGrammarREs, strings.NewReader("=> stuff"))
	if err != nil {
		t.Error(err)
		return
	}
	term := TagTerm{Tag: "hello"}
	items, remaining, err := term.Parse(ByHandGrammar, tokens)
	if err != nil {
		t.Error(err)
		return
	}
	if tag, ok := items[0].(Tag); ok {
		if tag != "hello" {
			t.Errorf("Expected %q, got %q.", "hello", tag)
			return
		}
	} else {
		t.Errorf("Expected Tag, got %T.", items[0])
		return
	}
	if !reflect.DeepEqual(remaining, tokens) {
		t.Errorf("Got wrong tokens remaining.")
		return
	}
}

func TestParseLiteral(t *testing.T) {
	tokens, err := Tokenize(ByHandGrammarREs, strings.NewReader("=> stuff"))
	if err != nil {
		t.Error(err)
		return
	}
	term := LiteralTerm{Literal: "=>"}
	items, remaining, err := term.Parse(ByHandGrammar, tokens)
	if err != nil {
		t.Error(err)
		return
	}
	if lit, ok := items[0].(Literal); ok {
		if lit != "=>" {
			t.Errorf("Expected %q, got %q.", "=>", lit)
			return
		}
	} else {
		t.Errorf("Expected Literal, got %T.", items[0])
		return
	}
	if !reflect.DeepEqual(remaining, tokens[1:]) {
		t.Errorf("Got wrong tokens remaining.")
		return
	}
}
<commit_msg>more testing, Tokenize() returns error<commit_after>package gopp

import (
	"reflect"
	"strings"
	"testing"
)

var ByHandGrammarREs []TypedRegexp

func init() {
	var err error
	ByHandGrammarREs, err = ByHandGrammar.TokenREs()
	if err != nil {
		panic(err)
	}
}

func TestParseSymbol(t *testing.T) {
	tokens, err := Tokenize(ByHandGrammarREs, strings.NewReader("'=>' stuff"))
	if err != nil {
		t.Error(err)
		return
	}
	term := InlineRuleTerm{Name:"literal"}
}

func TestParseTag(t *testing.T) {
	tokens, err := Tokenize(ByHandGrammarREs, strings.NewReader("=> stuff"))
	if err != nil {
		t.Error(err)
		return
	}
	term := TagTerm{Tag: "hello"}
	items, remaining, err := term.Parse(ByHandGrammar, tokens)
	if err != nil {
		t.Error(err)
		return
	}
	if tag, ok := items[0].(Tag); ok {
		if tag != "hello" {
			t.Errorf("Expected %q, got %q.", "hello", tag)
			return
		}
	} else {
		t.Errorf("Expected Tag, got %T.", items[0])
		return
	}
	if !reflect.DeepEqual(remaining, tokens) {
		t.Errorf("Got wrong tokens remaining.")
		return
	}
}

func TestParseLiteral(t *testing.T) {
	tokens, err := Tokenize(ByHandGrammarREs, strings.NewReader("=> stuff"))
	if err != nil {
		t.Error(err)
		return
	}
	term := LiteralTerm{Literal: "=>"}
	items, remaining, err := term.Parse(ByHandGrammar, tokens)
	if err != nil {
		t.Error(err)
		return
	}
	if lit, ok := items[0].(Literal); ok {
		if lit != "=>" {
			t.Errorf("Expected %q, got %q.", "=>", lit)
			return
		}
	} else {
		t.Errorf("Expected Literal, got %T.", items[0])
		return
	}
	if !reflect.DeepEqual(remaining, tokens[1:]) {
		t.Errorf("Got wrong tokens remaining.")
		return
	}
}
<|endoftext|>


<commit_before>package inst

import (
	"bytes"
	"fmt"
	"testing"

	test "github.com/openark/golib/tests"
)

var (
	i710k = InstanceKey{Hostname: "i710", Port: 3306}
	i720k = InstanceKey{Hostname: "i720", Port: 3306}
	i730k = InstanceKey{Hostname: "i730", Port: 3306}
)

func mkTestInstances() []*Instance {
	i710 := Instance{Key: i710k, ServerID: 710, ExecBinlogCoordinates: BinlogCoordinates{LogFile: "mysql.000007", LogPos: 10}}
	i720 := Instance{Key: i720k, ServerID: 720, ExecBinlogCoordinates: BinlogCoordinates{LogFile: "mysql.000007", LogPos: 20}}
	i730 := Instance{Key: i730k, ServerID: 730, ExecBinlogCoordinates: BinlogCoordinates{LogFile: "mysql.000007", LogPos: 30}}
	instances := []*Instance{&i710, &i720, &i730}
	for _, instance := range instances {
		instance.Version = "5.6.7"
		instance.VersionComment = "MySQL"
		instance.Binlog_format = "STATEMENT"
	}
	return instances
}

func TestMkInsertOdku(t *testing.T) {
	instances := mkTestInstances()

	sql, args := mkInsertOdkuForInstances(nil, true, true)
	test.S(t).ExpectEquals(sql, "")
	test.S(t).ExpectEquals(len(args), 0)

	// one instance
	s1 := `INSERT ignore INTO database_instance
                (hostname, port, last_checked, last_attempted_check, uptime, server_id, server_uuid, version, version_comment, binlog_server, read_only, binlog_format, log_bin, log_slave_updates, binary_log_file, binary_log_pos, master_host, master_port, slave_sql_running, slave_io_running, has_replication_filters, supports_oracle_gtid, oracle_gtid, executed_gtid_set, gtid_purged, mariadb_gtid, pseudo_gtid, master_log_file, read_master_log_pos, relay_master_log_file, exec_master_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, seconds_behind_master, slave_lag_seconds, sql_delay, num_slave_hosts, slave_hosts, cluster_name, suggested_cluster_alias, data_center, physical_environment, replication_depth, is_co_master, replication_credentials_available, has_replication_credentials, allow_tls, semi_sync_enforced, instance_alias, last_seen)
        VALUES
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
        ON DUPLICATE KEY UPDATE
                hostname=VALUES(hostname), port=VALUES(port), last_checked=VALUES(last_checked), last_attempted_check=VALUES(last_attempted_check), uptime=VALUES(uptime), server_id=VALUES(server_id), server_uuid=VALUES(server_uuid), version=VALUES(version), version_comment=VALUES(version_comment), binlog_server=VALUES(binlog_server), read_only=VALUES(read_only), binlog_format=VALUES(binlog_format), log_bin=VALUES(log_bin), log_slave_updates=VALUES(log_slave_updates), binary_log_file=VALUES(binary_log_file), binary_log_pos=VALUES(binary_log_pos), master_host=VALUES(master_host), master_port=VALUES(master_port), slave_sql_running=VALUES(slave_sql_running), slave_io_running=VALUES(slave_io_running), has_replication_filters=VALUES(has_replication_filters), supports_oracle_gtid=VALUES(supports_oracle_gtid), oracle_gtid=VALUES(oracle_gtid), executed_gtid_set=VALUES(executed_gtid_set), gtid_purged=VALUES(gtid_purged), mariadb_gtid=VALUES(mariadb_gtid), pseudo_gtid=VALUES(pseudo_gtid), master_log_file=VALUES(master_log_file), read_master_log_pos=VALUES(read_master_log_pos), relay_master_log_file=VALUES(relay_master_log_file), exec_master_log_pos=VALUES(exec_master_log_pos), relay_log_file=VALUES(relay_log_file), relay_log_pos=VALUES(relay_log_pos), last_sql_error=VALUES(last_sql_error), last_io_error=VALUES(last_io_error), seconds_behind_master=VALUES(seconds_behind_master), slave_lag_seconds=VALUES(slave_lag_seconds), sql_delay=VALUES(sql_delay), num_slave_hosts=VALUES(num_slave_hosts), slave_hosts=VALUES(slave_hosts), cluster_name=VALUES(cluster_name), suggested_cluster_alias=VALUES(suggested_cluster_alias), data_center=VALUES(data_center), physical_environment=VALUES(physical_environment), replication_depth=VALUES(replication_depth), is_co_master=VALUES(is_co_master), replication_credentials_available=VALUES(replication_credentials_available), has_replication_credentials=VALUES(has_replication_credentials), allow_tls=VALUES(allow_tls), semi_sync_enforced=VALUES(semi_sync_enforced), instance_alias=VALUES(instance_alias), last_seen=VALUES(last_seen)
        `
	a1 := `i710, 3306, 0, 710, , 5.6.7, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 10, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , `

	sql1, args1 := mkInsertOdkuForInstances(instances[:1], false, true)

	test.S(t).ExpectEquals(sql1, s1)
	test.S(t).ExpectEquals(fmtArgs(args1), a1)

	// three instances
	s3 := `INSERT  INTO database_instance
                (hostname, port, last_checked, last_attempted_check, uptime, server_id, server_uuid, version, version_comment, binlog_server, read_only, binlog_format, log_bin, log_slave_updates, binary_log_file, binary_log_pos, master_host, master_port, slave_sql_running, slave_io_running, has_replication_filters, supports_oracle_gtid, oracle_gtid, executed_gtid_set, gtid_purged, mariadb_gtid, pseudo_gtid, master_log_file, read_master_log_pos, relay_master_log_file, exec_master_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, seconds_behind_master, slave_lag_seconds, sql_delay, num_slave_hosts, slave_hosts, cluster_name, suggested_cluster_alias, data_center, physical_environment, replication_depth, is_co_master, replication_credentials_available, has_replication_credentials, allow_tls, semi_sync_enforced, instance_alias, last_seen)
        VALUES
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()),
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()),
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
        ON DUPLICATE KEY UPDATE
                hostname=VALUES(hostname), port=VALUES(port), last_checked=VALUES(last_checked), last_attempted_check=VALUES(last_attempted_check), uptime=VALUES(uptime), server_id=VALUES(server_id), server_uuid=VALUES(server_uuid), version=VALUES(version), version_comment=VALUES(version_comment), binlog_server=VALUES(binlog_server), read_only=VALUES(read_only), binlog_format=VALUES(binlog_format), log_bin=VALUES(log_bin), log_slave_updates=VALUES(log_slave_updates), binary_log_file=VALUES(binary_log_file), binary_log_pos=VALUES(binary_log_pos), master_host=VALUES(master_host), master_port=VALUES(master_port), slave_sql_running=VALUES(slave_sql_running), slave_io_running=VALUES(slave_io_running), has_replication_filters=VALUES(has_replication_filters), supports_oracle_gtid=VALUES(supports_oracle_gtid), oracle_gtid=VALUES(oracle_gtid), executed_gtid_set=VALUES(executed_gtid_set), gtid_purged=VALUES(gtid_purged), mariadb_gtid=VALUES(mariadb_gtid), pseudo_gtid=VALUES(pseudo_gtid), master_log_file=VALUES(master_log_file), read_master_log_pos=VALUES(read_master_log_pos), relay_master_log_file=VALUES(relay_master_log_file), exec_master_log_pos=VALUES(exec_master_log_pos), relay_log_file=VALUES(relay_log_file), relay_log_pos=VALUES(relay_log_pos), last_sql_error=VALUES(last_sql_error), last_io_error=VALUES(last_io_error), seconds_behind_master=VALUES(seconds_behind_master), slave_lag_seconds=VALUES(slave_lag_seconds), sql_delay=VALUES(sql_delay), num_slave_hosts=VALUES(num_slave_hosts), slave_hosts=VALUES(slave_hosts), cluster_name=VALUES(cluster_name), suggested_cluster_alias=VALUES(suggested_cluster_alias), data_center=VALUES(data_center), physical_environment=VALUES(physical_environment), replication_depth=VALUES(replication_depth), is_co_master=VALUES(is_co_master), replication_credentials_available=VALUES(replication_credentials_available), has_replication_credentials=VALUES(has_replication_credentials), allow_tls=VALUES(allow_tls), semi_sync_enforced=VALUES(semi_sync_enforced), instance_alias=VALUES(instance_alias), last_seen=VALUES(last_seen)
        `
	a3 := `i710, 3306, 0, 710, , 5.6.7, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 10, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , i720, 3306, 0, 720, , 5.6.7, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 20, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , i730, 3306, 0, 730, , 5.6.7, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 30, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , `

	sql3, args3 := mkInsertOdkuForInstances(instances[:3], true, true)

	test.S(t).ExpectEquals(sql3, s3)
	test.S(t).ExpectEquals(fmtArgs(args3), a3)
}

func fmtArgs(args []interface{}) string {
	b := &bytes.Buffer{}
	for _, a := range args {
		fmt.Fprint(b, a)
		fmt.Fprint(b, ", ")
	}
	return b.String()
}
<commit_msg>fixed failing tests<commit_after>package inst

import (
	"bytes"
	"fmt"
	"regexp"
	"strings"
	"testing"

	test "github.com/openark/golib/tests"
)

var (
	i710k = InstanceKey{Hostname: "i710", Port: 3306}
	i720k = InstanceKey{Hostname: "i720", Port: 3306}
	i730k = InstanceKey{Hostname: "i730", Port: 3306}
)

var (
	spacesRegexp = regexp.MustCompile(`[ \t\n\r]+`)
)

func normalizeQuery(name string) string {
	name = strings.Replace(name, "`", "", -1)
	name = spacesRegexp.ReplaceAllString(name, " ")
	name = strings.TrimSpace(name)
	return name
}

func mkTestInstances() []*Instance {
	i710 := Instance{Key: i710k, ServerID: 710, ExecBinlogCoordinates: BinlogCoordinates{LogFile: "mysql.000007", LogPos: 10}}
	i720 := Instance{Key: i720k, ServerID: 720, ExecBinlogCoordinates: BinlogCoordinates{LogFile: "mysql.000007", LogPos: 20}}
	i730 := Instance{Key: i730k, ServerID: 730, ExecBinlogCoordinates: BinlogCoordinates{LogFile: "mysql.000007", LogPos: 30}}
	instances := []*Instance{&i710, &i720, &i730}
	for _, instance := range instances {
		instance.Version = "5.6.7"
		instance.VersionComment = "MySQL"
		instance.Binlog_format = "STATEMENT"
	}
	return instances
}

func TestMkInsertOdku(t *testing.T) {
	instances := mkTestInstances()

	sql, args := mkInsertOdkuForInstances(nil, true, true)
	test.S(t).ExpectEquals(sql, "")
	test.S(t).ExpectEquals(len(args), 0)

	// one instance
	s1 := `INSERT ignore INTO database_instance
                (hostname, port, last_checked, last_attempted_check, uptime, server_id, server_uuid, version, major_version, version_comment, binlog_server, read_only, binlog_format, log_bin, log_slave_updates, binary_log_file, binary_log_pos, master_host, master_port, slave_sql_running, slave_io_running, has_replication_filters, supports_oracle_gtid, oracle_gtid, executed_gtid_set, gtid_purged, mariadb_gtid, pseudo_gtid, master_log_file, read_master_log_pos, relay_master_log_file, exec_master_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, seconds_behind_master, slave_lag_seconds, sql_delay, num_slave_hosts, slave_hosts, cluster_name, suggested_cluster_alias, data_center, physical_environment, replication_depth, is_co_master, replication_credentials_available, has_replication_credentials, allow_tls, semi_sync_enforced, instance_alias, last_seen)
        VALUES
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
        ON DUPLICATE KEY UPDATE
                hostname=VALUES(hostname), port=VALUES(port), last_checked=VALUES(last_checked), last_attempted_check=VALUES(last_attempted_check), uptime=VALUES(uptime), server_id=VALUES(server_id), server_uuid=VALUES(server_uuid), version=VALUES(version), major_version=VALUES(major_version), version_comment=VALUES(version_comment), binlog_server=VALUES(binlog_server), read_only=VALUES(read_only), binlog_format=VALUES(binlog_format), log_bin=VALUES(log_bin), log_slave_updates=VALUES(log_slave_updates), binary_log_file=VALUES(binary_log_file), binary_log_pos=VALUES(binary_log_pos), master_host=VALUES(master_host), master_port=VALUES(master_port), slave_sql_running=VALUES(slave_sql_running), slave_io_running=VALUES(slave_io_running), has_replication_filters=VALUES(has_replication_filters), supports_oracle_gtid=VALUES(supports_oracle_gtid), oracle_gtid=VALUES(oracle_gtid), executed_gtid_set=VALUES(executed_gtid_set), gtid_purged=VALUES(gtid_purged), mariadb_gtid=VALUES(mariadb_gtid), pseudo_gtid=VALUES(pseudo_gtid), master_log_file=VALUES(master_log_file), read_master_log_pos=VALUES(read_master_log_pos), relay_master_log_file=VALUES(relay_master_log_file), exec_master_log_pos=VALUES(exec_master_log_pos), relay_log_file=VALUES(relay_log_file), relay_log_pos=VALUES(relay_log_pos), last_sql_error=VALUES(last_sql_error), last_io_error=VALUES(last_io_error), seconds_behind_master=VALUES(seconds_behind_master), slave_lag_seconds=VALUES(slave_lag_seconds), sql_delay=VALUES(sql_delay), num_slave_hosts=VALUES(num_slave_hosts), slave_hosts=VALUES(slave_hosts), cluster_name=VALUES(cluster_name), suggested_cluster_alias=VALUES(suggested_cluster_alias), data_center=VALUES(data_center), physical_environment=VALUES(physical_environment), replication_depth=VALUES(replication_depth), is_co_master=VALUES(is_co_master), replication_credentials_available=VALUES(replication_credentials_available), has_replication_credentials=VALUES(has_replication_credentials), allow_tls=VALUES(allow_tls), semi_sync_enforced=VALUES(semi_sync_enforced), instance_alias=VALUES(instance_alias), last_seen=VALUES(last_seen)
        `
	a1 := `i710, 3306, 0, 710, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 10, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , `

	sql1, args1 := mkInsertOdkuForInstances(instances[:1], false, true)

	test.S(t).ExpectEquals(normalizeQuery(sql1), normalizeQuery(s1))
	test.S(t).ExpectEquals(fmtArgs(args1), a1)

	// three instances
	s3 := `INSERT  INTO database_instance
                (hostname, port, last_checked, last_attempted_check, uptime, server_id, server_uuid, version, major_version, version_comment, binlog_server, read_only, binlog_format, log_bin, log_slave_updates, binary_log_file, binary_log_pos, master_host, master_port, slave_sql_running, slave_io_running, has_replication_filters, supports_oracle_gtid, oracle_gtid, executed_gtid_set, gtid_purged, mariadb_gtid, pseudo_gtid, master_log_file, read_master_log_pos, relay_master_log_file, exec_master_log_pos, relay_log_file, relay_log_pos, last_sql_error, last_io_error, seconds_behind_master, slave_lag_seconds, sql_delay, num_slave_hosts, slave_hosts, cluster_name, suggested_cluster_alias, data_center, physical_environment, replication_depth, is_co_master, replication_credentials_available, has_replication_credentials, allow_tls, semi_sync_enforced, instance_alias, last_seen)
        VALUES
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()),
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()),
                (?, ?, NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
        ON DUPLICATE KEY UPDATE
                hostname=VALUES(hostname), port=VALUES(port), last_checked=VALUES(last_checked), last_attempted_check=VALUES(last_attempted_check), uptime=VALUES(uptime), server_id=VALUES(server_id), server_uuid=VALUES(server_uuid), version=VALUES(version), major_version=VALUES(major_version), version_comment=VALUES(version_comment), binlog_server=VALUES(binlog_server), read_only=VALUES(read_only), binlog_format=VALUES(binlog_format), log_bin=VALUES(log_bin), log_slave_updates=VALUES(log_slave_updates), binary_log_file=VALUES(binary_log_file), binary_log_pos=VALUES(binary_log_pos), master_host=VALUES(master_host), master_port=VALUES(master_port), slave_sql_running=VALUES(slave_sql_running), slave_io_running=VALUES(slave_io_running), has_replication_filters=VALUES(has_replication_filters), supports_oracle_gtid=VALUES(supports_oracle_gtid), oracle_gtid=VALUES(oracle_gtid), executed_gtid_set=VALUES(executed_gtid_set), gtid_purged=VALUES(gtid_purged), mariadb_gtid=VALUES(mariadb_gtid), pseudo_gtid=VALUES(pseudo_gtid), master_log_file=VALUES(master_log_file), read_master_log_pos=VALUES(read_master_log_pos), relay_master_log_file=VALUES(relay_master_log_file), exec_master_log_pos=VALUES(exec_master_log_pos), relay_log_file=VALUES(relay_log_file), relay_log_pos=VALUES(relay_log_pos), last_sql_error=VALUES(last_sql_error), last_io_error=VALUES(last_io_error), seconds_behind_master=VALUES(seconds_behind_master), slave_lag_seconds=VALUES(slave_lag_seconds), sql_delay=VALUES(sql_delay), num_slave_hosts=VALUES(num_slave_hosts), slave_hosts=VALUES(slave_hosts), cluster_name=VALUES(cluster_name), suggested_cluster_alias=VALUES(suggested_cluster_alias), data_center=VALUES(data_center), physical_environment=VALUES(physical_environment), replication_depth=VALUES(replication_depth), is_co_master=VALUES(is_co_master), replication_credentials_available=VALUES(replication_credentials_available), has_replication_credentials=VALUES(has_replication_credentials), allow_tls=VALUES(allow_tls), semi_sync_enforced=VALUES(semi_sync_enforced), instance_alias=VALUES(instance_alias), last_seen=VALUES(last_seen)
        `
	a3 := `i710, 3306, 0, 710, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 10, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , i720, 3306, 0, 720, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 20, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , i730, 3306, 0, 730, , 5.6.7, 5.6, MySQL, false, false, STATEMENT, false, false, , 0, , 0, false, false, false, false, false, , , false, false, , 0, mysql.000007, 30, , 0, , , {0 false}, {0 false}, 0, 0, [], , , , , 0, false, false, false, false, false, , `

	sql3, args3 := mkInsertOdkuForInstances(instances[:3], true, true)

	test.S(t).ExpectEquals(normalizeQuery(sql3), normalizeQuery(s3))
	test.S(t).ExpectEquals(fmtArgs(args3), a3)
}

func fmtArgs(args []interface{}) string {
	b := &bytes.Buffer{}
	for _, a := range args {
		fmt.Fprint(b, a)
		fmt.Fprint(b, ", ")
	}
	return b.String()
}
<|endoftext|>


<commit_before>package record

import (
	"bytes"
	"fmt"
	"testing"
)

func TestOpFrame(t *testing.T) {
	var buf bytes.Buffer

	frame := &OpFrame{}
	frame.Ops = []Op{
		&OpMemMap{0x1000, 0x1000, 7},
		&OpMemWrite{0x1000, []byte{0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00}}, // mov rax, 1
		&OpExecAbs{0x1000, 0x7},
		&OpRegChange{35, 1}, // 35 is rax
	}

	if _, err := Pack(&buf, frame); err != nil {
		t.Fatal(err)
	}

	fmt.Println(buf.Bytes())

	op, _, err := Unpack(&buf)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println(op)
}
<commit_msg>remove prints from ops_test<commit_after>package record

import (
	"bytes"
	"testing"
)

func TestOpFrame(t *testing.T) {
	var buf bytes.Buffer

	frame := &OpFrame{}
	frame.Ops = []Op{
		&OpMemMap{0x1000, 0x1000, 7},
		&OpMemWrite{0x1000, []byte{0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00}}, // mov rax, 1
		&OpExecAbs{0x1000, 0x7},
		&OpRegChange{35, 1}, // 35 is rax
	}

	if _, err := Pack(&buf, frame); err != nil {
		t.Fatal(err)
	}

	_, _, err := Unpack(&buf)
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Package binary has functions for abusing a binary tree.
//
// To use the binary tree functions, start with an empty
// tree.Interface and Insert all elements into it:
//     empty := Ints([]int{})
//     for i := 0; i < 10; i++ {
//       binary.Insert(empty)
//     }
//
package binary

type BinaryTree struct {
	root *node
	size int
}

// Returns a new, empty binary tree.
func New() *BinaryTree {
	return &BinaryTree{size: 0}
}

type node struct {
	parent *node
	left   *node
	right  *node
	value  Comparable
}

// A type that implements the comparable interface can be used in binary trees.
type Comparable interface {
	// Returns -1 if other is less, 0 if they are equal and 1 if other is greater.
	CompareTo(other Comparable) int
}

func walkInOrder(node *node, walked []Comparable) []Comparable {
	if node != nil {
		walked = walkInOrder(node.left, walked)
		walked = append(walked, node.value)
		walked = walkInOrder(node.right, walked)
		return walked
	}
	return nil
}

// Returns an in order Comparable slice of the binary tree.
func (b *BinaryTree) Walk() []Comparable {
	walked := make([]Comparable, 0, b.size)
	return walkInOrder(b.root, walked)
}

func walkPreOrder(node *node, walked []Comparable) []Comparable {
	if node != nil {
		walked = append(walked, node.value)
		walked = walkPreOrder(node.left, walked)
		walked = walkPreOrder(node.right, walked)
		return walked
	}
	return nil
}

// Returns a pre order Comparable slice of the binary tree.
func (b *BinaryTree) WalkPreOrder() []Comparable {
	walked := make([]Comparable, 0, b.size)
	return walkPreOrder(b.root, walked)
}

func walkPostOrder(node *node, walked []Comparable) []Comparable {
	if node != nil {
		walked = walkPostOrder(node.left, walked)
		walked = walkPostOrder(node.right, walked)
		walked = append(walked, node.value)
		return walked
	}
	return nil
}

// Returns a post order Comparable slice of the binary tree.
func (b *BinaryTree) WalkPostOrder() []Comparable {
	walked := make([]Comparable, 0, b.size)
	return walkPostOrder(b.root, walked)
}

// Search for a Comparable in the tree and returns the node that contains it.
// If no node does, returns nil.
func (b *BinaryTree) search(target Comparable) *node {
	current := b.root
	for current != nil {
		switch current.value.CompareTo(target) {
		case -1:
			current = current.left
		case 0:
			return current
		case 1:
			current = current.right
		}
	}
	return nil
}

// Returns true if the binary tree contains the target Comparable
func (b *BinaryTree) Contains(target Comparable) bool {
	return b.search(target) != nil
}

// returns a pointer to the node that contains the minimum
// starting from the start node
func minimum(startNode *node) *node {
	current := startNode
	for current.left != nil {
		current = current.left
	}
	return current
}

// Returns a pointer to a copy of the minimum value
// in the binary tree. If the tree is empty, this will
// return nil.
func (b *BinaryTree) Minimum() *Comparable {
	return &minimum(b.root).value
}

// Returns a pointer to a copy of the maximum value
// in the binary tree. If the tree is empty, this will
// return nil.
func (b *BinaryTree) Maximum() *Comparable {
	current := b.root
	for current.right != nil {
		current = current.right
	}
	// copy
	rval := current.value
	return &rval
}

// Returns a pointer to the successor value of a target Comparable in a binary tree.
// The pointer will be nil if the tree does not contain the target
// of if there is no successor (i.e., you want the successor to the maximum value)
func (b *BinaryTree) Successor(target Comparable) *Comparable {
	current := b.search(target)
	if current == nil {
		return nil
	}
	if current.right != nil {
		return &minimum(current.right).value
	}
	if current == b.root { // if root has no right side, at max, no successor
		return nil
	}
	parent := current.parent
	for parent != nil && current == parent.right {
		current = parent
		parent = current.parent
	}
	return &current.value
}

// Inserts a comparable value into a binary tree.
// Expected running time O(lg n), worst case running time O(n)
// for a tree with n nodes.
func (b *BinaryTree) Insert(value Comparable) {
	newNode := new(node)
	newNode.value = value
	y := (*node)(nil)
	x := b.root
	for x != nil {
		y = x
		if value.CompareTo(x.value) == -1 { // less
			x = x.left
		} else {
			x = x.right
		}
	}
	newNode.parent = y
	if newNode.parent == nil {
		b.root = newNode
	} else if newNode.value.CompareTo(y.value) == -1 {
		y.left = newNode
	} else {
		y.right = newNode
	}
	b.size++
}

// Sets the replacement node's parent information to that
// of the old node and updates the parent of the old node
// to point to the replacement. This also updates pointer
// values on the old node to nil. For garbage collection.
func (b *BinaryTree) transplant(old, replacement *node) {
	if old.parent == nil {
		b.root = replacement
	} else if old == old.parent.left {
		old.parent.left = replacement
	} else {
		old.parent.right = replacement
	}
	if replacement != nil {
		replacement.parent = old.parent
	}
	// garbage collect
	old.parent = nil
	old.left = nil
	old.right = nil
}

// Deletes and returns a pointer to a Comparable value from
// the binary tree. If the value was not in the binary tree,
// this function returns nil. Running time is O(h) for a tree of
// height h.
func (b *BinaryTree) Delete(value Comparable) *Comparable {
	// first find the node because of my decision to not allow access to nodes.
	node := b.search(value)
	if node == nil {
		return nil
	}
	if node.left == nil {
		b.transplant(node, node.right)
	} else if node.right == nil {
		b.transplant(node, node.left)
	} else {
		replacement := minimum(node.right)
		// testing if replacement.parent == node only saves a few steps
		// at the expense of making the delete more confusing
		b.transplant(replacement, replacement.right) // sever ties
		replacement.right = node.right
		replacement.right.parent = replacement
		replacement.left = node.left
		replacement.left.parent = replacement
		b.transplant(node, replacement)
	}
	return &node.value
}
<commit_msg>update documentation<commit_after>// Package binary has functions for abusing a binary tree.
package binary

type BinaryTree struct {
	root *node
	size int
}

// Returns a new, empty binary tree.
func New() *BinaryTree {
	return &BinaryTree{size: 0}
}

type node struct {
	parent *node
	left   *node
	right  *node
	value  Comparable
}

// A type that implements the comparable interface can be used in binary trees.
type Comparable interface {
	// Returns -1 if other is less, 0 if they are equal and 1 if other is greater.
	CompareTo(other Comparable) int
}

func walkInOrder(node *node, walked []Comparable) []Comparable {
	if node != nil {
		walked = walkInOrder(node.left, walked)
		walked = append(walked, node.value)
		walked = walkInOrder(node.right, walked)
		return walked
	}
	return nil
}

// Returns an in order Comparable slice of the binary tree.
func (b *BinaryTree) Walk() []Comparable {
	walked := make([]Comparable, 0, b.size)
	return walkInOrder(b.root, walked)
}

func walkPreOrder(node *node, walked []Comparable) []Comparable {
	if node != nil {
		walked = append(walked, node.value)
		walked = walkPreOrder(node.left, walked)
		walked = walkPreOrder(node.right, walked)
		return walked
	}
	return nil
}

// Returns a pre order Comparable slice of the binary tree.
func (b *BinaryTree) WalkPreOrder() []Comparable {
	walked := make([]Comparable, 0, b.size)
	return walkPreOrder(b.root, walked)
}

func walkPostOrder(node *node, walked []Comparable) []Comparable {
	if node != nil {
		walked = walkPostOrder(node.left, walked)
		walked = walkPostOrder(node.right, walked)
		walked = append(walked, node.value)
		return walked
	}
	return nil
}

// Returns a post order Comparable slice of the binary tree.
func (b *BinaryTree) WalkPostOrder() []Comparable {
	walked := make([]Comparable, 0, b.size)
	return walkPostOrder(b.root, walked)
}

// Search for a Comparable in the tree and returns the node that contains it.
// If no node does, returns nil.
func (b *BinaryTree) search(target Comparable) *node {
	current := b.root
	for current != nil {
		switch current.value.CompareTo(target) {
		case -1:
			current = current.left
		case 0:
			return current
		case 1:
			current = current.right
		}
	}
	return nil
}

// Returns true if the binary tree contains the target Comparable
func (b *BinaryTree) Contains(target Comparable) bool {
	return b.search(target) != nil
}

// returns a pointer to the node that contains the minimum
// starting from the start node
func minimum(startNode *node) *node {
	current := startNode
	for current.left != nil {
		current = current.left
	}
	return current
}

// Returns a pointer to a copy of the minimum value
// in the binary tree. If the tree is empty, this will
// return nil.
func (b *BinaryTree) Minimum() *Comparable {
	return &minimum(b.root).value
}

// Returns a pointer to a copy of the maximum value
// in the binary tree. If the tree is empty, this will
// return nil.
func (b *BinaryTree) Maximum() *Comparable {
	current := b.root
	for current.right != nil {
		current = current.right
	}
	// copy
	rval := current.value
	return &rval
}

// Returns a pointer to the successor value of a target Comparable in a binary tree.
// The pointer will be nil if the tree does not contain the target
// of if there is no successor (i.e., you want the successor to the maximum value)
func (b *BinaryTree) Successor(target Comparable) *Comparable {
	current := b.search(target)
	if current == nil {
		return nil
	}
	if current.right != nil {
		return &minimum(current.right).value
	}
	if current == b.root { // if root has no right side, at max, no successor
		return nil
	}
	parent := current.parent
	for parent != nil && current == parent.right {
		current = parent
		parent = current.parent
	}
	return &current.value
}

// Inserts a comparable value into a binary tree.
// Expected running time O(lg n), worst case running time O(n)
// for a tree with n nodes.
func (b *BinaryTree) Insert(value Comparable) {
	newNode := new(node)
	newNode.value = value
	y := (*node)(nil)
	x := b.root
	for x != nil {
		y = x
		if value.CompareTo(x.value) == -1 { // less
			x = x.left
		} else {
			x = x.right
		}
	}
	newNode.parent = y
	if newNode.parent == nil {
		b.root = newNode
	} else if newNode.value.CompareTo(y.value) == -1 {
		y.left = newNode
	} else {
		y.right = newNode
	}
	b.size++
}

// Sets the replacement node's parent information to that
// of the old node and updates the parent of the old node
// to point to the replacement. This also updates pointer
// values on the old node to nil. For garbage collection.
func (b *BinaryTree) transplant(old, replacement *node) {
	if old.parent == nil {
		b.root = replacement
	} else if old == old.parent.left {
		old.parent.left = replacement
	} else {
		old.parent.right = replacement
	}
	if replacement != nil {
		replacement.parent = old.parent
	}
	// garbage collect
	old.parent = nil
	old.left = nil
	old.right = nil
}

// Deletes and returns a pointer to a Comparable value from
// the binary tree. If the value was not in the binary tree,
// this function returns nil. Running time is O(h) for a tree of
// height h.
func (b *BinaryTree) Delete(value Comparable) *Comparable {
	// first find the node because of my decision to not allow access to nodes.
	node := b.search(value)
	if node == nil {
		return nil
	}
	if node.left == nil {
		b.transplant(node, node.right)
	} else if node.right == nil {
		b.transplant(node, node.left)
	} else {
		replacement := minimum(node.right)
		// testing if replacement.parent == node only saves a few steps
		// at the expense of making the delete more confusing
		b.transplant(replacement, replacement.right) // sever ties
		replacement.right = node.right
		replacement.right.parent = replacement
		replacement.left = node.left
		replacement.left.parent = replacement
		b.transplant(node, replacement)
	}
	return &node.value
}
<|endoftext|>