<commit_before>package store

import (
	"fmt"
	"io"
	"reflect"
	"sync"
	"time"

	"code.google.com/p/go-sqlite/go1/sqlite3"
)

const (
	add = iota
	get
	update
	remove
	getPage
)

type TypeMap map[string]interface{}

type Interface interface {
	Get() TypeMap
	Key() string
}

type InterfaceTable interface {
	Interface
	TableName() string
}

type statement struct {
	*sqlite3.Stmt
	vars []string
}

func (s statement) Vars(t map[string]interface{}) []interface{} {
	r := make([]interface{}, len(s.vars))
	for i, v := range s.vars {
		r[i] = t[v]
	}
	return r
}

type Store struct {
	db         *sqlite3.Conn
	mutex      sync.Mutex
	statements map[string][]statement
}

func NewStore(filename string) (*Store, error) {
	s, err := sqlite3.Open(filename)
	if err != nil {
		return nil, err
	}
	return &Store{
		db:         s,
		statements: make(map[string][]statement),
	}, nil
}

func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) Register(t Interface) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	tableName := TableName(t)
	tVars := t.Get()

	var sqlVars, sqlParams, setSQLParams, tableVars string
	vars := make([]string, 0, len(tVars))

	first := true
	oFirst := true
	primary := t.Key()

	for typeName, typeVal := range tVars {
		if first {
			first = false
		} else {
			tableVars += ", "
		}
		if primary != typeName {
			if oFirst {
				oFirst = false
			} else {
				sqlVars += ", "
				setSQLParams += ", "
				sqlParams += ", "
			}
		}
		varType := getType(typeVal)
		tableVars += "[" + typeName + "] " + varType
		if primary == typeName {
			tableVars += " PRIMARY KEY AUTOINCREMENT"
		} else {
			sqlVars += "[" + typeName + "]"
			setSQLParams += "[" + typeName + "] = ?"
			sqlParams += "?"
			vars = append(vars, typeName)
		}
	}
	sql := "CREATE TABLE IF NOT EXISTS [" + tableName + "](" + tableVars + ");"
	err := s.db.Exec(sql)
	if err != nil {
		return err
	}
	s.statements[tableName] = make([]statement, 5)

	sql = "INSERT INTO [" + tableName + "] (" + sqlVars + ") VALUES (" + sqlParams + ");"
	stmt, err := s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][add] = statement{Stmt: stmt, vars: vars}

	sql = "SELECT " + sqlVars + " FROM [" + tableName + "] WHERE [" + primary + "] = ? LIMIT 1;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][get] = statement{Stmt: stmt, vars: vars}

	sql = "UPDATE [" + tableName + "] SET " + setSQLParams + " WHERE [" + primary + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][update] = statement{Stmt: stmt, vars: append(vars, primary)}

	sql = "DELETE FROM [" + tableName + "] WHERE [" + primary + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][remove] = statement{Stmt: stmt}

	sql = "SELECT " + sqlVars + " FROM [" + tableName + "] ORDER BY [" + primary + "] LIMIT ? OFFSET ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][getPage] = statement{Stmt: stmt, vars: vars}

	return nil
}

func (s *Store) Set(ts ...Interface) (id int, err error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, t := range ts {
		tableName := TableName(t)
		vars := t.Get()
		p := vars[t.Key()]
		var primary int
		if v, ok := p.(*int); ok {
			primary = *v
		} else {
			err = fmt.Errorf("could not decide key")
			return
		}
		if primary == 0 {
			stmt := s.statements[tableName][add]
			err = stmt.Exec(unPointers(stmt.Vars(vars))...)
			if err != nil {
				return
			}
			id = int(s.db.LastInsertId())
		} else {
			stmt := s.statements[tableName][update]
			err = stmt.Exec(unPointers(stmt.Vars(vars))...)
			if err != nil {
				return
			}
			id = primary
		}
	}
	return
}

func (s *Store) Get(ts ...Interface) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, t := range ts {
		tableName := TableName(t)
		stmt := s.statements[tableName][get]
		vars := t.Get()
		err := stmt.Query(unPointer(vars[t.Key()]))
		if err != nil && err != io.EOF {
			return err
		}
		err = stmt.Scan(stmt.Vars(vars)...)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) GetPage(data []Interface, offset int) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if len(data) < 1 {
		return nil
	}
	tableName := TableName(data[0])
	stmt := s.statements[tableName][getPage]
	var (
		err error
		pos int
	)
	for err = stmt.Query(len(data), offset); err == nil; err = stmt.Next() {
		err = stmt.Scan(stmt.Vars(data[pos].Get()))
		if err != nil {
			break
		}
		pos++
	}
	if err != nil && err != io.EOF {
		return err
	}
	return nil
}

func (s *Store) Delete(ts ...Interface) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, t := range ts {
		tableName := TableName(t)
		err := s.statements[tableName][remove].Exec(unPointer(t.Get()[t.Key()]))
		if err != nil {
			return err
		}
	}
	return nil
}

func TableName(t Interface) string {
	if it, ok := t.(InterfaceTable); ok {
		return it.TableName()
	}
	name := reflect.TypeOf(t).String()
	if name[0] == '*' {
		name = name[1:]
	}
	return name
}

func getType(i interface{}) string {
	switch i.(type) {
	case *int, *int64, *time.Time:
		return "INTEGER"
	case *float64:
		return "FLOAT"
	case *string:
		return "TEXT"
	case *[]byte:
		return "BLOB"
	}
	return ""
}

func unPointers(is []interface{}) []interface{} {
	for n := range is {
		is[n] = unPointer(is[n])
	}
	return is
}

func unPointer(i interface{}) interface{} {
	switch v := i.(type) {
	case *int:
		return *v
	case *int64:
		return *v
	case *float64:
		return *v
	case *string:
		return *v
	case *[]byte:
		return *v
	default:
		return nil
	}
}
<commit_msg>Added bool support<commit_after>package store

import (
	"fmt"
	"io"
	"reflect"
	"sync"
	"time"

	"code.google.com/p/go-sqlite/go1/sqlite3"
)

const (
	add = iota
	get
	update
	remove
	getPage
)

type TypeMap map[string]interface{}

type Interface interface {
	Get() TypeMap
	Key() string
}

type InterfaceTable interface {
	Interface
	TableName() string
}

type statement struct {
	*sqlite3.Stmt
	vars []string
}

func (s statement) Vars(t map[string]interface{}) []interface{} {
	r := make([]interface{}, len(s.vars))
	for i, v := range s.vars {
		r[i] = t[v]
	}
	return r
}

type Store struct {
	db         *sqlite3.Conn
	mutex      sync.Mutex
	statements map[string][]statement
}

func NewStore(filename string) (*Store, error) {
	s, err := sqlite3.Open(filename)
	if err != nil {
		return nil, err
	}
	return &Store{
		db:         s,
		statements: make(map[string][]statement),
	}, nil
}

func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) Register(t Interface) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	tableName := TableName(t)
	tVars := t.Get()

	var sqlVars, sqlParams, setSQLParams, tableVars string
	vars := make([]string, 0, len(tVars))

	first := true
	oFirst := true
	primary := t.Key()

	for typeName, typeVal := range tVars {
		if first {
			first = false
		} else {
			tableVars += ", "
		}
		if primary != typeName {
			if oFirst {
				oFirst = false
			} else {
				sqlVars += ", "
				setSQLParams += ", "
				sqlParams += ", "
			}
		}
		varType := getType(typeVal)
		tableVars += "[" + typeName + "] " + varType
		if primary == typeName {
			tableVars += " PRIMARY KEY AUTOINCREMENT"
		} else {
			sqlVars += "[" + typeName + "]"
			setSQLParams += "[" + typeName + "] = ?"
			sqlParams += "?"
			vars = append(vars, typeName)
		}
	}
	sql := "CREATE TABLE IF NOT EXISTS [" + tableName + "](" + tableVars + ");"
	err := s.db.Exec(sql)
	if err != nil {
		return err
	}
	s.statements[tableName] = make([]statement, 5)

	sql = "INSERT INTO [" + tableName + "] (" + sqlVars + ") VALUES (" + sqlParams + ");"
	stmt, err := s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][add] = statement{Stmt: stmt, vars: vars}

	sql = "SELECT " + sqlVars + " FROM [" + tableName + "] WHERE [" + primary + "] = ? LIMIT 1;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][get] = statement{Stmt: stmt, vars: vars}

	sql = "UPDATE [" + tableName + "] SET " + setSQLParams + " WHERE [" + primary + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][update] = statement{Stmt: stmt, vars: append(vars, primary)}

	sql = "DELETE FROM [" + tableName + "] WHERE [" + primary + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][remove] = statement{Stmt: stmt}

	sql = "SELECT " + sqlVars + " FROM [" + tableName + "] ORDER BY [" + primary + "] LIMIT ? OFFSET ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	s.statements[tableName][getPage] = statement{Stmt: stmt, vars: vars}

	return nil
}

func (s *Store) Set(ts ...Interface) (id int, err error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, t := range ts {
		tableName := TableName(t)
		vars := t.Get()
		p := vars[t.Key()]
		var primary int
		if v, ok := p.(*int); ok {
			primary = *v
		} else {
			err = fmt.Errorf("could not decide key")
			return
		}
		if primary == 0 {
			stmt := s.statements[tableName][add]
			err = stmt.Exec(unPointers(stmt.Vars(vars))...)
			if err != nil {
				return
			}
			id = int(s.db.LastInsertId())
		} else {
			stmt := s.statements[tableName][update]
			err = stmt.Exec(unPointers(stmt.Vars(vars))...)
			if err != nil {
				return
			}
			id = primary
		}
	}
	return
}

func (s *Store) Get(ts ...Interface) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, t := range ts {
		tableName := TableName(t)
		stmt := s.statements[tableName][get]
		vars := t.Get()
		err := stmt.Query(unPointer(vars[t.Key()]))
		if err != nil && err != io.EOF {
			return err
		}
		err = stmt.Scan(stmt.Vars(vars)...)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) GetPage(data []Interface, offset int) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if len(data) < 1 {
		return nil
	}
	tableName := TableName(data[0])
	stmt := s.statements[tableName][getPage]
	var (
		err error
		pos int
	)
	for err = stmt.Query(len(data), offset); err == nil; err = stmt.Next() {
		err = stmt.Scan(stmt.Vars(data[pos].Get()))
		if err != nil {
			break
		}
		pos++
	}
	if err != nil && err != io.EOF {
		return err
	}
	return nil
}

func (s *Store) Delete(ts ...Interface) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, t := range ts {
		tableName := TableName(t)
		err := s.statements[tableName][remove].Exec(unPointer(t.Get()[t.Key()]))
		if err != nil {
			return err
		}
	}
	return nil
}

func TableName(t Interface) string {
	if it, ok := t.(InterfaceTable); ok {
		return it.TableName()
	}
	name := reflect.TypeOf(t).String()
	if name[0] == '*' {
		name = name[1:]
	}
	return name
}

func getType(i interface{}) string {
	switch i.(type) {
	case *bool, *int, *int64, *time.Time:
		return "INTEGER"
	case *float64:
		return "FLOAT"
	case *string:
		return "TEXT"
	case *[]byte:
		return "BLOB"
	}
	return ""
}

func unPointers(is []interface{}) []interface{} {
	for n := range is {
		is[n] = unPointer(is[n])
	}
	return is
}

