<commit_before>package osc

// #cgo LDFLAGS: -llo
// #include "lo/lo.h"
import "C"

import (
    "unsafe"
    "runtime"
    )

/* to implement liblo's high level OSC interface, we need the following:
timetag type
message arg type, can act as int32, int64, float, double, char, unsigned char, uint8[4], and timetag
 */

type Timetag struct {
    uint32 Sec
    uint32 Frac
}

type OscType byte

type Arg interface {
    Type() OscType
    GetChar() uint8
    GetDouble() float64
    GetFloat() float32
    GetInt64() int64
    GetInt32() int32
    GetMidiMsg() uint8[4]
    GetSymbol() string
    GetString() string
    GetTimetag() Timetag
}

const (
    Udp = iota
    Tcp
    Unix
    )

const (
    Int32 = 'i'
    Float = 'f'
    String = 's'
    Blob = 'b'
    Int64 = 'h'
    Timetag = 't'
    Double = 'd'
    Symbol = 'S'
    Char = 'c'
    MidiMsg = 'm'
    True = 'T'
    False = 'F'
    Nil = 'N'
    Infinitum = 'I'
)

var Now = Timetag{0,1}

/* opaque address type */
type Address struct {
    lo_address unsafe.Pointer
    dead bool
}

func NewAddress(host, port string) (ret *Address) {
    ret = new(Address)
    ret.dead = false
    chost := C.CString(host)
    defer C.free(unsafe.Pointer(chost))
    cport := C.CString(port)
    defer C.free(unsafe.Pointer(cport))
    ret.lo_address = C.lo_address_new(chost, cport)
    runtime.SetFinalizer(ret, (*Address).Free)
    return
}

func NewAddressWithProto(proto int, host, port string) (ret *Address) {
    ret = new(Address)
    ret.dead = false
    chost := C.CString(host)
    defer C.free(unsafe.Pointer(chost))
    cport := C.CString(port)
    defer C.free(unsafe.Pointer(cport))
    ret.lo_address = C.lo_address_new_with_proto(proto, chost, cport)
    runtime.SetFinalizer(ret, (*Address).Free)
    return
}

func NewAddressFromUrl(url string) (ret *Address) {
    ret = new(Address)
    ret.dead = false
    curl := C.CString(url)
    defer C.free(unsafe.Pointer(curl))
    ret.lo_address = C.lo_address_new_from_url(curl)
    runtime.SetFinalizer(ret, (*Address).Free)
    return
}

func (this *Address) SetTtl(ttl int) {
    if (this.dead) {
        panic("Method called on dead object")
    }
    C.lo_address_set_ttl(this.lo_address, ttl)
}

func (this *Address) GetTtl() (ttl int) {
    if (this.dead) {
        panic("Method called on dead object")
    }
    ttl = C.lo_address_get_ttl(this.lo_address)
}

func (this *Address) Errno() int {
    if (this.dead) {
        panic("Method called on dead object")
    }
    return C.lo_address_errno(this.lo_address)
}

func (this *Address) Errstr() string {
    if (this.dead) {
        panic("Method called on dead object")
    }
    return C.GoString(C.lo_address_errstr(this.lo_address))
}

func (this *Address) Free() {
    if (this.dead) {
        panic("Method called on dead object")
    }
    C.lo_address_free(this.lo_address)
    this.lo_address = nil
    this.dead = true
}

/* Why go through a bunch of junk to use the lo blob type? Just make a byte slice and call lo_blob_new when we add it to a message */
type Blob []byte

type Message struct {
    lo_message unsafe.Pointer
    dead bool
}

func NewMessage() (ret *Message) {
    ret.dead = false
    lo_message = C.lo_message_new()
    runtime.SetFinalizer(ret, (*Message).Free)
    return
}

func (this *Message) Free() {
    this.dead = true
    C.lo_message_free(this.lo_message)
}

func (this *Message) Add(arg Arg) int {

func Send(targ Address, path string, args ...Arg) ret int {
    msg := NewMessage(args)
}

type Bundle struct {
    lo_address unsafe.Pointer
}<commit_msg>more progress<commit_after>package osc

// #cgo LDFLAGS: -llo
// #include "lo/lo.h"
import "C"

import (
    "unsafe"
    "runtime"
    )

/* to implement liblo's high level OSC interface, we need the following:
timetag type
message arg type, can act as int32, int64, float, double, char, unsigned char, uint8[4], and timetag
 */

type Timetag struct {
    uint32 Sec
    uint32 Frac
}

type MidiMsg uint8[4]

type OscType byte

type Arg interface {
    GetType() OscType
    GetValue() interface{}
}

type SymbolType string

func (this *SymbolType) GetType() OscType {
    return Symbol
}

func (this *SymbolType) GetValue interface{} {
    return *this
}

type InfinitumType interface{}

func (this *InfinitumType) GetType() OscType {
    return Infinitum
}

func (this *InfinitumType) GetValue interface{} {
    return nil
}

type goTypeWrapper struct {
    Arg
    value interface{}
}

func (this *goTypeWrapper) GetType() o OscType {
    switch i := (*this).(type) {
    case int32:
        o = Int32
    case float32:
        o = Float32
    case string:
        o = String
    case Blob:
        o = BlobCode
    case int64:
        o = Int64
    case Timetag:
        o = TimetagCode
    case float64:
        o = Double
        // no way to automatically detect symbols!
    case byte:
        o = Char
    case MidiMsg:
        o = MidiMsgCode
    case bool:
        if bool(*this) {
            o = True
        } else {
            o = False
        }
    case nil:
        o = Nil
        // no way to detect infinitum
    }
}

const (
    Udp = iota
    Tcp
    Unix
    )

const (
    Int32 OscType = 'i'
    Float OscType = 'f'
    String OscType = 's'
    BlobCode OscType = 'b'
    Int64 OscType = 'h'
    TimetagCode OscType = 't'
    Double OscType = 'd'
    Symbol OscType = 'S'
    Char OscType = 'c'
    MidiMsgCode OscType = 'm'
    True OscType = 'T'
    False OscType = 'F'
    Nil OscType = 'N'
    Infinitum OscType = 'I'
)

const Now = Timetag{0,1}

/* opaque address type */
type Address struct {
    lo_address unsafe.Pointer
    dead bool
}

func NewAddress(host, port string) (ret *Address) {
    ret = new(Address)
    ret.dead = false
    chost := C.CString(host)
    defer C.free(unsafe.Pointer(chost))
    cport := C.CString(port)
    defer C.free(unsafe.Pointer(cport))
    ret.lo_address = C.lo_address_new(chost, cport)
    runtime.SetFinalizer(ret, (*Address).Free)
    return
}

func NewAddressWithProto(proto int, host, port string) (ret *Address) {
    ret = new(Address)
    ret.dead = false
    chost := C.CString(host)
    defer C.free(unsafe.Pointer(chost))
    cport := C.CString(port)
    defer C.free(unsafe.Pointer(cport))
    ret.lo_address = C.lo_address_new_with_proto(proto, chost, cport)
    runtime.SetFinalizer(ret, (*Address).Free)
    return
}

func NewAddressFromUrl(url string) (ret *Address) {
    ret = new(Address)
    ret.dead = false
    curl := C.CString(url)
    defer C.free(unsafe.Pointer(curl))
    ret.lo_address = C.lo_address_new_from_url(curl)
    runtime.SetFinalizer(ret, (*Address).Free)
    return
}

func (this *Address) SetTtl(ttl int) {
    if (this.dead) {
        panic("Method called on dead object")
    }
    C.lo_address_set_ttl(this.lo_address, ttl)
}

func (this *Address) GetTtl() (ttl int) {
    if (this.dead) {
        panic("Method called on dead object")
    }
    ttl = C.lo_address_get_ttl(this.lo_address)
}

func (this *Address) Errno() int {
    if (this.dead) {
        panic("Method called on dead object")
    }
    return C.lo_address_errno(this.lo_address)
}

func (this *Address) Errstr() string {
    if (this.dead) {
        panic("Method called on dead object")
    }
    return C.GoString(C.lo_address_errstr(this.lo_address))
}

func (this *Address) Free() {
    if (this.dead) {
        return
    }
    C.lo_address_free(this.lo_address)
    this.lo_address = nil
    this.dead = true
}

/* Why go through a bunch of junk to use the lo blob type? Just make a byte slice and call lo_blob_new when we add it to a message */
type Blob []byte

/* why use the message type before it's time to send it? */
type Message []Arg

func (this *Message) Send(targ Address, path string) ret int {
    ret = this.SendTimestamped(targ, Now, path)
}

func (this *Message) SendTimestamped(targ Address, time Timetag, path string) ret int {
    // build a new lo_message
    m := C.lo_message_new()
    defer C.lo_message_free(unsafe.Pointer(m))
    for _, arg := range m {
        switch arg.GetType() {
        case Int32:
            C.lo_message_add_int32(m, int32(arg.GetValue()))
        case Float:
            C.lo_message_add_float(m, float32(arg.GetValue()))
        case Blob:
            b := C.lo_message_blob_new(len(Blob(arg)), unsafe.Pointer(arg))
            defer C.lo_blob_free(unsafe.Pointer(b))
            C.lo_message_add_blob(m, b)
        case:

    }
}

type Bundle struct {
    lo_address unsafe.Pointer
}<|endoftext|>


<commit_before>package gootp

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
	"math"
	"strconv"
	"strings"
	"time"
)

func hmacMsg(c, key []byte, h func() hash.Hash) ([]byte, error) {
	//Hash the message derived from the time with the key from the TOTP secret
	mac := hmac.New(h, key)
	_, err := mac.Write(c)
	if err != nil {
		return make([]byte, 0), err
	}
	return mac.Sum(nil), nil
}

func generateHashKey(s string) ([]byte, error) {
	//Decode and convert the base32 encoded secret string to a byte array
	key, err := base32.StdEncoding.DecodeString(strings.ToUpper(s))
	if err != nil {
		return make([]byte, 0), err
	}
	return key, nil
}

func generateHashCountFromTime(t time.Time) ([]byte, error) {
	//The message to be hashed is the count of the number of time intervals
	c := t.Unix() / 30
	return generateHashCount(c)
}

func generateHashCount(c int64) ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, c)
	if err != nil {
		return make([]byte, 0), err
	}
	return buf.Bytes(), nil
}

func extractFromHash(hash []byte) uint32 {
	//Get the last 4 bits of the hash (a value ranging from 0-15)
	// This will be the index into the 20-byte value
	iBits := hash[len(hash)-1] & 0xf
	iInt := int64(iBits)

	//Extract the next 4 bytes starting at the index and convert to uint32
	r := bytes.NewReader(hash)
	var b [4]byte
	r.ReadAt(b[:], iInt)
	b[0] = b[0] & 0x7f
	return binary.BigEndian.Uint32(b[:])
}

