<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
	"github.com/lhcb-org/pkr/yum"
)

type External struct {
	cmd string
	err error
}
type FixFct func(*Context) error

type Context struct {
	msg       *logger.Logger
	cfg       Config
	siteroot  string // where to install software, binaries, ...
	repourl   string
	rpmprefix string
	dbpath    string
	etcdir    string
	yumconf   string
	yumreposd string
	yum       *yum.Client
	tmpdir    string
	bindir    string
	libdir    string
	initfile  string

	extstatus map[string]External
	reqext    []string
	extfix    map[string]FixFct
}

func New(cfg Config, dbg bool) (*Context, error) {
	var err error
	siteroot := cfg.Siteroot()
	if siteroot == "" {
		siteroot = "/opt/cern-sw"
	}

	ctx := Context{
		cfg:       cfg,
		msg:       logger.NewLogger("pkr", logger.INFO, os.Stdout),
		siteroot:  siteroot,
		repourl:   cfg.RepoUrl(),
		rpmprefix: cfg.Prefix(),
		dbpath:    filepath.Join(siteroot, "var", "lib", "rpm"),
		etcdir:    filepath.Join(siteroot, "etc"),
		yumconf:   filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposd: filepath.Join(siteroot, "etc", "yum.repos.d"),
		tmpdir:    filepath.Join(siteroot, "tmp"),
		bindir:    filepath.Join(siteroot, "usr", "bin"),
		libdir:    filepath.Join(siteroot, "lib"),
		initfile:  filepath.Join(siteroot, "etc", "repoinit"),
	}
	if dbg {
		ctx.msg.SetLevel(logger.DEBUG)
	}
	for _, dir := range []string{
		ctx.tmpdir,
		ctx.bindir,
		ctx.libdir,
	} {
		err = os.MkdirAll(dir, 0644)
		if err != nil {
			ctx.msg.Errorf("could not create directory %q: %v\n", dir, err)
			return nil, err
		}
	}
	os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+ctx.bindir)

	// make sure the db is initialized
	err = ctx.initRpmDb()
	if err != nil {
		return nil, err
	}

	// yum
	err = ctx.initYum()
	if err != nil {
		return nil, err
	}

	ctx.yum, err = yum.New(ctx.siteroot)
	if err != nil {
		return nil, err
	}

	// defining structures and checking if all needed tools are available
	ctx.extstatus = make(map[string]External)
	ctx.reqext = []string{"rpm"}
	ctx.extfix = make(map[string]FixFct)
	err = ctx.checkPreRequisites()
	if err != nil {
		return nil, err
	}

	err = ctx.checkRepository()
	if err != nil {
		return nil, err
	}

	return &ctx, err
}

func (ctx *Context) Exit(rc int) {
	os.Exit(rc)
}

// initRpmDb initializes the RPM database
func (ctx *Context) initRpmDb() error {
	var err error
	msg := ctx.msg
	msg.Infof("RPM DB in %q\n", ctx.dbpath)
	err = os.MkdirAll(ctx.dbpath, 0644)
	if err != nil {
		msg.Errorf(
			"could not create directory %q for RPM DB: %v\n",
			ctx.dbpath,
			err,
		)
		return err
	}

	pkgdir := filepath.Join(ctx.dbpath, "Packages")
	if !path_exists(pkgdir) {
		msg.Infof("Initializing RPM db\n")
		cmd := exec.Command(
			"rpm",
			"--dbpath", ctx.dbpath,
			"--initdb",
		)
		out, err := cmd.CombinedOutput()
		msg.Debugf(string(out))
		if err != nil {
			return fmt.Errorf("error initializing RPM DB: %v", err)
		}
	}
	return err
}

func (ctx *Context) initYum() error {
	var err error
	err = os.MkdirAll(ctx.etcdir, 0644)
	if err != nil {
		return fmt.Errorf("could not create dir %q: %v", ctx.etcdir, err)
	}

	if !path_exists(ctx.yumconf) {
		yum, err := os.Create(ctx.yumconf)
		if err != nil {
			return err
		}
		defer yum.Close()
		err = ctx.writeYumConf(yum)
		if err != nil {
			return err
		}
		err = yum.Sync()
		if err != nil {
			return err
		}
		err = yum.Close()
		if err != nil {
			return err
		}
	}
	err = ctx.cfg.InitYum(ctx)
	return err
}

// checkPreRequisites makes sure that all external tools required by
// this tool to perform the installation are present
func (ctx *Context) checkPreRequisites() error {
	var err error
	extmissing := false
	missing := make([]string, 0)

	for _, ext := range ctx.reqext {
		cmd, err := exec.LookPath(ext)
		ctx.extstatus[ext] = External{
			cmd: cmd,
			err: err,
		}
	}

	for k, ext := range ctx.extstatus {
		if ext.err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, ext.cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing - trying compensatory measure\n", k)
		fix, ok := ctx.extfix[k]
		if !ok {
			extmissing = true
			missing = append(missing, k)
			continue
		}

		err = fix(ctx)
		if err != nil {
			return err
		}

		cmd, err := exec.LookPath(k)
		ctx.extstatus[k] = External{
			cmd: cmd,
			err: err,
		}
		if err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing\n", k)
		extmissing = true
		missing = append(missing, k)
	}

	if extmissing {
		err = fmt.Errorf("missing external(s): %v", missing)
	}
	return err
}

func (ctx *Context) checkRepository() error {
	var err error
	if !path_exists(ctx.initfile) {
		fini, err := os.Create(ctx.initfile)
		if err != nil {
			return err
		}
		defer fini.Close()
		_, err = fini.WriteString(time.Now().Format(time.RFC3339) + "\n")
		if err != nil {
			return err
		}
		err = fini.Sync()
		if err != nil {
			return err
		}
		return fini.Close()
	}
	err = ctx.checkUpdates()
	return err
}

func (ctx *Context) writeYumConf(w io.Writer) error {
	var err error
	const tmpl = `
[main]
#CONFVERSION 0001
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
obsoletes=1
plugins=1
gpgcheck=0
installroot=%s
reposdir=/etc/yum.repos.d
`
	_, err = fmt.Fprintf(w, tmpl, ctx.siteroot)
	return err
}

func (ctx *Context) writeYumRepo(w io.Writer, data map[string]string) error {
	var err error
	const tmpl = `
[%s]
#REPOVERSION 0001
name=%s
baseurl=%s
enabled=1
`
	_, err = fmt.Fprintf(w, tmpl,
		data["name"],
		data["name"],
		data["url"],
	)
	return err
}

// checkUpdates checks whether packages could be updated in the repository
func (ctx *Context) checkUpdates() error {
	var err error
	return err
}

// install performs the whole download/install procedure (eq. yum install)
func (ctx *Context) install(project, version, cmtconfig string) error {
	var err error
	ctx.msg.Infof("Installing %s/%s/%s\n", project, version, cmtconfig)
	return err
}

// InstallRPM installs a RPM by name
func (ctx *Context) InstallRPM(name, version, release string, forceInstall, update bool) error {
	var err error
	pkg, err := ctx.yum.FindLatestMatchingName(name, version, release)
	if err != nil {
		return err
	}
	err = ctx.InstallPackage(pkg, forceInstall, update)
	return err
}

// InstallPackage installs a specific RPM, checking if not already installed
func (ctx *Context) InstallPackage(pkg string, forceInstall, update bool) error {
	var err error
	return err
}

// ListPackages lists all packages satisfying pattern (a regexp)
func (ctx *Context) ListPackages(name, version, release string) error {
	var err error
	total := 0
	pkgs, err := ctx.yum.ListPackages(name, version, release)
	if err != nil {
		return err
	}

	for _, pkg := range pkgs {
		fmt.Printf("%s\n", pkg.RpmName())
		total += 1
	}
	ctx.msg.Infof("Total matching: %d\n", total)
	return err
}

// EOF
<commit_msg>context: fixup InstallPackage API<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
	"github.com/lhcb-org/pkr/yum"
)

type External struct {
	cmd string
	err error
}
type FixFct func(*Context) error

type Context struct {
	msg       *logger.Logger
	cfg       Config
	siteroot  string // where to install software, binaries, ...
	repourl   string
	rpmprefix string
	dbpath    string
	etcdir    string
	yumconf   string
	yumreposd string
	yum       *yum.Client
	tmpdir    string
	bindir    string
	libdir    string
	initfile  string

	extstatus map[string]External
	reqext    []string
	extfix    map[string]FixFct
}

func New(cfg Config, dbg bool) (*Context, error) {
	var err error
	siteroot := cfg.Siteroot()
	if siteroot == "" {
		siteroot = "/opt/cern-sw"
	}

	ctx := Context{
		cfg:       cfg,
		msg:       logger.NewLogger("pkr", logger.INFO, os.Stdout),
		siteroot:  siteroot,
		repourl:   cfg.RepoUrl(),
		rpmprefix: cfg.Prefix(),
		dbpath:    filepath.Join(siteroot, "var", "lib", "rpm"),
		etcdir:    filepath.Join(siteroot, "etc"),
		yumconf:   filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposd: filepath.Join(siteroot, "etc", "yum.repos.d"),
		tmpdir:    filepath.Join(siteroot, "tmp"),
		bindir:    filepath.Join(siteroot, "usr", "bin"),
		libdir:    filepath.Join(siteroot, "lib"),
		initfile:  filepath.Join(siteroot, "etc", "repoinit"),
	}
	if dbg {
		ctx.msg.SetLevel(logger.DEBUG)
	}
	for _, dir := range []string{
		ctx.tmpdir,
		ctx.bindir,
		ctx.libdir,
	} {
		err = os.MkdirAll(dir, 0644)
		if err != nil {
			ctx.msg.Errorf("could not create directory %q: %v\n", dir, err)
			return nil, err
		}
	}
	os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+ctx.bindir)

	// make sure the db is initialized
	err = ctx.initRpmDb()
	if err != nil {
		return nil, err
	}

	// yum
	err = ctx.initYum()
	if err != nil {
		return nil, err
	}

	ctx.yum, err = yum.New(ctx.siteroot)
	if err != nil {
		return nil, err
	}

	// defining structures and checking if all needed tools are available
	ctx.extstatus = make(map[string]External)
	ctx.reqext = []string{"rpm"}
	ctx.extfix = make(map[string]FixFct)
	err = ctx.checkPreRequisites()
	if err != nil {
		return nil, err
	}

	err = ctx.checkRepository()
	if err != nil {
		return nil, err
	}

	return &ctx, err
}

func (ctx *Context) Exit(rc int) {
	os.Exit(rc)
}

// initRpmDb initializes the RPM database
func (ctx *Context) initRpmDb() error {
	var err error
	msg := ctx.msg
	msg.Infof("RPM DB in %q\n", ctx.dbpath)
	err = os.MkdirAll(ctx.dbpath, 0644)
	if err != nil {
		msg.Errorf(
			"could not create directory %q for RPM DB: %v\n",
			ctx.dbpath,
			err,
		)
		return err
	}

	pkgdir := filepath.Join(ctx.dbpath, "Packages")
	if !path_exists(pkgdir) {
		msg.Infof("Initializing RPM db\n")
		cmd := exec.Command(
			"rpm",
			"--dbpath", ctx.dbpath,
			"--initdb",
		)
		out, err := cmd.CombinedOutput()
		msg.Debugf(string(out))
		if err != nil {
			return fmt.Errorf("error initializing RPM DB: %v", err)
		}
	}
	return err
}

func (ctx *Context) initYum() error {
	var err error
	err = os.MkdirAll(ctx.etcdir, 0644)
	if err != nil {
		return fmt.Errorf("could not create dir %q: %v", ctx.etcdir, err)
	}

	if !path_exists(ctx.yumconf) {
		yum, err := os.Create(ctx.yumconf)
		if err != nil {
			return err
		}
		defer yum.Close()
		err = ctx.writeYumConf(yum)
		if err != nil {
			return err
		}
		err = yum.Sync()
		if err != nil {
			return err
		}
		err = yum.Close()
		if err != nil {
			return err
		}
	}
	err = ctx.cfg.InitYum(ctx)
	return err
}