func unPointer(i interface{}) interface{} {
	switch v := i.(type) {
	case *bool:
		return *v
	case *int:
		return *v
	case *int64:
		return *v
	case *float64:
		return *v
	case *string:
		return *v
	case *[]byte:
		return *v
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package bolster

import (
	"errors"
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/nochso/bolster/bytesort"
	"github.com/nochso/bolster/codec"
	"github.com/nochso/bolster/codec/json"
	"github.com/nochso/bolster/errlist"
)

const (
	tagBolster       = "bolster"
	tagID            = "id"
	tagAutoIncrement = "inc"
)

// Store can store and retrieve structs.
type Store struct {
	codec codec.Interface
	db    *bolt.DB
	types map[reflect.Type]structType
}

// Open creates and opens a Store.
func Open(path string, mode os.FileMode, options *bolt.Options) (*Store, error) {
	db, err := bolt.Open(path, mode, options)
	if err != nil {
		return nil, err
	}
	st := &Store{
		codec: json.Codec,
		db:    db,
		types: make(map[reflect.Type]structType),
	}
	return st, nil
}

// Bolt returns the bolt.DB instance.
func (s *Store) Bolt() *bolt.DB {
	return s.db
}

// Close releases all database resources.
// All transactions must be closed before closing the database.
func (s *Store) Close() error {
	return s.db.Close()
}

// Read executes a function within the context of a managed read-only transaction.
// Any error that is returned from the function is returned from the View() method.
func (s *Store) Read(fn func(*Tx) error) error {
	return s.db.View(func(btx *bolt.Tx) error {
		tx := &Tx{btx: btx, store: s, errs: errlist.New()}
		err := fn(tx)
		if err != nil {
			return err
		}
		return tx.errs.ErrorOrNil()
	})
}

// Write executes a function within the context of a read-write managed transaction.
// If no error is returned from the function then the transaction is committed.
// If an error is returned then the entire transaction is rolled back.
// Any error that is returned from the function or returned from the commit is
// returned from the Write() method.
func (s *Store) Write(fn func(*Tx) error) error {
	return s.db.Update(func(btx *bolt.Tx) error {
		tx := &Tx{btx: btx, store: s, errs: errlist.New()}
		err := fn(tx)
		if err != nil {
			return err
		}
		return tx.errs.ErrorOrNil()
	})
}

// Register validates struct types for later use.
// Structs that have not been registered can not be used.
func (s *Store) Register(v ...interface{}) error {
	errs := errlist.New()
	for _, vv := range v {
		errs.Append(s.register(vv))
	}
	return errs.ErrorOrNil()
}

func (s *Store) register(v interface{}) error {
	e := newErrorFactory(register)
	t := reflect.TypeOf(v)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return e.with(fmt.Errorf("expected struct, got %v", t.Kind()))
	}
	if st, exists := s.types[t]; exists {
		e.structType = st
		return e.with(errors.New("type is already registered"))
	}
	st, err := newStructType(t)
	e.structType = st
	if err != nil {
		return e.with(err)
	}
	s.types[st.Type] = st
	err = s.Write(func(tx *Tx) error {
		_, err = tx.btx.CreateBucketIfNotExists(st.FullName)
		return err
	})
	return e.with(err)
}

type structType struct {
	FullName []byte
	ID       idField
	Type     reflect.Type
}

func newStructType(t reflect.Type) (structType, error) {
	st := &structType{
		FullName: []byte(t.PkgPath() + "." + t.Name()),
		Type:     t,
	}
	var err error
	st.ID, err = newIDField(t)
	if err != nil {
		return *st, err
	}
	err = st.validateBytesort()
	return *st, err
}

func (st *structType) validateBytesort() error {
	zv := reflect.Zero(st.ID.Type)
	_, err := bytesort.Encode(zv.Interface())
	if err != nil {
		err = fmt.Errorf("ID field %q is not byte encodable: %s", st.ID.Name, err)
	}
	return err
}

func (st structType) String() string {
	return string(st.FullName)
}

type idField struct {
	StructPos     int
	AutoIncrement bool
	reflect.StructField
}

func newIDField(t reflect.Type) (idField, error) {
	id := idField{StructPos: -1}
	tags := newTagList(t)
	idKeys := tags.filter(tagID)
	if len(idKeys) > 1 {
		return id, fmt.Errorf("must not have multiple fields with tag %q", tagID)
	} else if len(idKeys) == 1 {
		id.StructPos = idKeys[0]
	} else if idField, ok := t.FieldByName("ID"); ok {
		id.StructPos = idField.Index[0]
	}
	if id.StructPos == -1 {
		return id, errors.New("unable to find ID field: field has to be named \"ID\" or tagged with `bolster:\"id\"`")
	}
	id.StructField = t.Field(id.StructPos)
	id.AutoIncrement = tags.contains(id.StructPos, tagAutoIncrement)
	return id, nil
}

type tagList [][]string

// newTagList returns a list of bolster tags for each struct field.
func newTagList(rt reflect.Type) tagList {
	tl := make([][]string, 0, rt.NumField())
	for i := 0; i < rt.NumField(); i++ {
		ftags := strings.Split(rt.Field(i).Tag.Get(tagBolster), ",")
		tl = append(tl, ftags)
	}
	return tl
}

// filter returns the positions of fields containing a tag s.
func (tl tagList) filter(s string) []int {
	keys := []int{}
	for i := range tl {
		if tl.contains(i, s) {
			keys = append(keys, i)
		}
	}
	return keys
}

// contains returns true when i'th field contains tag s.
func (tl tagList) contains(i int, s string) bool {
	for _, w := range tl[i] {
		if w == s {
			return true
		}
	}
	return false
}
<commit_msg>Non-integer IDs can not be autoincremented<commit_after>package bolster

import (
	"errors"
	"fmt"
	"os"
	"reflect"
	"strings"

	"github.com/boltdb/bolt"
	"github.com/nochso/bolster/bytesort"
	"github.com/nochso/bolster/codec"
	"github.com/nochso/bolster/codec/json"
	"github.com/nochso/bolster/errlist"
)

const (
	tagBolster       = "bolster"
	tagID            = "id"
	tagAutoIncrement = "inc"
)

// Store can store and retrieve structs.
type Store struct {
	codec codec.Interface
	db    *bolt.DB
	types map[reflect.Type]structType
}

// Open creates and opens a Store.
func Open(path string, mode os.FileMode, options *bolt.Options) (*Store, error) {
	db, err := bolt.Open(path, mode, options)
	if err != nil {
		return nil, err
	}
	st := &Store{
		codec: json.Codec,
		db:    db,
		types: make(map[reflect.Type]structType),
	}
	return st, nil
}

// Bolt returns the bolt.DB instance.
func (s *Store) Bolt() *bolt.DB {
	return s.db
}

// Close releases all database resources.
// All transactions must be closed before closing the database.
func (s *Store) Close() error {
	return s.db.Close()
}

// Read executes a function within the context of a managed read-only transaction.
// Any error that is returned from the function is returned from the View() method.
func (s *Store) Read(fn func(*Tx) error) error {
	return s.db.View(func(btx *bolt.Tx) error {
		tx := &Tx{btx: btx, store: s, errs: errlist.New()}
		err := fn(tx)
		if err != nil {
			return err
		}
		return tx.errs.ErrorOrNil()
	})
}

// Write executes a function within the context of a read-write managed transaction.
// If no error is returned from the function then the transaction is committed.
// If an error is returned then the entire transaction is rolled back.
// Any error that is returned from the function or returned from the commit is
// returned from the Write() method.
func (s *Store) Write(fn func(*Tx) error) error {
	return s.db.Update(func(btx *bolt.Tx) error {
		tx := &Tx{btx: btx, store: s, errs: errlist.New()}
		err := fn(tx)
		if err != nil {
			return err
		}
		return tx.errs.ErrorOrNil()
	})
}

// Register validates struct types for later use.
// Structs that have not been registered can not be used.
func (s *Store) Register(v ...interface{}) error {
	errs := errlist.New()
	for _, vv := range v {
		errs.Append(s.register(vv))
	}
	return errs.ErrorOrNil()
}

func (s *Store) register(v interface{}) error {
	e := newErrorFactory(register)
	t := reflect.TypeOf(v)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return e.with(fmt.Errorf("expected struct, got %v", t.Kind()))
	}
	if st, exists := s.types[t]; exists {
		e.structType = st
		return e.with(errors.New("type is already registered"))
	}
	st, err := newStructType(t)
	e.structType = st
	if err != nil {
		return e.with(err)
	}
	s.types[st.Type] = st
	err = s.Write(func(tx *Tx) error {
		_, err = tx.btx.CreateBucketIfNotExists(st.FullName)
		return err
	})
	return e.with(err)
}

type structType struct {
	FullName []byte
	ID       idField
	Type     reflect.Type
}

func newStructType(t reflect.Type) (structType, error) {
	st := &structType{
		FullName: []byte(t.PkgPath() + "." + t.Name()),
		Type:     t,
	}
	var err error
	st.ID, err = newIDField(t)
	if err != nil {
		return *st, err
	}
	err = st.validateBytesort()
	return *st, err
}

func (st *structType) validateBytesort() error {
	zv := reflect.Zero(st.ID.Type)
	_, err := bytesort.Encode(zv.Interface())
	if err != nil {
		err = fmt.Errorf("ID field %q is not byte encodable: %s", st.ID.Name, err)
	}
	return err
}

func (st structType) String() string {
	return string(st.FullName)
}

type idField struct {
	StructPos     int
	AutoIncrement bool
	reflect.StructField
}

func (i idField) isInteger() bool {
	return i.Type.Kind() >= reflect.Int && i.Type.Kind() <= reflect.Uint64
}

func newIDField(t reflect.Type) (idField, error) {
	id := idField{StructPos: -1}
	tags := newTagList(t)
	idKeys := tags.filter(tagID)
	if len(idKeys) > 1 {
		return id, fmt.Errorf("must not have multiple fields with tag %q", tagID)
	} else if len(idKeys) == 1 {
		id.StructPos = idKeys[0]
	} else if idField, ok := t.FieldByName("ID"); ok {
		id.StructPos = idField.Index[0]
	}
	if id.StructPos == -1 {
		return id, errors.New("unable to find ID field: field has to be named \"ID\" or tagged with `bolster:\"id\"`")
	}
	id.StructField = t.Field(id.StructPos)
	id.AutoIncrement = tags.contains(id.StructPos, tagAutoIncrement)
	if !id.isInteger() && id.AutoIncrement {
		return id, fmt.Errorf("autoincremented IDs must be integer, got %s", id.Type.Kind())
	}
	return id, nil
}

type tagList [][]string

// newTagList returns a list of bolster tags for each struct field.
func newTagList(rt reflect.Type) tagList {
	tl := make([][]string, 0, rt.NumField())
	for i := 0; i < rt.NumField(); i++ {
		ftags := strings.Split(rt.Field(i).Tag.Get(tagBolster), ",")
		tl = append(tl, ftags)
	}
	return tl
}

// filter returns the positions of fields containing a tag s.
func (tl tagList) filter(s string) []int {
	keys := []int{}
	for i := range tl {
		if tl.contains(i, s) {
			keys = append(keys, i)
		}
	}
	return keys
}