/*
Get the HMAC-based One Time Password (RFC 4226). Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length
	- Counter value, the moving factor (see RFC 4226 section 5.2).  This counter MUST be synchronized between the HOTP generator (client) and the HOTP validator (server).
	- A hash function to use, eg SHA1, SHA256, SHA512
	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate
*/
func GetHOTP(secret string, count int64, mode func() hash.Hash, digits int) (otp string, err error) {
	if digits < 6 {
		err = errors.New("The number of digits of the OTP generated must be at least 6")
		return
	}
	if len(secret) < 16 {
		err = errors.New("The secret string used to generate the OTP must be at least 128 bits")
		return
	}
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCount(count)
	if err != nil {
		return
	}
	if mode == nil {
		mode = sha1.New
	}
	hash, err := hmacMsg(msg, key, mode)
	if err != nil {
		return
	}
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

/*
Get the Time-based One Time Password (RFC 6238) for the current time. Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length.
	- A hash function to use, eg SHA1, SHA256, SHA512.
	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
The number of seconds the OTP is valid for is also returned.
*/
func GetTOTPNow(secret string, mode func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	otp, timeRemaining, err = GetTOTPAt(secret, time.Now().UTC(), mode, digits)
	return
}

/*
Get the Time-based One Time Password (RFC 6238) for a specific time. Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length.
	- The UTC time for which the TOTP should be generated.
	- A hash function to use, eg SHA1, SHA256, SHA512.
	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
The number of seconds the OTP is valid for is also returned.
*/
func GetTOTPAt(secret string, t time.Time, h func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCountFromTime(t)
	if err != nil {
		return
	}
	hash, err := hmacMsg(msg, key, h)
	if err != nil {
		return
	}
	timeRemaining = int(30 - math.Mod(float64(t.Unix()), 30))
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

/*
Get a Time-based One Time Password history (RFC 6238). Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length.
 	- A hash function to use, eg SHA1, SHA256, SHA512.
 	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is an array of strings as a leading zero is valid so an integer type is not appropriate. The first element in the array is the current OTP.
The number of seconds the current OTP is valid for is also returned.
*/
func GetTOTPHistory(secret string, h func() hash.Hash, digits int, history int) (otps []string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	c := time.Now().UTC().Unix() / 30
	timeRemaining = int(30 - math.Mod(float64(time.Now().UTC().Unix()), 30))
	for i := 0; i < history; i++ {
		c = c - int64(i)
		msg, ierr := generateHashCount(c)
		if ierr != nil {
			err = ierr
			return
		}
		hash, ierr := hmacMsg(msg, key, h)
		if ierr != nil {
			err = ierr
			return
		}
		otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
		otp := fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
		otps = append(otps, otp)
	}
	return
}

// Generate a base32 encoded secret string to be shared between the client and the server for OTPs
// Specify the length of the secret to generate in bytes. Note this needs to be at least 16 bytes / 128 bits.
func GenerateOTPSecret(s int) (string, error) {
	if s < 16 {
		return "", errors.New("The secret size needs to be at least 16 bytes / 128 bits")
	}
	b, err := generateRandomBytes(s)
	return base32.StdEncoding.EncodeToString(b), err
}

func generateRandomBytes(n int) ([]byte, error) {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		return nil, err
	}

	return b, nil
}
<commit_msg>update<commit_after>package gootp

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base32"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
	"math"
	"strconv"
	"strings"
	"time"
)

func hmacMsg(c, key []byte, h func() hash.Hash) ([]byte, error) {
	//Hash the message derived from the time with the key from the TOTP secret
	mac := hmac.New(h, key)
	_, err := mac.Write(c)
	if err != nil {
		return make([]byte, 0), err
	}
	return mac.Sum(nil), nil
}

func generateHashKey(s string) ([]byte, error) {
	//Decode and convert the base32 encoded secret string to a byte array
	key, err := base32.StdEncoding.DecodeString(strings.ToUpper(s))
	if err != nil {
		return make([]byte, 0), err
	}
	return key, nil
}

func generateHashCountFromTime(t time.Time) ([]byte, error) {
	//The message to be hashed is the count of the number of time intervals
	c := t.Unix() / 30
	return generateHashCount(c)
}

func generateHashCount(c int64) ([]byte, error) {
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.BigEndian, c)
	if err != nil {
		return make([]byte, 0), err
	}
	return buf.Bytes(), nil
}

func extractFromHash(hash []byte) uint32 {
	//Get the last 4 bits of the hash (a value ranging from 0-15)
	// This will be the index into the 20-byte value
	iBits := hash[len(hash)-1] & 0xf
	iInt := int64(iBits)

	//Extract the next 4 bytes starting at the index and convert to uint32
	r := bytes.NewReader(hash)
	var b [4]byte
	r.ReadAt(b[:], iInt)
	b[0] = b[0] & 0x7f
	return binary.BigEndian.Uint32(b[:])
}

/*
Get the HMAC-based One Time Password (RFC 4226). Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length
	- Counter value, the moving factor (see RFC 4226 section 5.2).  This counter MUST be synchronized between the HOTP generator (client) and the HOTP validator (server).
	- A hash function to use, eg SHA1, SHA256, SHA512
	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate
*/
func GetHOTP(secret string, count int64, mode func() hash.Hash, digits int) (otp string, err error) {
	if digits < 6 {
		err = errors.New("The number of digits of the OTP generated must be at least 6")
		return
	}
	if len(secret) < 16 {
		err = errors.New("The secret string used to generate the OTP must be at least 128 bits")
		return
	}
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCount(count)
	if err != nil {
		return
	}
	if mode == nil {
		mode = sha1.New
	}
	hash, err := hmacMsg(msg, key, mode)
	if err != nil {
		return
	}
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

/*
Get the Time-based One Time Password (RFC 6238) for the current time. Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length.
	- A hash function to use, eg SHA1, SHA256, SHA512.
	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
The number of seconds the OTP is valid for is also returned.
*/
func GetTOTPNow(secret string, mode func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	otp, timeRemaining, err = GetTOTPAt(secret, time.Now().UTC(), mode, digits)
	return
}

/*
Get the Time-based One Time Password (RFC 6238) for a specific time. Providing the following inputs:
	- Secret string at least 16 bytes / 128 bits in length.
	- The UTC time for which the TOTP should be generated.
	- A hash function to use, eg SHA1, SHA256, SHA512.
	- The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is a string as a leading zero is valid so an integer type is not appropriate.
The number of seconds the OTP is valid for is also returned.
*/
func GetTOTPAt(secret string, t time.Time, h func() hash.Hash, digits int) (otp string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	msg, err := generateHashCountFromTime(t)
	if err != nil {
		return
	}
	hash, err := hmacMsg(msg, key, h)
	if err != nil {
		return
	}
	timeRemaining = int(30 - math.Mod(float64(t.Unix()), 30))
	otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
	otp = fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
	return
}

/*
Get a Time-based One Time Password history (RFC 6238). Providing the following inputs:
 - Secret string at least 16 bytes / 128 bits in length.
 - A hash function to use, eg SHA1, SHA256, SHA512.
 - The number of digits to be returned in the OTP. Must be a minimum of 6.

Note that the returned OTP is an array of strings as a leading zero is valid so an integer type is not appropriate. The first element in the array is the current OTP.
The number of seconds the current OTP is valid for is also returned.
*/
func GetTOTPHistory(secret string, h func() hash.Hash, digits int, history int) (otps []string, timeRemaining int, err error) {
	key, err := generateHashKey(secret)
	if err != nil {
		return
	}
	c := time.Now().UTC().Unix() / 30
	timeRemaining = int(30 - math.Mod(float64(time.Now().UTC().Unix()), 30))
	for i := 0; i < history; i++ {
		c = c - int64(i)
		msg, ierr := generateHashCount(c)
		if ierr != nil {
			err = ierr
			return
		}
		hash, ierr := hmacMsg(msg, key, h)
		if ierr != nil {
			err = ierr
			return
		}
		otpInt := int(math.Mod(float64(extractFromHash(hash)), math.Pow(10, float64(digits))))
		otp := fmt.Sprintf("%0"+strconv.Itoa(digits)+"d", otpInt)
		otps = append(otps, otp)
	}
	return
}

// Generate a base32 encoded secret string to be shared between the client and the server for OTPs
// Specify the length of the secret to generate in bytes. Note this needs to be at least 16 bytes / 128 bits.
func GenerateOTPSecret(s int) (string, error) {
	if s < 16 {
		return "", errors.New("The secret size needs to be at least 16 bytes / 128 bits")
	}
	b, err := generateRandomBytes(s)
	return base32.StdEncoding.EncodeToString(b), err
}

func generateRandomBytes(n int) ([]byte, error) {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		return nil, err
	}

	return b, nil
}
<|endoftext|>


<commit_before>package pie

import (
	"errors"
	"os"
)

var (
	// ErrTableNotFound is returned when referencing a table that doesn't exist.
	ErrTableNotFound = errors.New("table not found")

	// ErrTableExists is returned when creating a table that already exists.
	ErrTableExists = errors.New("table already exists")

	// ErrTableNameRequired is returned when a blank table name is passed in.
	ErrTableNameRequired = errors.New("table name required")
)

// Database represents a collection of tables.
type Database struct {
	path   string
	tables map[string]*Table
}

// NewDatabase returns a new instance of Database.
func NewDatabase() *Database {
	return &Database{
		tables: make(map[string]*Table),
	}
}

// Open opens and initializes a database at a given file path.
func (db *Database) Open(path string) error {
	// Make a new directory.
	if err := os.Mkdir(path, 0700); err != nil {
		return err
	}

	// Set the path.
	db.path = path

	// TODO: Open meta file.

	return nil
}

func (db *Database) Close() error {
	// Unset the path.
	db.path = ""
	return nil
}

// Table returns a table by name.
func (db *Database) Table(name string) *Table {
	return db.tables[name]
}

// CreateTable creates a new table.
// Returns an error if name is blank or if table already exists.
func (db *Database) CreateTable(name string, columns []*Column) error {
	// TODO: Check for blank name.
	// TODO: Check for existing table with the same name.

	// Create table.
	t := &Table{Name: name, Columns: columns}

	// Add table to the database.
	db.tables[name] = t

	return nil
}

// DeleteTable removes an existing table by name.
// Returns an error if name is blank or table is not found.
func (db *Database) DeleteTable(name string) error {
	// TODO: Check for blank name.
	// TODO: Check that table exists.
	// TODO: Remove table from the database.
	return nil
}

// Table represents a tabular set of data.
type Table struct {
	Name    string
	Columns []*Column
	Rows    [][]string
}

// Column represents a column in a table.
type Column struct {
	Name string
}
<commit_msg>add ability to add and delete tables<commit_after>package pie

import (
	"errors"
	"os"
)

var (
	// ErrTableNotFound is returned when referencing a table that doesn't exist.
	ErrTableNotFound = errors.New("table not found")

	// ErrTableExists is returned when creating a table that already exists.
	ErrTableExists = errors.New("table already exists")

	// ErrTableNameRequired is returned when a blank table name is passed in.
	ErrTableNameRequired = errors.New("table name required")
)

// Database represents a collection of tables.
type Database struct {
	path   string
	tables map[string]*Table
}

// NewDatabase returns a new instance of Database.
func NewDatabase() *Database {
	return &Database{
		tables: make(map[string]*Table),
	}
}

// Open opens and initializes a database at a given file path.
func (db *Database) Open(path string) error {
	// Make a new directory.
	if err := os.Mkdir(path, 0700); err != nil {
		return err
	}

	// Set the path.
	db.path = path

	// TODO: Open meta file.

	return nil
}

func (db *Database) Close() error {
	// Unset the path.
	db.path = ""
	return nil
}

// Table returns a table by name.
func (db *Database) Table(name string) *Table {
	return db.tables[name]
}

// CreateTable creates a new table.
// Returns an error if name is blank or if table already exists.
func (db *Database) CreateTable(name string, columns []*Column) error {
  if name == "" {
    return  ErrTableNameRequired
  } else if db.tables[name] != nil {
    return ErrTableExists
  }

	db.tables[name] = &Table{Name: name, Columns: columns}

	return nil
}

// DeleteTable removes an existing table by name.
// Returns an error if name is blank or table is not found.
func (db *Database) DeleteTable(name string) error {
	// TODO: Check for blank name.
  if name == "" {
    return ErrTableNameRequired
  } else if db.tables[name] == nil {
    return ErrTableNotFound
  }
	// TODO: Check that table exists.
	// TODO: Remove table from the database.
  delete(db.tables, name)

	return nil
}

// Table represents a tabular set of data.
type Table struct {
	Name    string
	Columns []*Column
	Rows    [][]string
}

// Column represents a column in a table.
type Column struct {
	Name string
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"math/rand"
	"os"
	"path"
	"strconv"

	"github.com/MJKWoolnough/memio"
	"github.com/MJKWoolnough/minecraft"
	"github.com/MJKWoolnough/minewebgen/internal/data"
)

type RPC struct {
	c *Config
}

func (r RPC) Settings(_ struct{}, settings *data.ServerSettings) error {
	*settings = r.c.Settings()
	return nil
}

func (r RPC) SetSettings(settings data.ServerSettings, _ *struct{}) error {
	settings.DirMaps = path.Clean(settings.DirMaps)
	settings.DirServers = path.Clean(settings.DirServers)
	if settings.DirMaps == settings.DirServers {
		return errors.New("map and server paths cannot be the same")
	}
	r.c.SetSettings(settings)
	go r.c.Save()
	return nil
}

