<commit_before>// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// boot allows to handover a system running linuxboot/u-root
// to a legacy preinstalled operating system by replacing the traditional
// bootloader path

//
// Synopsis:
//	boot [-dev][-v][-dryrun]
//
// Description:
//	If returns to u-root shell, the code didn't found a local bootable option
//      -dev glob to use; default is /sys/class/block/*
//      -v prints messages
//      -dryrun doesn't really boot
//
// Notes:
//	The code is looking for boot/grub/grub.cfg file as to identify the
//	boot option.
//	The first bootable device found in the block device tree is the one used
//	Windows is not supported (that is a work in progress)
//
// Example:
//	boot -v 	- Start the script in verbose mode for debugging purpose

package main

import (
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"github.com/u-root/u-root/pkg/kexec"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	bootableMBR     = 0xaa55
	signatureOffset = 510
)

var (
	devGlob = flag.String("dev", "/sys/block/*", "Glob for devices")
	v       = flag.Bool("v", false, "Print debug messages")
	verbose = func(string, ...interface{}) {}
	dryrun  = flag.Bool("dryrun", true, "Boot")
	uroot string
)

// checkForBootableMBR is looking for bootable MBR signature
// Current support is limited to Hard disk devices and USB devices
func checkForBootableMBR(path string) error {
	var sig uint16
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	if err := binary.Read(io.NewSectionReader(f, signatureOffset, 2), binary.LittleEndian, &sig); err != nil {
		return err
	}
	if sig != bootableMBR {
		err := errors.New("Not a bootable device")
		return err
	}
	return nil
}

// getSupportedFilesystem returns all block file system supported by the linuxboot kernel
func getSupportedFilesystem() ([]string, error) {
	var err error
	fs, err := ioutil.ReadFile("/proc/filesystems")
	if err != nil {
		return nil, err
	}
	var returnValue []string
	for _, f := range strings.Split(string(fs), "\n") {
		n := strings.Fields(f)
		if len(n) != 1 {
			continue
		}
		returnValue = append(returnValue, n[0])
	}
	return returnValue, err

}

// mountEntry tries to mount a specific block device using a list of
// supported file systems. We have to try to mount the device
// itself, since devices can be filesystem formatted but not
// partitioned; and all its partitions.
func mountEntry(d string, supportedFilesystem []string) error {
	var err error
	verbose("Try to mount %v", d)

	// find or create the mountpoint.
	m := filepath.Join(uroot, d)
	if _, err = os.Stat(m); err != nil && os.IsNotExist(err) {
		err = os.MkdirAll(m, 0777)
	}
	if err != nil {
		return err
	}
	for _, filesystem := range supportedFilesystem {
		// Was supposed to be unecessary for kernel 4.x.x
		var flags = uintptr(syscall.MS_MGC_VAL)
		if err := syscall.Mount(d, m, filesystem, flags, ""); err == nil {
			return err
		}
	}
	return errors.New("Unable to mount any partition on " + d)
}

func umountEntry(path string) bool {
	var returnValue bool
	var flags int
	// Was supposed to be unecessary for kernel 4.x.x
	flags = syscall.MNT_DETACH
	err := syscall.Unmount(path, flags)
	if err == nil {
		return true
	}
	returnValue = false
	return returnValue
}

// checkBootEntry is looking for grub.cfg file
// and return absolute path to it
func checkBootEntry(mountPoint string) ([]byte, string) {
	grub, err := ioutil.ReadFile(filepath.Join(mountPoint, "/boot/grub/grub.cfg"))
	if err == nil {
		return grub, filepath.Join(mountPoint, "/boot/grub")
	}
	return grub, ""

}

// getFileMenuContent is parsing a grub.cfg file
// input: absolute directory path to grub.cfg
// output: Return a list of strings with the following format
//	 line[3*x] - menuconfig
//	 line[3*x+1] - linux kernel + boot options
// 	 line[3*x+2] - initrd
// and the default boot entry configured into grub.cfg
func getFileMenuContent(file []byte) ([]string, int, error) {
	var returnValue []string
	var err error
	var status int
	var intReturn int
	intReturn = 0
	status = 0
	// When status = 0 we are looking for a menu entry
	// When status = 1 we are looking for a linux entry
	// When status = 2 we are looking for a initrd entry
	var trimmedLine string
	s := string(file)
	for _, line := range strings.Split(s, "\n") {
		trimmedLine = strings.TrimSpace(line)
		trimmedLine = strings.Join(strings.Fields(trimmedLine), " ")
		if strings.HasPrefix(trimmedLine, "#") {
			continue
		}
		if (strings.HasPrefix(trimmedLine, "set default=")) && (status == 0) {
			fmt.Sscanf(trimmedLine, "set default=\"%d\"", &intReturn)
		}
		if (strings.HasPrefix(trimmedLine, "menuentry ")) && (status == 0) {
			status = 1
			returnValue = append(returnValue, trimmedLine)
		}
		if (strings.HasPrefix(trimmedLine, "linux ")) && (status == 1) {
			status = 2
			returnValue = append(returnValue, trimmedLine)
		}
		if (strings.HasPrefix(trimmedLine, "initrd ")) && (status == 2) {
			status = 0
			returnValue = append(returnValue, trimmedLine)
		}
	}
	return returnValue, intReturn, err

}

func copyLocal(path string) (string, error) {
	var dest string
	var err error
	result := strings.Split(path, "/")
	for _, entry := range result {
		dest = entry
	}
	dest = "/tmp/" + dest
	srcFile, err := os.Open(path)
	if err != nil {
		return dest, err
	}

	destFile, err := os.Create(dest) // creates if file doesn't exist
	if err != nil {
		return dest, err
	}

	_, err = io.Copy(destFile, srcFile) // check first var for number of bytes copied
	if err != nil {
		return dest, err
	}

	err = destFile.Sync()
	if err != nil {
		return dest, err
	}
	err = destFile.Close()
	if err != nil {
		return dest, err
	}
	err = srcFile.Close()
	if err != nil {
		return dest, err
	}
	return dest, nil
}