// contains returns true when i'th field contains tag s.
func (tl tagList) contains(i int, s string) bool {
	for _, w := range tl[i] {
		if w == s {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import "sync"

type Storage struct {
    memory      *MemoryStore
    redis       *RedisStore
    StorageType string

    userMu      sync.Mutex
    pageMu      sync.Mutex
}

func initStore(Config *Configuration) *Storage {
    store_type := "memory"
    var redisStore *RedisStore

    redis_enabled := Config.Get("redis_enabled");
    if(redis_enabled == "true") {
        redis_host := Config.Get("redis_host")
        redis_port := uint(Config.GetInt("redis_port"))

        redisStore = newRedisStore(redis_host, redis_port)
        store_type = "redis"
    }

    var Store = Storage{
        &MemoryStore{make(map[string]map[string] *Socket), make(map[string] map[string] *Socket), 0},
        redisStore,
        store_type,

        sync.Mutex{},
        sync.Mutex{},
    }

    return &Store
}

func (this *Storage) Save(sock *Socket) (error) {
    this.userMu.Lock()

    this.memory.Save(sock)

    if this.StorageType == "redis" {
        if err := this.redis.Save(sock); err != nil {
            return err
        }
    }

    this.userMu.Unlock()
    return nil
}

func (this *Storage) Remove(sock *Socket) (error) {
    this.userMu.Lock()

    this.memory.Remove(sock)

    if this.StorageType == "redis" {
        if err := this.redis.Remove(sock); err != nil {
            return err
        }
    }

    this.userMu.Unlock()
    return nil
}

func (this *Storage) Client(UID string) (map[string]*Socket, error) {
    return this.memory.Client(UID)
}

func (this *Storage) Clients() (map[string]map[string]*Socket) {
    return this.memory.Clients()
}

func (this *Storage) ClientList() ([]string, error) {
    if this.StorageType == "redis" {
        return this.redis.Clients()
    }

    return nil, nil
}

func (this *Storage) Count() (int64, error) {
    if this.StorageType == "redis" {
        return this.redis.Count()
    }

    return this.memory.Count()
}

func (this *Storage) SetPage(sock *Socket) error {
    this.pageMu.Lock()

    this.memory.SetPage(sock)

    if this.StorageType == "redis" {
        if err := this.redis.SetPage(sock); err != nil {
            return err
        }
    }

    this.pageMu.Unlock()
    return nil
}

func (this *Storage) UnsetPage(sock *Socket) error {
    this.pageMu.Lock()
    this.memory.UnsetPage(sock)

    if this.StorageType == "redis" {
        if err := this.redis.UnsetPage(sock); err != nil {
            return err
        }
    }

    this.pageMu.Unlock()
    return nil
}

func (this *Storage) getPage(page string) map[string]*Socket {
    return this.memory.getPage(page)
}
<commit_msg>only put mutex around memory store calls, should speed up connects and disconnects<commit_after>package main

import "sync"

type Storage struct {
    memory      *MemoryStore
    redis       *RedisStore
    StorageType string

    userMu      sync.Mutex
    pageMu      sync.Mutex
}

func initStore(Config *Configuration) *Storage {
    store_type := "memory"
    var redisStore *RedisStore

    redis_enabled := Config.Get("redis_enabled");
    if(redis_enabled == "true") {
        redis_host := Config.Get("redis_host")
        redis_port := uint(Config.GetInt("redis_port"))

        redisStore = newRedisStore(redis_host, redis_port)
        store_type = "redis"
    }

    var Store = Storage{
        &MemoryStore{make(map[string]map[string] *Socket), make(map[string] map[string] *Socket), 0},
        redisStore,
        store_type,

        sync.Mutex{},
        sync.Mutex{},
    }

    return &Store
}

func (this *Storage) Save(sock *Socket) (error) {
    this.userMu.Lock()
    this.memory.Save(sock)
    this.userMu.Unlock()

    if this.StorageType == "redis" {
        if err := this.redis.Save(sock); err != nil {
            return err
        }
    }

    return nil
}

func (this *Storage) Remove(sock *Socket) (error) {
    this.userMu.Lock()
    this.memory.Remove(sock)
    this.userMu.Unlock()

    if this.StorageType == "redis" {
        if err := this.redis.Remove(sock); err != nil {
            return err
        }
    }

    return nil
}

func (this *Storage) Client(UID string) (map[string]*Socket, error) {
    return this.memory.Client(UID)
}

func (this *Storage) Clients() (map[string]map[string]*Socket) {
    return this.memory.Clients()
}

func (this *Storage) ClientList() ([]string, error) {
    if this.StorageType == "redis" {
        return this.redis.Clients()
    }

    return nil, nil
}

func (this *Storage) Count() (int64, error) {
    if this.StorageType == "redis" {
        return this.redis.Count()
    }

    return this.memory.Count()
}

func (this *Storage) SetPage(sock *Socket) error {
    this.pageMu.Lock()
    this.memory.SetPage(sock)
    this.pageMu.Unlock()

    if this.StorageType == "redis" {
        if err := this.redis.SetPage(sock); err != nil {
            return err
        }
    }

    return nil
}

func (this *Storage) UnsetPage(sock *Socket) error {
    this.pageMu.Lock()
    this.memory.UnsetPage(sock)
    this.pageMu.Unlock()

    if this.StorageType == "redis" {
        if err := this.redis.UnsetPage(sock); err != nil {
            return err
        }
    }

    return nil
}

func (this *Storage) getPage(page string) map[string]*Socket {
    return this.memory.getPage(page)
}
<|endoftext|>


<commit_before>// Package store automatically configures a database to store structured information in an sql database
package store

import (
	"database/sql"
	"errors"
	"reflect"
	"strings"
	"sync"

	_ "github.com/mxk/go-sqlite/sqlite3"
)

const (
	add = iota
	get
	update
	remove
	getPage
	count
)

type field struct {
	isStruct bool
	pos      int
	name     string
}

type typeInfo struct {
	primary    int
	fields     []field
	statements []*sql.Stmt
}

type Store struct {
	db    *sql.DB
	types map[string]typeInfo
	mutex sync.Mutex
}

func New(dataSourceName string) (*Store, error) {
	db, err := sql.Open("sqlite3", dataSourceName)
	if err != nil {
		return nil, err
	}
	return &Store{
		db:    db,
		types: make(map[string]typeInfo),
	}, nil
}

func (s *Store) Close() error {
	err := s.db.Close()
	s.db = nil
	return err
}

func (s *Store) Register(i interface{}) error {
	if s.db == nil {
		return ErrDBClosed
	} else if !isPointerStruct(i) {
		return ErrNoPointerStruct
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.defineType(i)
}

func (s *Store) defineType(i interface{}) error {
	name := typeName(i)
	if _, ok := s.types[name]; ok {
		return nil
	}

	s.types[name] = typeInfo{}

	v := reflect.ValueOf(i).Elem()
	numFields := v.Type().NumField()
	fields := make([]field, 0, numFields)
	id := 0
	idType := 0

	for n := 0; n < numFields; n++ {
		f := v.Type().Field(n)
		if f.PkgPath != "" { // not exported
			continue
		}
		fieldName := f.Name
		if fn := f.Tag.Get("store"); fn != "" {
			fieldName = fn
		}
		if fieldName == "-" { // Skip field
			continue
		}
		tmp := strings.ToLower(fieldName)
		for _, tf := range fields {
			if strings.ToLower(tf.name) == tmp {
				return ErrDuplicateColumn
			}
		}
		isPointer := f.Type.Kind() == reflect.Ptr
		var iface interface{}
		if isPointer {
			iface = v.Field(n).Interface()
		} else {
			iface = v.Field(n).Addr().Interface()
		}
		isStruct := false
		if isPointerStruct(iface) {
			s.defineType(iface)
			isStruct = true
		} else if !isValidType(iface) {
			continue
		}
		if isValidKeyType(iface) {
			if idType < 3 && f.Tag.Get("key") == "1" {
				idType = 3
				id = len(fields)
			} else if idType < 2 && strings.ToLower(fieldName) == "id" {
				idType = 2
				id = len(fields)
			} else if idType < 1 {
				idType = 1
				id = len(fields)
			}
		}
		fields = append(fields, field{
			isStruct,
			n,
			fieldName,
		})
	}
	if idType == 0 {
		return ErrNoKey
	}
	s.types[name] = typeInfo{
		primary: id,
	}

	// create statements
	var (
		sqlVars, sqlParams, setSQLParams, tableVars string
		doneFirst, doneFirstNonKey                  bool
	)

	for pos, f := range fields {
		if doneFirst {
			tableVars += ", "
		} else {
			doneFirst = true
		}
		if pos != id {
			if doneFirstNonKey {
				sqlVars += ", "
				setSQLParams += ", "
				sqlParams += ", "
			} else {
				doneFirstNonKey = true
			}
		}
		var varType string
		if f.isStruct {
			varType = "INTEGER"
		} else {
			varType = getType(i, f.pos)
		}
		tableVars += "[" + f.name + "] " + varType
		if pos == id {
			tableVars += " PRIMARY KEY AUTOINCREMENT"
		} else {
			sqlVars += "[" + f.name + "]"
			setSQLParams += "[" + f.name + "] = ?"
			sqlParams += "?"
		}
	}

	statements := make([]*sql.Stmt, 6)

	sql := "CREATE TABLE IF NOT EXISTS [" + name + "](" + tableVars + ");"
	_, err := s.db.Exec(sql)
	if err != nil {
		return err
	}

	sql = "INSERT INTO [" + name + "] (" + sqlVars + ") VALUES (" + sqlParams + ");"
	stmt, err := s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[add] = stmt

	sql = "SELECT " + sqlVars + " FROM [" + name + "] WHERE [" + fields[id].name + "] = ? LIMIT 1;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[get] = stmt

	sql = "UPDATE [" + name + "] SET " + setSQLParams + " WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[update] = stmt

	sql = "DELETE FROM [" + name + "] WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[remove] = stmt

	sql = "SELECT [" + fields[id].name + "] FROM [" + name + "] ORDER BY [" + fields[id].name + "] LIMIT ? OFFSET ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[getPage] = stmt

	sql = "SELECT COUNT(1) FROM [" + name + "];"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[count] = stmt

	s.types[name] = typeInfo{
		primary:    id,
		fields:     fields,
		statements: statements,
	}
	return nil
}

func (s *Store) Set(is ...interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	var toSet []interface{}
	for _, i := range is {
		t, ok := s.types[typeName(i)]
		if !ok {
			return ErrUnregisteredType
		}
		toSet = toSet[:0]
		err := s.set(i, &t, &toSet)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) set(i interface{}, t *typeInfo, toSet *[]interface{}) error {
	for _, oi := range *toSet {
		if oi == i {
			return nil
		}
	}
	(*toSet) = append(*toSet, i)
	id := t.GetID(i)
	isUpdate := id != 0
	vars := make([]interface{}, 0, len(t.fields))
	for pos, f := range t.fields {
		if pos == t.primary {
			continue
		}
		if f.isStruct {
			ni := getFieldPointer(i, f.pos)
			nt := s.types[typeName(ni)]
			err := s.set(ni, &nt, toSet)
			if err != nil {
				return err
			}
			vars = append(vars, getField(ni, nt.fields[nt.primary].pos))
		} else {
			vars = append(vars, getField(i, f.pos))
		}
	}
	if isUpdate {
		r, err := t.statements[update].Exec(append(vars, id)...)
		if err != nil {
			return err
		}
		if ra, err := r.RowsAffected(); err != nil {
			return err
		} else if ra > 0 {
			return nil
		}
		// id wasn't found, so insert...
	}
	r, err := t.statements[add].Exec(vars...)
	if err != nil {
		return err
	}
	lid, err := r.LastInsertId()
	if err != nil {
		return err
	}
	t.SetID(i, lid)
	return nil
}

func (s *Store) Get(is ...interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.get(is...)
}
func (s *Store) get(is ...interface{}) error {
	for _, i := range is {
		t, ok := s.types[typeName(i)]
		if !ok {
			return ErrUnregisteredType
		}
		id := t.GetID(i)
		if id == 0 {
			continue
		}
		vars := make([]interface{}, 0, len(t.fields)-1)
		var toGet []interface{}
		for pos, f := range t.fields {
			if pos == t.primary {
				continue
			}
			if f.isStruct {
				ni := getFieldPointer(i, f.pos)
				nt := s.types[typeName(ni)]
				toGet = append(toGet, ni)
				vars = append(vars, getFieldPointer(ni, nt.fields[nt.primary].pos))
			} else {
				vars = append(vars, getFieldPointer(i, f.pos))
			}
		}
		row := t.statements[get].QueryRow(id)
		err := row.Scan(vars...)
		if err == sql.ErrNoRows {
			t.SetID(i, 0)
		} else if err != nil {
			return err
		} else if len(toGet) > 0 {
			if err = s.get(toGet...); err != nil {
				return err
			}
		}
	}
	return nil
}

func (s *Store) GetPage(is []interface{}, offset int) (int, error) {
	if len(is) == 0 {
		return 0, nil
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	t := s.types[typeName(is[0])]
	rows, err := t.statements[getPage].Query(len(is), offset)
	if err != nil {
		return 0, err
	}
	defer rows.Close()
	return s.getPage(is, rows)
}

func (s *Store) getPage(is []interface{}, rows *sql.Rows) (int, error) {
	t := s.types[typeName(is[0])]
	n := 0
	for rows.Next() {
		var id int64
		if err := rows.Scan(&id); err != nil {
			return 0, err
		}
		t.SetID(is[n], id)
		n++
	}
	is = is[:n]
	if err := rows.Err(); err == sql.ErrNoRows {
		return 0, nil
	} else if err != nil {
		return 0, err
	} else if err = s.get(is...); err != nil {
		return 0, err
	}
	return n, nil
}

func (s *Store) Remove(is ...interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, i := range is {
		t, ok := s.types[typeName(i)]
		if !ok {
			return ErrUnregisteredType
		}
		_, err := t.statements[remove].Exec(t.GetID(i))
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) Count(i interface{}) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if !isPointerStruct(i) {
		return 0, ErrNoPointerStruct
	}
	name := typeName(i)
	stmt := s.types[name].statements[count]
	res, err := stmt.Query()
	if err != nil {
		return 0, err
	}
	num := 0
	err = res.Scan(&num)
	return num, err
}

// Errors

var (
	ErrDBClosed         = errors.New("database already closed")
	ErrNoPointerStruct  = errors.New("given variable is not a pointer to a struct")
	ErrNoKey            = errors.New("could not determine key")
	ErrDuplicateColumn  = errors.New("duplicate column name found")
	ErrUnregisteredType = errors.New("type not registered")
	ErrInvalidType      = errors.New("invalid type")
)
<commit_msg>Correct Count code<commit_after>// Package store automatically configures a database to store structured information in an sql database
package store

import (
	"database/sql"
	"errors"
	"reflect"
	"strings"
	"sync"

	_ "github.com/mxk/go-sqlite/sqlite3"
)

const (
	add = iota
	get
	update
	remove
	getPage
	count
)

type field struct {
	isStruct bool
	pos      int
	name     string
}

type typeInfo struct {
	primary    int
	fields     []field
	statements []*sql.Stmt
}

type Store struct {
	db    *sql.DB
	types map[string]typeInfo
	mutex sync.Mutex
}

func New(dataSourceName string) (*Store, error) {
	db, err := sql.Open("sqlite3", dataSourceName)
	if err != nil {
		return nil, err
	}
	return &Store{
		db:    db,
		types: make(map[string]typeInfo),
	}, nil
}

func (s *Store) Close() error {
	err := s.db.Close()
	s.db = nil
	return err
}

func (s *Store) Register(i interface{}) error {
	if s.db == nil {
		return ErrDBClosed
	} else if !isPointerStruct(i) {
		return ErrNoPointerStruct
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.defineType(i)
}

func (s *Store) defineType(i interface{}) error {
	name := typeName(i)
	if _, ok := s.types[name]; ok {
		return nil
	}

	s.types[name] = typeInfo{}

	v := reflect.ValueOf(i).Elem()
	numFields := v.Type().NumField()
	fields := make([]field, 0, numFields)
	id := 0
	idType := 0

	for n := 0; n < numFields; n++ {
		f := v.Type().Field(n)
		if f.PkgPath != "" { // not exported
			continue
		}
		fieldName := f.Name
		if fn := f.Tag.Get("store"); fn != "" {
			fieldName = fn
		}
		if fieldName == "-" { // Skip field
			continue
		}
		tmp := strings.ToLower(fieldName)
		for _, tf := range fields {
			if strings.ToLower(tf.name) == tmp {
				return ErrDuplicateColumn
			}
		}
		isPointer := f.Type.Kind() == reflect.Ptr
		var iface interface{}
		if isPointer {
			iface = v.Field(n).Interface()
		} else {
			iface = v.Field(n).Addr().Interface()
		}
		isStruct := false
		if isPointerStruct(iface) {
			s.defineType(iface)
			isStruct = true
		} else if !isValidType(iface) {
			continue
		}
		if isValidKeyType(iface) {
			if idType < 3 && f.Tag.Get("key") == "1" {
				idType = 3
				id = len(fields)
			} else if idType < 2 && strings.ToLower(fieldName) == "id" {
				idType = 2
				id = len(fields)
			} else if idType < 1 {
				idType = 1
				id = len(fields)
			}
		}
		fields = append(fields, field{
			isStruct,
			n,
			fieldName,
		})
	}
	if idType == 0 {
		return ErrNoKey
	}
	s.types[name] = typeInfo{
		primary: id,
	}

	// create statements
	var (
		sqlVars, sqlParams, setSQLParams, tableVars string
		doneFirst, doneFirstNonKey                  bool
	)

	for pos, f := range fields {
		if doneFirst {
			tableVars += ", "
		} else {
			doneFirst = true
		}
		if pos != id {
			if doneFirstNonKey {
				sqlVars += ", "
				setSQLParams += ", "
				sqlParams += ", "
			} else {
				doneFirstNonKey = true
			}
		}
		var varType string
		if f.isStruct {
			varType = "INTEGER"
		} else {
			varType = getType(i, f.pos)
		}
		tableVars += "[" + f.name + "] " + varType
		if pos == id {
			tableVars += " PRIMARY KEY AUTOINCREMENT"
		} else {
			sqlVars += "[" + f.name + "]"
			setSQLParams += "[" + f.name + "] = ?"
			sqlParams += "?"
		}
	}

	statements := make([]*sql.Stmt, 6)

	sql := "CREATE TABLE IF NOT EXISTS [" + name + "](" + tableVars + ");"
	_, err := s.db.Exec(sql)
	if err != nil {
		return err
	}

	sql = "INSERT INTO [" + name + "] (" + sqlVars + ") VALUES (" + sqlParams + ");"
	stmt, err := s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[add] = stmt

	sql = "SELECT " + sqlVars + " FROM [" + name + "] WHERE [" + fields[id].name + "] = ? LIMIT 1;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[get] = stmt

	sql = "UPDATE [" + name + "] SET " + setSQLParams + " WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[update] = stmt

	sql = "DELETE FROM [" + name + "] WHERE [" + fields[id].name + "] = ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[remove] = stmt

	sql = "SELECT [" + fields[id].name + "] FROM [" + name + "] ORDER BY [" + fields[id].name + "] LIMIT ? OFFSET ?;"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[getPage] = stmt

	sql = "SELECT COUNT(1) FROM [" + name + "];"
	stmt, err = s.db.Prepare(sql)
	if err != nil {
		return err
	}
	statements[count] = stmt

	s.types[name] = typeInfo{
		primary:    id,
		fields:     fields,
		statements: statements,
	}
	return nil
}

func (s *Store) Set(is ...interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	var toSet []interface{}
	for _, i := range is {
		t, ok := s.types[typeName(i)]
		if !ok {
			return ErrUnregisteredType
		}
		toSet = toSet[:0]
		err := s.set(i, &t, &toSet)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) set(i interface{}, t *typeInfo, toSet *[]interface{}) error {
	for _, oi := range *toSet {
		if oi == i {
			return nil
		}
	}
	(*toSet) = append(*toSet, i)
	id := t.GetID(i)
	isUpdate := id != 0
	vars := make([]interface{}, 0, len(t.fields))
	for pos, f := range t.fields {
		if pos == t.primary {
			continue
		}
		if f.isStruct {
			ni := getFieldPointer(i, f.pos)
			nt := s.types[typeName(ni)]
			err := s.set(ni, &nt, toSet)
			if err != nil {
				return err
			}
			vars = append(vars, getField(ni, nt.fields[nt.primary].pos))
		} else {
			vars = append(vars, getField(i, f.pos))
		}
	}
	if isUpdate {
		r, err := t.statements[update].Exec(append(vars, id)...)
		if err != nil {
			return err
		}
		if ra, err := r.RowsAffected(); err != nil {
			return err
		} else if ra > 0 {
			return nil
		}
		// id wasn't found, so insert...
	}
	r, err := t.statements[add].Exec(vars...)
	if err != nil {
		return err
	}
	lid, err := r.LastInsertId()
	if err != nil {
		return err
	}
	t.SetID(i, lid)
	return nil
}

func (s *Store) Get(is ...interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.get(is...)
}
func (s *Store) get(is ...interface{}) error {
	for _, i := range is {
		t, ok := s.types[typeName(i)]
		if !ok {
			return ErrUnregisteredType
		}
		id := t.GetID(i)
		if id == 0 {
			continue
		}
		vars := make([]interface{}, 0, len(t.fields)-1)
		var toGet []interface{}
		for pos, f := range t.fields {
			if pos == t.primary {
				continue
			}
			if f.isStruct {
				ni := getFieldPointer(i, f.pos)
				nt := s.types[typeName(ni)]
				toGet = append(toGet, ni)
				vars = append(vars, getFieldPointer(ni, nt.fields[nt.primary].pos))
			} else {
				vars = append(vars, getFieldPointer(i, f.pos))
			}
		}
		row := t.statements[get].QueryRow(id)
		err := row.Scan(vars...)
		if err == sql.ErrNoRows {
			t.SetID(i, 0)
		} else if err != nil {
			return err
		} else if len(toGet) > 0 {
			if err = s.get(toGet...); err != nil {
				return err
			}
		}
	}
	return nil
}

func (s *Store) GetPage(is []interface{}, offset int) (int, error) {
	if len(is) == 0 {
		return 0, nil
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	t := s.types[typeName(is[0])]
	rows, err := t.statements[getPage].Query(len(is), offset)
	if err != nil {
		return 0, err
	}
	defer rows.Close()
	return s.getPage(is, rows)
}

func (s *Store) getPage(is []interface{}, rows *sql.Rows) (int, error) {
	t := s.types[typeName(is[0])]
	n := 0
	for rows.Next() {
		var id int64
		if err := rows.Scan(&id); err != nil {
			return 0, err
		}
		t.SetID(is[n], id)
		n++
	}
	is = is[:n]
	if err := rows.Err(); err == sql.ErrNoRows {
		return 0, nil
	} else if err != nil {
		return 0, err
	} else if err = s.get(is...); err != nil {
		return 0, err
	}
	return n, nil
}

func (s *Store) Remove(is ...interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for _, i := range is {
		t, ok := s.types[typeName(i)]
		if !ok {
			return ErrUnregisteredType
		}
		_, err := t.statements[remove].Exec(t.GetID(i))
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Store) Count(i interface{}) (int, error) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	if !isPointerStruct(i) {
		return 0, ErrNoPointerStruct
	}
	t, ok := s.types[typeName(i)]
	if !ok {
		return 0, ErrUnregisteredType
	}
	num := 0
	err := t.statements[count].QueryRow().Scan(&num)
	return num, err
}

// Errors

var (
	ErrDBClosed         = errors.New("database already closed")
	ErrNoPointerStruct  = errors.New("given variable is not a pointer to a struct")
	ErrNoKey            = errors.New("could not determine key")
	ErrDuplicateColumn  = errors.New("duplicate column name found")
	ErrUnregisteredType = errors.New("type not registered")
	ErrInvalidType      = errors.New("invalid type")
)
<|endoftext|>


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

// svctl is an interactive runit controller.
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/adrg/xdg"
	"github.com/peterh/liner"
)

// status Represents current status of a single process.
// Note that it gather all information during construction,
// so it is generally meant to be short lived.
type status struct {
	name string
	err  error

	Offsets []int

	sv       []byte
	svStatus string
	svPid    uint
	svTime   uint64
}

// newStatus Creates new status representation for given directory and name.
// Name is optional and for display purposes only, if not specified Base(dir) is used.
func newStatus(dir, name string) *status {
	s := &status{Offsets: make([]int, 2)}

	if name != "" {
		s.name = name
	} else {
		s.name = path.Base(dir)
	}
	s.Offsets[0] = len(s.name)

	status, err := s.status(dir)
	if err != nil {
		s.err = err

		s.Offsets[1] = len("ERROR")
	} else {
		s.svStatus = svStatus(status)
		s.svPid = svPid(status)
		s.svTime = svTime(status)

		s.Offsets[1] = len(s.svStatus)
		if s.svStatus == "RUNNING" {
			s.Offsets[1] += len(fmt.Sprintf(" (pid %d)", s.svPid))
		}
	}
	s.sv = status

	return s
}

// status Reads current status from specified dir.
func (s *status) status(dir string) ([]byte, error) {
	if _, err := os.OpenFile(path.Join(dir, "supervise/ok"), os.O_WRONLY, 0600); err != nil {
		return nil, fmt.Errorf("unable to open supervise/ok")
	}

	fstatus, err := os.Open(path.Join(dir, "supervise/status"))
	if err != nil {
		return nil, fmt.Errorf("unable to open supervise/status")
	}

	b := make([]byte, 20)
	_, err = io.ReadFull(fstatus, b)
	fstatus.Close()
	if err != nil {
		if err == io.ErrUnexpectedEOF {
			return nil, fmt.Errorf("unable to read supervise/status: wrong format")
		}
		return nil, fmt.Errorf("unable to read supervise/status")
	}
	return b, nil
}

// Check Performs svCheck on status, if retrieved successfully.
func (s *status) Check(action []byte, start uint64) bool {
	if s.err != nil {
		return true
	}
	return svCheck(action, s.sv, start)
}

// CheckControl Performs svCheckControl on status.
func (s *status) CheckControl(action []byte) bool {
	return svCheckControl(action, s.sv)
}

// String Returns nicely stringified version of the status.
//
// s.Offsets can be set from the outside to make indentation uniform
// among multiple statuses.
func (s *status) String() string {
	var status bytes.Buffer
	fmt.Fprintf(&status, "%-[1]*s", s.Offsets[0]+3, s.name)
	if s.err != nil {
		fmt.Fprintf(&status, "%-[1]*s%s", s.Offsets[1]+3, "ERROR", s.err)
		return status.String()
	}
	fmt.Fprintf(&status, s.svStatus)
	if s.svStatus == "RUNNING" {
		fmt.Fprintf(&status, " (pid %d)", s.svPid)
	}
	fmt.Fprintf(&status, "%-[1]*s", s.Offsets[1]+3-status.Len()+s.Offsets[0]+3, "")
	status.WriteString(fmt.Sprintf("%ds   ", svNow()-s.svTime))
	return status.String()
}

// Errored Returns whether status retrieval ended with error or not.
func (s *status) Errored() bool {
	return s.err != nil
}

// ctl Represents main svctl entry point.
type ctl struct {
	line    *liner.State
	basedir string
}

// newCtl Creates new ctl instance.
// Initializes input prompt, reads history, reads $SVDIR.
func newCtl() *ctl {
	c := &ctl{line: liner.NewLiner()}

	fn, _ := xdg.DataFile("svctl/hist")
	if f, err := os.Open(fn); err == nil {
		c.line.ReadHistory(f)
		f.Close()
	}
	c.basedir = os.Getenv("SVDIR")
	if c.basedir == "" {
		c.basedir = "/service"
	}

	c.line.SetTabCompletionStyle(liner.TabPrints)
	c.line.SetCompleter(func(l string) []string {
		s := strings.Split(l, " ")
		if len(s) <= 1 {
			if len(s) == 0 {
				return cmdMatchName("")
			}
			return cmdMatchName(s[0])
		}
		services := c.Services(fmt.Sprintf("%s*", s[len(s)-1]))
		compl := make([]string, len(services))
		for i, service := range services {
			compl[i] = fmt.Sprintf(
				"%s %s ",
				strings.Join(s[:len(s)-1], " "), path.Base(service),
			)
		}
		return compl
	})

	return c
}

// Close Closes input prompt, saves history to file.
func (c *ctl) Close() {
	fn, _ := xdg.DataFile("svctl/hist")
	if f, err := os.Create(fn); err == nil {
		if n, err := c.line.WriteHistory(f); err != nil {
			log.Printf("error writing history file: %s, lines written: %d\n", err, n)
		}
	} else {
		log.Printf("error opening history file: %s\n", err)
	}
	c.line.Close()
}

// Services Returns paths to all services matching pattern.
func (c *ctl) Services(pattern string) []string {
	if len(pattern) < len(c.basedir) || pattern[:len(c.basedir)] != c.basedir {
		pattern = path.Join(c.basedir, pattern)
	}
	files, err := filepath.Glob(pattern)
	if err != nil {
		log.Printf("error getting services list: %s\n", err)
	}
	return files
}

// Status Prints all statuses matching id and optionally their log process statuses.
func (c *ctl) Status(id string, toLog bool) {
	// TODO: normally (up|down) and stuff?
	statuses := []*status{}
	for _, dir := range c.Services(id) {
		if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
			continue
		}

		status := newStatus(dir, "")
		statuses = append(statuses, status)

		if toLog {
			logdir := path.Join(dir, "log")
			if _, err := os.Stat(logdir); !os.IsNotExist(err) {
				status = newStatus(logdir, fmt.Sprintf("%s/LOG", path.Base(dir)))
				statuses = append(statuses, status)
			}
		}

		for i, offset := range status.Offsets {
			if statuses[0].Offsets[i] < offset {
				statuses[0].Offsets[i] = offset
			}
		}
	}
	for _, status := range statuses {
		for i, offset := range statuses[0].Offsets {
			status.Offsets[i] = offset
		}
		fmt.Println(status)
	}
}

// control Sends action byte to service.
func (c *ctl) control(action []byte, service string) error {
	f, err := os.OpenFile(
		path.Join(service, "supervise/control"), os.O_WRONLY, 0600,
	)
	if err != nil {
		return fmt.Errorf("%s: unable to open supervise/control", path.Base(service))
	}
	defer f.Close()
	if _, err := f.Write(action); err != nil {
		return fmt.Errorf("%s: unable to write to supervise/control", path.Base(service))
	}
	return nil
}

// Ctl Handles command supplied by user.
//
// Depending on the command, it might just exit, print help or propagate
// command to cmds to hand the action off to runsv.
//
// If more than one service was specified with the command,
// actions are handed off asynchronically.
func (c *ctl) Ctl(cmd string) bool {
	c.line.AppendHistory(cmd)
	start := svNow()
	params := strings.Split(cmd, " ")
	var action []byte
	switch params[0] {
	case "e", "exit":
		return true
	case "s", "status":
		if len(params) == 1 {
			c.Status("*", true)
		} else {
			for _, dir := range params[1:] {
				c.Status(dir, true)
			}
		}
		return false
	case "?", "help":
		if len(params) == 1 {
			for _, cmd := range cmdAll() {
				fmt.Println(cmd.Help())
			}
			return false
		}
		for _, param := range params[1:] {
			cmd := cmdMatch(param)
			if cmd == nil {
				fmt.Printf("%s: unable to find action\n", param)
			} else {
				fmt.Println(cmd.Help())
			}
		}
		return false
	default:
		cmd := cmdMatch(params[0])
		if cmd == nil {
			fmt.Printf("%s: unable to find action\n", params[0])
			return false
		}
		action = cmd.Action()
	}

	if len(params) == 1 {
		params = append(params, "*")
	}
	var wg sync.WaitGroup
	for _, param := range params[1:] {
		if param == "" {
			continue
		}
		for _, service := range c.Services(param) {
			status := newStatus(service, "")
			if status.Errored() {
				continue
			}
			if status.CheckControl(action) {
				if err := c.control(action, service); err != nil {
					fmt.Println(err)
					continue
				}
			}

			wg.Add(1)
			go func(service string) {
				defer wg.Done()
				timeout := time.After(7 * time.Second)
				tick := time.Tick(100 * time.Millisecond)
				for {
					select {
					case <-timeout:
						fmt.Printf("TIMEOUT: ")
						c.Status(service, false)
						return
					case <-tick:
						status := newStatus(service, "")
						if status.Check(action, start) {
							fmt.Println(status)
							return
						}
					}
				}
			}(service)
		}
	}
	wg.Wait()

	return false
}

// Run Performs one tick of a input prompt event loop.
// If this function returns true, the outside loop should terminate.
func (c *ctl) Run() bool {
	cmd, err := c.line.Prompt("svctl> ")
	if err == io.EOF {
		fmt.Println()
		return true
	} else if err != nil {
		log.Printf("error reading prompt contents: %s\n", err)
		return false
	}
	return c.Ctl(cmd)
}

// main Creates svctl entry point, prints all processes statuses and launches event loop.
func main() {
	ctl := newCtl()
	defer ctl.Close()
	ctl.Status("*", true)
	for !ctl.Run() {
	}
}
<commit_msg>refactor: split ctl.Ctl in two<commit_after>// svctl
// Copyright (C) 2015 Karol 'Kenji Takahashi' Woźniak
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// svctl is an interactive runit controller.
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/adrg/xdg"
	"github.com/peterh/liner"
)

// status Represents current status of a single process.
// Note that it gather all information during construction,
// so it is generally meant to be short lived.
type status struct {
	name string
	err  error

	Offsets []int

	sv       []byte
	svStatus string
	svPid    uint
	svTime   uint64
}

// newStatus Creates new status representation for given directory and name.
// Name is optional and for display purposes only, if not specified Base(dir) is used.
func newStatus(dir, name string) *status {
	s := &status{Offsets: make([]int, 2)}

	if name != "" {
		s.name = name
	} else {
		s.name = path.Base(dir)
	}
	s.Offsets[0] = len(s.name)

	status, err := s.status(dir)
	if err != nil {
		s.err = err

		s.Offsets[1] = len("ERROR")
	} else {
		s.svStatus = svStatus(status)
		s.svPid = svPid(status)
		s.svTime = svTime(status)

		s.Offsets[1] = len(s.svStatus)
		if s.svStatus == "RUNNING" {
			s.Offsets[1] += len(fmt.Sprintf(" (pid %d)", s.svPid))
		}
	}
	s.sv = status

	return s
}

// status Reads current status from specified dir.
func (s *status) status(dir string) ([]byte, error) {
	if _, err := os.OpenFile(path.Join(dir, "supervise/ok"), os.O_WRONLY, 0600); err != nil {
		return nil, fmt.Errorf("unable to open supervise/ok")
	}

	fstatus, err := os.Open(path.Join(dir, "supervise/status"))
	if err != nil {
		return nil, fmt.Errorf("unable to open supervise/status")
	}

	b := make([]byte, 20)
	_, err = io.ReadFull(fstatus, b)
	fstatus.Close()
	if err != nil {
		if err == io.ErrUnexpectedEOF {
			return nil, fmt.Errorf("unable to read supervise/status: wrong format")
		}
		return nil, fmt.Errorf("unable to read supervise/status")
	}
	return b, nil
}

// Check Performs svCheck on status, if retrieved successfully.
func (s *status) Check(action []byte, start uint64) bool {
	if s.err != nil {
		return true
	}
	return svCheck(action, s.sv, start)
}

// CheckControl Performs svCheckControl on status.
func (s *status) CheckControl(action []byte) bool {
	return svCheckControl(action, s.sv)
}

// String Returns nicely stringified version of the status.
//
// s.Offsets can be set from the outside to make indentation uniform
// among multiple statuses.
func (s *status) String() string {
	var status bytes.Buffer
	fmt.Fprintf(&status, "%-[1]*s", s.Offsets[0]+3, s.name)
	if s.err != nil {
		fmt.Fprintf(&status, "%-[1]*s%s", s.Offsets[1]+3, "ERROR", s.err)
		return status.String()
	}
	fmt.Fprintf(&status, s.svStatus)
	if s.svStatus == "RUNNING" {
		fmt.Fprintf(&status, " (pid %d)", s.svPid)
	}
	fmt.Fprintf(&status, "%-[1]*s", s.Offsets[1]+3-status.Len()+s.Offsets[0]+3, "")
	status.WriteString(fmt.Sprintf("%ds   ", svNow()-s.svTime))
	return status.String()
}

// Errored Returns whether status retrieval ended with error or not.
func (s *status) Errored() bool {
	return s.err != nil
}

// ctl Represents main svctl entry point.
type ctl struct {
	line    *liner.State
	basedir string
}

// newCtl Creates new ctl instance.
// Initializes input prompt, reads history, reads $SVDIR.
func newCtl() *ctl {
	c := &ctl{line: liner.NewLiner()}

	fn, _ := xdg.DataFile("svctl/hist")
	if f, err := os.Open(fn); err == nil {
		c.line.ReadHistory(f)
		f.Close()
	}
	c.basedir = os.Getenv("SVDIR")
	if c.basedir == "" {
		c.basedir = "/service"
	}

	c.line.SetTabCompletionStyle(liner.TabPrints)
	c.line.SetCompleter(func(l string) []string {
		s := strings.Split(l, " ")
		if len(s) <= 1 {
			if len(s) == 0 {
				return cmdMatchName("")
			}
			return cmdMatchName(s[0])
		}
		services := c.Services(fmt.Sprintf("%s*", s[len(s)-1]))
		compl := make([]string, len(services))
		for i, service := range services {
			compl[i] = fmt.Sprintf(
				"%s %s ",
				strings.Join(s[:len(s)-1], " "), path.Base(service),
			)
		}
		return compl
	})

	return c
}

// Close Closes input prompt, saves history to file.
func (c *ctl) Close() {
	fn, _ := xdg.DataFile("svctl/hist")
	if f, err := os.Create(fn); err == nil {
		if n, err := c.line.WriteHistory(f); err != nil {
			log.Printf("error writing history file: %s, lines written: %d\n", err, n)
		}
	} else {
		log.Printf("error opening history file: %s\n", err)
	}
	c.line.Close()
}

// Services Returns paths to all services matching pattern.
func (c *ctl) Services(pattern string) []string {
	if len(pattern) < len(c.basedir) || pattern[:len(c.basedir)] != c.basedir {
		pattern = path.Join(c.basedir, pattern)
	}
	files, err := filepath.Glob(pattern)
	if err != nil {
		log.Printf("error getting services list: %s\n", err)
	}
	return files
}

// Status Prints all statuses matching id and optionally their log process statuses.
func (c *ctl) Status(id string, toLog bool) {
	// TODO: normally (up|down) and stuff?
	statuses := []*status{}
	for _, dir := range c.Services(id) {
		if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
			continue
		}

		status := newStatus(dir, "")
		statuses = append(statuses, status)

		if toLog {
			logdir := path.Join(dir, "log")
			if _, err := os.Stat(logdir); !os.IsNotExist(err) {
				status = newStatus(logdir, fmt.Sprintf("%s/LOG", path.Base(dir)))
				statuses = append(statuses, status)
			}
		}

		for i, offset := range status.Offsets {
			if statuses[0].Offsets[i] < offset {
				statuses[0].Offsets[i] = offset
			}
		}
	}
	for _, status := range statuses {
		for i, offset := range statuses[0].Offsets {
			status.Offsets[i] = offset
		}
		fmt.Println(status)
	}
}

// control Sends action byte to service.
func (c *ctl) control(action []byte, service string) error {
	f, err := os.OpenFile(
		path.Join(service, "supervise/control"), os.O_WRONLY, 0600,
	)
	if err != nil {
		return fmt.Errorf("%s: unable to open supervise/control", path.Base(service))
	}
	defer f.Close()
	if _, err := f.Write(action); err != nil {
		return fmt.Errorf("%s: unable to write to supervise/control", path.Base(service))
	}
	return nil
}

// ctl Delegates a single action for single service.
func (c *ctl) ctl(action []byte, service string, start uint64, wg *sync.WaitGroup) {
	defer wg.Done()

	status := newStatus(service, "")
	if status.Errored() {
		return
	}
	if status.CheckControl(action) {
		if err := c.control(action, service); err != nil {
			fmt.Println(err)
			return
		}
	}

	timeout := time.After(7 * time.Second)
	tick := time.Tick(100 * time.Millisecond)
	for {
		select {
		case <-timeout:
			fmt.Printf("TIMEOUT: ")
			c.Status(service, false)
			return
		case <-tick:
			status := newStatus(service, "")
			if status.Check(action, start) {
				fmt.Println(status)
				return
			}
		}
	}
}

// Ctl Handles command supplied by user.
//
// Depending on the command, it might just exit, print help or propagate
// command to cmds to delegate action to runsv.
//
// If more than one service was specified with the command,
// actions are delegated asynchronically.
func (c *ctl) Ctl(cmd string) bool {
	c.line.AppendHistory(cmd)
	start := svNow()
	params := strings.Split(cmd, " ")
	var action []byte
	switch params[0] {
	case "e", "exit":
		return true
	case "s", "status":
		if len(params) == 1 {
			c.Status("*", true)
		} else {
			for _, dir := range params[1:] {
				c.Status(dir, true)
			}
		}
		return false
	case "?", "help":
		if len(params) == 1 {
			for _, cmd := range cmdAll() {
				fmt.Println(cmd.Help())
			}
			return false
		}
		for _, param := range params[1:] {
			cmd := cmdMatch(param)
			if cmd == nil {
				fmt.Printf("%s: unable to find action\n", param)
			} else {
				fmt.Println(cmd.Help())
			}
		}
		return false
	default:
		cmd := cmdMatch(params[0])
		if cmd == nil {
			fmt.Printf("%s: unable to find action\n", params[0])
			return false
		}
		action = cmd.Action()
	}

	if len(params) == 1 {
		params = append(params, "*")
	}
	var wg sync.WaitGroup
	for _, param := range params[1:] {
		if param == "" {
			continue
		}
		for _, service := range c.Services(param) {
			wg.Add(1)
			go c.ctl(action, service, start, &wg)
		}
	}
	wg.Wait()

	return false
}

// Run Performs one tick of a input prompt event loop.
// If this function returns true, the outside loop should terminate.
func (c *ctl) Run() bool {
	cmd, err := c.line.Prompt("svctl> ")
	if err == io.EOF {
		fmt.Println()
		return true
	} else if err != nil {
		log.Printf("error reading prompt contents: %s\n", err)
		return false
	}
	return c.Ctl(cmd)
}

// main Creates svctl entry point, prints all processes statuses and launches event loop.
func main() {
	ctl := newCtl()
	defer ctl.Close()
	ctl.Status("*", true)
	for !ctl.Run() {
	}
}
<|endoftext|>


<commit_before>// Package ninow implements a magical sql database ORM.
package ninow

import (
	"database/sql"
	"reflect"
	"strconv"
	"strings"
)

type Table struct {
	db    *sql.DB
	rtype reflect.Type

	name    string
	columns []string
	fields  []string
}

func TableFor(row interface{}, db *sql.DB) *Table {
	rtype := reflect.TypeOf(row)
	name := strings.ToLower(rtype.Name()) + "s"

	columns := []string{}
	fields := []string{}
	for i := 0; i < rtype.NumField(); i++ {
		fieldValue := rtype.Field(i)
		//TODO fix this piece
		columns = append(columns, fieldNameToColumnName(fieldValue.Name))
		fields = append(fields, fieldValue.Name)
	}

	return &Table{db, rtype, name, columns, fields}
}

func (t *Table) Select(id int) (interface{}, error) {
	row := t.db.QueryRow("SELECT "+csv(t.columns)+" FROM "+t.name+" WHERE id=?", id)
	value := reflect.New(t.rtype)
	err := row.Scan(fieldPointers(t.fields, value)...)

	return value.Interface(), err
}

func (t *Table) SelectAllBy(column, value string) (interface{}, error) {
	query := "SELECT " + csv(t.columns) + " FROM " + t.name

	if column != "" && value != "" {
		// TODO Make sure this line is safe
		query += " WHERE " + column + "=" + value
	}

	rows, err := t.db.Query(query)
	if err != nil {
		return nil, err
	}

	sliceOfValue := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(t.rtype)), 0, 10)

	for rows.Next() {
		value := reflect.New(t.rtype)
		err := rows.Scan(fieldPointers(t.fields, value)...)
		if err != nil {
			return nil, err
		}

		sliceOfValue = reflect.Append(sliceOfValue, value)
	}

	return sliceOfValue.Interface(), nil
}

func (t *Table) Insert(row interface{}) (int, error) {
	query := "INSERT INTO " + t.name
	query += " (" + csv(t.columns[1:]) + ") "
	query += " values (" + csQ(len(t.columns)-1) + ")"

	res, err := t.db.Exec(query, fieldValues(t.fields[1:], reflect.ValueOf(row))...)
	if err != nil {
		return 0, err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}

	return int(id), nil
}

func (t *Table) Update(row interface{}) error {
	values := fieldValues(t.fields, reflect.ValueOf(row))

	query := "UPDATE " + t.name
	query += " SET " + csc(t.columns[1:])
	query += " WHERE id=" + strconv.Itoa(values[0].(int))

	_, err := t.db.Exec(query, values[1:]...)
	return err
}

func (t *Table) Delete(id int) error {
	query := "DELETE FROM " + t.name + " WHERE id =? "

	_, err := t.db.Exec(query, id)
	return err
}
<commit_msg>Add Table.SelectBy()<commit_after>// Package ninow implements a magical sql database ORM.
package ninow

import (
	"database/sql"
	"reflect"
	"strconv"
	"strings"
)

type Table struct {
	db    *sql.DB
	rtype reflect.Type

	name    string
	columns []string
	fields  []string
}

func TableFor(row interface{}, db *sql.DB) *Table {
	rtype := reflect.TypeOf(row)
	name := strings.ToLower(rtype.Name()) + "s"

	columns := []string{}
	fields := []string{}
	for i := 0; i < rtype.NumField(); i++ {
		fieldValue := rtype.Field(i)
		//TODO fix this piece
		columns = append(columns, fieldNameToColumnName(fieldValue.Name))
		fields = append(fields, fieldValue.Name)
	}

	return &Table{db, rtype, name, columns, fields}
}

func (t *Table) Select(id int) (interface{}, error) {
	return t.SelectBy("id", strconv.Itoa(id))
}

func (t *Table) SelectBy(column, cValue string) (interface{}, error) {
	row := t.db.QueryRow("SELECT "+csv(t.columns)+" FROM "+t.name+" WHERE "+column+"=?", cValue)
	value := reflect.New(t.rtype)
	err := row.Scan(fieldPointers(t.fields, value)...)

	return value.Interface(), err
}

func (t *Table) SelectAllBy(column, value string) (interface{}, error) {
	query := "SELECT " + csv(t.columns) + " FROM " + t.name

	if column != "" && value != "" {
		// TODO Make sure this line is safe
		query += " WHERE " + column + "=" + value
	}

	rows, err := t.db.Query(query)
	if err != nil {
		return nil, err
	}

	sliceOfValue := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(t.rtype)), 0, 10)

	for rows.Next() {
		value := reflect.New(t.rtype)
		err := rows.Scan(fieldPointers(t.fields, value)...)
		if err != nil {
			return nil, err
		}

		sliceOfValue = reflect.Append(sliceOfValue, value)
	}

	return sliceOfValue.Interface(), nil
}

func (t *Table) Insert(row interface{}) (int, error) {
	query := "INSERT INTO " + t.name
	query += " (" + csv(t.columns[1:]) + ") "
	query += " values (" + csQ(len(t.columns)-1) + ")"

	res, err := t.db.Exec(query, fieldValues(t.fields[1:], reflect.ValueOf(row))...)
	if err != nil {
		return 0, err
	}

	id, err := res.LastInsertId()
	if err != nil {
		return 0, err
	}

	return int(id), nil
}

func (t *Table) Update(row interface{}) error {
	values := fieldValues(t.fields, reflect.ValueOf(row))

	query := "UPDATE " + t.name
	query += " SET " + csc(t.columns[1:])
	query += " WHERE id=" + strconv.Itoa(values[0].(int))

	_, err := t.db.Exec(query, values[1:]...)
	return err
}

func (t *Table) Delete(id int) error {
	query := "DELETE FROM " + t.name + " WHERE id =? "

	_, err := t.db.Exec(query, id)
	return err
}
<|endoftext|>


<commit_before>// +build tcell

package main

import (
	"errors"
	"runtime"
	"unicode"

	"github.com/gdamore/tcell"
)

type termui struct {
	tcell.Screen
	cursor position
	small  bool
}

func (ui *termui) Init() error {
	screen, err := tcell.NewScreen()
	ui.Screen = screen
	if err != nil {
		return err
	}
	return ui.Screen.Init()
}

func (ui *termui) Close() {
	ui.Screen.Fini()
}

func (ui *termui) PostInit() {
	ui.Screen.SetStyle(tcell.StyleDefault)
	if runtime.GOOS != "openbsd" {
		ui.Screen.EnableMouse()
	}
	ui.Screen.HideCursor()
	ui.HideCursor()
}

func (ui *termui) Clear() {
	ui.Screen.Clear()
	w, h := ui.Screen.Size()
	st := tcell.StyleDefault
	st = st.Foreground(tcell.Color(ColorFg)).Background(tcell.Color(ColorBg))
	for row := 0; row < h; row++ {
		for col := 0; col < w; col++ {
			ui.Screen.SetContent(col, row, ' ', nil, st)
		}
	}
}

var SmallScreen = false

func (ui *termui) Flush() {
	ui.Screen.Show()
	w, h := ui.Screen.Size()
	if w <= UIWidth-8 || h <= UIHeight-2 {
		SmallScreen = true
	} else {
		SmallScreen = false
	}
}

func (ui *termui) ApplyToggleLayout() {
	gameConfig.Small = !gameConfig.Small
}

func (ui *termui) Small() bool {
	return gameConfig.Small || SmallScreen
}

func (ui *termui) Interrupt() {
	ui.Screen.PostEvent(tcell.NewEventInterrupt(nil))
}

func (ui *termui) HideCursor() {
	ui.cursor = InvalidPos
}

func (ui *termui) SetCursor(pos position) {
	ui.cursor = pos
}

func (ui *termui) SetCell(x, y int, r rune, fg, bg uicolor) {
	st := tcell.StyleDefault
	st = st.Foreground(tcell.Color(fg)).Background(tcell.Color(bg))
	ui.Screen.SetContent(x, y, r, nil, st)
}

func (ui *termui) SetMapCell(x, y int, r rune, fg, bg uicolor) {
	ui.SetCell(x, y, r, fg, bg)
}

func (ui *termui) WaitForContinue(g *game, line int) {
loop:
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			if tev.Key() == tcell.KeyEsc {
				break loop
			}
			if tev.Rune() == ' ' {
				break loop
			}
		case *tcell.EventMouse:
			switch tev.Buttons() {
			case tcell.Button1:
				x, y := tev.Position()
				if line >= 0 {
					if y > line || x > DungeonWidth {
						break loop
					}
				} else {
					break loop
				}
			case tcell.Button2:
				break loop
			}
		}
	}
}