func (r RPC) ServerName(_ struct{}, serverName *string) error {
	*serverName = r.c.Settings().ServerName
	return nil
}

func (r RPC) ServerList(_ struct{}, list *[]data.Server) error {
	r.c.Servers.mu.RLock()
	defer r.c.Servers.mu.RUnlock()
	*list = make([]data.Server, len(r.c.Servers.List))
	for n, s := range r.c.Servers.List {
		(*list)[n] = *s
	}
	return nil
}

func (r RPC) MapList(_ struct{}, list *[]data.Map) error {
	r.c.Maps.mu.RLock()
	defer r.c.Maps.mu.RUnlock()
	*list = make([]data.Map, len(r.c.Maps.List))
	for n, m := range r.c.Maps.List {
		(*list)[n] = *m
	}
	return nil
}

func (r RPC) Server(id int, s *data.Server) error {
	ser := r.c.Server(id)
	ser.RLock()
	defer ser.RUnlock()
	*s = *ser
	return nil
}

func (r RPC) Map(id int, m *data.Map) error {
	mp := r.c.Map(id)
	mp.RLock()
	defer mp.RUnlock()
	*m = *mp
	return nil
}

func (r RPC) SetServer(s data.Server, _ *struct{}) error {
	ser := r.c.Server(s.ID)
	if ser == nil {
		return ErrUnknownServer
	}
	if ser.State != data.StateStopped {
		return ErrServerRunning
	}
	ser.Lock()
	defer ser.Unlock()
	ser.Name = s.Name
	ser.Args = s.Args
	go r.c.Save()
	return nil
}

func (r RPC) SetMap(m data.Map, _ *struct{}) error {
	mp := r.c.Map(m.ID)
	if mp == nil {
		return ErrUnknownMap
	}
	mp.RLock()
	sID := mp.Server
	mp.RUnlock()
	if mp.Server != -1 {
		ser := r.c.Server(mp.Server)
		if ser != nil {
			ser.RLock()
			s := ser.State
			ser.RUnlock()
			if s != StateStopped {
				return ErrServerRunning
			}
		}
	}
	mp.Lock()
	defer mp.Unlock()
	mp.Name = m.Name
	go r.c.Save()
	return nil
}

func (r RPC) SetServerMap(ids [2]int, _ *struct{}) error {
	if ids[0] != -1 {
		serv := r.c.Server(ids[0])
		if serv == nil {
			return ErrUnknownServer
		}
		serv.RLock()
		mID := serv.Map
		serv.RUnlock()
		if mID == ids[1] {
			return nil
		}
		if mID != -1 {
			mp := r.c.Map(mID)
			if mp != nil {
				mp.Lock()
				mp.Server = -1
				mp.Unlock()
			}
		}
		serv.Lock()
		serv.Map = ids[1]
		serv.Unlock()
	}
	if ids[1] != -1 {
		mp := r.c.Map(ids[1])
		if mp == nil {
			return ErrUnknownMap
		}
		mp.RLock()
		sID := mp.Server
		mp.RUnlock()
		if sID != -1 {
			serv := r.c.Server(sID)
			if serv != nil {
				serv.Lock()
				serv.Map = -1
				serv.Unlock()
			}
		}
		mp.Lock()
		mp.Server = ids[0]
		mp.Unlock()
	}
	go r.c.Save()
	return nil
}

func (r RPC) ServerProperties(id int, sp *ServerProperties) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Open(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	*sp = make(ServerProperties)
	return sp.ReadFrom(f)
}

func (r RPC) SetServerProperties(sp data.ServerProperties, _ *struct{}) error {
	s := r.c.Server(sp.ID)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Create(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) MapProperties(id int, mp *ServerProperties) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Open(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	*mp = make(ServerProperties)
	return mp.ReadFrom(f)
}

func (r RPC) SetMapProperties(sp data.ServerProperties, _ *struct{}) error {
	m := r.c.Map(sp.ID)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Create(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) RemoveServer(id int, _ *struct{}) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.Lock()
	defer s.Unlock()
	if s.State != data.StateStopped {
		return ErrServerRunning
	}
	if s.Map >= 0 {
		m := r.c.Map(s.Map)
		m.Lock()
		m.Server = -1
		m.Unlock()
	}
	s.ID = -1
	r.c.RemoveServer(id)
	go r.c.Save()
	return nil
}

func (r RPC) RemoveMap(id int, _ *struct{}) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.Lock()
	defer m.Unlock()
	if m.Server >= 0 {
		s := r.c.Server(m.Server)
		m.Lock()
		defer m.Unlock()
		if s.State != data.StateStopped {
			return ErrServerRunning
		}
		m.Server = -1
	}
	m.ID = -1
	r.c.RemoveMap(id)
	go r.c.Save()
	return nil
}

func (r RPC) CreateDefaultMap(data data.DefaultMap, _ *struct{}) error {
	return r.createMap(data, "")
}

func (r RPC) createMap(data data.DefaultMap, generatorSettings string) error {
	if data.Seed == 0 {
		data.Seed = rand.Int63()
	}
	m := r.c.NewMap()
	if m == nil {
		return errors.New("failed to create map")
	}
	m.Lock()
	defer m.Unlock()
	p, err := minecraft.NewFilePath(m.Path)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l, err := minecraft.NewLevel(p)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l.GameMode(data.GameMode)
	l.LevelName(data.Name)
	l.LevelName(data.Name)
	switch data.Mode {
	case 0:
		l.Generator(minecraft.DefaultGenerator)
	case 1:
		l.Generator(minecraft.FlatGenerator)
	case 2:
		l.Generator(minecraft.LargeBiomeGenerator)
	case 3:
		l.Generator(minecraft.AmplifiedGenerator)
	case 4:
		l.Generator(minecraft.CustomGenerator)
	}
	l.Seed(data.Seed)
	l.AllowCommands(data.Cheats)
	l.MapFeatures(data.Structures)
	if generatorSettings != "" {
		l.GeneratorOptions(generatorSettings)
	}
	l.Save()
	f, err := os.Create(path.Join(m.Path))
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	defer f.Close()
	ms := DefaultMapSettings()
	ms["gamemode"] = strconv.Itoa(int(data.GameMode))
	if !data.Structures {
		ms["generate-structures"] = "false"
	}
	if data.GameMode == 3 {
		ms["hardcore"] = "true"
	}
	if generatorSettings != "" {
		ms["generator-settings"] = generatorSettings
	}
	ms["level-seed"] = strconv.FormatInt(data.Seed, 10)
	ms["motd"] = data.Name
	switch data.Mode {
	case 0:
		ms["level-type"] = minecraft.DefaultGenerator
	case 1:
		ms["level-type"] = minecraft.FlatGenerator
	case 2:
		ms["level-type"] = minecraft.LargeBiomeGenerator
	case 3:
		ms["level-type"] = minecraft.AmplifiedGenerator
	case 4:
		ms["level-type"] = minecraft.CustomGenerator
	case 5:
		ms["level-type"] = minecraft.DebugGenerator
	}
	if err := ms.WriteTo(f); err != nil {
		return err
	}
	go r.c.Save()
	return nil
}

func (r RPC) CreateSuperflatMap(data data.SuperFlatMap, _ *struct{}) error {
	return r.createMap(data.DefaultMap, data.GeneratorSettings)
}

func (r RPC) CreateCustomMap(data data.CustomMap, _ *struct{}) error {
	// check settings for validity
	var buf []byte
	err := json.NewEncoder(memio.Create(&buf)).Encode(data.GeneratorSettings)
	if err != nil {
		return err
	}
	return r.createMap(data.DefaultMap, string(buf))
}

// Errors

var (
	ErrUnknownServer = errors.New("unknown server")
	ErrUnknownMap    = errors.New("unknown map")
	ErrServerRunning = errors.New("server running")
)
<commit_msg>Incorrect constant<commit_after>package main

import (
	"encoding/json"
	"errors"
	"math/rand"
	"os"
	"path"
	"strconv"

	"github.com/MJKWoolnough/memio"
	"github.com/MJKWoolnough/minecraft"
	"github.com/MJKWoolnough/minewebgen/internal/data"
)

type RPC struct {
	c *Config
}

func (r RPC) Settings(_ struct{}, settings *data.ServerSettings) error {
	*settings = r.c.Settings()
	return nil
}

func (r RPC) SetSettings(settings data.ServerSettings, _ *struct{}) error {
	settings.DirMaps = path.Clean(settings.DirMaps)
	settings.DirServers = path.Clean(settings.DirServers)
	if settings.DirMaps == settings.DirServers {
		return errors.New("map and server paths cannot be the same")
	}
	r.c.SetSettings(settings)
	go r.c.Save()
	return nil
}

func (r RPC) ServerName(_ struct{}, serverName *string) error {
	*serverName = r.c.Settings().ServerName
	return nil
}

func (r RPC) ServerList(_ struct{}, list *[]data.Server) error {
	r.c.Servers.mu.RLock()
	defer r.c.Servers.mu.RUnlock()
	*list = make([]data.Server, len(r.c.Servers.List))
	for n, s := range r.c.Servers.List {
		(*list)[n] = *s
	}
	return nil
}

func (r RPC) MapList(_ struct{}, list *[]data.Map) error {
	r.c.Maps.mu.RLock()
	defer r.c.Maps.mu.RUnlock()
	*list = make([]data.Map, len(r.c.Maps.List))
	for n, m := range r.c.Maps.List {
		(*list)[n] = *m
	}
	return nil
}

func (r RPC) Server(id int, s *data.Server) error {
	ser := r.c.Server(id)
	ser.RLock()
	defer ser.RUnlock()
	*s = *ser
	return nil
}

func (r RPC) Map(id int, m *data.Map) error {
	mp := r.c.Map(id)
	mp.RLock()
	defer mp.RUnlock()
	*m = *mp
	return nil
}

func (r RPC) SetServer(s data.Server, _ *struct{}) error {
	ser := r.c.Server(s.ID)
	if ser == nil {
		return ErrUnknownServer
	}
	if ser.State != data.StateStopped {
		return ErrServerRunning
	}
	ser.Lock()
	defer ser.Unlock()
	ser.Name = s.Name
	ser.Args = s.Args
	go r.c.Save()
	return nil
}

func (r RPC) SetMap(m data.Map, _ *struct{}) error {
	mp := r.c.Map(m.ID)
	if mp == nil {
		return ErrUnknownMap
	}
	mp.RLock()
	sID := mp.Server
	mp.RUnlock()
	if mp.Server != -1 {
		ser := r.c.Server(mp.Server)
		if ser != nil {
			ser.RLock()
			s := ser.State
			ser.RUnlock()
			if s != data.StateStopped {
				return ErrServerRunning
			}
		}
	}
	mp.Lock()
	defer mp.Unlock()
	mp.Name = m.Name
	go r.c.Save()
	return nil
}

func (r RPC) SetServerMap(ids [2]int, _ *struct{}) error {
	if ids[0] != -1 {
		serv := r.c.Server(ids[0])
		if serv == nil {
			return ErrUnknownServer
		}
		serv.RLock()
		mID := serv.Map
		serv.RUnlock()
		if mID == ids[1] {
			return nil
		}
		if mID != -1 {
			mp := r.c.Map(mID)
			if mp != nil {
				mp.Lock()
				mp.Server = -1
				mp.Unlock()
			}
		}
		serv.Lock()
		serv.Map = ids[1]
		serv.Unlock()
	}
	if ids[1] != -1 {
		mp := r.c.Map(ids[1])
		if mp == nil {
			return ErrUnknownMap
		}
		mp.RLock()
		sID := mp.Server
		mp.RUnlock()
		if sID != -1 {
			serv := r.c.Server(sID)
			if serv != nil {
				serv.Lock()
				serv.Map = -1
				serv.Unlock()
			}
		}
		mp.Lock()
		mp.Server = ids[0]
		mp.Unlock()
	}
	go r.c.Save()
	return nil
}

func (r RPC) ServerProperties(id int, sp *ServerProperties) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Open(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	*sp = make(ServerProperties)
	return sp.ReadFrom(f)
}