// checkPreRequisites makes sure that all external tools required by
// this tool to perform the installation are present
func (ctx *Context) checkPreRequisites() error {
	var err error
	extmissing := false
	missing := make([]string, 0)

	for _, ext := range ctx.reqext {
		cmd, err := exec.LookPath(ext)
		ctx.extstatus[ext] = External{
			cmd: cmd,
			err: err,
		}
	}

	for k, ext := range ctx.extstatus {
		if ext.err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, ext.cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing - trying compensatory measure\n", k)
		fix, ok := ctx.extfix[k]
		if !ok {
			extmissing = true
			missing = append(missing, k)
			continue
		}

		err = fix(ctx)
		if err != nil {
			return err
		}

		cmd, err := exec.LookPath(k)
		ctx.extstatus[k] = External{
			cmd: cmd,
			err: err,
		}
		if err == nil {
			ctx.msg.Infof("%s: Found %q\n", k, cmd)
			continue
		}
		ctx.msg.Infof("%s: Missing\n", k)
		extmissing = true
		missing = append(missing, k)
	}

	if extmissing {
		err = fmt.Errorf("missing external(s): %v", missing)
	}
	return err
}

func (ctx *Context) checkRepository() error {
	var err error
	if !path_exists(ctx.initfile) {
		fini, err := os.Create(ctx.initfile)
		if err != nil {
			return err
		}
		defer fini.Close()
		_, err = fini.WriteString(time.Now().Format(time.RFC3339) + "\n")
		if err != nil {
			return err
		}
		err = fini.Sync()
		if err != nil {
			return err
		}
		return fini.Close()
	}
	err = ctx.checkUpdates()
	return err
}

func (ctx *Context) writeYumConf(w io.Writer) error {
	var err error
	const tmpl = `
[main]
#CONFVERSION 0001
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
pkgpolicy=newest
distroverpkg=redhat-release
tolerant=1
exactarch=1
obsoletes=1
plugins=1
gpgcheck=0
installroot=%s
reposdir=/etc/yum.repos.d
`
	_, err = fmt.Fprintf(w, tmpl, ctx.siteroot)
	return err
}

func (ctx *Context) writeYumRepo(w io.Writer, data map[string]string) error {
	var err error
	const tmpl = `
[%s]
#REPOVERSION 0001
name=%s
baseurl=%s
enabled=1
`
	_, err = fmt.Fprintf(w, tmpl,
		data["name"],
		data["name"],
		data["url"],
	)
	return err
}

// checkUpdates checks whether packages could be updated in the repository
func (ctx *Context) checkUpdates() error {
	var err error
	return err
}

// install performs the whole download/install procedure (eq. yum install)
func (ctx *Context) install(project, version, cmtconfig string) error {
	var err error
	ctx.msg.Infof("Installing %s/%s/%s\n", project, version, cmtconfig)
	return err
}

// InstallRPM installs a RPM by name
func (ctx *Context) InstallRPM(name, version, release string, forceInstall, update bool) error {
	var err error
	pkg, err := ctx.yum.FindLatestMatchingName(name, version, release)
	if err != nil {
		return err
	}
	err = ctx.InstallPackage(pkg, forceInstall, update)
	return err
}

// InstallPackage installs a specific RPM, checking if not already installed
func (ctx *Context) InstallPackage(pkg *yum.Package, forceInstall, update bool) error {
	var err error
	return err
}

// ListPackages lists all packages satisfying pattern (a regexp)
func (ctx *Context) ListPackages(name, version, release string) error {
	var err error
	total := 0
	pkgs, err := ctx.yum.ListPackages(name, version, release)
	if err != nil {
		return err
	}

	for _, pkg := range pkgs {
		fmt.Printf("%s\n", pkg.RpmName())
		total += 1
	}
	ctx.msg.Infof("Total matching: %d\n", total)
	return err
}

// EOF
<|endoftext|>


<commit_before>package proto

import (
	pb "code.google.com/p/goprotobuf/proto"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type FileList struct {
	RootDirectory string
	Files         []*File
}

func (fl *FileList) VisitDir(path string, f os.FileInfo) bool {
	return true
}

func (fl *FileList) VisitFile(path string, f os.FileInfo) {
	file := fl.buildFile(path)
	fl.Files = append(fl.Files, file)
}

func CollectFiles(dir string) []*File {
	fileList := &FileList{
		RootDirectory: dir,
		Files:         make([]*File, 0),
	}

	markFn := func(path string, info os.FileInfo, err error) error {

		if info == nil {
      return filepath.SkipDir
		}

		if info.IsDir() {
		    return filepath.SkipDir
		}
		if err != nil {
		    return err
		}
		file := fileList.buildFile(path)
		fileList.Files = append(fileList.Files, file)
		return nil
	}

	filepath.Walk(dir, markFn)

	return fileList.Files
}

func (fl *FileList) Unpack(verbose bool) (err error) {
	for _, file := range fl.Files {
		absolutePath := file.AbsolutePath(fl.RootDirectory)
		path, _ := filepath.Split(absolutePath)
		err = os.MkdirAll(path, 0755)

		if err != nil {
			panic(err.Error())
		}

		err = file.Write(fl.RootDirectory)

		if err != nil {
			return
		}

		if verbose {
			fmt.Printf("* %v\n", absolutePath)
		}
	}
	return
}

func (fl *FileList) buildFile(path string) *File {

	data, err := ioutil.ReadFile(path)

	if err != nil {
		panic("Could not read file (" + path + "):" + err.Error())
	}

	pdata := make([][]uint8, 0)
	pdata = append(pdata, data)

	path = relativePath(fl.RootDirectory, path)

	file := &File{
		Path: pb.String(path),
		Data: pdata,
	}

	return file
}

func (f *File) AbsolutePath(dir string) string {
	return filepath.Join(dir, pb.GetString(f.Path))
}

func (f *File) Write(dir string) (err error) {
	absolutePath := f.AbsolutePath(dir)

	err = ioutil.WriteFile(absolutePath, f.Data[0], 0644)

	if err != nil {
		err = errors.New("Couldn't write file (" + absolutePath + "):" + err.Error())
	}

	return
}

func relativePath(parentDirectory string, path string) string {
	cleanedParentPath := filepath.Clean(parentDirectory)
	relativePath := strings.Replace(path, cleanedParentPath, "", 1)
	return relativePath
}
<commit_msg>One more fix. We don't want to skip directories<commit_after>package proto

import (
	pb "code.google.com/p/goprotobuf/proto"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type FileList struct {
	RootDirectory string
	Files         []*File
}

func (fl *FileList) VisitDir(path string, f os.FileInfo) bool {
	return true
}

func (fl *FileList) VisitFile(path string, f os.FileInfo) {
	file := fl.buildFile(path)
	fl.Files = append(fl.Files, file)
}

func CollectFiles(dir string) []*File {
	fileList := &FileList{
		RootDirectory: dir,
		Files:         make([]*File, 0),
	}

	markFn := func(path string, info os.FileInfo, err error) error {

		if info == nil {
			return nil
		}

		if info.IsDir() {
				return nil
		}

		if err != nil {
		    return err
		}
		file := fileList.buildFile(path)
		fileList.Files = append(fileList.Files, file)

		return nil
	}

	filepath.Walk(dir, markFn)

	return fileList.Files
}

func (fl *FileList) Unpack(verbose bool) (err error) {
	for _, file := range fl.Files {
		absolutePath := file.AbsolutePath(fl.RootDirectory)
		path, _ := filepath.Split(absolutePath)
		err = os.MkdirAll(path, 0755)

		if err != nil {
			panic(err.Error())
		}

		err = file.Write(fl.RootDirectory)

		if err != nil {
			return
		}

		if verbose {
			fmt.Printf("* %v\n", absolutePath)
		}
	}
	return
}

func (fl *FileList) buildFile(path string) *File {

	data, err := ioutil.ReadFile(path)

	if err != nil {
		panic("Could not read file (" + path + "):" + err.Error())
	}

	pdata := make([][]uint8, 0)
	pdata = append(pdata, data)

	path = relativePath(fl.RootDirectory, path)

	file := &File{
		Path: pb.String(path),
		Data: pdata,
	}

	return file
}

func (f *File) AbsolutePath(dir string) string {
	return filepath.Join(dir, pb.GetString(f.Path))
}

func (f *File) Write(dir string) (err error) {
	absolutePath := f.AbsolutePath(dir)

	err = ioutil.WriteFile(absolutePath, f.Data[0], 0644)

	if err != nil {
		err = errors.New("Couldn't write file (" + absolutePath + "):" + err.Error())
	}

	return
}

func relativePath(parentDirectory string, path string) string {
	cleanedParentPath := filepath.Clean(parentDirectory)
	relativePath := strings.Replace(path, cleanedParentPath, "", 1)
	return relativePath
}
<|endoftext|>


<commit_before>package hamt64_test

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/lleo/go-hamt-functional/hamt64"
	"github.com/lleo/go-hamt/key"
	"github.com/lleo/go-hamt/stringkey"
	"github.com/lleo/stringutil"
	"github.com/pkg/errors"
)

//var numHugeKvs int = 1024
var numHugeKvs int = 5 * 1024 * 1024
var hugeKvs []key.KeyVal

var LookupHamt64 hamt64.Hamt
var DeleteHamt64 hamt64.Hamt

var StartTime = make(map[string]time.Time)
var RunTime = make(map[string]time.Duration)

var Inc = stringutil.Lower.Inc

var TableOption int

func TestMain(m *testing.M) {
	// flags
	var fullonly, componly, hybrid, all bool
	flag.BoolVar(&fullonly, "F", false, "Use full tables only and exclude C and H TableOption.")
	flag.BoolVar(&componly, "C", false, "Use compressed tables only and exclude F and H TableOption.")
	flag.BoolVar(&hybrid, "H", false, "Use compressed tables initially and exclude F and C TableOption.")
	flag.BoolVar(&all, "A", false, "Run all Tests w/ TableOption set to hamt64.FullTablesOnly, hamt64.CompTablesOnly, and hamt64.HybridTables; in that order.")

	flag.Parse()

	// If all flag not set, only one of -F, -C, or -H can be set.
	if !all {
		// only one flag may be set between fullonly, componly, and hybrid
		if (fullonly && (componly || hybrid)) ||
			(componly && (fullonly || hybrid)) ||
			(hybrid && (componly || fullonly)) {
			flag.PrintDefaults()
			os.Exit(1)
		}
	}

	// If no flags given, run all tests.
	if !(all || fullonly || componly || hybrid) {
		all = true
	}

	// log
	log.SetFlags(log.Lshortfile)

	var logfile, err = os.Create("test.log")
	if err != nil {
		log.Fatal(errors.Wrap(err, "failed to os.Create(\"test.log\")"))
	}
	defer logfile.Close()

	log.SetOutput(logfile)

	log.Println("TestMain: and so it begins...")

	hugeKvs = buildKeyVals(numHugeKvs)

	// execute
	var xit int
	//var tableOption int
	if all {
		//Full Tables Only
		hamt64.GradeTables = false
		hamt64.FullTableInit = true
		log.Println("TestMain: Full Tables Only")
		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
		if xit != 0 {
			os.Exit(1)
		}

		//Compressed Tables Only
		hamt64.GradeTables = false
		hamt64.FullTableInit = false
		log.Println("TestMain: Compressed Tables Only")
		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
		if xit != 0 {
			os.Exit(1)
		}

		//Hybrid Tables
		hamt64.GradeTables = true
		hamt64.FullTableInit = false
		log.Println("TestMain: Hybrid Tables")
		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
	} else {
		if hybrid {
			hamt64.GradeTables = true
			hamt64.FullTableInit = false
			log.Println("TestMain: Hybrid Tables")
		} else if fullonly {
			hamt64.GradeTables = false
			hamt64.FullTableInit = true
			log.Println("TestMain: Full Tables Only")
		} else /* if componly */ {
			hamt64.GradeTables = false
			hamt64.FullTableInit = false
			log.Println("TestMain: Compressed Tables Only")
		}

		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
	}

	log.Println("\n", RunTimes())
	log.Println("TestMain: the end.")
	os.Exit(xit)
}

func RunTimes() string {
	var s = ""

	s += "Key                                                               Val\n"
	s += "=================================================================+==========\n"

	for key, val := range RunTime {
		s += fmt.Sprintf("%-65s %s\n", key, val)
	}
	return s
}

func initialize() {
	var funcName = "hamt64: initialize()"

	var metricName = funcName + ": build Lookup/Delete Hamt64"
	log.Println(metricName, "called.")
	log.Printf("initialize: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
	StartTime[metricName] = time.Now()

	LookupHamt64 = hamt64.Hamt{}
	DeleteHamt64 = hamt64.Hamt{}

	for _, kv := range genRandomizedKvs(hugeKvs) {
		var inserted bool
		LookupHamt64, inserted = LookupHamt64.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to LookupHamt64.Put(%s, %v)", kv.Key, kv.Val)
		}

		DeleteHamt64, inserted = DeleteHamt64.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to DeleteHamt64.Put(%s, %v)", kv.Key, kv.Val)
		}
	}

	RunTime[metricName] = time.Since(StartTime[metricName])
}

