<commit_before>// Package colog implements prefix based logging by setting itself as output of the standard library
package colog

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sync"
	"time"
)

// std is the global singleton
// analog of the standard log.std
var std = NewCoLog(os.Stderr, "", 0)

// CoLog encapsulates our log writer
type CoLog struct {
	mu           sync.Mutex
	host         string
	prefix       string
	minLevel     Level
	defaultLevel Level
	headers      HeaderMap
	extractor    Extractor
	formatter    Formatter
	customFmt    bool
	parseFields  bool
	fixed        Fields
	hooks        hookPool
	out          io.Writer
}

// Entry represents a message being logged and all attached data
type Entry struct {
	Level   Level     // severity: trace, debug, info, warning, error, alert
	Time    time.Time // time of the event
	Host    string    // host origin of the message
	Prefix  string    // Prefix set to the logger
	File    string    // file where the log was called
	Line    int       // line in the file where the log was called
	Message []byte    // logged message
	Fields  Fields    // map of key-value data parsed from the message
}

// Level represents severity level
type Level uint8

// LevelMap links levels with output header bytes
type LevelMap map[Level][]byte

// HeaderMap links input header strings with levels
type HeaderMap map[string]Level

// hookPool is a list of registered pool, grouped by Level
type hookPool map[Level][]Hook

// Fields is the key-value map for extracted data
type Fields map[string]interface{}

const (
	// Unknown severity level
	unknown Level = iota
	// LTrace represents trace severity level
	LTrace
	// LDebug represents debug severity level
	LDebug
	// LInfo represents info severity level
	LInfo
	// LWarning represents warning severity level
	LWarning
	// LError represents error severity level
	LError
	// LAlert represents alert severity level
	LAlert
)

// String implements the Stringer interface for levels
func (level Level) String() string {
	switch level {
	case LTrace:
		return "trace"
	case LDebug:
		return "debug"
	case LInfo:
		return "info"
	case LWarning:
		return "warning"
	case LError:
		return "error"
	case LAlert:
		return "alert"
	}

	return "unknown"
}

var initialMinLevel = LTrace
var initialDefaultLevel = LInfo

var defaultHeaders = HeaderMap{
	"t: ":       LTrace,
	"trc: ":     LTrace,
	"trace: ":   LTrace,
	"d: ":       LDebug,
	"dbg: ":     LDebug,
	"debug: ":   LDebug,
	"i: ":       LInfo,
	"inf: ":     LInfo,
	"info: ":    LInfo,
	"w: ":       LWarning,
	"wrn: ":     LWarning,
	"warn: ":    LWarning,
	"warning: ": LWarning,
	"e: ":       LError,
	"err: ":     LError,
	"error: ":   LError,
	"a: ":       LAlert,
	"alr: ":     LAlert,
	"alert: ":   LAlert,
}

// NewCoLog returns CoLog instance ready to be used in logger.SetOutput()
func NewCoLog(out io.Writer, prefix string, flags int) *CoLog {
	cl := new(CoLog)
	cl.minLevel = initialMinLevel
	cl.defaultLevel = initialDefaultLevel
	cl.hooks = make(hookPool)
	cl.fixed = make(Fields)
	cl.headers = defaultHeaders
	cl.prefix = prefix
	cl.formatter = &StdFormatter{Flag: flags}
	cl.extractor = &StdExtractor{}
	cl.SetOutput(out)
	if host, err := os.Hostname(); err != nil {
		cl.host = host
	}

	return cl
}

// Register sets CoLog as output for the default logger.
// It "hijacks" the standard logger flags and prefix previously set.
// It's not possible to know the output previously set, so the
// default os.Stderr is assumed.
func Register() {
	// Inherit standard logger flags and prefix if appropriate
	if !std.customFmt {
		std.formatter.SetFlags(log.Flags())
	}

	if log.Prefix() != "" && std.prefix == "" {
		std.SetPrefix(log.Prefix())
	}

	// Disable all extras
	log.SetPrefix("")
	log.SetFlags(0)

	// Set CoLog as output
	log.SetOutput(std)
}

// AddHook adds a hook to be fired on every event with
// matching level being logged. See the hook interface
func (cl *CoLog) AddHook(hook Hook) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	for _, l := range hook.Levels() {
		cl.hooks[l] = append(cl.hooks[l], hook)
	}
}

// SetHost sets the logger hostname assigned to the entries
func (cl *CoLog) SetHost(host string) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.host = host
}

// SetPrefix sets the logger output prefix
func (cl *CoLog) SetPrefix(prefix string) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.prefix = prefix
}

// SetMinLevel sets the minimum level that will be actually logged
func (cl *CoLog) SetMinLevel(l Level) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.minLevel = l
}

// SetDefaultLevel sets the level that will be used when no level is detected
func (cl *CoLog) SetDefaultLevel(l Level) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.defaultLevel = l
}

// ParseFields activates or deactivates field parsing in the message
func (cl *CoLog) ParseFields(active bool) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.parseFields = active
}

// SetHeaders sets custom headers as the input headers to be search for to determine the level
func (cl *CoLog) SetHeaders(headers HeaderMap) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.headers = headers
}

// AddHeader adds a custom header to the input headers to be search for to determine the level
func (cl *CoLog) AddHeader(header string, level Level) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.headers[header] = level
}

// SetFormatter sets the formatter to use
func (cl *CoLog) SetFormatter(f Formatter) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.customFmt = true
	cl.formatter = f
}

// SetExtractor sets the formatter to use
func (cl *CoLog) SetExtractor(ex Extractor) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.extractor = ex
}

// FixedValue sets a key-value pair that will get automatically
// added to every log entry in this logger
func (cl *CoLog) FixedValue(key string, value interface{}) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.fixed[key] = value
}

// ClearFixedValues removes all previously set fields from the logger
func (cl *CoLog) ClearFixedValues() {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.fixed = make(Fields)
}

// Flags returns the output flags for the formatter if any
func (cl *CoLog) Flags() int {
	cl.mu.Lock()
	defer cl.mu.Unlock()
	if cl.formatter == nil {
		return 0
	}

	return cl.formatter.Flags()
}

// SetFlags sets the output flags for the formatter if any
func (cl *CoLog) SetFlags(flags int) {
	cl.mu.Lock()
	defer cl.mu.Unlock()
	if cl.formatter == nil {
		return
	}

	cl.formatter.SetFlags(flags)
}

// SetOutput is analog to log.SetOutput sets the output destination.
func (cl *CoLog) SetOutput(w io.Writer) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.out = w

	// if we have a color formatter, notify if new output supports color
	if _, ok := cl.formatter.(ColorFormatter); ok {
		cl.formatter.(ColorFormatter).ColorSupported(cl.colorSupported())
	}
}

// NewLogger returns a colog-enabled logger
func (cl *CoLog) NewLogger() *log.Logger {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	return log.New(cl, "", 0)
}

// Write implements io.Writer interface to that the standard logger uses.
func (cl *CoLog) Write(p []byte) (n int, err error) {
	cl.mu.Lock()
	defer func() {
		cl.mu.Unlock()
		if r := recover(); r != nil {
			err = fmt.Errorf("error: colog: recovered panic %v\n", r)
			fmt.Fprintln(os.Stderr, err.Error())
		}
	}()

	e := cl.parse(p)
	cl.extractFields(e)
	cl.fireHooks(e)

	if e.Level != unknown && e.Level < cl.minLevel {
		return 0, nil
	}

	if e.Level == unknown && cl.defaultLevel < cl.minLevel {
		return 0, nil
	}

	if cl.formatter == nil {
		err = errors.New("error: colog: missing formatter")
		fmt.Fprintln(os.Stderr, err.Error())
		return 0, err
	}

	fp, err := cl.formatter.Format(e)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: colog: failed to format entry: %v\n", err)
		return 0, err
	}

	n, err = cl.out.Write(fp)
	if err != nil {
		return n, err
	}

	return len(p), nil
}

func (cl *CoLog) parse(p []byte) *Entry {
	e := &Entry{
		Time:    time.Now(),
		Host:    cl.host,
		Prefix:  cl.prefix,
		Fields:  make(Fields),
		Message: bytes.TrimRight(p, "\n"),
	}

	// Apply fixed fields
	for k, v := range cl.fixed {
		e.Fields[k] = v
	}

	cl.applyLevel(e)

	// this is a bit expensive, check is anyone might actually need it
	if len(cl.hooks) != 0 || cl.formatter.Flags()&(log.Lshortfile|log.Llongfile) != 0 {
		e.File, e.Line = getFileLine(5)
	}

	return e
}

func (cl *CoLog) applyLevel(e *Entry) {
	for k, v := range cl.headers {
		header := []byte(k)
		if bytes.HasPrefix(e.Message, header) {
			e.Level = v                                     // apply level
			e.Message = bytes.TrimPrefix(e.Message, header) // remove header from message
			return
		}
	}

	e.Level = cl.defaultLevel
	return
}

// figure if output supports color
func (cl *CoLog) colorSupported() bool {

	// ColorSupporters can decide themselves
	if ce, ok := cl.out.(ColorSupporter); ok {
		return ce.ColorSupported()
	}

	// Windows users need ColorSupporter outputs
	if runtime.GOOS == "windows" {
		return false
	}

	// Check for Fd() method
	output, ok := cl.out.(interface {
		Fd() uintptr
	})

	// If no file descriptor it's not a TTY
	if !ok {
		return false
	}

	return isTerminal(int(output.Fd()))
}

func (cl *CoLog) extractFields(e *Entry) {
	if cl.parseFields && cl.extractor != nil {
		err := cl.extractor.Extract(e)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: colog: failed to extract fields: %v\n", err)
		}
	}
}

func (cl *CoLog) fireHooks(e *Entry) {
	for k := range cl.hooks[e.Level] {
		err := cl.hooks[e.Level][k].Fire(e)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: colog: failed to fire hook: %v\n", err)
		}
	}
}

// Standard logger functions

// AddHook adds a hook to be fired on every event with
// matching level being logged on the standard logger
func AddHook(hook Hook) {
	std.AddHook(hook)
}

// SetHost sets the logger hostname assigned to the entries of the standard logger
func SetHost(host string) {
	std.SetHost(host)
}

// SetPrefix sets the logger output prefix of the standard logger
func SetPrefix(prefix string) {
	std.SetPrefix(prefix)
}

// SetMinLevel sets the minimum level that will be actually logged by the standard logger
func SetMinLevel(l Level) {
	std.SetMinLevel(l)
}

// SetDefaultLevel sets the level that will be used when no level is detected for the standard logger
func SetDefaultLevel(l Level) {
	std.SetDefaultLevel(l)
}

// ParseFields activates or deactivates field parsing in the message for the standard logger
func ParseFields(active bool) {
	std.ParseFields(active)
}

// SetHeaders sets custom headers as the input headers to be search for to determine the level for the standard logger
func SetHeaders(headers HeaderMap) {
	std.SetHeaders(headers)
}

// AddHeader adds a custom header to the input headers to be search for to determine the level for the standard logger
func AddHeader(header string, level Level) {
	std.AddHeader(header, level)
}

// Flags returns the output flags for the standard log formatter if any
func Flags() int {
	return std.Flags()
}

// SetFlags sets the output flags for the standard log formatter if any
func SetFlags(flags int) {
	std.SetFlags(flags)
}

// SetOutput is analog to log.SetOutput sets the output destination for the standard logger
func SetOutput(w io.Writer) {
	std.SetOutput(w)
}

// SetFormatter sets the formatter to use by the standard logger
func SetFormatter(f Formatter) {
	std.SetFormatter(f)
}

// SetExtractor sets the extractor to use by the standard logger
func SetExtractor(ex Extractor) {
	std.SetExtractor(ex)
}

// FixedValue sets a field-value pair that will get automatically
// added to every log entry in the standard logger
func FixedValue(key string, value interface{}) {
	std.FixedValue(key, value)
}

// ClearFixedValues removes all previously set field-value in the standard logger
func ClearFixedValues() {
	std.ClearFixedValues()
}

// ParseLevel parses a string into a type Level
func ParseLevel(level string) (Level, error) {
	if lvl, ok := std.headers[level]; ok {
		return lvl, nil
	}

	return unknown, fmt.Errorf("could not parse level %s", level)
}
<commit_msg>docs: extend godoc intro<commit_after>// Package colog implements prefix based logging by setting itself as output of the standard library
// and parsing the log messages. Level prefixes are called headers in CoLog terms to not confuse with
// log.Prefix() which is independent.
// Basic usage only requires registering:
//	func main() {
//		colog.Register()
//		log.Print("info: that's all it takes!")
//	}
//
// CoLog requires the standard logger to submit messages without prefix or flags. So it resets them
// while registering and assigns them to itself, unfortunately CoLog cannot be aware of any output
// previously set.
package colog

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sync"
	"time"
)