func (r RPC) SetServerProperties(sp data.ServerProperties, _ *struct{}) error {
	s := r.c.Server(sp.ID)
	if s == nil {
		return ErrUnknownServer
	}
	s.RLock()
	p := s.Path
	s.RUnlock()
	f, err := os.Create(path.Join(p, "properties.server"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) MapProperties(id int, mp *ServerProperties) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Open(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	*mp = make(ServerProperties)
	return mp.ReadFrom(f)
}

func (r RPC) SetMapProperties(sp data.ServerProperties, _ *struct{}) error {
	m := r.c.Map(sp.ID)
	if m == nil {
		return ErrUnknownMap
	}
	m.RLock()
	p := m.Path
	m.RUnlock()
	f, err := os.Create(path.Join(p, "properties.map"))
	if err != nil {
		return err
	}
	defer f.Close()
	return ServerProperties(sp.Properties).WriteTo(f)
}

func (r RPC) RemoveServer(id int, _ *struct{}) error {
	s := r.c.Server(id)
	if s == nil {
		return ErrUnknownServer
	}
	s.Lock()
	defer s.Unlock()
	if s.State != data.StateStopped {
		return ErrServerRunning
	}
	if s.Map >= 0 {
		m := r.c.Map(s.Map)
		m.Lock()
		m.Server = -1
		m.Unlock()
	}
	s.ID = -1
	r.c.RemoveServer(id)
	go r.c.Save()
	return nil
}

func (r RPC) RemoveMap(id int, _ *struct{}) error {
	m := r.c.Map(id)
	if m == nil {
		return ErrUnknownMap
	}
	m.Lock()
	defer m.Unlock()
	if m.Server >= 0 {
		s := r.c.Server(m.Server)
		m.Lock()
		defer m.Unlock()
		if s.State != data.StateStopped {
			return ErrServerRunning
		}
		m.Server = -1
	}
	m.ID = -1
	r.c.RemoveMap(id)
	go r.c.Save()
	return nil
}

func (r RPC) CreateDefaultMap(data data.DefaultMap, _ *struct{}) error {
	return r.createMap(data, "")
}

func (r RPC) createMap(data data.DefaultMap, generatorSettings string) error {
	if data.Seed == 0 {
		data.Seed = rand.Int63()
	}
	m := r.c.NewMap()
	if m == nil {
		return errors.New("failed to create map")
	}
	m.Lock()
	defer m.Unlock()
	p, err := minecraft.NewFilePath(m.Path)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l, err := minecraft.NewLevel(p)
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	l.GameMode(data.GameMode)
	l.LevelName(data.Name)
	l.LevelName(data.Name)
	switch data.Mode {
	case 0:
		l.Generator(minecraft.DefaultGenerator)
	case 1:
		l.Generator(minecraft.FlatGenerator)
	case 2:
		l.Generator(minecraft.LargeBiomeGenerator)
	case 3:
		l.Generator(minecraft.AmplifiedGenerator)
	case 4:
		l.Generator(minecraft.CustomGenerator)
	}
	l.Seed(data.Seed)
	l.AllowCommands(data.Cheats)
	l.MapFeatures(data.Structures)
	if generatorSettings != "" {
		l.GeneratorOptions(generatorSettings)
	}
	l.Save()
	f, err := os.Create(path.Join(m.Path))
	if err != nil {
		r.c.RemoveMap(m.ID)
		return err
	}
	defer f.Close()
	ms := DefaultMapSettings()
	ms["gamemode"] = strconv.Itoa(int(data.GameMode))
	if !data.Structures {
		ms["generate-structures"] = "false"
	}
	if data.GameMode == 3 {
		ms["hardcore"] = "true"
	}
	if generatorSettings != "" {
		ms["generator-settings"] = generatorSettings
	}
	ms["level-seed"] = strconv.FormatInt(data.Seed, 10)
	ms["motd"] = data.Name
	switch data.Mode {
	case 0:
		ms["level-type"] = minecraft.DefaultGenerator
	case 1:
		ms["level-type"] = minecraft.FlatGenerator
	case 2:
		ms["level-type"] = minecraft.LargeBiomeGenerator
	case 3:
		ms["level-type"] = minecraft.AmplifiedGenerator
	case 4:
		ms["level-type"] = minecraft.CustomGenerator
	case 5:
		ms["level-type"] = minecraft.DebugGenerator
	}
	if err := ms.WriteTo(f); err != nil {
		return err
	}
	go r.c.Save()
	return nil
}

func (r RPC) CreateSuperflatMap(data data.SuperFlatMap, _ *struct{}) error {
	return r.createMap(data.DefaultMap, data.GeneratorSettings)
}

func (r RPC) CreateCustomMap(data data.CustomMap, _ *struct{}) error {
	// check settings for validity
	var buf []byte
	err := json.NewEncoder(memio.Create(&buf)).Encode(data.GeneratorSettings)
	if err != nil {
		return err
	}
	return r.createMap(data.DefaultMap, string(buf))
}

// Errors

var (
	ErrUnknownServer = errors.New("unknown server")
	ErrUnknownMap    = errors.New("unknown map")
	ErrServerRunning = errors.New("server running")
)
<|endoftext|>


<commit_before>package gcfg

import (
	"fmt"
	"math/big"
	"reflect"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/baobabus/gcfg/types"
)

type metadata struct {
	ident       string
	intMode     string
	constraints constraints
	err         error
}

func getIntTag(tag reflect.StructTag, constraint string, dflt int) (int, error) {
	c := tag.Get(constraint)
	if c == "" {
		return dflt, nil
	}
	r, perr := strconv.ParseInt(c, 10, 0)
	if perr != nil {
		return dflt, fmt.Errorf("invalid %s constraint (%s)", constraint, c)
	}
	return int(r), nil
}

func newMetadata(ts string, tag reflect.StructTag) metadata {
	t := metadata{}
	s := strings.Split(ts, ",")
	t.ident = s[0]
	for _, tse := range s[1:] {
		if strings.HasPrefix(tse, "int=") {
			t.intMode = tse[len("int="):]
		}
	}
	t.constraints.min = tag.Get("min")
	t.constraints.max = tag.Get("max")
	t.constraints.minlen, t.err = getIntTag(tag, "minlen", -1)
	if t.err == nil {
		t.constraints.maxlen, t.err = getIntTag(tag, "maxlen", -1)
	}
	return t
}

func fieldFold(v reflect.Value, name string) (reflect.Value, metadata) {
	var n string
	r0, _ := utf8.DecodeRuneInString(name)
	if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
		n = "X"
	}
	n += strings.Replace(name, "-", "_", -1)
	// Find by tag first
	f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
		if !v.FieldByName(fieldName).CanSet() {
			return false
		}
		f, _ := v.Type().FieldByName(fieldName)
		t := newMetadata(f.Tag.Get("gcfg"), f.Tag)
		if t.ident != "" {
			return strings.EqualFold(t.ident, name)
		}
		return false
	})
	// Only if tag match fails, look up by field name
	if !ok {
		f, ok = v.Type().FieldByNameFunc(func(fieldName string) bool {
			if !v.FieldByName(fieldName).CanSet() {
				return false
			}
			res := strings.EqualFold(n, fieldName)
			if res {
				f, _ := v.Type().FieldByName(fieldName)
				t := newMetadata(f.Tag.Get("gcfg"), f.Tag)
				res = t.ident == ""
			}
			return res
		})
	}
	if !ok {
		return reflect.Value{}, metadata{}
	}
	return v.FieldByName(f.Name), newMetadata(f.Tag.Get("gcfg"), f.Tag)
}

type setter func(destp interface{}, blank bool, val string, t metadata) error

var errUnsupportedType = fmt.Errorf("unsupported type")
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")

var setters = []setter{
	typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
}

func textUnmarshalerSetter(d interface{}, blank bool, val string, t metadata) error {
	dtu, ok := d.(textUnmarshaler)
	if !ok {
		return errUnsupportedType
	}
	if blank {
		return errBlankUnsupported
	}
	if err := dtu.UnmarshalText([]byte(val)); err != nil { return err; }
	return checkConstraints(d, t, unmarshalBoundary)
}

func boolSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
		return nil
	}
	b, err := types.ParseBool(val)
	if err == nil {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
	}
	return err
}

func intMode(mode string) types.IntMode {
	var m types.IntMode
	if strings.ContainsAny(mode, "dD") {
		m |= types.Dec
	}
	if strings.ContainsAny(mode, "hH") {
		m |= types.Hex
	}
	if strings.ContainsAny(mode, "oO") {
		m |= types.Oct
	}
	return m
}

var typeModes = map[reflect.Type]types.IntMode{
	reflect.TypeOf(int(0)):    types.Dec | types.Hex,
	reflect.TypeOf(int8(0)):   types.Dec | types.Hex,
	reflect.TypeOf(int16(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int32(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int64(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint(0)):   types.Dec | types.Hex,
	reflect.TypeOf(uint8(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
	// use default mode (allow dec/hex/oct) for uintptr type
	reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
}

func intModeDefault(t reflect.Type) types.IntMode {
	m, ok := typeModes[t]
	if !ok {
		m = types.Dec | types.Hex | types.Oct
	}
	return m
}

func intSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		return errBlankUnsupported
	}
	mode := intMode(t.intMode)
	if mode == 0 {
		mode = intModeDefault(reflect.TypeOf(d).Elem())
	}
	return types.ParseInt(d, val, mode)
}

func stringSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		return errBlankUnsupported
	}
	dsp, ok := d.(*string)
	if !ok {
		return errUnsupportedType
	}
	*dsp = val
	return nil
}

var kindSetters = map[reflect.Kind]setter{
	reflect.String:  stringSetter,
	reflect.Bool:    boolSetter,
	reflect.Int:     intSetter,
	reflect.Int8:    intSetter,
	reflect.Int16:   intSetter,
	reflect.Int32:   intSetter,
	reflect.Int64:   intSetter,
	reflect.Uint:    intSetter,
	reflect.Uint8:   intSetter,
	reflect.Uint16:  intSetter,
	reflect.Uint32:  intSetter,
	reflect.Uint64:  intSetter,
	reflect.Uintptr: intSetter,
}

var typeSetters = map[reflect.Type]setter{
	reflect.TypeOf(big.Int{}): intSetter,
}

func typeSetter(d interface{}, blank bool, val string, tt metadata) error {
	t := reflect.ValueOf(d).Type().Elem()
	setter, ok := typeSetters[t]
	if !ok {
		return errUnsupportedType
	}
	if err := setter(d, blank, val, tt); err != nil { return err; }
	boundaryGetter := func(d interface{}, val string) (*reflect.Value, error) {
		if val == "" { return nil, nil; }
		v := reflect.New(reflect.ValueOf(d).Elem().Type())
		r := &v
		if err := setter(r.Interface(), false, val, tt); err != nil { return nil, err; }
		return r, nil
	}
	return checkConstraints(d, tt, boundaryGetter)
}

func kindSetter(d interface{}, blank bool, val string, t metadata) error {
	k := reflect.ValueOf(d).Type().Elem().Kind()
	setter, ok := kindSetters[k]
	if !ok {
		return errUnsupportedType
	}
	if err := setter(d, blank, val, t); err != nil { return err; }
	boundaryGetter := func(d interface{}, val string) (*reflect.Value, error) {
		if val == "" { return nil, nil; }
		v := reflect.New(reflect.ValueOf(d).Elem().Type())
		r := &v
		if err := setter(r.Interface(), false, val, t); err != nil { return nil, err; }
		return r, nil
	}
	return checkConstraints(d, t, boundaryGetter)
}

func scanSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		return errBlankUnsupported
	}
	if err := types.ScanFully(d, val, 'v'); err != nil {
		return err
	}
	return checkConstraints(d, t, scanBoundary)
}