// kexecEntry is booting new kernel based on the content of grub.cfg
func kexecEntry(grubConfPath string, grub []byte, mountPoint string) error {
	var fileMenuContent []string
	var entry int
	var localKernelPath string
	var localInitrdPath string
	verbose(grubConfPath)
	fileMenuContent, entry, err := getFileMenuContent(grub)
	if err != nil {
		return err
	}
	var kernel string
	var kernelParameter string
	var initrd string
	var kernelInfos []string
	kernelInfos = strings.Fields(fileMenuContent[3*entry+1])
	kernel = kernelInfos[1]
	var count int
	count = 0
	for _, field := range kernelInfos {
		if count > 1 {
			kernelParameter = kernelParameter + " " + field
		}
		count = count + 1
	}
	fmt.Sscanf(fileMenuContent[3*entry+2], "initrd %s", &initrd)
	if *v {
		log.Printf("************** boot parameters  ********************")
		log.Printf(kernel)
		log.Printf(kernelParameter)
		log.Printf(initrd)
		log.Printf("****************************************************")
	}
	localKernelPath, err = copyLocal(mountPoint + kernel)
	if err != nil {
		return err
	}
	localInitrdPath, err = copyLocal(mountPoint + initrd)
	if err != nil {
		return err
	}
	verbose(localKernelPath)

	umountEntry(mountPoint)
	// We can kexec the kernel with localKernelPath as kernel entry, kernelParameter as parameter and initrd as initrd !
	log.Printf("Loading %s for kernel\n", localKernelPath)

	kernelDesc, err := os.OpenFile(localKernelPath, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	// defer kernelDesc.Close()

	var ramfs *os.File
	ramfs, err = os.OpenFile(localInitrdPath, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	// defer ramfs.Close()

	if err := kexec.FileLoad(kernelDesc, ramfs, kernelParameter); err != nil {
		return err
	}
	err = ramfs.Close()
	if err != nil {
		return err
	}
	err = kernelDesc.Close()
	if err != nil {
		return err
	}
	if *dryrun {
		return nil
	}
	if err := kexec.Reboot(); err != nil {
		return err
	}
	return err

}

func main() {
	flag.Parse()

	if *v {
		verbose = log.Printf
	}
	fs, err := getSupportedFilesystem()
	if err != nil {
		log.Panic("No filesystem support found")
	}
	verbose("Supported filesystems: %v", fs)
	sysList, err := filepath.Glob(*devGlob)
	if err != nil {
		log.Panic("No available block devices to boot from")
	}
	// The Linux /sys file system is a bit, er, awkward. You can't find
	// the device special in there; just everything else.
	var blkList []string
	for _, b := range sysList {
		blkList = append(blkList, filepath.Join("/dev", filepath.Base(b)))
	}

	// We must validate if the MBR is bootable or not and keep the
	// devices which do have such support drive are easy to
	// detect.  This whole loop is pretty bogus at present, it
	// assumes the first partiton we find with grub.cfg is the one
	// we want. It works for now but ...
	var allparts []string
	for _, d := range blkList {
		err := checkForBootableMBR(d)
		if err != nil {
			// Not sure it matters; there can be many bogus entries?
			log.Printf("MBR for %s failed: %v", d, err)
			continue
		}
		verbose("Bootable device %v found", d)
		// You can't just look for numbers to match. Consider names like
		// mmcblk0, where has parts like mmcblk0p1. Just glob.
		g := d + "*"
		all, err := filepath.Glob(g)
		if err != nil {
			log.Printf("Glob for all partitions of %s failed: %v", g, err)
		}
		allparts = append(allparts, all...)
	}
	uroot, err = ioutil.TempDir("", "u-root-boot")
	if err != nil {
		log.Fatalf("Can't create tmpdir: %v", err)
	}
	verbose("Trying to boot from %v", allparts)
	for _, d := range allparts {
		if err := mountEntry(d, fs); err != nil {
			continue
		}
		verbose("mount succeed")
		u := filepath.Join(uroot, d)
		var grubContent, grubConfPath = checkBootEntry(u)
		if grubConfPath != "" {
			verbose("calling basic kexec")
			if err = kexecEntry(grubConfPath, grubContent, u); err != nil {
				log.Fatalf("kexec failed: %v", err)
			}
		}

		umountEntry(u)
	}
	log.Fatalf("Sorry no bootable device found")
}
<commit_msg>boot: change parsing code<commit_after>// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// boot allows to handover a system running linuxboot/u-root
// to a legacy preinstalled operating system by replacing the traditional
// bootloader path

//
// Synopsis:
//	boot [-dev][-v][-dryrun]
//
// Description:
//	If returns to u-root shell, the code didn't found a local bootable option
//      -dev glob to use; default is /sys/class/block/*
//      -v prints messages
//      -dryrun doesn't really boot
//
// Notes:
//	The code is looking for boot/grub/grub.cfg file as to identify the
//	boot option.
//	The first bootable device found in the block device tree is the one used
//	Windows is not supported (that is a work in progress)
//
// Example:
//	boot -v 	- Start the script in verbose mode for debugging purpose

package main

import (
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"github.com/u-root/u-root/pkg/kexec"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
)

const (
	bootableMBR     = 0xaa55
	signatureOffset = 510
)

type bootEntry struct {
	kernel  string
	initrd  string
	cmdline string
}

var (
	devGlob     = flag.String("dev", "/sys/block/*", "Glob for devices")
	v           = flag.Bool("v", false, "Print debug messages")
	verbose     = func(string, ...interface{}) {}
	dryrun      = flag.Bool("dryrun", true, "Boot")
	defaultBoot = flag.String("boot", "default", "Default entry to boot")
	uroot       string
)

// checkForBootableMBR is looking for bootable MBR signature
// Current support is limited to Hard disk devices and USB devices
func checkForBootableMBR(path string) error {
	var sig uint16
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	if err := binary.Read(io.NewSectionReader(f, signatureOffset, 2), binary.LittleEndian, &sig); err != nil {
		return err
	}
	if sig != bootableMBR {
		err := errors.New("Not a bootable device")
		return err
	}
	return nil
}

// getSupportedFilesystem returns all block file system supported by the linuxboot kernel
func getSupportedFilesystem() ([]string, error) {
	var err error
	fs, err := ioutil.ReadFile("/proc/filesystems")
	if err != nil {
		return nil, err
	}
	var returnValue []string
	for _, f := range strings.Split(string(fs), "\n") {
		n := strings.Fields(f)
		if len(n) != 1 {
			continue
		}
		returnValue = append(returnValue, n[0])
	}
	return returnValue, err

}

// mountEntry tries to mount a specific block device using a list of
// supported file systems. We have to try to mount the device
// itself, since devices can be filesystem formatted but not
// partitioned; and all its partitions.
func mountEntry(d string, supportedFilesystem []string) error {
	var err error
	verbose("Try to mount %v", d)

	// find or create the mountpoint.
	m := filepath.Join(uroot, d)
	if _, err = os.Stat(m); err != nil && os.IsNotExist(err) {
		err = os.MkdirAll(m, 0777)
	}
	if err != nil {
		return err
	}
	for _, filesystem := range supportedFilesystem {
		// Was supposed to be unecessary for kernel 4.x.x
		var flags = uintptr(syscall.MS_MGC_VAL)
		if err := syscall.Mount(d, m, filesystem, flags, ""); err == nil {
			return err
		}
	}
	return errors.New("Unable to mount any partition on " + d)
}

func umountEntry(path string) bool {
	var returnValue bool
	var flags int
	// Was supposed to be unecessary for kernel 4.x.x
	flags = syscall.MNT_DETACH
	err := syscall.Unmount(path, flags)
	if err == nil {
		return true
	}
	returnValue = false
	return returnValue
}

// checkBootEntry is looking for grub.cfg file
// and return absolute path to it
func checkBootEntry(mountPoint string) ([]byte, string) {
	grub, err := ioutil.ReadFile(filepath.Join(mountPoint, "/boot/grub/grub.cfg"))
	if err == nil {
		return grub, filepath.Join(mountPoint, "/boot/grub")
	}
	return grub, ""

}

// getFileMenuContent is parsing a grub.cfg file
// output: bootEntries
// grub parsing is a good deal messier than it looks.
// This is a simple parser will fail on anything tricky.
func getBootEntries(lines []byte) (map[string]*bootEntry, map[string]string, error) {
	// There are two ways to reference a bootEntry: its order in the file and its
	// name.
	var (
		lineno int
		line string
		err      error
		curEntry string
		numEntry int
		b        = make(map[string]*bootEntry)
		v        = make(map[string]string)
		f []string
		be *bootEntry
	)
	defer func() {
		switch err := recover().(type) {
		case nil:
		case error:
			log.Fatalf("Bummer: %v, line #%d, line %q, fields %q", err, lineno, line, f)
		default:
			log.Fatalf("unexpected panic value: %T(%v)", err, err)
		}
	}()

	verbose("getBootEntries: %s", string(lines))
	for _, line = range strings.Split(string(lines), "\n") {
		lineno++
		f = strings.Fields(line)
		verbose("%d: %q, %q", lineno, line, f)
		if len(f) == 0 {
			continue
		}
		switch f[0] {
		default:
		case "#":
		case "set":
			vals := strings.SplitN(f[1], "=", 2)
			if len(vals) == 2 {
				v[vals[0]] = vals[1]
			}
		case "menuentry":
			verbose("Menuentry %v", line)
			// nasty, but the alternatives are not much better.
			// grub config language is kind of arbitrary
			curEntry = fmt.Sprintf("\"%s\"", strconv.Itoa(numEntry))
			be = &bootEntry{}
			b[f[1]] = be
			b[curEntry] = be
			numEntry++
		case "linux":
			verbose("linux %v", line)
			be.kernel = f[1]
			be.cmdline = strings.Join(f[2:], " ")
		case "initrd":
			verbose("initrd %v", line)
			be.initrd = f[1]
		}
	}
	verbose("grub config menu decoded to [%q, %q, %v]", b, v, err)
	return b, v, err

}

func copyLocal(path string) (string, error) {
	var dest string
	var err error
	result := strings.Split(path, "/")
	for _, entry := range result {
		dest = entry
	}
	dest = "/tmp/" + dest
	srcFile, err := os.Open(path)
	if err != nil {
		return dest, err
	}

	destFile, err := os.Create(dest) // creates if file doesn't exist
	if err != nil {
		return dest, err
	}

	_, err = io.Copy(destFile, srcFile) // check first var for number of bytes copied
	if err != nil {
		return dest, err
	}

	err = destFile.Sync()
	if err != nil {
		return dest, err
	}
	err = destFile.Close()
	if err != nil {
		return dest, err
	}
	err = srcFile.Close()
	if err != nil {
		return dest, err
	}
	return dest, nil
}

// kexecEntry is booting new kernel based on the content of grub.cfg
func kexecEntry(grubConfPath string, grub []byte, mountPoint string) error {
	verbose(grubConfPath)
	b, v, err := getBootEntries(grub)
	if err != nil {
		return err
	}

	verbose("Boot Entries: %q", b)
	entry, ok := v[*defaultBoot]
	if ! ok {
		return fmt.Errorf("Entry %v not found in config file", *defaultBoot)
	}
	be, ok := b[entry]
	if ! ok {
		return fmt.Errorf("Entry %v not found in boot entries file", entry)
	}

	verbose("Boot params: %q", be)
	localKernelPath, err := copyLocal(filepath.Join(mountPoint, be.kernel))
	if err != nil {
		return err
	}
	localInitrdPath, err := copyLocal(filepath.Join(mountPoint, be.initrd))
	if err != nil {
		return err
	}
	verbose(localKernelPath)

	umountEntry(mountPoint)
	// We can kexec the kernel with localKernelPath as kernel entry, kernelParameter as parameter and initrd as initrd !
	log.Printf("Loading %s for kernel\n", localKernelPath)

	kernelDesc, err := os.OpenFile(localKernelPath, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	// defer kernelDesc.Close()

	var ramfs *os.File
	ramfs, err = os.OpenFile(localInitrdPath, os.O_RDONLY, 0)
	if err != nil {
		return err
	}
	// defer ramfs.Close()

	if err := kexec.FileLoad(kernelDesc, ramfs, be.cmdline); err != nil {
		return err
	}
	err = ramfs.Close()
	if err != nil {
		return err
	}
	err = kernelDesc.Close()
	if err != nil {
		return err
	}
	if *dryrun {
		return nil
	}
	if err := kexec.Reboot(); err != nil {
		return err
	}
	return err

}

func main() {
	flag.Parse()

	if *v {
		verbose = log.Printf
	}
	fs, err := getSupportedFilesystem()
	if err != nil {
		log.Panic("No filesystem support found")
	}
	verbose("Supported filesystems: %v", fs)
	sysList, err := filepath.Glob(*devGlob)
	if err != nil {
		log.Panic("No available block devices to boot from")
	}
	// The Linux /sys file system is a bit, er, awkward. You can't find
	// the device special in there; just everything else.
	var blkList []string
	for _, b := range sysList {
		blkList = append(blkList, filepath.Join("/dev", filepath.Base(b)))
	}

	// We must validate if the MBR is bootable or not and keep the
	// devices which do have such support drive are easy to
	// detect.  This whole loop is pretty bogus at present, it
	// assumes the first partiton we find with grub.cfg is the one
	// we want. It works for now but ...
	var allparts []string
	for _, d := range blkList {
		err := checkForBootableMBR(d)
		if err != nil {
			// Not sure it matters; there can be many bogus entries?
			log.Printf("MBR for %s failed: %v", d, err)
			continue
		}
		verbose("Bootable device %v found", d)
		// You can't just look for numbers to match. Consider names like
		// mmcblk0, where has parts like mmcblk0p1. Just glob.
		g := d + "*"
		all, err := filepath.Glob(g)
		if err != nil {
			log.Printf("Glob for all partitions of %s failed: %v", g, err)
		}
		allparts = append(allparts, all...)
	}
	uroot, err = ioutil.TempDir("", "u-root-boot")
	if err != nil {
		log.Fatalf("Can't create tmpdir: %v", err)
	}
	verbose("Trying to boot from %v", allparts)
	for _, d := range allparts {
		if err := mountEntry(d, fs); err != nil {
			continue
		}
		verbose("mount succeed")
		u := filepath.Join(uroot, d)
		var grubContent, grubConfPath = checkBootEntry(u)
		if grubConfPath != "" {
			verbose("calling basic kexec")
			if err = kexecEntry(grubConfPath, grubContent, u); err != nil {
				log.Fatalf("kexec failed: %v", err)
			}
		}

		umountEntry(u)
	}
	log.Fatalf("Sorry no bootable device found")
}
<|endoftext|>


<commit_before>package pgtest

import (
	stdsql "database/sql"
	"io/ioutil"
	"log"
	"math/rand"
	"net/url"
	"os"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/lib/pq"
	"golang.org/x/net/context"

	"chain/database/pg"
	"chain/database/sql"
	"chain/testutil"
)

var (
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
	dbpool pool
)

const DefaultURL = "postgres:///postgres?sslmode=disable"

var (
	DBURL      = os.Getenv("DB_URL_TEST")
	SchemaPath = os.Getenv("CHAIN") + "/api/appdb/schema.sql"
)

const (
	gcDur      = 3 * time.Minute
	timeFormat = "20060102150405"
)

type DB struct {
	URL     string // connection string of the form "postgres://..."
	DBName  string // randomized database name
	DB      *sql.DB
	BaseURL string
}

// NewContext calls Open using the background context,
// DBURL, and SchemaPath.
// It puts the resulting sql.DB into a context.
//
// It also registers a finalizer for the DB, so callers
// can discard it without closing it explicitly, and the
// test program is nevertheless unlikely to run out of
// connection slots in the server.
//
// Prefer NewTx whenever the caller can do its
// work in exactly one transaction.
func NewContext(t testing.TB) context.Context {
	ctx := context.Background()
	s, err := Open(ctx, DBURL, SchemaPath)
	if err != nil {
		t.Fatal(err)
	}
	runtime.SetFinalizer(s.DB, (*sql.DB).Close)
	ctx = pg.NewContext(ctx, s.DB)
	return ctx
}

// NewTx begins a new transaction on a database
// opened with Open, using DBURL and SchemaPath.
//
// It also registers a finalizer for the Tx, so callers
// can discard it without closing it explicitly, and the
// test program is nevertheless unlikely to run out of
// connection slots in the server.
func NewTx(t testing.TB) *sql.Tx {
	ctx := context.Background()
	db, err := dbpool.get(ctx, DBURL, SchemaPath)
	if err != nil {
		t.Fatal(err)
	}
	tx, err := db.DB.Begin(ctx)
	if err != nil {
		db.DB.Close()
		t.Fatal(err)
	}
	// NOTE(kr): we do not set a finalizer on the DB.
	// It is closed explicitly, if necessary, by finalizeTx.
	runtime.SetFinalizer(tx, db.finalizeTx)
	return tx
}

// baseURL should be a URL of the form "postgres://.../postgres?...".
// If it is the empty string, DefaultURL will be used.
// The database name component will be replaced with a random name,
// and the resulting URL will be in the returned DB.
func Open(ctx context.Context, baseURL, schemaFile string) (*DB, error) {
	if baseURL == "" {
		baseURL = DefaultURL
	}

	u, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}

	ctldb, err := stdsql.Open("postgres", baseURL)
	if err != nil {
		return nil, err
	}
	defer ctldb.Close()

	err = gcdbs(ctldb)
	if err != nil {
		log.Println(err)
	}

	dbname := pickDBName()
	u.Path = "/" + dbname
	_, err = ctldb.Exec("CREATE DATABASE " + pq.QuoteIdentifier(dbname))
	if err != nil {
		return nil, err
	}

	schema, err := ioutil.ReadFile(schemaFile)
	if err != nil {
		return nil, err
	}
	db, err := sql.Open("postgres", u.String())
	if err != nil {
		return nil, err
	}
	_, err = db.Exec(ctx, string(schema))
	if err != nil {
		db.Close()
		return nil, err
	}
	s := &DB{
		URL:     u.String(),
		DBName:  dbname,
		DB:      db,
		BaseURL: baseURL,
	}
	return s, nil
}

func (db *DB) finalizeTx(tx *sql.Tx) {
	ctx := context.Background()
	go func() { // don't block the finalizer goroutine for too long
		err := tx.Rollback(ctx)
		if err != nil {
			// If the tx has been committed (or if anything
			// else goes wrong), we can't reuse db.
			db.DB.Close()
			return
		}
		dbpool.put(db)
	}()
}

func gcdbs(db *stdsql.DB) error {
	gcTime := time.Now().Add(-gcDur)
	const q = `
		SELECT datname FROM pg_database
		WHERE datname LIKE 'pgtest_%' AND datname < $1
	`
	rows, err := db.Query(q, formatPrefix(gcTime))
	if err != nil {
		return err
	}
	var names []string
	for rows.Next() {
		var name string
		err = rows.Scan(&name)
		if err != nil {
			return err
		}
		names = append(names, name)
	}
	if rows.Err() != nil {
		return rows.Err()
	}
	for i, name := range names {
		if i > 5 {
			break // drop up to five databases per test
		}
		go db.Exec("DROP DATABASE " + pq.QuoteIdentifier(name))
	}
	return nil
}

// A pool contains initialized, pristine databases,
// as returned from Open. It is the client's job to
// make sure a database is in this state
// (for example, by rolling back a transaction)
// before returning it to the pool.
type pool struct {
	mu  sync.Mutex // protects dbs
	dbs []*DB
}

func (p *pool) get(ctx context.Context, url, path string) (*DB, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if len(p.dbs) > 0 {
		db := p.dbs[0]
		p.dbs = p.dbs[1:]
		return db, nil
	}

	return Open(ctx, url, path)
}

func (p *pool) put(db *DB) {
	p.mu.Lock()
	p.dbs = append(p.dbs, db)
	p.mu.Unlock()
}

func pickDBName() (s string) {
	const chars = "abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < 10; i++ {
		s += string(chars[random.Intn(len(chars))])
	}
	return formatPrefix(time.Now()) + s
}

func formatPrefix(t time.Time) string {
	return "pgtest_" + t.UTC().Format(timeFormat) + "Z_"
}

// Exec executes q in the database or transaction in ctx.
// If there is an error, it fails t.
func Exec(ctx context.Context, t testing.TB, q string) {
	_, err := pg.Exec(ctx, q)
	if err != nil {
		testutil.FatalErr(t, err)
	}
}
<commit_msg>database/pg/pgtest: warn when $CHAIN is unset<commit_after>package pgtest

import (
	stdsql "database/sql"
	"io/ioutil"
	"log"
	"math/rand"
	"net/url"
	"os"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/lib/pq"
	"golang.org/x/net/context"

	"chain/database/pg"
	"chain/database/sql"
	"chain/testutil"
)

var (
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
	dbpool pool
)

const DefaultURL = "postgres:///postgres?sslmode=disable"

var (
	DBURL      = os.Getenv("DB_URL_TEST")
	SchemaPath = os.Getenv("CHAIN") + "/api/appdb/schema.sql"
)

const (
	gcDur      = 3 * time.Minute
	timeFormat = "20060102150405"
)

type DB struct {
	URL     string // connection string of the form "postgres://..."
	DBName  string // randomized database name
	DB      *sql.DB
	BaseURL string
}

// NewContext calls Open using the background context,
// DBURL, and SchemaPath.
// It puts the resulting sql.DB into a context.
//
// It also registers a finalizer for the DB, so callers
// can discard it without closing it explicitly, and the
// test program is nevertheless unlikely to run out of
// connection slots in the server.
//
// Prefer NewTx whenever the caller can do its
// work in exactly one transaction.
func NewContext(t testing.TB) context.Context {
	ctx := context.Background()
	if os.Getenv("CHAIN") == "" {
		t.Log("warning: $CHAIN not set; probably can't find schema")
	}
	s, err := Open(ctx, DBURL, SchemaPath)
	if err != nil {
		t.Fatal(err)
	}
	runtime.SetFinalizer(s.DB, (*sql.DB).Close)
	ctx = pg.NewContext(ctx, s.DB)
	return ctx
}

// NewTx begins a new transaction on a database
// opened with Open, using DBURL and SchemaPath.
//
// It also registers a finalizer for the Tx, so callers
// can discard it without closing it explicitly, and the
// test program is nevertheless unlikely to run out of
// connection slots in the server.
func NewTx(t testing.TB) *sql.Tx {
	ctx := context.Background()
	if os.Getenv("CHAIN") == "" {
		t.Log("warning: $CHAIN not set; probably can't find schema")
	}
	db, err := dbpool.get(ctx, DBURL, SchemaPath)
	if err != nil {
		t.Fatal(err)
	}
	tx, err := db.DB.Begin(ctx)
	if err != nil {
		db.DB.Close()
		t.Fatal(err)
	}
	// NOTE(kr): we do not set a finalizer on the DB.
	// It is closed explicitly, if necessary, by finalizeTx.
	runtime.SetFinalizer(tx, db.finalizeTx)
	return tx
}

// baseURL should be a URL of the form "postgres://.../postgres?...".
// If it is the empty string, DefaultURL will be used.
// The database name component will be replaced with a random name,
// and the resulting URL will be in the returned DB.
func Open(ctx context.Context, baseURL, schemaFile string) (*DB, error) {
	if baseURL == "" {
		baseURL = DefaultURL
	}

	u, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}

	ctldb, err := stdsql.Open("postgres", baseURL)
	if err != nil {
		return nil, err
	}
	defer ctldb.Close()

	err = gcdbs(ctldb)
	if err != nil {
		log.Println(err)
	}

	dbname := pickDBName()
	u.Path = "/" + dbname
	_, err = ctldb.Exec("CREATE DATABASE " + pq.QuoteIdentifier(dbname))
	if err != nil {
		return nil, err
	}

	schema, err := ioutil.ReadFile(schemaFile)
	if err != nil {
		return nil, err
	}
	db, err := sql.Open("postgres", u.String())
	if err != nil {
		return nil, err
	}
	_, err = db.Exec(ctx, string(schema))
	if err != nil {
		db.Close()
		return nil, err
	}
	s := &DB{
		URL:     u.String(),
		DBName:  dbname,
		DB:      db,
		BaseURL: baseURL,
	}
	return s, nil
}