func (ui *termui) PromptConfirmation(g *game) bool {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			if tev.Rune() == 'Y' || tev.Rune() == 'y' {
				return true
			}
		}
		return false
	}
}

func (ui *termui) PressAnyKey() error {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			return nil
		case *tcell.EventInterrupt:
			return errors.New("interrupted")
		case *tcell.EventMouse:
			switch tev.Buttons() {
			case tcell.Button1, tcell.Button2, tcell.Button3:
				return nil
			}
		}
	}
}

func (ui *termui) PlayerTurnEvent(g *game, ev event) (err error, again, quit bool) {
	again = true
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		again = false
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyLeft:
			// TODO: will not work if user changes keybindings
			r = '4'
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		case tcell.KeyRight:
			r = '6'
		case tcell.KeyHome:
			r = '7'
		case tcell.KeyEnd:
			r = '1'
		case tcell.KeyPgUp:
			r = '9'
		case tcell.KeyPgDn:
			r = '3'
		case tcell.KeyDelete:
			r = '5'
		case tcell.KeyCtrlW:
			ui.EnterWizard(g)
			return nil, true, false
		case tcell.KeyCtrlQ:
			if ui.Quit(g) {
				return nil, false, true
			}
			return nil, true, false
		case tcell.KeyCtrlP:
			r = 'm'
		}
		err, again, quit = ui.HandleKeyAction(g, runeKeyAction{r: r})
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.ButtonNone:
		case tcell.Button1:
			x, y := tev.Position()
			pos := position{X: x, Y: y}
			if y == DungeonHeight {
				m, ok := ui.WhichButton(g, x)
				if !ok {
					again = true
					break
				}
				err, again, quit = ui.HandleKeyAction(g, runeKeyAction{k: m.Key(g)})
			} else if x >= DungeonWidth || y >= DungeonHeight {
				again = true
			} else {
				err, again, quit = ui.ExaminePos(g, ev, pos)
			}
		case tcell.Button3:
			err, again, quit = ui.HandleKeyAction(g, runeKeyAction{k: KeyMenu})
		}
	}
	if err != nil {
		again = true
	}
	return err, again, quit
}