func buildKeyVals(num int) []key.KeyVal {
	var kvs = make([]key.KeyVal, num, num)

	s := "aaa"
	for i := 0; i < num; i++ {
		kvs[i].Key = stringkey.New(s)
		kvs[i].Val = i

		s = Inc(s)
	}

	return kvs
}

func genRandomizedKvs(kvs []key.KeyVal) []key.KeyVal {
	randKvs := make([]key.KeyVal, len(kvs))
	copy(randKvs, kvs)

	//From: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
	for i := len(randKvs) - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		randKvs[i], randKvs[j] = randKvs[j], randKvs[i]
	}

	return randKvs
}

func rebuildDeleteHamt64(kvs []key.KeyVal) {
	for _, kv := range kvs {
		var inserted bool
		DeleteHamt64, inserted = DeleteHamt64.Put(kv.Key, kv.Val)
		if !inserted {
			//log.Printf("BenchmarkHamt64Del: inserted,%v := DeleteHamt64.Put(%s, %d)", inserted, kv.Key, kv.Val)

			// we delete inorder so we can stop rebuilding when the entries start existing
			break
		}
	}
}
<commit_msg>lowered the numHugeKvs to 2 * 1024 * 1024<commit_after>package hamt64_test

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"testing"
	"time"

	"github.com/lleo/go-hamt-functional/hamt64"
	"github.com/lleo/go-hamt/key"
	"github.com/lleo/go-hamt/stringkey"
	"github.com/lleo/stringutil"
	"github.com/pkg/errors"
)

//var numHugeKvs int = 1024
var numHugeKvs int = 2 * 1024 * 1024
var hugeKvs []key.KeyVal

var LookupHamt64 hamt64.Hamt
var DeleteHamt64 hamt64.Hamt

var StartTime = make(map[string]time.Time)
var RunTime = make(map[string]time.Duration)

var Inc = stringutil.Lower.Inc

var TableOption int

func TestMain(m *testing.M) {
	// flags
	var fullonly, componly, hybrid, all bool
	flag.BoolVar(&fullonly, "F", false, "Use full tables only and exclude C and H TableOption.")
	flag.BoolVar(&componly, "C", false, "Use compressed tables only and exclude F and H TableOption.")
	flag.BoolVar(&hybrid, "H", false, "Use compressed tables initially and exclude F and C TableOption.")
	flag.BoolVar(&all, "A", false, "Run all Tests w/ TableOption set to hamt64.FullTablesOnly, hamt64.CompTablesOnly, and hamt64.HybridTables; in that order.")

	flag.Parse()

	// If all flag not set, only one of -F, -C, or -H can be set.
	if !all {
		// only one flag may be set between fullonly, componly, and hybrid
		if (fullonly && (componly || hybrid)) ||
			(componly && (fullonly || hybrid)) ||
			(hybrid && (componly || fullonly)) {
			flag.PrintDefaults()
			os.Exit(1)
		}
	}

	// If no flags given, run all tests.
	if !(all || fullonly || componly || hybrid) {
		all = true
	}

	// log
	log.SetFlags(log.Lshortfile)

	var logfile, err = os.Create("test.log")
	if err != nil {
		log.Fatal(errors.Wrap(err, "failed to os.Create(\"test.log\")"))
	}
	defer logfile.Close()

	log.SetOutput(logfile)

	log.Println("TestMain: and so it begins...")

	hugeKvs = buildKeyVals(numHugeKvs)

	// execute
	var xit int
	//var tableOption int
	if all {
		//Full Tables Only
		hamt64.GradeTables = false
		hamt64.FullTableInit = true
		log.Println("TestMain: Full Tables Only")
		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
		if xit != 0 {
			os.Exit(1)
		}

		//Compressed Tables Only
		hamt64.GradeTables = false
		hamt64.FullTableInit = false
		log.Println("TestMain: Compressed Tables Only")
		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
		if xit != 0 {
			os.Exit(1)
		}

		//Hybrid Tables
		hamt64.GradeTables = true
		hamt64.FullTableInit = false
		log.Println("TestMain: Hybrid Tables")
		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
	} else {
		if hybrid {
			hamt64.GradeTables = true
			hamt64.FullTableInit = false
			log.Println("TestMain: Hybrid Tables")
		} else if fullonly {
			hamt64.GradeTables = false
			hamt64.FullTableInit = true
			log.Println("TestMain: Full Tables Only")
		} else /* if componly */ {
			hamt64.GradeTables = false
			hamt64.FullTableInit = false
			log.Println("TestMain: Compressed Tables Only")
		}

		log.Printf("TestMain: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
		initialize()
		xit = m.Run()
	}

	log.Println("\n", RunTimes())
	log.Println("TestMain: the end.")
	os.Exit(xit)
}

func RunTimes() string {
	var s = ""

	s += "Key                                                               Val\n"
	s += "=================================================================+==========\n"

	for key, val := range RunTime {
		s += fmt.Sprintf("%-65s %s\n", key, val)
	}
	return s
}

func initialize() {
	var funcName = "hamt64: initialize()"

	var metricName = funcName + ": build Lookup/Delete Hamt64"
	log.Println(metricName, "called.")
	log.Printf("initialize: GradeTables=%t; FullTableInit=%t\n", hamt64.GradeTables, hamt64.FullTableInit)
	StartTime[metricName] = time.Now()

	LookupHamt64 = hamt64.Hamt{}
	DeleteHamt64 = hamt64.Hamt{}

	for _, kv := range genRandomizedKvs(hugeKvs) {
		var inserted bool
		LookupHamt64, inserted = LookupHamt64.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to LookupHamt64.Put(%s, %v)", kv.Key, kv.Val)
		}

		DeleteHamt64, inserted = DeleteHamt64.Put(kv.Key, kv.Val)
		if !inserted {
			log.Fatalf("failed to DeleteHamt64.Put(%s, %v)", kv.Key, kv.Val)
		}
	}

	RunTime[metricName] = time.Since(StartTime[metricName])
}

func buildKeyVals(num int) []key.KeyVal {
	var kvs = make([]key.KeyVal, num, num)

	s := "aaa"
	for i := 0; i < num; i++ {
		kvs[i].Key = stringkey.New(s)
		kvs[i].Val = i

		s = Inc(s)
	}

	return kvs
}

func genRandomizedKvs(kvs []key.KeyVal) []key.KeyVal {
	randKvs := make([]key.KeyVal, len(kvs))
	copy(randKvs, kvs)

	//From: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
	for i := len(randKvs) - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		randKvs[i], randKvs[j] = randKvs[j], randKvs[i]
	}

	return randKvs
}

func rebuildDeleteHamt64(kvs []key.KeyVal) {
	for _, kv := range kvs {
		var inserted bool
		DeleteHamt64, inserted = DeleteHamt64.Put(kv.Key, kv.Val)
		if !inserted {
			//log.Printf("BenchmarkHamt64Del: inserted,%v := DeleteHamt64.Put(%s, %d)", inserted, kv.Key, kv.Val)

			// we delete inorder so we can stop rebuilding when the entries start existing
			break
		}
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/nelsonleduc/calmanbot/cache"
	"github.com/nelsonleduc/calmanbot/handlers/models"
)

type builtinDescription struct {
	trigger     string
	description string
}

type builtin struct {
	builtinDescription
	handler func([]string, models.Bot, cache.QueryCache) string
}

// Descriptions
var helpDescription = builtinDescription{
	"(help)",
	"See this",
}
var topDescription = builtinDescription{
	"(top)",
	"List top 10 liked posts",
}
var showDescription = builtinDescription{
	"show ([1-10])",
	"Repost nth top post",
}

var descriptions = []builtinDescription{
	helpDescription,
	topDescription,
	showDescription,
}
var builtins = []builtin{
	builtin{
		helpDescription,
		responseForHelp,
	},
	builtin{
		topDescription,
		responseForLeaderboard,
	},
	builtin{
		showDescription,
		responseForShow,
	},
}

// Handlers
func responseForLeaderboard(matched []string, bot models.Bot, cache cache.QueryCache) string {
	entries := cache.LeaderboardEntries(bot.GroupID, 10)
	leaderboardAccumulatr := "Top posts:"
	for _, e := range entries {
		leaderboardAccumulatr += "\n" + strconv.Itoa(e.LikeCount) + "    " + e.Query
	}

	return leaderboardAccumulatr
}

func responseForShow(matched []string, bot models.Bot, cache cache.QueryCache) string {
	entries := cache.LeaderboardEntries(bot.GroupID, 10)
	num, error := strconv.Atoi(matched[1])
	num--
	if len(entries) <= num || error != nil {
		return "There is nothing to display"
	}

	return entries[num].Result
}

func responseForHelp(matched []string, bot models.Bot, cache cache.QueryCache) string {
	actions, _ := models.FetchActions(true)
	sort.Sort(models.ByPriority(actions))
	botName := bot.SanitizedBotNames()[0]

	helpAccumulator := "Commands:"
	longest := 0
	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}

		if len(*a.Pattern) > longest {
			longest = len(*a.Pattern)
		}
	}
	for _, b := range descriptions {
		length := len("&" + botName + " " + b.trigger)
		if length > longest {
			longest = length
		}
	}
	paddingFmt := fmt.Sprintf("%%-%ds", longest+2)

	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}
		printablePattern := *a.Pattern
		printablePattern = strings.Replace(printablePattern, "{_botname_}", botName, -1)
		re := regexp.MustCompile("^\\[(.)\\]")
		matched := re.FindStringSubmatch(printablePattern)
		thing := ""
		if len(matched) > 1 && matched[1] != "" {
			thing = matched[1]
		}
		printablePattern = re.ReplaceAllLiteralString(printablePattern, thing)

		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, "\""+printablePattern+"\"") + "\n\t" + *a.Description
	}
	for _, b := range descriptions {
		printablePattern := "&" + botName + " " + b.trigger
		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, "\""+printablePattern+"\"") + "\n\t" + b.description
	}

	return helpAccumulator
}
<commit_msg>Fix show regex<commit_after>package handlers

import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/nelsonleduc/calmanbot/cache"
	"github.com/nelsonleduc/calmanbot/handlers/models"
)

type builtinDescription struct {
	trigger     string
	description string
}

type builtin struct {
	builtinDescription
	handler func([]string, models.Bot, cache.QueryCache) string
}

// Descriptions
var helpDescription = builtinDescription{
	"(help)",
	"See this",
}
var topDescription = builtinDescription{
	"(top)",
	"List top 10 liked posts",
}
var showDescription = builtinDescription{
	"show (10|[1-9])(?:$| )",
	"Repost nth top post",
}

var descriptions = []builtinDescription{
	helpDescription,
	topDescription,
	showDescription,
}
var builtins = []builtin{
	builtin{
		helpDescription,
		responseForHelp,
	},
	builtin{
		topDescription,
		responseForLeaderboard,
	},
	builtin{
		showDescription,
		responseForShow,
	},
}

// Handlers
func responseForLeaderboard(matched []string, bot models.Bot, cache cache.QueryCache) string {
	entries := cache.LeaderboardEntries(bot.GroupID, 10)
	leaderboardAccumulatr := "Top posts:"
	for _, e := range entries {
		leaderboardAccumulatr += "\n" + strconv.Itoa(e.LikeCount) + "    " + e.Query
	}

	return leaderboardAccumulatr
}

func responseForShow(matched []string, bot models.Bot, cache cache.QueryCache) string {
	entries := cache.LeaderboardEntries(bot.GroupID, 10)
	num, error := strconv.Atoi(matched[1])
	num--
	if len(entries) <= num || error != nil {
		return "There is nothing to display"
	}

	return entries[num].Result
}