func (db *DB) finalizeTx(tx *sql.Tx) {
	ctx := context.Background()
	go func() { // don't block the finalizer goroutine for too long
		err := tx.Rollback(ctx)
		if err != nil {
			// If the tx has been committed (or if anything
			// else goes wrong), we can't reuse db.
			db.DB.Close()
			return
		}
		dbpool.put(db)
	}()
}

func gcdbs(db *stdsql.DB) error {
	gcTime := time.Now().Add(-gcDur)
	const q = `
		SELECT datname FROM pg_database
		WHERE datname LIKE 'pgtest_%' AND datname < $1
	`
	rows, err := db.Query(q, formatPrefix(gcTime))
	if err != nil {
		return err
	}
	var names []string
	for rows.Next() {
		var name string
		err = rows.Scan(&name)
		if err != nil {
			return err
		}
		names = append(names, name)
	}
	if rows.Err() != nil {
		return rows.Err()
	}
	for i, name := range names {
		if i > 5 {
			break // drop up to five databases per test
		}
		go db.Exec("DROP DATABASE " + pq.QuoteIdentifier(name))
	}
	return nil
}

// A pool contains initialized, pristine databases,
// as returned from Open. It is the client's job to
// make sure a database is in this state
// (for example, by rolling back a transaction)
// before returning it to the pool.
type pool struct {
	mu  sync.Mutex // protects dbs
	dbs []*DB
}