func set(cfg interface{}, sect, sub, name string, blank bool, value string) error {
	vPCfg := reflect.ValueOf(cfg)
	if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
		panic(fmt.Errorf("config must be a pointer to a struct"))
	}
	vCfg := vPCfg.Elem()
	vSect, _ := fieldFold(vCfg, sect)
	if !vSect.IsValid() {
		return fmt.Errorf("invalid section: section %q", sect)
	}
	if vSect.Kind() == reflect.Map {
		vst := vSect.Type()
		if vst.Key().Kind() != reflect.String ||
			vst.Elem().Kind() != reflect.Ptr ||
			vst.Elem().Elem().Kind() != reflect.Struct {
			panic(fmt.Errorf("map field for section must have string keys and "+
				" pointer-to-struct values: section %q", sect))
		}
		if vSect.IsNil() {
			vSect.Set(reflect.MakeMap(vst))
		}
		k := reflect.ValueOf(sub)
		pv := vSect.MapIndex(k)
		if !pv.IsValid() {
			vType := vSect.Type().Elem().Elem()
			pv = reflect.New(vType)
			vSect.SetMapIndex(k, pv)
		}
		vSect = pv.Elem()
	} else if vSect.Kind() == reflect.Ptr && (vSect.IsNil() || vSect.Elem().Kind() == reflect.Struct) {
		if vSect.IsNil() {
			vSect.Set(reflect.New(vSect.Type().Elem()))
		}
		vSect = vSect.Elem()
	} else if vSect.Kind() != reflect.Struct {
		panic(fmt.Errorf("field for section must be a map or a struct: "+
			"section %q", sect))
	} else if sub != "" {
		return fmt.Errorf("invalid subsection: "+
			"section %q subsection %q", sect, sub)
	}
	vVar, t := fieldFold(vSect, name)
	if !vVar.IsValid() {
		return fmt.Errorf("invalid variable: "+
			"section %q subsection %q variable %q", sect, sub, name)
	}
	if t.err != nil {
		return fmt.Errorf("%s: "+
			"section %q subsection %q variable %q", t.err, sect, sub, name)
	}
	// vVal is either single-valued var, or newly allocated value within multi-valued var
	var vVal reflect.Value
	// multi-value if unnamed slice type
	isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice
	if isMulti && blank {
		vVar.Set(reflect.Zero(vVar.Type()))
		return nil
	}
	if isMulti {
		vVal = reflect.New(vVar.Type().Elem()).Elem()
	} else {
		vVal = vVar
	}
	isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
	isNew := isDeref && vVal.IsNil()
	// vAddr is address of value to set (dereferenced & allocated as needed)
	var vAddr reflect.Value
	switch {
	case isNew:
		vAddr = reflect.New(vVal.Type().Elem())
	case isDeref && !isNew:
		vAddr = vVal
	default:
		vAddr = vVal.Addr()
	}
	vAddrI := vAddr.Interface()
	err, ok := error(nil), false
	for _, s := range setters {
		err = s(vAddrI, blank, value, t)
		if err == nil {
			ok = true
			break
		}
		if err != errUnsupportedType {
			return err
		}
	}
	if !ok {
		// in case all setters returned errUnsupportedType
		return err
	}
	if isNew { // set reference if it was dereferenced and newly allocated
		vVal.Set(vAddr)
	}
	if isMulti { // append if multi-valued
		vVar.Set(reflect.Append(vVar, vVal))
	}
	return nil
}

type TypeParser func(blank bool, val string) (interface{}, error)

// Registers type parser function.
func RegisterTypeParser(tgtType reflect.Type, typeParser TypeParser) error {
	typeSetters[tgtType] = func(d interface{}, blank bool, val string, t metadata) error {
		v, err := typeParser(blank, val)
		if err == nil {
			sv := reflect.ValueOf(v)
			tv := reflect.ValueOf(d)
			if tv.CanSet() {
				// TODO properly enumerate over Kind
				if sv.Kind() < reflect.Array {
					tv.Set(reflect.ValueOf(v))
				} else {
					tv.Set(reflect.ValueOf(v).Elem())
				}
			} else {
				// TODO properly enumerate over Kind
				if sv.Kind() < reflect.Array {
					tv.Elem().Set(reflect.ValueOf(v))
				} else {
					tv.Elem().Set(reflect.ValueOf(v).Elem())
				}
			}
		}
		return err
	}
	return nil
}
<commit_msg>Support for callbacks and for traversing into optional nested structs.<commit_after>package gcfg

import (
	"fmt"
	"math/big"
	"reflect"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/baobabus/gcfg/types"
)

type metadata struct {
	ident       string
	intMode     string
	callback    string
	constraints constraints
	err         error
}

func getIntTag(tag reflect.StructTag, constraint string, dflt int) (int, error) {
	c := tag.Get(constraint)
	if c == "" {
		return dflt, nil
	}
	r, perr := strconv.ParseInt(c, 10, 0)
	if perr != nil {
		return dflt, fmt.Errorf("invalid %s constraint (%s)", constraint, c)
	}
	return int(r), nil
}

func newMetadata(ts string, tag reflect.StructTag) metadata {
	t := metadata{}
	s := strings.Split(ts, ",")
	t.ident = s[0]
	for _, tse := range s[1:] {
		if strings.HasPrefix(tse, "int=") {
			t.intMode = tse[len("int="):]
		}
		if strings.HasPrefix(tse, "cb=") {
			t.callback = tse[len("cb="):]
		}
	}
	t.constraints.min = tag.Get("min")
	t.constraints.max = tag.Get("max")
	t.constraints.minlen, t.err = getIntTag(tag, "minlen", -1)
	if t.err == nil {
		t.constraints.maxlen, t.err = getIntTag(tag, "maxlen", -1)
	}
	return t
}

func fieldFold(v reflect.Value, name string) (reflect.Value, []int, metadata) {
	var n string
	ixs := []int{}
	r0, _ := utf8.DecodeRuneInString(name)
	if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
		n = "X"
	}
	n += strings.Replace(name, "-", "_", -1)
	// Find by tag first
	f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
		// TODO Implement proper recursive traversal
		// to deal with potential field name collisions
		defer func() {
			recover()
		}()
		if !v.FieldByName(fieldName).CanSet() {
			return false
		}
		f, _ := v.Type().FieldByName(fieldName)
		t := newMetadata(f.Tag.Get("gcfg"), f.Tag)
		if t.ident != "" {
			return strings.EqualFold(t.ident, name)
		}
		return false
	})
	// Only if tag match fails, look up by field name
	if !ok {
		f, ok = v.Type().FieldByNameFunc(func(fieldName string) bool {
			// TODO Implement proper recursive traversal
			// to deal with potential field name collisions
			defer func() {
				recover()
			}()
			if !v.FieldByName(fieldName).CanSet() {
				return false
			}
			res := strings.EqualFold(n, fieldName)
			if res {
				f, _ := v.Type().FieldByName(fieldName)
				t := newMetadata(f.Tag.Get("gcfg"), f.Tag)
				res = t.ident == ""
			}
			return res
		})
	}
	if !ok {
		return reflect.Value{}, ixs, metadata{}
	}
	r := v.FieldByName(f.Name)
	if f, ok := v.Type().FieldByName(f.Name); ok {
		ixs = f.Index
	}
	return r, ixs, newMetadata(f.Tag.Get("gcfg"), f.Tag)
}

type setter func(destp interface{}, blank bool, val string, t metadata) error

var errUnsupportedType = fmt.Errorf("unsupported type")
var errBlankUnsupported = fmt.Errorf("blank value not supported for type")

var setters = []setter{
	typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
}

func textUnmarshalerSetter(d interface{}, blank bool, val string, t metadata) error {
	dtu, ok := d.(textUnmarshaler)
	if !ok {
		return errUnsupportedType
	}
	if blank {
		return errBlankUnsupported
	}
	if err := dtu.UnmarshalText([]byte(val)); err != nil { return err; }
	return checkConstraints(d, t, unmarshalBoundary)
}

func boolSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
		return nil
	}
	b, err := types.ParseBool(val)
	if err == nil {
		reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
	}
	return err
}

func intMode(mode string) types.IntMode {
	var m types.IntMode
	if strings.ContainsAny(mode, "dD") {
		m |= types.Dec
	}
	if strings.ContainsAny(mode, "hH") {
		m |= types.Hex
	}
	if strings.ContainsAny(mode, "oO") {
		m |= types.Oct
	}
	return m
}

var typeModes = map[reflect.Type]types.IntMode{
	reflect.TypeOf(int(0)):    types.Dec | types.Hex,
	reflect.TypeOf(int8(0)):   types.Dec | types.Hex,
	reflect.TypeOf(int16(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int32(0)):  types.Dec | types.Hex,
	reflect.TypeOf(int64(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint(0)):   types.Dec | types.Hex,
	reflect.TypeOf(uint8(0)):  types.Dec | types.Hex,
	reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
	reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
	// use default mode (allow dec/hex/oct) for uintptr type
	reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
}

func intModeDefault(t reflect.Type) types.IntMode {
	m, ok := typeModes[t]
	if !ok {
		m = types.Dec | types.Hex | types.Oct
	}
	return m
}

func intSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		return errBlankUnsupported
	}
	mode := intMode(t.intMode)
	if mode == 0 {
		mode = intModeDefault(reflect.TypeOf(d).Elem())
	}
	return types.ParseInt(d, val, mode)
}

func stringSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		return errBlankUnsupported
	}
	dsp, ok := d.(*string)
	if !ok {
		return errUnsupportedType
	}
	*dsp = val
	return nil
}

var kindSetters = map[reflect.Kind]setter{
	reflect.String:  stringSetter,
	reflect.Bool:    boolSetter,
	reflect.Int:     intSetter,
	reflect.Int8:    intSetter,
	reflect.Int16:   intSetter,
	reflect.Int32:   intSetter,
	reflect.Int64:   intSetter,
	reflect.Uint:    intSetter,
	reflect.Uint8:   intSetter,
	reflect.Uint16:  intSetter,
	reflect.Uint32:  intSetter,
	reflect.Uint64:  intSetter,
	reflect.Uintptr: intSetter,
}

var typeSetters = map[reflect.Type]setter{
	reflect.TypeOf(big.Int{}): intSetter,
}

func typeSetter(d interface{}, blank bool, val string, tt metadata) error {
	t := reflect.ValueOf(d).Type().Elem()
	setter, ok := typeSetters[t]
	if !ok {
		return errUnsupportedType
	}
	if err := setter(d, blank, val, tt); err != nil { return err; }
	boundaryGetter := func(d interface{}, val string) (*reflect.Value, error) {
		if val == "" { return nil, nil; }
		v := reflect.New(reflect.ValueOf(d).Elem().Type())
		r := &v
		if err := setter(r.Interface(), false, val, tt); err != nil { return nil, err; }
		return r, nil
	}
	return checkConstraints(d, tt, boundaryGetter)
}

func kindSetter(d interface{}, blank bool, val string, t metadata) error {
	k := reflect.ValueOf(d).Type().Elem().Kind()
	setter, ok := kindSetters[k]
	if !ok {
		return errUnsupportedType
	}
	if err := setter(d, blank, val, t); err != nil { return err; }
	boundaryGetter := func(d interface{}, val string) (*reflect.Value, error) {
		if val == "" { return nil, nil; }
		v := reflect.New(reflect.ValueOf(d).Elem().Type())
		r := &v
		if err := setter(r.Interface(), false, val, t); err != nil { return nil, err; }
		return r, nil
	}
	return checkConstraints(d, t, boundaryGetter)
}

func scanSetter(d interface{}, blank bool, val string, t metadata) error {
	if blank {
		return errBlankUnsupported
	}
	if err := types.ScanFully(d, val, 'v'); err != nil {
		return err
	}
	return checkConstraints(d, t, scanBoundary)
}