// std is the global singleton
// analog of the standard log.std
var std = NewCoLog(os.Stderr, "", 0)

// CoLog encapsulates our log writer
type CoLog struct {
	mu           sync.Mutex
	host         string
	prefix       string
	minLevel     Level
	defaultLevel Level
	headers      HeaderMap
	extractor    Extractor
	formatter    Formatter
	customFmt    bool
	parseFields  bool
	fixed        Fields
	hooks        hookPool
	out          io.Writer
}

// Entry represents a message being logged and all attached data
type Entry struct {
	Level   Level     // severity: trace, debug, info, warning, error, alert
	Time    time.Time // time of the event
	Host    string    // host origin of the message
	Prefix  string    // Prefix set to the logger
	File    string    // file where the log was called
	Line    int       // line in the file where the log was called
	Message []byte    // logged message
	Fields  Fields    // map of key-value data parsed from the message
}

// Level represents severity level
type Level uint8

// LevelMap links levels with output header bytes
type LevelMap map[Level][]byte

// HeaderMap links input header strings with levels
type HeaderMap map[string]Level

// hookPool is a list of registered pool, grouped by Level
type hookPool map[Level][]Hook

// Fields is the key-value map for extracted data
type Fields map[string]interface{}

const (
	// Unknown severity level
	unknown Level = iota
	// LTrace represents trace severity level
	LTrace
	// LDebug represents debug severity level
	LDebug
	// LInfo represents info severity level
	LInfo
	// LWarning represents warning severity level
	LWarning
	// LError represents error severity level
	LError
	// LAlert represents alert severity level
	LAlert
)

// String implements the Stringer interface for levels
func (level Level) String() string {
	switch level {
	case LTrace:
		return "trace"
	case LDebug:
		return "debug"
	case LInfo:
		return "info"
	case LWarning:
		return "warning"
	case LError:
		return "error"
	case LAlert:
		return "alert"
	}

	return "unknown"
}

var initialMinLevel = LTrace
var initialDefaultLevel = LInfo

var defaultHeaders = HeaderMap{
	"t: ":       LTrace,
	"trc: ":     LTrace,
	"trace: ":   LTrace,
	"d: ":       LDebug,
	"dbg: ":     LDebug,
	"debug: ":   LDebug,
	"i: ":       LInfo,
	"inf: ":     LInfo,
	"info: ":    LInfo,
	"w: ":       LWarning,
	"wrn: ":     LWarning,
	"warn: ":    LWarning,
	"warning: ": LWarning,
	"e: ":       LError,
	"err: ":     LError,
	"error: ":   LError,
	"a: ":       LAlert,
	"alr: ":     LAlert,
	"alert: ":   LAlert,
}

// NewCoLog returns CoLog instance ready to be used in logger.SetOutput()
func NewCoLog(out io.Writer, prefix string, flags int) *CoLog {
	cl := new(CoLog)
	cl.minLevel = initialMinLevel
	cl.defaultLevel = initialDefaultLevel
	cl.hooks = make(hookPool)
	cl.fixed = make(Fields)
	cl.headers = defaultHeaders
	cl.prefix = prefix
	cl.formatter = &StdFormatter{Flag: flags}
	cl.extractor = &StdExtractor{}
	cl.SetOutput(out)
	if host, err := os.Hostname(); err != nil {
		cl.host = host
	}

	return cl
}

// Register sets CoLog as output for the default logger.
// It "hijacks" the standard logger flags and prefix previously set.
// It's not possible to know the output previously set, so the
// default os.Stderr is assumed.
func Register() {
	// Inherit standard logger flags and prefix if appropriate
	if !std.customFmt {
		std.formatter.SetFlags(log.Flags())
	}

	if log.Prefix() != "" && std.prefix == "" {
		std.SetPrefix(log.Prefix())
	}

	// Disable all extras
	log.SetPrefix("")
	log.SetFlags(0)

	// Set CoLog as output
	log.SetOutput(std)
}

// AddHook adds a hook to be fired on every event with
// matching level being logged. See the hook interface
func (cl *CoLog) AddHook(hook Hook) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	for _, l := range hook.Levels() {
		cl.hooks[l] = append(cl.hooks[l], hook)
	}
}

// SetHost sets the logger hostname assigned to the entries
func (cl *CoLog) SetHost(host string) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.host = host
}

// SetPrefix sets the logger output prefix
func (cl *CoLog) SetPrefix(prefix string) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.prefix = prefix
}

// SetMinLevel sets the minimum level that will be actually logged
func (cl *CoLog) SetMinLevel(l Level) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.minLevel = l
}

// SetDefaultLevel sets the level that will be used when no level is detected
func (cl *CoLog) SetDefaultLevel(l Level) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.defaultLevel = l
}

// ParseFields activates or deactivates field parsing in the message
func (cl *CoLog) ParseFields(active bool) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.parseFields = active
}

// SetHeaders sets custom headers as the input headers to be search for to determine the level
func (cl *CoLog) SetHeaders(headers HeaderMap) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.headers = headers
}

// AddHeader adds a custom header to the input headers to be search for to determine the level
func (cl *CoLog) AddHeader(header string, level Level) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.headers[header] = level
}

// SetFormatter sets the formatter to use
func (cl *CoLog) SetFormatter(f Formatter) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.customFmt = true
	cl.formatter = f
}

// SetExtractor sets the formatter to use
func (cl *CoLog) SetExtractor(ex Extractor) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.extractor = ex
}

// FixedValue sets a key-value pair that will get automatically
// added to every log entry in this logger
func (cl *CoLog) FixedValue(key string, value interface{}) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.fixed[key] = value
}

// ClearFixedValues removes all previously set fields from the logger
func (cl *CoLog) ClearFixedValues() {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.fixed = make(Fields)
}

// Flags returns the output flags for the formatter if any
func (cl *CoLog) Flags() int {
	cl.mu.Lock()
	defer cl.mu.Unlock()
	if cl.formatter == nil {
		return 0
	}

	return cl.formatter.Flags()
}

// SetFlags sets the output flags for the formatter if any
func (cl *CoLog) SetFlags(flags int) {
	cl.mu.Lock()
	defer cl.mu.Unlock()
	if cl.formatter == nil {
		return
	}

	cl.formatter.SetFlags(flags)
}

// SetOutput is analog to log.SetOutput sets the output destination.
func (cl *CoLog) SetOutput(w io.Writer) {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	cl.out = w

	// if we have a color formatter, notify if new output supports color
	if _, ok := cl.formatter.(ColorFormatter); ok {
		cl.formatter.(ColorFormatter).ColorSupported(cl.colorSupported())
	}
}

// NewLogger returns a colog-enabled logger
func (cl *CoLog) NewLogger() *log.Logger {
	cl.mu.Lock()
	defer cl.mu.Unlock()

	return log.New(cl, "", 0)
}

// Write implements io.Writer interface to that the standard logger uses.
func (cl *CoLog) Write(p []byte) (n int, err error) {
	cl.mu.Lock()
	defer func() {
		cl.mu.Unlock()
		if r := recover(); r != nil {
			err = fmt.Errorf("error: colog: recovered panic %v\n", r)
			fmt.Fprintln(os.Stderr, err.Error())
		}
	}()

	e := cl.parse(p)
	cl.extractFields(e)
	cl.fireHooks(e)

	if e.Level != unknown && e.Level < cl.minLevel {
		return 0, nil
	}

	if e.Level == unknown && cl.defaultLevel < cl.minLevel {
		return 0, nil
	}

	if cl.formatter == nil {
		err = errors.New("error: colog: missing formatter")
		fmt.Fprintln(os.Stderr, err.Error())
		return 0, err
	}

	fp, err := cl.formatter.Format(e)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: colog: failed to format entry: %v\n", err)
		return 0, err
	}

	n, err = cl.out.Write(fp)
	if err != nil {
		return n, err
	}

	return len(p), nil
}

func (cl *CoLog) parse(p []byte) *Entry {
	e := &Entry{
		Time:    time.Now(),
		Host:    cl.host,
		Prefix:  cl.prefix,
		Fields:  make(Fields),
		Message: bytes.TrimRight(p, "\n"),
	}

	// Apply fixed fields
	for k, v := range cl.fixed {
		e.Fields[k] = v
	}

	cl.applyLevel(e)

	// this is a bit expensive, check is anyone might actually need it
	if len(cl.hooks) != 0 || cl.formatter.Flags()&(log.Lshortfile|log.Llongfile) != 0 {
		e.File, e.Line = getFileLine(5)
	}

	return e
}

func (cl *CoLog) applyLevel(e *Entry) {
	for k, v := range cl.headers {
		header := []byte(k)
		if bytes.HasPrefix(e.Message, header) {
			e.Level = v                                     // apply level
			e.Message = bytes.TrimPrefix(e.Message, header) // remove header from message
			return
		}
	}

	e.Level = cl.defaultLevel
	return
}

// figure if output supports color
func (cl *CoLog) colorSupported() bool {

	// ColorSupporters can decide themselves
	if ce, ok := cl.out.(ColorSupporter); ok {
		return ce.ColorSupported()
	}

	// Windows users need ColorSupporter outputs
	if runtime.GOOS == "windows" {
		return false
	}

	// Check for Fd() method
	output, ok := cl.out.(interface {
		Fd() uintptr
	})

	// If no file descriptor it's not a TTY
	if !ok {
		return false
	}

	return isTerminal(int(output.Fd()))
}

func (cl *CoLog) extractFields(e *Entry) {
	if cl.parseFields && cl.extractor != nil {
		err := cl.extractor.Extract(e)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: colog: failed to extract fields: %v\n", err)
		}
	}
}

func (cl *CoLog) fireHooks(e *Entry) {
	for k := range cl.hooks[e.Level] {
		err := cl.hooks[e.Level][k].Fire(e)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: colog: failed to fire hook: %v\n", err)
		}
	}
}

// Standard logger functions

// AddHook adds a hook to be fired on every event with
// matching level being logged on the standard logger
func AddHook(hook Hook) {
	std.AddHook(hook)
}

// SetHost sets the logger hostname assigned to the entries of the standard logger
func SetHost(host string) {
	std.SetHost(host)
}

// SetPrefix sets the logger output prefix of the standard logger
func SetPrefix(prefix string) {
	std.SetPrefix(prefix)
}

// SetMinLevel sets the minimum level that will be actually logged by the standard logger
func SetMinLevel(l Level) {
	std.SetMinLevel(l)
}

// SetDefaultLevel sets the level that will be used when no level is detected for the standard logger
func SetDefaultLevel(l Level) {
	std.SetDefaultLevel(l)
}

// ParseFields activates or deactivates field parsing in the message for the standard logger
func ParseFields(active bool) {
	std.ParseFields(active)
}

// SetHeaders sets custom headers as the input headers to be search for to determine the level for the standard logger
func SetHeaders(headers HeaderMap) {
	std.SetHeaders(headers)
}

// AddHeader adds a custom header to the input headers to be search for to determine the level for the standard logger
func AddHeader(header string, level Level) {
	std.AddHeader(header, level)
}

// Flags returns the output flags for the standard log formatter if any
func Flags() int {
	return std.Flags()
}

// SetFlags sets the output flags for the standard log formatter if any
func SetFlags(flags int) {
	std.SetFlags(flags)
}

// SetOutput is analog to log.SetOutput sets the output destination for the standard logger
func SetOutput(w io.Writer) {
	std.SetOutput(w)
}

// SetFormatter sets the formatter to use by the standard logger
func SetFormatter(f Formatter) {
	std.SetFormatter(f)
}

// SetExtractor sets the extractor to use by the standard logger
func SetExtractor(ex Extractor) {
	std.SetExtractor(ex)
}

// FixedValue sets a field-value pair that will get automatically
// added to every log entry in the standard logger
func FixedValue(key string, value interface{}) {
	std.FixedValue(key, value)
}

// ClearFixedValues removes all previously set field-value in the standard logger
func ClearFixedValues() {
	std.ClearFixedValues()
}

// ParseLevel parses a string into a type Level
func ParseLevel(level string) (Level, error) {
	if lvl, ok := std.headers[level]; ok {
		return lvl, nil
	}

	return unknown, fmt.Errorf("could not parse level %s", level)
}
<|endoftext|>


<commit_before>package cairo