func (p *pool) get(ctx context.Context, url, path string) (*DB, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if len(p.dbs) > 0 {
		db := p.dbs[0]
		p.dbs = p.dbs[1:]
		return db, nil
	}

	return Open(ctx, url, path)
}

func (p *pool) put(db *DB) {
	p.mu.Lock()
	p.dbs = append(p.dbs, db)
	p.mu.Unlock()
}

func pickDBName() (s string) {
	const chars = "abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < 10; i++ {
		s += string(chars[random.Intn(len(chars))])
	}
	return formatPrefix(time.Now()) + s
}

func formatPrefix(t time.Time) string {
	return "pgtest_" + t.UTC().Format(timeFormat) + "Z_"
}

// Exec executes q in the database or transaction in ctx.
// If there is an error, it fails t.
func Exec(ctx context.Context, t testing.TB, q string) {
	_, err := pg.Exec(ctx, q)
	if err != nil {
		testutil.FatalErr(t, err)
	}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2017
// Mainflux
// Cavium
//
// SPDX-License-Identifier: Apache-2.0
//

package client

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/drasko/edgex-export"
	"github.com/drasko/edgex-export/mongo"
	"github.com/go-zoo/bone"
	"go.uber.org/zap"
	"gopkg.in/mgo.v2/bson"
)