func (ui *termui) Scroll(n int) (m int, quit bool) {
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyEsc:
			quit = true
			return n, quit
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		}
		switch r {
		case 'u':
			n -= 12
		case 'd':
			n += 12
		case 'j', '2':
			n++
		case 'k', '8':
			n--
		case ' ':
			quit = true
		}
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.WheelUp:
			n -= 2
		case tcell.WheelDown:
			n += 2
		case tcell.Button2:
			quit = true
		case tcell.Button1:
			x, y := tev.Position()
			if x >= DungeonWidth {
				quit = true
				break
			}
			if y > UIHeight {
				break
			}
			n += y - (DungeonHeight+3)/2
		}
	}
	return n, quit
}

func (ui *termui) ReadRuneKey() rune {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			r := tev.Rune()
			if r == ' ' {
				return 0
			}
			if unicode.IsPrint(r) {
				return r
			}
			switch tev.Key() {
			case tcell.KeyEsc:
				return 0
			}
		}
	}
}

func (ui *termui) KeyMenuAction(n int) (m int, action keyConfigAction) {
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyEsc:
			action = QuitKeyConfig
			return n, action
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		}
		switch r {
		case 'a':
			action = ChangeKeys
		case 'u':
			n -= DungeonHeight / 2
		case 'd':
			n += DungeonHeight / 2
		case 'j', '2':
			n++
		case 'k', '8':
			n--
		case 'R':
			action = ResetKeys
		case ' ':
			action = QuitKeyConfig
		}
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.Button1:
			x, y := tev.Position()
			if x > DungeonWidth || y > DungeonHeight {
				action = QuitKeyConfig
			}
		case tcell.WheelUp:
			n -= 2
		case tcell.WheelDown:
			n += 2
		case tcell.Button2:
			action = QuitKeyConfig
		}
	}
	return n, action
}