func set(cfg interface{}, sect, sub, name string, blank bool, value string) error {
	vPCfg := reflect.ValueOf(cfg)
	if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
		panic(fmt.Errorf("config must be a pointer to a struct"))
	}
	vCfg := vPCfg.Elem()
	vSect, _, _ := fieldFold(vCfg, sect)
	if !vSect.IsValid() {
		return fmt.Errorf("invalid section: section %q", sect)
	}
	if vSect.Kind() == reflect.Map {
		vst := vSect.Type()
		if vst.Key().Kind() != reflect.String ||
			vst.Elem().Kind() != reflect.Ptr ||
			vst.Elem().Elem().Kind() != reflect.Struct {
			panic(fmt.Errorf("map field for section must have string keys and "+
				" pointer-to-struct values: section %q", sect))
		}
		if vSect.IsNil() {
			vSect.Set(reflect.MakeMap(vst))
		}
		k := reflect.ValueOf(sub)
		pv := vSect.MapIndex(k)
		if !pv.IsValid() {
			vType := vSect.Type().Elem().Elem()
			pv = reflect.New(vType)
			vSect.SetMapIndex(k, pv)
		}
		vSect = pv.Elem()
	} else if vSect.Kind() == reflect.Ptr && (vSect.IsNil() || vSect.Elem().Kind() == reflect.Struct) {
		if vSect.IsNil() {
			vSect.Set(reflect.New(vSect.Type().Elem()))
		}
		vSect = vSect.Elem()
	} else if vSect.Kind() != reflect.Struct {
		panic(fmt.Errorf("field for section must be a map or a struct: "+
			"section %q", sect))
	} else if sub != "" {
		return fmt.Errorf("invalid subsection: "+
			"section %q subsection %q", sect, sub)
	}
	vVar, ixs, t := fieldFold(vSect, name)
	if !vVar.IsValid() {
		return fmt.Errorf("invalid variable: "+
			"section %q subsection %q variable %q", sect, sub, name)
	}
	if t.err != nil {
		return fmt.Errorf("%s: "+
			"section %q subsection %q variable %q", t.err, sect, sub, name)
	}
	// vVal is either single-valued var, or newly allocated value within multi-valued var
	var vVal reflect.Value
	// multi-value if unnamed slice type
	isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice
	if isMulti && blank {
		vVar.Set(reflect.Zero(vVar.Type()))
		return nil
	}
	if isMulti {
		vVal = reflect.New(vVar.Type().Elem()).Elem()
	} else {
		vVal = vVar
	}
	isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
	isNew := isDeref && vVal.IsNil()
	// vAddr is address of value to set (dereferenced & allocated as needed)
	var vAddr reflect.Value
	switch {
	case isNew:
		vAddr = reflect.New(vVal.Type().Elem())
	case isDeref && !isNew:
		vAddr = vVal
	default:
		vAddr = vVal.Addr()
	}
	vAddrI := vAddr.Interface()
	err, ok := error(nil), false
	for _, s := range setters {
		err = s(vAddrI, blank, value, t)
		if err == nil {
			ok = true
			break
		}
		if err != errUnsupportedType {
			return err
		}
	}
	if !ok {
		// in case all setters returned errUnsupportedType
		return err
	}
	if isNew { // set reference if it was dereferenced and newly allocated
		vVal.Set(vAddr)
	}
	if isMulti { // append if multi-valued
		vVar.Set(reflect.Append(vVar, vVal))
	}
	if len(t.callback) > 0 {
		vs := vSect
		if len(ixs) > 0 {
			vs = vSect.FieldByIndex(ixs[1:])
		}
		vs = vs.Addr()
		if m := vs.MethodByName(t.callback); m.IsValid() {
			m.Call([]reflect.Value{})
		}
	}
	return nil
}

type TypeParser func(blank bool, val string) (interface{}, error)

