<commit_before>package drivers

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/lexers"
	"github.com/xo/dburl"

	"github.com/xo/usql/stmt"
	"github.com/xo/usql/text"
)

// DB is the common interface for database operations, compatible with
// database/sql.DB and database/sql.Tx.
type DB interface {
	Exec(string, ...interface{}) (sql.Result, error)
	Query(string, ...interface{}) (*sql.Rows, error)
	QueryRow(string, ...interface{}) *sql.Row
	Prepare(string) (*sql.Stmt, error)
}

// Driver holds funcs for a driver.
type Driver struct {
	// Name is a name to override the driver name with.
	Name string

	// AllowDollar will be passed to query buffers to enable dollar ($$) style
	// strings.
	AllowDollar bool

	// AllowMultilineComments will be passed to query buffers to enable
	// multiline (/**/) style comments.
	AllowMultilineComments bool

	// AllowCComments will be passed to query buffers to enable C (//) style
	// comments.
	AllowCComments bool

	// AllowHashComments will be passed to query buffers to enable hash (#)
	// style comments.
	AllowHashComments bool

	// RequirePreviousPassword will be used by RequirePreviousPassword.
	RequirePreviousPassword bool

	// LexerName is the name of the syntax lexer to use.
	LexerName string

	// ForceParams will be used to force parameters if defined.
	ForceParams func(*dburl.URL)

	// Open will be used by Open if defined.
	Open func(*dburl.URL) (func(string, string) (*sql.DB, error), error)

	// Version will be used by Version if defined.
	Version func(DB) (string, error)

	// User will be used by User if defined.
	User func(DB) (string, error)

	// ChangePassword will be used by ChangePassword if defined.
	ChangePassword func(DB, string, string, string) error

	// IsPasswordErr will be used by IsPasswordErr if defined.
	IsPasswordErr func(error) bool

	// Process will be used by Process if defined.
	Process func(string, string) (string, string, bool, error)

	// Columns will be used to retrieve the columns for the rows if
	// defined.
	Columns func(*sql.Rows) ([]string, error)

	// RowsAffected will be used by RowsAffected if defined.
	RowsAffected func(sql.Result) (int64, error)

	// Err will be used by Error.Error if defined.
	Err func(error) (string, string)

	// ConvertBytes will be used by ConvertBytes to convert a raw []byte
	// slice to a string if defined.
	ConvertBytes func([]byte, string) (string, error)

	// ConvertMap will be used by ConvertMap to convert a map[string]interface{}
	// to a string if defined.
	ConvertMap func(map[string]interface{}) (string, error)

	// ConvertSlice will be used by ConvertSlice to convert a []interface{} to
	// a string if defined.
	ConvertSlice func([]interface{}) (string, error)

	// ConvertDefault will be used by ConvertDefault to convert a interface{}
	// to a string if defined.
	ConvertDefault func(interface{}) (string, error)

	// BatchAsTransaction will cause batched queries to be done in a
	// transaction block.
	BatchAsTransaction bool

	// BatchQueryPrefixes will be used by BatchQueryPrefixes if defined.
	BatchQueryPrefixes map[string]string
}

// drivers is the map of drivers funcs.
var drivers map[string]Driver

func init() {
	drivers = make(map[string]Driver)
}

// Available returns the available drivers.
func Available() map[string]Driver {
	return drivers
}

// Register registers driver d with name and associated aliases.
func Register(name string, d Driver, aliases ...string) {
	if _, ok := drivers[name]; ok {
		panic(fmt.Sprintf("driver %s is already registered", name))
	}

	drivers[name] = d

	for _, alias := range aliases {
		if _, ok := drivers[alias]; ok {
			panic(fmt.Sprintf("alias %s is already registered", name))
		}

		drivers[alias] = d
	}
}

// Registered returns whether or not a specific driver has been registered.
func Registered(name string) bool {
	_, ok := drivers[name]
	return ok
}

// ForceParams forces parameters on the supplied DSN for the registered driver.
func ForceParams(u *dburl.URL) {
	d, ok := drivers[u.Driver]
	if ok && d.ForceParams != nil {
		d.ForceParams(u)
	}
}

// Open opens a sql.DB connection for the registered driver.
func Open(u *dburl.URL) (*sql.DB, error) {
	var err error

	d, ok := drivers[u.Driver]
	if !ok {
		return nil, WrapErr(u.Driver, text.ErrDriverNotAvailable)
	}

	f := sql.Open
	if d.Open != nil {
		f, err = d.Open(u)
		if err != nil {
			return nil, WrapErr(u.Driver, err)
		}
	}

	db, err := f(u.Driver, u.DSN)
	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	return db, nil
}

// stmtOpts returns statement options for the specified driver.
func stmtOpts(u *dburl.URL) []stmt.Option {
	if u != nil {
		if d, ok := drivers[u.Driver]; ok {
			return []stmt.Option{
				stmt.AllowDollar(d.AllowDollar),
				stmt.AllowMultilineComments(d.AllowMultilineComments),
				stmt.AllowCComments(d.AllowCComments),
				stmt.AllowHashComments(d.AllowHashComments),
			}
		}
	}

	return []stmt.Option{
		stmt.AllowDollar(true),
		stmt.AllowMultilineComments(true),
		stmt.AllowCComments(true),
		stmt.AllowHashComments(true),
	}
}

// NewStmt wraps creating a new stmt.Stmt for the specified driver.
func NewStmt(u *dburl.URL, f func() ([]rune, error), opts ...stmt.Option) *stmt.Stmt {
	return stmt.New(f, append(opts, stmtOpts(u)...)...)
}

// ConfigStmt sets the stmt.Stmt options for the specified driver.
func ConfigStmt(u *dburl.URL, s *stmt.Stmt) {
	if u == nil {
		return
	}
	for _, o := range stmtOpts(u) {
		o(s)
	}
}

// Version returns information about the database connection for the specified
// URL's driver.
func Version(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.Version != nil {
		ver, err := d.Version(db)
		return ver, WrapErr(u.Driver, err)
	}

	var ver string
	err := db.QueryRow(`select version();`).Scan(&ver)
	if err != nil || ver == "" {
		ver = "<unknown>"
	}
	return ver, nil
}

// User returns the current database user for the specified URL's driver.
func User(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.User != nil {
		user, err := d.User(db)
		return user, WrapErr(u.Driver, err)
	}

	var user string
	db.QueryRow(`select current_user`).Scan(&user)
	return user, nil
}

// Process processes the supplied SQL query for the specified URL's driver.
func Process(u *dburl.URL, prefix, sqlstr string) (string, string, bool, error) {
	if d, ok := drivers[u.Driver]; ok && d.Process != nil {
		a, b, c, err := d.Process(prefix, sqlstr)
		return a, b, c, WrapErr(u.Driver, err)
	}

	typ, q := QueryExecType(prefix, sqlstr)
	return typ, sqlstr, q, nil
}

// IsPasswordErr returns true if the specified err is a password error for the
// specified URL's driver.
func IsPasswordErr(u *dburl.URL, err error) bool {
	drv := u.Driver
	if e, ok := err.(*Error); ok {
		drv, err = e.Driver, e.Err
	}

	if d, ok := drivers[drv]; ok && d.IsPasswordErr != nil {
		return d.IsPasswordErr(err)
	}
	return false
}

// RequirePreviousPassword returns true if the specified URL's driver requires
// a previous password when changing a user's password.
func RequirePreviousPassword(u *dburl.URL) bool {
	if d, ok := drivers[u.Driver]; ok {
		return d.RequirePreviousPassword
	}
	return false
}

// CanChangePassword returns whether or not the specified driver's URL supports
// changing passwords.
func CanChangePassword(u *dburl.URL) error {
	if d, ok := drivers[u.Driver]; ok && d.ChangePassword != nil {
		return nil
	}
	return text.ErrPasswordNotSupportedByDriver
}

// ChangePassword initiates a user password change for the specified URL's
// driver. If user is not supplied, then the current user will be retrieved
// from User.
func ChangePassword(u *dburl.URL, db DB, user, new, old string) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ChangePassword != nil {
		var err error
		if user == "" {
			user, err = User(u, db)
			if err != nil {
				return "", err
			}
		}

		return user, d.ChangePassword(db, user, new, old)
	}
	return "", text.ErrPasswordNotSupportedByDriver
}

// Columns returns the column names for the SQL row result for the specified
// URL's driver.
func Columns(u *dburl.URL, rows *sql.Rows) ([]string, error) {
	var cols []string
	var err error

	if d, ok := drivers[u.Driver]; ok && d.Columns != nil {
		cols, err = d.Columns(rows)
	} else {
		cols, err = rows.Columns()
	}

	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	for i, c := range cols {
		if strings.TrimSpace(c) == "" {
			cols[i] = fmt.Sprintf("col%d", i)
		}
	}

	return cols, nil
}

// ConvertBytes returns a func to handle converting bytes for the specified
// URL's driver.
func ConvertBytes(u *dburl.URL) func([]byte, string) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertBytes != nil {
		return d.ConvertBytes
	}
	return func(buf []byte, _ string) (string, error) {
		return string(buf), nil
	}
}

// ConvertMap returns a func to handle converting a map[string]interface{} for
// the specified URL's driver.
func ConvertMap(u *dburl.URL) func(map[string]interface{}) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertMap != nil {
		return d.ConvertMap
	}
	return func(v map[string]interface{}) (string, error) {
		buf, err := json.Marshal(v)
		if err != nil {
			return "", err
		}
		return string(buf), nil
	}
}

// ConvertSlice returns a func to handle converting a []interface{} for
// the specified URL's driver.
func ConvertSlice(u *dburl.URL) func([]interface{}) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertSlice != nil {
		return d.ConvertSlice
	}
	return func(v []interface{}) (string, error) {
		buf, err := json.Marshal(v)
		if err != nil {
			return "", err
		}
		return string(buf), nil
	}
}

// ConvertDefault returns a func to handle converting a interface{} for
// the specified URL's driver.
func ConvertDefault(u *dburl.URL) func(interface{}) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertDefault != nil {
		return d.ConvertDefault
	}
	return func(v interface{}) (string, error) {
		return fmt.Sprintf("%v", v), nil
	}
}

// BatchAsTransaction returns whether or not the the specified URL's driver requires
// batched queries to be done within a transaction block.
func BatchAsTransaction(u *dburl.URL) bool {
	if d, ok := drivers[u.Driver]; ok {
		return d.BatchAsTransaction
	}
	return false
}