func responseForHelp(matched []string, bot models.Bot, cache cache.QueryCache) string {
	actions, _ := models.FetchActions(true)
	sort.Sort(models.ByPriority(actions))
	botName := bot.SanitizedBotNames()[0]

	helpAccumulator := "Commands:"
	longest := 0
	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}

		if len(*a.Pattern) > longest {
			longest = len(*a.Pattern)
		}
	}
	for _, b := range descriptions {
		length := len("&" + botName + " " + b.trigger)
		if length > longest {
			longest = length
		}
	}
	paddingFmt := fmt.Sprintf("%%-%ds", longest+2)

	for _, a := range actions {
		if a.Description == nil || a.Pattern == nil {
			continue
		}
		printablePattern := *a.Pattern
		printablePattern = strings.Replace(printablePattern, "{_botname_}", botName, -1)
		re := regexp.MustCompile("^\\[(.)\\]")
		matched := re.FindStringSubmatch(printablePattern)
		thing := ""
		if len(matched) > 1 && matched[1] != "" {
			thing = matched[1]
		}
		printablePattern = re.ReplaceAllLiteralString(printablePattern, thing)

		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, "\""+printablePattern+"\"") + "\n\t" + *a.Description
	}
	for _, b := range descriptions {
		printablePattern := "&" + botName + " " + b.trigger
		helpAccumulator += "\n" + fmt.Sprintf(paddingFmt, "\""+printablePattern+"\"") + "\n\t" + b.description
	}

	return helpAccumulator
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	sm "github.com/ifn/go-statemachine"
)

type DeskMsg struct {
	Desk []string `json:"desk"`
}

type PlayerMsg struct {
	Cmd  sm.EventType `json:"command"`
	Card string       `json:"card"`
}

//

var Suits []string = []string{
	"S",
	"C",
	"H",
	"D",
}

var CardValues map[string]int = map[string]int{
	"6": 6, "7": 7, "8": 8, "9": 9, "10": 10,
	"J": 11,
	"Q": 12,
	"K": 13,
	"A": 14,
}

func higher(c0, c1, t string) int {
	// c0 and c1 have the same suit
	if c0[0] == c1[0] {
		if CardValues[c0[1:]] > CardValues[c1[1:]] {
			return 1
		}
		if CardValues[c0[1:]] < CardValues[c1[1:]] {
			return -1
		}
		return 0
	}
	// c0 is trump, c1 is not
	if c0[:1] == t {
		return 1
	}
	// c1 is trump, c0 is not
	if c1[:1] == t {
		return -1
	}
	// suits are different, both are not trump
	return -2
}

var CardRE *regexp.Regexp = regexp.MustCompile(`[SCHD]([6-9JQKA]|10)`)

func isValid(c string) bool {
	return CardRE.MatchString(c)
}

//

const (
	cmdStart sm.EventType = iota
	cmdMove

	cmdCount
)

const (
	stateCollection sm.State = iota

	stateAttack
	stateDefense

	stateCount
)

type roundResult int

const (
	None roundResult = iota
	Beat
	NotBeat
)

func stateToString(s sm.State) string {
	return [...]string{
		stateCollection: "COLLECTION",
		stateDefense:    "DEFENSE",
		stateAttack:     "ATTACK",
	}[s]
}

func cmdToString(t sm.EventType) string {
	return [...]string{
		cmdStart: "START",
		cmdMove:  "MOVE",
	}[t]
}

//

type cmdArgs struct {
	conn *playerConn
	card string
}

//

func logOutOfTurn(pconn *playerConn) {
	log.Printf("out of turn: %v", pconn.conn.RemoteAddr())
}

func logWontBeat(c1, c2, t string) {
	log.Printf("%v won't bit %v, trump is %v", c1, c2, t)
}

func logWrongNumber(n int) {
	log.Printf("wrong number of players: %v", n)
}

func logStrangeCard(c string, pc *playerConn) {
	log.Printf("%v doesn't own %v", pc, c)
}

//

type gameState struct {
	// 1. fields that don't change during a game

	sm  *sm.StateMachine
	hub *hub

	// trump suit
	trump string

	// 2. fields that don't change during a round

	deck []string

	// attacker that started a round
	aconnStart *playerConn
	// defender
	dconn *playerConn

	// 3. fields that change during a round

	desk []string

	// attacker
	aconn *playerConn
	// card that should be beaten
	cardToBeat string
}

func (self *gameState) markInactive() (numActive int) {
	if len(self.deck) == 0 {
		for pc := range self.hub.conns.Enumerate() {
			if pc := pc.(*playerConn); len(pc.cards) == 0 {
				pc.active = false
				continue
			}
			numActive++
		}
	}
	return
}

func (self *gameState) activeFrom(c *playerConn) *playerConn {
	for pc := range self.hub.conns.(*mapRing).EnumerateFrom(c) {
		if pc := pc.(*playerConn); pc.active {
			return pc
		}
	}
	return nil
}

func (self *gameState) nextPlayer(c *playerConn) *playerConn {
	return self.hub.conns.(*mapRing).Next(c).(*playerConn)
}

func (self *gameState) activeAfter(c *playerConn) *playerConn {
	return self.activeFrom(self.nextPlayer(c))
}

//

func (self *gameState) initDeck() {
	numCards := len(Suits) * len(CardValues)

	deck := make([]string, 0, numCards)
	for _, suit := range Suits {
		for cv := range CardValues {
			deck = append(deck, suit+cv)
		}
	}

	order := rand.Perm(numCards)
	for _, pos := range order {
		self.deck = append(self.deck, deck[pos])
	}
}

func (self *gameState) popCard() (card string) {
	if deck := self.deck; len(deck) > 0 {
		card = deck[0]
		self.deck = deck[1:]
	}
	return
}

func (self *gameState) dealCards() (card string) {
	for i := 0; i < 6; i++ {
		for pc := range self.hub.conns.Enumerate() {
			card = pc.(*playerConn).fromDeck()
		}
	}
	return
}

func (self *gameState) takeCards() {
	conns := self.hub.conns.(*mapRing)

	takeCards := func(pc *playerConn) {
		for len(self.deck) > 0 && len(pc.cards) < 6 {
			pc.fromDeck()
		}
	}

	for pc := range conns.EnumerateFrom(self.aconnStart) {
		if pc := pc.(*playerConn); pc != self.dconn {
			takeCards(pc)
		}
	}
	takeCards(self.dconn)
}

//

//TODO: don't like it
func (self *gameState) setTrump(card string) {
	tcard := self.popCard()
	if tcard == "" {
		tcard = card
	} else {
		self.deck = append(self.deck, tcard)
	}
	self.trump = tcard[:1]
}

func (self *gameState) chooseStarting() *playerConn {
	conns := self.hub.conns.(*mapRing)

	return conns.Nth(rand.Intn(conns.Len())).(*playerConn)
}

//

func (self *gameState) setRoles(res roundResult) {
	switch res {
	case None:
		self.aconn = self.chooseStarting()
	case Beat:
		self.aconn = self.activeFrom(self.dconn)
	case NotBeat:
		self.aconn = self.activeAfter(self.dconn)
	}
	self.dconn = self.activeAfter(self.aconn)
	self.aconnStart = self.aconn
}

func (self *gameState) newRound(res roundResult) {
	switch res {
	case None:
		self.initDeck()
		card := self.dealCards()
		self.setTrump(card)
	case NotBeat:
		self.dconn.fromDesk()
		self.takeCards()
	case Beat:
		self.desk = self.desk[:0]
		self.takeCards()
	}

	numActive := self.markInactive()
	if numActive < 2 {
		//game over
	}

	self.setRoles(res)
	self.cardToBeat = ""
}

// event handlers
// event handlers are actually transition functions.
// in case error event handler should neither change the gameState,
// nor return the state value different from passed to it as an argument.

func (self *gameState) handleStartInCollection(s sm.State, e *sm.Event) sm.State {
	if n := self.hub.conns.(*mapRing).Len(); n < 2 || n > 6 {
		logWrongNumber(n)
		return s
	}

	self.newRound(None)
	return stateAttack
}

func (self *gameState) handleMoveInAttack(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.aconn {
		logOutOfTurn(conn)
		return s
	}

	// attacker sent the card
	if card != "" {
		// throw card to desk
		if _, ok := conn.cards[card]; !ok {
			logStrangeCard(card, conn)
			return s
		}
		conn.toDesk(card)

		self.cardToBeat = card
		return stateDefense
	}

	// attacker sent no card

	aconn := self.activeAfter(self.aconn)
	if aconn == self.dconn {
		aconn = self.activeAfter(aconn)
	}

	// check if all attackers have been polled
	if aconn == self.aconnStart {
		self.newRound(Beat)
		return stateAttack
	}

	self.aconn = aconn
	return stateAttack
}

func (self *gameState) handleMoveInDefense(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.dconn {
		logOutOfTurn(conn)
		return s
	}

	// defender takes the cards
	if card == "" {
		self.newRound(NotBeat)
		return stateAttack
	}

	// check that the sent card is capable to beat
	if higher(card, self.cardToBeat, self.trump) != 1 {
		logWontBeat(card, self.cardToBeat, self.trump)
		return s
	}

	// throw card to desk
	if _, ok := conn.cards[card]; !ok {
		logStrangeCard(card, conn)
		return s
	}
	conn.toDesk(card)

	return stateAttack
}

func (self *gameState) showDesk(s sm.State, e *sm.Event) sm.State {
	desk, err := json.Marshal(DeskMsg{self.desk})
	if err != nil {
		log.Println(err)
		return s
	}

	self.hub.bcastChan <- desk

	return s
}

func (self *gameState) log(s sm.State, e *sm.Event) sm.State {
	log.Println(self)
	return s
}

//

func NewGameState() *gameState {
	gst := new(gameState)

	gst.sm = sm.New(stateCollection, uint(stateCount), uint(cmdCount))

	gst.sm.OnChain(cmdStart,
		[]sm.State{stateCollection},
		[]sm.EventHandler{
			gst.handleStartInCollection,
			gst.showDesk,
			gst.log,
		},
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack},
		gst.handleMoveInAttack,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateDefense},
		gst.handleMoveInDefense,
	)

	gst.sm.OnChain(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		[]sm.EventHandler{
			gst.showDesk,
			gst.log,
		},
	)

	gst.deck = make([]string, 0, len(Suits)*len(CardValues)+ /*for trump*/ 1)
	gst.desk = make([]string, 0, 12)

	gst.hub = NewHub()

	return gst
}

//

type playerConn struct {
	gst *gameState

	cards  map[string]struct{}
	active bool

	conn      *websocket.Conn
	hubToConn chan []byte
}

func (self *playerConn) fromDeck() (card string) {
	if card = self.gst.popCard(); card != "" {
		self.cards[card] = struct{}{}
	}
	return
}

func (self *playerConn) toDesk(card string) {
	delete(self.cards, card)

	self.gst.desk = append(self.gst.desk, card)
}

func (self *playerConn) fromDesk() {
	for _, card := range self.gst.desk {
		self.cards[card] = struct{}{}
	}

	self.gst.desk = self.gst.desk[:0]
}

func (self *playerConn) write() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		select {
		case m, ok := <-self.hubToConn:
			if !ok {
				return
			}
			//TODO: text or binary?
			err := self.conn.WriteMessage(websocket.TextMessage, m)
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (self *playerConn) read() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		var m PlayerMsg

		err := self.conn.ReadJSON(&m)
		if err != nil {
			log.Println(err)
			return
		}

		var event *sm.Event
		switch m.Cmd {
		case cmdStart:
			event = &sm.Event{cmdStart, nil}
		case cmdMove:
			event = &sm.Event{cmdMove, cmdArgs{self, m.Card}}
		default:
			log.Printf("unknown command: %v", m.Cmd)
			continue
		}

		err = self.gst.sm.Emit(event)
		if err != nil {
			log.Println(err)
		}
	}
}

//

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

func playerHandler(gst *gameState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}

		p := &playerConn{gst, make(map[string]struct{}), true, conn, make(chan []byte)}

		gst.hub.regChan <- p
		defer func() {
			gst.hub.unregChan <- p
		}()

		go p.write()
		p.read()
	}
}

//

func startDurakSrv() error {
	gst := NewGameState()

	http.HandleFunc("/", playerHandler(gst))

	return http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func main() {
	err := startDurakSrv()
	if err != nil {
		log.Fatal(err)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())

	runtime.GOMAXPROCS(runtime.NumCPU())
}
<commit_msg>added stateGameOver<commit_after>package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	sm "github.com/ifn/go-statemachine"
)

type DeskMsg struct {
	Desk []string `json:"desk"`
}

type PlayerMsg struct {
	Cmd  sm.EventType `json:"command"`
	Card string       `json:"card"`
}

//

var Suits []string = []string{
	"S",
	"C",
	"H",
	"D",
}