func (ui *termui) TargetModeEvent(g *game, targ Targeter, data *examineData) (err error, again, quit, notarg bool) {
	again = true
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyLeft:
			r = '4'
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		case tcell.KeyRight:
			r = '6'
		case tcell.KeyHome:
			r = '7'
		case tcell.KeyEnd:
			r = '1'
		case tcell.KeyPgUp:
			r = '9'
		case tcell.KeyPgDn:
			r = '3'
		case tcell.KeyDelete:
			r = '5'
		case tcell.KeyEsc:
			g.Targeting = InvalidPos
			notarg = true
			return
		case tcell.KeyEnter:
			r = '.'
		}
		err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{r: r}, data)
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.Button1:
			x, y := tev.Position()
			if y == DungeonHeight {
				m, ok := ui.WhichButton(g, x)
				if !ok {
					g.Targeting = InvalidPos
					notarg = true
					err = errors.New(DoNothing)
					break
				}
				err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: m.Key(g)}, data)
			} else if x >= DungeonWidth || y >= DungeonHeight {
				g.Targeting = InvalidPos
				notarg = true
				err = errors.New(DoNothing)
			} else {
				again, notarg = ui.CursorMouseLeft(g, targ, position{X: x, Y: y}, data)
			}
		case tcell.Button3:
			x, y := tev.Position()
			if y >= DungeonHeight || x >= DungeonWidth {
				err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: KeyMenu}, data)
			} else {
				err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: KeyDescription}, data)
			}
		case tcell.Button2:
			err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: KeyExclude}, data)
		}
	}
	return err, again, quit, notarg
}