// IsBatchQueryPrefix returns whether or not the supplied query prefix is a
// batch query prefix, and the closing prefix. Used to direct the handler to
// continue accumulating statements.
func IsBatchQueryPrefix(u *dburl.URL, prefix string) (string, string, bool) {
	// normalize
	typ, q := QueryExecType(prefix, "")

	d, ok := drivers[u.Driver]
	if q || !ok || d.BatchQueryPrefixes == nil {
		return typ, "", false
	}

	end, ok := d.BatchQueryPrefixes[typ]
	return typ, end, ok
}

// RowsAffected returns the rows affected for the SQL result for a specified
// URL's driver.
func RowsAffected(u *dburl.URL, res sql.Result) (int64, error) {
	var count int64
	var err error
	if d, ok := drivers[u.Driver]; ok && d.RowsAffected != nil {
		count, err = d.RowsAffected(res)
	} else {
		count, err = res.RowsAffected()
	}
	if err != nil {
		return 0, WrapErr(u.Driver, err)
	}

	return count, nil
}

// Ping pings the database for a specified URL's driver.
func Ping(u *dburl.URL, db *sql.DB) error {
	return WrapErr(u.Driver, db.Ping())
}

// Lexer returns the syntax lexer for a specified URL's driver.
func Lexer(u *dburl.URL) chroma.Lexer {
	var l chroma.Lexer
	if u != nil {
		if d, ok := drivers[u.Driver]; ok && d.LexerName != "" {
			l = lexers.Get(d.LexerName)
		}
	}
	if l == nil {
		l = lexers.Get("sql")
	}

	l.Config().EnsureNL = false

	return l
}

// ForceQueryParameters is a utility func that wraps forcing params of name,
// value pairs.
func ForceQueryParameters(params []string) func(*dburl.URL) {
	if len(params)%2 != 0 {
		panic("invalid query params")
	}
	return func(u *dburl.URL) {
		if len(params) != 0 {
			v := u.Query()
			for i := 0; i < len(params); i += 2 {
				v.Set(params[i], params[i+1])
			}
			u.RawQuery = v.Encode()
		}
	}
}

// NextResultSet is a wrapper around the go1.8 introduced
// sql.Rows.NextResultSet call.
func NextResultSet(q *sql.Rows) bool {
	return q.NextResultSet()
}
<commit_msg>Adding check for driver.ResultNoRows to drivers.RowsAffected<commit_after>package drivers

import (
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/alecthomas/chroma"
	"github.com/alecthomas/chroma/lexers"
	"github.com/xo/dburl"

	"github.com/xo/usql/stmt"
	"github.com/xo/usql/text"
)

// DB is the common interface for database operations, compatible with
// database/sql.DB and database/sql.Tx.
type DB interface {
	Exec(string, ...interface{}) (sql.Result, error)
	Query(string, ...interface{}) (*sql.Rows, error)
	QueryRow(string, ...interface{}) *sql.Row
	Prepare(string) (*sql.Stmt, error)
}

// Driver holds funcs for a driver.
type Driver struct {
	// Name is a name to override the driver name with.
	Name string

	// AllowDollar will be passed to query buffers to enable dollar ($$) style
	// strings.
	AllowDollar bool

	// AllowMultilineComments will be passed to query buffers to enable
	// multiline (/**/) style comments.
	AllowMultilineComments bool

	// AllowCComments will be passed to query buffers to enable C (//) style
	// comments.
	AllowCComments bool

	// AllowHashComments will be passed to query buffers to enable hash (#)
	// style comments.
	AllowHashComments bool

	// RequirePreviousPassword will be used by RequirePreviousPassword.
	RequirePreviousPassword bool

	// LexerName is the name of the syntax lexer to use.
	LexerName string

	// ForceParams will be used to force parameters if defined.
	ForceParams func(*dburl.URL)

	// Open will be used by Open if defined.
	Open func(*dburl.URL) (func(string, string) (*sql.DB, error), error)

	// Version will be used by Version if defined.
	Version func(DB) (string, error)

	// User will be used by User if defined.
	User func(DB) (string, error)

	// ChangePassword will be used by ChangePassword if defined.
	ChangePassword func(DB, string, string, string) error

	// IsPasswordErr will be used by IsPasswordErr if defined.
	IsPasswordErr func(error) bool

	// Process will be used by Process if defined.
	Process func(string, string) (string, string, bool, error)

	// Columns will be used to retrieve the columns for the rows if
	// defined.
	Columns func(*sql.Rows) ([]string, error)

	// RowsAffected will be used by RowsAffected if defined.
	RowsAffected func(sql.Result) (int64, error)

	// Err will be used by Error.Error if defined.
	Err func(error) (string, string)

	// ConvertBytes will be used by ConvertBytes to convert a raw []byte
	// slice to a string if defined.
	ConvertBytes func([]byte, string) (string, error)

	// ConvertMap will be used by ConvertMap to convert a map[string]interface{}
	// to a string if defined.
	ConvertMap func(map[string]interface{}) (string, error)

	// ConvertSlice will be used by ConvertSlice to convert a []interface{} to
	// a string if defined.
	ConvertSlice func([]interface{}) (string, error)

	// ConvertDefault will be used by ConvertDefault to convert a interface{}
	// to a string if defined.
	ConvertDefault func(interface{}) (string, error)

	// BatchAsTransaction will cause batched queries to be done in a
	// transaction block.
	BatchAsTransaction bool

	// BatchQueryPrefixes will be used by BatchQueryPrefixes if defined.
	BatchQueryPrefixes map[string]string
}

// drivers is the map of drivers funcs.
var drivers map[string]Driver

func init() {
	drivers = make(map[string]Driver)
}

// Available returns the available drivers.
func Available() map[string]Driver {
	return drivers
}

// Register registers driver d with name and associated aliases.
func Register(name string, d Driver, aliases ...string) {
	if _, ok := drivers[name]; ok {
		panic(fmt.Sprintf("driver %s is already registered", name))
	}

	drivers[name] = d

	for _, alias := range aliases {
		if _, ok := drivers[alias]; ok {
			panic(fmt.Sprintf("alias %s is already registered", name))
		}

		drivers[alias] = d
	}
}

// Registered returns whether or not a specific driver has been registered.
func Registered(name string) bool {
	_, ok := drivers[name]
	return ok
}

// ForceParams forces parameters on the supplied DSN for the registered driver.
func ForceParams(u *dburl.URL) {
	d, ok := drivers[u.Driver]
	if ok && d.ForceParams != nil {
		d.ForceParams(u)
	}
}

// Open opens a sql.DB connection for the registered driver.
func Open(u *dburl.URL) (*sql.DB, error) {
	var err error

	d, ok := drivers[u.Driver]
	if !ok {
		return nil, WrapErr(u.Driver, text.ErrDriverNotAvailable)
	}

	f := sql.Open
	if d.Open != nil {
		f, err = d.Open(u)
		if err != nil {
			return nil, WrapErr(u.Driver, err)
		}
	}

	db, err := f(u.Driver, u.DSN)
	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	return db, nil
}

// stmtOpts returns statement options for the specified driver.
func stmtOpts(u *dburl.URL) []stmt.Option {
	if u != nil {
		if d, ok := drivers[u.Driver]; ok {
			return []stmt.Option{
				stmt.AllowDollar(d.AllowDollar),
				stmt.AllowMultilineComments(d.AllowMultilineComments),
				stmt.AllowCComments(d.AllowCComments),
				stmt.AllowHashComments(d.AllowHashComments),
			}
		}
	}

	return []stmt.Option{
		stmt.AllowDollar(true),
		stmt.AllowMultilineComments(true),
		stmt.AllowCComments(true),
		stmt.AllowHashComments(true),
	}
}

// NewStmt wraps creating a new stmt.Stmt for the specified driver.
func NewStmt(u *dburl.URL, f func() ([]rune, error), opts ...stmt.Option) *stmt.Stmt {
	return stmt.New(f, append(opts, stmtOpts(u)...)...)
}

// ConfigStmt sets the stmt.Stmt options for the specified driver.
func ConfigStmt(u *dburl.URL, s *stmt.Stmt) {
	if u == nil {
		return
	}
	for _, o := range stmtOpts(u) {
		o(s)
	}
}

// Version returns information about the database connection for the specified
// URL's driver.
func Version(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.Version != nil {
		ver, err := d.Version(db)
		return ver, WrapErr(u.Driver, err)
	}

	var ver string
	err := db.QueryRow(`select version();`).Scan(&ver)
	if err != nil || ver == "" {
		ver = "<unknown>"
	}
	return ver, nil
}

// User returns the current database user for the specified URL's driver.
func User(u *dburl.URL, db DB) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.User != nil {
		user, err := d.User(db)
		return user, WrapErr(u.Driver, err)
	}

	var user string
	db.QueryRow(`select current_user`).Scan(&user)
	return user, nil
}

// Process processes the supplied SQL query for the specified URL's driver.
func Process(u *dburl.URL, prefix, sqlstr string) (string, string, bool, error) {
	if d, ok := drivers[u.Driver]; ok && d.Process != nil {
		a, b, c, err := d.Process(prefix, sqlstr)
		return a, b, c, WrapErr(u.Driver, err)
	}

	typ, q := QueryExecType(prefix, sqlstr)
	return typ, sqlstr, q, nil
}

// IsPasswordErr returns true if the specified err is a password error for the
// specified URL's driver.
func IsPasswordErr(u *dburl.URL, err error) bool {
	drv := u.Driver
	if e, ok := err.(*Error); ok {
		drv, err = e.Driver, e.Err
	}

	if d, ok := drivers[drv]; ok && d.IsPasswordErr != nil {
		return d.IsPasswordErr(err)
	}
	return false
}

// RequirePreviousPassword returns true if the specified URL's driver requires
// a previous password when changing a user's password.
func RequirePreviousPassword(u *dburl.URL) bool {
	if d, ok := drivers[u.Driver]; ok {
		return d.RequirePreviousPassword
	}
	return false
}

// CanChangePassword returns whether or not the specified driver's URL supports
// changing passwords.
func CanChangePassword(u *dburl.URL) error {
	if d, ok := drivers[u.Driver]; ok && d.ChangePassword != nil {
		return nil
	}
	return text.ErrPasswordNotSupportedByDriver
}