import "C"

import (
	"image/color"
)

func clamp01(f float64) float64 {
	switch {
	case f < 0:
		return 0
	case f > 1:
		return 1
	}
	return f
}

//convert f from [0,1] to uint32.
func ctoi(f float64) uint32 {
	f = clamp01(f)
	return uint32(f*float64(1<<32-1) + .5)
}

//convert i to [0,1].
func cto01(i uint32) float64 {
	return float64(i) / float64(1<<32-1)
}

//Color represents an RGB color where each component is in [0, 1].
type Color struct {
	R, G, B float64
}

//Canon returns a new color with all values clamped to [0,1].
func (c Color) Canon() Color {
	return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)}
}

func (co Color) c() (r, g, b C.double) {
	return C.double(co.R), C.double(co.G), C.double(co.G)
}

func (c Color) RGBA() (r, g, b, a uint32) {
	return ctoi(c.R), ctoi(c.G), ctoi(c.B), 0xffff
}

//AlphaColor represents an RGBA color where each component is in [0, 1].
type AlphaColor struct {
	R, G, B, A float64
}

//Canon returns a new color with all values clamped to [0,1].
func (a AlphaColor) Canon() AlphaColor {
	return AlphaColor{clamp01(a.R), clamp01(a.G), clamp01(a.B), clamp01(a.A)}
}

func (a AlphaColor) c() (r, g, b, alpha C.double) {
	return C.double(a.R), C.double(a.G), C.double(a.G), C.double(a.A)
}

func (a AlphaColor) RGBA() (r, g, b, alpha uint32) {
	return ctoi(a.R), ctoi(a.G), ctoi(a.B), ctoi(a.A)
}

//These models can convert any color.Color to themselves.
//
//The conversion may be lossy.
var (
	ColorModel = func(c color.Color) color.Color {
		if c, ok := c.(Color); ok {
			return c
		}
		r, g, b, _ := c.RGBA()
		return Color{cto01(r), cto01(g), cto01(b)}
	}
	AlphaColorModel = func(c color.Color) color.Color {
		if c, ok := c.(AlphaColor); ok {
			return c
		}
		r, g, b, a := c.RGBA()
		return AlphaColor{cto01(r), cto01(g), cto01(b), cto01(a)}
	}
)
<commit_msg>forgot to wrap model funcs in modelfunc<commit_after>package cairo

import "C"

import (
	"image/color"
)

func clamp01(f float64) float64 {
	switch {
	case f < 0:
		return 0
	case f > 1:
		return 1
	}
	return f
}

//convert f from [0,1] to uint32.
func ctoi(f float64) uint32 {
	f = clamp01(f)
	return uint32(f*float64(1<<32-1) + .5)
}

//convert i to [0,1].
func cto01(i uint32) float64 {
	return float64(i) / float64(1<<32-1)
}

//Color represents an RGB color where each component is in [0, 1].
type Color struct {
	R, G, B float64
}

//Canon returns a new color with all values clamped to [0,1].
func (c Color) Canon() Color {
	return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)}
}

func (co Color) c() (r, g, b C.double) {
	return C.double(co.R), C.double(co.G), C.double(co.G)
}

func (c Color) RGBA() (r, g, b, a uint32) {
	return ctoi(c.R), ctoi(c.G), ctoi(c.B), 0xffff
}

//AlphaColor represents an RGBA color where each component is in [0, 1].
type AlphaColor struct {
	R, G, B, A float64
}

//Canon returns a new color with all values clamped to [0,1].
func (a AlphaColor) Canon() AlphaColor {
	return AlphaColor{clamp01(a.R), clamp01(a.G), clamp01(a.B), clamp01(a.A)}
}

func (a AlphaColor) c() (r, g, b, alpha C.double) {
	return C.double(a.R), C.double(a.G), C.double(a.G), C.double(a.A)
}

func (a AlphaColor) RGBA() (r, g, b, alpha uint32) {
	return ctoi(a.R), ctoi(a.G), ctoi(a.B), ctoi(a.A)
}

//These models can convert any color.Color to themselves.
//
//The conversion may be lossy.
var (
	ColorModel = color.ModelFunc(func(c color.Color) color.Color {
		if c, ok := c.(Color); ok {
			return c
		}
		r, g, b, _ := c.RGBA()
		return Color{cto01(r), cto01(g), cto01(b)}
	})
	AlphaColorModel = color.ModelFunc(func(c color.Color) color.Color {
		if c, ok := c.(AlphaColor); ok {
			return c
		}
		r, g, b, a := c.RGBA()
		return AlphaColor{cto01(r), cto01(g), cto01(b), cto01(a)}
	})
)
<|endoftext|>


<commit_before>package hermes_test

import (
	"testing"
	"time"

	_ "github.com/lib/pq"
	"github.com/sbowman/hermes"
)

const (
	driver   = "postgres"
	database = "postgres://postgres@127.0.0.1/hermes_test?sslmode=disable&connect_timeout=10"
)

func init() {
	hermes.MaxElapsedTime = 1 * time.Second
}

// Return a connection to the database.  Will generate a fatal error if unable
// to connect.
func connect(t *testing.T) *hermes.DB {
	db, err := hermes.Connect(driver, database, 5, 1)
	if err != nil {
		t.Fatalf("Failed to connect to the hermes_test database: %s", err)
	}

	return db
}

func TestConnection(t *testing.T) {
	db := connect(t)
	defer db.Close()
	hermes.MaxElapsedTime = 1 * time.Second

	if err := db.Ping(); err != nil {
		t.Fatalf("Unable to ping the database: %s", err)
	}
}

func TestBadDatabase(t *testing.T) {
	_, err := hermes.Connect(driver, "postgres://postgres@127.0.0.1/nemo?sslmode=disable&connect_timeout=10", 5, 1)
	if err == nil {
		t.Fatalf(`Missing "nemo" database didn't generate an error!  Does it exist?`)
	}
}
<commit_msg>Remove duplicate setting of hermes.MaxElapsedTime<commit_after>package hermes_test

import (
	"testing"
	"time"

	_ "github.com/lib/pq"
	"github.com/sbowman/hermes"
)

const (
	driver   = "postgres"
	database = "postgres://postgres@127.0.0.1/hermes_test?sslmode=disable&connect_timeout=10"
)

func init() {
	hermes.MaxElapsedTime = 1 * time.Second
}

// Return a connection to the database.  Will generate a fatal error if unable
// to connect.
func connect(t *testing.T) *hermes.DB {
	db, err := hermes.Connect(driver, database, 5, 1)
	if err != nil {
		t.Fatalf("Failed to connect to the hermes_test database: %s", err)
	}

	return db
}

func TestConnection(t *testing.T) {
	db := connect(t)
	defer db.Close()

	if err := db.Ping(); err != nil {
		t.Fatalf("Unable to ping the database: %s", err)
	}
}

func TestBadDatabase(t *testing.T) {
	_, err := hermes.Connect(driver, "postgres://postgres@127.0.0.1/nemo?sslmode=disable&connect_timeout=10", 5, 1)
	if err == nil {
		t.Fatalf(`Missing "nemo" database didn't generate an error!  Does it exist?`)
	}
}
<|endoftext|>


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

package reddit

const (
	UpVote     = "1"
	DownVote   = "-1"
	RemoveVote = "0"
)

var DefaultUserAgent = "github.com/jzelinskie/reddit"
<commit_msg>added a helper for http api calls; will make use later<commit_after>// Copyright 2012 Jimmy Zelinskie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package reddit

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	UpVote     = "1"
	DownVote   = "-1"
	RemoveVote = "0"
)

var DefaultUserAgent = "github.com/jzelinskie/reddit"

func getResponse(url string, vals *url.Values, s *Session) (*bytes.Buffer, error) {
	// Determine the HTTP action.
	var action, finalurl string
	if vals == nil {
		action = "GET"
		finalurl = url
	} else {
		action = "POST"
		finalurl = url + vals.Encode()
	}

	// Create a request and add the proper headers.
	req, err := http.NewRequest(action, finalurl, nil)
	if err != nil {
		return nil, err
	}
	if s == nil {
		req.AddCookie(s.Cookie)
	}
	req.Header.Set("User-Agent", DefaultUserAgent)

	// Handle the request
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(resp.Status)
	}

	respbytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return bytes.NewBuffer(respbytes), nil
}
<|endoftext|>


<commit_before>package scipipe

const (
	// BUFSIZE is the standard buffer size used for channels connecting processes
	BUFSIZE = 16
	// Version is the SciPipe version in string format
	Version = "0.9.2"
)
<commit_msg>Bump version to 0.9.3<commit_after>package scipipe

const (
	// BUFSIZE is the standard buffer size used for channels connecting processes
	BUFSIZE = 16
	// Version is the SciPipe version in string format
	Version = "0.9.3"
)
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path"
)

const (
	rcFname            = "rc.txt"
	blockedFname       = "auto-blocked.txt"
	directFname        = "auto-direct.txt"
	alwaysBlockedFname = "blocked.txt"
	alwaysDirectFname  = "direct.txt"
	chouFname          = "chou.txt"

	newLine = "\r\n"
)

func initConfigDir() {
	// On windows, put the configuration file in the same directory of cow executable
	home := path.Base(os.Args[0])
	if home == os.Args[0] {
		// Invoked in the current directory
		home = ""
	}
	dsFile.dir = home
}
<commit_msg>Fix binary directory detection on windows.<commit_after>package main

import (
	"os"
	"path"
)

const (
	rcFname            = "rc.txt"
	blockedFname       = "auto-blocked.txt"
	directFname        = "auto-direct.txt"
	alwaysBlockedFname = "blocked.txt"
	alwaysDirectFname  = "direct.txt"
	chouFname          = "chou.txt"

	newLine = "\r\n"
)

func initConfigDir() {
	// On windows, put the configuration file in the same directory of cow executable
	// This is not a reliable way to detect binary directory, but it works for double click and run
	dsFile.dir = path.Dir(os.Args[0])
}
<|endoftext|>


<commit_before>package main

const build = "ba52b25180802b024570049d5e8b6fec81d7a6d8"
<commit_msg>update build sequence<commit_after>package main

const build = "6e18767356365e2414bf1d3d15e53e2b3130383c"
<|endoftext|>