func (ui *termui) Select(g *game, l int) (index int, alternate bool, err error) {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			if tev.Key() == tcell.KeyEsc {
				return -1, false, errors.New(DoNothing)
			}
			r := tev.Rune()
			if 97 <= r && int(r) < 97+l {
				return int(r - 97), false, nil
			}
			if r == '?' {
				return -1, true, nil
			}
			if r == ' ' {
				return -1, false, errors.New(DoNothing)
			}
		case *tcell.EventMouse:
			switch tev.Buttons() {
			case tcell.Button1:
				x, y := tev.Position()
				if y < 0 || y > l || x >= DungeonWidth {
					return -1, false, errors.New(DoNothing)
				}
				if y == 0 {
					return -1, true, nil
				}
				return y - 1, false, nil
			case tcell.Button3:
				return -1, true, nil
			case tcell.Button2:
				return -1, false, errors.New(DoNothing)
			}
		}
	}
}
<commit_msg>improve screen clearing code in tcell<commit_after>// +build tcell

package main

import (
	"errors"
	"runtime"
	"unicode"

	"github.com/gdamore/tcell"
)

type termui struct {
	tcell.Screen
	cursor position
	small  bool
}

func (ui *termui) Init() error {
	screen, err := tcell.NewScreen()
	ui.Screen = screen
	if err != nil {
		return err
	}
	return ui.Screen.Init()
}

func (ui *termui) Close() {
	ui.Screen.Fini()
}

func (ui *termui) PostInit() {
	ui.Screen.SetStyle(tcell.StyleDefault)
	if runtime.GOOS != "openbsd" {
		ui.Screen.EnableMouse()
	}
	ui.Screen.HideCursor()
	ui.HideCursor()
}

func (ui *termui) Clear() {
	w, h := ui.Screen.Size()
	if w > UIWidth {
		w = UIWidth
	}
	if h > UIHeight {
		h = UIHeight
	}
	st := tcell.StyleDefault
	st = st.Foreground(tcell.Color(ColorFg)).Background(tcell.Color(ColorBg))
	for row := 0; row < h; row++ {
		for col := 0; col < w; col++ {
			ui.Screen.SetContent(col, row, ' ', nil, st)
		}
	}
}

var SmallScreen = false

func (ui *termui) Flush() {
	ui.Screen.Show()
	w, h := ui.Screen.Size()
	if w <= UIWidth-8 || h <= UIHeight-2 {
		SmallScreen = true
	} else {
		SmallScreen = false
	}
}

func (ui *termui) ApplyToggleLayout() {
	gameConfig.Small = !gameConfig.Small
}

func (ui *termui) Small() bool {
	return gameConfig.Small || SmallScreen
}

func (ui *termui) Interrupt() {
	ui.Screen.PostEvent(tcell.NewEventInterrupt(nil))
}

func (ui *termui) HideCursor() {
	ui.cursor = InvalidPos
}

func (ui *termui) SetCursor(pos position) {
	ui.cursor = pos
}

func (ui *termui) SetCell(x, y int, r rune, fg, bg uicolor) {
	st := tcell.StyleDefault
	st = st.Foreground(tcell.Color(fg)).Background(tcell.Color(bg))
	ui.Screen.SetContent(x, y, r, nil, st)
}

func (ui *termui) SetMapCell(x, y int, r rune, fg, bg uicolor) {
	ui.SetCell(x, y, r, fg, bg)
}