// ChangePassword initiates a user password change for the specified URL's
// driver. If user is not supplied, then the current user will be retrieved
// from User.
func ChangePassword(u *dburl.URL, db DB, user, new, old string) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ChangePassword != nil {
		var err error
		if user == "" {
			user, err = User(u, db)
			if err != nil {
				return "", err
			}
		}

		return user, d.ChangePassword(db, user, new, old)
	}
	return "", text.ErrPasswordNotSupportedByDriver
}

// Columns returns the column names for the SQL row result for the specified
// URL's driver.
func Columns(u *dburl.URL, rows *sql.Rows) ([]string, error) {
	var cols []string
	var err error

	if d, ok := drivers[u.Driver]; ok && d.Columns != nil {
		cols, err = d.Columns(rows)
	} else {
		cols, err = rows.Columns()
	}

	if err != nil {
		return nil, WrapErr(u.Driver, err)
	}

	for i, c := range cols {
		if strings.TrimSpace(c) == "" {
			cols[i] = fmt.Sprintf("col%d", i)
		}
	}

	return cols, nil
}

// ConvertBytes returns a func to handle converting bytes for the specified
// URL's driver.
func ConvertBytes(u *dburl.URL) func([]byte, string) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertBytes != nil {
		return d.ConvertBytes
	}
	return func(buf []byte, _ string) (string, error) {
		return string(buf), nil
	}
}

// ConvertMap returns a func to handle converting a map[string]interface{} for
// the specified URL's driver.
func ConvertMap(u *dburl.URL) func(map[string]interface{}) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertMap != nil {
		return d.ConvertMap
	}
	return func(v map[string]interface{}) (string, error) {
		buf, err := json.Marshal(v)
		if err != nil {
			return "", err
		}
		return string(buf), nil
	}
}

// ConvertSlice returns a func to handle converting a []interface{} for
// the specified URL's driver.
func ConvertSlice(u *dburl.URL) func([]interface{}) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertSlice != nil {
		return d.ConvertSlice
	}
	return func(v []interface{}) (string, error) {
		buf, err := json.Marshal(v)
		if err != nil {
			return "", err
		}
		return string(buf), nil
	}
}

// ConvertDefault returns a func to handle converting a interface{} for
// the specified URL's driver.
func ConvertDefault(u *dburl.URL) func(interface{}) (string, error) {
	if d, ok := drivers[u.Driver]; ok && d.ConvertDefault != nil {
		return d.ConvertDefault
	}
	return func(v interface{}) (string, error) {
		return fmt.Sprintf("%v", v), nil
	}
}

// BatchAsTransaction returns whether or not the the specified URL's driver requires
// batched queries to be done within a transaction block.
func BatchAsTransaction(u *dburl.URL) bool {
	if d, ok := drivers[u.Driver]; ok {
		return d.BatchAsTransaction
	}
	return false
}

// IsBatchQueryPrefix returns whether or not the supplied query prefix is a
// batch query prefix, and the closing prefix. Used to direct the handler to
// continue accumulating statements.
func IsBatchQueryPrefix(u *dburl.URL, prefix string) (string, string, bool) {
	// normalize
	typ, q := QueryExecType(prefix, "")

	d, ok := drivers[u.Driver]
	if q || !ok || d.BatchQueryPrefixes == nil {
		return typ, "", false
	}

	end, ok := d.BatchQueryPrefixes[typ]
	return typ, end, ok
}

// RowsAffected returns the rows affected for the SQL result for a specified
// URL's driver.
func RowsAffected(u *dburl.URL, res sql.Result) (int64, error) {
	if res == driver.ResultNoRows {
		return 0, nil
	}

	var count int64
	var err error
	if d, ok := drivers[u.Driver]; ok && d.RowsAffected != nil {
		count, err = d.RowsAffected(res)
	} else {
		count, err = res.RowsAffected()
	}
	if err != nil {
		return 0, WrapErr(u.Driver, err)
	}

	return count, nil
}

// Ping pings the database for a specified URL's driver.
func Ping(u *dburl.URL, db *sql.DB) error {
	return WrapErr(u.Driver, db.Ping())
}

// Lexer returns the syntax lexer for a specified URL's driver.
func Lexer(u *dburl.URL) chroma.Lexer {
	var l chroma.Lexer
	if u != nil {
		if d, ok := drivers[u.Driver]; ok && d.LexerName != "" {
			l = lexers.Get(d.LexerName)
		}
	}
	if l == nil {
		l = lexers.Get("sql")
	}

	l.Config().EnsureNL = false

	return l
}

// ForceQueryParameters is a utility func that wraps forcing params of name,
// value pairs.
func ForceQueryParameters(params []string) func(*dburl.URL) {
	if len(params)%2 != 0 {
		panic("invalid query params")
	}
	return func(u *dburl.URL) {
		if len(params) != 0 {
			v := u.Query()
			for i := 0; i < len(params); i += 2 {
				v.Set(params[i], params[i+1])
			}
			u.RawQuery = v.Encode()
		}
	}
}

// NextResultSet is a wrapper around the go1.8 introduced
// sql.Rows.NextResultSet call.
func NextResultSet(q *sql.Rows) bool {
	return q.NextResultSet()
}
<|endoftext|>


<commit_before>package revgrep

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// Checker provides APIs to filter static analysis tools to specific commits,
// such as showing only issues since last commit.
type Checker struct {
	// Patch file (unified) to read to detect lines being changed, if nil revgrep
	// will attempt to detect the VCS and generate an appropriate patch. Auto
	// detection will search for uncommitted changes first, if none found, will
	// generate a patch from last committed change. File paths within patches
	// must be relative to current working directory.
	Patch io.Reader
	// NewFiles is a list of file names (with absolute paths) where the entire
	// contents of the file is new.
	NewFiles []string
	// Debug sets the debug writer for additional output.
	Debug io.Writer
	// RevisionFrom check revision starting at, leave blank for auto detection
	// ignored if patch is set.
	RevisionFrom string
	// RevisionTo checks revision finishing at, leave blank for auto detection
	// ignored if patch is set.
	RevisionTo string
}

// Issue contains metadata about an issue found.
type Issue struct {
	// File is the name of the file as it appeared from the patch.
	File string
	// LineNo is the line number of the file.
	LineNo int
	// HunkPos is position from file's first @@, or zero if this is a new file.
	//
	// See also: https://developer.github.com/v3/pulls/comments/#create-a-comment
	HunkPos int
	// Issue text as it appeared from the tool.
	Issue string
}

var (
	// file:lineNumber
	lineRE = regexp.MustCompile("^(.*):([0-9]+)")
)

// Check scans reader and writes any lines to writer that have been added in
// Checker.Patch.
//
// Returns issues written to writer when no error occurs.
//
// If no VCS could be found or other VCS errors occur, all issues are written
// to writer and an error is returned.
//
// File paths in reader must be relative to current working directory or
// absolute.
func (c Checker) Check(reader io.Reader, writer io.Writer) (issues []Issue, err error) {
	// Check if patch is supplied, if not, retrieve from VCS
	var (
		writeAll  bool
		returnErr error
	)
	if c.Patch == nil {
		c.Patch, c.NewFiles, err = GitPatch(c.RevisionFrom, c.RevisionTo)
		if err != nil {
			writeAll = true
			returnErr = fmt.Errorf("could not read git repo: %s", err)
		}
		if c.Patch == nil {
			writeAll = true
			returnErr = errors.New("no version control repository found")
		}
	}

	// TODO consider lazy loading this, if there's nothing in stdin, no point
	// checking for recent changes
	linesChanged := c.linesChanged()
	c.debug(fmt.Sprintf("lines changed: %+v", linesChanged))

	cwd, err := os.Getwd()
	if err != nil {
		returnErr = fmt.Errorf("could not get current working directory: %s", err)
	}

	// Scan each line in reader and only write those lines if lines changed
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := lineRE.FindSubmatch(scanner.Bytes())
		if line == nil {
			c.debug("cannot parse file+line number:", scanner.Text())
			continue
		}

		if writeAll {
			fmt.Fprintln(writer, scanner.Text())
			continue
		}

		// Parse line number
		lno, err := strconv.ParseUint(string(line[2]), 10, 64)
		if err != nil {
			c.debug("cannot parse line number:", scanner.Text())
			continue
		}

		// Make absolute path names relative
		path := string(line[1])
		if rel, err := filepath.Rel(cwd, path); err == nil {
			c.debug("rewrote path from %q to %q", path, rel)
			path = rel
		}

		var (
			fpos    pos
			changed bool
		)
		if fchanges, ok := linesChanged[path]; ok {
			// found file, see if lines matched
			for _, pos := range fchanges {
				if pos.lineNo == int(lno) {
					fpos = pos
					changed = true
				}
			}
			if changed || fchanges == nil {
				// either file changed or it's a new file
				issue := Issue{
					File:    path,
					LineNo:  fpos.lineNo,
					HunkPos: fpos.lineNo,
					Issue:   scanner.Text(),
				}
				if changed {
					// file changed
					issue.HunkPos = fpos.hunkPos
				}
				issues = append(issues, issue)
				fmt.Fprintln(writer, scanner.Text())
			}
		}
		if !changed {
			c.debug("unchanged:", scanner.Text())
		}
	}
	if err := scanner.Err(); err != nil {
		returnErr = fmt.Errorf("error reading standard input: %s", err)
	}
	return issues, returnErr
}

func (c Checker) debug(s ...interface{}) {
	if c.Debug != nil {
		fmt.Fprint(c.Debug, "DEBUG: ")
		fmt.Fprintln(c.Debug, s...)
	}
}

type pos struct {
	lineNo  int // line number
	hunkPos int // position relative to first @@ in file
}