// Registers type parser function.
func RegisterTypeParser(tgtType reflect.Type, typeParser TypeParser) error {
	typeSetters[tgtType] = func(d interface{}, blank bool, val string, t metadata) error {
		v, err := typeParser(blank, val)
		if err == nil {
			sv := reflect.ValueOf(v)
			tv := reflect.ValueOf(d)
			if tv.CanSet() {
				// TODO properly enumerate over Kind
				if sv.Kind() < reflect.Array {
					tv.Set(reflect.ValueOf(v))
				} else {
					tv.Set(reflect.ValueOf(v).Elem())
				}
			} else {
				// TODO properly enumerate over Kind
				if sv.Kind() < reflect.Array {
					tv.Elem().Set(reflect.ValueOf(v))
				} else {
					tv.Elem().Set(reflect.ValueOf(v).Elem())
				}
			}
		}
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package riakpbc

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type Pool struct {
	nodes   map[string]*Node // index the node with its address string
	current *Node
	sync.Mutex
}

// NewPool returns an instantiated pool given a slice of node addresses.
func NewPool(cluster []string) *Pool {
	rand.Seed(time.Now().UTC().UnixNano())
	nodeMap := make(map[string]*Node, len(cluster))

	for _, node := range cluster {
		newNode, err := NewNode(node, 10e8, 10e8)
		if err == nil {
			nodeMap[node] = newNode
		}
	}

	pool := &Pool{
		nodes: nodeMap,
	}

	return pool
}

// SelectNode returns a node from the pool using weighted error selection.
//
// Each node has an assignable error rate, which is incremented when an error
// occurs, and decays over time - 50% each 10 seconds by default.
func (pool *Pool) SelectNode() *Node {
	pool.Lock()
	errorThreshold := 0.1
	var possibleNodes []*Node

	for _, node := range pool.nodes {
		nodeErrorValue := node.ErrorRate()

		if nodeErrorValue < errorThreshold {
			possibleNodes = append(possibleNodes, node)
		} else {
			if node.ok == false && node.ErrorRate() < 100.0 {
				go func(iNode *Node) {
					nodeGood := iNode.Ping()
					if nodeGood == false {
						iNode.Lock()
						iNode.Close()
						iNode.Dial()
						iNode.Unlock()
					} else {
						iNode.ok = true
					}
				}(node)
			}
		}
	}

	numPossibleNodes := len(possibleNodes)

	var chosenNode *Node
	if numPossibleNodes > 0 {
		chosenNode = possibleNodes[rand.Int31n(int32(numPossibleNodes))]
	} else {
		chosenNode = pool.RandomNode()
	}

	pool.current = chosenNode
	pool.Unlock()

	return chosenNode
}

func (pool *Pool) RandomNode() *Node {
	var randomNode *Node

	var randVal float32
	randVal = 0

	for _, node := range pool.nodes {
		throwAwayRand := rand.Float32()

		if throwAwayRand > randVal {
			randomNode = node
			randVal = throwAwayRand
		}
	}

	return randomNode
}

func (pool *Pool) DeleteNode(nodeKey string) {
	delete(pool.nodes, nodeKey)
}

func (pool *Pool) Close() {
	for _, node := range pool.nodes {
		node.Close()
	}
}

func (pool *Pool) Current() *Node {
	node := pool.current
	return node
}

func (pool *Pool) Size() int {
	return len(pool.nodes)
}

func (pool *Pool) String() string {
	var outString string
	for _, node := range pool.nodes {
		nodeString := fmt.Sprintf(" [%s %f <%t>] ", node.addr, node.ErrorRate(), node.ok)
		outString += nodeString
	}
	return outString
}
<commit_msg>One more little safeguard<commit_after>package riakpbc

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type Pool struct {
	nodes   map[string]*Node // index the node with its address string
	current *Node
	sync.Mutex
}

// NewPool returns an instantiated pool given a slice of node addresses.
func NewPool(cluster []string) *Pool {
	rand.Seed(time.Now().UTC().UnixNano())
	nodeMap := make(map[string]*Node, len(cluster))

	for _, node := range cluster {
		newNode, err := NewNode(node, 10e8, 10e8)
		if err == nil {
			nodeMap[node] = newNode
		}
	}

	pool := &Pool{
		nodes: nodeMap,
	}

	return pool
}

// SelectNode returns a node from the pool using weighted error selection.
//
// Each node has an assignable error rate, which is incremented when an error
// occurs, and decays over time - 50% each 10 seconds by default.
func (pool *Pool) SelectNode() *Node {
	pool.Lock()
	errorThreshold := 0.1
	var possibleNodes []*Node

	for _, node := range pool.nodes {
		nodeErrorValue := node.ErrorRate()

		if nodeErrorValue < errorThreshold {
			possibleNodes = append(possibleNodes, node)
		} else {
			if node.ok == false && node.ErrorRate() < 100.0 {
				go func(iNode *Node) {
					nodeGood := iNode.Ping()
					if nodeGood == false {
            iNode.RecordError(100.0)
						iNode.Lock()
						iNode.Close()
						iNode.Dial()
						iNode.Unlock()
					} else {
						iNode.ok = true
					}
				}(node)
			}
		}
	}

	numPossibleNodes := len(possibleNodes)

	var chosenNode *Node
	if numPossibleNodes > 0 {
		chosenNode = possibleNodes[rand.Int31n(int32(numPossibleNodes))]
	} else {
		chosenNode = pool.RandomNode()
	}

	pool.current = chosenNode
	pool.Unlock()

	return chosenNode
}

func (pool *Pool) RandomNode() *Node {
	var randomNode *Node

	var randVal float32
	randVal = 0

	for _, node := range pool.nodes {
		throwAwayRand := rand.Float32()

		if throwAwayRand > randVal {
			randomNode = node
			randVal = throwAwayRand
		}
	}

	return randomNode
}

func (pool *Pool) DeleteNode(nodeKey string) {
	delete(pool.nodes, nodeKey)
}

func (pool *Pool) Close() {
	for _, node := range pool.nodes {
		node.Close()
	}
}

func (pool *Pool) Current() *Node {
	node := pool.current
	return node
}

func (pool *Pool) Size() int {
	return len(pool.nodes)
}

func (pool *Pool) String() string {
	var outString string
	for _, node := range pool.nodes {
		nodeString := fmt.Sprintf(" [%s %f <%t>] ", node.addr, node.ErrorRate(), node.ok)
		outString += nodeString
	}
	return outString
}
<|endoftext|>


<commit_before>/* Copyright (c) 2013, Stefan Talpalaru <stefan.talpalaru@od-eon.com>, Odeon Consulting Group Pte Ltd <od-eon.com>
 * All rights reserved. */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Package pool provides a worker pool.
package pool

import (
	"fmt"
	"log"
	"sync"
	"time"
)

// Job holds all the data related to a worker's instance.
type Job struct {
	F      func(...interface{}) interface{}
	Args   []interface{}
	Result interface{}
	Err    error
}

// stats is a structure holding statistical data about the pool.
type stats struct {
	Submitted int
	Running   int
	Completed int
}

// Pool is the main data structure.
type Pool struct {
	workers_started              bool
	supervisor_started              bool
	num_workers          int
	job_pipe             chan *Job
	done_pipe            chan *Job
	add_pipe             chan *Job
	result_pipe          chan *Job
	jobs_ready_to_run    []*Job
	num_jobs_submitted   int
	num_jobs_running     int
	num_jobs_completed   int
	jobs_completed       []*Job
	interval             time.Duration // for sleeping, in ms
	working_pipe         chan bool
	stats_pipe           chan stats
	worker_kill_pipe     chan bool
	supervisor_kill_pipe chan bool
	worker_wg            sync.WaitGroup
	supervisor_wg        sync.WaitGroup
}

// subworker catches any panic while running the job.
func (pool *Pool) subworker(job *Job) {
	defer func() {
		if err := recover(); err != nil {
			log.Println("panic while running job:", err)
			job.Result = nil
			job.Err = fmt.Errorf(err.(string))
		}
	}()
	job.Result = job.F(job.Args...)
}

// worker gets a job from the job_pipe, passes it to a
// subworker and puts the job in the done_pipe when finished.
func (pool *Pool) worker(num int) {
WORKER_LOOP:
	for {
		select {
		case <-pool.worker_kill_pipe:
			// worker suicide
			break WORKER_LOOP
		case job := <-pool.job_pipe:
			pool.subworker(job)
			pool.done_pipe <- job
		}
	}
	pool.worker_wg.Done()
}

// NewPool creates a new Pool.
func NewPool(workers int) (pool *Pool) {
	pool = new(Pool)
	pool.num_workers = workers
	pool.job_pipe = make(chan *Job)
	pool.done_pipe = make(chan *Job)
	pool.add_pipe = make(chan *Job)
	pool.result_pipe = make(chan *Job)
	pool.jobs_ready_to_run = make([]*Job, 0)
	pool.jobs_completed = make([]*Job, 0)
	pool.working_pipe = make(chan bool)
	pool.stats_pipe = make(chan stats)
	pool.worker_kill_pipe = make(chan bool)
	pool.supervisor_kill_pipe = make(chan bool)
	pool.interval = 1
	// start the supervisor here so we can accept jobs before a Run call
	pool.startSupervisor()
	return
}

// supervisor feeds jobs to workers and keeps track of them.
func (pool *Pool) supervisor() {
SUPERVISOR_LOOP:
	for {
		select {
		case job := <-pool.add_pipe:
			pool.jobs_ready_to_run = append(pool.jobs_ready_to_run, job)
			pool.num_jobs_submitted++
		default:
		}

		num_ready_jobs := len(pool.jobs_ready_to_run)
		if num_ready_jobs > 0 {
			select {
			case pool.job_pipe <- pool.jobs_ready_to_run[num_ready_jobs-1]:
				pool.num_jobs_running++
				pool.jobs_ready_to_run = pool.jobs_ready_to_run[:num_ready_jobs-1]
			default:
			}
		}

		if pool.num_jobs_running > 0 {
			select {
			case job := <-pool.done_pipe:
				pool.num_jobs_running--
				pool.jobs_completed = append(pool.jobs_completed, job)
				pool.num_jobs_completed++
			default:
			}
		}

		working := true
		if len(pool.jobs_ready_to_run) == 0 && pool.num_jobs_running == 0 {
			working = false
		}
		select {
		case pool.working_pipe <- working:
		default:
		}

		res := (*Job)(nil)
		if len(pool.jobs_completed) > 0 {
			res = pool.jobs_completed[0]
		}
		select {
		case pool.result_pipe <- res:
			if len(pool.jobs_completed) > 0 {
				pool.jobs_completed = pool.jobs_completed[1:]
			}
		default:
		}

		pool_stats := stats{pool.num_jobs_submitted, pool.num_jobs_running, pool.num_jobs_completed}
		select {
		case pool.stats_pipe <- pool_stats:
		default:
		}

		// stopping
		select {
		case <-pool.supervisor_kill_pipe:
			break SUPERVISOR_LOOP
		default:
		}

		time.Sleep(pool.interval * time.Millisecond)
	}
	pool.supervisor_wg.Done()
}

// Run starts the Pool by launching the workers.
// It's OK to start an empty Pool. The jobs will be fed to the workers as soon
// as they become available.
func (pool *Pool) Run() {
	if pool.workers_started {
		panic("trying to start a pool that's already running")
	}
	for i := 0; i < pool.num_workers; i++ {
		pool.worker_wg.Add(1)
		go pool.worker(i)
	}
	pool.workers_started = true
	// handle the supervisor
	if !pool.supervisor_started {
		pool.startSupervisor()
	}
}

// Stop will signal the workers to exit and wait for them to actually do that.
// It also releases any other resources (e.g.: it stops the supervisor goroutine)
// so call this method when you're done with the Pool instance to allow the GC
// to do its job.
func (pool *Pool) Stop() {
	if !pool.workers_started {
		panic("trying to stop a pool that's already stopped")
	}
	// stop the workers
	for i := 0; i < pool.num_workers; i++ {
		pool.worker_kill_pipe <- true
	}
	pool.worker_wg.Wait()
	// set the flag
	pool.workers_started = false
	// handle the supervisor
	if pool.supervisor_started {
		pool.stopSupervisor()
	}
}

func (pool *Pool) startSupervisor() {
	pool.supervisor_wg.Add(1)
	go pool.supervisor()
	pool.supervisor_started = true
}

func (pool *Pool) stopSupervisor() {
	pool.supervisor_kill_pipe <- true
	pool.supervisor_wg.Wait()
	pool.supervisor_started = false
}

// Add creates a Job from the given function and args and
// adds it to the Pool.
func (pool *Pool) Add(f func(...interface{}) interface{}, args ...interface{}) {
	pool.add_pipe <- &Job{f, args, nil, nil}
}

// Wait blocks until all the jobs in the Pool are done.
func (pool *Pool) Wait() {
	for <-pool.working_pipe {
		time.Sleep(pool.interval * time.Millisecond)
	}
}

// Results retrieves the completed jobs.
func (pool *Pool) Results() (res []*Job) {
	res = make([]*Job, len(pool.jobs_completed))
	for i, job := range pool.jobs_completed {
		res[i] = job
	}
	pool.jobs_completed = pool.jobs_completed[0:0]
	return
}

// WaitForJob blocks until a completed job is available and returns it.
// If there are no jobs running, it returns nil.
func (pool *Pool) WaitForJob() *Job {
	for {
		working := <-pool.working_pipe
		r := <-pool.result_pipe
		if r == (*Job)(nil) {
			if !working {
				break
			}
		} else {
			return r
		}
	}
	return nil
}

// Status returns a "stats" instance.
func (pool *Pool) Status() stats {
	if pool.supervisor_started {
		return <-pool.stats_pipe
	}
	// the supervisor wasn't started so we return a zeroed structure
	return stats{}
}
<commit_msg>Add now waits until the supervisor added the job to the pool<commit_after>/* Copyright (c) 2013, Stefan Talpalaru <stefan.talpalaru@od-eon.com>, Odeon Consulting Group Pte Ltd <od-eon.com>
 * All rights reserved. */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Package pool provides a worker pool.
package pool

import (
	"fmt"
	"log"
	"sync"
	"time"
)

// Job holds all the data related to a worker's instance.
type Job struct {
	F      func(...interface{}) interface{}
	Args   []interface{}
	Result interface{}
	Err    error
	added chan bool // used by Pool.Add to wait for the supervisor
}

// stats is a structure holding statistical data about the pool.
type stats struct {
	Submitted int
	Running   int
	Completed int
}

// Pool is the main data structure.
type Pool struct {
	workers_started              bool
	supervisor_started              bool
	num_workers          int
	job_pipe             chan *Job
	done_pipe            chan *Job
	add_pipe             chan *Job
	result_pipe          chan *Job
	jobs_ready_to_run    []*Job
	num_jobs_submitted   int
	num_jobs_running     int
	num_jobs_completed   int
	jobs_completed       []*Job
	interval             time.Duration // for sleeping, in ms
	working_pipe         chan bool
	stats_pipe           chan stats
	worker_kill_pipe     chan bool
	supervisor_kill_pipe chan bool
	worker_wg            sync.WaitGroup
	supervisor_wg        sync.WaitGroup
}

// subworker catches any panic while running the job.
func (pool *Pool) subworker(job *Job) {
	defer func() {
		if err := recover(); err != nil {
			log.Println("panic while running job:", err)
			job.Result = nil
			job.Err = fmt.Errorf(err.(string))
		}
	}()
	job.Result = job.F(job.Args...)
}

// worker gets a job from the job_pipe, passes it to a
// subworker and puts the job in the done_pipe when finished.
func (pool *Pool) worker(num int) {
WORKER_LOOP:
	for {
		select {
		case <-pool.worker_kill_pipe:
			// worker suicide
			break WORKER_LOOP
		case job := <-pool.job_pipe:
			pool.subworker(job)
			pool.done_pipe <- job
		}
	}
	pool.worker_wg.Done()
}

// NewPool creates a new Pool.
func NewPool(workers int) (pool *Pool) {
	pool = new(Pool)
	pool.num_workers = workers
	pool.job_pipe = make(chan *Job)
	pool.done_pipe = make(chan *Job)
	pool.add_pipe = make(chan *Job)
	pool.result_pipe = make(chan *Job)
	pool.jobs_ready_to_run = make([]*Job, 0)
	pool.jobs_completed = make([]*Job, 0)
	pool.working_pipe = make(chan bool)
	pool.stats_pipe = make(chan stats)
	pool.worker_kill_pipe = make(chan bool)
	pool.supervisor_kill_pipe = make(chan bool)
	pool.interval = 1
	// start the supervisor here so we can accept jobs before a Run call
	pool.startSupervisor()
	return
}

// supervisor feeds jobs to workers and keeps track of them.
func (pool *Pool) supervisor() {
SUPERVISOR_LOOP:
	for {
		select {
		case job := <-pool.add_pipe:
			pool.jobs_ready_to_run = append(pool.jobs_ready_to_run, job)
			pool.num_jobs_submitted++
			job.added <- true
		default:
		}

		num_ready_jobs := len(pool.jobs_ready_to_run)
		if num_ready_jobs > 0 {
			select {
			case pool.job_pipe <- pool.jobs_ready_to_run[num_ready_jobs-1]:
				pool.num_jobs_running++
				pool.jobs_ready_to_run = pool.jobs_ready_to_run[:num_ready_jobs-1]
			default:
			}
		}

		if pool.num_jobs_running > 0 {
			select {
			case job := <-pool.done_pipe:
				pool.num_jobs_running--
				pool.jobs_completed = append(pool.jobs_completed, job)
				pool.num_jobs_completed++
			default:
			}
		}

		working := true
		if len(pool.jobs_ready_to_run) == 0 && pool.num_jobs_running == 0 {
			working = false
		}
		select {
		case pool.working_pipe <- working:
		default:
		}

		res := (*Job)(nil)
		if len(pool.jobs_completed) > 0 {
			res = pool.jobs_completed[0]
		}
		select {
		case pool.result_pipe <- res:
			if len(pool.jobs_completed) > 0 {
				pool.jobs_completed = pool.jobs_completed[1:]
			}
		default:
		}

		pool_stats := stats{pool.num_jobs_submitted, pool.num_jobs_running, pool.num_jobs_completed}
		select {
		case pool.stats_pipe <- pool_stats:
		default:
		}

		// stopping
		select {
		case <-pool.supervisor_kill_pipe:
			break SUPERVISOR_LOOP
		default:
		}

		time.Sleep(pool.interval * time.Millisecond)
	}
	pool.supervisor_wg.Done()
}

// Run starts the Pool by launching the workers.
// It's OK to start an empty Pool. The jobs will be fed to the workers as soon
// as they become available.
func (pool *Pool) Run() {
	if pool.workers_started {
		panic("trying to start a pool that's already running")
	}
	for i := 0; i < pool.num_workers; i++ {
		pool.worker_wg.Add(1)
		go pool.worker(i)
	}
	pool.workers_started = true
	// handle the supervisor
	if !pool.supervisor_started {
		pool.startSupervisor()
	}
}

// Stop will signal the workers to exit and wait for them to actually do that.
// It also releases any other resources (e.g.: it stops the supervisor goroutine)
// so call this method when you're done with the Pool instance to allow the GC
// to do its job.
func (pool *Pool) Stop() {
	if !pool.workers_started {
		panic("trying to stop a pool that's already stopped")
	}
	// stop the workers
	for i := 0; i < pool.num_workers; i++ {
		pool.worker_kill_pipe <- true
	}
	pool.worker_wg.Wait()
	// set the flag
	pool.workers_started = false
	// handle the supervisor
	if pool.supervisor_started {
		pool.stopSupervisor()
	}
}

func (pool *Pool) startSupervisor() {
	pool.supervisor_wg.Add(1)
	go pool.supervisor()
	pool.supervisor_started = true
}

func (pool *Pool) stopSupervisor() {
	pool.supervisor_kill_pipe <- true
	pool.supervisor_wg.Wait()
	pool.supervisor_started = false
}

// Add creates a Job from the given function and args and
// adds it to the Pool.
func (pool *Pool) Add(f func(...interface{}) interface{}, args ...interface{}) {
	job := &Job{f, args, nil, nil, make(chan bool)}
	pool.add_pipe <-job
	<-job.added
}

// Wait blocks until all the jobs in the Pool are done.
func (pool *Pool) Wait() {
	for <-pool.working_pipe {
		time.Sleep(pool.interval * time.Millisecond)
	}
}

// Results retrieves the completed jobs.
func (pool *Pool) Results() (res []*Job) {
	res = make([]*Job, len(pool.jobs_completed))
	for i, job := range pool.jobs_completed {
		res[i] = job
	}
	pool.jobs_completed = pool.jobs_completed[0:0]
	return
}

// WaitForJob blocks until a completed job is available and returns it.
// If there are no jobs running, it returns nil.
func (pool *Pool) WaitForJob() *Job {
	for {
		working := <-pool.working_pipe
		r := <-pool.result_pipe
		if r == (*Job)(nil) {
			if !working {
				break
			}
		} else {
			return r
		}
	}
	return nil
}

// Status returns a "stats" instance.
func (pool *Pool) Status() stats {
	if pool.supervisor_started {
		return <-pool.stats_pipe
	}
	// the supervisor wasn't started so we return a zeroed structure
	return stats{}
}
<|endoftext|>


<commit_before>package modd

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/cortesi/modd/conf"
	"github.com/cortesi/modd/varcmd"
	"github.com/cortesi/modd/watch"
	"github.com/cortesi/termlog"
)

const moddVar = "@mods"

// MinRestart is the minimum amount of time between daemon restarts
const MinRestart = 1 * time.Second

const lineLimit = 80

// shortCommand shortens a command to a name we can use in a notification
// header.
func shortCommand(command string) string {
	ret := command
	parts := strings.Split(command, "\n")
	for _, i := range parts {
		i = strings.TrimLeft(i, " \t#")
		i = strings.TrimRight(i, " \t\\")
		if i != "" {
			ret = i
			break
		}
	}
	return ret
}

// niceHeader tries to produce a nicer process name. We condense whitespace to
// make commands split over multiple lines with indentation more legible, and
// limit the line length to 80 characters.
func niceHeader(preamble string, command string) string {
	pre := termlog.DefaultPalette.Timestamp.SprintFunc()(preamble)
	command = termlog.DefaultPalette.Header.SprintFunc()(shortCommand(command))
	return pre + command
}

func getShell() string {
	return "bash"
}

func logOutput(wg *sync.WaitGroup, fp io.ReadCloser, out func(string, ...interface{})) {
	defer wg.Done()
	r := bufio.NewReader(fp)
	for {
		line, _, err := r.ReadLine()
		if err != nil {
			return
		}
		out(string(line))
	}
}

// ProcError is a process error, possibly containing command output
type ProcError struct {
	shorttext string
	Output    string
}

func (p ProcError) Error() string {
	return p.shorttext
}

// RunProc runs a process to completion, sending output to log
func RunProc(cmd string, log termlog.Stream) error {
	log.Header()
	sh := getShell()
	c := exec.Command(sh, "-c", cmd)
	stdo, err := c.StdoutPipe()
	if err != nil {
		return err
	}
	stde, err := c.StderrPipe()
	if err != nil {
		return err
	}
	wg := sync.WaitGroup{}
	wg.Add(2)
	buff := new(bytes.Buffer)
	mut := sync.Mutex{}
	go logOutput(
		&wg, stde,
		func(s string, args ...interface{}) {
			log.Warn(s)

			mut.Lock()
			defer mut.Unlock()
			buff.WriteString(s + "\n")
		},
	)
	go logOutput(&wg, stdo, log.Say)
	err = c.Start()
	if err != nil {
		return err
	}
	err = c.Wait()
	wg.Wait()
	if err != nil {
		log.Shout("%s", c.ProcessState.String())
		return ProcError{err.Error(), buff.String()}
	}
	log.Notice(">> done (%s)", c.ProcessState.UserTime())
	return nil
}

// RunPreps runs all commands in sequence. Stops if any command returns an error.
func RunPreps(b conf.Block, vars map[string]string, mod *watch.Mod, log termlog.TermLog) error {
	vcmd := varcmd.VarCmd{Block: &b, Mod: mod, Vars: vars}
	for _, p := range b.Preps {
		cmd, err := vcmd.Render(p.Command)
		if err != nil {
			return err
		}
		err = RunProc(cmd, log.Stream(niceHeader("prep: ", p.Command)))
		if err != nil {
			return err
		}
	}
	return nil
}

type daemon struct {
	conf conf.Daemon
	log  termlog.Stream
	cmd  *exec.Cmd
	vars map[string]string
	stop bool
}

func (d *daemon) Run() {
	var lastStart time.Time
	for d.stop != true {
		d.log.Notice(">> starting...")
		since := time.Now().Sub(lastStart)
		if since < MinRestart {
			time.Sleep(MinRestart - since)
		}
		lastStart = time.Now()
		sh := getShell()

		vcmd := varcmd.VarCmd{Block: nil, Mod: nil, Vars: d.vars}
		finalcmd, err := vcmd.Render(d.conf.Command)
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}

		c := exec.Command(sh, "-c", finalcmd)
		stdo, err := c.StdoutPipe()
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}
		stde, err := c.StderrPipe()
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}
		wg := sync.WaitGroup{}
		wg.Add(2)
		go logOutput(&wg, stde, d.log.Warn)
		go logOutput(&wg, stdo, d.log.Say)
		err = c.Start()
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}
		d.cmd = c
		err = c.Wait()
		wg.Wait()
		if err != nil {
			d.log.Shout("%s", c.ProcessState.String())
			continue
		}
	}
}