var CardValues map[string]int = map[string]int{
	"6": 6, "7": 7, "8": 8, "9": 9, "10": 10,
	"J": 11,
	"Q": 12,
	"K": 13,
	"A": 14,
}

func higher(c0, c1, t string) int {
	// c0 and c1 have the same suit
	if c0[0] == c1[0] {
		if CardValues[c0[1:]] > CardValues[c1[1:]] {
			return 1
		}
		if CardValues[c0[1:]] < CardValues[c1[1:]] {
			return -1
		}
		return 0
	}
	// c0 is trump, c1 is not
	if c0[:1] == t {
		return 1
	}
	// c1 is trump, c0 is not
	if c1[:1] == t {
		return -1
	}
	// suits are different, both are not trump
	return -2
}

var CardRE *regexp.Regexp = regexp.MustCompile(`[SCHD]([6-9JQKA]|10)`)

func isValid(c string) bool {
	return CardRE.MatchString(c)
}

//

const (
	cmdStart sm.EventType = iota
	cmdMove

	cmdCount
)

const (
	stateCollection sm.State = iota
	stateAttack
	stateDefense
	stateGameOver

	stateCount
)

type roundResult int

const (
	None roundResult = iota
	Beat
	NotBeat
)

func stateToString(s sm.State) string {
	return [...]string{
		stateCollection: "COLLECTION",
		stateDefense:    "DEFENSE",
		stateAttack:     "ATTACK",
	}[s]
}

func cmdToString(t sm.EventType) string {
	return [...]string{
		cmdStart: "START",
		cmdMove:  "MOVE",
	}[t]
}

//

type cmdArgs struct {
	conn *playerConn
	card string
}

//

func logOutOfTurn(pconn *playerConn) {
	log.Printf("out of turn: %v", pconn.conn.RemoteAddr())
}

func logWontBeat(c1, c2, t string) {
	log.Printf("%v won't bit %v, trump is %v", c1, c2, t)
}

func logWrongNumber(n int) {
	log.Printf("wrong number of players: %v", n)
}

func logStrangeCard(c string, pc *playerConn) {
	log.Printf("%v doesn't own %v", pc, c)
}

//

type gameState struct {
	// 1. fields that don't change during a game

	sm  *sm.StateMachine
	hub *hub

	// trump suit
	trump string

	// 2. fields that don't change during a round

	deck []string

	// attacker that started a round
	aconnStart *playerConn
	// defender
	dconn *playerConn

	// 3. fields that change during a round

	desk []string

	// attacker
	aconn *playerConn
	// card that should be beaten
	cardToBeat string
}

func (self *gameState) markInactive() (numActive int) {
	if len(self.deck) == 0 {
		for pc := range self.hub.conns.Enumerate() {
			if pc := pc.(*playerConn); len(pc.cards) == 0 {
				pc.active = false
				continue
			}
			numActive++
		}
	}
	return
}

func (self *gameState) activeFrom(c *playerConn) *playerConn {
	for pc := range self.hub.conns.(*mapRing).EnumerateFrom(c) {
		if pc := pc.(*playerConn); pc.active {
			return pc
		}
	}
	return nil
}

func (self *gameState) nextPlayer(c *playerConn) *playerConn {
	return self.hub.conns.(*mapRing).Next(c).(*playerConn)
}

func (self *gameState) activeAfter(c *playerConn) *playerConn {
	return self.activeFrom(self.nextPlayer(c))
}

//

func (self *gameState) initDeck() {
	numCards := len(Suits) * len(CardValues)

	deck := make([]string, 0, numCards)
	for _, suit := range Suits {
		for cv := range CardValues {
			deck = append(deck, suit+cv)
		}
	}

	order := rand.Perm(numCards)
	for _, pos := range order {
		self.deck = append(self.deck, deck[pos])
	}
}

func (self *gameState) popCard() (card string) {
	if deck := self.deck; len(deck) > 0 {
		card = deck[0]
		self.deck = deck[1:]
	}
	return
}

func (self *gameState) dealCards() (card string) {
	for i := 0; i < 6; i++ {
		for pc := range self.hub.conns.Enumerate() {
			card = pc.(*playerConn).fromDeck()
		}
	}
	return
}

func (self *gameState) takeCards() {
	conns := self.hub.conns.(*mapRing)

	takeCards := func(pc *playerConn) {
		for len(self.deck) > 0 && len(pc.cards) < 6 {
			pc.fromDeck()
		}
	}

	for pc := range conns.EnumerateFrom(self.aconnStart) {
		if pc := pc.(*playerConn); pc != self.dconn {
			takeCards(pc)
		}
	}
	takeCards(self.dconn)
}

//

//TODO: don't like it
func (self *gameState) setTrump(card string) {
	tcard := self.popCard()
	if tcard == "" {
		tcard = card
	} else {
		self.deck = append(self.deck, tcard)
	}
	self.trump = tcard[:1]
}

func (self *gameState) chooseStarting() *playerConn {
	conns := self.hub.conns.(*mapRing)

	return conns.Nth(rand.Intn(conns.Len())).(*playerConn)
}

//

func (self *gameState) setRoles(res roundResult) {
	switch res {
	case None:
		self.aconn = self.chooseStarting()
	case Beat:
		self.aconn = self.activeFrom(self.dconn)
	case NotBeat:
		self.aconn = self.activeAfter(self.dconn)
	}
	self.dconn = self.activeAfter(self.aconn)
	self.aconnStart = self.aconn
}

func (self *gameState) newRound(res roundResult) sm.State {
	switch res {
	case None:
		self.initDeck()
		card := self.dealCards()
		self.setTrump(card)
	case NotBeat:
		self.dconn.fromDesk()
		self.takeCards()
	case Beat:
		self.desk = self.desk[:0]
		self.takeCards()
	}

	numActive := self.markInactive()
	if numActive < 2 {
		return stateGameOver
	}

	self.setRoles(res)
	self.cardToBeat = ""

	return stateAttack
}

// event handlers
// event handlers are actually transition functions.
// in case error event handler should neither change the gameState,
// nor return the state value different from passed to it as an argument.

func (self *gameState) handleStartInCollection(s sm.State, e *sm.Event) sm.State {
	if n := self.hub.conns.(*mapRing).Len(); n < 2 || n > 6 {
		logWrongNumber(n)
		return s
	}

	return self.newRound(None)
}

func (self *gameState) handleMoveInAttack(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.aconn {
		logOutOfTurn(conn)
		return s
	}

	// attacker sent the card
	if card != "" {
		// throw card to desk
		if _, ok := conn.cards[card]; !ok {
			logStrangeCard(card, conn)
			return s
		}
		conn.toDesk(card)

		self.cardToBeat = card
		return stateDefense
	}

	// attacker sent no card

	aconn := self.activeAfter(self.aconn)
	if aconn == self.dconn {
		aconn = self.activeAfter(aconn)
	}

	// check if all attackers have been polled
	if aconn == self.aconnStart {
		return self.newRound(Beat)
	}

	self.aconn = aconn
	return stateAttack
}

func (self *gameState) handleMoveInDefense(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.dconn {
		logOutOfTurn(conn)
		return s
	}

	// defender takes the cards
	if card == "" {
		return self.newRound(NotBeat)
	}

	// check that the sent card is capable to beat
	if higher(card, self.cardToBeat, self.trump) != 1 {
		logWontBeat(card, self.cardToBeat, self.trump)
		return s
	}

	// throw card to desk
	if _, ok := conn.cards[card]; !ok {
		logStrangeCard(card, conn)
		return s
	}
	conn.toDesk(card)

	return stateAttack
}

func (self *gameState) showDesk(s sm.State, e *sm.Event) sm.State {
	desk, err := json.Marshal(DeskMsg{self.desk})
	if err != nil {
		log.Println(err)
		return s
	}

	self.hub.bcastChan <- desk

	return s
}

func (self *gameState) log(s sm.State, e *sm.Event) sm.State {
	log.Println(self)
	return s
}

//

func NewGameState() *gameState {
	gst := new(gameState)

	gst.sm = sm.New(stateCollection, uint(stateCount), uint(cmdCount))

	gst.sm.OnChain(cmdStart,
		[]sm.State{stateCollection},
		[]sm.EventHandler{
			gst.handleStartInCollection,
			gst.showDesk,
			gst.log,
		},
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack},
		gst.handleMoveInAttack,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateDefense},
		gst.handleMoveInDefense,
	)

	gst.sm.OnChain(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		[]sm.EventHandler{
			gst.showDesk,
			gst.log,
		},
	)

	gst.deck = make([]string, 0, len(Suits)*len(CardValues)+ /*for trump*/ 1)
	gst.desk = make([]string, 0, 12)

	gst.hub = NewHub()

	return gst
}

//

type playerConn struct {
	gst *gameState

	cards  map[string]struct{}
	active bool

	conn      *websocket.Conn
	hubToConn chan []byte
}

func (self *playerConn) fromDeck() (card string) {
	if card = self.gst.popCard(); card != "" {
		self.cards[card] = struct{}{}
	}
	return
}

func (self *playerConn) toDesk(card string) {
	delete(self.cards, card)

	self.gst.desk = append(self.gst.desk, card)
}

func (self *playerConn) fromDesk() {
	for _, card := range self.gst.desk {
		self.cards[card] = struct{}{}
	}

	self.gst.desk = self.gst.desk[:0]
}

func (self *playerConn) write() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		select {
		case m, ok := <-self.hubToConn:
			if !ok {
				return
			}
			//TODO: text or binary?
			err := self.conn.WriteMessage(websocket.TextMessage, m)
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (self *playerConn) read() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		var m PlayerMsg

		err := self.conn.ReadJSON(&m)
		if err != nil {
			log.Println(err)
			return
		}

		var event *sm.Event
		switch m.Cmd {
		case cmdStart:
			event = &sm.Event{cmdStart, nil}
		case cmdMove:
			event = &sm.Event{cmdMove, cmdArgs{self, m.Card}}
		default:
			log.Printf("unknown command: %v", m.Cmd)
			continue
		}

		err = self.gst.sm.Emit(event)
		if err != nil {
			log.Println(err)
		}
	}
}

//

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

func playerHandler(gst *gameState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}

		p := &playerConn{gst, make(map[string]struct{}), true, conn, make(chan []byte)}

		gst.hub.regChan <- p
		defer func() {
			gst.hub.unregChan <- p
		}()

		go p.write()
		p.read()
	}
}

//