// linesChanges returns a map of file names to line numbers being changed
func (c Checker) linesChanged() map[string][]pos {
	type state struct {
		file    string
		lineNo  int   // current line number within chunk
		hunkPos int   // current line count since first @@ in file
		changes []pos // position of changes
	}

	var (
		s       state
		changes = make(map[string][]pos)
	)

	for _, file := range c.NewFiles {
		changes[file] = nil
	}

	if c.Patch == nil {
		return changes
	}

	scanner := bufio.NewScanner(c.Patch)
	for scanner.Scan() {
		line := scanner.Text() // TODO scanner.Bytes()
		c.debug(line)
		s.lineNo++
		switch {
		case strings.HasPrefix(line, "+++ ") && len(line) > 4:
			if s.changes != nil {
				// record the last state
				changes[s.file] = s.changes
			}
			// 6 removes "+++ b/"
			s = state{file: line[6:]}
		case strings.HasPrefix(line, "@@ "):
			//      @@ -1 +2,4 @@
			// chdr ^^^^^^^^^^^^^
			// ahdr       ^^^^
			// cstart      ^
			chdr := strings.Split(line, " ")
			ahdr := strings.Split(chdr[2], ",")
			// [1:] to remove leading plus
			cstart, err := strconv.ParseUint(ahdr[0][1:], 10, 64)
			if err != nil {
				panic(err)
			}
			s.lineNo = int(cstart) - 1 // -1 as cstart is the next line number
		case strings.HasPrefix(line, " "):
			s.hunkPos++
		case strings.HasPrefix(line, "-"):
			s.hunkPos++
			s.lineNo--
		case strings.HasPrefix(line, "+"):
			s.hunkPos++
			s.changes = append(s.changes, pos{lineNo: s.lineNo, hunkPos: s.hunkPos})
		}

	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}
	// record the last state
	changes[s.file] = s.changes

	return changes
}

// GitPatch returns a patch from a git repository, if no git repository was
// was found and no errors occurred, nil is returned, else an error is returned
// revisionFrom and revisionTo defines the git diff parameters, if left blank
// and there are unstaged changes or untracked files, only those will be returned
// else only check changes since HEAD~. If revisionFrom is set but revisionTo
// is not, untracked files will be included, to exclude untracked files set
// revisionTo to HEAD~. It's incorrect to specify revisionTo without a
// revisionFrom.
func GitPatch(revisionFrom, revisionTo string) (io.Reader, []string, error) {
	var patch bytes.Buffer

	// check if git repo exists
	if err := exec.Command("git", "status").Run(); err != nil {
		// don't return an error, we assume the error is not repo exists
		return nil, nil, nil
	}

	// make a patch for untracked files
	var newFiles []string
	ls, err := exec.Command("git", "ls-files", "-o").CombinedOutput()
	if err != nil {
		return nil, nil, fmt.Errorf("error executing git ls-files: %s", err)
	}
	for _, file := range bytes.Split(ls, []byte{'\n'}) {
		if len(file) == 0 || bytes.HasSuffix(file, []byte{'/'}) {
			// ls-files was sometimes showing directories when they were ignored
			// I couldn't create a test case for this as I couldn't reproduce correctly
			// for the moment, just exclude files with trailing /
			continue
		}
		newFiles = append(newFiles, string(file))
	}

	if revisionFrom != "" {
		cmd := exec.Command("git", "diff", revisionFrom)
		if revisionTo != "" {
			cmd.Args = append(cmd.Args, revisionTo)
		}
		cmd.Stdout = &patch
		if err := cmd.Run(); err != nil {
			return nil, nil, fmt.Errorf("error executing git diff %q %q: %s", revisionFrom, revisionTo, err)
		}

		if revisionTo == "" {
			return &patch, newFiles, nil
		}
		return &patch, nil, nil
	}

	// make a patch for unstaged changes
	// use --no-prefix to remove b/ given: +++ b/main.go
	cmd := exec.Command("git", "diff")
	cmd.Stdout = &patch
	if err := cmd.Run(); err != nil {
		return nil, nil, fmt.Errorf("error executing git diff: %s", err)
	}
	unstaged := patch.Len() > 0

	// If there's unstaged changes OR untracked changes (or both), then this is
	// a suitable patch
	if unstaged || newFiles != nil {
		return &patch, newFiles, nil
	}

	// check for changes in recent commit

	cmd = exec.Command("git", "diff", "HEAD~")
	cmd.Stdout = &patch
	if err := cmd.Run(); err != nil {
		return nil, nil, fmt.Errorf("error executing git diff HEAD~: %s", err)
	}

	return &patch, nil, nil
}
<commit_msg>Correct an error is documentation<commit_after>package revgrep

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// Checker provides APIs to filter static analysis tools to specific commits,
// such as showing only issues since last commit.
type Checker struct {
	// Patch file (unified) to read to detect lines being changed, if nil revgrep
	// will attempt to detect the VCS and generate an appropriate patch. Auto
	// detection will search for uncommitted changes first, if none found, will
	// generate a patch from last committed change. File paths within patches
	// must be relative to current working directory.
	Patch io.Reader
	// NewFiles is a list of file names (with absolute paths) where the entire
	// contents of the file is new.
	NewFiles []string
	// Debug sets the debug writer for additional output.
	Debug io.Writer
	// RevisionFrom check revision starting at, leave blank for auto detection
	// ignored if patch is set.
	RevisionFrom string
	// RevisionTo checks revision finishing at, leave blank for auto detection
	// ignored if patch is set.
	RevisionTo string
}

// Issue contains metadata about an issue found.
type Issue struct {
	// File is the name of the file as it appeared from the patch.
	File string
	// LineNo is the line number of the file.
	LineNo int
	// HunkPos is position from file's first @@, for new files this will be the
	// line number.
	//
	// See also: https://developer.github.com/v3/pulls/comments/#create-a-comment
	HunkPos int
	// Issue text as it appeared from the tool.
	Issue string
}

var (
	// file:lineNumber
	lineRE = regexp.MustCompile("^(.*):([0-9]+)")
)

// Check scans reader and writes any lines to writer that have been added in
// Checker.Patch.
//
// Returns issues written to writer when no error occurs.
//
// If no VCS could be found or other VCS errors occur, all issues are written
// to writer and an error is returned.
//
// File paths in reader must be relative to current working directory or
// absolute.
func (c Checker) Check(reader io.Reader, writer io.Writer) (issues []Issue, err error) {
	// Check if patch is supplied, if not, retrieve from VCS
	var (
		writeAll  bool
		returnErr error
	)
	if c.Patch == nil {
		c.Patch, c.NewFiles, err = GitPatch(c.RevisionFrom, c.RevisionTo)
		if err != nil {
			writeAll = true
			returnErr = fmt.Errorf("could not read git repo: %s", err)
		}
		if c.Patch == nil {
			writeAll = true
			returnErr = errors.New("no version control repository found")
		}
	}

	// TODO consider lazy loading this, if there's nothing in stdin, no point
	// checking for recent changes
	linesChanged := c.linesChanged()
	c.debug(fmt.Sprintf("lines changed: %+v", linesChanged))

	cwd, err := os.Getwd()
	if err != nil {
		returnErr = fmt.Errorf("could not get current working directory: %s", err)
	}

	// Scan each line in reader and only write those lines if lines changed
	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		line := lineRE.FindSubmatch(scanner.Bytes())
		if line == nil {
			c.debug("cannot parse file+line number:", scanner.Text())
			continue
		}

		if writeAll {
			fmt.Fprintln(writer, scanner.Text())
			continue
		}

		// Parse line number
		lno, err := strconv.ParseUint(string(line[2]), 10, 64)
		if err != nil {
			c.debug("cannot parse line number:", scanner.Text())
			continue
		}

		// Make absolute path names relative
		path := string(line[1])
		if rel, err := filepath.Rel(cwd, path); err == nil {
			c.debug("rewrote path from %q to %q", path, rel)
			path = rel
		}

		var (
			fpos    pos
			changed bool
		)
		if fchanges, ok := linesChanged[path]; ok {
			// found file, see if lines matched
			for _, pos := range fchanges {
				if pos.lineNo == int(lno) {
					fpos = pos
					changed = true
				}
			}
			if changed || fchanges == nil {
				// either file changed or it's a new file
				issue := Issue{
					File:    path,
					LineNo:  fpos.lineNo,
					HunkPos: fpos.lineNo,
					Issue:   scanner.Text(),
				}
				if changed {
					// file changed
					issue.HunkPos = fpos.hunkPos
				}
				issues = append(issues, issue)
				fmt.Fprintln(writer, scanner.Text())
			}
		}
		if !changed {
			c.debug("unchanged:", scanner.Text())
		}
	}
	if err := scanner.Err(); err != nil {
		returnErr = fmt.Errorf("error reading standard input: %s", err)
	}
	return issues, returnErr
}

func (c Checker) debug(s ...interface{}) {
	if c.Debug != nil {
		fmt.Fprint(c.Debug, "DEBUG: ")
		fmt.Fprintln(c.Debug, s...)
	}
}

type pos struct {
	lineNo  int // line number
	hunkPos int // position relative to first @@ in file
}

// linesChanges returns a map of file names to line numbers being changed
func (c Checker) linesChanged() map[string][]pos {
	type state struct {
		file    string
		lineNo  int   // current line number within chunk
		hunkPos int   // current line count since first @@ in file
		changes []pos // position of changes
	}

	var (
		s       state
		changes = make(map[string][]pos)
	)

	for _, file := range c.NewFiles {
		changes[file] = nil
	}

	if c.Patch == nil {
		return changes
	}

	scanner := bufio.NewScanner(c.Patch)
	for scanner.Scan() {
		line := scanner.Text() // TODO scanner.Bytes()
		c.debug(line)
		s.lineNo++
		switch {
		case strings.HasPrefix(line, "+++ ") && len(line) > 4:
			if s.changes != nil {
				// record the last state
				changes[s.file] = s.changes
			}
			// 6 removes "+++ b/"
			s = state{file: line[6:]}
		case strings.HasPrefix(line, "@@ "):
			//      @@ -1 +2,4 @@
			// chdr ^^^^^^^^^^^^^
			// ahdr       ^^^^
			// cstart      ^
			chdr := strings.Split(line, " ")
			ahdr := strings.Split(chdr[2], ",")
			// [1:] to remove leading plus
			cstart, err := strconv.ParseUint(ahdr[0][1:], 10, 64)
			if err != nil {
				panic(err)
			}
			s.lineNo = int(cstart) - 1 // -1 as cstart is the next line number
		case strings.HasPrefix(line, " "):
			s.hunkPos++
		case strings.HasPrefix(line, "-"):
			s.hunkPos++
			s.lineNo--
		case strings.HasPrefix(line, "+"):
			s.hunkPos++
			s.changes = append(s.changes, pos{lineNo: s.lineNo, hunkPos: s.hunkPos})
		}

	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}
	// record the last state
	changes[s.file] = s.changes

	return changes
}