func (ui *termui) WaitForContinue(g *game, line int) {
loop:
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			if tev.Key() == tcell.KeyEsc {
				break loop
			}
			if tev.Rune() == ' ' {
				break loop
			}
		case *tcell.EventMouse:
			switch tev.Buttons() {
			case tcell.Button1:
				x, y := tev.Position()
				if line >= 0 {
					if y > line || x > DungeonWidth {
						break loop
					}
				} else {
					break loop
				}
			case tcell.Button2:
				break loop
			}
		}
	}
}

func (ui *termui) PromptConfirmation(g *game) bool {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			if tev.Rune() == 'Y' || tev.Rune() == 'y' {
				return true
			}
		}
		return false
	}
}

func (ui *termui) PressAnyKey() error {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			return nil
		case *tcell.EventInterrupt:
			return errors.New("interrupted")
		case *tcell.EventMouse:
			switch tev.Buttons() {
			case tcell.Button1, tcell.Button2, tcell.Button3:
				return nil
			}
		}
	}
}

func (ui *termui) PlayerTurnEvent(g *game, ev event) (err error, again, quit bool) {
	again = true
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		again = false
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyLeft:
			// TODO: will not work if user changes keybindings
			r = '4'
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		case tcell.KeyRight:
			r = '6'
		case tcell.KeyHome:
			r = '7'
		case tcell.KeyEnd:
			r = '1'
		case tcell.KeyPgUp:
			r = '9'
		case tcell.KeyPgDn:
			r = '3'
		case tcell.KeyDelete:
			r = '5'
		case tcell.KeyCtrlW:
			ui.EnterWizard(g)
			return nil, true, false
		case tcell.KeyCtrlQ:
			if ui.Quit(g) {
				return nil, false, true
			}
			return nil, true, false
		case tcell.KeyCtrlP:
			r = 'm'
		}
		err, again, quit = ui.HandleKeyAction(g, runeKeyAction{r: r})
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.ButtonNone:
		case tcell.Button1:
			x, y := tev.Position()
			pos := position{X: x, Y: y}
			if y == DungeonHeight {
				m, ok := ui.WhichButton(g, x)
				if !ok {
					again = true
					break
				}
				err, again, quit = ui.HandleKeyAction(g, runeKeyAction{k: m.Key(g)})
			} else if x >= DungeonWidth || y >= DungeonHeight {
				again = true
			} else {
				err, again, quit = ui.ExaminePos(g, ev, pos)
			}
		case tcell.Button3:
			err, again, quit = ui.HandleKeyAction(g, runeKeyAction{k: KeyMenu})
		}
	}
	if err != nil {
		again = true
	}
	return err, again, quit
}

func (ui *termui) Scroll(n int) (m int, quit bool) {
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyEsc:
			quit = true
			return n, quit
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		}
		switch r {
		case 'u':
			n -= 12
		case 'd':
			n += 12
		case 'j', '2':
			n++
		case 'k', '8':
			n--
		case ' ':
			quit = true
		}
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.WheelUp:
			n -= 2
		case tcell.WheelDown:
			n += 2
		case tcell.Button2:
			quit = true
		case tcell.Button1:
			x, y := tev.Position()
			if x >= DungeonWidth {
				quit = true
				break
			}
			if y > UIHeight {
				break
			}
			n += y - (DungeonHeight+3)/2
		}
	}
	return n, quit
}

func (ui *termui) ReadRuneKey() rune {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			r := tev.Rune()
			if r == ' ' {
				return 0
			}
			if unicode.IsPrint(r) {
				return r
			}
			switch tev.Key() {
			case tcell.KeyEsc:
				return 0
			}
		}
	}
}

func (ui *termui) KeyMenuAction(n int) (m int, action keyConfigAction) {
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyEsc:
			action = QuitKeyConfig
			return n, action
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		}
		switch r {
		case 'a':
			action = ChangeKeys
		case 'u':
			n -= DungeonHeight / 2
		case 'd':
			n += DungeonHeight / 2
		case 'j', '2':
			n++
		case 'k', '8':
			n--
		case 'R':
			action = ResetKeys
		case ' ':
			action = QuitKeyConfig
		}
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.Button1:
			x, y := tev.Position()
			if x > DungeonWidth || y > DungeonHeight {
				action = QuitKeyConfig
			}
		case tcell.WheelUp:
			n -= 2
		case tcell.WheelDown:
			n += 2
		case tcell.Button2:
			action = QuitKeyConfig
		}
	}
	return n, action
}

func (ui *termui) TargetModeEvent(g *game, targ Targeter, data *examineData) (err error, again, quit, notarg bool) {
	again = true
	switch tev := ui.Screen.PollEvent().(type) {
	case *tcell.EventKey:
		r := tev.Rune()
		switch tev.Key() {
		case tcell.KeyLeft:
			r = '4'
		case tcell.KeyDown:
			r = '2'
		case tcell.KeyUp:
			r = '8'
		case tcell.KeyRight:
			r = '6'
		case tcell.KeyHome:
			r = '7'
		case tcell.KeyEnd:
			r = '1'
		case tcell.KeyPgUp:
			r = '9'
		case tcell.KeyPgDn:
			r = '3'
		case tcell.KeyDelete:
			r = '5'
		case tcell.KeyEsc:
			g.Targeting = InvalidPos
			notarg = true
			return
		case tcell.KeyEnter:
			r = '.'
		}
		err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{r: r}, data)
	case *tcell.EventMouse:
		switch tev.Buttons() {
		case tcell.Button1:
			x, y := tev.Position()
			if y == DungeonHeight {
				m, ok := ui.WhichButton(g, x)
				if !ok {
					g.Targeting = InvalidPos
					notarg = true
					err = errors.New(DoNothing)
					break
				}
				err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: m.Key(g)}, data)
			} else if x >= DungeonWidth || y >= DungeonHeight {
				g.Targeting = InvalidPos
				notarg = true
				err = errors.New(DoNothing)
			} else {
				again, notarg = ui.CursorMouseLeft(g, targ, position{X: x, Y: y}, data)
			}
		case tcell.Button3:
			x, y := tev.Position()
			if y >= DungeonHeight || x >= DungeonWidth {
				err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: KeyMenu}, data)
			} else {
				err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: KeyDescription}, data)
			}
		case tcell.Button2:
			err, again, quit, notarg = ui.CursorKeyAction(g, targ, runeKeyAction{k: KeyExclude}, data)
		}
	}
	return err, again, quit, notarg
}

func (ui *termui) Select(g *game, l int) (index int, alternate bool, err error) {
	for {
		switch tev := ui.Screen.PollEvent().(type) {
		case *tcell.EventKey:
			if tev.Key() == tcell.KeyEsc {
				return -1, false, errors.New(DoNothing)
			}
			r := tev.Rune()
			if 97 <= r && int(r) < 97+l {
				return int(r - 97), false, nil
			}
			if r == '?' {
				return -1, true, nil
			}
			if r == ' ' {
				return -1, false, errors.New(DoNothing)
			}
		case *tcell.EventMouse:
			switch tev.Buttons() {
			case tcell.Button1:
				x, y := tev.Position()
				if y < 0 || y > l || x >= DungeonWidth {
					return -1, false, errors.New(DoNothing)
				}
				if y == 0 {
					return -1, true, nil
				}
				return y - 1, false, nil
			case tcell.Button3:
				return -1, true, nil
			case tcell.Button2:
				return -1, false, errors.New(DoNothing)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/tzaffi/go-playground/z/node"
	"github.com/tzaffi/go-playground/z/q"
)

func main() {
	switch cmd := "BFS_BY_LEVEL"; cmd {
	case "BFS_BY_LEVEL":
		fmt.Println("BFS by level")
		bfsByLevel()
	default:
		fmt.Print("did not select a viable choice to run\n")
	}
}

/*  7
   / \
  8   4
 /\   /
1  7 3
  / \
 5   9  */
func getTreeExample() *node.Node {
	five, nine := node.Node{Val: 5}, node.Node{Val: 9}
	one, seven, three := node.Node{Val: 1}, node.Node{Val: 7, Left: &five, Right: &nine}, node.Node{Val: 3}
	eight, four := node.Node{Val: 8, Left: &one, Right: &seven}, node.Node{Val: 4, Left: &three}
	return &node.Node{Val: 7, Left: &eight, Right: &four}
}

func printBFS(root *node.Node) {
	if root == nil {
		return
	}
	currDepth := 0
	root.Depth = currDepth

	q := queue.NodeQ{}
	q.Push(root)
	for !q.Empty() {
		n, _ := q.Pop()
		if n.Depth > currDepth {
			fmt.Print("\n")
			currDepth = n.Depth
		}
		fmt.Printf("%d ", n.Val)
		l, r := n.Left, n.Right
		if l != nil {
			l.Depth = currDepth + 1
			q.Push(l)
		}
		if r != nil {
			r.Depth = currDepth + 1
			q.Push(r)
		}
	}
}

func bfsByLevel() {
	root := getTreeExample()
	printBFS(root)
}
<commit_msg>adding paintFill()<commit_after>package main

import (
	"fmt"
	"github.com/tzaffi/go-playground/z/node"
	"github.com/tzaffi/go-playground/z/q"
)

func main() {
	switch cmd := "PAINT_FILL"; cmd {
	case "BFS_BY_LEVEL":
		fmt.Println("BFS by level")
		bfsByLevel()
	case "PAINT_FILL":
		fmt.Println("Paint Fill")
		paintFill()
	default:
		fmt.Print("did not select a viable choice to run\n")
	}
}

type Screen [][]uint32

var screen Screen
var w, h int

func (a Screen) String() string {
	res := ""
	for _, r := range a {
		res += fmt.Sprintf("%v\n", r)
	}
	return res
}

func paintFill() {
	w, h := 20, 10
	screen = Screen(make([][]uint32, h))
	for i := range screen {
		screen[i] = make([]uint32, w)
	}
	//make diagonal:
	for x, y := w-1, 0; x >= 0 && y < h; x, y = x-1, y+1 {
		screen[y][x] = 1
	}

	fmt.Printf("BEFORE:\n%s\n", screen.String())

	var paintR func(x, y int, oc, color uint32)
	paintR = func(x, y int, oc, color uint32) {
		if x < 0 || y < 0 || x >= w || y >= h || screen[y][x] != oc {
			return
		}
		screen[y][x] = color
		paintR(x+1, y, oc, color)
		paintR(x, y+1, oc, color)
		paintR(x-1, y, oc, color)
		paintR(x, y-1, oc, color)
	}

	paintIn := func(x, y int, color uint32) {
		oc := screen[y][x]
		paintR(x, y, oc, color)
	}

	paintIn(7, 7, 7)
	fmt.Printf("AFTER paintIn(%d, %d, %d):\n%s\n", 7, 7, 7, screen.String())

	paintIn(19, 9, 3)
	fmt.Printf("AFTER paintIn(%d, %d, %d):\n%s\n", 9, 19, 3, screen.String())
}

/*  7
   / \
  8   4
 /\   /
1  7 3
  / \
 5   9  */
func getTreeExample() *node.Node {
	five, nine := node.Node{Val: 5}, node.Node{Val: 9}
	one, seven, three := node.Node{Val: 1}, node.Node{Val: 7, Left: &five, Right: &nine}, node.Node{Val: 3}
	eight, four := node.Node{Val: 8, Left: &one, Right: &seven}, node.Node{Val: 4, Left: &three}
	return &node.Node{Val: 7, Left: &eight, Right: &four}
}

func printBFS(root *node.Node) {
	if root == nil {
		return
	}
	currDepth := 0
	root.Depth = currDepth

	q := queue.NodeQ{}
	q.Push(root)
	for !q.Empty() {
		n, _ := q.Pop()
		if n.Depth > currDepth {
			fmt.Print("\n")
			currDepth = n.Depth
		}
		fmt.Printf("%d ", n.Val)
		l, r := n.Left, n.Right
		if l != nil {
			l.Depth = currDepth + 1
			q.Push(l)
		}
		if r != nil {
			r.Depth = currDepth + 1
			q.Push(r)
		}
	}
}

func bfsByLevel() {
	root := getTreeExample()
	printBFS(root)
}
<|endoftext|>