func startDurakSrv() error {
	gst := NewGameState()

	http.HandleFunc("/", playerHandler(gst))

	return http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func main() {
	err := startDurakSrv()
	if err != nil {
		log.Fatal(err)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())

	runtime.GOMAXPROCS(runtime.NumCPU())
}
<|endoftext|>


<commit_before>package jsonhl

import (
	"strings"
	"testing"
)

var colorReplacer = strings.NewReplacer("kc", keyColor, "vc", valueColor, "gc", bracketColor, "rc", resetColor)

func TestHighlightJson(t *testing.T) {

	validate(
		`{"hello":"world"}`,
		`gc{rckc"hello"rcgc:rcvc"world"rcgc}rc`, t)

	validate(
		`{"key1":"value1","key2":"value2"}`,
		`gc{rckc"key1"rcgc:rcvc"value1"rcgc,rckc"key2"rcgc:rcvc"value2"rcgc}rc`, t)

}

func validate(input string, expected string, t *testing.T) {
	expected = colorReplacer.Replace(expected)
	if result := Highlight(input); result != expected {
		t.Error("ERROR: " + result)
	}
}
<commit_msg>Fix tests<commit_after>package jsonhl

import (
	"strings"
	"testing"
)

var colorReplacer = strings.NewReplacer("kc", keyColor, "vc", valueColor, "gc", bracketColor, "rc", resetColor)

func TestHighlightJson(t *testing.T) {

	validate(
		`{"hello":"world"}`,
		`gc{rckc"hello"rcgc:rcvc"world"rcgc}rc`, t)

	validate(
		`{"key1":"value1","key2":"value2"}`,
		`gc{rckc"key1"rcgc:rcvc"value1"rcgc,rckc"key2"rcgc:rcvc"value2"rcgc}rc`, t)

}

func validate(input string, expected string, t *testing.T) {
	expected = colorReplacer.Replace(expected)
	if result := HighlightString(input); result != expected {
		t.Error("ERROR: " + result)
	}
}
<|endoftext|>


<commit_before>package leds

import (
	"fmt"
	"math"
	"math/rand"

	"github.com/siggy/bbox/bbox"
	"github.com/siggy/rpi_ws281x/golang/ws2811"
)

const (
	LED_COUNT    = 150
	FREEFORM_IDX = 100
	TICK_DELAY   = 17 // match sound to LEDs
)

type Row struct {
	start   int
	end     int
	buttons [bbox.BEATS]int
}

// TODO: cache?
func (r *Row) TickToLed(tick int) (buttonIdx int) {
	// determine where we are in the buttons array
	// 0 <= tick < 160
	// 0 <= beat < 16
	floatBeat := float64(tick) / float64(bbox.TICKS_PER_BEAT) // 127 => 12.7
	f := math.Floor(floatBeat)                                // 12
	c := math.Ceil(floatBeat)                                 // 13

	var floor int
	var ceil int

	if f == 0 {
		// between start and first beat
		floor = r.start
		ceil = r.buttons[int(c)]
	} else if c == bbox.BEATS {
		// between last beat and end
		floor = r.buttons[int(f)]
		ceil = r.end
	} else {
		// between first and last beat
		floor = r.buttons[int(f)]
		ceil = r.buttons[int(c)]
	}

	percentAhead := floatBeat - f // 12.7 - 12 => 0.7
	diff := percentAhead * (float64(ceil) - float64(floor))

	led = floor + int(diff)
	buttonIdx = -1
	if led == floor {
		buttonIdx = int(f)
	}

	return
}

type LedBeats struct {
	beats   bbox.Beats
	closing chan struct{}
	msgs    <-chan bbox.Beats
	ticks   <-chan int
}

var (
	rows = [bbox.SOUNDS]Row{
		// rows 0 and 1 are LED strip 0
		Row{
			start: 33,
			end:   0,
			buttons: [bbox.BEATS]int{
				30, 28, 26, 24,
				22, 20, 18, 16,
				14, 12, 10, 8,
				6, 4, 2, 0,
			},
		},
		Row{
			start: 35,
			end:   69,
			buttons: [bbox.BEATS]int{
				33, 35, 38, 40,
				42, 45, 47, 49,
				52, 54, 57, 59,
				61, 63, 65, 68,
			},
		},

		// rows 2 and 3 are LED strip 1
		Row{
			start: 43,
			end:   0,
			buttons: [bbox.BEATS]int{
				38, 35, 33, 31,
				28, 25, 22, 20,
				18, 15, 13, 10,
				8, 5, 3, 0,
			},
		},
		Row{
			start: 45,
			end:   87,
			buttons: [bbox.BEATS]int{
				42, 45, 48, 51,
				54, 57, 60, 63,
				65, 68, 71, 74,
				77, 80, 83, 86,
			},
		},
	}
)

func InitLedBeats(msgs <-chan bbox.Beats, ticks <-chan int) *LedBeats {
	InitLeds(LED_COUNT, LED_COUNT)

	return &LedBeats{
		closing: make(chan struct{}),
		msgs:    msgs,
		ticks:   ticks,
	}
}

func (l *LedBeats) Run() {
	defer func() {
		ws2811.Clear()
		ws2811.Render()
		ws2811.Wait()
		ws2811.Fini()
	}()

	ws2811.Clear()
	err := ws2811.Render()
	if err != nil {
		fmt.Printf("ws2811.Render failed: %+v\n", err)
		panic(err)
	}
	err = ws2811.Wait()
	if err != nil {
		fmt.Printf("ws2811.Wait failed: %+v\n", err)
		panic(err)
	}

	for {
		select {
		case _, more := <-l.closing:
			if !more {
				fmt.Printf("LEDs closing\n")
				return
			}
		case tick := <-l.ticks:
			tick = (tick + bbox.TICKS - TICK_DELAY) % bbox.TICKS
			ws2811.Clear()

			buttonIdxs := [len(rows)]int{}
			for i, r := range rows {
				buttonIdxs[i] = r.TickToLed(tick)
			}

			// light all leds around current position
			for i, _ := range rows[0:2] {
				sineMap = leds.getSineVals(LED_COUNT, leds.float64(tick)/float64(bbox.TICKS_PER_BEAT))
				for led, value := range sineMap {
					ws2811.SetLed(0, led, MkColor(0, 0, 0, value))
				}
			}
			for i, _ := range rows[2:4] {
				sineMap = leds.getSineVals(LED_COUNT, leds.float64(tick)/float64(bbox.TICKS_PER_BEAT))
				for led, value := range sineMap {
					ws2811.SetLed(1, led, MkColor(0, 0, 0, value))
				}
			}

			actives := 0

			// light active beats
			for i, beat := range l.beats[0:2] {
				for j, t := range beat {
					if t {
						if j == buttonIdxs[i] {
							actives++
							ws2811.SetLed(0, rows[i].buttons[j], purple)
						} else {
							ws2811.SetLed(0, rows[i].buttons[j], trueRed)
						}
					}
				}
			}
			for i, beat := range l.beats[2:4] {
				for j, t := range beat {
					if t {
						if j == buttonIdxs[i+2] {
							actives++
							ws2811.SetLed(1, rows[i+2].buttons[j], purple)
						} else {
							ws2811.SetLed(1, rows[i+2].buttons[j], trueRed)
						}
					}
				}
			}

			// light freeform leds based on beat activity
			for i := 0; i < actives; i++ {
				r := rand.Intn(LED_COUNT - FREEFORM_IDX)
				ws2811.SetLed(0,
					r+FREEFORM_IDX,
					Colors[r%len(Colors)],
				)
				ws2811.SetLed(1,
					r+FREEFORM_IDX,
					Colors[(r+1)%len(Colors)],
				)
			}

			err = ws2811.Render()
			if err != nil {
				fmt.Printf("ws2811.Render failed: %+v\n", err)
				panic(err)
			}
			err = ws2811.Wait()
			if err != nil {
				fmt.Printf("ws2811.Wait failed: %+v\n", err)
				panic(err)
			}
		case beats, more := <-l.msgs:
			if more {
				// incoming beat update from keyboard
				l.beats = beats
			} else {
				// closing
				fmt.Printf("LEDs closing\n")
				return
			}
		}
	}
}

func (l *LedBeats) Close() {
	// TODO: this doesn't block?
	close(l.closing)
}
<commit_msg>fixup sine code<commit_after>package leds

import (
	"fmt"
	"math"
	"math/rand"

	"github.com/siggy/bbox/bbox"
	"github.com/siggy/rpi_ws281x/golang/ws2811"
)

const (
	LED_COUNT    = 150
	FREEFORM_IDX = 100
	TICK_DELAY   = 17 // match sound to LEDs
)

type Row struct {
	start   int
	end     int
	buttons [bbox.BEATS]int
}

// TODO: cache?
func (r *Row) TickToLed(tick int) (buttonIdx int, peak float64) {
	// determine where we are in the buttons array
	// 0 <= tick < 160
	// 0 <= beat < 16
	floatBeat := float64(tick) / float64(bbox.TICKS_PER_BEAT) // 127 => 12.7
	f := math.Floor(floatBeat)                                // 12
	c := math.Ceil(floatBeat)                                 // 13

	var floor int
	var ceil int

	if f == 0 {
		// between start and first beat
		floor = r.start
		ceil = r.buttons[int(c)]
	} else if c == bbox.BEATS {
		// between last beat and end
		floor = r.buttons[int(f)]
		ceil = r.end
	} else {
		// between first and last beat
		floor = r.buttons[int(f)]
		ceil = r.buttons[int(c)]
	}

	percentAhead := floatBeat - f // 12.7 - 12 => 0.7
	diff := percentAhead * (float64(ceil) - float64(floor))

	peak = float64(floor) + diff
	buttonIdx = -1
	if int(diff) == 0 {
		buttonIdx = int(f)
	}

	return
}

type LedBeats struct {
	beats   bbox.Beats
	closing chan struct{}
	msgs    <-chan bbox.Beats
	ticks   <-chan int
}

var (
	rows = [bbox.SOUNDS]Row{
		// rows 0 and 1 are LED strip 0
		Row{
			start: 33,
			end:   0,
			buttons: [bbox.BEATS]int{
				30, 28, 26, 24,
				22, 20, 18, 16,
				14, 12, 10, 8,
				6, 4, 2, 0,
			},
		},
		Row{
			start: 35,
			end:   69,
			buttons: [bbox.BEATS]int{
				33, 35, 38, 40,
				42, 45, 47, 49,
				52, 54, 57, 59,
				61, 63, 65, 68,
			},
		},

		// rows 2 and 3 are LED strip 1
		Row{
			start: 43,
			end:   0,
			buttons: [bbox.BEATS]int{
				38, 35, 33, 31,
				28, 25, 22, 20,
				18, 15, 13, 10,
				8, 5, 3, 0,
			},
		},
		Row{
			start: 45,
			end:   87,
			buttons: [bbox.BEATS]int{
				42, 45, 48, 51,
				54, 57, 60, 63,
				65, 68, 71, 74,
				77, 80, 83, 86,
			},
		},
	}
)

func InitLedBeats(msgs <-chan bbox.Beats, ticks <-chan int) *LedBeats {
	InitLeds(LED_COUNT, LED_COUNT)

	return &LedBeats{
		closing: make(chan struct{}),
		msgs:    msgs,
		ticks:   ticks,
	}
}

func (l *LedBeats) Run() {
	defer func() {
		ws2811.Clear()
		ws2811.Render()
		ws2811.Wait()
		ws2811.Fini()
	}()

	ws2811.Clear()
	err := ws2811.Render()
	if err != nil {
		fmt.Printf("ws2811.Render failed: %+v\n", err)
		panic(err)
	}
	err = ws2811.Wait()
	if err != nil {
		fmt.Printf("ws2811.Wait failed: %+v\n", err)
		panic(err)
	}

	for {
		select {
		case _, more := <-l.closing:
			if !more {
				fmt.Printf("LEDs closing\n")
				return
			}
		case tick := <-l.ticks:
			tick = (tick + bbox.TICKS - TICK_DELAY) % bbox.TICKS
			ws2811.Clear()

			buttonIdxs := [len(rows)]int{}
			peakVals := [len(rows)]float64{}
			for i, r := range rows {
				buttonIdxs[i], peakVals[i] = r.TickToLed(tick)
			}

			// light all leds around current position
			for i, _ := range rows[0:2] {
				sineMap := leds.getSineVals(LED_COUNT, peakVals[i])
				for led, value := range sineMap {
					ws2811.SetLed(0, led, MkColor(0, 0, 0, value))
				}
			}
			for i, _ := range rows[2:4] {
				sineMap := leds.getSineVals(LED_COUNT, peakVals[i+2])
				for led, value := range sineMap {
					ws2811.SetLed(1, led, MkColor(0, 0, 0, value))
				}
			}

			actives := 0

			// light active beats
			for i, beat := range l.beats[0:2] {
				for j, t := range beat {
					if t {
						if j == buttonIdxs[i] {
							actives++
							ws2811.SetLed(0, rows[i].buttons[j], purple)
						} else {
							ws2811.SetLed(0, rows[i].buttons[j], trueRed)
						}
					}
				}
			}
			for i, beat := range l.beats[2:4] {
				for j, t := range beat {
					if t {
						if j == buttonIdxs[i+2] {
							actives++
							ws2811.SetLed(1, rows[i+2].buttons[j], purple)
						} else {
							ws2811.SetLed(1, rows[i+2].buttons[j], trueRed)
						}
					}
				}
			}

			// light freeform leds based on beat activity
			for i := 0; i < actives; i++ {
				r := rand.Intn(LED_COUNT - FREEFORM_IDX)
				ws2811.SetLed(0,
					r+FREEFORM_IDX,
					Colors[r%len(Colors)],
				)
				ws2811.SetLed(1,
					r+FREEFORM_IDX,
					Colors[(r+1)%len(Colors)],
				)
			}

			err = ws2811.Render()
			if err != nil {
				fmt.Printf("ws2811.Render failed: %+v\n", err)
				panic(err)
			}
			err = ws2811.Wait()
			if err != nil {
				fmt.Printf("ws2811.Wait failed: %+v\n", err)
				panic(err)
			}
		case beats, more := <-l.msgs:
			if more {
				// incoming beat update from keyboard
				l.beats = beats
			} else {
				// closing
				fmt.Printf("LEDs closing\n")
				return
			}
		}
	}
}

func (l *LedBeats) Close() {
	// TODO: this doesn't block?
	close(l.closing)
}
<|endoftext|>


<commit_before>package beat

import (
	"fmt"
	"github.com/christiangalsterer/execbeat/config"
	"github.com/elastic/beats/libbeat/beat"
	"github.com/elastic/beats/libbeat/cfgfile"
	"github.com/elastic/beats/libbeat/common"
	"github.com/elastic/beats/libbeat/logp"
	"github.com/elastic/beats/libbeat/publisher"
)

type Execbeat struct {
	done       chan bool
	ExecConfig config.ConfigSettings
	client     publisher.Client
}

func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
	bt := &Execbeat{
		done: make(chan bool),
	}

	err := cfgfile.Read(&bt.ExecConfig, "")
	if err != nil {
		logp.Err("Error reading config file: %v", err)
		return nil, fmt.Errorf("Error reading config file: %v", err)
	}

	return bt, nil
}