// GitPatch returns a patch from a git repository, if no git repository was
// was found and no errors occurred, nil is returned, else an error is returned
// revisionFrom and revisionTo defines the git diff parameters, if left blank
// and there are unstaged changes or untracked files, only those will be returned
// else only check changes since HEAD~. If revisionFrom is set but revisionTo
// is not, untracked files will be included, to exclude untracked files set
// revisionTo to HEAD~. It's incorrect to specify revisionTo without a
// revisionFrom.
func GitPatch(revisionFrom, revisionTo string) (io.Reader, []string, error) {
	var patch bytes.Buffer

	// check if git repo exists
	if err := exec.Command("git", "status").Run(); err != nil {
		// don't return an error, we assume the error is not repo exists
		return nil, nil, nil
	}

	// make a patch for untracked files
	var newFiles []string
	ls, err := exec.Command("git", "ls-files", "-o").CombinedOutput()
	if err != nil {
		return nil, nil, fmt.Errorf("error executing git ls-files: %s", err)
	}
	for _, file := range bytes.Split(ls, []byte{'\n'}) {
		if len(file) == 0 || bytes.HasSuffix(file, []byte{'/'}) {
			// ls-files was sometimes showing directories when they were ignored
			// I couldn't create a test case for this as I couldn't reproduce correctly
			// for the moment, just exclude files with trailing /
			continue
		}
		newFiles = append(newFiles, string(file))
	}

	if revisionFrom != "" {
		cmd := exec.Command("git", "diff", revisionFrom)
		if revisionTo != "" {
			cmd.Args = append(cmd.Args, revisionTo)
		}
		cmd.Stdout = &patch
		if err := cmd.Run(); err != nil {
			return nil, nil, fmt.Errorf("error executing git diff %q %q: %s", revisionFrom, revisionTo, err)
		}

		if revisionTo == "" {
			return &patch, newFiles, nil
		}
		return &patch, nil, nil
	}

	// make a patch for unstaged changes
	// use --no-prefix to remove b/ given: +++ b/main.go
	cmd := exec.Command("git", "diff")
	cmd.Stdout = &patch
	if err := cmd.Run(); err != nil {
		return nil, nil, fmt.Errorf("error executing git diff: %s", err)
	}
	unstaged := patch.Len() > 0

	// If there's unstaged changes OR untracked changes (or both), then this is
	// a suitable patch
	if unstaged || newFiles != nil {
		return &patch, newFiles, nil
	}

	// check for changes in recent commit

	cmd = exec.Command("git", "diff", "HEAD~")
	cmd.Stdout = &patch
	if err := cmd.Run(); err != nil {
		return nil, nil, fmt.Errorf("error executing git diff HEAD~: %s", err)
	}

	return &patch, nil, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Martin Gallagher. All rights reserved.
// Use of this source code is governed by the Apache License,
// Version 2.0 that can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"gopkg.in/yaml.v2"
)

var (
	inputFile       = flag.String("i", "routes.yaml", "Routes input file")
	outputFile      = flag.String("o", "routes.go", "Routes output file")
	packageName     = flag.String("p", "", "Package name")
	varName         = flag.String("v", "Routes", "Variable name")
	errInvalidInput = errors.New("missing routes input file")
	errInvalidPath  = errors.New("invalid route path")
)

type routemap map[string]*route

type routes struct {
	params map[string]string
	routes routemap
}

type route struct {
	child                *route
	children             routemap
	param, check, handle string
}

func main() {
	flag.Parse()
	log.SetFlags(log.Lmicroseconds)

	if *inputFile == "" {
		log.Fatal("input filename is required (use -i flag)")
	} else if *packageName == "" {
		log.Fatal("package name is required (use -p flag)")
	}

	f, err := os.OpenFile(*outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)

	if err != nil {
		log.Fatal(err)
	}

	defer f.Close()

	r, err := loadRoutes()

	//spew.Dump(r)

	if err != nil {
		log.Fatal(err)
	}

	buf := &bytes.Buffer{}

	fmt.Fprintf(buf, `package %s

import "github.com/martingallagher/routify/router"

var %s =  router.Routes{
`, *packageName, *varName)

	for k, v := range r.routes {
		if len(v.children) == 0 {
			continue
		}

		r.writeRule(buf, k, v)
	}

	buf.WriteString("\n}")

	if _, err = buf.WriteTo(f); err != nil {
		log.Fatal(err)
	}
}

func (r *routes) add(method, path, handle string) error {
	if _, exists := r.routes[method]; !exists {
		r.routes[method] = &route{children: routemap{}}
	}

	var (
		p []string
		c = r.routes[method]
	)

	if path != "/" {
		p = strings.Split(path, "/")
	} else {
		p = []string{"/"}
	}

	for _, v := range p {
		if v == "" {
			return errInvalidPath
		}

		// Parameter
		if v[0] == '$' {
			c.child = &route{
				param:    v[1:],
				check:    r.params[v],
				children: routemap{},
			}

			c = c.child

			continue
		}

		// Allocate map for static routes
		if _, exists := c.children[v]; !exists {
			c.children[v] = &route{children: routemap{}}
		}

		c = c.children[v]
	}

	c.handle = handle

	return nil
}

func (r *routes) writeChild(buf *bytes.Buffer, c *route) {
	fmt.Fprintf(buf, "Child: &router.Route{\nParam: \"%s\",\n", c.param)

	if c.check != "" {
		fmt.Fprintf(buf, "Check: %s,\n", c.check)
	}

	if c.handle != "" {
		fmt.Fprintf(buf, "HandlerFunc: %s,\n", c.handle)
	}

	if len(c.children) > 0 {
		r.writeChildren(buf, c)
	} else if c.child != nil {
		r.writeChild(buf, c.child)
	}

	buf.WriteString("},\n")
}

func (r *routes) writeChildren(buf *bytes.Buffer, c *route) {
	buf.WriteString("Children: router.Routes{\n")

	for k, v := range c.children {
		r.writeRule(buf, k, v)
	}

	buf.WriteString("},\n")
}

func (r *routes) writeRule(buf *bytes.Buffer, p string, c *route) {
	fmt.Fprintf(buf, "\"%s\": &router.Route{\n", p)

	if c.handle != "" {
		fmt.Fprintf(buf, "HandlerFunc: %s,\n", c.handle)
	}

	if len(c.children) > 0 {
		r.writeChildren(buf, c)
	} else if c.child != nil {
		r.writeChild(buf, c.child)
	}

	buf.WriteString("},\n")
}

func loadRoutes() (*routes, error) {
	f, err := os.Open(*inputFile)

	if err != nil {
		return nil, err
	}

	defer f.Close()

	b, err := ioutil.ReadAll(f)

	if err != nil {
		return nil, err
	}

	f.Close()

	var m map[string]interface{}

	if err = yaml.Unmarshal(b, &m); err != nil {
		return nil, err
	}

	var (
		l [][]string
		r = &routes{map[string]string{}, routemap{}}
	)

	for k, v := range m {
		p, ok := v.(map[interface{}]interface{})

		if !ok {
			continue
		}

		switch u := strings.ToUpper(k); u {
		case "PARAMS", "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HELP":
			for a, b := range p {
				t, ok := a.(string)

				if !ok {
					break
				}

				f, ok := b.(string)

				if !ok {
					break
				}

				if u == "PARAMS" {
					r.params[t] = f

					continue
				}

				l = append(l, []string{u, t, f})
			}

		default:
			for a, b := range p {
				t, ok := a.(string)

				if !ok {
					break
				} else if t != "GET" && t != "POST" && t != "PUT" && t != "PATCH" && t != "DELETE" && t != "OPTIONS" && t != "HELP" {
					continue
				}

				f, ok := b.(string)

				if !ok {
					continue
				}

				l = append(l, []string{t, k, f})
			}
		}
	}

	for _, c := range l {
		if err = r.add(c[0], c[1], c[2]); err != nil {
			return nil, err
		}
	}

	return r, nil
}
<commit_msg>Simplify routes construction; use the output file directly.<commit_after>// Copyright 2015 Martin Gallagher. All rights reserved.
// Use of this source code is governed by the Apache License,
// Version 2.0 that can be found in the LICENSE file.

package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"gopkg.in/yaml.v2"
)

var (
	inputFile       = flag.String("i", "routes.yaml", "Routes input file")
	outputFile      = flag.String("o", "routes.go", "Routes output file")
	packageName     = flag.String("p", "", "Package name")
	varName         = flag.String("v", "Routes", "Variable name")
	errInvalidInput = errors.New("missing routes input file")
	errInvalidPath  = errors.New("invalid route path")
)

type routemap map[string]*route

type routes struct {
	params map[string]string
	routes routemap
}

type route struct {
	child                *route
	children             routemap
	param, check, handle string
}

func main() {
	flag.Parse()
	log.SetFlags(log.Lmicroseconds)

	if *inputFile == "" {
		log.Fatal("input filename is required (use -i flag)")
	} else if *packageName == "" {
		log.Fatal("package name is required (use -p flag)")
	}

	f, err := os.OpenFile(*outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)

	if err != nil {
		log.Fatal(err)
	}

	defer f.Close()

	r, err := loadRoutes()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Fprintf(f, `package %s

import "github.com/martingallagher/routify/router"

var %s =  router.Routes{
`, *packageName, *varName)

	for k, v := range r.routes {
		if len(v.children) == 0 {
			continue
		}

		r.writeRule(f, k, v)
	}

	f.WriteString("\n}")

	if err = f.Sync(); err != nil {
		log.Fatal(err)
	}
}

func (r *routes) add(method, path, handle string) error {
	if _, exists := r.routes[method]; !exists {
		r.routes[method] = &route{children: routemap{}}
	}

	var (
		p []string
		c = r.routes[method]
	)

	if path != "/" {
		p = strings.Split(path, "/")
	} else {
		p = []string{"/"}
	}

	for _, v := range p {
		if v == "" {
			return errInvalidPath
		}

		// Parameter
		if v[0] == '$' {
			c.child = &route{
				param:    v[1:],
				check:    r.params[v],
				children: routemap{},
			}

			c = c.child

			continue
		}

		// Allocate map for static routes
		if _, exists := c.children[v]; !exists {
			c.children[v] = &route{children: routemap{}}
		}

		c = c.children[v]
	}

	c.handle = handle

	return nil
}