<commit_before>/*
Open Source Initiative OSI - The MIT License (MIT):Licensing

The MIT License (MIT)
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package mapset

import "fmt"
import "strings"

type Set struct {
	set map[interface{}]_placeHolder
}

type _placeHolder struct{}

func NewSet() *Set {
	return &Set{make(map[interface{}]_placeHolder)}
}

func (set *Set) Add(i interface{}) bool {
	_, found := set.set[i]
	set.set[i] = _placeHolder{} //not working this line
	return !found               //False if it existed already
}

func (set *Set) Contains(i interface{}) bool {
	if _, found := set.set[i]; found {
		return found //true if it existed already
	}
	return false
}

func (set *Set) IsSubset(other *Set) bool {
	counter := 0
	for key, _ := range set.set {
		if other.Contains(key) {
			counter++
		}
	}
	return counter == set.Size()
}

func (set *Set) IsSuperset(other *Set) bool {
	counter := 0
	for key, _ := range other.set {
		if set.Contains(key) {
			counter++
		}
	}
	return counter == other.Size()
}

func (set *Set) Union(other *Set) *Set {
	if set != nil && other != nil {
		unionedSet := NewSet()

		for key, _ := range set.set {
			unionedSet.Add(key)
		}
		for key, _ := range other.set {
			unionedSet.Add(key)
		}
		return unionedSet
	}
	return nil
}

func (set *Set) Intersect(other *Set) *Set {
	if set != nil && other != nil {
		intersectedSet := NewSet()
		for key, _ := range set.set {
			if other.Contains(key) {
				intersectedSet.Add(key)
			}
		}
		return intersectedSet
	}
	return nil
}

func (set *Set) Difference(other *Set) *Set {
	if set != nil && other != nil {
		differencedSet := NewSet()

		for key, _ := range set.set {
			differencedSet.Add(key)
		}

		for key, _ := range other.set {
			differencedSet.Add(key)
		}

		for key, _ := range other.set {
			differencedSet.Remove(key)
		}

		return differencedSet
	}
	return nil
}

func (set *Set) SymmetricDifference(other *Set) *Set {
	if set != nil && other != nil {
		aDiff := set.Difference(other)
		bDiff := other.Difference(set)

		symDifferencedSet := aDiff.Union(bDiff)

		return symDifferencedSet
	}
	return nil
}

func (set *Set) Clear() {
	set.set = make(map[interface{}]_placeHolder)
}

func (set *Set) Remove(i interface{}) {
	delete(set.set, i)
}

func (set *Set) Size() int {
	return len(set.set)
}

func (set *Set) String() string {
	items := make([]string, 0, len(set.set))

	for key := range set.set {
		items = append(items, fmt.Sprintf("%v", key))
	}
	return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
}
<commit_msg>early return on IsSubset and IsSuperset which saves iterating through the whole set.  Good catch JohnStow on: http://www.reddit.com/r/golang/comments/1hlijg/a_simple_set_type_for_the_go_language/<commit_after>/*
Open Source Initiative OSI - The MIT License (MIT):Licensing

The MIT License (MIT)
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package mapset

import "fmt"
import "strings"

type Set struct {
	set map[interface{}]_placeHolder
}

type _placeHolder struct{}

func NewSet() *Set {
	return &Set{make(map[interface{}]_placeHolder)}
}

func (set *Set) Add(i interface{}) bool {
	_, found := set.set[i]
	set.set[i] = _placeHolder{} //not working this line
	return !found               //False if it existed already
}

func (set *Set) Contains(i interface{}) bool {
	if _, found := set.set[i]; found {
		return found //true if it existed already
	}
	return false
}

func (set *Set) IsSubset(other *Set) bool {
	for key, _ := range set.set {
		if !other.Contains(key) {
			return false
		}
	}
	return true
}

func (set *Set) IsSuperset(other *Set) bool {
	for key, _ := range other.set {
		if !set.Contains(key) {
			return false
		}
	}
	return true
}

func (set *Set) Union(other *Set) *Set {
	if set != nil && other != nil {
		unionedSet := NewSet()

		for key, _ := range set.set {
			unionedSet.Add(key)
		}
		for key, _ := range other.set {
			unionedSet.Add(key)
		}
		return unionedSet
	}
	return nil
}

func (set *Set) Intersect(other *Set) *Set {
	if set != nil && other != nil {
		intersectedSet := NewSet()
		for key, _ := range set.set {
			if other.Contains(key) {
				intersectedSet.Add(key)
			}
		}
		return intersectedSet
	}
	return nil
}

func (set *Set) Difference(other *Set) *Set {
	if set != nil && other != nil {
		differencedSet := NewSet()

		for key, _ := range set.set {
			differencedSet.Add(key)
		}

		for key, _ := range other.set {
			differencedSet.Add(key)
		}

		for key, _ := range other.set {
			differencedSet.Remove(key)
		}

		return differencedSet
	}
	return nil
}

func (set *Set) SymmetricDifference(other *Set) *Set {
	if set != nil && other != nil {
		aDiff := set.Difference(other)
		bDiff := other.Difference(set)

		symDifferencedSet := aDiff.Union(bDiff)

		return symDifferencedSet
	}
	return nil
}

func (set *Set) Clear() {
	set.set = make(map[interface{}]_placeHolder)
}

func (set *Set) Remove(i interface{}) {
	delete(set.set, i)
}

func (set *Set) Size() int {
	return len(set.set)
}

func (set *Set) String() string {
	items := make([]string, 0, len(set.set))

	for key := range set.set {
		items = append(items, fmt.Sprintf("%v", key))
	}
	return fmt.Sprintf("Set{%s}", strings.Join(items, ", "))
}
<|endoftext|>


<commit_before>// Copyright 2019 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"bufio"
	"expvar"
	"strings"
	"testing"
	"time"

	"github.com/google/mtail/internal/logline"
	"github.com/google/mtail/internal/metrics"
	"github.com/google/mtail/internal/testutil"
	"github.com/google/mtail/internal/watcher"
)

var vmTests = []struct {
	name string
	prog string
	log  string
}{
	{"single-dash-parseint",
		`counter c

/(?P<x>-)/ {
    $x == "-" {
        c++
    }
}
`, `123 a
- b
`,
	},
}

func TestVmEndToEnd(t *testing.T) {
	if testing.Verbose() {
		defer testutil.TestSetFlag(t, "vmodule", "vm=2,loader=2")()
	}
	for _, tc := range vmTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			w := watcher.NewFakeWatcher()
			lines := make(chan *logline.LogLine, 0)
			store := metrics.NewStore()
			l, err := NewLoader("", store, lines, w, ErrorsAbort, DumpAst, DumpAstTypes, DumpBytecode, OmitMetricSource)
			testutil.FatalIfErr(t, err)
			compileErrors := l.CompileAndRun(tc.name, strings.NewReader(tc.prog))
			testutil.FatalIfErr(t, compileErrors)
			scanner := bufio.NewScanner(strings.NewReader(tc.log))
			lineCount := 0
			for scanner.Scan() {
				lineCount++
				lines <- logline.New(tc.name, scanner.Text())
			}
			close(lines)
			w.Close()

			check := func() (bool, error) {
				if expvar.Get("line_count").String() != string(lineCount) {
					return false, nil
				}
				return true, nil
			}
			ok, err := testutil.DoOrTimeout(check, 100*time.Millisecond, 10*time.Millisecond)
			if err != nil {
				t.Errorf("Busted, timeout waiting for line count.")
			}

			if !ok {
				t.Errorf("Incorrect lines read: expected %d received %s", lineCount, expvar.Get("line_count"))
			}
			// This is not good.
			if expvar.Get("prog_runtime_errors").(*expvar.Map).Get(tc.name).String() != "0" {
				t.Errorf("Nonzero runtime errors from program: got %s", expvar.Get("prog_runtime_errors").(*expvar.Map).Get(tc.name))
			}
			t.Logf("Store is %v", store)
		})
	}
}
<commit_msg>Skip vm integration test for now.<commit_after>// Copyright 2019 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"bufio"
	"expvar"
	"strings"
	"testing"
	"time"

	"github.com/google/mtail/internal/logline"
	"github.com/google/mtail/internal/metrics"
	"github.com/google/mtail/internal/testutil"
	"github.com/google/mtail/internal/watcher"
)

var vmTests = []struct {
	name string
	prog string
	log  string
}{
	{"single-dash-parseint",
		`counter c

/(?P<x>-)/ {
    $x == "-" {
        c++
    }
}
`, `123 a
- b
`,
	},
}

func TestVmEndToEnd(t *testing.T) {
	t.Skip("busted")
	if testing.Verbose() {
		defer testutil.TestSetFlag(t, "vmodule", "vm=2,loader=2")()
	}
	for _, tc := range vmTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			w := watcher.NewFakeWatcher()
			lines := make(chan *logline.LogLine, 0)
			store := metrics.NewStore()
			l, err := NewLoader("", store, lines, w, ErrorsAbort, DumpAst, DumpAstTypes, DumpBytecode, OmitMetricSource)
			testutil.FatalIfErr(t, err)
			compileErrors := l.CompileAndRun(tc.name, strings.NewReader(tc.prog))
			testutil.FatalIfErr(t, compileErrors)
			scanner := bufio.NewScanner(strings.NewReader(tc.log))
			lineCount := 0
			for scanner.Scan() {
				lineCount++
				lines <- logline.New(tc.name, scanner.Text())
			}
			close(lines)
			w.Close()

			check := func() (bool, error) {
				if expvar.Get("line_count").String() != string(lineCount) {
					return false, nil
				}
				return true, nil
			}
			ok, err := testutil.DoOrTimeout(check, 100*time.Millisecond, 10*time.Millisecond)
			if err != nil {
				t.Errorf("Busted, timeout waiting for line count.")
			}

			if !ok {
				t.Errorf("Incorrect lines read: expected %d received %s", lineCount, expvar.Get("line_count"))
			}
			// This is not good.
			if expvar.Get("prog_runtime_errors").(*expvar.Map).Get(tc.name).String() != "0" {
				t.Errorf("Nonzero runtime errors from program: got %s", expvar.Get("prog_runtime_errors").(*expvar.Map).Get(tc.name))
			}
			t.Logf("Store is %v", store)
		})
	}
}
<|endoftext|>


<commit_before>package gocql

import (
	"log"
	"net"
	"time"
)

type HostInfo struct {
	Peer       string
	DataCenter string
	Rack       string
	HostId     string
	Tokens     []string
}

// Polls system.peers at a specific interval to find new hosts
type ringDescriber struct {
	dcFilter   string
	rackFilter string
	previous   []HostInfo
	session    *Session
}

func (r *ringDescriber) GetHosts() []HostInfo {
	// we need conn to be the same because we need to query system.peers and system.local
	// on the same node to get the whole cluster
	conn := r.session.Pool.Pick(nil)
	if conn == nil {
		return r.previous
	}

	query := r.session.Query("SELECT data_center, rack, host_id, tokens FROM system.local")
	iter := conn.executeQuery(query)

	host := &HostInfo{}
	iter.Scan(&host.DataCenter, &host.Rack, &host.HostId, &host.Tokens)

	if err := iter.Close(); err != nil {
		log.Printf("GetHosts: unable to get local host info: %v\n", err)
		return r.previous
	}

	addr, _, err := net.SplitHostPort(conn.Address())
	if err != nil {
		// this should not happen, ever, as this is the address that was dialed by conn, here
		// a panic makes sense, please report a bug if it occurs.
		panic(err)
	}

	host.Peer = addr

	hosts := []HostInfo{*host}

	query = r.session.Query("SELECT peer, data_center, rack, host_id, tokens FROM system.peers")
	iter = conn.executeQuery(query)

	for iter.Scan(&host.Peer, &host.DataCenter, &host.Rack, &host.HostId, &host.Tokens) {
		if r.matchFilter(host) {
			hosts = append(hosts, *host)
		}
	}

	if err := iter.Close(); err != nil {
		log.Printf("GetHosts: unable to get ring host info: %v\n", err)
		return r.previous
	}

	r.previous = hosts

	return hosts
}

func (r *ringDescriber) matchFilter(host *HostInfo) bool {
	if r.dcFilter == "" && r.rackFilter == "" {
		return true
	}

	if r.dcFilter != "" && r.dcFilter != host.DataCenter {
		return false
	}

	if r.rackFilter != "" && r.rackFilter != host.Rack {
		return false
	}

	return true
}

func (h *ringDescriber) run(sleep time.Duration) {
	if sleep == 0 {
		sleep = 30 * time.Second
	}

	for {
		// if we have 0 hosts this will return the previous list of hosts to
		// attempt to reconnect to the cluster otherwise we would never find
		// downed hosts again, could possibly have an optimisation to only
		// try to add new hosts if GetHosts didnt error and the hosts didnt change.
		h.session.Pool.SetHosts(h.GetHosts())

		time.Sleep(sleep)
	}
}
<commit_msg>Only set the hosts if the ring describer didnt error<commit_after>package gocql

import (
	"log"
	"net"
	"time"
)

type HostInfo struct {
	Peer       string
	DataCenter string
	Rack       string
	HostId     string
	Tokens     []string
}

// Polls system.peers at a specific interval to find new hosts
type ringDescriber struct {
	dcFilter   string
	rackFilter string
	previous   []HostInfo
	session    *Session
}

func (r *ringDescriber) GetHosts() ([]HostInfo, error) {
	// we need conn to be the same because we need to query system.peers and system.local
	// on the same node to get the whole cluster
	conn := r.session.Pool.Pick(nil)
	if conn == nil {
		return r.previous, nil
	}

	query := r.session.Query("SELECT data_center, rack, host_id, tokens FROM system.local")
	iter := conn.executeQuery(query)

	host := &HostInfo{}
	iter.Scan(&host.DataCenter, &host.Rack, &host.HostId, &host.Tokens)

	if err := iter.Close(); err != nil {
		return nil, err
	}

	addr, _, err := net.SplitHostPort(conn.Address())
	if err != nil {
		// this should not happen, ever, as this is the address that was dialed by conn, here
		// a panic makes sense, please report a bug if it occurs.
		panic(err)
	}

	host.Peer = addr

	hosts := []HostInfo{*host}

	query = r.session.Query("SELECT peer, data_center, rack, host_id, tokens FROM system.peers")
	iter = conn.executeQuery(query)

	for iter.Scan(&host.Peer, &host.DataCenter, &host.Rack, &host.HostId, &host.Tokens) {
		if r.matchFilter(host) {
			hosts = append(hosts, *host)
		}
	}

	if err := iter.Close(); err != nil {
		return nil, err
	}

	r.previous = hosts

	return hosts, nil
}

func (r *ringDescriber) matchFilter(host *HostInfo) bool {
	if r.dcFilter == "" && r.rackFilter == "" {
		return true
	}

	if r.dcFilter != "" && r.dcFilter != host.DataCenter {
		return false
	}

	if r.rackFilter != "" && r.rackFilter != host.Rack {
		return false
	}

	return true
}

func (h *ringDescriber) run(sleep time.Duration) {
	if sleep == 0 {
		sleep = 30 * time.Second
	}

	for {
		// if we have 0 hosts this will return the previous list of hosts to
		// attempt to reconnect to the cluster otherwise we would never find
		// downed hosts again, could possibly have an optimisation to only
		// try to add new hosts if GetHosts didnt error and the hosts didnt change.
		hosts, err := h.GetHosts()
		if err != nil {
			log.Println("RingDescriber: unable to get ring topology:", err)
		} else {
			h.session.Pool.SetHosts(hosts)
		}

		time.Sleep(sleep)
	}
}
<|endoftext|>


<commit_before>//go:build go1.10
// +build go1.10

package proxy

import (
	"context"
	"database/sql/driver"
	"errors"
	"io"
	"testing"
)

var _ io.Closer = (*Connector)(nil)
var _ driver.Connector = (*Connector)(nil)

var _ io.Closer = (*closerConnector)(nil)
var _ driver.Connector = (*closerConnector)(nil)

type closerConnector struct {
	// the result of Close() method
	errClose error

	// a flag whether Close() method is called
	closed bool
}

func (c *closerConnector) Connect(ctx context.Context) (driver.Conn, error) {
	panic("never used")
}

func (c *closerConnector) Driver() driver.Driver {
	return fdriverctx
}

func (c *closerConnector) Close() error {
	c.closed = true
	return c.errClose
}

func TestConnectorClose(t *testing.T) {
	t.Run("Closing c.Connector succeeds", func(t *testing.T) {
		c0 := &closerConnector{}
		c1 := NewConnector(c0)
		if err := c1.(io.Closer).Close(); err != nil {
			t.Fatal(err)
		}
		if !c0.closed {
			t.Errorf("c.Connector should be closed, but not")
		}
	})

	t.Run("Closing c.Connector fails", func(t *testing.T) {
		errClose := errors.New("some error while closing")
		c0 := &closerConnector{
			errClose: errClose,
		}
		c1 := NewConnector(c0)
		if err := c1.(io.Closer).Close(); err != errClose {
			t.Errorf("want err is %v, got %v", errClose, err)
		}
		if !c0.closed {
			t.Errorf("c.Connector should be closed, but not")
		}
	})
}
<commit_msg>add test case c.Connector doesn't implement io.Closer<commit_after>//go:build go1.10
// +build go1.10

package proxy

import (
	"context"
	"database/sql/driver"
	"errors"
	"io"
	"testing"
)

var _ io.Closer = (*Connector)(nil)
var _ driver.Connector = (*Connector)(nil)

var _ io.Closer = (*closerConnector)(nil)
var _ driver.Connector = (*closerConnector)(nil)

type closerConnector struct {
	// the result of Close() method
	errClose error

	// a flag whether Close() method is called
	closed bool
}

func (c *closerConnector) Connect(ctx context.Context) (driver.Conn, error) {
	panic("never used")
}

func (c *closerConnector) Driver() driver.Driver {
	return fdriverctx
}

func (c *closerConnector) Close() error {
	c.closed = true
	return c.errClose
}

func TestConnectorClose(t *testing.T) {
	t.Run("c.Connector doesn't implement io.Closer", func(t *testing.T) {
		c0 := &fakeConnector{
			driver: &fakeDriverCtx{},
		}
		c1 := NewConnector(c0)
		if err := c1.(io.Closer).Close(); err != nil {
			t.Fatal(err)
		}
	})

	t.Run("Closing c.Connector succeeds", func(t *testing.T) {
		c0 := &closerConnector{}
		c1 := NewConnector(c0)
		if err := c1.(io.Closer).Close(); err != nil {
			t.Fatal(err)
		}
		if !c0.closed {
			t.Errorf("c.Connector should be closed, but not")
		}
	})

	t.Run("Closing c.Connector fails", func(t *testing.T) {
		errClose := errors.New("some error while closing")
		c0 := &closerConnector{
			errClose: errClose,
		}
		c1 := NewConnector(c0)
		if err := c1.(io.Closer).Close(); err != errClose {
			t.Errorf("want err is %v, got %v", errClose, err)
		}
		if !c0.closed {
			t.Errorf("c.Connector should be closed, but not")
		}
	})
}
<|endoftext|>


<commit_before>package game

import (
	"fmt"
	"log"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/rpg2d"
	"github.com/ghthor/engine/rpg2d/entity"
)

// Used to determine the next type that's in the
// buffer so we can decode it into a real value.
// We'll decode an encoded type and switch on its
// value so we'll have the correct value to decode
// into.
type EncodedType int

//go:generate stringer -type=EncodedType
const (
	ET_ERROR EncodedType = iota
	ET_DISCONNECT

	ET_REQ_LOGIN
	ET_REQ_CREATE

	ET_RESP_ACTOR_ALREADY_CONNECTED
	ET_RESP_AUTH_FAILED
	ET_RESP_ACTOR_EXISTS
	ET_RESP_ACTOR_DOESNT_EXIST

	ET_RESP_LOGIN_SUCCESS
	ET_RESP_CREATE_SUCCESS

	ET_REQ_CONNECT
	ET_CONNECTED

	ET_WORLD_STATE
	ET_WORLD_STATE_DIFF

	ET_REQ_MOVE
	ET_REQ_USE
	ET_REQ_CHAT
)

type Conn interface {
	EncodeAndSend(EncodedType, interface{}) error
	ReadNextType() (EncodedType, error)
	Decode(interface{}) error
}

type InputReceiver interface {
	SubmitMoveRequest(MoveRequest)
	SubmitUseRequest(UseRequest)
	SubmitChatRequest(ChatRequest)

	Close()
}

type PreLoginConn interface {
	HandleLogin() (LoggedInConn, error)
}

type ActorConnector func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)

type LoggedInConn interface {
	HandleConnect(ActorConnector) (ConnectedActorConn, error)
}

type ConnectedActorConn interface {
	HandleIO() error
}

type preLoginConn struct {
	Conn
	datastore datastore.Datastore
}

type preLoginResult struct {
	preLoginConn

	// result set by handling packets
	loggedInActor datastore.Actor
}

type stateFn func() (stateFn, error)

func (c *preLoginResult) handleLogin() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	case ET_REQ_LOGIN:
		return c.handleLoginReq, nil
	case ET_REQ_CREATE:
		return c.handleCreateReq, nil

	default:
		log.Println("unexpected encoded type: ", eType)
	}

	return c.handleLogin, nil
}

func (c *preLoginResult) handleLoginReq() (stateFn, error) {
	var r ReqLogin
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_DOESNT_EXIST, RespActorDoesntExist{
			r.Name, r.Password,
		})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	if !actor.CanBeConnected() {
		err := c.EncodeAndSend(ET_RESP_ACTOR_ALREADY_CONNECTED, RespActorAlreadyConnected{actor.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		err := c.EncodeAndSend(ET_RESP_AUTH_FAILED, RespAuthFailed{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	c.loggedInActor = actor

	err = c.EncodeAndSend(ET_RESP_LOGIN_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (c *preLoginResult) handleCreateReq() (stateFn, error) {
	var r ReqCreate
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_EXISTS, RespActorExists{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		//      we should retry contacting the database a
		//      few times
		return nil, err
	}

	c.loggedInActor = actor

	err = c.EncodeAndSend(ET_RESP_CREATE_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (c preLoginConn) HandleLogin() (LoggedInConn, error) {
	var err error
	result := preLoginResult{preLoginConn: c}

	f := result.handleLogin
	for f != nil && err == nil {
		f, err = f()
	}

	if err != nil {
		return nil, err
	}

	return loggedInConn{
		Conn:          c.Conn,
		loggedInActor: result.loggedInActor,
	}, nil
}

type loggedInConn struct {
	Conn
	loggedInActor datastore.Actor
}

type loggedInResult struct {
	loggedInConn

	connectedConn connectedConn
}

type initialStateWriter struct {
	sendState  chan<- rpg2d.WorldState
	diffWriter <-chan DiffWriter
}

func (c initialStateWriter) WriteWorldState(s rpg2d.WorldState) DiffWriter {
	// Pass state out to connection to be written
	c.sendState <- s
	// Only 1 world state will ever be written
	close(c.sendState)

	// Return a state writer to the muxer
	return <-c.diffWriter
}

func (c loggedInResult) connect(connectActor ActorConnector) (InputReceiver, entity.State, <-chan rpg2d.WorldState, chan<- DiffWriter) {
	initialStateCh := make(chan rpg2d.WorldState)
	diffWriterCh := make(chan DiffWriter)

	actor, entity := connectActor(c.loggedInActor, initialStateWriter{
		sendState:  initialStateCh,
		diffWriter: diffWriterCh,
	})
	return actor, entity, initialStateCh, diffWriterCh
}

func (c *loggedInResult) handleConnect(connectActor ActorConnector) stateFn {
	var handleConnect stateFn

	handleConnect = func() (stateFn, error) {
		eType, err := c.ReadNextType()
		if err != nil {
			return nil, err
		}

		switch eType {
		default:
			// TODO Send a protocol error to the client
			return handleConnect, nil

		case ET_REQ_CONNECT:
		}

		var r ReqConnect
		err = c.Decode(&r)
		if err != nil {
			return nil, err
		}

		if <-c.loggedInActor.IsConnected {
			c.loggedInActor.IsConnected <- true

			err := c.EncodeAndSend(ET_RESP_ACTOR_ALREADY_CONNECTED, RespActorAlreadyConnected{c.loggedInActor.Name})
			if err != nil {
				return nil, err
			}

			return handleConnect, nil
		}

		actor, entity, initialState, diffWriter := c.connect(connectActor)

		err = c.EncodeAndSend(ET_CONNECTED, entity)
		if err != nil {
			return nil, err
		}

		err = c.EncodeAndSend(ET_WORLD_STATE, <-initialState)
		if err != nil {
			return nil, err
		}

		c.loggedInActor.IsConnected <- true

		c.connectedConn = connectedConn{
			Conn:           c.Conn,
			connectedActor: c.loggedInActor,
			actor:          actor,
		}

		diffWriter <- c.connectedConn

		return nil, nil
	}

	return handleConnect
}

func (c loggedInConn) HandleConnect(connectActor ActorConnector) (ConnectedActorConn, error) {
	var err error
	result := loggedInResult{loggedInConn: c}

	f := result.handleConnect(connectActor)
	for f != nil && err == nil {
		f, err = f()
	}

	if err != nil {
		return nil, err
	}

	return result.connectedConn, nil
}

type connectedConn struct {
	Conn

	connectedActor datastore.Actor

	actor InputReceiver
}

func (c *connectedConn) handleInputReq() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	default:
		return c.handleInputReq, fmt.Errorf("unexpected type %v when handling input", eType)

	case ET_REQ_MOVE:
		return c.handleMoveReq, nil
	case ET_REQ_USE:
		return c.handleUseReq, nil
	case ET_REQ_CHAT:
		return c.handleChatReq, nil
	}

	return c.handleInputReq, nil
}

func (c *connectedConn) handleMoveReq() (stateFn, error) {
	var r MoveRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitMoveRequest(r)
	return c.handleInputReq, nil
}

func (c *connectedConn) handleUseReq() (stateFn, error) {
	var r UseRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitUseRequest(r)
	return c.handleInputReq, nil
}

func (c *connectedConn) handleChatReq() (stateFn, error) {
	var r ChatRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitChatRequest(r)
	return c.handleInputReq, nil
}

func (c connectedConn) WriteWorldStateDiff(s rpg2d.WorldStateDiff) {
	// TODO Handle this potentional write error
	c.EncodeAndSend(ET_WORLD_STATE_DIFF, s)
}

func (c connectedConn) HandleIO() (err error) {
	f := c.handleInputReq
	for f != nil && err == nil {
		f, err = f()
	}

	c.actor.Close()

	if <-c.connectedActor.IsConnected {
		c.connectedActor.IsConnected <- false
	}

	return
}

func NewPreLoginConn(conn Conn, ds datastore.Datastore) PreLoginConn {
	return preLoginConn{
		Conn:      conn,
		datastore: ds,
	}
}

func RunServer(loginConn PreLoginConn, actorConnector ActorConnector) error {
	loggedInConn, err := loginConn.HandleLogin()
	if err != nil {
		return err
	}

	connectedConn, err := loggedInConn.HandleConnect(actorConnector)
	if err != nil {
		return err
	}

	return connectedConn.HandleIO()
}
<commit_msg>[game] document that this field is set by the execution of the state machine<commit_after>package game

import (
	"fmt"
	"log"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/rpg2d"
	"github.com/ghthor/engine/rpg2d/entity"
)

// Used to determine the next type that's in the
// buffer so we can decode it into a real value.
// We'll decode an encoded type and switch on its
// value so we'll have the correct value to decode
// into.
type EncodedType int

//go:generate stringer -type=EncodedType
const (
	ET_ERROR EncodedType = iota
	ET_DISCONNECT

	ET_REQ_LOGIN
	ET_REQ_CREATE

	ET_RESP_ACTOR_ALREADY_CONNECTED
	ET_RESP_AUTH_FAILED
	ET_RESP_ACTOR_EXISTS
	ET_RESP_ACTOR_DOESNT_EXIST

	ET_RESP_LOGIN_SUCCESS
	ET_RESP_CREATE_SUCCESS

	ET_REQ_CONNECT
	ET_CONNECTED

	ET_WORLD_STATE
	ET_WORLD_STATE_DIFF

	ET_REQ_MOVE
	ET_REQ_USE
	ET_REQ_CHAT
)

type Conn interface {
	EncodeAndSend(EncodedType, interface{}) error
	ReadNextType() (EncodedType, error)
	Decode(interface{}) error
}

type InputReceiver interface {
	SubmitMoveRequest(MoveRequest)
	SubmitUseRequest(UseRequest)
	SubmitChatRequest(ChatRequest)

	Close()
}

type PreLoginConn interface {
	HandleLogin() (LoggedInConn, error)
}

type ActorConnector func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)

type LoggedInConn interface {
	HandleConnect(ActorConnector) (ConnectedActorConn, error)
}

type ConnectedActorConn interface {
	HandleIO() error
}

type preLoginConn struct {
	Conn
	datastore datastore.Datastore
}

type preLoginResult struct {
	preLoginConn

	// result set by handling packets
	loggedInActor datastore.Actor
}

type stateFn func() (stateFn, error)

func (c *preLoginResult) handleLogin() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	case ET_REQ_LOGIN:
		return c.handleLoginReq, nil
	case ET_REQ_CREATE:
		return c.handleCreateReq, nil

	default:
		log.Println("unexpected encoded type: ", eType)
	}

	return c.handleLogin, nil
}

func (c *preLoginResult) handleLoginReq() (stateFn, error) {
	var r ReqLogin
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_DOESNT_EXIST, RespActorDoesntExist{
			r.Name, r.Password,
		})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	if !actor.CanBeConnected() {
		err := c.EncodeAndSend(ET_RESP_ACTOR_ALREADY_CONNECTED, RespActorAlreadyConnected{actor.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		err := c.EncodeAndSend(ET_RESP_AUTH_FAILED, RespAuthFailed{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	c.loggedInActor = actor

	err = c.EncodeAndSend(ET_RESP_LOGIN_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (c *preLoginResult) handleCreateReq() (stateFn, error) {
	var r ReqCreate
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_EXISTS, RespActorExists{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		//      we should retry contacting the database a
		//      few times
		return nil, err
	}

	c.loggedInActor = actor

	err = c.EncodeAndSend(ET_RESP_CREATE_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (c preLoginConn) HandleLogin() (LoggedInConn, error) {
	var err error
	result := preLoginResult{preLoginConn: c}

	f := result.handleLogin
	for f != nil && err == nil {
		f, err = f()
	}

	if err != nil {
		return nil, err
	}

	return loggedInConn{
		Conn:          c.Conn,
		loggedInActor: result.loggedInActor,
	}, nil
}

type loggedInConn struct {
	Conn
	loggedInActor datastore.Actor
}

type loggedInResult struct {
	loggedInConn

	// result set by connecting
	connectedConn connectedConn
}

type initialStateWriter struct {
	sendState  chan<- rpg2d.WorldState
	diffWriter <-chan DiffWriter
}

func (c initialStateWriter) WriteWorldState(s rpg2d.WorldState) DiffWriter {
	// Pass state out to connection to be written
	c.sendState <- s
	// Only 1 world state will ever be written
	close(c.sendState)

	// Return a state writer to the muxer
	return <-c.diffWriter
}

func (c loggedInResult) connect(connectActor ActorConnector) (InputReceiver, entity.State, <-chan rpg2d.WorldState, chan<- DiffWriter) {
	initialStateCh := make(chan rpg2d.WorldState)
	diffWriterCh := make(chan DiffWriter)

	actor, entity := connectActor(c.loggedInActor, initialStateWriter{
		sendState:  initialStateCh,
		diffWriter: diffWriterCh,
	})
	return actor, entity, initialStateCh, diffWriterCh
}

func (c *loggedInResult) handleConnect(connectActor ActorConnector) stateFn {
	var handleConnect stateFn

	handleConnect = func() (stateFn, error) {
		eType, err := c.ReadNextType()
		if err != nil {
			return nil, err
		}

		switch eType {
		default:
			// TODO Send a protocol error to the client
			return handleConnect, nil

		case ET_REQ_CONNECT:
		}

		var r ReqConnect
		err = c.Decode(&r)
		if err != nil {
			return nil, err
		}

		if <-c.loggedInActor.IsConnected {
			c.loggedInActor.IsConnected <- true

			err := c.EncodeAndSend(ET_RESP_ACTOR_ALREADY_CONNECTED, RespActorAlreadyConnected{c.loggedInActor.Name})
			if err != nil {
				return nil, err
			}

			return handleConnect, nil
		}

		actor, entity, initialState, diffWriter := c.connect(connectActor)

		err = c.EncodeAndSend(ET_CONNECTED, entity)
		if err != nil {
			return nil, err
		}

		err = c.EncodeAndSend(ET_WORLD_STATE, <-initialState)
		if err != nil {
			return nil, err
		}

		c.loggedInActor.IsConnected <- true

		c.connectedConn = connectedConn{
			Conn:           c.Conn,
			connectedActor: c.loggedInActor,
			actor:          actor,
		}

		diffWriter <- c.connectedConn

		return nil, nil
	}

	return handleConnect
}

func (c loggedInConn) HandleConnect(connectActor ActorConnector) (ConnectedActorConn, error) {
	var err error
	result := loggedInResult{loggedInConn: c}

	f := result.handleConnect(connectActor)
	for f != nil && err == nil {
		f, err = f()
	}

	if err != nil {
		return nil, err
	}

	return result.connectedConn, nil
}

type connectedConn struct {
	Conn

	connectedActor datastore.Actor

	actor InputReceiver
}

func (c *connectedConn) handleInputReq() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	default:
		return c.handleInputReq, fmt.Errorf("unexpected type %v when handling input", eType)

	case ET_REQ_MOVE:
		return c.handleMoveReq, nil
	case ET_REQ_USE:
		return c.handleUseReq, nil
	case ET_REQ_CHAT:
		return c.handleChatReq, nil
	}

	return c.handleInputReq, nil
}

func (c *connectedConn) handleMoveReq() (stateFn, error) {
	var r MoveRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitMoveRequest(r)
	return c.handleInputReq, nil
}

func (c *connectedConn) handleUseReq() (stateFn, error) {
	var r UseRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitUseRequest(r)
	return c.handleInputReq, nil
}

func (c *connectedConn) handleChatReq() (stateFn, error) {
	var r ChatRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitChatRequest(r)
	return c.handleInputReq, nil
}

func (c connectedConn) WriteWorldStateDiff(s rpg2d.WorldStateDiff) {
	// TODO Handle this potentional write error
	c.EncodeAndSend(ET_WORLD_STATE_DIFF, s)
}

func (c connectedConn) HandleIO() (err error) {
	f := c.handleInputReq
	for f != nil && err == nil {
		f, err = f()
	}

	c.actor.Close()

	if <-c.connectedActor.IsConnected {
		c.connectedActor.IsConnected <- false
	}

	return
}

func NewPreLoginConn(conn Conn, ds datastore.Datastore) PreLoginConn {
	return preLoginConn{
		Conn:      conn,
		datastore: ds,
	}
}

func RunServer(loginConn PreLoginConn, actorConnector ActorConnector) error {
	loggedInConn, err := loginConn.HandleLogin()
	if err != nil {
		return err
	}

	connectedConn, err := loggedInConn.HandleConnect(actorConnector)
	if err != nil {
		return err
	}

	return connectedConn.HandleIO()
}
<|endoftext|>


<commit_before>// Package couch provides a CouchDB API.
package couch

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/dustin/httputil"
)

var defaultHdrs = map[string][]string{}

// HTTP Client used by typical requests.
//
// Defaults to http.DefaultClient
var HTTPClient *http.Client

func init() {
	if len(os.Getenv("GO_COUCH_DISABLE_CERT_CHECK")) > 0 {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		HTTPClient = &http.Client{Transport: tr}
	} else {
		HTTPClient = http.DefaultClient
	}
}

func createReq(u string) (*http.Request, error) {
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	if req.URL.User != nil {
		if p, hasp := req.URL.User.Password(); hasp {
			req.SetBasicAuth(req.URL.User.Username(), p)
		}
	}
	return req, nil
}

func unmarshalURL(u string, results interface{}) error {
	req, err := createReq(u)
	if err != nil {
		return err
	}

	r, err := HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	if r.StatusCode != 200 {
		return httputil.HTTPError(r)
	}

	return json.NewDecoder(r.Body).Decode(results)
}

type idAndRev struct {
	ID  string `json:"_id"`
	Rev string `json:"_rev"`
}

// Sends a query to CouchDB and parses the response back.
// method: the name of the HTTP method (POST, PUT,...)
// url: the URL to interact with
// headers: additional headers to pass to the request
// in: body of the request
// out: a structure to fill in with the returned JSON document
func interact(method, u string, headers map[string][]string, in []byte, out interface{}) (int, error) {
	fullHeaders := map[string][]string{}
	for k, v := range headers {
		fullHeaders[k] = v
	}
	if in != nil {
		fullHeaders["Content-Type"] = []string{"application/json"}
	}

	req, err := http.NewRequest(method, u, bytes.NewReader(in))
	if err != nil {
		return 0, err
	}

	req.ContentLength = int64(len(in))
	req.Header = fullHeaders
	req.Close = true

	res, err := HTTPClient.Do(req)
	if err != nil {
		return 0, err
	}
	defer res.Body.Close()

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return res.StatusCode, httputil.HTTPError(res)
	}
	return res.StatusCode, json.NewDecoder(res.Body).Decode(out)
}

// Database represents operations available on an existing CouchDB
type Database struct {
	Scheme   string
	Host     string
	Port     string
	Name     string
	authinfo *url.Userinfo

	changesDialer    func(string, string) (net.Conn, error)
	changesFailDelay time.Duration
}

// BaseURL returns the URL to the database server containing this database.
func (p Database) BaseURL() string {
	var scheme string
	if p.Scheme == "" {
		scheme = "http"
	} else {
		scheme = p.Scheme
	}
	if p.authinfo == nil {
		return fmt.Sprintf("%s://%s:%s", scheme, p.Host, p.Port)
	}
	return fmt.Sprintf("%s://%s@%s:%s", scheme, p.authinfo.String(), p.Host, p.Port)
}

// DBURL returns the URL to this specific database.
func (p Database) DBURL() string {
	return fmt.Sprintf("%s/%s", p.BaseURL(), p.Name)
}

// Running returns true if CouchDB is running (ignores Database.Name)
func (p Database) Running() bool {
	var welcomeJson map[string]interface{}
	u := fmt.Sprintf("%s", p.BaseURL())
	err := unmarshalURL(u, &welcomeJson)
	if err != nil {
		log.Printf("unmarshalURL failed: %s", err.Error())
		return false
	}
	_, ok := welcomeJson["version"]
	return ok
}

type databaseInfo struct {
	DBName string `json:"db_name"`
	// other stuff too, ignore for now
}

// Exists returns true if this database exists on the CouchDB server
func (p Database) Exists() bool {
	di := &databaseInfo{}
	return unmarshalURL(p.DBURL(), &di) == nil && di.DBName == p.Name
}

func (p Database) simpleOp(method, url string, nokerr error) error {
	ir := Response{}
	if _, err := interact(method, url, defaultHdrs, nil, &ir); err != nil {
		return err
	}
	if !ir.Ok {
		return nokerr
	}
	return nil
}

var (
	errNewDB = errors.New("create database operation returned not-OK")
	errDelDB = errors.New("delete database operation returned not-OK")
)

func (p Database) createDatabase() error {
	return p.simpleOp("PUT", p.DBURL(), errNewDB)
}

// DeleteDatabase deletes the given database and all documents
func (p Database) DeleteDatabase() error {
	return p.simpleOp("DELETE", p.DBURL(), errDelDB)
}

var errNotRunning = errors.New("couchdb not running")

// Connect to the database at the given URL.
// example:   couch.Connect("http://localhost:5984/testdb/")
func Connect(dburl string) (Database, error) {
	u, err := url.Parse(dburl)
	if err != nil {
		return Database{}, err
	}

	host := u.Host
	port := "80"
	if hp := strings.Split(u.Host, ":"); len(hp) > 1 {
		host = hp[0]
		port = hp[1]
	}

	db := Database{
		Scheme:           u.Scheme,
		Host:             host,
		Port:             port,
		Name:             u.Path[1:],
		authinfo:         u.User,
		changesDialer:    net.Dial,
		changesFailDelay: defaultChangeDelay,
	}
	if !db.Running() {
		return Database{}, errNotRunning
	}
	if !db.Exists() {
		return Database{}, errors.New("database does not exist")
	}

	return db, nil
}

// NewDatabase connects to a CouchDB server and creates the specified
// database if it does not exist.
func NewDatabase(host, port, name string) (Database, error) {
	db := Database{"http", host, port, name, nil, net.Dial, defaultChangeDelay}
	if !db.Running() {
		return db, errNotRunning
	}
	if !db.Exists() {
		if err := db.createDatabase(); err != nil {
			return db, err
		}
	}
	return db, nil
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

// Strip _id and _rev from d, returning them separately if they exist
func cleanJSON(d interface{}) (jsonBuf []byte, id, rev string, err error) {
	jsonBuf, err = json.Marshal(d)
	if err != nil {
		return
	}
	m := map[string]interface{}{}
	must(json.Unmarshal(jsonBuf, &m))
	id, _ = m["_id"].(string)
	delete(m, "_id")
	rev, _ = m["_rev"].(string)
	delete(m, "_rev")
	jsonBuf, err = json.Marshal(m)
	return
}

// Response represents a typical command response from against a CouchDB server.
type Response struct {
	Ok     bool
	ID     string
	Rev    string
	Error  string
	Reason string
}

// Bulk modification interface.
// Each item should be JSON serializable into a valid document.
// "_id" and "_rev" will be honored.
// To delete, add a "_deleted" field with a value of "true" as well
// as a valid "_rev" field.
func (p Database) Bulk(docs []interface{}) (results []Response, err error) {
	m := map[string]interface{}{}
	m["docs"] = docs
	var jsonBuf []byte
	jsonBuf, err = json.Marshal(m)
	if err != nil {
		return
	}

	results = make([]Response, 0, len(docs))
	_, err = interact("POST", p.DBURL()+"/_bulk_docs", defaultHdrs, jsonBuf, &results)
	return
}

// Insert a document into CouchDB, returning id and rev on success.
// Document may specify both "_id" and "_rev" fields (will overwrite existing)
//	or just "_id" (will use that id, but not overwrite existing)
//	or neither (will use autogenerated id)
func (p Database) Insert(d interface{}) (string, string, error) {
	jsonBuf, id, rev, err := cleanJSON(d)
	if err != nil {
		return "", "", err
	}
	if id != "" && rev != "" {
		newRev, err2 := p.Edit(d)
		return id, newRev, err2
	} else if id != "" {
		return p.insertWith(jsonBuf, id)
	} else {
		return p.insert(jsonBuf)
	}
}

// Private implementation of simple autogenerated-id insert
func (p Database) insert(jsonBuf []byte) (string, string, error) {
	ir := Response{}

	// workaround bug where inserting fails be because it's missing
	// a trailing slash in the POST request
	workaroundUrl := fmt.Sprintf("%s/", p.DBURL())

	if _, err := interact("POST", workaroundUrl, defaultHdrs, jsonBuf, &ir); err != nil {
		return "", "", err
	}
	if !ir.Ok {
		return "", "", fmt.Errorf("%s: %s", ir.Error, ir.Reason)
	}
	return ir.ID, ir.Rev, nil
}

// InsertWith inserts the given document (shouldn't contain "_id" or
// "_rev" tagged fields) using the passed 'id' as the _id. Will fail
// if the id already exists.
func (p Database) InsertWith(d interface{}, id string) (string, string, error) {
	jsonBuf, err := json.Marshal(d)
	if err != nil {
		return "", "", err
	}
	return p.insertWith(jsonBuf, id)
}

// Private implementation of insert with given id
func (p Database) insertWith(jsonBuf []byte, id string) (string, string, error) {
	u := fmt.Sprintf("%s/%s", p.DBURL(), url.QueryEscape(id))
	ir := Response{}
	if _, err := interact("PUT", u, defaultHdrs, jsonBuf, &ir); err != nil {
		return "", "", err
	}
	if !ir.Ok {
		return "", "", fmt.Errorf("%s: %s", ir.Error, ir.Reason)
	}
	return ir.ID, ir.Rev, nil
}

var errNoRev = errors.New("rev not specified in interface (try InsertWith)")

// Edit edits the given document, returning the new revision.
// d must contain "_id" and "_rev" tagged fields.
func (p Database) Edit(d interface{}) (string, error) {
	jsonBuf, err := json.Marshal(d)
	if err != nil {
		return "", err
	}
	idRev := idAndRev{}
	must(json.Unmarshal(jsonBuf, &idRev))
	if idRev.ID == "" {
		return "", errNoID
	}
	if idRev.Rev == "" {
		return "", errNoRev
	}
	u := fmt.Sprintf("%s/%s", p.DBURL(), url.QueryEscape(idRev.ID))
	ir := Response{}
	if _, err = interact("PUT", u, defaultHdrs, jsonBuf, &ir); err != nil {
		return "", err
	}
	return ir.Rev, nil
}

// EditWith edits the given document, returning the new revision.
// d should not contain "_id" or "_rev" tagged fields. If it does, they will
// be overwritten with the passed values.
func (p Database) EditWith(d interface{}, id, rev string) (string, error) {
	if id == "" {
		return "", errNoID
	}
	if rev == "" {
		return "", errNoRev
	}
	jsonBuf, err := json.Marshal(d)
	if err != nil {
		return "", err
	}
	m := map[string]interface{}{}
	must(json.Unmarshal(jsonBuf, &m))
	m["_id"] = id
	m["_rev"] = rev
	return p.Edit(m)
}

var errNoID = errors.New("no id specified")

// Retrieve unmarshals the document matching id to the given interface
func (p Database) Retrieve(id string, d interface{}) error {
	if id == "" {
		return errNoID
	}

	return unmarshalURL(fmt.Sprintf("%s/%s", p.DBURL(), id), d)
}

// Delete deletes document given by id and rev.
func (p Database) Delete(id, rev string) error {
	headers := map[string][]string{
		"If-Match": []string{rev},
	}
	u := fmt.Sprintf("%s/%s", p.DBURL(), id)
	ir := Response{}
	if _, err := interact("DELETE", u, headers, nil, &ir); err != nil {
		return err
	}
	if !ir.Ok {
		return fmt.Errorf("%s: %s", ir.Error, ir.Reason)
	}
	return nil
}

// DBInfo represents the result from GetInfo
type DBInfo struct {
	Name        string `json:"db_name"`
	DocCount    int64  `json:"doc_count"`
	DocDelCount int64  `json:"doc_del_count"`
	UpdateSeq   int64  `json:"update_seq"`
	PurgeSeq    int64  `json:"purge_seq"`
	Compacting  bool   `json:"compact_running"`
	DiskSize    int64  `json:"disk_size"`
	DataSize    int64  `json:"data_size"`
	StartTime   string `json:"instance_start_time"`
	Version     int    `json:"disk_format_version"`
	CommitedSeq int64  `json:"committed_update_seq"`
}

// GetInfo gets the DBInfo for this database.
func (p Database) GetInfo() (DBInfo, error) {
	rv := DBInfo{}
	err := unmarshalURL(p.DBURL(), &rv)
	return rv, err
}
<commit_msg>feat: retrieve attachments by name<commit_after>// Package couch provides a CouchDB API.
package couch

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/dustin/httputil"
)

var defaultHdrs = map[string][]string{}

// HTTP Client used by typical requests.
//
// Defaults to http.DefaultClient
var HTTPClient *http.Client

func init() {
	if len(os.Getenv("GO_COUCH_DISABLE_CERT_CHECK")) > 0 {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		HTTPClient = &http.Client{Transport: tr}
	} else {
		HTTPClient = http.DefaultClient
	}
}

func createReq(u string) (*http.Request, error) {
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	if req.URL.User != nil {
		if p, hasp := req.URL.User.Password(); hasp {
			req.SetBasicAuth(req.URL.User.Username(), p)
		}
	}
	return req, nil
}

func readURL(u string) ([]byte, error) {
	req, err := createReq(u)
	if err != nil {
		return nil, err
	}

	r, err := HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()

	if r.StatusCode != 200 {
		return nil, httputil.HTTPError(r)
	}
	return ioutil.ReadAll(r.Body)
}

func unmarshalURL(u string, results interface{}) error {
	content, err := readURL(u)
	if err != nil {
		return err
	}
	return json.NewDecoder(bytes.NewBuffer(content)).Decode(results)
}

type idAndRev struct {
	ID  string `json:"_id"`
	Rev string `json:"_rev"`
}

// Sends a query to CouchDB and parses the response back.
// method: the name of the HTTP method (POST, PUT,...)
// url: the URL to interact with
// headers: additional headers to pass to the request
// in: body of the request
// out: a structure to fill in with the returned JSON document
func interact(method, u string, headers map[string][]string, in []byte, out interface{}) (int, error) {
	fullHeaders := map[string][]string{}
	for k, v := range headers {
		fullHeaders[k] = v
	}
	if in != nil {
		fullHeaders["Content-Type"] = []string{"application/json"}
	}

	req, err := http.NewRequest(method, u, bytes.NewReader(in))
	if err != nil {
		return 0, err
	}

	req.ContentLength = int64(len(in))
	req.Header = fullHeaders
	req.Close = true

	res, err := HTTPClient.Do(req)
	if err != nil {
		return 0, err
	}
	defer res.Body.Close()

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return res.StatusCode, httputil.HTTPError(res)
	}
	return res.StatusCode, json.NewDecoder(res.Body).Decode(out)
}

// Database represents operations available on an existing CouchDB
type Database struct {
	Scheme   string
	Host     string
	Port     string
	Name     string
	authinfo *url.Userinfo

	changesDialer    func(string, string) (net.Conn, error)
	changesFailDelay time.Duration
}

// BaseURL returns the URL to the database server containing this database.
func (p Database) BaseURL() string {
	var scheme string
	if p.Scheme == "" {
		scheme = "http"
	} else {
		scheme = p.Scheme
	}
	if p.authinfo == nil {
		return fmt.Sprintf("%s://%s:%s", scheme, p.Host, p.Port)
	}
	return fmt.Sprintf("%s://%s@%s:%s", scheme, p.authinfo.String(), p.Host, p.Port)
}

// DBURL returns the URL to this specific database.
func (p Database) DBURL() string {
	return fmt.Sprintf("%s/%s", p.BaseURL(), p.Name)
}

// Running returns true if CouchDB is running (ignores Database.Name)
func (p Database) Running() bool {
	var welcomeJson map[string]interface{}
	u := fmt.Sprintf("%s", p.BaseURL())
	err := unmarshalURL(u, &welcomeJson)
	if err != nil {
		log.Printf("unmarshalURL failed: %s", err.Error())
		return false
	}
	_, ok := welcomeJson["version"]
	return ok
}

type databaseInfo struct {
	DBName string `json:"db_name"`
	// other stuff too, ignore for now
}

// Exists returns true if this database exists on the CouchDB server
func (p Database) Exists() bool {
	di := &databaseInfo{}
	return unmarshalURL(p.DBURL(), &di) == nil && di.DBName == p.Name
}

func (p Database) simpleOp(method, url string, nokerr error) error {
	ir := Response{}
	if _, err := interact(method, url, defaultHdrs, nil, &ir); err != nil {
		return err
	}
	if !ir.Ok {
		return nokerr
	}
	return nil
}

var (
	errNewDB = errors.New("create database operation returned not-OK")
	errDelDB = errors.New("delete database operation returned not-OK")
)

func (p Database) createDatabase() error {
	return p.simpleOp("PUT", p.DBURL(), errNewDB)
}

// DeleteDatabase deletes the given database and all documents
func (p Database) DeleteDatabase() error {
	return p.simpleOp("DELETE", p.DBURL(), errDelDB)
}

var errNotRunning = errors.New("couchdb not running")

// Connect to the database at the given URL.
// example:   couch.Connect("http://localhost:5984/testdb/")
func Connect(dburl string) (Database, error) {
	u, err := url.Parse(dburl)
	if err != nil {
		return Database{}, err
	}

	host := u.Host
	port := "80"
	if hp := strings.Split(u.Host, ":"); len(hp) > 1 {
		host = hp[0]
		port = hp[1]
	}

	db := Database{
		Scheme:           u.Scheme,
		Host:             host,
		Port:             port,
		Name:             u.Path[1:],
		authinfo:         u.User,
		changesDialer:    net.Dial,
		changesFailDelay: defaultChangeDelay,
	}
	if !db.Running() {
		return Database{}, errNotRunning
	}
	if !db.Exists() {
		return Database{}, errors.New("database does not exist")
	}

	return db, nil
}

// NewDatabase connects to a CouchDB server and creates the specified
// database if it does not exist.
func NewDatabase(host, port, name string) (Database, error) {
	db := Database{"http", host, port, name, nil, net.Dial, defaultChangeDelay}
	if !db.Running() {
		return db, errNotRunning
	}
	if !db.Exists() {
		if err := db.createDatabase(); err != nil {
			return db, err
		}
	}
	return db, nil
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

// Strip _id and _rev from d, returning them separately if they exist
func cleanJSON(d interface{}) (jsonBuf []byte, id, rev string, err error) {
	jsonBuf, err = json.Marshal(d)
	if err != nil {
		return
	}
	m := map[string]interface{}{}
	must(json.Unmarshal(jsonBuf, &m))
	id, _ = m["_id"].(string)
	delete(m, "_id")
	rev, _ = m["_rev"].(string)
	delete(m, "_rev")
	jsonBuf, err = json.Marshal(m)
	return
}

// Response represents a typical command response from against a CouchDB server.
type Response struct {
	Ok     bool
	ID     string
	Rev    string
	Error  string
	Reason string
}

// Bulk modification interface.
// Each item should be JSON serializable into a valid document.
// "_id" and "_rev" will be honored.
// To delete, add a "_deleted" field with a value of "true" as well
// as a valid "_rev" field.
func (p Database) Bulk(docs []interface{}) (results []Response, err error) {
	m := map[string]interface{}{}
	m["docs"] = docs
	var jsonBuf []byte
	jsonBuf, err = json.Marshal(m)
	if err != nil {
		return
	}

	results = make([]Response, 0, len(docs))
	_, err = interact("POST", p.DBURL()+"/_bulk_docs", defaultHdrs, jsonBuf, &results)
	return
}

// Insert a document into CouchDB, returning id and rev on success.
// Document may specify both "_id" and "_rev" fields (will overwrite existing)
//	or just "_id" (will use that id, but not overwrite existing)
//	or neither (will use autogenerated id)
func (p Database) Insert(d interface{}) (string, string, error) {
	jsonBuf, id, rev, err := cleanJSON(d)
	if err != nil {
		return "", "", err
	}
	if id != "" && rev != "" {
		newRev, err2 := p.Edit(d)
		return id, newRev, err2
	} else if id != "" {
		return p.insertWith(jsonBuf, id)
	} else {
		return p.insert(jsonBuf)
	}
}

// Private implementation of simple autogenerated-id insert
func (p Database) insert(jsonBuf []byte) (string, string, error) {
	ir := Response{}

	// workaround bug where inserting fails be because it's missing
	// a trailing slash in the POST request
	workaroundUrl := fmt.Sprintf("%s/", p.DBURL())

	if _, err := interact("POST", workaroundUrl, defaultHdrs, jsonBuf, &ir); err != nil {
		return "", "", err
	}
	if !ir.Ok {
		return "", "", fmt.Errorf("%s: %s", ir.Error, ir.Reason)
	}
	return ir.ID, ir.Rev, nil
}

// InsertWith inserts the given document (shouldn't contain "_id" or
// "_rev" tagged fields) using the passed 'id' as the _id. Will fail
// if the id already exists.
func (p Database) InsertWith(d interface{}, id string) (string, string, error) {
	jsonBuf, err := json.Marshal(d)
	if err != nil {
		return "", "", err
	}
	return p.insertWith(jsonBuf, id)
}

// Private implementation of insert with given id
func (p Database) insertWith(jsonBuf []byte, id string) (string, string, error) {
	u := fmt.Sprintf("%s/%s", p.DBURL(), url.QueryEscape(id))
	ir := Response{}
	if _, err := interact("PUT", u, defaultHdrs, jsonBuf, &ir); err != nil {
		return "", "", err
	}
	if !ir.Ok {
		return "", "", fmt.Errorf("%s: %s", ir.Error, ir.Reason)
	}
	return ir.ID, ir.Rev, nil
}

var errNoRev = errors.New("rev not specified in interface (try InsertWith)")

// Edit edits the given document, returning the new revision.
// d must contain "_id" and "_rev" tagged fields.
func (p Database) Edit(d interface{}) (string, error) {
	jsonBuf, err := json.Marshal(d)
	if err != nil {
		return "", err
	}
	idRev := idAndRev{}
	must(json.Unmarshal(jsonBuf, &idRev))
	if idRev.ID == "" {
		return "", errNoID
	}
	if idRev.Rev == "" {
		return "", errNoRev
	}
	u := fmt.Sprintf("%s/%s", p.DBURL(), url.QueryEscape(idRev.ID))
	ir := Response{}
	if _, err = interact("PUT", u, defaultHdrs, jsonBuf, &ir); err != nil {
		return "", err
	}
	return ir.Rev, nil
}

// EditWith edits the given document, returning the new revision.
// d should not contain "_id" or "_rev" tagged fields. If it does, they will
// be overwritten with the passed values.
func (p Database) EditWith(d interface{}, id, rev string) (string, error) {
	if id == "" {
		return "", errNoID
	}
	if rev == "" {
		return "", errNoRev
	}
	jsonBuf, err := json.Marshal(d)
	if err != nil {
		return "", err
	}
	m := map[string]interface{}{}
	must(json.Unmarshal(jsonBuf, &m))
	m["_id"] = id
	m["_rev"] = rev
	return p.Edit(m)
}

var errNoID = errors.New("no id specified")

// Retrieve unmarshals the document matching id to the given interface
func (p Database) Retrieve(id string, d interface{}) error {
	if id == "" {
		return errNoID
	}

	return unmarshalURL(fmt.Sprintf("%s/%s", p.DBURL(), id), d)
}

func (p Database) RetrieveAttachment(id string, name string) (io.Reader, error) {
	if id == "" {
		return nil, errNoID
	}
	content, err := readURL(fmt.Sprintf("%s/%s/%s", p.DBURL(), id, name))
	if err != nil {
		return nil, err
	}
	return bytes.NewBuffer(content), nil
}

// Delete deletes document given by id and rev.
func (p Database) Delete(id, rev string) error {
	headers := map[string][]string{
		"If-Match": []string{rev},
	}
	u := fmt.Sprintf("%s/%s", p.DBURL(), id)
	ir := Response{}
	if _, err := interact("DELETE", u, headers, nil, &ir); err != nil {
		return err
	}
	if !ir.Ok {
		return fmt.Errorf("%s: %s", ir.Error, ir.Reason)
	}
	return nil
}

// DBInfo represents the result from GetInfo
type DBInfo struct {
	Name        string `json:"db_name"`
	DocCount    int64  `json:"doc_count"`
	DocDelCount int64  `json:"doc_del_count"`
	UpdateSeq   int64  `json:"update_seq"`
	PurgeSeq    int64  `json:"purge_seq"`
	Compacting  bool   `json:"compact_running"`
	DiskSize    int64  `json:"disk_size"`
	DataSize    int64  `json:"data_size"`
	StartTime   string `json:"instance_start_time"`
	Version     int    `json:"disk_format_version"`
	CommitedSeq int64  `json:"committed_update_seq"`
}

// GetInfo gets the DBInfo for this database.
func (p Database) GetInfo() (DBInfo, error) {
	rv := DBInfo{}
	err := unmarshalURL(p.DBURL(), &rv)
	return rv, err
}
<|endoftext|>


<commit_before>//+build !go1.9

package jsoniter

import (
	"reflect"
	"sync"
)

type frozenConfig struct {
	configBeforeFrozen            Config
	sortMapKeys                   bool
	indentionStep                 int
	objectFieldMustBeSimpleString bool
	onlyTaggedField               bool
	disallowUnknownFields         bool
	cacheLock                     *sync.RWMutex
	decoderCache                  map[reflect2.Type]ValDecoder
	encoderCache                  map[reflect2.Type]ValEncoder
	extensions                    []Extension
	streamPool                    chan *Stream
	iteratorPool                  chan *Iterator
}

func (cfg *frozenConfig) initCache() {
	cfg.cacheLock = &sync.RWMutex{}
	cfg.decoderCache = map[reflect2.Type]ValDecoder{}
	cfg.encoderCache = map[reflect2.Type]ValEncoder{}
}

func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect2.Type, decoder ValDecoder) {
	cfg.cacheLock.Lock()
	cfg.decoderCache[cacheKey] = decoder
	cfg.cacheLock.Unlock()
}

func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect2.Type, encoder ValEncoder) {
	cfg.cacheLock.Lock()
	cfg.encoderCache[cacheKey] = encoder
	cfg.cacheLock.Unlock()
}

func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect2.Type) ValDecoder {
	cfg.cacheLock.RLock()
	decoder, _ := cfg.decoderCache[cacheKey].(ValDecoder)
	cfg.cacheLock.RUnlock()
	return decoder
}

func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect2.Type) ValEncoder {
	cfg.cacheLock.RLock()
	encoder, _ := cfg.encoderCache[cacheKey].(ValEncoder)
	cfg.cacheLock.RUnlock()
	return encoder
}

var cfgCacheLock = &sync.RWMutex{}
var cfgCache = map[Config]*frozenConfig{}

func getFrozenConfigFromCache(cfg Config) *frozenConfig {
	cfgCacheLock.RLock()
	frozenConfig := cfgCache[cfg]
	cfgCacheLock.RUnlock()
	return frozenConfig
}

func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
	cfgCacheLock.Lock()
	cfgCache[cfg] = frozenConfig
	cfgCacheLock.Unlock()
}
<commit_msg>fix 1.8<commit_after>//+build !go1.9

package jsoniter

import (
	"sync"
)

type frozenConfig struct {
	configBeforeFrozen            Config
	sortMapKeys                   bool
	indentionStep                 int
	objectFieldMustBeSimpleString bool
	onlyTaggedField               bool
	disallowUnknownFields         bool
	cacheLock                     *sync.RWMutex
	decoderCache                  map[uintptr]ValDecoder
	encoderCache                  map[uintptr]ValEncoder
	extensions                    []Extension
	streamPool                    chan *Stream
	iteratorPool                  chan *Iterator
}

func (cfg *frozenConfig) initCache() {
	cfg.cacheLock = &sync.RWMutex{}
	cfg.decoderCache = map[uintptr]ValDecoder{}
	cfg.encoderCache = map[uintptr]ValEncoder{}
}

func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
	cfg.cacheLock.Lock()
	cfg.decoderCache[cacheKey] = decoder
	cfg.cacheLock.Unlock()
}

func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
	cfg.cacheLock.Lock()
	cfg.encoderCache[cacheKey] = encoder
	cfg.cacheLock.Unlock()
}

func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
	cfg.cacheLock.RLock()
	decoder, _ := cfg.decoderCache[cacheKey].(ValDecoder)
	cfg.cacheLock.RUnlock()
	return decoder
}

func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
	cfg.cacheLock.RLock()
	encoder, _ := cfg.encoderCache[cacheKey].(ValEncoder)
	cfg.cacheLock.RUnlock()
	return encoder
}

var cfgCacheLock = &sync.RWMutex{}
var cfgCache = map[Config]*frozenConfig{}

func getFrozenConfigFromCache(cfg Config) *frozenConfig {
	cfgCacheLock.RLock()
	frozenConfig := cfgCache[cfg]
	cfgCacheLock.RUnlock()
	return frozenConfig
}

func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
	cfgCacheLock.Lock()
	cfgCache[cfg] = frozenConfig
	cfgCacheLock.Unlock()
}
<|endoftext|>