func (exexBeat *Execbeat) Run(b *beat.Beat) error {
	var err error
	var poller *Executor
	logp.Info("execbeat is running! Hit CTRL-C to stop it.")
	exexBeat.client = b.Publisher.Connect()

	for i, exitConfig := range exexBeat.ExecConfig.Execbeat.Execs {
		logp.Debug("execbeat", "Creating poller # %v with command: %v", i, exitConfig.Command)
		poller = NewExecutor(exexBeat, exitConfig)
		go poller.Run()
	}

	for {
		select {
		case <-exexBeat.done:
			return nil
		}
	}
}

func (execBeat *Execbeat) Cleanup(b *beat.Beat) error {
	return nil
}

func (execBeat *Execbeat) Stop() {
	close(execBeat.done)
}
<commit_msg>Upgrade to Beats v5.0.1 - removed uncreachable code found by make check<commit_after>package beat

import (
	"fmt"
	"github.com/christiangalsterer/execbeat/config"
	"github.com/elastic/beats/libbeat/beat"
	"github.com/elastic/beats/libbeat/cfgfile"
	"github.com/elastic/beats/libbeat/common"
	"github.com/elastic/beats/libbeat/logp"
	"github.com/elastic/beats/libbeat/publisher"
)

type Execbeat struct {
	done       chan bool
	ExecConfig config.ConfigSettings
	client     publisher.Client
}

func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
	bt := &Execbeat{
		done: make(chan bool),
	}

	err := cfgfile.Read(&bt.ExecConfig, "")
	if err != nil {
		logp.Err("Error reading config file: %v", err)
		return nil, fmt.Errorf("Error reading config file: %v", err)
	}

	return bt, nil
}

func (exexBeat *Execbeat) Run(b *beat.Beat) error {
	var poller *Executor
	logp.Info("execbeat is running! Hit CTRL-C to stop it.")
	exexBeat.client = b.Publisher.Connect()

	for i, exitConfig := range exexBeat.ExecConfig.Execbeat.Execs {
		logp.Debug("execbeat", "Creating poller # %v with command: %v", i, exitConfig.Command)
		poller = NewExecutor(exexBeat, exitConfig)
		go poller.Run()
	}

	for {
		select {
		case <-exexBeat.done:
			return nil
		}
	}
}

func (execBeat *Execbeat) Cleanup(b *beat.Beat) error {
	return nil
}

func (execBeat *Execbeat) Stop() {
	close(execBeat.done)
}
<|endoftext|>


<commit_before>/*

moves is a convenience package that implements composable Moves to make it
easy to implement common logic. The Base move type is a very simple move that
implements the basic stubs necessary for your straightforward moves to have
minimal boilerplate.

You interact with and configure various move types by implementing interfaces.
Those interfaes are defined in the moveinterfaces subpackage, to make this
package's design more clear.

There are many move types defined. Some are designed to be used directly with
minimal modification; others are powerful move types that are designed to be
sub-classed.

Base Move

Implementing a Move requires a lot of stub methods to implement the
boardgame.Move interface, but also a lot of logic, especially to support
Phases correctly. moves.Base is a move that all moves should embed somewhere
in their hierarchy. It is very important to always call your superclasse's
Legal(), because moves.Base.Legal contains important logic to implement phases
and ordered moves within phases.

Move Deal and Collect Component Moves

Generally when moving components from one place to another it makes sense to
move one component at a time, so that each component is animated separately.
However, this is a pain to implement, because it requires implementing a move
that knows how many times to apply itself in a row, which is fincky and error-
prone.

There is a collection of 9 moves that all do basically the same thing for
moving components, one at a time, from stack to stack. Move-type moves move
components between two specific stacks, often both on your GameState. Deal and
Collect type moves move components between a stack in GameState and a stack in
each Player's PlayerState. Deal-type moves move components from the game stack
to the player stack, and Collect-type moves move components from each player
to the GameState.

All of these moves define a way to define the source and destination stack.
For Move-type moves, you define SourceStack() and DestinationStack(). For Deal
and Collect-type moves, you implement GameStack() and PlayerStack().

All moves in this collection implement TargetCount() int, and all of them
default to 1. Override this if you want a different number of components
checked for in the end condition.

Each of Move, Deal, and Collect have three variants based on the end
condition. Note that Move-type moves have only two stacks, but Deal and
Collect type moves operate on n pairs of stacks, where n is the number of
players in the game. In general for Deal and Collect type moves, the condition
is met when all pairs of stacks meet the end condition.

{Move,Deal,Collect}CountComponents simply apply that many moves without regard
to the number of components in the source or destination stacks. Move names
that end in CountReached operate until the destination stacks all have
TargetCount or more items. Move names that end in CountLeft operate until the
source stacks all have TargetCount or fewer items in them.

ApplyUntil ApplyUntilCount and ApplyCountTimes

These moves are what the MoveComponents moves are based off of and are
designed to be subclassed. They apply the move in question until some
condition is reached.

RoundRobin and RoundRobinNumRounds

Round Robin moves are like ApplyUntilCount and friends, except they go around
and operate on each player in succession. RoundRobinNumRounds goes around each
player until NumRounds() cycles have completed. The base RoundRobin goes
around until the PlayerCondition has been met for each player. These are the
most complicated moves in the set; if you subclass one directly you're most
likely to subclass RoundRobinNumRounds.

FinishTurn

FinishTurn is a move that is designed to be used as a fix-up move during
normal phases of play in your game. It checks whether the current player's
turn is done (based on criteria you specify) and if so advances to the next
player, resetting state as appropriate.

StartPhase

The StartPhase move is designed to set your game's phase to the next phase.
It's generally used as the last move in an ordered phase, for example, the
last move in your game's SetUp phase. In general you don't need to subclass
this move directly at all; just use NewStartPhaseMoveConfig to get a
MoveTyepConfig that does what you want.

ShuffleStack

Shuffle stack is a simple move that just shuffles the stack denoted by
SourceStack.

*/
package moves
<commit_msg>Forgot to document the Current Player Move in package doc. Part of #516.<commit_after>/*

moves is a convenience package that implements composable Moves to make it
easy to implement common logic. The Base move type is a very simple move that
implements the basic stubs necessary for your straightforward moves to have
minimal boilerplate.

You interact with and configure various move types by implementing interfaces.
Those interfaes are defined in the moveinterfaces subpackage, to make this
package's design more clear.

There are many move types defined. Some are designed to be used directly with
minimal modification; others are powerful move types that are designed to be
sub-classed.

Base Move

Implementing a Move requires a lot of stub methods to implement the
boardgame.Move interface, but also a lot of logic, especially to support
Phases correctly. moves.Base is a move that all moves should embed somewhere
in their hierarchy. It is very important to always call your superclasse's
Legal(), because moves.Base.Legal contains important logic to implement phases
and ordered moves within phases.

Current Player Move

These moves are for moves that are only legal to be made by the current
player. Their Legal() will verify that it is the proposer's turn.

Move Deal and Collect Component Moves

Generally when moving components from one place to another it makes sense to
move one component at a time, so that each component is animated separately.
However, this is a pain to implement, because it requires implementing a move
that knows how many times to apply itself in a row, which is fincky and error-
prone.

There is a collection of 9 moves that all do basically the same thing for
moving components, one at a time, from stack to stack. Move-type moves move
components between two specific stacks, often both on your GameState. Deal and
Collect type moves move components between a stack in GameState and a stack in
each Player's PlayerState. Deal-type moves move components from the game stack
to the player stack, and Collect-type moves move components from each player
to the GameState.

All of these moves define a way to define the source and destination stack.
For Move-type moves, you define SourceStack() and DestinationStack(). For Deal
and Collect-type moves, you implement GameStack() and PlayerStack().

All moves in this collection implement TargetCount() int, and all of them
default to 1. Override this if you want a different number of components
checked for in the end condition.

Each of Move, Deal, and Collect have three variants based on the end
condition. Note that Move-type moves have only two stacks, but Deal and
Collect type moves operate on n pairs of stacks, where n is the number of
players in the game. In general for Deal and Collect type moves, the condition
is met when all pairs of stacks meet the end condition.

{Move,Deal,Collect}CountComponents simply apply that many moves without regard
to the number of components in the source or destination stacks. Move names
that end in CountReached operate until the destination stacks all have
TargetCount or more items. Move names that end in CountLeft operate until the
source stacks all have TargetCount or fewer items in them.

ApplyUntil ApplyUntilCount and ApplyCountTimes

These moves are what the MoveComponents moves are based off of and are
designed to be subclassed. They apply the move in question until some
condition is reached.

RoundRobin and RoundRobinNumRounds

Round Robin moves are like ApplyUntilCount and friends, except they go around
and operate on each player in succession. RoundRobinNumRounds goes around each
player until NumRounds() cycles have completed. The base RoundRobin goes
around until the PlayerCondition has been met for each player. These are the
most complicated moves in the set; if you subclass one directly you're most
likely to subclass RoundRobinNumRounds.

FinishTurn

FinishTurn is a move that is designed to be used as a fix-up move during
normal phases of play in your game. It checks whether the current player's
turn is done (based on criteria you specify) and if so advances to the next
player, resetting state as appropriate.

StartPhase

The StartPhase move is designed to set your game's phase to the next phase.
It's generally used as the last move in an ordered phase, for example, the
last move in your game's SetUp phase. In general you don't need to subclass
this move directly at all; just use NewStartPhaseMoveConfig to get a
MoveTyepConfig that does what you want.

ShuffleStack

Shuffle stack is a simple move that just shuffles the stack denoted by
SourceStack.

*/
package moves
<|endoftext|>


<commit_before>// Copyright © 2016 Jacob Dearing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package havoc

import (
	"runtime/debug"
)

// FreeMemory forces VM to release unused memory back to the system
func FreeMemory() {
	debug.FreeOSMemory()
}

// Forever runs forever.
func Forever() {
	for {
		// evah
	}
}
<commit_msg>add note for future<commit_after>// Copyright © 2016 Jacob Dearing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package havoc

import (
	"runtime/debug"
)

// FreeMemory forces VM to release unused memory back to the system
func FreeMemory() {
	debug.FreeOSMemory()
}

// Forever runs forever.
// TODO: too intensive
func Forever() {
	for {
		// evah
	}
}
<|endoftext|>


<commit_before>package packer

import "fmt"
import "io"

// The Ui interface handles all communication for Packer with the outside
// world. This sort of control allows us to strictly control how output
// is formatted and various levels of output.
type Ui interface {
	Say(format string, a ...interface{})
	Error(format string, a...interface{})
}

// The ReaderWriterUi is a UI that writes and reads from standard Go
// io.Reader and io.Writer.
type ReaderWriterUi struct {
	Reader io.Reader
	Writer io.Writer
}

func (rw *ReaderWriterUi) Say(format string, a ...interface{}) {
	fmt.Fprintf(rw.Writer, format, a...)
}

func (rw *ReaderWriterUi) Error(format string, a ...interface{}) {
	fmt.Fprintf(rw.Writer, format, a...)
}
<commit_msg>packer: error if UI fails<commit_after>package packer

import "fmt"
import "io"

// The Ui interface handles all communication for Packer with the outside
// world. This sort of control allows us to strictly control how output
// is formatted and various levels of output.
type Ui interface {
	Say(format string, a ...interface{})
	Error(format string, a...interface{})
}

// The ReaderWriterUi is a UI that writes and reads from standard Go
// io.Reader and io.Writer.
type ReaderWriterUi struct {
	Reader io.Reader
	Writer io.Writer
}

func (rw *ReaderWriterUi) Say(format string, a ...interface{}) {
	_, err := fmt.Fprintf(rw.Writer, format, a...)
	if err != nil {
		panic(err)
	}
}