func getRegByID(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	id := bone.GetValue(r, "id")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	reg := export.Registration{}
	if err := c.Find(bson.M{"id": id}).One(&reg); err != nil {
		logger.Error("Failed to query by id", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	res, err := json.Marshal(reg)
	if err != nil {
		logger.Error("Failed to query by id", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, string(res))
}

func getRegList(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	t := bone.GetValue(r, "type")

	var l string

	switch t {
	case "algorithms":
		l = `["None","Aes"]`
	case "compressions":
		l = `["None","Gzip","Zip"]`
	case "formats":
		l = `["JSON","XML","Serialized","IotCoreJSON","AzureJSON","CSV"]`
	case "destinations":
		l = `["DestMQTT", "TeDestZMQller", "DestIotCoreMQTT,
			"DestAzureMQTT", "DestRest"]`
	default:
		logger.Error("Unknown type: " + t)
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, "Unknown type: "+t)
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, l)
}

func getAllReg(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	reg := []export.Registration{}
	if err := c.Find(nil).All(&reg); err != nil {
		logger.Error("Failed to query all registrations", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	res, err := json.Marshal(reg)
	if err != nil {
		logger.Error("Failed to query all registrations", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, string(res))
}

func getRegByName(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	name := bone.GetValue(r, "name")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	reg := export.Registration{}
	if err := c.Find(bson.M{"name": name}).One(&reg); err != nil {
		logger.Error("Failed to query by name", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	res, err := json.Marshal(reg)
	if err != nil {
		logger.Error("Failed to query by name", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, string(res))
}

func addReg(w http.ResponseWriter, r *http.Request) {
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, err.Error())
		return
	}

	reg := export.Registration{}
	if err := json.Unmarshal(data, &reg); err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, err.Error())
		return
	}

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	count, err := c.Find(bson.M{"name": reg.Name}).Count()
	if err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}
	if count != 0 {
		logger.Error("Username already taken: " + reg.Name)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if err := c.Insert(reg); err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusCreated)
	notifyUpdatedRegistrations()
}

func updateReg(w http.ResponseWriter, r *http.Request) {
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		logger.Error("Failed to query update registration", zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, err.Error())
		return
	}

	var body map[string]interface{}
	if err := json.Unmarshal(data, &body); err != nil {
		logger.Error("Failed to query update registration", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
	}

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	name := body["name"]
	query := bson.M{"name": name}
	update := bson.M{"$set": body}

	if err := c.Update(query, update); err != nil {
		logger.Error("Failed to query update registration", zap.Error(err))
		w.WriteHeader(http.StatusNotFound)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	notifyUpdatedRegistrations()
}

func delRegByID(w http.ResponseWriter, r *http.Request) {
	id := bone.GetValue(r, "id")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	if err := c.Remove(bson.M{"id": id}); err != nil {
		logger.Error("Failed to query by id", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	notifyUpdatedRegistrations()
}

func delRegByName(w http.ResponseWriter, r *http.Request) {
	name := bone.GetValue(r, "name")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	if err := c.Remove(bson.M{"name": name}); err != nil {
		logger.Error("Failed to query by name", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	notifyUpdatedRegistrations()
}

func notifyUpdatedRegistrations() {
	go func() {
		// TODO make configurable distro host/port
		http.Get("http://127.0.0.1:48070/api/v1/notify/registrations")
	}()
}
<commit_msg>Changing get to put<commit_after>//
// Copyright (c) 2017
// Mainflux
// Cavium
//
// SPDX-License-Identifier: Apache-2.0
//

package client

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/drasko/edgex-export"
	"github.com/drasko/edgex-export/mongo"
	"github.com/go-zoo/bone"
	"go.uber.org/zap"
	"gopkg.in/mgo.v2/bson"
)

func getRegByID(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	id := bone.GetValue(r, "id")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	reg := export.Registration{}
	if err := c.Find(bson.M{"id": id}).One(&reg); err != nil {
		logger.Error("Failed to query by id", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	res, err := json.Marshal(reg)
	if err != nil {
		logger.Error("Failed to query by id", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, string(res))
}

func getRegList(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	t := bone.GetValue(r, "type")

	var l string

	switch t {
	case "algorithms":
		l = `["None","Aes"]`
	case "compressions":
		l = `["None","Gzip","Zip"]`
	case "formats":
		l = `["JSON","XML","Serialized","IotCoreJSON","AzureJSON","CSV"]`
	case "destinations":
		l = `["DestMQTT", "TeDestZMQller", "DestIotCoreMQTT,
			"DestAzureMQTT", "DestRest"]`
	default:
		logger.Error("Unknown type: " + t)
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, "Unknown type: "+t)
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, l)
}

func getAllReg(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	reg := []export.Registration{}
	if err := c.Find(nil).All(&reg); err != nil {
		logger.Error("Failed to query all registrations", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	res, err := json.Marshal(reg)
	if err != nil {
		logger.Error("Failed to query all registrations", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, string(res))
}

func getRegByName(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	name := bone.GetValue(r, "name")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	reg := export.Registration{}
	if err := c.Find(bson.M{"name": name}).One(&reg); err != nil {
		logger.Error("Failed to query by name", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	res, err := json.Marshal(reg)
	if err != nil {
		logger.Error("Failed to query by name", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	io.WriteString(w, string(res))
}

func addReg(w http.ResponseWriter, r *http.Request) {
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, err.Error())
		return
	}

	reg := export.Registration{}
	if err := json.Unmarshal(data, &reg); err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, err.Error())
		return
	}

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	count, err := c.Find(bson.M{"name": reg.Name}).Count()
	if err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}
	if count != 0 {
		logger.Error("Username already taken: " + reg.Name)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if err := c.Insert(reg); err != nil {
		logger.Error("Failed to query add registration", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusCreated)
	notifyUpdatedRegistrations()
}

func updateReg(w http.ResponseWriter, r *http.Request) {
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		logger.Error("Failed to query update registration", zap.Error(err))
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, err.Error())
		return
	}

	var body map[string]interface{}
	if err := json.Unmarshal(data, &body); err != nil {
		logger.Error("Failed to query update registration", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
	}

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	name := body["name"]
	query := bson.M{"name": name}
	update := bson.M{"$set": body}

	if err := c.Update(query, update); err != nil {
		logger.Error("Failed to query update registration", zap.Error(err))
		w.WriteHeader(http.StatusNotFound)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	notifyUpdatedRegistrations()
}

func delRegByID(w http.ResponseWriter, r *http.Request) {
	id := bone.GetValue(r, "id")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	if err := c.Remove(bson.M{"id": id}); err != nil {
		logger.Error("Failed to query by id", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	notifyUpdatedRegistrations()
}

func delRegByName(w http.ResponseWriter, r *http.Request) {
	name := bone.GetValue(r, "name")

	s := repo.Session.Copy()
	defer s.Close()
	c := s.DB(mongo.DBName).C(mongo.CollectionName)

	if err := c.Remove(bson.M{"name": name}); err != nil {
		logger.Error("Failed to query by name", zap.Error(err))
		w.WriteHeader(http.StatusInternalServerError)
		io.WriteString(w, err.Error())
		return
	}

	w.WriteHeader(http.StatusOK)
	notifyUpdatedRegistrations()
}

func notifyUpdatedRegistrations() {
	go func() {
		// TODO make configurable distro host/port
		client := &http.Client{}
		req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:48070/api/v1/notify/registrations", nil)
		if err != nil {
			logger.Info("Error creating http request")
			return
		}
		_, err = client.Do(req)
		if err != nil {
			logger.Info("Error notifying updated registrations to distro")
		}
	}()
}
<|endoftext|>


<commit_before>package erro

import (
	"fmt"
	"runtime"
)

// デバッグ用エラー。
type Tracer struct {
	cause error
	trace string
}

func (err *Tracer) Error() string {
	return err.cause.Error() + "\n" +
		"--------------------------------------------------\n" +
		err.trace
}

func (err *Tracer) Cause() error {
	return err.cause
}

const traceLen = 8192

// スタックトレースを付加する。
// nil をそのまま返すので、 return Wrap(func() error) みたいな使い方ができる。
// 囲うのは最初だけでも良いし、既に Wrap されている場合はそのまま返すので、
// 最初かどうか分からなければ、毎回囲っても良い。
// スタックトレースの先頭はこの関数になるが、気にしない。
func Wrap(err error) error {
	if err == nil {
		return nil
	} else if _, ok := err.(*Tracer); ok {
		return err
	}

	buff := make([]byte, traceLen)
	len := runtime.Stack(buff, false)

	for len > 0 && buff[len-1] == '\n' {
		len--
	}

	return &Tracer{err, string(buff[:len])}
}

type Error string

func (err Error) Error() string {
	return string(err)
}

// 引数から 1 つの文字列をつくって最初のエラーにして、それを Wrap して返す。
// 文字列の成形は fmt.Print() 形式。
func New(a ...interface{}) error {
	return Wrap(Error(fmt.Sprint(a...)))
}
<commit_msg>表示の調整<commit_after>package erro

import (
	"fmt"
	"runtime"
)

// デバッグ用エラー。
type Tracer struct {
	cause error
	trace string
}

func (err *Tracer) Error() string {
	return err.cause.Error() + "\n" +
		//"--------------------------------------------------\n" +
		err.trace
}

func (err *Tracer) Cause() error {
	return err.cause
}

const traceLen = 8192

// スタックトレースを付加する。
// nil をそのまま返すので、 return Wrap(func() error) みたいな使い方ができる。
// 囲うのは最初だけでも良いし、既に Wrap されている場合はそのまま返すので、
// 最初かどうか分からなければ、毎回囲っても良い。
// スタックトレースの先頭はこの関数になるが、気にしない。
func Wrap(err error) error {
	if err == nil {
		return nil
	} else if _, ok := err.(*Tracer); ok {
		return err
	}

	buff := make([]byte, traceLen)
	len := runtime.Stack(buff, false)

	for len > 0 && buff[len-1] == '\n' {
		len--
	}

	return &Tracer{err, string(buff[:len])}
}

type Error string

func (err Error) Error() string {
	return string(err)
}

// 引数から 1 つの文字列をつくって最初のエラーにして、それを Wrap して返す。
// 文字列の成形は fmt.Print() 形式。
func New(a ...interface{}) error {
	return Wrap(Error(fmt.Sprint(a...)))
}
<|endoftext|>


<commit_before>package backoff

import (
	"math/rand"
	"time"
)

/*
ExponentialBackoff is an implementation of BackOff that increases the back off
period for each retry attempt using a randomization function that grows exponentially.

NextBackOff() is calculated using the following formula:

	randomized_interval =
	    retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])

In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval. For example, using 2 seconds as the base retry
interval and 0.5 as the randomization factor, the actual back off period used in the next retry
attempt will be between 1 and 3 seconds.

Note: max_interval caps the retry_interval and not the randomized_interval.

If the time elapsed since an ExponentialBackOff instance is created goes past the
max_elapsed_time then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().

Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, default
multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence will be
(values in seconds) and assuming we go over the max_elapsed_time on the 10th try:

	request#     retry_interval     randomized_interval

	1             0.5                [0.25,   0.75]
	2             0.75               [0.375,  1.125]
	3             1.125              [0.562,  1.687]
	4             1.687              [0.8435, 2.53]
	5             2.53               [1.265,  3.795]
	6             3.795              [1.897,  5.692]
	7             5.692              [2.846,  8.538]
	8             8.538              [4.269, 12.807]
	9            12.807              [6.403, 19.210]
	10           19.210              backoff.Stop

Implementation is not thread-safe.
*/
type ExponentialBackoff struct {
	InitialInterval     time.Duration
	RandomizationFactor float64
	Multiplier          float64
	MaxInterval         time.Duration
	MaxElapsedTime      time.Duration
	Clock               Clock

	currentInterval time.Duration
	startTime       time.Time
}

type Clock interface {
	Now() time.Time
}

// Default values for ExponentialBackoff.
const (
	DefaultInitialInterval     = time.Duration(500 * time.Millisecond)
	DefaultRandomizationFactor = 0.5
	DefaultMultiplier          = 1.5
	DefaultMaxInterval         = time.Duration(60 * time.Second)
	DefaultMaxElapsedTime      = time.Duration(15 * time.Minute)
)

// NewExponentialBackoff creates an instance of ExponentialBackoff using default values.
func NewExponentialBackoff() *ExponentialBackoff {
	return &ExponentialBackoff{
		InitialInterval:     DefaultInitialInterval,
		RandomizationFactor: DefaultRandomizationFactor,
		Multiplier:          DefaultMultiplier,
		MaxInterval:         DefaultMaxInterval,
		MaxElapsedTime:      DefaultMaxElapsedTime,
		Clock:               systemClock{},
	}
}

type systemClock struct{}

func (t systemClock) Now() time.Time {
	return time.Now()
}

// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackoff) Reset() {
	b.currentInterval = b.InitialInterval
	b.startTime = b.Clock.Now()
}

// NextBackOff calculates the next back off interval using the formula:
// 	randomized_interval = retry_interval +/- (randomization_factor * retry_interval)
func (b *ExponentialBackoff) NextBackOff() time.Duration {
	// Make sure we have not gone over the maximum elapsed time.
	if b.GetElapsedTime() > b.MaxElapsedTime {
		return Stop
	}
	var randomizedInterval time.Duration = getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
	b.incrementCurrentInterval()
	return randomizedInterval
}

// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano().
func (b *ExponentialBackoff) GetElapsedTime() time.Duration {
	return b.Clock.Now().Sub(b.startTime)
}

// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackoff) incrementCurrentInterval() {
	// Check for overflow, if overflow is detected set the current interval to the max interval.
	if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
		b.currentInterval = b.MaxInterval
	} else {
		b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
	}
}

// Returns a random value from the interval:
// 	[randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
	var delta float64 = randomizationFactor * float64(currentInterval)
	var minInterval float64 = float64(currentInterval) - delta
	var maxInterval float64 = float64(currentInterval) + delta
	// Get a random value from the range [minInterval, maxInterval].
	// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
	// we want a 33% chance for selecting either 1, 2 or 3.
	var randomValue time.Duration = time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
	return randomValue
}
<commit_msg>use defer<commit_after>package backoff

import (
	"math/rand"
	"time"
)

/*
ExponentialBackoff is an implementation of BackOff that increases the back off
period for each retry attempt using a randomization function that grows exponentially.

NextBackOff() is calculated using the following formula:

	randomized_interval =
	    retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])

In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval. For example, using 2 seconds as the base retry
interval and 0.5 as the randomization factor, the actual back off period used in the next retry
attempt will be between 1 and 3 seconds.

Note: max_interval caps the retry_interval and not the randomized_interval.

If the time elapsed since an ExponentialBackOff instance is created goes past the
max_elapsed_time then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().

Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5, default
multiplier is 1.5 and the default max_interval is 1 minute. For 10 tries the sequence will be
(values in seconds) and assuming we go over the max_elapsed_time on the 10th try:

	request#     retry_interval     randomized_interval

	1             0.5                [0.25,   0.75]
	2             0.75               [0.375,  1.125]
	3             1.125              [0.562,  1.687]
	4             1.687              [0.8435, 2.53]
	5             2.53               [1.265,  3.795]
	6             3.795              [1.897,  5.692]
	7             5.692              [2.846,  8.538]
	8             8.538              [4.269, 12.807]
	9            12.807              [6.403, 19.210]
	10           19.210              backoff.Stop

Implementation is not thread-safe.
*/
type ExponentialBackoff struct {
	InitialInterval     time.Duration
	RandomizationFactor float64
	Multiplier          float64
	MaxInterval         time.Duration
	MaxElapsedTime      time.Duration
	Clock               Clock

	currentInterval time.Duration
	startTime       time.Time
}

type Clock interface {
	Now() time.Time
}

// Default values for ExponentialBackoff.
const (
	DefaultInitialInterval     = time.Duration(500 * time.Millisecond)
	DefaultRandomizationFactor = 0.5
	DefaultMultiplier          = 1.5
	DefaultMaxInterval         = time.Duration(60 * time.Second)
	DefaultMaxElapsedTime      = time.Duration(15 * time.Minute)
)

// NewExponentialBackoff creates an instance of ExponentialBackoff using default values.
func NewExponentialBackoff() *ExponentialBackoff {
	return &ExponentialBackoff{
		InitialInterval:     DefaultInitialInterval,
		RandomizationFactor: DefaultRandomizationFactor,
		Multiplier:          DefaultMultiplier,
		MaxInterval:         DefaultMaxInterval,
		MaxElapsedTime:      DefaultMaxElapsedTime,
		Clock:               systemClock{},
	}
}

type systemClock struct{}

func (t systemClock) Now() time.Time {
	return time.Now()
}

// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackoff) Reset() {
	b.currentInterval = b.InitialInterval
	b.startTime = b.Clock.Now()
}

// NextBackOff calculates the next back off interval using the formula:
// 	randomized_interval = retry_interval +/- (randomization_factor * retry_interval)
func (b *ExponentialBackoff) NextBackOff() time.Duration {
	// Make sure we have not gone over the maximum elapsed time.
	if b.GetElapsedTime() > b.MaxElapsedTime {
		return Stop
	}
	defer b.incrementCurrentInterval()
	return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
}

// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano().
func (b *ExponentialBackoff) GetElapsedTime() time.Duration {
	return b.Clock.Now().Sub(b.startTime)
}

// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackoff) incrementCurrentInterval() {
	// Check for overflow, if overflow is detected set the current interval to the max interval.
	if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
		b.currentInterval = b.MaxInterval
	} else {
		b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
	}
}

// Returns a random value from the interval:
// 	[randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
	var delta float64 = randomizationFactor * float64(currentInterval)
	var minInterval float64 = float64(currentInterval) - delta
	var maxInterval float64 = float64(currentInterval) + delta
	// Get a random value from the range [minInterval, maxInterval].
	// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
	// we want a 33% chance for selecting either 1, 2 or 3.
	var randomValue time.Duration = time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
	return randomValue
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gitchander/permutation"
	"github.com/jyggen/advent-of-go/intcode"
	"github.com/jyggen/advent-of-go/solver"
	"github.com/jyggen/advent-of-go/utils"
	"os"
	"strconv"
)

func main() {
	p1, p2, err := solver.SolveFromFile(os.Stdin, SolvePart1, SolvePart2)

	if err != nil {
		panic(err)
	}

	fmt.Println(p1)
	fmt.Println(p2)
}

func SolvePart1(input string) (string, error) {
	instructions, err := utils.ToIntegerSlice(input, ",")

	if err != nil {
		return "", err
	}

	best := 0
	sequence := []int{0, 1, 2, 3, 4}
	p := permutation.New(permutation.IntSlice(sequence))

	for p.Next() {
		in := 0

		for i := 0; i < 5; i++ {
			pc := intcode.NewComputer(instructions)
			pcInput := make(chan int, 1)
			pcOutput := make(chan int, 1)

			go pc.Execute(pcInput, pcOutput)

			pcInput <- sequence[i]
			pcInput <- in
			close(pcInput)
			in = <-pcOutput
		}

		if in > best {
			best = in
		}
	}

	return strconv.Itoa(best), err
}

type metadata struct {
	pc     *intcode.Computer
	input  chan int
	output chan int
}

func SolvePart2(input string) (string, error) {
	instructions, err := utils.ToIntegerSlice(input, ",")

	if err != nil {
		return "", err
	}

	best := 0
	sequence := []int{5, 6, 7, 8, 9}
	p := permutation.New(permutation.IntSlice(sequence))

	for p.Next() {
		pcs := make([]*metadata, 5)

		for i := 0; i < 5; i++ {
			pcs[i] = &metadata{
				intcode.NewComputer(instructions),
				make(chan int, 1),
				make(chan int, 1),
			}
		}

		for i := 0; i < 5; i++ {
			pcs[i] = &metadata{
				intcode.NewComputer(instructions),
				pcs[(5+i-1)%5].output,
				pcs[(i+1)%5].input,
			}
		}

		pcs[4].output = make(chan int, 1)

		for i, pc := range pcs {
			go pc.pc.Execute(pc.input, pc.output)

			pc.input <- sequence[i]
		}

		pcs[0].input <- 0

		for output := range pcs[4].output {
			pcs[0].input <- output

			if output > best {
				best = output
			}
		}
	}

	return strconv.Itoa(best), err
}
<commit_msg>Optimize 2019.07<commit_after>package main

import (
	"fmt"
	"github.com/gitchander/permutation"
	"github.com/jyggen/advent-of-go/intcode"
	"github.com/jyggen/advent-of-go/solver"
	"github.com/jyggen/advent-of-go/utils"
	"os"
	"strconv"
)

func main() {
	p1, p2, err := solver.SolveFromFile(os.Stdin, SolvePart1, SolvePart2)

	if err != nil {
		panic(err)
	}

	fmt.Println(p1)
	fmt.Println(p2)
}

func SolvePart1(input string) (string, error) {
	instructions, err := utils.ToIntegerSlice(input, ",")

	if err != nil {
		return "", err
	}

	best := 0
	sequence := []int{0, 1, 2, 3, 4}
	p := permutation.New(permutation.IntSlice(sequence))

	for p.Next() {
		in := 0

		for i := 0; i < 5; i++ {
			pc := intcode.NewComputer(instructions)
			pcInput := make(chan int, 1)
			pcOutput := make(chan int, 1)

			go pc.Execute(pcInput, pcOutput)

			pcInput <- sequence[i]
			pcInput <- in
			close(pcInput)
			in = <-pcOutput
		}

		if in > best {
			best = in
		}
	}

	return strconv.Itoa(best), err
}

type metadata struct {
	pc     *intcode.Computer
	input  chan int
	output chan int
}

func SolvePart2(input string) (string, error) {
	instructions, err := utils.ToIntegerSlice(input, ",")

	if err != nil {
		return "", err
	}

	best := 0
	sequence := []int{5, 6, 7, 8, 9}
	p := permutation.New(permutation.IntSlice(sequence))

	for p.Next() {
		pcs := make([]*metadata, 5)

		for i := 0; i < 5; i++ {
			pcs[i] = &metadata{
				intcode.NewComputer(instructions),
				make(chan int, 1),
				make(chan int, 1),
			}
		}

		for i := 0; i < 5; i++ {
			pcs[i].input = pcs[(5+i-1)%5].output
			pcs[i].output = pcs[(i+1)%5].input
		}

		pcs[4].output = make(chan int, 1)

		for i, pc := range pcs {
			go pc.pc.Execute(pc.input, pc.output)

			pc.input <- sequence[i]
		}

		pcs[0].input <- 0

		for output := range pcs[4].output {
			pcs[0].input <- output

			if output > best {
				best = output
			}
		}
	}

	return strconv.Itoa(best), err
}
<|endoftext|>


<commit_before>package pattern

import (
	"fmt"
	"go/token"
	"reflect"
	"strings"
)

var (
	_ Node = Ellipsis{}
	_ Node = Binding{}
	_ Node = RangeStmt{}
	_ Node = AssignStmt{}
	_ Node = IndexExpr{}
	_ Node = Ident{}
	_ Node = Builtin{}
	_ Node = String("")
	_ Node = Any{}
	_ Node = ValueSpec{}
	_ Node = List{}
	_ Node = GenDecl{}
	_ Node = BinaryExpr{}
	_ Node = ForStmt{}
	_ Node = ArrayType{}
	_ Node = DeferStmt{}
	_ Node = MapType{}
	_ Node = ReturnStmt{}
	_ Node = SliceExpr{}
	_ Node = StarExpr{}
	_ Node = UnaryExpr{}
	_ Node = SendStmt{}
	_ Node = SelectStmt{}
	_ Node = ImportSpec{}
	_ Node = IfStmt{}
	_ Node = GoStmt{}
	_ Node = Field{}
	_ Node = SelectorExpr{}
	_ Node = StructType{}
	_ Node = KeyValueExpr{}
	_ Node = FuncType{}
	_ Node = FuncLit{}
	_ Node = FuncDecl{}
	_ Node = Token(0)
	_ Node = ChanType{}
	_ Node = CallExpr{}
	_ Node = CaseClause{}
	_ Node = CommClause{}
	_ Node = CompositeLit{}
	_ Node = EmptyStmt{}
	_ Node = SwitchStmt{}
	_ Node = TypeSwitchStmt{}
	_ Node = TypeAssertExpr{}
	_ Node = TypeSpec{}
	_ Node = InterfaceType{}
	_ Node = BranchStmt{}
	_ Node = IncDecStmt{}
	_ Node = BasicLit{}
	_ Node = Nil{}
	_ Node = Object{}
	_ Node = Function{}
	_ Node = Not{}
	_ Node = Or{}
)

type Function struct {
	Name Node
}

type Token token.Token

type Nil struct {
}

type Ellipsis struct {
	Elt Node
}

type IncDecStmt struct {
	X   Node
	Tok Node
}

type BranchStmt struct {
	Tok   Node
	Label Node
}

type InterfaceType struct {
	Methods Node
}

type TypeSpec struct {
	Name Node
	Type Node
}

type TypeAssertExpr struct {
	X    Node
	Type Node
}

type TypeSwitchStmt struct {
	Init   Node
	Assign Node
	Body   Node
}

type SwitchStmt struct {
	Init Node
	Tag  Node
	Body Node
}

type EmptyStmt struct {
}

type CompositeLit struct {
	Type Node
	Elts Node
}

type CommClause struct {
	Comm Node
	Body Node
}

type CaseClause struct {
	List Node
	Body Node
}

type CallExpr struct {
	Fun  Node
	Args Node
	// XXX handle ellipsis
}

// TODO(dh): add a ChanDir node, and a way of instantiating it.

type ChanType struct {
	Dir   Node
	Value Node
}

type FuncDecl struct {
	Recv Node
	Name Node
	Type Node
	Body Node
}

type FuncLit struct {
	Type Node
	Body Node
}

type FuncType struct {
	Params  Node
	Results Node
}

type KeyValueExpr struct {
	Key   Node
	Value Node
}

type StructType struct {
	Fields Node
}

type SelectorExpr struct {
	X   Node
	Sel Node
}

type Field struct {
	Names Node
	Type  Node
	Tag   Node
}

type GoStmt struct {
	Call Node
}

type IfStmt struct {
	Init Node
	Cond Node
	Body Node
	Else Node
}

type ImportSpec struct {
	Name Node
	Path Node
}

type SelectStmt struct {
	Body Node
}

type ArrayType struct {
	Len Node
	Elt Node
}

type DeferStmt struct {
	Call Node
}

type MapType struct {
	Key   Node
	Value Node
}

type ReturnStmt struct {
	Results Node
}

type SliceExpr struct {
	X    Node
	Low  Node
	High Node
	Max  Node
}

type StarExpr struct {
	X Node
}

type UnaryExpr struct {
	Op Node
	X  Node
}

type SendStmt struct {
	Chan  Node
	Value Node
}

type Binding struct {
	Name string
	Node Node
}

type RangeStmt struct {
	Key   Node
	Value Node
	Tok   Node
	X     Node
	Body  Node
}

type AssignStmt struct {
	Lhs Node
	Tok Node
	Rhs Node
}

type IndexExpr struct {
	X     Node
	Index Node
}

type Node interface {
	String() string
	isNode()
}

type Ident struct {
	Name Node
}

type Object struct {
	Name Node
}

type Builtin struct {
	Name Node
}

type String string

type Any struct{}

type ValueSpec struct {
	Names  Node
	Type   Node
	Values Node
}

type List struct {
	Head Node
	Tail Node
}

type GenDecl struct {
	Tok   Node
	Specs Node
}

type BasicLit struct {
	Kind  Node
	Value Node
}

type BinaryExpr struct {
	X  Node
	Op Node
	Y  Node
}

type ForStmt struct {
	Init Node
	Cond Node
	Post Node
	Body Node
}

type Or struct {
	Nodes []Node
}

type Not struct {
	Node Node
}

func stringify(n Node) string {
	v := reflect.ValueOf(n)
	var parts []string
	parts = append(parts, v.Type().Name())
	for i := 0; i < v.NumField(); i++ {
		parts = append(parts, fmt.Sprintf("%s", v.Field(i)))
	}
	return "(" + strings.Join(parts, " ") + ")"
}

func (stmt AssignStmt) String() string     { return stringify(stmt) }
func (expr IndexExpr) String() string      { return stringify(expr) }
func (id Ident) String() string            { return stringify(id) }
func (spec ValueSpec) String() string      { return stringify(spec) }
func (decl GenDecl) String() string        { return stringify(decl) }
func (lit BasicLit) String() string        { return stringify(lit) }
func (expr BinaryExpr) String() string     { return stringify(expr) }
func (stmt ForStmt) String() string        { return stringify(stmt) }
func (stmt RangeStmt) String() string      { return stringify(stmt) }
func (typ ArrayType) String() string       { return stringify(typ) }
func (stmt DeferStmt) String() string      { return stringify(stmt) }
func (typ MapType) String() string         { return stringify(typ) }
func (stmt ReturnStmt) String() string     { return stringify(stmt) }
func (expr SliceExpr) String() string      { return stringify(expr) }
func (expr StarExpr) String() string       { return stringify(expr) }
func (expr UnaryExpr) String() string      { return stringify(expr) }
func (stmt SendStmt) String() string       { return stringify(stmt) }
func (spec ImportSpec) String() string     { return stringify(spec) }
func (stmt SelectStmt) String() string     { return stringify(stmt) }
func (stmt IfStmt) String() string         { return stringify(stmt) }
func (stmt IncDecStmt) String() string     { return stringify(stmt) }
func (stmt GoStmt) String() string         { return stringify(stmt) }
func (field Field) String() string         { return stringify(field) }
func (expr SelectorExpr) String() string   { return stringify(expr) }
func (typ StructType) String() string      { return stringify(typ) }
func (expr KeyValueExpr) String() string   { return stringify(expr) }
func (typ FuncType) String() string        { return stringify(typ) }
func (lit FuncLit) String() string         { return stringify(lit) }
func (decl FuncDecl) String() string       { return stringify(decl) }
func (stmt BranchStmt) String() string     { return stringify(stmt) }
func (expr CallExpr) String() string       { return stringify(expr) }
func (clause CaseClause) String() string   { return stringify(clause) }
func (typ ChanType) String() string        { return stringify(typ) }
func (clause CommClause) String() string   { return stringify(clause) }
func (lit CompositeLit) String() string    { return stringify(lit) }
func (stmt EmptyStmt) String() string      { return stringify(stmt) }
func (typ InterfaceType) String() string   { return stringify(typ) }
func (stmt SwitchStmt) String() string     { return stringify(stmt) }
func (expr TypeAssertExpr) String() string { return stringify(expr) }
func (spec TypeSpec) String() string       { return stringify(spec) }
func (stmt TypeSwitchStmt) String() string { return stringify(stmt) }
func (nil Nil) String() string             { return "nil" }
func (builtin Builtin) String() string     { return stringify(builtin) }
func (obj Object) String() string          { return stringify(obj) }
func (fn Function) String() string         { return stringify(fn) }
func (el Ellipsis) String() string         { return stringify(el) }
func (not Not) String() string             { return stringify(not) }

func (or Or) String() string {
	s := "(Or"
	for _, node := range or.Nodes {
		s += " "
		s += node.String()
	}
	s += ")"
	return s
}

func isProperList(l List) bool {
	if l.Head == nil && l.Tail == nil {
		return true
	}
	switch tail := l.Tail.(type) {
	case nil:
		return false
	case List:
		return isProperList(tail)
	default:
		return false
	}
}

func (l List) String() string {
	if l.Head == nil && l.Tail == nil {
		return "[]"
	}

	if isProperList(l) {
		// pretty-print the list
		var objs []string
		for l.Head != nil {
			objs = append(objs, l.Head.String())
			l = l.Tail.(List)
		}
		return fmt.Sprintf("[%s]", strings.Join(objs, " "))
	}

	return fmt.Sprintf("%s:%s", l.Head, l.Tail)
}

func (bind Binding) String() string {
	if bind.Node == nil {
		return bind.Name
	}
	return fmt.Sprintf("%s@%s", bind.Name, bind.Node)
}

func (s String) String() string { return fmt.Sprintf("%q", string(s)) }

func (tok Token) String() string {
	return fmt.Sprintf("%q", strings.ToUpper(token.Token(tok).String()))
}

func (Any) String() string { return "_" }

func (AssignStmt) isNode()     {}
func (IndexExpr) isNode()      {}
func (Ident) isNode()          {}
func (ValueSpec) isNode()      {}
func (GenDecl) isNode()        {}
func (BasicLit) isNode()       {}
func (BinaryExpr) isNode()     {}
func (ForStmt) isNode()        {}
func (RangeStmt) isNode()      {}
func (ArrayType) isNode()      {}
func (DeferStmt) isNode()      {}
func (MapType) isNode()        {}
func (ReturnStmt) isNode()     {}
func (SliceExpr) isNode()      {}
func (StarExpr) isNode()       {}
func (UnaryExpr) isNode()      {}
func (SendStmt) isNode()       {}
func (ImportSpec) isNode()     {}
func (SelectStmt) isNode()     {}
func (IfStmt) isNode()         {}
func (IncDecStmt) isNode()     {}
func (GoStmt) isNode()         {}
func (Field) isNode()          {}
func (SelectorExpr) isNode()   {}
func (StructType) isNode()     {}
func (KeyValueExpr) isNode()   {}
func (FuncType) isNode()       {}
func (FuncLit) isNode()        {}
func (FuncDecl) isNode()       {}
func (BranchStmt) isNode()     {}
func (CallExpr) isNode()       {}
func (CaseClause) isNode()     {}
func (ChanType) isNode()       {}
func (CommClause) isNode()     {}
func (CompositeLit) isNode()   {}
func (EmptyStmt) isNode()      {}
func (InterfaceType) isNode()  {}
func (SwitchStmt) isNode()     {}
func (TypeAssertExpr) isNode() {}
func (TypeSpec) isNode()       {}
func (TypeSwitchStmt) isNode() {}
func (Nil) isNode()            {}
func (Builtin) isNode()        {}
func (Object) isNode()         {}
func (Function) isNode()       {}
func (Ellipsis) isNode()       {}
func (Or) isNode()             {}
func (List) isNode()           {}
func (String) isNode()         {}
func (Token) isNode()          {}
func (Any) isNode()            {}
func (Binding) isNode()        {}
func (Not) isNode()            {}
<commit_msg>pattern: ignore false positive in staticcheck 2019.2.3<commit_after>package pattern

import (
	"fmt"
	"go/token"
	"reflect"
	"strings"
)

var (
	_ Node = Ellipsis{}
	_ Node = Binding{}
	_ Node = RangeStmt{}
	_ Node = AssignStmt{}
	_ Node = IndexExpr{}
	_ Node = Ident{}
	_ Node = Builtin{}
	_ Node = String("")
	_ Node = Any{}
	_ Node = ValueSpec{}
	_ Node = List{}
	_ Node = GenDecl{}
	_ Node = BinaryExpr{}
	_ Node = ForStmt{}
	_ Node = ArrayType{}
	_ Node = DeferStmt{}
	_ Node = MapType{}
	_ Node = ReturnStmt{}
	_ Node = SliceExpr{}
	_ Node = StarExpr{}
	_ Node = UnaryExpr{}
	_ Node = SendStmt{}
	_ Node = SelectStmt{}
	_ Node = ImportSpec{}
	_ Node = IfStmt{}
	_ Node = GoStmt{}
	_ Node = Field{}
	_ Node = SelectorExpr{}
	_ Node = StructType{}
	_ Node = KeyValueExpr{}
	_ Node = FuncType{}
	_ Node = FuncLit{}
	_ Node = FuncDecl{}
	_ Node = Token(0)
	_ Node = ChanType{}
	_ Node = CallExpr{}
	_ Node = CaseClause{}
	_ Node = CommClause{}
	_ Node = CompositeLit{}
	_ Node = EmptyStmt{}
	_ Node = SwitchStmt{}
	_ Node = TypeSwitchStmt{}
	_ Node = TypeAssertExpr{}
	_ Node = TypeSpec{}
	_ Node = InterfaceType{}
	_ Node = BranchStmt{}
	_ Node = IncDecStmt{}
	_ Node = BasicLit{}
	_ Node = Nil{}
	_ Node = Object{}
	_ Node = Function{}
	_ Node = Not{}
	_ Node = Or{}
)

type Function struct {
	Name Node
}

type Token token.Token

type Nil struct {
}

type Ellipsis struct {
	Elt Node
}

type IncDecStmt struct {
	X   Node
	Tok Node
}

type BranchStmt struct {
	Tok   Node
	Label Node
}

type InterfaceType struct {
	Methods Node
}

type TypeSpec struct {
	Name Node
	Type Node
}

type TypeAssertExpr struct {
	X    Node
	Type Node
}

type TypeSwitchStmt struct {
	Init   Node
	Assign Node
	Body   Node
}

type SwitchStmt struct {
	Init Node
	Tag  Node
	Body Node
}

type EmptyStmt struct {
}

type CompositeLit struct {
	Type Node
	Elts Node
}

type CommClause struct {
	Comm Node
	Body Node
}

type CaseClause struct {
	List Node
	Body Node
}

type CallExpr struct {
	Fun  Node
	Args Node
	// XXX handle ellipsis
}

// TODO(dh): add a ChanDir node, and a way of instantiating it.

type ChanType struct {
	Dir   Node
	Value Node
}

type FuncDecl struct {
	Recv Node
	Name Node
	Type Node
	Body Node
}

type FuncLit struct {
	Type Node
	Body Node
}

type FuncType struct {
	Params  Node
	Results Node
}

type KeyValueExpr struct {
	Key   Node
	Value Node
}

type StructType struct {
	Fields Node
}

type SelectorExpr struct {
	X   Node
	Sel Node
}

type Field struct {
	Names Node
	Type  Node
	Tag   Node
}

type GoStmt struct {
	Call Node
}

type IfStmt struct {
	Init Node
	Cond Node
	Body Node
	Else Node
}

type ImportSpec struct {
	Name Node
	Path Node
}

type SelectStmt struct {
	Body Node
}

type ArrayType struct {
	Len Node
	Elt Node
}

type DeferStmt struct {
	Call Node
}

type MapType struct {
	Key   Node
	Value Node
}

type ReturnStmt struct {
	Results Node
}

type SliceExpr struct {
	X    Node
	Low  Node
	High Node
	Max  Node
}

type StarExpr struct {
	X Node
}

type UnaryExpr struct {
	Op Node
	X  Node
}

type SendStmt struct {
	Chan  Node
	Value Node
}

type Binding struct {
	Name string
	Node Node
}

type RangeStmt struct {
	Key   Node
	Value Node
	Tok   Node
	X     Node
	Body  Node
}

type AssignStmt struct {
	Lhs Node
	Tok Node
	Rhs Node
}

type IndexExpr struct {
	X     Node
	Index Node
}

type Node interface {
	String() string
	isNode()
}

type Ident struct {
	Name Node
}

type Object struct {
	Name Node
}

type Builtin struct {
	Name Node
}

type String string

type Any struct{}

type ValueSpec struct {
	Names  Node
	Type   Node
	Values Node
}

type List struct {
	Head Node
	Tail Node
}

type GenDecl struct {
	Tok   Node
	Specs Node
}

type BasicLit struct {
	Kind  Node
	Value Node
}

type BinaryExpr struct {
	X  Node
	Op Node
	Y  Node
}

type ForStmt struct {
	Init Node
	Cond Node
	Post Node
	Body Node
}

type Or struct {
	Nodes []Node
}

type Not struct {
	Node Node
}

func stringify(n Node) string {
	v := reflect.ValueOf(n)
	var parts []string
	parts = append(parts, v.Type().Name())
	for i := 0; i < v.NumField(); i++ {
		//lint:ignore S1025 false positive in staticcheck 2019.2.3
		parts = append(parts, fmt.Sprintf("%s", v.Field(i)))
	}
	return "(" + strings.Join(parts, " ") + ")"
}

func (stmt AssignStmt) String() string     { return stringify(stmt) }
func (expr IndexExpr) String() string      { return stringify(expr) }
func (id Ident) String() string            { return stringify(id) }
func (spec ValueSpec) String() string      { return stringify(spec) }
func (decl GenDecl) String() string        { return stringify(decl) }
func (lit BasicLit) String() string        { return stringify(lit) }
func (expr BinaryExpr) String() string     { return stringify(expr) }
func (stmt ForStmt) String() string        { return stringify(stmt) }
func (stmt RangeStmt) String() string      { return stringify(stmt) }
func (typ ArrayType) String() string       { return stringify(typ) }
func (stmt DeferStmt) String() string      { return stringify(stmt) }
func (typ MapType) String() string         { return stringify(typ) }
func (stmt ReturnStmt) String() string     { return stringify(stmt) }
func (expr SliceExpr) String() string      { return stringify(expr) }
func (expr StarExpr) String() string       { return stringify(expr) }
func (expr UnaryExpr) String() string      { return stringify(expr) }
func (stmt SendStmt) String() string       { return stringify(stmt) }
func (spec ImportSpec) String() string     { return stringify(spec) }
func (stmt SelectStmt) String() string     { return stringify(stmt) }
func (stmt IfStmt) String() string         { return stringify(stmt) }
func (stmt IncDecStmt) String() string     { return stringify(stmt) }
func (stmt GoStmt) String() string         { return stringify(stmt) }
func (field Field) String() string         { return stringify(field) }
func (expr SelectorExpr) String() string   { return stringify(expr) }
func (typ StructType) String() string      { return stringify(typ) }
func (expr KeyValueExpr) String() string   { return stringify(expr) }
func (typ FuncType) String() string        { return stringify(typ) }
func (lit FuncLit) String() string         { return stringify(lit) }
func (decl FuncDecl) String() string       { return stringify(decl) }
func (stmt BranchStmt) String() string     { return stringify(stmt) }
func (expr CallExpr) String() string       { return stringify(expr) }
func (clause CaseClause) String() string   { return stringify(clause) }
func (typ ChanType) String() string        { return stringify(typ) }
func (clause CommClause) String() string   { return stringify(clause) }
func (lit CompositeLit) String() string    { return stringify(lit) }
func (stmt EmptyStmt) String() string      { return stringify(stmt) }
func (typ InterfaceType) String() string   { return stringify(typ) }
func (stmt SwitchStmt) String() string     { return stringify(stmt) }
func (expr TypeAssertExpr) String() string { return stringify(expr) }
func (spec TypeSpec) String() string       { return stringify(spec) }
func (stmt TypeSwitchStmt) String() string { return stringify(stmt) }
func (nil Nil) String() string             { return "nil" }
func (builtin Builtin) String() string     { return stringify(builtin) }
func (obj Object) String() string          { return stringify(obj) }
func (fn Function) String() string         { return stringify(fn) }
func (el Ellipsis) String() string         { return stringify(el) }
func (not Not) String() string             { return stringify(not) }

func (or Or) String() string {
	s := "(Or"
	for _, node := range or.Nodes {
		s += " "
		s += node.String()
	}
	s += ")"
	return s
}

func isProperList(l List) bool {
	if l.Head == nil && l.Tail == nil {
		return true
	}
	switch tail := l.Tail.(type) {
	case nil:
		return false
	case List:
		return isProperList(tail)
	default:
		return false
	}
}

func (l List) String() string {
	if l.Head == nil && l.Tail == nil {
		return "[]"
	}

	if isProperList(l) {
		// pretty-print the list
		var objs []string
		for l.Head != nil {
			objs = append(objs, l.Head.String())
			l = l.Tail.(List)
		}
		return fmt.Sprintf("[%s]", strings.Join(objs, " "))
	}

	return fmt.Sprintf("%s:%s", l.Head, l.Tail)
}

func (bind Binding) String() string {
	if bind.Node == nil {
		return bind.Name
	}
	return fmt.Sprintf("%s@%s", bind.Name, bind.Node)
}

func (s String) String() string { return fmt.Sprintf("%q", string(s)) }

func (tok Token) String() string {
	return fmt.Sprintf("%q", strings.ToUpper(token.Token(tok).String()))
}

func (Any) String() string { return "_" }

func (AssignStmt) isNode()     {}
func (IndexExpr) isNode()      {}
func (Ident) isNode()          {}
func (ValueSpec) isNode()      {}
func (GenDecl) isNode()        {}
func (BasicLit) isNode()       {}
func (BinaryExpr) isNode()     {}
func (ForStmt) isNode()        {}
func (RangeStmt) isNode()      {}
func (ArrayType) isNode()      {}
func (DeferStmt) isNode()      {}
func (MapType) isNode()        {}
func (ReturnStmt) isNode()     {}
func (SliceExpr) isNode()      {}
func (StarExpr) isNode()       {}
func (UnaryExpr) isNode()      {}
func (SendStmt) isNode()       {}
func (ImportSpec) isNode()     {}
func (SelectStmt) isNode()     {}
func (IfStmt) isNode()         {}
func (IncDecStmt) isNode()     {}
func (GoStmt) isNode()         {}
func (Field) isNode()          {}
func (SelectorExpr) isNode()   {}
func (StructType) isNode()     {}
func (KeyValueExpr) isNode()   {}
func (FuncType) isNode()       {}
func (FuncLit) isNode()        {}
func (FuncDecl) isNode()       {}
func (BranchStmt) isNode()     {}
func (CallExpr) isNode()       {}
func (CaseClause) isNode()     {}
func (ChanType) isNode()       {}
func (CommClause) isNode()     {}
func (CompositeLit) isNode()   {}
func (EmptyStmt) isNode()      {}
func (InterfaceType) isNode()  {}
func (SwitchStmt) isNode()     {}
func (TypeAssertExpr) isNode() {}
func (TypeSpec) isNode()       {}
func (TypeSwitchStmt) isNode() {}
func (Nil) isNode()            {}
func (Builtin) isNode()        {}
func (Object) isNode()         {}
func (Function) isNode()       {}
func (Ellipsis) isNode()       {}
func (Or) isNode()             {}
func (List) isNode()           {}
func (String) isNode()         {}
func (Token) isNode()          {}
func (Any) isNode()            {}
func (Binding) isNode()        {}
func (Not) isNode()            {}
<|endoftext|>