func (r *routes) writeChild(f *os.File, c *route) {
	fmt.Fprintf(f, "Child: &router.Route{\nParam: \"%s\",\n", c.param)

	if c.check != "" {
		fmt.Fprintf(f, "Check: %s,\n", c.check)
	}

	if c.handle != "" {
		fmt.Fprintf(f, "HandlerFunc: %s,\n", c.handle)
	}

	if len(c.children) > 0 {
		r.writeChildren(f, c)
	} else if c.child != nil {
		r.writeChild(f, c.child)
	}

	f.WriteString("},\n")
}

func (r *routes) writeChildren(f *os.File, c *route) {
	f.WriteString("Children: router.Routes{\n")

	for k, v := range c.children {
		r.writeRule(f, k, v)
	}

	f.WriteString("},\n")
}

func (r *routes) writeRule(f *os.File, p string, c *route) {
	fmt.Fprintf(f, "\"%s\": &router.Route{\n", p)

	if c.handle != "" {
		fmt.Fprintf(f, "HandlerFunc: %s,\n", c.handle)
	}

	if len(c.children) > 0 {
		r.writeChildren(f, c)
	} else if c.child != nil {
		r.writeChild(f, c.child)
	}

	f.WriteString("},\n")
}

func loadRoutes() (*routes, error) {
	f, err := os.Open(*inputFile)

	if err != nil {
		return nil, err
	}

	defer f.Close()

	b, err := ioutil.ReadAll(f)

	if err != nil {
		return nil, err
	}

	f.Close()

	var m map[string]interface{}

	if err = yaml.Unmarshal(b, &m); err != nil {
		return nil, err
	}

	var (
		l [][]string
		r = &routes{map[string]string{}, routemap{}}
	)

	for k, v := range m {
		p, ok := v.(map[interface{}]interface{})

		if !ok {
			continue
		}

		switch u := strings.ToUpper(k); u {
		case "PARAMS", "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HELP":
			for a, b := range p {
				t, ok := a.(string)

				if !ok {
					break
				}

				f, ok := b.(string)

				if !ok {
					break
				}

				if u == "PARAMS" {
					r.params[t] = f

					continue
				}

				l = append(l, []string{u, t, f})
			}

		default:
			for a, b := range p {
				t, ok := a.(string)

				if !ok {
					break
				} else if t != "GET" && t != "POST" && t != "PUT" && t != "PATCH" && t != "DELETE" && t != "OPTIONS" && t != "HELP" {
					continue
				}

				f, ok := b.(string)

				if !ok {
					continue
				}

				l = append(l, []string{t, k, f})
			}
		}
	}

	for _, c := range l {
		if err = r.add(c[0], c[1], c[2]); err != nil {
			return nil, err
		}
	}

	return r, nil
}
<|endoftext|>


<commit_before>package checkerlution

import (
	"fmt"
	"github.com/couchbaselabs/logg"
	cbot "github.com/tleyden/checkers-bot"
	core "github.com/tleyden/checkers-core"
	ng "github.com/tleyden/neurgo"
)

type OperationMode int

const (
	RUNNING_MODE = iota
	TRAINING_MODE
)

type Checkerlution struct {
	ourTeamId            cbot.TeamType
	cortex               *ng.Cortex
	currentGameState     GameStateVector
	currentPossibleMove  ValidMoveCortexInput
	latestActuatorOutput []float64
	mode                 OperationMode
	latestFitnessScore   float64
}

func (c *Checkerlution) Start(ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.CreateNeurgoCortex()
	cortex := c.cortex
	cortex.Run()

}

func (c *Checkerlution) StartWithCortex(cortex *ng.Cortex, ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.setSensorActuatorFunctions(cortex)
	c.cortex = cortex
	cortex.Run()
}

func (c *Checkerlution) Think(gameState cbot.GameState) (bestMove cbot.ValidMove, ok bool) {

	ok = true
	ourTeam := gameState.Teams[c.ourTeamId]
	allValidMoves := ourTeam.AllValidMoves()
	if len(allValidMoves) > 0 {

		// convert into core.board representation
		board := gameState.Export()
		logg.LogTo("DEBUG", "Before move %v", board.CompactString(true))

		// generate best move (will be a core.move) -- initially, pick random
		move := c.generateBestMove(board)

		// search allValidMoves to find corresponding valid move
		found, bestValidMoveIndex := cbot.CorrespondingValidMoveIndex(move, allValidMoves)

		if !found {
			msg := "Could not find corresponding valid move: %v in %v"
			logg.LogPanic(msg, move, allValidMoves)
		} else {
			bestMove = allValidMoves[bestValidMoveIndex]
		}

		// this is just for debugging purposes
		player := cbot.GetCorePlayer(c.ourTeamId)
		boardPostMove := board.ApplyMove(player, move)
		logg.LogTo("DEBUG", "After move %v", boardPostMove.CompactString(true))

		return

	} else {
		ok = false
	}

	return

}

func (c *Checkerlution) GameFinished(gameState cbot.GameState) (shouldQuit bool) {
	switch c.mode {
	case TRAINING_MODE:
		shouldQuit = true
		c.latestFitnessScore = c.calculateFitness(gameState)
	case RUNNING_MODE:
		shouldQuit = false
	}
	return
}

func (c Checkerlution) Cortex() *ng.Cortex {
	return c.cortex
}

func (c *Checkerlution) SetMode(mode OperationMode) {
	c.mode = mode
}

func (c Checkerlution) calculateFitness(gameState cbot.GameState) (fitness float64) {
	weWon := (gameState.WinningTeam == c.ourTeamId)
	switch weWon {
	case true:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on winning.  Turn: %v", gameState.Turn)
		// fitness will be a positive number
		// the least amount of moves we made, the higher the fitness
		fitness = 200
		fitness -= float64(gameState.Turn)
		if fitness < 1 {
			fitness = 1 // lowest possible fitness when winning
		}
	case false:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on losing.  Turn: %v", gameState.Turn)
		// fitness will be a negative number
		// the least amount of moves we made, the lower (more negative)
		// the fitness, because we didn't put up much of a fight
		fitness = -200
		fitness += float64(gameState.Turn)
		if fitness > -1 {
			fitness = -1 // highest possible fitness when losing
		}
	}

	logg.LogTo("CHECKERLUTION", "calculateFitness returning: %v", fitness)
	return
}

func (c *Checkerlution) CreateNeurgoCortex() {

	uuid := ng.NewUuid()
	cortexUuid := fmt.Sprintf("cortex-%s", uuid)
	nodeId := ng.NewCortexId(cortexUuid)

	c.cortex = &ng.Cortex{
		NodeId: nodeId,
	}

	c.cortex.Init()

	c.CreateSensors()

	outputNeuron := c.CreateOutputNeuron()
	layer1Neurons := c.CreateHiddenLayer1Neurons(outputNeuron)
	layer2Neurons := c.CreateHiddenLayer2Neurons(layer1Neurons, outputNeuron)

	// combine all into single slice and add neurons to cortex
	neurons := []*ng.Neuron{}
	neurons = append(neurons, layer1Neurons...)
	neurons = append(neurons, layer2Neurons...)
	neurons = append(neurons, outputNeuron)
	c.cortex.SetNeurons(neurons)

	actuator := c.CreateActuator()

	// workaround for error
	// Cannot make outbound connection, dataChan == nil [recovered]
	c.cortex.Init()

	outputNeuron.ConnectOutbound(actuator)
	actuator.ConnectInbound(outputNeuron)

	c.cortex.MarshalJSONToFile("checkerlution_cortex.json")

}

func (c *Checkerlution) LoadNeurgoCortex(filename string) {

	cortex, err := ng.NewCortexFromJSONFile(filename)
	if err != nil {
		logg.LogPanic("Error reading cortex from: %v.  Err: %v", filename, err)
	}

	c.setSensorActuatorFunctions(cortex)

	c.cortex = cortex
}

func (c *Checkerlution) setSensorActuatorFunctions(cortex *ng.Cortex) {

	sensor := cortex.FindSensor(ng.NewSensorId("SensorGameState", 0))
	sensor.SensorFunction = c.sensorFuncGameState()
	sensor = cortex.FindSensor(ng.NewSensorId("SensorPossibleMove", 0))
	sensor.SensorFunction = c.sensorFuncPossibleMove()

	actuator := cortex.FindActuator(ng.NewActuatorId("Actuator", 0))
	actuator.ActuatorFunction = c.actuatorFunc()

}