func (d *daemon) Restart() {
	if d.cmd != nil {
		d.log.Notice(">> sending signal %s", d.conf.RestartSignal)
		d.cmd.Process.Signal(d.conf.RestartSignal)
	}
}

func (d *daemon) Shutdown(sig os.Signal) {
	d.stop = true
	if d.cmd != nil {
		d.cmd.Process.Signal(sig)
		d.cmd.Wait()
	}
}

// DaemonPen is a group of daemons, managed as a unit.
type DaemonPen struct {
	daemons *[]daemon
	sync.Mutex
}

var ws = regexp.MustCompile(`\s\s+`)

// Start starts set of daemons, each specified by a command
func (dp *DaemonPen) Start(daemons []conf.Daemon, vars map[string]string, log termlog.TermLog) {
	dp.Lock()
	defer dp.Unlock()
	d := make([]daemon, len(daemons))
	for i, dmn := range daemons {
		d[i] = daemon{
			conf: dmn,
			vars: vars,
			log: log.Stream(
				niceHeader("daemon: ", dmn.Command),
			),
		}
		go d[i].Run()
	}
	dp.daemons = &d
}

// Restart all daemons in the pen
func (dp *DaemonPen) Restart() {
	dp.Lock()
	defer dp.Unlock()
	if dp.daemons != nil {
		for _, d := range *dp.daemons {
			d.Restart()
		}
	}
}

// Shutdown all daemons in the pen
func (dp *DaemonPen) Shutdown(sig os.Signal) {
	dp.Lock()
	defer dp.Unlock()
	if dp.daemons != nil {
		for _, d := range *dp.daemons {
			d.Shutdown(sig)
		}
	}
}
<commit_msg>Show full, variable-expanded commands in logs<commit_after>package modd

import (
	"bufio"
	"bytes"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/cortesi/modd/conf"
	"github.com/cortesi/modd/varcmd"
	"github.com/cortesi/modd/watch"
	"github.com/cortesi/termlog"
)

const moddVar = "@mods"

// MinRestart is the minimum amount of time between daemon restarts
const MinRestart = 1 * time.Second

const lineLimit = 80

// shortCommand shortens a command to a name we can use in a notification
// header.
func shortCommand(command string) string {
	ret := command
	parts := strings.Split(command, "\n")
	for _, i := range parts {
		i = strings.TrimLeft(i, " \t#")
		i = strings.TrimRight(i, " \t\\")
		if i != "" {
			ret = i
			break
		}
	}
	return ret
}

// niceHeader tries to produce a nicer process name. We condense whitespace to
// make commands split over multiple lines with indentation more legible, and
// limit the line length to 80 characters.
func niceHeader(preamble string, command string) string {
	pre := termlog.DefaultPalette.Timestamp.SprintFunc()(preamble)
	command = termlog.DefaultPalette.Header.SprintFunc()(shortCommand(command))
	return pre + command
}

func getShell() string {
	return "bash"
}

func logOutput(wg *sync.WaitGroup, fp io.ReadCloser, out func(string, ...interface{})) {
	defer wg.Done()
	r := bufio.NewReader(fp)
	for {
		line, _, err := r.ReadLine()
		if err != nil {
			return
		}
		out(string(line))
	}
}

// ProcError is a process error, possibly containing command output
type ProcError struct {
	shorttext string
	Output    string
}

func (p ProcError) Error() string {
	return p.shorttext
}

// RunProc runs a process to completion, sending output to log
func RunProc(cmd string, log termlog.Stream) error {
	log.Header()
	sh := getShell()
	c := exec.Command(sh, "-c", cmd)
	stdo, err := c.StdoutPipe()
	if err != nil {
		return err
	}
	stde, err := c.StderrPipe()
	if err != nil {
		return err
	}
	wg := sync.WaitGroup{}
	wg.Add(2)
	buff := new(bytes.Buffer)
	mut := sync.Mutex{}
	go logOutput(
		&wg, stde,
		func(s string, args ...interface{}) {
			log.Warn(s)

			mut.Lock()
			defer mut.Unlock()
			buff.WriteString(s + "\n")
		},
	)
	go logOutput(&wg, stdo, log.Say)
	err = c.Start()
	if err != nil {
		return err
	}
	err = c.Wait()
	wg.Wait()
	if err != nil {
		log.Shout("%s", c.ProcessState.String())
		return ProcError{err.Error(), buff.String()}
	}
	log.Notice(">> done (%s)", c.ProcessState.UserTime())
	return nil
}

// RunPreps runs all commands in sequence. Stops if any command returns an error.
func RunPreps(b conf.Block, vars map[string]string, mod *watch.Mod, log termlog.TermLog) error {
	vcmd := varcmd.VarCmd{Block: &b, Mod: mod, Vars: vars}
	for _, p := range b.Preps {
		cmd, err := vcmd.Render(p.Command)
		if err != nil {
			return err
		}
		err = RunProc(cmd, log.Stream(niceHeader("prep: ", cmd)))
		if err != nil {
			return err
		}
	}
	return nil
}

type daemon struct {
	conf conf.Daemon
	log  termlog.Stream
	cmd  *exec.Cmd
	stop bool
}

func (d *daemon) Run() {
	var lastStart time.Time
	for d.stop != true {
		d.log.Notice(">> starting...")
		since := time.Now().Sub(lastStart)
		if since < MinRestart {
			time.Sleep(MinRestart - since)
		}
		lastStart = time.Now()
		sh := getShell()

		c := exec.Command(sh, "-c", d.conf.Command)
		stdo, err := c.StdoutPipe()
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}
		stde, err := c.StderrPipe()
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}
		wg := sync.WaitGroup{}
		wg.Add(2)
		go logOutput(&wg, stde, d.log.Warn)
		go logOutput(&wg, stdo, d.log.Say)
		err = c.Start()
		if err != nil {
			d.log.Shout("%s", err)
			continue
		}
		d.cmd = c
		err = c.Wait()
		wg.Wait()
		if err != nil {
			d.log.Shout("%s", c.ProcessState.String())
			continue
		}
	}
}

func (d *daemon) Restart() {
	if d.cmd != nil {
		d.log.Notice(">> sending signal %s", d.conf.RestartSignal)
		d.cmd.Process.Signal(d.conf.RestartSignal)
	}
}

func (d *daemon) Shutdown(sig os.Signal) {
	d.stop = true
	if d.cmd != nil {
		d.cmd.Process.Signal(sig)
		d.cmd.Wait()
	}
}

// DaemonPen is a group of daemons, managed as a unit.
type DaemonPen struct {
	daemons *[]daemon
	sync.Mutex
}

var ws = regexp.MustCompile(`\s\s+`)

// Start starts set of daemons, each specified by a command
func (dp *DaemonPen) Start(daemons []conf.Daemon, vars map[string]string, log termlog.TermLog) {
	dp.Lock()
	defer dp.Unlock()
	d := make([]daemon, len(daemons))
	for i, dmn := range daemons {
		vcmd := varcmd.VarCmd{Block: nil, Mod: nil, Vars: vars}
		finalcmd, err := vcmd.Render(dmn.Command)
		if err != nil {
			log.Shout("%s", err)
			continue
		}
		dmn.Command = finalcmd
		d[i] = daemon{
			conf: dmn,
			log: log.Stream(
				niceHeader("daemon: ", dmn.Command),
			),
		}
		go d[i].Run()
	}
	dp.daemons = &d
}

// Restart all daemons in the pen
func (dp *DaemonPen) Restart() {
	dp.Lock()
	defer dp.Unlock()
	if dp.daemons != nil {
		for _, d := range *dp.daemons {
			d.Restart()
		}
	}
}

// Shutdown all daemons in the pen
func (dp *DaemonPen) Shutdown(sig os.Signal) {
	dp.Lock()
	defer dp.Unlock()
	if dp.daemons != nil {
		for _, d := range *dp.daemons {
			d.Shutdown(sig)
		}
	}
}
<|endoftext|>