func (rw *ReaderWriterUi) Error(format string, a ...interface{}) {
	_, err := fmt.Fprintf(rw.Writer, format, a...)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// GoMySQL - A MySQL client library for Go
//
// Copyright 2010-2011 Phil Bayfield. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mysql

import "fmt"

// Client error codes
type Errno int

const (
	CR_UNKNOWN_ERROR        Errno = 2000
	CR_SOCKET_CREATE_ERROR  Errno = 2001
	CR_CONNECTION_ERROR     Errno = 2002
	CR_CONN_HOST_ERROR      Errno = 2003
	CR_IPSOCK_ERROR         Errno = 2004
	CR_UNKNOWN_HOST         Errno = 2005
	CR_SERVER_GONE_ERROR    Errno = 2006
	CR_SERVER_HANDSHAKE_ERR Errno = 2012
	CR_SERVER_LOST          Errno = 2013
	CR_COMMANDS_OUT_OF_SYNC Errno = 2014
	CR_MALFORMED_PACKET     Errno = 2027
	CR_NO_PREPARE_STMT      Errno = 2030
	CR_PARAMS_NOT_BOUND     Errno = 2031
	CR_DATA_TRUNCATED       Errno = 2032
	CR_NO_RESULT_SET        Errno = 2053
	CR_NOT_IMPLEMENTED      Errno = 2054
	CR_ALREADY_CONNECTED    Errno = 2058
)

// Client error descriptions
type Error string

const (
	CR_UNKNOWN_ERROR_STR        Error = "Unknown MySQL error"
	CR_SOCKET_CREATE_ERROR_STR  Error = "Can't create UNIX socket (%d)"
	CR_CONNECTION_ERROR_STR     Error = "Can't connect to local MySQL server through socket '%s'"
	CR_CONN_HOST_ERROR_STR      Error = "Can't connect to MySQL server on '%s'"
	CR_IPSOCK_ERROR_STR         Error = "Can't create TCP/IP socket (%d)"
	CR_UNKNOWN_HOST_STR         Error = "Uknown MySQL server host '%s' (%d)"
	CR_SERVER_GONE_ERROR_STR    Error = "MySQL server has gone away"
	CR_SERVER_HANDSHAKE_ERR_STR Error = "Error in server handshake"
	CR_SERVER_LOST_STR          Error = "Lost connection to MySQL server during query"
	CR_COMMANDS_OUT_OF_SYNC_STR Error = "Commands out of sync; you can't run this command now"
	CR_MALFORMED_PACKET_STR     Error = "Malformed Packet"
	CR_NO_PREPARE_STMT_STR      Error = "Statement not prepared"
	CR_PARAMS_NOT_BOUND_STR     Error = "No data supplied for parameters in prepared statement"
	CR_DATA_TRUNCATED_STR       Error = "Data truncated"
	CR_NO_RESULT_SET_STR        Error = "Attempt to read a row while there is no result set associated with the statement"
	CR_NOT_IMPLEMENTED_STR      Error = "This feature is not implemented yet"
	CR_ALREADY_CONNECTED_STR    Error = "This handle is already connected"
)

// Client error struct
type ClientError struct {
	Errno Errno
	Error Error
}

// Convert to string
func (e *ClientError) String() string {
	return fmt.Sprintf("#%d %s", e.Errno, e.Error)
}

// Server error struct
type ServerError struct {
	Errno Errno
	Error Error
}

// Convert to string
func (e *ServerError) String() string {
	return fmt.Sprintf("#%d %s", e.Errno, e.Error)
}
<commit_msg>added more error types and rearranged<commit_after>// GoMySQL - A MySQL client library for Go
//
// Copyright 2010-2011 Phil Bayfield. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mysql

import "fmt"

// Client error types
type Errno int
type Error string

const (
	CR_UNKNOWN_ERROR               Errno = 2000
	CR_UNKNOWN_ERROR_STR           Error = "Unknown MySQL error"
	CR_SOCKET_CREATE_ERROR         Errno = 2001
	CR_SOCKET_CREATE_ERROR_STR     Error = "Can't create UNIX socket (%d)"
	CR_CONNECTION_ERROR            Errno = 2002
	CR_CONNECTION_ERROR_STR        Error = "Can't connect to local MySQL server through socket '%s'"
	CR_CONN_HOST_ERROR             Errno = 2003
	CR_CONN_HOST_ERROR_STR         Error = "Can't connect to MySQL server on '%s'"
	CR_IPSOCK_ERROR                Errno = 2004
	CR_IPSOCK_ERROR_STR            Error = "Can't create TCP/IP socket (%d)"
	CR_UNKNOWN_HOST                Errno = 2005
	CR_UNKNOWN_HOST_STR            Error = "Uknown MySQL server host '%s' (%d)"
	CR_SERVER_GONE_ERROR           Errno = 2006
	CR_SERVER_GONE_ERROR_STR       Error = "MySQL server has gone away"
	CR_VERSION_ERROR               Errno = 2007
	CR_VERSION_ERROR_STR           Error = "Protocol mismatch; server version = %d, client version = %d"
	CR_OUT_OF_MEMORY               Errno = 2008
	CR_OUT_OF_MEMORY_STR           Error = "MySQL client ran out of memory"
	CR_WRONG_HOST_INFO             Errno = 2009
	CR_WRONG_HOST_INFO_STR         Error = "Wrong host info"
	CR_LOCALHOST_CONNECTION        Errno = 2010
	CR_LOCALHOST_CONNECTION_STR    Error = "Localhost via UNIX socket"
	CR_TCP_CONNECTION              Errno = 2011
	CR_TCP_CONNECTION_STR          Error = "%s via TCP/IP"
	CR_SERVER_HANDSHAKE_ERR        Errno = 2012
	CR_SERVER_HANDSHAKE_ERR_STR    Error = "Error in server handshake"
	CR_SERVER_LOST                 Errno = 2013
	CR_SERVER_LOST_STR             Error = "Lost connection to MySQL server during query"
	CR_COMMANDS_OUT_OF_SYNC        Errno = 2014
	CR_COMMANDS_OUT_OF_SYNC_STR    Error = "Commands out of sync; you can't run this command now"
	CR_NAMEDPIPE_CONNECTION        Errno = 2015
	CR_NAMEDPIPE_CONNECTION_STR    Error = "Named pipe: %s"
	CR_NAMEDPIPEWAIT_ERROR         Errno = 2016
	CR_NAMEDPIPEWAIT_ERROR_STR     Error = "Can't wait for named pipe to host: %s pipe: %s (%lu)"
	CR_NAMEDPIPEOPEN_ERROR         Errno = 2017
	CR_NAMEDPIPEOPEN_ERROR_STR     Error = "Can't open named pipe to host: %s pipe: %s (%lu)"
	CR_NAMEDPIPESETSTATE_ERROR     Errno = 2018
	CR_NAMEDPIPESETSTATE_ERROR_STR Error = "Can't set state of named pipe to host: %s pipe: %s (%lu)"
	CR_CANT_READ_CHARSET           Errno = 2019
	CR_CANT_READ_CHARSET_STR       Error = "Can't initialize character set %s (path: %s)"
	CR_NET_PACKET_TOO_LARGE        Errno = 2020
	CR_NET_PACKET_TOO_LARGE_STR    Error = "Got packet bigger than 'max_allowed_packet' bytes"
	CR_EMBEDDED_CONNECTION         Errno = 2021
	CR_EMBEDDED_CONNECTION_STR     Error = "Embedded server"
	CR_PROBE_SLAVE_STATUS          Errno = 2022
	CR_PROBE_SLAVE_STATUS_STR      Error = "Error on SHOW SLAVE STATUS:"
	CR_PROBE_SLAVE_HOSTS           Errno = 2023
	CR_PROBE_SLAVE_HOSTS_STR       Error = "Error on SHOW SLAVE HOSTS:"
	CR_PROBE_SLAVE_CONNECT         Errno = 2024
	CR_PROBE_SLAVE_CONNECT_STR     Error = "Error connecting to slave:"
	CR_PROBE_MASTER_CONNECT        Errno = 2025
	CR_PROBE_MASTER_CONNECT_STR    Error = "Error connecting to master:"
	CR_SSL_CONNECTION_ERROR        Errno = 2026
	CR_SSL_CONNECTION_ERROR_STR    Error = "SSL connection error"
	CR_MALFORMED_PACKET            Errno = 2027
	CR_MALFORMED_PACKET_STR        Error = "Malformed Packet"
	CR_WRONG_LICENSE               Errno = 2028
	CR_WRONG_LICENSE_STR           Error = "This client library is licensed only for use with MySQL servers having '%s' license"
	CR_NULL_POINTER                Errno = 2029
	CR_NULL_POINTER_STR            Error = "Invalid use of null pointer"
	CR_NO_PREPARE_STMT             Errno = 2030
	CR_NO_PREPARE_STMT_STR         Error = "Statement not prepared"
	CR_PARAMS_NOT_BOUND            Errno = 2031
	CR_PARAMS_NOT_BOUND_STR        Error = "No data supplied for parameters in prepared statement"
	CR_DATA_TRUNCATED              Errno = 2032
	CR_DATA_TRUNCATED_STR          Error = "Data truncated"
	CR_NO_PARAMETERS_EXISTS        Errno = 2033
	CR_NO_PARAMETERS_EXISTS_STR    Error = "No parameters exist in the statement"
	CR_INVALID_PARAMETER_NO        Errno = 2034
	CR_INVALID_PARAMETER_NO_STR    Error = "Invalid parameter number"
	CR_INVALID_BUFFER_USE          Errno = 2035
	CR_INVALID_BUFFER_USE_STR      Error = "Can't send long data for non-string/non-binary data types (parameter: %d)"
	CR_UNSUPPORTED_PARAM_TYPE      Errno = 2036
	CR_UNSUPPORTED_PARAM_TYPE_STR  Error = "Using unsupported buffer type: %d (parameter: %d)"
	CR_CONN_UNKNOW_PROTOCOL        Errno = 2047
	CR_CONN_UNKNOW_PROTOCOL_STR    Error = "Wrong or unknown protocol"
	CR_SECURE_AUTH                 Errno = 2049
	CR_SECURE_AUTH_STR             Error = "Connection using old (pre-4.1.1) authentication protocol refused (client option 'secure_auth' enabled)"
	CR_FETCH_CANCELED              Errno = 2050
	CR_FETCH_CANCELED_STR          Error = "Row retrieval was canceled by mysql_stmt_close() call"
	CR_NO_DATA                     Errno = 2051
	CR_NO_DATA_STR                 Error = "Attempt to read column without prior row fetch"
	CR_NO_STMT_METADATA            Errno = 2052
	CR_NO_STMT_METADATA_STR        Error = "Prepared statement contains no metadata"
	CR_NO_RESULT_SET               Errno = 2053
	CR_NO_RESULT_SET_STR           Error = "Attempt to read a row while there is no result set associated with the statement"
	CR_NOT_IMPLEMENTED             Errno = 2054
	CR_NOT_IMPLEMENTED_STR         Error = "This feature is not implemented yet"
	CR_SERVER_LOST_EXTENDED        Errno = 2055
	CR_SERVER_LOST_EXTENDED_STR    Error = "Lost connection to MySQL server at '%s', system error: %d"
	CR_STMT_CLOSED                 Errno = 2056
	CR_STMT_CLOSED_STR             Error = "Statement closed indirectly because of a preceeding %s() call"
	CR_NEW_STMT_METADATA           Errno = 2057
	CR_NEW_STMT_METADATA_STR       Error = "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again"
	CR_ALREADY_CONNECTED           Errno = 2058
	CR_ALREADY_CONNECTED_STR       Error = "This handle is already connected"
	CR_AUTH_PLUGIN_CANNOT_LOAD     Errno = 2059
	CR_AUTH_PLUGIN_CANNOT_LOAD_STR Error = "Authentication plugin '%s' cannot be loaded: %s"
)

// Client error struct
type ClientError struct {
	Errno Errno
	Error Error
}

// Convert to string
func (e *ClientError) String() string {
	return fmt.Sprintf("#%d %s", e.Errno, e.Error)
}

// Server error struct
type ServerError struct {
	Errno Errno
	Error Error
}

// Convert to string
func (e *ServerError) String() string {
	return fmt.Sprintf("#%d %s", e.Errno, e.Error)
}
<|endoftext|>