func (c *Checkerlution) CreateHiddenLayer1Neurons(outputNeuron *ng.Neuron) []*ng.Neuron {

	cortex := c.cortex
	neurons := []*ng.Neuron{}
	layerIndex := 0.25

	for i := 0; i < 40; i++ {
		name := fmt.Sprintf("hidden-layer-%f-n-%d", layerIndex, i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, sensor := range cortex.Sensors {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateHiddenLayer2Neurons(layer1Neurons []*ng.Neuron, outputNeuron *ng.Neuron) []*ng.Neuron {

	neurons := []*ng.Neuron{}
	layerIndex := 0.35

	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("hidden-layer-%f-n-%d", layerIndex, i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, layer1Neuron := range layer1Neurons {
			layer1Neuron.ConnectOutbound(neuron)
			weights := ng.RandomWeights(1)
			neuron.ConnectInboundWeighted(layer1Neuron, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateOutputNeuron() *ng.Neuron {

	layerIndex := 0.45
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableTanh(),
		NodeId:             ng.NewNeuronId("OutputNeuron", layerIndex),
		Bias:               ng.RandomBias(),
	}

	// Workaround for error:
	// Cannot make outbound connection, dataChan == nil [recovered]
	// The best fix is to just load nn from json
	neuron.Init()

	return neuron

}

func (c *Checkerlution) CreateActuator() *ng.Actuator {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: c.actuatorFunc(),
	}
	c.cortex.SetActuators([]*ng.Actuator{actuator})
	return actuator

}

func (c *Checkerlution) actuatorFunc() ng.ActuatorFunction {
	return func(outputs []float64) {
		logg.LogTo("CHECKERLUTION", "actuator func called with: %v", outputs)
		c.latestActuatorOutput = outputs
	}
}

func (c *Checkerlution) CreateSensors() {

	sensorLayer := 0.0

	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: c.sensorFuncGameState(),
	}

	c.cortex.SetSensors([]*ng.Sensor{sensorGameState})

}

func (c *Checkerlution) sensorFuncGameState() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func game state called on thinker: %p, returning: %v", c, c.currentGameState)
		if len(c.currentGameState) == 0 {
			logg.LogPanic("sensor would return invalid gamestate")
		}
		return c.currentGameState
	}
}

func (c *Checkerlution) sensorFuncPossibleMove() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func possible move called")
		return c.currentPossibleMove.VectorRepresentation()
	}
}

func (c Checkerlution) Stop() {

}

func (c *Checkerlution) generateBestMove(board core.Board) core.Move {
	evalFunc := c.getEvaluationFunction()
	player := cbot.GetCorePlayer(c.ourTeamId)

	// with depth = 5, not working too well on first move.  when
	// there are only a few pieces on the board it seems to work,
	// but with full board .. taking a long time.

	depth := 4 // TODO: crank this up higher
	bestMove, scorePostMove := board.Minimax(player, depth, evalFunc)
	logg.LogTo("DEBUG", "scorePostMove: %v", scorePostMove)
	return bestMove
}

func (c *Checkerlution) getEvaluationFunction() core.EvaluationFunction {

	evalFunc := func(currentPlayer core.Player, board core.Board) float64 {

		// convert the board into inputs for the neural net (32 elt vector)
		// taking into account whether this player is "us" or not
		gameStateVector := NewGameStateVector()
		gameStateVector.loadFromBoard(board, currentPlayer)

		// send input to the neural net
		logg.LogTo("CHECKERLUTION", "set currentGameState %v", gameStateVector)
		c.currentGameState = gameStateVector
		c.cortex.SyncSensors()
		c.cortex.SyncActuators()

		// get output
		logg.LogTo("CHECKERLUTION", "actuator output %v", c.latestActuatorOutput[0])

		// return output
		return c.latestActuatorOutput[0]

	}
	return evalFunc

}
<commit_msg>fix crash that happened when trying to run in training mode<commit_after>package checkerlution

import (
	"fmt"
	"github.com/couchbaselabs/logg"
	cbot "github.com/tleyden/checkers-bot"
	core "github.com/tleyden/checkers-core"
	ng "github.com/tleyden/neurgo"
)

type OperationMode int

const (
	RUNNING_MODE = iota
	TRAINING_MODE
)

type Checkerlution struct {
	ourTeamId            cbot.TeamType
	cortex               *ng.Cortex
	currentGameState     GameStateVector
	currentPossibleMove  ValidMoveCortexInput
	latestActuatorOutput []float64
	mode                 OperationMode
	latestFitnessScore   float64
}

func (c *Checkerlution) Start(ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.CreateNeurgoCortex()
	cortex := c.cortex
	cortex.Run()

}

func (c *Checkerlution) StartWithCortex(cortex *ng.Cortex, ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.setSensorActuatorFunctions(cortex)
	c.cortex = cortex
	cortex.Run()
}

func (c *Checkerlution) Think(gameState cbot.GameState) (bestMove cbot.ValidMove, ok bool) {

	ok = true
	ourTeam := gameState.Teams[c.ourTeamId]
	allValidMoves := ourTeam.AllValidMoves()
	if len(allValidMoves) > 0 {

		// convert into core.board representation
		board := gameState.Export()
		logg.LogTo("DEBUG", "Before move %v", board.CompactString(true))

		// generate best move (will be a core.move) -- initially, pick random
		move := c.generateBestMove(board)

		// search allValidMoves to find corresponding valid move
		found, bestValidMoveIndex := cbot.CorrespondingValidMoveIndex(move, allValidMoves)

		if !found {
			msg := "Could not find corresponding valid move: %v in %v"
			logg.LogPanic(msg, move, allValidMoves)
		} else {
			bestMove = allValidMoves[bestValidMoveIndex]
		}

		// this is just for debugging purposes
		player := cbot.GetCorePlayer(c.ourTeamId)
		boardPostMove := board.ApplyMove(player, move)
		logg.LogTo("DEBUG", "After move %v", boardPostMove.CompactString(true))

		return

	} else {
		ok = false
	}

	return

}

func (c *Checkerlution) GameFinished(gameState cbot.GameState) (shouldQuit bool) {
	switch c.mode {
	case TRAINING_MODE:
		shouldQuit = true
		c.latestFitnessScore = c.calculateFitness(gameState)
	case RUNNING_MODE:
		shouldQuit = false
	}
	return
}

func (c Checkerlution) Cortex() *ng.Cortex {
	return c.cortex
}

func (c *Checkerlution) SetMode(mode OperationMode) {
	c.mode = mode
}

func (c Checkerlution) calculateFitness(gameState cbot.GameState) (fitness float64) {
	weWon := (gameState.WinningTeam == c.ourTeamId)
	switch weWon {
	case true:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on winning.  Turn: %v", gameState.Turn)
		// fitness will be a positive number
		// the least amount of moves we made, the higher the fitness
		fitness = 200
		fitness -= float64(gameState.Turn)
		if fitness < 1 {
			fitness = 1 // lowest possible fitness when winning
		}
	case false:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on losing.  Turn: %v", gameState.Turn)
		// fitness will be a negative number
		// the least amount of moves we made, the lower (more negative)
		// the fitness, because we didn't put up much of a fight
		fitness = -200
		fitness += float64(gameState.Turn)
		if fitness > -1 {
			fitness = -1 // highest possible fitness when losing
		}
	}

	logg.LogTo("CHECKERLUTION", "calculateFitness returning: %v", fitness)
	return
}

func (c *Checkerlution) CreateNeurgoCortex() {

	uuid := ng.NewUuid()
	cortexUuid := fmt.Sprintf("cortex-%s", uuid)
	nodeId := ng.NewCortexId(cortexUuid)

	c.cortex = &ng.Cortex{
		NodeId: nodeId,
	}

	c.cortex.Init()

	c.CreateSensors()

	outputNeuron := c.CreateOutputNeuron()
	layer1Neurons := c.CreateHiddenLayer1Neurons(outputNeuron)
	layer2Neurons := c.CreateHiddenLayer2Neurons(layer1Neurons, outputNeuron)

	// combine all into single slice and add neurons to cortex
	neurons := []*ng.Neuron{}
	neurons = append(neurons, layer1Neurons...)
	neurons = append(neurons, layer2Neurons...)
	neurons = append(neurons, outputNeuron)
	c.cortex.SetNeurons(neurons)

	actuator := c.CreateActuator()

	// workaround for error
	// Cannot make outbound connection, dataChan == nil [recovered]
	c.cortex.Init()

	outputNeuron.ConnectOutbound(actuator)
	actuator.ConnectInbound(outputNeuron)

	c.cortex.MarshalJSONToFile("checkerlution_cortex.json")

}

func (c *Checkerlution) LoadNeurgoCortex(filename string) {

	cortex, err := ng.NewCortexFromJSONFile(filename)
	if err != nil {
		logg.LogPanic("Error reading cortex from: %v.  Err: %v", filename, err)
	}

	c.setSensorActuatorFunctions(cortex)

	c.cortex = cortex
}

func (c *Checkerlution) setSensorActuatorFunctions(cortex *ng.Cortex) {

	sensor := cortex.FindSensor(ng.NewSensorId("SensorGameState", 0))
	sensor.SensorFunction = c.sensorFuncGameState()

	actuator := cortex.FindActuator(ng.NewActuatorId("Actuator", 0))
	actuator.ActuatorFunction = c.actuatorFunc()

}

func (c *Checkerlution) CreateHiddenLayer1Neurons(outputNeuron *ng.Neuron) []*ng.Neuron {

	cortex := c.cortex
	neurons := []*ng.Neuron{}
	layerIndex := 0.25

	for i := 0; i < 40; i++ {
		name := fmt.Sprintf("hidden-layer-%f-n-%d", layerIndex, i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, sensor := range cortex.Sensors {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateHiddenLayer2Neurons(layer1Neurons []*ng.Neuron, outputNeuron *ng.Neuron) []*ng.Neuron {

	neurons := []*ng.Neuron{}
	layerIndex := 0.35

	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("hidden-layer-%f-n-%d", layerIndex, i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, layer1Neuron := range layer1Neurons {
			layer1Neuron.ConnectOutbound(neuron)
			weights := ng.RandomWeights(1)
			neuron.ConnectInboundWeighted(layer1Neuron, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateOutputNeuron() *ng.Neuron {

	layerIndex := 0.45
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableTanh(),
		NodeId:             ng.NewNeuronId("OutputNeuron", layerIndex),
		Bias:               ng.RandomBias(),
	}

	// Workaround for error:
	// Cannot make outbound connection, dataChan == nil [recovered]
	// The best fix is to just load nn from json
	neuron.Init()

	return neuron

}

func (c *Checkerlution) CreateActuator() *ng.Actuator {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: c.actuatorFunc(),
	}
	c.cortex.SetActuators([]*ng.Actuator{actuator})
	return actuator

}

func (c *Checkerlution) actuatorFunc() ng.ActuatorFunction {
	return func(outputs []float64) {
		logg.LogTo("CHECKERLUTION", "actuator func called with: %v", outputs)
		c.latestActuatorOutput = outputs
	}
}

func (c *Checkerlution) CreateSensors() {

	sensorLayer := 0.0

	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: c.sensorFuncGameState(),
	}

	c.cortex.SetSensors([]*ng.Sensor{sensorGameState})

}

func (c *Checkerlution) sensorFuncGameState() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func game state called on thinker: %p, returning: %v", c, c.currentGameState)
		if len(c.currentGameState) == 0 {
			logg.LogPanic("sensor would return invalid gamestate")
		}
		return c.currentGameState
	}
}

func (c *Checkerlution) sensorFuncPossibleMove() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func possible move called")
		return c.currentPossibleMove.VectorRepresentation()
	}
}

func (c Checkerlution) Stop() {

}

func (c *Checkerlution) generateBestMove(board core.Board) core.Move {
	evalFunc := c.getEvaluationFunction()
	player := cbot.GetCorePlayer(c.ourTeamId)

	// with depth = 5, not working too well on first move.  when
	// there are only a few pieces on the board it seems to work,
	// but with full board .. taking a long time.

	depth := 4 // TODO: crank this up higher
	bestMove, scorePostMove := board.Minimax(player, depth, evalFunc)
	logg.LogTo("DEBUG", "scorePostMove: %v", scorePostMove)
	return bestMove
}

func (c *Checkerlution) getEvaluationFunction() core.EvaluationFunction {

	evalFunc := func(currentPlayer core.Player, board core.Board) float64 {

		// convert the board into inputs for the neural net (32 elt vector)
		// taking into account whether this player is "us" or not
		gameStateVector := NewGameStateVector()
		gameStateVector.loadFromBoard(board, currentPlayer)

		// send input to the neural net
		logg.LogTo("CHECKERLUTION", "set currentGameState %v", gameStateVector)
		c.currentGameState = gameStateVector
		c.cortex.SyncSensors()
		c.cortex.SyncActuators()

		// get output
		logg.LogTo("CHECKERLUTION", "actuator output %v", c.latestActuatorOutput[0])

		// return output
		return c.latestActuatorOutput[0]

	}
	return evalFunc

}
<|endoftext|>


<commit_before>package test

import (
	"fmt"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"testing"
	"time"
)

// TimeOut is used to panic if a test takes to long.
// It will print the current goroutines and panic.
// It is meant as an aid in debugging deadlocks.
func TimeOut(t time.Duration) *time.Timer {
	return time.AfterFunc(t, func() {
		if err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1); err != nil {
			fmt.Printf("failed to print goroutines: %v \n", err)
		}
		panic("timeout")
	})
}

// CheckRoutines is used to check for leaked go-routines
func CheckRoutines(t *testing.T) func() {
	initial := getRoutines()
	return func() {
		try := 0
		ticker := time.NewTicker(200 * time.Millisecond)
		defer ticker.Stop()
		for range ticker.C {
			routines := getRoutines()
			if len(routines) <= len(initial) {
				return
			}
			if try >= 50 {
				t.Fatalf("Unexpected routines: \n%s", strings.Join(routines, "\n\n"))
			}
			try++
		}
	}
}

func getRoutines() []string {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	return filterRoutines(strings.Split(string(buf), "\n\n"))
}

func filterRoutines(routines []string) []string {
	result := []string{}
	for _, stack := range routines {
		if stack == "" || // Empty
			strings.Contains(stack, "testing.Main(") || // Tests
			strings.Contains(stack, "testing.(*T).Run(") || // Test run
			strings.Contains(stack, "test.getRoutines(") { // This routine
			continue
		}
		result = append(result, stack)
	}
	return result
}

// GatherErrs gathers all errors returned by a channel.
// It blocks until the channel is closed.
func GatherErrs(c chan error) []error {
	errs := make([]error, 0)

	for err := range c {
		errs = append(errs, err)
	}

	return errs
}

// FlattenErrs flattens a slice of errors into a single error
func FlattenErrs(errs []error) error {
	var errstrings []string

	for _, err := range errs {
		if err != nil {
			errstrings = append(errstrings, err.Error())
		}
	}

	if len(errstrings) == 0 {
		return nil
	}

	return fmt.Errorf(strings.Join(errstrings, "\n"))
}
<commit_msg>Assert no leaks during startup in CheckRoutines<commit_after>package test

import (
	"fmt"
	"os"
	"runtime"
	"runtime/pprof"
	"strings"
	"testing"
	"time"
)

// TimeOut is used to panic if a test takes to long.
// It will print the current goroutines and panic.
// It is meant as an aid in debugging deadlocks.
func TimeOut(t time.Duration) *time.Timer {
	return time.AfterFunc(t, func() {
		if err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1); err != nil {
			fmt.Printf("failed to print goroutines: %v \n", err)
		}
		panic("timeout")
	})
}

// CheckRoutines is used to check for leaked go-routines
func CheckRoutines(t *testing.T) func() {
	tryLoop := func(failMessage string) {
		try := 0
		ticker := time.NewTicker(200 * time.Millisecond)
		defer ticker.Stop()
		for range ticker.C {
			routines := getRoutines()
			if len(routines) == 0 {
				return
			}
			if try >= 50 {
				t.Fatalf("%s: \n%s", failMessage, strings.Join(routines, "\n\n"))
			}
			try++
		}

	}

	tryLoop("Unexpected routines on test startup")
	return func() {
		tryLoop("Unexpected routines on test end")
	}
}

func getRoutines() []string {
	buf := make([]byte, 2<<20)
	buf = buf[:runtime.Stack(buf, true)]
	return filterRoutines(strings.Split(string(buf), "\n\n"))
}

func filterRoutines(routines []string) []string {
	result := []string{}
	for _, stack := range routines {
		if stack == "" || // Empty
			strings.Contains(stack, "testing.Main(") || // Tests
			strings.Contains(stack, "testing.(*T).Run(") || // Test run
			strings.Contains(stack, "test.getRoutines(") { // This routine
			continue
		}
		result = append(result, stack)
	}
	return result
}

// GatherErrs gathers all errors returned by a channel.
// It blocks until the channel is closed.
func GatherErrs(c chan error) []error {
	errs := make([]error, 0)

	for err := range c {
		errs = append(errs, err)
	}

	return errs
}

// FlattenErrs flattens a slice of errors into a single error
func FlattenErrs(errs []error) error {
	var errstrings []string

	for _, err := range errs {
		if err != nil {
			errstrings = append(errstrings, err.Error())
		}
	}

	if len(errstrings) == 0 {
		return nil
	}

	return fmt.Errorf(strings.Join(errstrings, "\n"))
}
<|endoftext|>


<commit_before>// +build go1.4

package jwt

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
)

// Implements the RSAPSS family of signing methods signing methods
type SigningMethodRSAPSS struct {
	*SigningMethodRSA
	Options *rsa.PSSOptions
}

// Specific instances for RS/PS and company
var (
	SigningMethodPS256 *SigningMethodRSAPSS
	SigningMethodPS384 *SigningMethodRSAPSS
	SigningMethodPS512 *SigningMethodRSAPSS
)

func init() {
	// PS256
	SigningMethodPS256 = &SigningMethodRSAPSS{
		&SigningMethodRSA{
			Name: "PS256",
			Hash: crypto.SHA256,
		},
		&rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthAuto,
			Hash:       crypto.SHA256,
		},
	}
	RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod {
		return SigningMethodPS256
	})

	// PS384
	SigningMethodPS384 = &SigningMethodRSAPSS{
		&SigningMethodRSA{
			Name: "PS384",
			Hash: crypto.SHA384,
		},
		&rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthAuto,
			Hash:       crypto.SHA384,
		},
	}
	RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod {
		return SigningMethodPS384
	})

	// PS512
	SigningMethodPS512 = &SigningMethodRSAPSS{
		&SigningMethodRSA{
			Name: "PS512",
			Hash: crypto.SHA512,
		},
		&rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthAuto,
			Hash:       crypto.SHA512,
		},
	}
	RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod {
		return SigningMethodPS512
	})
}

// Implements the Verify method from SigningMethod
// For this verify method, key must be an rsa.PrivateKey struct
func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error {
	var err error

	// Decode the signature
	var sig []byte
	if sig, err = DecodeSegment(signature); err != nil {
		return err
	}

	var rsaKey *rsa.PublicKey
	switch k := key.(type) {
	case *rsa.PublicKey:
		rsaKey = k
	default:
		return ErrInvalidKey
	}

	// Create hasher
	if !m.Hash.Available() {
		return ErrHashUnavailable
	}
	hasher := m.Hash.New()
	hasher.Write([]byte(signingString))

	return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options)
}

// Implements the Sign method from SigningMethod
// For this signing method, key must be an rsa.PublicKey struct
func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) {
	var rsaKey *rsa.PrivateKey

	switch k := key.(type) {
	case *rsa.PrivateKey:
		rsaKey = k
	default:
		return "", ErrInvalidKey
	}

	// Create the hasher
	if !m.Hash.Available() {
		return "", ErrHashUnavailable
	}

	hasher := m.Hash.New()
	hasher.Write([]byte(signingString))

	// Sign the string and return the encoded bytes
	if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil {
		return EncodeSegment(sigBytes), nil
	} else {
		return "", err
	}
}
<commit_msg>fixes #95 - incorrect documentation<commit_after>// +build go1.4

package jwt

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
)

// Implements the RSAPSS family of signing methods signing methods
type SigningMethodRSAPSS struct {
	*SigningMethodRSA
	Options *rsa.PSSOptions
}

// Specific instances for RS/PS and company
var (
	SigningMethodPS256 *SigningMethodRSAPSS
	SigningMethodPS384 *SigningMethodRSAPSS
	SigningMethodPS512 *SigningMethodRSAPSS
)

func init() {
	// PS256
	SigningMethodPS256 = &SigningMethodRSAPSS{
		&SigningMethodRSA{
			Name: "PS256",
			Hash: crypto.SHA256,
		},
		&rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthAuto,
			Hash:       crypto.SHA256,
		},
	}
	RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod {
		return SigningMethodPS256
	})

	// PS384
	SigningMethodPS384 = &SigningMethodRSAPSS{
		&SigningMethodRSA{
			Name: "PS384",
			Hash: crypto.SHA384,
		},
		&rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthAuto,
			Hash:       crypto.SHA384,
		},
	}
	RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod {
		return SigningMethodPS384
	})

	// PS512
	SigningMethodPS512 = &SigningMethodRSAPSS{
		&SigningMethodRSA{
			Name: "PS512",
			Hash: crypto.SHA512,
		},
		&rsa.PSSOptions{
			SaltLength: rsa.PSSSaltLengthAuto,
			Hash:       crypto.SHA512,
		},
	}
	RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod {
		return SigningMethodPS512
	})
}

// Implements the Verify method from SigningMethod
// For this verify method, key must be an rsa.PublicKey struct
func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error {
	var err error

	// Decode the signature
	var sig []byte
	if sig, err = DecodeSegment(signature); err != nil {
		return err
	}

	var rsaKey *rsa.PublicKey
	switch k := key.(type) {
	case *rsa.PublicKey:
		rsaKey = k
	default:
		return ErrInvalidKey
	}

	// Create hasher
	if !m.Hash.Available() {
		return ErrHashUnavailable
	}
	hasher := m.Hash.New()
	hasher.Write([]byte(signingString))

	return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options)
}

// Implements the Sign method from SigningMethod
// For this signing method, key must be an rsa.PrivateKey struct
func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) {
	var rsaKey *rsa.PrivateKey

	switch k := key.(type) {
	case *rsa.PrivateKey:
		rsaKey = k
	default:
		return "", ErrInvalidKey
	}

	// Create the hasher
	if !m.Hash.Available() {
		return "", ErrHashUnavailable
	}

	hasher := m.Hash.New()
	hasher.Write([]byte(signingString))

	// Sign the string and return the encoded bytes
	if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil {
		return EncodeSegment(sigBytes), nil
	} else {
		return "", err
	}
}
<|endoftext|>