<commit_before>// query_benchmarker speed tests ElasticSearch using requests from stdin.
//
// It reads encoded Query objects from stdin, and makes concurrent requests
// to the provided HTTP endpoint. This program currently has no knowledge of
// the internals of the endpoint.
//
// TODO(rw): On my machine, this only decodes 700k/sec messages from stdin.
package main

import (
	"bufio"
	"encoding/gob"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime/pprof"
	"sort"
	"sync"
	"time"
)

// Program option vars:
var (
	daemonUrl            string
	workers              int
	debug                int
	prettyPrintResponses bool
	limit                int64
	burnIn               uint64
	printInterval        uint64
	memProfile           string
)

// Global vars:
var (
	queryPool    sync.Pool
	queryChan    chan *Query
	statPool     sync.Pool
	statChan     chan *Stat
	workersGroup sync.WaitGroup
	statGroup    sync.WaitGroup
)

// Parse args:
func init() {
	flag.StringVar(&daemonUrl, "url", "http://localhost:9200", "Daemon URL.")
	flag.IntVar(&workers, "workers", 1, "Number of concurrent requests to make.")
	flag.IntVar(&debug, "debug", 0, "Whether to print debug messages.")
	flag.Int64Var(&limit, "limit", -1, "Limit the number of queries to send.")
	flag.Uint64Var(&burnIn, "burn-in", 0, "Number of queries to ignore before collecting statistics.")
	flag.Uint64Var(&printInterval, "print-interval", 100, "Print timing stats to stderr after this many queries (0 to disable)")
	flag.BoolVar(&prettyPrintResponses, "print-responses", false, "Pretty print JSON response bodies (for correctness checking) (default false).")
	flag.StringVar(&memProfile, "memprofile", "", "Write a memory profile to this file.")

	flag.Parse()
}

func main() {
	// Make pools to minimize heap usage:
	queryPool = sync.Pool{
		New: func() interface{} {
			return &Query{
				HumanLabel:       make([]byte, 0, 1024),
				HumanDescription: make([]byte, 0, 1024),
				Method:           make([]byte, 0, 1024),
				Path:             make([]byte, 0, 1024),
				Body:             make([]byte, 0, 1024),
			}
		},
	}

	statPool = sync.Pool{
		New: func() interface{} {
			return &Stat{
				Label: make([]byte, 0, 1024),
				Value: 0.0,
			}
		},
	}

	// Make data and control channels:
	queryChan = make(chan *Query, workers)
	statChan = make(chan *Stat, workers)

	// Launch the stats processor:
	statGroup.Add(1)
	go processStats()

	// Launch the query processors:
	for i := 0; i < workers; i++ {
		workersGroup.Add(1)
		w := NewHTTPClient(daemonUrl, debug)
		go processQueries(w)
	}

	// Read in jobs, closing the job channel when done:
	input := bufio.NewReaderSize(os.Stdin, 1<<20)
	wallStart := time.Now()
	scan(input)
	close(queryChan)

	// Block for workers to finish sending requests, closing the stats
	// channel when done:
	workersGroup.Wait()
	close(statChan)

	// Wait on the stat collector to finish (and print its results):
	statGroup.Wait()

	wallEnd := time.Now()
	wallTook := wallEnd.Sub(wallStart)
	_, err := fmt.Printf("wall clock time: %fsec\n", float64(wallTook.Nanoseconds())/1e9)
	if err != nil {
		log.Fatal(err)
	}

	// (Optional) create a memory profile:
	if memProfile != "" {
		f, err := os.Create(memProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
	}
}

// scan reads encoded Queries and places them onto the workqueue.
func scan(r io.Reader) {
	dec := gob.NewDecoder(r)

	n := int64(0)
	for {
		if limit >= 0 && n >= limit {
			break
		}

		q := queryPool.Get().(*Query)
		err := dec.Decode(q)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}

		q.ID = n

		queryChan <- q

		n++

	}
}

// processQueries reads byte buffers from queryChan and writes them to the
// target server, while tracking latency.
func processQueries(w *HTTPClient) {
	opts := &HTTPClientDoOptions{
		Debug:                debug,
		PrettyPrintResponses: prettyPrintResponses,
	}
	for q := range queryChan {
		lag, err := w.Do(q, opts)

		stat := statPool.Get().(*Stat)
		stat.Init(q.HumanLabel, lag)
		statChan <- stat

		queryPool.Put(q)
		if err != nil {
			log.Fatalf("Error during request: %s\n", err.Error())
		}
	}
	workersGroup.Done()
}

// processStats collects latency results, aggregating them into summary
// statistics. Optionally, they are printed to stderr at regular intervals.
func processStats() {
	const allQueriesLabel = "all queries"
	statMapping := map[string]*StatGroup{
		allQueriesLabel: &StatGroup{},
	}

	i := uint64(0)
	for stat := range statChan {
		if i < burnIn {
			i++
			statPool.Put(stat)
			continue
		} else if i == burnIn && burnIn > 0 {
			_, err := fmt.Fprintf(os.Stderr, "burn-in complete after %d queries with %d workers\n", burnIn, workers)
			if err != nil {
				log.Fatal(err)
			}
		}

		if _, ok := statMapping[string(stat.Label)]; !ok {
			statMapping[string(stat.Label)] = &StatGroup{}
		}

		statMapping[allQueriesLabel].Push(stat.Value)
		statMapping[string(stat.Label)].Push(stat.Value)

		statPool.Put(stat)

		i++

		// print stats to stderr (if printInterval is greater than zero):
		if printInterval > 0 && i > 0 && i%printInterval == 0 && (int64(i) < limit || limit < 0) {
			_, err := fmt.Fprintf(os.Stderr, "after %d queries with %d workers:\n", i - burnIn, workers)
			if err != nil {
				log.Fatal(err)
			}
			fprintStats(os.Stderr, statMapping)
			_, err = fmt.Fprintf(os.Stderr, "\n")
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	// the final stats output goes to stdout:
	_, err := fmt.Printf("run complete after %d queries with %d workers:\n", i - burnIn, workers)
	if err != nil {
		log.Fatal(err)
	}
	fprintStats(os.Stdout, statMapping)
	statGroup.Done()
}

// fprintStats pretty-prints stats to the given writer.
func fprintStats(w io.Writer, statGroups map[string]*StatGroup) {
	maxKeyLength := 0
	keys := make([]string, 0, len(statGroups))
	for k := range statGroups {
		if len(k) > maxKeyLength {
			maxKeyLength = len(k)
		}
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := statGroups[k]
		minRate := 1e3 / v.Min
		meanRate := 1e3 / v.Mean
		maxRate := 1e3 / v.Max
		paddedKey := fmt.Sprintf("%s", k)
		for len(paddedKey) < maxKeyLength {
			paddedKey += " "
		}
		_, err := fmt.Fprintf(w, "%s : min: %8.2fms (%7.2f/sec), mean: %8.2fms (%7.2f/sec), max: %7.2fms (%6.2f/sec), count: %8d, sum: %5.1fsec \n", paddedKey, v.Min, minRate, v.Mean, meanRate, v.Max, maxRate, v.Count, v.Sum/1e3)
		if err != nil {
			log.Fatal(err)
		}
	}

}
<commit_msg>round-robin requests for es query benchmarker<commit_after>// query_benchmarker speed tests ElasticSearch using requests from stdin.
//
// It reads encoded Query objects from stdin, and makes concurrent requests
// to the provided HTTP endpoint. This program currently has no knowledge of
// the internals of the endpoint.
//
// TODO(rw): On my machine, this only decodes 700k/sec messages from stdin.
package main

import (
	"bufio"
	"encoding/gob"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime/pprof"
	"sort"
	"strings"
	"sync"
	"time"
)

// Program option vars:
var (
	csvDaemonUrls        string
	daemonUrls           []string
	workers              int
	debug                int
	prettyPrintResponses bool
	limit                int64
	burnIn               uint64
	printInterval        uint64
	memProfile           string
)

// Global vars:
var (
	queryPool    sync.Pool
	queryChan    chan *Query
	statPool     sync.Pool
	statChan     chan *Stat
	workersGroup sync.WaitGroup
	statGroup    sync.WaitGroup
)

// Parse args:
func init() {
	flag.StringVar(&csvDaemonUrls, "urls", "http://localhost:9200", "Daemon URLs, comma-separated. Will be used in a round-robin fashion.")
	flag.IntVar(&workers, "workers", 1, "Number of concurrent requests to make.")
	flag.IntVar(&debug, "debug", 0, "Whether to print debug messages.")
	flag.Int64Var(&limit, "limit", -1, "Limit the number of queries to send.")
	flag.Uint64Var(&burnIn, "burn-in", 0, "Number of queries to ignore before collecting statistics.")
	flag.Uint64Var(&printInterval, "print-interval", 100, "Print timing stats to stderr after this many queries (0 to disable)")
	flag.BoolVar(&prettyPrintResponses, "print-responses", false, "Pretty print JSON response bodies (for correctness checking) (default false).")
	flag.StringVar(&memProfile, "memprofile", "", "Write a memory profile to this file.")

	flag.Parse()

	daemonUrls = strings.Split(csvDaemonUrls, ",")
	if len(daemonUrls) == 0 {
		log.Fatal("missing 'urls' flag")
	}
	fmt.Printf("daemon URLs: %v\n", daemonUrls)
}

func main() {
	// Make pools to minimize heap usage:
	queryPool = sync.Pool{
		New: func() interface{} {
			return &Query{
				HumanLabel:       make([]byte, 0, 1024),
				HumanDescription: make([]byte, 0, 1024),
				Method:           make([]byte, 0, 1024),
				Path:             make([]byte, 0, 1024),
				Body:             make([]byte, 0, 1024),
			}
		},
	}

	statPool = sync.Pool{
		New: func() interface{} {
			return &Stat{
				Label: make([]byte, 0, 1024),
				Value: 0.0,
			}
		},
	}

	// Make data and control channels:
	queryChan = make(chan *Query, workers)
	statChan = make(chan *Stat, workers)

	// Launch the stats processor:
	statGroup.Add(1)
	go processStats()

	// Launch the query processors:
	for i := 0; i < workers; i++ {
		daemonUrl := daemonUrls[i%len(daemonUrls)]
		workersGroup.Add(1)
		w := NewHTTPClient(daemonUrl, debug)
		go processQueries(w)
	}

	// Read in jobs, closing the job channel when done:
	input := bufio.NewReaderSize(os.Stdin, 1<<20)
	wallStart := time.Now()
	scan(input)
	close(queryChan)

	// Block for workers to finish sending requests, closing the stats
	// channel when done:
	workersGroup.Wait()
	close(statChan)

	// Wait on the stat collector to finish (and print its results):
	statGroup.Wait()

	wallEnd := time.Now()
	wallTook := wallEnd.Sub(wallStart)
	_, err := fmt.Printf("wall clock time: %fsec\n", float64(wallTook.Nanoseconds())/1e9)
	if err != nil {
		log.Fatal(err)
	}

	// (Optional) create a memory profile:
	if memProfile != "" {
		f, err := os.Create(memProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
	}
}

// scan reads encoded Queries and places them onto the workqueue.
func scan(r io.Reader) {
	dec := gob.NewDecoder(r)

	n := int64(0)
	for {
		if limit >= 0 && n >= limit {
			break
		}

		q := queryPool.Get().(*Query)
		err := dec.Decode(q)
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}

		q.ID = n

		queryChan <- q

		n++

	}
}

// processQueries reads byte buffers from queryChan and writes them to the
// target server, while tracking latency.
func processQueries(w *HTTPClient) {
	opts := &HTTPClientDoOptions{
		Debug:                debug,
		PrettyPrintResponses: prettyPrintResponses,
	}
	for q := range queryChan {
		lag, err := w.Do(q, opts)

		stat := statPool.Get().(*Stat)
		stat.Init(q.HumanLabel, lag)
		statChan <- stat

		queryPool.Put(q)
		if err != nil {
			log.Fatalf("Error during request: %s\n", err.Error())
		}
	}
	workersGroup.Done()
}

// processStats collects latency results, aggregating them into summary
// statistics. Optionally, they are printed to stderr at regular intervals.
func processStats() {
	const allQueriesLabel = "all queries"
	statMapping := map[string]*StatGroup{
		allQueriesLabel: &StatGroup{},
	}

	i := uint64(0)
	for stat := range statChan {
		if i < burnIn {
			i++
			statPool.Put(stat)
			continue
		} else if i == burnIn && burnIn > 0 {
			_, err := fmt.Fprintf(os.Stderr, "burn-in complete after %d queries with %d workers\n", burnIn, workers)
			if err != nil {
				log.Fatal(err)
			}
		}

		if _, ok := statMapping[string(stat.Label)]; !ok {
			statMapping[string(stat.Label)] = &StatGroup{}
		}

		statMapping[allQueriesLabel].Push(stat.Value)
		statMapping[string(stat.Label)].Push(stat.Value)

		statPool.Put(stat)

		i++

		// print stats to stderr (if printInterval is greater than zero):
		if printInterval > 0 && i > 0 && i%printInterval == 0 && (int64(i) < limit || limit < 0) {
			_, err := fmt.Fprintf(os.Stderr, "after %d queries with %d workers:\n", i-burnIn, workers)
			if err != nil {
				log.Fatal(err)
			}
			fprintStats(os.Stderr, statMapping)
			_, err = fmt.Fprintf(os.Stderr, "\n")
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	// the final stats output goes to stdout:
	_, err := fmt.Printf("run complete after %d queries with %d workers:\n", i-burnIn, workers)
	if err != nil {
		log.Fatal(err)
	}
	fprintStats(os.Stdout, statMapping)
	statGroup.Done()
}

// fprintStats pretty-prints stats to the given writer.
func fprintStats(w io.Writer, statGroups map[string]*StatGroup) {
	maxKeyLength := 0
	keys := make([]string, 0, len(statGroups))
	for k := range statGroups {
		if len(k) > maxKeyLength {
			maxKeyLength = len(k)
		}
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := statGroups[k]
		minRate := 1e3 / v.Min
		meanRate := 1e3 / v.Mean
		maxRate := 1e3 / v.Max
		paddedKey := fmt.Sprintf("%s", k)
		for len(paddedKey) < maxKeyLength {
			paddedKey += " "
		}
		_, err := fmt.Fprintf(w, "%s : min: %8.2fms (%7.2f/sec), mean: %8.2fms (%7.2f/sec), max: %7.2fms (%6.2f/sec), count: %8d, sum: %5.1fsec \n", paddedKey, v.Min, minRate, v.Mean, meanRate, v.Max, maxRate, v.Count, v.Sum/1e3)
		if err != nil {
			log.Fatal(err)
		}
	}

}
<|endoftext|>


<commit_before>package client

import (
	"github.com/neptulon/cmap"
	"github.com/neptulon/neptulon"
	"github.com/neptulon/neptulon/middleware"
)

// Client is a Titan client.
type Client struct {
	ID           string     // Randomly generated unique client connection ID.
	Session      *cmap.CMap // Thread-safe data store for storing arbitrary data for this connection session.
	conn         *neptulon.Conn
	router       *middleware.Router
	inMsgHandler func(m []Message) error
}

// NewClient creates a new Client object.
func NewClient() (*Client, error) {
	c, err := neptulon.NewConn()
	if err != nil {
		return nil, err
	}
	r := middleware.NewRouter()
	c.Middleware(r)
	s := &Client{ID: c.ID, Session: c.Session, conn: c, router: r, inMsgHandler: func(m []Message) error { return nil }}
	r.Request("msg.recv", s.inMsgRoute)
	return s, nil
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
func (c *Client) SetDeadline(seconds int) {
	c.conn.SetDeadline(seconds)
}

// Connect connectes to the server at given network address and starts receiving messages.
func (c *Client) Connect(addr string) error {
	return c.conn.Connect(addr)
}

// Close closes a client connection.
func (c *Client) Close() error {
	return c.conn.Close()
}

func (c *Client) inMsgRoute(ctx *neptulon.ReqCtx) error {
	var msg []Message
	if err := ctx.Params(msg); err != nil {
		return err
	}

	if err := c.inMsgHandler(msg); err != nil {
		return err
	}

	return ctx.Next()
}
<commit_msg>inMsgRoute ACKs messages<commit_after>package client

import (
	"github.com/neptulon/cmap"
	"github.com/neptulon/neptulon"
	"github.com/neptulon/neptulon/middleware"
)

// Client is a Titan client.
type Client struct {
	ID           string     // Randomly generated unique client connection ID.
	Session      *cmap.CMap // Thread-safe data store for storing arbitrary data for this connection session.
	conn         *neptulon.Conn
	router       *middleware.Router
	inMsgHandler func(m []Message) error
}

// NewClient creates a new Client object.
func NewClient() (*Client, error) {
	c, err := neptulon.NewConn()
	if err != nil {
		return nil, err
	}
	r := middleware.NewRouter()
	c.Middleware(r)
	s := &Client{ID: c.ID, Session: c.Session, conn: c, router: r, inMsgHandler: func(m []Message) error { return nil }}
	r.Request("msg.recv", s.inMsgRoute)
	return s, nil
}

// SetDeadline set the read/write deadlines for the connection, in seconds.
func (c *Client) SetDeadline(seconds int) {
	c.conn.SetDeadline(seconds)
}

// Connect connectes to the server at given network address and starts receiving messages.
func (c *Client) Connect(addr string) error {
	return c.conn.Connect(addr)
}

// Close closes a client connection.
func (c *Client) Close() error {
	return c.conn.Close()
}

func (c *Client) inMsgRoute(ctx *neptulon.ReqCtx) error {
	var msg []Message
	if err := ctx.Params(msg); err != nil {
		return err
	}

	if err := c.inMsgHandler(msg); err != nil {
		return err
	}

	ctx.Res = "ACK"
	return ctx.Next()
}
<|endoftext|>


<commit_before>package client

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	conn net.Conn
}

func NewClient(o *Options) (*Client, error) {
	conn, err := o.getDialer()()
	return &Client{
		conn: conn,
	}, err
}

func (c *Client) Cmd(args ...interface{}) (r *Reply) {
	r = &Reply{
		State: ReplyError,
	}

	if c.conn == nil {
		r.State = ReplyFail
		return
	}

	if err := c.send(args); err != nil {
		r.State = ReplyFail
		return
	}
	r = c.recv()

	return
}

func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) send(args []interface{}) (err error) {
	var buf []byte

	buf, err = c.send_buf(args)
	if err == nil {
		_, err = c.conn.Write(buf)
	}
	return
}

func (c *Client) recv() (r *Reply) {
	var line []byte
	reader := bufio.NewReader(c.conn)

	// *<number of arguments>CRLF
	line, err = reader.ReadBytes('\n')
	if err != nil {
		return
	}

	if line[0] == '+' || line[0] == '-' {
		return &Reply{
			State: string(line[1 : len(line)-2]),
		}
	}
	if line[0] == ':' {
		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[1 : len(line)-2]},
		}
	}

	if line[0] == '$' {
		var dLen int
		dLen, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		line, _ = reader.ReadBytes('\n')

		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[:dLen]},
		}
	}

	var data [][]byte
	if line[0] == '*' {
		var count int
		count, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		for i := 0; i < count; i++ {
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			dLen, _ := strconv.Atoi(string(line[1 : len(line)-2]))
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			data = append(data, line[:dLen])
		}
	}

	r = &Reply{
		State: ReplyOK,
		Data:  data,
	}

	return
}

func (c *Client) send_buf(args []interface{}) (b []byte, err error) {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("*%d\r\n", len(args)))

	for _, arg := range args {
		switch arg.(type) {
		case string:
			buf.WriteString(fmt.Sprintf("$%d\r\n", len(arg.(string))))
			buf.WriteString(arg.(string) + "\r\n")
			continue
		}
	}

	b = buf.Bytes()

	return
}
<commit_msg>Add comments.<commit_after>package client

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strconv"
)

type Client struct {
	conn net.Conn
}

// NewClient will connect to the server and create a client.
func NewClient(o *Options) (*Client, error) {
	conn, err := o.getDialer()()
	return &Client{
		conn: conn,
	}, err
}

// Cmd will send command, receive data from server and build reply.
func (c *Client) Cmd(args ...interface{}) (r *Reply) {
	r = &Reply{
		State: ReplyError,
	}

	if c.conn == nil {
		r.State = ReplyFail
		return
	}

	if err := c.send(args); err != nil {
		r.State = ReplyFail
		return
	}
	r = c.recv()

	return
}

// Close socks.
func (c *Client) Close() {
	if c.conn != nil {
		c.conn.Close()
	}
}

func (c *Client) send(args []interface{}) (err error) {
	var buf []byte

	buf, err = c.send_buf(args)
	if err == nil {
		_, err = c.conn.Write(buf)
	}
	return
}

func (c *Client) recv() (r *Reply) {
	var line []byte
	reader := bufio.NewReader(c.conn)

	// *<number of arguments>CRLF
	line, err = reader.ReadBytes('\n')
	if err != nil {
		return
	}

	if line[0] == '+' || line[0] == '-' {
		return &Reply{
			State: string(line[1 : len(line)-2]),
		}
	}
	if line[0] == ':' {
		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[1 : len(line)-2]},
		}
	}

	if line[0] == '$' {
		var dLen int
		dLen, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		line, _ = reader.ReadBytes('\n')

		return &Reply{
			State: ReplyOK,
			Data:  [][]byte{line[:dLen]},
		}
	}

	var data [][]byte
	if line[0] == '*' {
		var count int
		count, err = strconv.Atoi(string(line[1 : len(line)-2]))
		if err != nil {
			return
		}
		for i := 0; i < count; i++ {
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			dLen, _ := strconv.Atoi(string(line[1 : len(line)-2]))
			line, err = reader.ReadBytes('\n')
			if err != nil {
				return
			}
			data = append(data, line[:dLen])
		}
	}

	r = &Reply{
		State: ReplyOK,
		Data:  data,
	}

	return
}

func (c *Client) send_buf(args []interface{}) (b []byte, err error) {
	var buf bytes.Buffer
	buf.WriteString(fmt.Sprintf("*%d\r\n", len(args)))

	for _, arg := range args {
		switch arg.(type) {
		case string:
			buf.WriteString(fmt.Sprintf("$%d\r\n", len(arg.(string))))
			buf.WriteString(arg.(string) + "\r\n")
			continue
		}
	}

	b = buf.Bytes()

	return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/go.crypto/ssh/agent"

	"github.com/coreos/shortbread/api"
	"github.com/coreos/shortbread/util"
)

const (
	SHORTBREAD_PUBLIC_KEY = "SHORTBREAD_PUBLIC_KEY"
)

func main() {
	svc, err := util.GetHTTPClientService()
	if err != nil {
		log.Fatal(err)
	}

	crtSvc := api.NewCertService(svc)
	publicKeyPath := util.GetenvWithDefault(SHORTBREAD_PUBLIC_KEY, os.ExpandEnv("$HOME/.ssh/id_rsa.pub"))
	privateKeyPath := strings.Split(publicKeyPath, ".pub")[0]
	pk := util.LoadPublicKey(publicKeyPath)

	for {
		time.Sleep(2000 * time.Millisecond)
		certsWithKey, err := crtSvc.GetCerts(pk).Do()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s", err.Error())
		}
		err = updateSSHAgent(certsWithKey.List, privateKeyPath)
		if err != nil {
			log.Fatal(err)
		}
	}
}

// updateSSHAgent takes the list of certificates and path to the private key (corresponding to the signed public key). Adds the cert if it's not present in the agent.
func updateSSHAgent(certsWithKeyList []*api.CertificateAndPrivateKey, privateKeyPath string) error {
	conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err != nil {
		return err
	}
	defer conn.Close()

	sshAgent := agent.NewClient(conn)
	certsInSSHAgent, err := sshAgent.List()
	if err != nil {
		return err
	}

	privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return err
	}

	privateKeyInterface, err := ssh.ParseRawPrivateKey(privateKeyBytes)
	if err != nil {
		return err
	}

	// TODO optimize this. currently O(N^2)
	for _, certAndKey := range certsWithKeyList {
		cert, err := util.ParseSSHCert([]byte(certAndKey.Cert))
		for _, key := range certsInSSHAgent {
			certBlob := key.Blob
			if err != nil {
				return err
			}
			if bytes.Equal(certBlob, cert.Marshal()) {
				break
			}
		}
		err = sshAgent.Add(privateKeyInterface, cert, "certificated added by shortbread")
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>removed log.Fatal<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
	"time"

	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/go.crypto/ssh/agent"

	"github.com/coreos/shortbread/api"
	"github.com/coreos/shortbread/util"
)

const (
	SHORTBREAD_PUBLIC_KEY = "SHORTBREAD_PUBLIC_KEY"
)

func main() {
	svc, err := util.GetHTTPClientService()
	if err != nil {
		log.Println(err)
	}

	crtSvc := api.NewCertService(svc)
	publicKeyPath := util.GetenvWithDefault(SHORTBREAD_PUBLIC_KEY, os.ExpandEnv("$HOME/.ssh/id_rsa.pub"))
	privateKeyPath := strings.Split(publicKeyPath, ".pub")[0]
	pk := util.LoadPublicKey(publicKeyPath)

	for {
		time.Sleep(2000 * time.Millisecond)
		certsWithKey, err := crtSvc.GetCerts(pk).Do()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s", err.Error())
		}
		err = updateSSHAgent(certsWithKey.List, privateKeyPath)
		if err != nil {
			log.Println(err)
		}
	}
}

// updateSSHAgent takes the list of certificates and path to the private key (corresponding to the signed public key). Adds the cert if it's not present in the agent.
func updateSSHAgent(certsWithKeyList []*api.CertificateAndPrivateKey, privateKeyPath string) error {
	conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err != nil {
		return err
	}
	defer conn.Close()

	sshAgent := agent.NewClient(conn)
	certsInSSHAgent, err := sshAgent.List()
	if err != nil {
		return err
	}

	privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return err
	}

	privateKeyInterface, err := ssh.ParseRawPrivateKey(privateKeyBytes)
	if err != nil {
		return err
	}

	// TODO optimize this. currently O(N^2)
	for _, certAndKey := range certsWithKeyList {
		cert, err := util.ParseSSHCert([]byte(certAndKey.Cert))
		for _, key := range certsInSSHAgent {
			certBlob := key.Blob
			if err != nil {
				return err
			}
			if bytes.Equal(certBlob, cert.Marshal()) {
				break
			}
		}
		err = sshAgent.Add(privateKeyInterface, cert, "certificated added by shortbread")
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
)

type Client http.Client

var DefaultClient = &Client{}

// Value type for json decoded values for request params and response
// body of a HTTP API
type Hash map[string]interface{}

// NewRequest is wrapper over http.NewRequest handling json encoding
// for params.
func NewRequest(method string, url string, params interface{}) (*http.Request, error) {
	body, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	return req, nil
}

// DoRequest a wrapper over Do handling json encoding
func (c *Client) DoRequest(req *http.Request, response interface{}) error{
	resp, err := (*http.Client)(c).Do(req)
	if err != nil {
		return err
	}

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

	err = json.Unmarshal(data, response)
	if err != nil {
		return err
	}

	// XXX: accept other codes
	if !(resp.StatusCode == 200 || resp.StatusCode == 302) {
		return fmt.Errorf("HTTP request with failure code (%d); body -- %v",
			resp.StatusCode, response)
	}

	return nil
}

// Post is a version of http.Post accepting JSON params and returning
// the same.
func Post(url string, params interface{}) (Hash, error) {
	req, err := NewRequest("POST", url, params)
	if err != nil {
		return nil, err
	}

	var h Hash
	return h, DefaultClient.DoRequest(req, &h)
}
<commit_msg>Hash is no longer requires as we now passing the response type (addr of value) to request functions<commit_after>package client

import (
	"fmt"
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
)

type Client http.Client

var DefaultClient = &Client{}

// NewRequest is wrapper over http.NewRequest handling json encoding
// for params.
func NewRequest(method string, url string, params interface{}) (*http.Request, error) {
	body, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	return req, nil
}

// DoRequest a wrapper over Do handling json encoding
func (c *Client) DoRequest(req *http.Request, response interface{}) error{
	resp, err := (*http.Client)(c).Do(req)
	if err != nil {
		return err
	}

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

	err = json.Unmarshal(data, response)
	if err != nil {
		return err
	}

	// XXX: accept other codes
	if !(resp.StatusCode == 200 || resp.StatusCode == 302) {
		return fmt.Errorf("HTTP request with failure code (%d); body -- %v",
			resp.StatusCode, response)
	}

	return nil
}

// Post is a version of http.Post accepting JSON params and returning
// the same.
func Post(url string, params interface{}, response interface{}) error {
	req, err := NewRequest("POST", url, params)
	if err != nil {
		return err
	}

	return DefaultClient.DoRequest(req, response)
}
<|endoftext|>


<commit_before>// Package client provides I/O for Ios clients
package main

import (
	"bufio"
	"encoding/csv"
	"errors"
	"flag"
	"github.com/golang/glog"
	"github.com/heidi-ann/ios/api/interactive"
	"github.com/heidi-ann/ios/api/rest"
	"github.com/heidi-ann/ios/config"
	"github.com/heidi-ann/ios/msgs"
	"github.com/heidi-ann/ios/test"
	"io"
	"net"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"
)

type API interface {
	Next() (string, bool, bool)
	Return(string)
}

var config_file = flag.String("config", "client/example.conf", "Client configuration file")
var auto_file = flag.String("auto", "test/workload.conf", "If workload is automatically generated, configure file for workload")
var stat_file = flag.String("stat", "latency.csv", "File to write stats to")
var mode = flag.String("mode", "interactive", "interactive, rest or test")
var id = flag.Int("id", -1, "ID of client (must be unique)")

func connect(addrs []string, tries int, hint int) (net.Conn, int, error) {
	var conn net.Conn
	var err error

	// reset invalid hint
	if len(addrs) >= hint {
		hint = 0
	}

	// first, try on to connect to the most likely leader
	glog.Info("Trying to connect to ", addrs[hint])
	conn, err = net.Dial("tcp", addrs[hint])
	// if successful
	if err == nil {
		glog.Infof("Connect established to %s", addrs[hint])
		return conn, hint, err
	}
	//if unsuccessful
	glog.Warning(err)

	// if fails, try everyone else
	for i := range addrs {
		for t := tries; t > 0; t-- {
			glog.Info("Trying to connect to ", addrs[i])
			conn, err = net.Dial("tcp", addrs[i])

			// if successful
			if err == nil {
				glog.Infof("Connect established to %s", addrs[i])
				return conn, i, err
			}

			//if unsuccessful
			glog.Warning(err)
		}
	}

	// calc most likely next leader
	hint += 1
	if len(addrs) == hint {
		hint = 0
	}
	return conn, hint, err
}

// send bytes and wait for reply, return bytes returned if succussful or error otherwise
func dispatcher(b []byte, conn net.Conn, r *bufio.Reader, timeout time.Duration) ([]byte, error) {
	// setup channels for timeout implementation
	errCh := make(chan error, 1)
	replyCh := make(chan []byte, 1)

	go func() {
		// send request
		_, err := conn.Write(b)
		_, err = conn.Write([]byte("\n"))
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}

		glog.Info("Sent")

		// read response
		reply, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}

		// success, return reply
		replyCh <- reply
	}()

	//handling outcomes
	select {
	case reply := <-replyCh:
		return reply, nil
	case err := <-errCh:
		return nil, err
	case <-time.After(timeout):
		return nil, errors.New("Timeout")
	}
}

func main() {
	// set up logging
	flag.Parse()
	defer glog.Flush()

	// always flush (whatever happens)
	sigs := make(chan os.Signal, 1)
	finish := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// parse config files
	conf := config.ParseClientConfig(*config_file)
	timeout := time.Millisecond * time.Duration(conf.Parameters.Timeout)
	// TODO: find a better way to handle required flags
	if *id == -1 {
		glog.Fatal("ID must be provided")
	}

	glog.Info("Starting up client ", *id)
	defer glog.Info("Shutting down client ", *id)

	// set up stats collection
	filename := *stat_file
	glog.Info("Opening file: ", filename)
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
	if err != nil {
		glog.Fatal(err)
	}
	stats := csv.NewWriter(file)
	defer stats.Flush()

	// set up request id
	// TODO: write this value to disk
	requestID := 1

	// connecting to server
	conn, leader, err := connect(conf.Addresses.Address, 1, 0)
	if err != nil {
		glog.Fatal(err)
	}
	rd := bufio.NewReader(conn)

	// setup API
	var ioapi API
	switch *mode {
	case "interactive":
		ioapi = interactive.Create()
	case "test":
		ioapi = test.Generate(test.ParseAuto(*auto_file))
	case "rest":
		ioapi = rest.Create()
	default:
		glog.Fatal("Invalid mode: ", mode)
	}

	glog.Info("Client is ready to start processing incoming requests")
	go func() {
		for {
			// get next command
			text, replicate, ok := ioapi.Next()
			if !ok {
				finish <- true
				break
			}
			glog.Info("Request ", requestID, " is: ", text)

			// encode as request
			req := msgs.ClientRequest{
				*id, requestID, replicate, text}
			b, err := msgs.Marshal(req)
			if err != nil {
				glog.Fatal(err)
			}
			glog.Info(string(b))

			startTime := time.Now()
			tries := 0

			// dispatch request until successful
			var reply *msgs.ClientResponse
			for {
				tries++
				replyBytes, err := dispatcher(b, conn, rd, timeout)
				if err == nil {

					//handle reply
					reply = new(msgs.ClientResponse)
					err = msgs.Unmarshal(replyBytes, reply)

					if err == nil {
						break
					}
				}
				glog.Warning("Request ", requestID, " failed due to: ", err)

				// try to establish a new connection
				for {
					conn, leader, err = connect(conf.Addresses.Address, leader+1, conf.Parameters.Retries)
					if err == nil {
						break
					}
					glog.Warning("Serious connectivity issues")
					time.Sleep(time.Second)
				}

				rd = bufio.NewReader(conn)

			}

			//check reply is not nil
			if *reply == (msgs.ClientResponse{}) {
				glog.Fatal("Response is nil")
			}

			//check reply is as expected
			if reply.ClientID != *id {
				glog.Fatal("Response received has wrong ClientID: expected ",
					*id, " ,received ", reply.ClientID)
			}
			if reply.RequestID != requestID {
				glog.Fatal("Response received has wrong RequestID: expected ",
					requestID, " ,received ", reply.RequestID)
			}

			// write to latency to log
			latency := strconv.FormatInt(time.Since(startTime).Nanoseconds(), 10)
			err = stats.Write([]string{strconv.Itoa(startTime.UnixNano()), strconv.Itoa(requestID), latency, strconv.Itoa(tries)})
			if err != nil {
				glog.Fatal(err)
			}
			stats.Flush()
			// TODO: call error to check if successful

			requestID++
			// writing result to user
			// time.Since(startTime)
			ioapi.Return(reply.Response)

		}
	}()

	select {
	case sig := <-sigs:
		glog.Warning("Termination due to: ", sig)
	case <-finish:
		glog.Info("No more commands")
	}
	glog.Flush()

}
<commit_msg>strconv.Itoa -> strconv.FormatInt in client.go<commit_after>// Package client provides I/O for Ios clients
package main

import (
	"bufio"
	"encoding/csv"
	"errors"
	"flag"
	"github.com/golang/glog"
	"github.com/heidi-ann/ios/api/interactive"
	"github.com/heidi-ann/ios/api/rest"
	"github.com/heidi-ann/ios/config"
	"github.com/heidi-ann/ios/msgs"
	"github.com/heidi-ann/ios/test"
	"io"
	"net"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"
)

type API interface {
	Next() (string, bool, bool)
	Return(string)
}

var config_file = flag.String("config", "client/example.conf", "Client configuration file")
var auto_file = flag.String("auto", "test/workload.conf", "If workload is automatically generated, configure file for workload")
var stat_file = flag.String("stat", "latency.csv", "File to write stats to")
var mode = flag.String("mode", "interactive", "interactive, rest or test")
var id = flag.Int("id", -1, "ID of client (must be unique)")

func connect(addrs []string, tries int, hint int) (net.Conn, int, error) {
	var conn net.Conn
	var err error

	// reset invalid hint
	if len(addrs) >= hint {
		hint = 0
	}

	// first, try on to connect to the most likely leader
	glog.Info("Trying to connect to ", addrs[hint])
	conn, err = net.Dial("tcp", addrs[hint])
	// if successful
	if err == nil {
		glog.Infof("Connect established to %s", addrs[hint])
		return conn, hint, err
	}
	//if unsuccessful
	glog.Warning(err)

	// if fails, try everyone else
	for i := range addrs {
		for t := tries; t > 0; t-- {
			glog.Info("Trying to connect to ", addrs[i])
			conn, err = net.Dial("tcp", addrs[i])

			// if successful
			if err == nil {
				glog.Infof("Connect established to %s", addrs[i])
				return conn, i, err
			}

			//if unsuccessful
			glog.Warning(err)
		}
	}

	// calc most likely next leader
	hint += 1
	if len(addrs) == hint {
		hint = 0
	}
	return conn, hint, err
}

// send bytes and wait for reply, return bytes returned if succussful or error otherwise
func dispatcher(b []byte, conn net.Conn, r *bufio.Reader, timeout time.Duration) ([]byte, error) {
	// setup channels for timeout implementation
	errCh := make(chan error, 1)
	replyCh := make(chan []byte, 1)

	go func() {
		// send request
		_, err := conn.Write(b)
		_, err = conn.Write([]byte("\n"))
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}

		glog.Info("Sent")

		// read response
		reply, err := r.ReadBytes('\n')
		if err != nil && err != io.EOF {
			glog.Warning(err)
			errCh <- err
		}

		// success, return reply
		replyCh <- reply
	}()

	//handling outcomes
	select {
	case reply := <-replyCh:
		return reply, nil
	case err := <-errCh:
		return nil, err
	case <-time.After(timeout):
		return nil, errors.New("Timeout")
	}
}

func main() {
	// set up logging
	flag.Parse()
	defer glog.Flush()

	// always flush (whatever happens)
	sigs := make(chan os.Signal, 1)
	finish := make(chan bool, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// parse config files
	conf := config.ParseClientConfig(*config_file)
	timeout := time.Millisecond * time.Duration(conf.Parameters.Timeout)
	// TODO: find a better way to handle required flags
	if *id == -1 {
		glog.Fatal("ID must be provided")
	}

	glog.Info("Starting up client ", *id)
	defer glog.Info("Shutting down client ", *id)

	// set up stats collection
	filename := *stat_file
	glog.Info("Opening file: ", filename)
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
	if err != nil {
		glog.Fatal(err)
	}
	stats := csv.NewWriter(file)
	defer stats.Flush()

	// set up request id
	// TODO: write this value to disk
	requestID := 1

	// connecting to server
	conn, leader, err := connect(conf.Addresses.Address, 1, 0)
	if err != nil {
		glog.Fatal(err)
	}
	rd := bufio.NewReader(conn)

	// setup API
	var ioapi API
	switch *mode {
	case "interactive":
		ioapi = interactive.Create()
	case "test":
		ioapi = test.Generate(test.ParseAuto(*auto_file))
	case "rest":
		ioapi = rest.Create()
	default:
		glog.Fatal("Invalid mode: ", mode)
	}

	glog.Info("Client is ready to start processing incoming requests")
	go func() {
		for {
			// get next command
			text, replicate, ok := ioapi.Next()
			if !ok {
				finish <- true
				break
			}
			glog.Info("Request ", requestID, " is: ", text)

			// encode as request
			req := msgs.ClientRequest{
				*id, requestID, replicate, text}
			b, err := msgs.Marshal(req)
			if err != nil {
				glog.Fatal(err)
			}
			glog.Info(string(b))

			startTime := time.Now()
			tries := 0

			// dispatch request until successful
			var reply *msgs.ClientResponse
			for {
				tries++
				replyBytes, err := dispatcher(b, conn, rd, timeout)
				if err == nil {

					//handle reply
					reply = new(msgs.ClientResponse)
					err = msgs.Unmarshal(replyBytes, reply)

					if err == nil {
						break
					}
				}
				glog.Warning("Request ", requestID, " failed due to: ", err)

				// try to establish a new connection
				for {
					conn, leader, err = connect(conf.Addresses.Address, leader+1, conf.Parameters.Retries)
					if err == nil {
						break
					}
					glog.Warning("Serious connectivity issues")
					time.Sleep(time.Second)
				}

				rd = bufio.NewReader(conn)

			}

			//check reply is not nil
			if *reply == (msgs.ClientResponse{}) {
				glog.Fatal("Response is nil")
			}

			//check reply is as expected
			if reply.ClientID != *id {
				glog.Fatal("Response received has wrong ClientID: expected ",
					*id, " ,received ", reply.ClientID)
			}
			if reply.RequestID != requestID {
				glog.Fatal("Response received has wrong RequestID: expected ",
					requestID, " ,received ", reply.RequestID)
			}

			// write to latency to log
			latency := strconv.FormatInt(time.Since(startTime).Nanoseconds(), 10)
			err = stats.Write([]string{strconv.FormatInt(startTime.UnixNano(),10), strconv.Itoa(requestID), latency, strconv.Itoa(tries)})
			if err != nil {
				glog.Fatal(err)
			}
			stats.Flush()
			// TODO: call error to check if successful

			requestID++
			// writing result to user
			// time.Since(startTime)
			ioapi.Return(reply.Response)

		}
	}()

	select {
	case sig := <-sigs:
		glog.Warning("Termination due to: ", sig)
	case <-finish:
		glog.Info("No more commands")
	}
	glog.Flush()

}
<|endoftext|>


<commit_before>package gitmediaclient

import (
	".."
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

func Put(filename string) error {
	oid := filepath.Base(filename)
	stat, err := os.Stat(filename)
	if err != nil {
		return err
	}

	file, err := os.Open(filename)
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	req.Body = file
	req.ContentLength = stat.Size()

	res, err := doRequest(req, creds)
	if err != nil {
		return err
	}

	fmt.Printf("Sending %s from %s: %d\n", oid, filename, res.StatusCode)
	return nil
}

func Get(filename string) (io.ReadCloser, error) {
	oid := filepath.Base(filename)
	if stat, err := os.Stat(filename); err != nil || stat == nil {
		req, creds, err := clientRequest("GET", oid)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Accept", "application/vnd.git-media")
		res, err := doRequest(req, creds)

		if err != nil {
			return nil, err
		}

		return res.Body, nil
	}

	return os.Open(filename)
}

func doRequest(req *http.Request, creds Creds) (*http.Response, error) {
	res, err := http.DefaultClient.Do(req)

	if err == nil {
		if res.StatusCode > 299 {
			execCreds(creds, "reject")

			apierr := &Error{}
			dec := json.NewDecoder(res.Body)
			if err := dec.Decode(apierr); err != nil {
				return res, err
			}

			return res, apierr
		}
	} else {
		execCreds(creds, "approve")
	}

	return res, err
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := gitmedia.Config()
	u, _ := url.Parse(c.Endpoint)
	u.Path = filepath.Join(u.Path, "/objects/"+oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<commit_msg>ララララー ラララー ララ<commit_after>package gitmediaclient

import (
	".."
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

const (
	gitMediaType     = "application/vnd.git-media"
	gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
)

func Put(filename string) error {
	oid := filepath.Base(filename)
	stat, err := os.Stat(filename)
	if err != nil {
		return err
	}

	file, err := os.Open(filename)
	if err != nil {
		return err
	}

	req, creds, err := clientRequest("PUT", oid)
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", gitMediaType)
	req.Header.Set("Accept", gitMediaMetaType)
	req.Body = file
	req.ContentLength = stat.Size()

	res, err := doRequest(req, creds)
	if err != nil {
		return err
	}

	fmt.Printf("Sending %s from %s: %d\n", oid, filename, res.StatusCode)
	return nil
}

func Get(filename string) (io.ReadCloser, error) {
	oid := filepath.Base(filename)
	if stat, err := os.Stat(filename); err != nil || stat == nil {
		req, creds, err := clientRequest("GET", oid)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Accept", gitMediaType)
		res, err := doRequest(req, creds)

		if err != nil {
			return nil, err
		}

		return res.Body, nil
	}

	return os.Open(filename)
}

func doRequest(req *http.Request, creds Creds) (*http.Response, error) {
	res, err := http.DefaultClient.Do(req)

	if err == nil {
		if res.StatusCode > 299 {
			execCreds(creds, "reject")

			apierr := &Error{}
			dec := json.NewDecoder(res.Body)
			if err := dec.Decode(apierr); err != nil {
				return res, err
			}

			return res, apierr
		}
	} else {
		execCreds(creds, "approve")
	}

	return res, err
}

func clientRequest(method, oid string) (*http.Request, Creds, error) {
	u := ObjectUrl(oid)
	req, err := http.NewRequest(method, u.String(), nil)
	if err == nil {
		creds, err := credentials(u)
		if err != nil {
			return req, nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return req, creds, nil
	}

	return req, nil, err
}

func ObjectUrl(oid string) *url.URL {
	c := gitmedia.Config()
	u, _ := url.Parse(c.Endpoint)
	u.Path = filepath.Join(u.Path, "/objects/"+oid)
	return u
}

type Error struct {
	Message   string `json:"message"`
	RequestId string `json:"request_id,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>package arn

import (
	"github.com/aerogo/api"
	"github.com/aerogo/nano"
	"github.com/animenotifier/jikan"
)

// Session ...
type Session map[string]interface{}

// Node represents the database node.
var Node = nano.New(5000)

// DB is the main database client.
var DB = Node.Namespace("arn").RegisterTypes(
	(*Analytics)(nil),
	(*Anime)(nil),
	(*AnimeCharacters)(nil),
	(*AnimeEpisodes)(nil),
	(*AnimeRelations)(nil),
	(*AnimeList)(nil),
	(*AniListToAnime)(nil),
	(*Character)(nil),
	(*Company)(nil),
	(*DraftIndex)(nil),
	(*MyAnimeListToAnime)(nil),
	(*EmailToUser)(nil),
	(*FacebookToUser)(nil),
	(*GoogleToUser)(nil),
	(*Group)(nil),
	(*GroupPost)(nil),
	(*Item)(nil),
	(*IDList)(nil),
	(*Inventory)(nil),
	(*NickToUser)(nil),
	(*Notification)(nil),
	(*PayPalPayment)(nil),
	(*Post)(nil),
	(*Purchase)(nil),
	(*PushSubscriptions)(nil),
	(*Quote)(nil),
	(*Session)(nil),
	(*Settings)(nil),
	(*SoundTrack)(nil),
	(*Thread)(nil),
	(*TwitterToUser)(nil),
	(*User)(nil),
	(*UserFollows)(nil),
	(*UserNotifications)(nil),
)

// API ...
var API = api.New("/api/", DB)

// init ...
func init() {
	Node.Namespace("jikan").RegisterTypes(
		(*jikan.Anime)(nil),
		(*jikan.Character)(nil),
	)
}
<commit_msg>Added MAL DB client<commit_after>package arn

import (
	"github.com/aerogo/api"
	"github.com/aerogo/nano"
	"github.com/animenotifier/jikan"
	"github.com/animenotifier/mal"
)

// Session ...
type Session map[string]interface{}

// Node represents the database node.
var Node = nano.New(5000)

// DB is the main database client.
var DB = Node.Namespace("arn").RegisterTypes(
	(*Analytics)(nil),
	(*Anime)(nil),
	(*AnimeCharacters)(nil),
	(*AnimeEpisodes)(nil),
	(*AnimeRelations)(nil),
	(*AnimeList)(nil),
	(*AniListToAnime)(nil),
	(*Character)(nil),
	(*Company)(nil),
	(*DraftIndex)(nil),
	(*MyAnimeListToAnime)(nil),
	(*EmailToUser)(nil),
	(*FacebookToUser)(nil),
	(*GoogleToUser)(nil),
	(*Group)(nil),
	(*GroupPost)(nil),
	(*Item)(nil),
	(*IDList)(nil),
	(*Inventory)(nil),
	(*NickToUser)(nil),
	(*Notification)(nil),
	(*PayPalPayment)(nil),
	(*Post)(nil),
	(*Purchase)(nil),
	(*PushSubscriptions)(nil),
	(*Quote)(nil),
	(*Session)(nil),
	(*Settings)(nil),
	(*SoundTrack)(nil),
	(*Thread)(nil),
	(*TwitterToUser)(nil),
	(*User)(nil),
	(*UserFollows)(nil),
	(*UserNotifications)(nil),
)

// MAL is the client for the MyAnimeList database.
var MAL = Node.Namespace("mal").RegisterTypes(
	(*mal.Anime)(nil),
)

// API ...
var API = api.New("/api/", DB)

// init ...
func init() {
	Node.Namespace("jikan").RegisterTypes(
		(*jikan.Anime)(nil),
		(*jikan.Character)(nil),
	)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"

	"github.com/olekukonko/tablewriter"
	"gopkg.in/alecthomas/kingpin.v2"
)

const version = "2.2.3"

var (
	app         = kingpin.New("download-geofabrik", "A command-line tool for downloading OSM files.")
	fService    = app.Flag("service", "Can switch to another service. You can use \"geofabrik\" or \"openstreetmap.fr\". It automatically change config file if -c is unused.").Default("geofabrik").String()
	fConfig     = app.Flag("config", "Set Config file.").Default("./geofabrik.yml").Short('c').String()
	fNodownload = app.Flag("nodownload", "Do not download file (test only)").Short('n').Bool()
	fVerbose    = app.Flag("verbose", "Be verbose").Short('v').Bool()
	fQuiet      = app.Flag("quiet", "Be quiet").Short('q').Bool()
	fProxyHTTP  = app.Flag("proxy-http", "Use http proxy, format: proxy_address:port").Default("").String()
	fProxySock5 = app.Flag("proxy-sock5", "Use Sock5 proxy, format: proxy_address:port").Default("").String()
	fProxyUser  = app.Flag("proxy-user", "Proxy user").Default("").String()
	fProxyPass  = app.Flag("proxy-pass", "Proxy password").Default("").String()

	update = app.Command("update", "Update geofabrik.yml from github *** DEPRECATED you should prefer use generate ***")
	fURL   = update.Flag("url", "Url for config source").Default("https://raw.githubusercontent.com/julien-noblet/download-geofabrik/master/geofabrik.yml").String()

	list = app.Command("list", "Show elements available")
	lmd  = list.Flag("markdown", "generate list in Markdown format").Bool()

	download = app.Command("download", "Download element") //TODO : add d as command
	delement = download.Arg("element", "OSM element").Required().String()
	dosmBz2  = download.Flag("osm.bz2", "Download osm.bz2 if available").Short('B').Bool()
	dshpZip  = download.Flag("shp.zip", "Download shp.zip if available").Short('S').Bool()
	dosmPbf  = download.Flag("osm.pbf", "Download osm.pbf (default)").Short('P').Bool()
	doshPbf  = download.Flag("osh.pbf", "Download osh.pbf").Short('H').Bool()
	dstate   = download.Flag("state", "Download state.txt file").Short('s').Bool()
	dpoly    = download.Flag("poly", "Download poly file").Short('p').Bool()
	dkml     = download.Flag("kml", "Download kml file").Short('k').Bool()
	dCheck   = download.Flag("check", "Control with checksum (default) Use --no-check to discard control").Default("true").Bool()

	generate = app.Command("generate", "Generate a new config file")
)

func listAllRegions(c Config, format string) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetHeader([]string{"ShortName", "Is in", "Long Name", "formats"})
	if format == "Markdown" {
		table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
		table.SetCenterSeparator("|")
	}
	keys := make(sort.StringSlice, len(c.Elements))
	i := 0
	for k := range c.Elements {
		keys[i] = k
		i++
	}
	keys.Sort()
	for _, item := range keys {
		table.Append([]string{item, c.Elements[c.Elements[item].Parent].Name, c.Elements[item].Name, miniFormats(c.Elements[item].Formats)})
	}
	table.Render()
	fmt.Printf("Total elements: %#v\n", len(c.Elements))
}

// UpdateConfig : simple script to download lastest config from repo
func UpdateConfig(myURL string, myconfig string) error {
	if !*fQuiet {
		log.Print("*** DEPRECATED you should prefer use generate ***")
	}
	err := downloadFromURL(myURL, myconfig)
	if err != nil {
		if *fVerbose {
			log.Println(err)
		}
		return fmt.Errorf("Can't updating %v please use generate", myconfig)
	}
	if *fVerbose && !*fQuiet {
		log.Println("Congratulation, you have the latest geofabrik.yml")
	}
	return nil
}

func checkService() bool {
	switch *fService {
	case "geofabrik":
		return true
	case "openstreetmap.fr":
		if strings.EqualFold(*fConfig, "./geofabrik.yml") {
			*fConfig = "./openstreetmap.fr.yml"
		}
		return true
	}
	return false
}

func catch(err error) {
	if err != nil {
		log.Fatalln(err.Error()) // Fatalln is better than Panic or Println
		// Println only log but dont do exit(1),
		// Panic add a lot of verbose detail for debug but it's too aggressive!
	}
}

func main() {
	app.Version(version) // Add version flag

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {

	case list.FullCommand():
		checkService()
		var format = ""
		if *lmd {
			format = "Markdown"
		}
		configPtr, err := loadConfig(*fConfig)
		catch(err)
		listAllRegions(*configPtr, format)
	case update.FullCommand():
		checkService()
		UpdateConfig(*fURL, *fConfig)
	case download.FullCommand():
		checkService()
		configPtr, err := loadConfig(*fConfig)
		catch(err)
		formatFile := getFormats()
		for _, format := range *formatFile {
			if ok, _, _ := isHashable(configPtr, format); *dCheck && ok {
				if fileExist(*delement + "." + format) {
					if !downloadChecksum(format) {
						if !*fQuiet {
							log.Println("Checksum mismatch, re-downloading", *delement+"."+format)
						}
						myElem, err := findElem(configPtr, *delement)
						catch(err)
						myURL, err := elem2URL(configPtr, myElem, format)
						catch(err)
						err = downloadFromURL(myURL, *delement+"."+format)
						catch(err)
						downloadChecksum(format)

					} else {
						if !*fQuiet {
							log.Printf("Checksum match, no download!")
						}
					}
				} else {
					myElem, err := findElem(configPtr, *delement)
					catch(err)
					myURL, err := elem2URL(configPtr, myElem, format)
					catch(err)
					err = downloadFromURL(myURL, *delement+"."+format)
					catch(err)
					if !downloadChecksum(format) && !*fQuiet {
						log.Println("Checksum mismatch, please re-download", *delement+"."+format)
					}
				}
			} else {
				myElem, err := findElem(configPtr, *delement)
				catch(err)
				myURL, err := elem2URL(configPtr, myElem, format)
				catch(err)
				err = downloadFromURL(myURL, *delement+"."+format)
				catch(err)
			}
		}
	case generate.FullCommand():
		checkService()
		Generate(*fConfig)
	}

}

func fileExist(filePath string) bool {
	if _, err := os.Stat(filePath); err == nil {
		return true
	}
	return false
}

func hashFileMD5(filePath string) (string, error) {
	var returnMD5String string
	if fileExist(filePath) {
		file, err := os.Open(filePath)
		if err != nil {
			return returnMD5String, err
		}
		defer file.Close()
		hash := md5.New()

		if _, err := io.Copy(hash, file); err != nil {
			return returnMD5String, err
		}
		hashInBytes := hash.Sum(nil)[:16]
		returnMD5String = hex.EncodeToString(hashInBytes)
		return returnMD5String, nil
	}
	return returnMD5String, nil
}

func controlHash(hashfile string, hash string) (bool, error) {
	if fileExist(hashfile) {
		file, err := ioutil.ReadFile(hashfile)
		if err != nil {
			return false, err
		}
		filehash := strings.Split(string(file), " ")[0]
		if *fVerbose && !*fQuiet {
			log.Println("Hash from file :", filehash)
		}
		return strings.EqualFold(hash, filehash), nil
	}
	return false, nil
}

func downloadChecksum(format string) bool {
	ret := false
	if *dCheck {
		hash := "md5"
		fhash := format + "." + hash
		configPtr, err := loadConfig(*fConfig)
		catch(err)
		if ok, _, _ := isHashable(configPtr, format); ok {
			myElem, err := findElem(configPtr, *delement)
			catch(err)
			myURL, err := elem2URL(configPtr, myElem, fhash)
			catch(err)
			downloadFromURL(myURL, *delement+"."+fhash)
			if *fVerbose && !*fQuiet {
				log.Println("Hashing", *delement+"."+format)
			}
			hashed, err := hashFileMD5(*delement + "." + format)
			if err != nil {
				log.Panic(fmt.Errorf(err.Error()))
			}
			if *fVerbose && !*fQuiet {
				log.Println("MD5 :", hashed)
			}
			ret, err := controlHash(*delement+"."+fhash, hashed)
			if err != nil {
				log.Panic(fmt.Errorf(err.Error()))
			}
			if !*fQuiet {
				if ret {
					log.Println("Checksum OK for", *delement+"."+format)
				} else {
					log.Println("Checksum MISMATCH for", *delement+"."+format)
				}
			}
			return ret
		}
		if !*fQuiet {
			log.Println("No checksum provided for", *delement+"."+format)
		}
	}
	return ret
}
<commit_msg>Bump to 2.3.0-beta<commit_after>package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"

	"github.com/olekukonko/tablewriter"
	"gopkg.in/alecthomas/kingpin.v2"
)

const version = "2.3.0-beta"

var (
	app         = kingpin.New("download-geofabrik", "A command-line tool for downloading OSM files.")
	fService    = app.Flag("service", "Can switch to another service. You can use \"geofabrik\" or \"openstreetmap.fr\". It automatically change config file if -c is unused.").Default("geofabrik").String()
	fConfig     = app.Flag("config", "Set Config file.").Default("./geofabrik.yml").Short('c').String()
	fNodownload = app.Flag("nodownload", "Do not download file (test only)").Short('n').Bool()
	fVerbose    = app.Flag("verbose", "Be verbose").Short('v').Bool()
	fQuiet      = app.Flag("quiet", "Be quiet").Short('q').Bool()
	fProxyHTTP  = app.Flag("proxy-http", "Use http proxy, format: proxy_address:port").Default("").String()
	fProxySock5 = app.Flag("proxy-sock5", "Use Sock5 proxy, format: proxy_address:port").Default("").String()
	fProxyUser  = app.Flag("proxy-user", "Proxy user").Default("").String()
	fProxyPass  = app.Flag("proxy-pass", "Proxy password").Default("").String()

	update = app.Command("update", "Update geofabrik.yml from github *** DEPRECATED you should prefer use generate ***")
	fURL   = update.Flag("url", "Url for config source").Default("https://raw.githubusercontent.com/julien-noblet/download-geofabrik/master/geofabrik.yml").String()

	list = app.Command("list", "Show elements available")
	lmd  = list.Flag("markdown", "generate list in Markdown format").Bool()

	download = app.Command("download", "Download element") //TODO : add d as command
	delement = download.Arg("element", "OSM element").Required().String()
	dosmBz2  = download.Flag("osm.bz2", "Download osm.bz2 if available").Short('B').Bool()
	dshpZip  = download.Flag("shp.zip", "Download shp.zip if available").Short('S').Bool()
	dosmPbf  = download.Flag("osm.pbf", "Download osm.pbf (default)").Short('P').Bool()
	doshPbf  = download.Flag("osh.pbf", "Download osh.pbf").Short('H').Bool()
	dstate   = download.Flag("state", "Download state.txt file").Short('s').Bool()
	dpoly    = download.Flag("poly", "Download poly file").Short('p').Bool()
	dkml     = download.Flag("kml", "Download kml file").Short('k').Bool()
	dCheck   = download.Flag("check", "Control with checksum (default) Use --no-check to discard control").Default("true").Bool()

	generate = app.Command("generate", "Generate a new config file")
)

func listAllRegions(c Config, format string) {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetHeader([]string{"ShortName", "Is in", "Long Name", "formats"})
	if format == "Markdown" {
		table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
		table.SetCenterSeparator("|")
	}
	keys := make(sort.StringSlice, len(c.Elements))
	i := 0
	for k := range c.Elements {
		keys[i] = k
		i++
	}
	keys.Sort()
	for _, item := range keys {
		table.Append([]string{item, c.Elements[c.Elements[item].Parent].Name, c.Elements[item].Name, miniFormats(c.Elements[item].Formats)})
	}
	table.Render()
	fmt.Printf("Total elements: %#v\n", len(c.Elements))
}

// UpdateConfig : simple script to download lastest config from repo
func UpdateConfig(myURL string, myconfig string) error {
	if !*fQuiet {
		log.Print("*** DEPRECATED you should prefer use generate ***")
	}
	err := downloadFromURL(myURL, myconfig)
	if err != nil {
		if *fVerbose {
			log.Println(err)
		}
		return fmt.Errorf("Can't updating %v please use generate", myconfig)
	}
	if *fVerbose && !*fQuiet {
		log.Println("Congratulation, you have the latest geofabrik.yml")
	}
	return nil
}

func checkService() bool {
	switch *fService {
	case "geofabrik":
		return true
	case "openstreetmap.fr":
		if strings.EqualFold(*fConfig, "./geofabrik.yml") {
			*fConfig = "./openstreetmap.fr.yml"
		}
		return true
	}
	return false
}

func catch(err error) {
	if err != nil {
		log.Fatalln(err.Error()) // Fatalln is better than Panic or Println
		// Println only log but dont do exit(1),
		// Panic add a lot of verbose detail for debug but it's too aggressive!
	}
}

func main() {
	app.Version(version) // Add version flag

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {

	case list.FullCommand():
		checkService()
		var format = ""
		if *lmd {
			format = "Markdown"
		}
		configPtr, err := loadConfig(*fConfig)
		catch(err)
		listAllRegions(*configPtr, format)
	case update.FullCommand():
		checkService()
		UpdateConfig(*fURL, *fConfig)
	case download.FullCommand():
		checkService()
		configPtr, err := loadConfig(*fConfig)
		catch(err)
		formatFile := getFormats()
		for _, format := range *formatFile {
			if ok, _, _ := isHashable(configPtr, format); *dCheck && ok {
				if fileExist(*delement + "." + format) {
					if !downloadChecksum(format) {
						if !*fQuiet {
							log.Println("Checksum mismatch, re-downloading", *delement+"."+format)
						}
						myElem, err := findElem(configPtr, *delement)
						catch(err)
						myURL, err := elem2URL(configPtr, myElem, format)
						catch(err)
						err = downloadFromURL(myURL, *delement+"."+format)
						catch(err)
						downloadChecksum(format)

					} else {
						if !*fQuiet {
							log.Printf("Checksum match, no download!")
						}
					}
				} else {
					myElem, err := findElem(configPtr, *delement)
					catch(err)
					myURL, err := elem2URL(configPtr, myElem, format)
					catch(err)
					err = downloadFromURL(myURL, *delement+"."+format)
					catch(err)
					if !downloadChecksum(format) && !*fQuiet {
						log.Println("Checksum mismatch, please re-download", *delement+"."+format)
					}
				}
			} else {
				myElem, err := findElem(configPtr, *delement)
				catch(err)
				myURL, err := elem2URL(configPtr, myElem, format)
				catch(err)
				err = downloadFromURL(myURL, *delement+"."+format)
				catch(err)
			}
		}
	case generate.FullCommand():
		checkService()
		Generate(*fConfig)
	}

}

func fileExist(filePath string) bool {
	if _, err := os.Stat(filePath); err == nil {
		return true
	}
	return false
}

func hashFileMD5(filePath string) (string, error) {
	var returnMD5String string
	if fileExist(filePath) {
		file, err := os.Open(filePath)
		if err != nil {
			return returnMD5String, err
		}
		defer file.Close()
		hash := md5.New()

		if _, err := io.Copy(hash, file); err != nil {
			return returnMD5String, err
		}
		hashInBytes := hash.Sum(nil)[:16]
		returnMD5String = hex.EncodeToString(hashInBytes)
		return returnMD5String, nil
	}
	return returnMD5String, nil
}

func controlHash(hashfile string, hash string) (bool, error) {
	if fileExist(hashfile) {
		file, err := ioutil.ReadFile(hashfile)
		if err != nil {
			return false, err
		}
		filehash := strings.Split(string(file), " ")[0]
		if *fVerbose && !*fQuiet {
			log.Println("Hash from file :", filehash)
		}
		return strings.EqualFold(hash, filehash), nil
	}
	return false, nil
}

func downloadChecksum(format string) bool {
	ret := false
	if *dCheck {
		hash := "md5"
		fhash := format + "." + hash
		configPtr, err := loadConfig(*fConfig)
		catch(err)
		if ok, _, _ := isHashable(configPtr, format); ok {
			myElem, err := findElem(configPtr, *delement)
			catch(err)
			myURL, err := elem2URL(configPtr, myElem, fhash)
			catch(err)
			downloadFromURL(myURL, *delement+"."+fhash)
			if *fVerbose && !*fQuiet {
				log.Println("Hashing", *delement+"."+format)
			}
			hashed, err := hashFileMD5(*delement + "." + format)
			if err != nil {
				log.Panic(fmt.Errorf(err.Error()))
			}
			if *fVerbose && !*fQuiet {
				log.Println("MD5 :", hashed)
			}
			ret, err := controlHash(*delement+"."+fhash, hashed)
			if err != nil {
				log.Panic(fmt.Errorf(err.Error()))
			}
			if !*fQuiet {
				if ret {
					log.Println("Checksum OK for", *delement+"."+format)
				} else {
					log.Println("Checksum MISMATCH for", *delement+"."+format)
				}
			}
			return ret
		}
		if !*fQuiet {
			log.Println("No checksum provided for", *delement+"."+format)
		}
	}
	return ret
}
<|endoftext|>


<commit_before>// +build !nogui

package main

import (
	"strconv"
)

// listUI manages the sections in the left-hand side list. It contains a number
// of items, which may have subheadlines and indicators (coloured dots).
type listUI struct {
	gui      GUI
	vboxName string
	entries  []listItem
	selected uint64
	nextId   int
}

type listItem struct {
	id                                                           uint64
	name, sepName, boxName, imageName, lineName, sublineTextName string
	insensitive                                                  bool
	background                                                   uint32
}

func (cs *listUI) Event(event interface{}) (uint64, bool) {
	if click, ok := event.(Click); ok {
		for _, entry := range cs.entries {
			if click.name == entry.boxName {
				if entry.insensitive {
					return 0, false
				}
				return entry.id, true
			}
		}
	}

	return 0, false
}

func (cs *listUI) Add(id uint64, name, subline string, indicator Indicator) {
	c := listItem{
		id:              id,
		name:            name,
		sepName:         cs.newIdent(),
		boxName:         cs.newIdent(),
		imageName:       cs.newIdent(),
		lineName:        cs.newIdent(),
		sublineTextName: cs.newIdent(),
		background:      colorGray,
	}
	cs.entries = append(cs.entries, c)
	index := len(cs.entries) - 1

	if index > 0 {
		// Add the separator bar.
		cs.gui.Actions() <- AddToBox{
			box:   cs.vboxName,
			pos:   index*2 - 1,
			child: EventBox{widgetBase: widgetBase{height: 1, background: 0xe5e6e6, name: c.sepName}},
		}
	}

	children := []Widget{
		HBox{
			widgetBase: widgetBase{padding: 1},
			children: []Widget{
				Label{
					widgetBase: widgetBase{
						name:    c.lineName,
						padding: 5,
						font:    fontListEntry,
					},
					text: name,
				},
			},
		},
	}

	var sublineChildren []Widget

	if len(subline) > 0 {
		sublineChildren = append(sublineChildren, Label{
			widgetBase: widgetBase{
				padding:    5,
				foreground: colorSubline,
				font:       fontListSubline,
				name:       c.sublineTextName,
			},
			text: subline,
		})
	}

	sublineChildren = append(sublineChildren, Image{
		widgetBase: widgetBase{
			padding: 4,
			expand:  true,
			fill:    true,
			name:    c.imageName,
		},
		image:  indicator,
		xAlign: 1,
		yAlign: 0.5,
	})

	children = append(children, HBox{
		widgetBase: widgetBase{padding: 1},
		children:   sublineChildren,
	})

	cs.gui.Actions() <- AddToBox{
		box: cs.vboxName,
		pos: index * 2,
		child: EventBox{
			widgetBase: widgetBase{name: c.boxName, background: c.background},
			child:      VBox{children: children},
		},
	}
	cs.gui.Signal()
}

func (cs *listUI) SetInsensitive(id uint64) {
	for i, entry := range cs.entries {
		if entry.id == id {
			cs.entries[i].insensitive = true
		}
	}
}

func (cs *listUI) Remove(id uint64) {
	newEntries := make([]listItem, 0, len(cs.entries))
	for i, entry := range cs.entries {
		if entry.id == id {
			if i > 0 {
				cs.gui.Actions() <- Destroy{name: entry.sepName}
			}
			cs.gui.Actions() <- Destroy{name: entry.boxName}
			cs.gui.Signal()
			if cs.selected == id {
				cs.selected = 0
			}
			continue
		}
		newEntries = append(newEntries, entry)
	}

	if len(newEntries) == len(cs.entries) {
		panic("unknown id passed to Remove")
	}
	cs.entries = newEntries
}

func (cs *listUI) Deselect() {
	if cs.selected == 0 {
		return
	}

	var currentlySelected *listItem

	for i, entry := range cs.entries {
		if entry.id == cs.selected {
			currentlySelected = &cs.entries[i]
			break
		}
	}

	cs.gui.Actions() <- SetBackground{name: currentlySelected.boxName, color: currentlySelected.background}
	cs.selected = 0
	cs.gui.Signal()
}

func (cs *listUI) Select(id uint64) {
	if id == cs.selected {
		return
	}

	var currentlySelected, newSelected *listItem

	for i, entry := range cs.entries {
		if entry.id == cs.selected {
			currentlySelected = &cs.entries[i]
		} else if entry.id == id {
			newSelected = &cs.entries[i]
		}

		if currentlySelected != nil && newSelected != nil {
			break
		}
	}

	if newSelected == nil {
		panic("internal error")
	}

	if currentlySelected != nil {
		cs.gui.Actions() <- SetBackground{name: currentlySelected.boxName, color: currentlySelected.background}
	}
	cs.gui.Actions() <- SetBackground{name: newSelected.boxName, color: colorHighlight}
	cs.selected = id
	cs.gui.Signal()
}

func (cs *listUI) SetIndicator(id uint64, indicator Indicator) {
	for _, entry := range cs.entries {
		if entry.id == id {
			cs.gui.Actions() <- SetImage{name: entry.imageName, image: indicator}
			cs.gui.Signal()
			break
		}
	}
}

func (cs *listUI) SetLine(id uint64, line string) {
	for _, entry := range cs.entries {
		if entry.id == id {
			cs.gui.Actions() <- SetText{name: entry.lineName, text: line}
			cs.gui.Signal()
			break
		}
	}
}

// SetSubline sets the second row of text in an entry.
func (cs *listUI) SetSubline(id uint64, subline string) {
	for _, entry := range cs.entries {
		if entry.id == id {
			if len(subline) > 0 {
				cs.gui.Actions() <- SetText{name: entry.sublineTextName, text: subline}
			} else {
				cs.gui.Actions() <- Destroy{name: entry.sublineTextName}
			}
			cs.gui.Signal()
			break
		}
	}
}

func (cs *listUI) SetBackground(id uint64, color uint32) {
	for i, entry := range cs.entries {
		if entry.id == id {
			cs.entries[i].background = color
			if cs.selected != id {
				cs.gui.Actions() <- SetBackground{name: entry.boxName, color: color}
				cs.gui.Signal()
			}
			break
		}
	}
}

func (cs *listUI) newIdent() string {
	id := cs.vboxName + "-" + strconv.Itoa(cs.nextId)
	cs.nextId++
	return id
}
<commit_msg>Fix crash when adding a subline to a listUI entry.<commit_after>// +build !nogui

package main

import (
	"strconv"
)

// listUI manages the sections in the left-hand side list. It contains a number
// of items, which may have subheadlines and indicators (coloured dots).
type listUI struct {
	gui        GUI
	vboxName   string
	entries    []listItem
	selected   uint64
	nextId     int
	hasSubline bool
}

type listItem struct {
	id                                                                           uint64
	name, sepName, boxName, imageName, lineName, sublineTextName, sublineBoxName string
	insensitive                                                                  bool
	hasSubline                                                                   bool
	background                                                                   uint32
}

func (cs *listUI) Event(event interface{}) (uint64, bool) {
	if click, ok := event.(Click); ok {
		for _, entry := range cs.entries {
			if click.name == entry.boxName {
				if entry.insensitive {
					return 0, false
				}
				return entry.id, true
			}
		}
	}

	return 0, false
}

func sublineLabel(name, text string) Label {
	return Label{
		widgetBase: widgetBase{
			padding:    5,
			foreground: colorSubline,
			font:       fontListSubline,
			name:       name,
		},
		text: text,
	}
}

func (cs *listUI) Add(id uint64, name, subline string, indicator Indicator) {
	c := listItem{
		id:              id,
		name:            name,
		sepName:         cs.newIdent(),
		boxName:         cs.newIdent(),
		imageName:       cs.newIdent(),
		lineName:        cs.newIdent(),
		sublineTextName: cs.newIdent(),
		sublineBoxName:  cs.newIdent(),
		background:      colorGray,
		hasSubline:      len(subline) > 0,
	}
	cs.entries = append(cs.entries, c)
	index := len(cs.entries) - 1

	if index > 0 {
		// Add the separator bar.
		cs.gui.Actions() <- AddToBox{
			box:   cs.vboxName,
			pos:   index*2 - 1,
			child: EventBox{widgetBase: widgetBase{height: 1, background: 0xe5e6e6, name: c.sepName}},
		}
	}

	children := []Widget{
		HBox{
			widgetBase: widgetBase{padding: 1},
			children: []Widget{
				Label{
					widgetBase: widgetBase{
						name:    c.lineName,
						padding: 5,
						font:    fontListEntry,
					},
					text: name,
				},
			},
		},
	}

	var sublineChildren []Widget

	if len(subline) > 0 {
		sublineChildren = append(sublineChildren, sublineLabel(c.sublineTextName, subline))
	}

	sublineChildren = append(sublineChildren, Image{
		widgetBase: widgetBase{
			padding: 4,
			expand:  true,
			fill:    true,
			name:    c.imageName,
		},
		image:  indicator,
		xAlign: 1,
		yAlign: 0.5,
	})

	children = append(children, HBox{
		widgetBase: widgetBase{padding: 1, name: c.sublineBoxName},
		children:   sublineChildren,
	})

	cs.gui.Actions() <- AddToBox{
		box: cs.vboxName,
		pos: index * 2,
		child: EventBox{
			widgetBase: widgetBase{name: c.boxName, background: c.background},
			child:      VBox{children: children},
		},
	}
	cs.gui.Signal()
}

func (cs *listUI) SetInsensitive(id uint64) {
	for i, entry := range cs.entries {
		if entry.id == id {
			cs.entries[i].insensitive = true
		}
	}
}

func (cs *listUI) Remove(id uint64) {
	newEntries := make([]listItem, 0, len(cs.entries))
	for i, entry := range cs.entries {
		if entry.id == id {
			if i > 0 {
				cs.gui.Actions() <- Destroy{name: entry.sepName}
			}
			cs.gui.Actions() <- Destroy{name: entry.boxName}
			cs.gui.Signal()
			if cs.selected == id {
				cs.selected = 0
			}
			continue
		}
		newEntries = append(newEntries, entry)
	}

	if len(newEntries) == len(cs.entries) {
		panic("unknown id passed to Remove")
	}
	cs.entries = newEntries
}

func (cs *listUI) Deselect() {
	if cs.selected == 0 {
		return
	}

	var currentlySelected *listItem

	for i, entry := range cs.entries {
		if entry.id == cs.selected {
			currentlySelected = &cs.entries[i]
			break
		}
	}

	cs.gui.Actions() <- SetBackground{name: currentlySelected.boxName, color: currentlySelected.background}
	cs.selected = 0
	cs.gui.Signal()
}

func (cs *listUI) Select(id uint64) {
	if id == cs.selected {
		return
	}

	var currentlySelected, newSelected *listItem

	for i, entry := range cs.entries {
		if entry.id == cs.selected {
			currentlySelected = &cs.entries[i]
		} else if entry.id == id {
			newSelected = &cs.entries[i]
		}

		if currentlySelected != nil && newSelected != nil {
			break
		}
	}

	if newSelected == nil {
		panic("internal error")
	}

	if currentlySelected != nil {
		cs.gui.Actions() <- SetBackground{name: currentlySelected.boxName, color: currentlySelected.background}
	}
	cs.gui.Actions() <- SetBackground{name: newSelected.boxName, color: colorHighlight}
	cs.selected = id
	cs.gui.Signal()
}

func (cs *listUI) SetIndicator(id uint64, indicator Indicator) {
	for _, entry := range cs.entries {
		if entry.id == id {
			cs.gui.Actions() <- SetImage{name: entry.imageName, image: indicator}
			cs.gui.Signal()
			break
		}
	}
}

func (cs *listUI) SetLine(id uint64, line string) {
	for _, entry := range cs.entries {
		if entry.id == id {
			cs.gui.Actions() <- SetText{name: entry.lineName, text: line}
			cs.gui.Signal()
			break
		}
	}
}

// SetSubline sets the second row of text in an entry.
func (cs *listUI) SetSubline(id uint64, subline string) {
	for _, entry := range cs.entries {
		if entry.id == id {
			if entry.hasSubline {
				if len(subline) > 0 {
					cs.gui.Actions() <- SetText{name: entry.sublineTextName, text: subline}
				} else {
					cs.gui.Actions() <- Destroy{name: entry.sublineTextName}
					entry.hasSubline = false
				}
			} else if len(subline) > 0 {
				cs.gui.Actions() <- AddToBox{
					box:   entry.sublineBoxName,
					pos:   0,
					child: sublineLabel(entry.sublineTextName, subline),
				}
				entry.hasSubline = true
			}
			cs.gui.Signal()
			break
		}
	}
}

func (cs *listUI) SetBackground(id uint64, color uint32) {
	for i, entry := range cs.entries {
		if entry.id == id {
			cs.entries[i].background = color
			if cs.selected != id {
				cs.gui.Actions() <- SetBackground{name: entry.boxName, color: color}
				cs.gui.Signal()
			}
			break
		}
	}
}

func (cs *listUI) newIdent() string {
	id := cs.vboxName + "-" + strconv.Itoa(cs.nextId)
	cs.nextId++
	return id
}
<|endoftext|>


<commit_before>package client

import (
	"log"
	"os"
)

// A LogI manages logging
type LogI interface {
	// Err writes the output for a error event
	Err(v ...interface{})

	// Debug writes the output for a debug event
	Debug(v ...interface{})

	// DebugF writes the output for a debug event. Arguments are handled in the manner of fmt.Printf.
	DebugF(format string, v ...interface{})
}

// A Log represents an active logging object.
type Log struct {
	errorLogger *log.Logger
	debugLogger *log.Logger
	isDebug     bool
}

// Err writes the output for a error event
func (l *Log) Err(v ...interface{}) {
	if l.isDebug {
		l.errorLogger.Println(v)
	}
}

// Debug writes the output for a debug event
func (l *Log) Debug(v ...interface{}) {
	if l.isDebug {
		l.debugLogger.Println(v)
	}
}

// DebugF writes the output for a debug event. Arguments are handled in the manner of fmt.Printf.
func (l *Log) DebugF(format string, v ...interface{}) {
	if l.isDebug {
		l.debugLogger.Printf(format, v)
	}
}

// L is the default implementation of LogI.
var L LogI

// PrepareLogger prepares L object.
func PrepareLogger(debug bool) {
	L = &Log{
		errorLogger: log.New(os.Stderr, "[ERROR] ", log.LstdFlags),
		debugLogger: log.New(os.Stdout, "[DEBUG] ", log.LstdFlags),
		isDebug:     debug,
	}
}
<commit_msg>fix logger<commit_after>package client

import (
	"log"
	"os"
)

// A LogI manages logging
type LogI interface {
	// Err writes the output for a error event
	Err(v ...interface{})

	// Debug writes the output for a debug event
	Debug(v ...interface{})

	// DebugF writes the output for a debug event. Arguments are handled in the manner of fmt.Printf.
	DebugF(format string, v ...interface{})
}

// A Log represents an active logging object.
type Log struct {
	errorLogger *log.Logger
	debugLogger *log.Logger
	isDebug     bool
}

// Err writes the output for a error event
func (l *Log) Err(v ...interface{}) {
	if l.isDebug {
		l.errorLogger.Println(v)
	}
}

// Debug writes the output for a debug event
func (l *Log) Debug(v ...interface{}) {
	if l.isDebug {
		l.debugLogger.Println(v)
	}
}

// DebugF writes the output for a debug event. Arguments are handled in the manner of fmt.Printf.
func (l *Log) DebugF(format string, v ...interface{}) {
	if l.isDebug {
		l.debugLogger.Printf(format, v...)
	}
}

// L is the default implementation of LogI.
var L LogI

// PrepareLogger prepares L object.
func PrepareLogger(debug bool) {
	L = &Log{
		errorLogger: log.New(os.Stderr, "[ERROR] ", log.LstdFlags),
		debugLogger: log.New(os.Stdout, "[DEBUG] ", log.LstdFlags),
		isDebug:     debug,
	}
}
<|endoftext|>


<commit_before>package editor

import (
	"strings"
	"testing"
)

func TestInsertMode(t *testing.T) {
	i := insert{}
	i.init()
	input := "abc"
	i.in = NewReader(strings.NewReader(input))
	for n := range input {
		c, _, err := i.Run()
		if err != nil {
			t.Errorf("Run: %v", err)
		}
		if c != cont {
			t.Errorf("Run: want %v, but got %v", cont, c)
		}
		if got, want := string(i.Runes()), input[:n+1]; got != want {
			t.Errorf("Run: want %v, but got %v", want, got)
		}
	}
}

func TestInputMatches(t *testing.T) {
	i := insert{}
	i.init()
	input := "a 'b"
	i.in = NewReader(strings.NewReader(input))
	tt := []string{"a", "a ", "a ''", "a 'b'"}
	for n := range input {
		c, _, err := i.Run()
		if err != nil {
			t.Errorf("Run: %v", err)
		}
		if c != cont {
			t.Errorf("Run: want %v, but got %v", cont, c)
		}
		if got, want := string(i.Runes()), tt[n]; got != want {
			t.Errorf("Run/%d: want %q, but got %q", n, want, got)
		}
	}
}
<commit_msg>Unify two test functions<commit_after>package editor

import (
	"fmt"
	"strings"
	"testing"
)

func testInsertMode(input string, tt []string) func(*testing.T) {
	return func(t *testing.T) {
		i := insert{}
		i.init()
		i.in = NewReader(strings.NewReader(input))
		for n := range input {
			c, _, err := i.Run()
			if err != nil {
				t.Errorf("Run: %v", err)
			}
			if c != cont {
				t.Errorf("Run: want %v, but got %v", cont, c)
			}
			if got, want := string(i.Runes()), tt[n]; got != want {
				t.Errorf("Run/%d: want %q, but got %q", n, want, got)
			}
		}
	}
}

func TestInputMatches(t *testing.T) {
	tests := []struct {
		input  string
		expect []string
	}{
		{
			input:  "abc",
			expect: []string{"a", "ab", "abc"},
		},
		{
			input:  "a 'b",
			expect: []string{"a", "a ", "a ''", "a 'b'"},
		},
	}
	for n, test := range tests {
		t.Run(fmt.Sprintf("%d", n), testInsertMode(test.input, test.expect))
	}
}
<|endoftext|>


<commit_before>package main

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

func main() {
	outChan := make(chan int, 100)
	errChan := make(chan error, 1)
	finishChan := make(chan struct{})
	wg := sync.WaitGroup{}
	wg.Add(100)
	for i := 0; i < 100; i++ {
		go func(outChan chan<- int, errChan chan<- error, val int, wg *sync.WaitGroup) {
			time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
			fmt.Println("finished job id:", val)
			outChan <- val
			if val == 60 {
				errChan <- errors.New("error in 60")
			}
			wg.Done()
		}(outChan, errChan, i, &wg)
	}

	go func() {
		wg.Wait()
		fmt.Println("finish all job")
		close(finishChan)
	}()

Loop:
	for {
		select {
		case val := <-outChan:
			fmt.Println("finished:", val)
		case err := <-errChan:
			fmt.Println("error:", err)
			break Loop
		case <-finishChan:
			break Loop
		case <-time.After(100000 * time.Millisecond):
			break Loop
		}
	}
}
<commit_msg>chore: add defer<commit_after>package main

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

func main() {
	outChan := make(chan int, 100)
	errChan := make(chan error, 1)
	finishChan := make(chan struct{})
	wg := sync.WaitGroup{}
	wg.Add(100)
	for i := 0; i < 100; i++ {
		go func(outChan chan<- int, errChan chan<- error, val int, wg *sync.WaitGroup) {
			defer wg.Done()
			time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
			fmt.Println("finished job id:", val)
			outChan <- val
			if val == 60 {
				errChan <- errors.New("error in 60")
			}

		}(outChan, errChan, i, &wg)
	}

	go func() {
		wg.Wait()
		fmt.Println("finish all job")
		close(finishChan)
	}()

Loop:
	for {
		select {
		case val := <-outChan:
			fmt.Println("finished:", val)
		case err := <-errChan:
			fmt.Println("error:", err)
			break Loop
		case <-finishChan:
			break Loop
		case <-time.After(100000 * time.Millisecond):
			break Loop
		}
	}
}
<|endoftext|>


<commit_before>package entities

import (
	"strconv"

	"github.com/oakmound/oak/collision"
	"github.com/oakmound/oak/event"
	"github.com/oakmound/oak/render"
)

// Reactive is parallel to Solid, but has a Reactive collision space instead of
// a regular collision space
type Reactive struct {
	Doodad
	W, H   float64
	RSpace *collision.ReactiveSpace
	Tree   *collision.Tree
}

// NewReactive returns a new Reactive struct. The added space will
// be added to the input tree, or DefTree if none is given.
func NewReactive(x, y, w, h float64, r render.Renderable, tree *collision.Tree, cid event.CID) *Reactive {
	rct := Reactive{}
	cid = cid.Parse(&rct)
	rct.Doodad = *NewDoodad(x, y, r, cid)
	rct.W = w
	rct.H = h
	rct.RSpace = collision.NewEmptyReactiveSpace(collision.NewSpace(x, y, w, h, cid))
	if tree == nil {
		tree = collision.DefTree
	}
	rct.Tree = tree
	rct.Tree.Add(rct.RSpace.Space)
	return &rct
}

// SetDim sets the dimensions of this reactive's space and it's logical dimensions
func (r *Reactive) SetDim(w, h float64) {
	r.SetLogicDim(w, h)
	r.RSpace.SetDim(w, h)
}

// GetLogicDim returns this Reactive's width and height
// todo: move wh into their own struct to compose into solid and reactive
func (r *Reactive) GetLogicDim() (float64, float64) {
	return r.W, r.H
}

// SetLogicDim sets the logical width and height of this reactive
// without changing the real dimensions of its collision space
func (r *Reactive) SetLogicDim(w, h float64) {
	r.W = w
	r.H = h
}

// SetSpace sets this reactive's collision space to the given reactive space,
// updating it's collision tree to include it.
func (r *Reactive) SetSpace(sp *collision.ReactiveSpace) {
	r.Tree.Remove(r.RSpace.Space)
	r.RSpace = sp
	r.Tree.Add(r.RSpace.Space)
}

// GetSpace returns this reactive's space underlying its RSpace
func (r *Reactive) GetSpace() *collision.Space {
	return r.RSpace.Space
}

// GetReactiveSpace returns this reactive's RSpace
func (r *Reactive) GetReactiveSpace() *collision.ReactiveSpace {
	return r.RSpace
}

// Overwrites

// Init satisfies event.Entity
func (r *Reactive) Init() event.CID {
	r.CID = event.NextID(r)
	return r.CID
}

// ShiftPos acts like SetPos if given r.X()+x, r.Y()+y
func (r *Reactive) ShiftPos(x, y float64) {
	r.SetPos(r.X()+x, r.Y()+y)
}

// SetPos sets this reactive's logical, renderable, and collision position to be x,y
func (r *Reactive) SetPos(x, y float64) {
	r.SetLogicPos(x, y)
	r.R.SetPos(x, y)
	r.Tree.UpdateSpace(r.X(), r.Y(), r.W, r.H, r.RSpace.Space)
}

// Destroy destroys this reactive's doodad component and removes its space
// from it's collision tree
func (r *Reactive) Destroy() {
	r.Doodad.Destroy()
	r.Tree.Remove(r.RSpace.Space)
}

func (r *Reactive) String() string {
	st := "Reactive:\n{"
	st += r.Doodad.String()
	st += " }, \n"
	w := strconv.FormatFloat(r.W, 'f', 2, 32)
	h := strconv.FormatFloat(r.H, 'f', 2, 32)
	st += "W: " + w + ", H: " + h
	st += ",\nS:{ "
	st += r.RSpace.String()
	st += "}"
	return st
}
<commit_msg>Entity Reactive removes from tree before performing destruction<commit_after>package entities

import (
	"strconv"

	"github.com/oakmound/oak/collision"
	"github.com/oakmound/oak/event"
	"github.com/oakmound/oak/render"
)

// Reactive is parallel to Solid, but has a Reactive collision space instead of
// a regular collision space
type Reactive struct {
	Doodad
	W, H   float64
	RSpace *collision.ReactiveSpace
	Tree   *collision.Tree
}

// NewReactive returns a new Reactive struct. The added space will
// be added to the input tree, or DefTree if none is given.
func NewReactive(x, y, w, h float64, r render.Renderable, tree *collision.Tree, cid event.CID) *Reactive {
	rct := Reactive{}
	cid = cid.Parse(&rct)
	rct.Doodad = *NewDoodad(x, y, r, cid)
	rct.W = w
	rct.H = h
	rct.RSpace = collision.NewEmptyReactiveSpace(collision.NewSpace(x, y, w, h, cid))
	if tree == nil {
		tree = collision.DefTree
	}
	rct.Tree = tree
	rct.Tree.Add(rct.RSpace.Space)
	return &rct
}

// SetDim sets the dimensions of this reactive's space and it's logical dimensions
func (r *Reactive) SetDim(w, h float64) {
	r.SetLogicDim(w, h)
	r.RSpace.SetDim(w, h)
}

// GetLogicDim returns this Reactive's width and height
// todo: move wh into their own struct to compose into solid and reactive
func (r *Reactive) GetLogicDim() (float64, float64) {
	return r.W, r.H
}

// SetLogicDim sets the logical width and height of this reactive
// without changing the real dimensions of its collision space
func (r *Reactive) SetLogicDim(w, h float64) {
	r.W = w
	r.H = h
}

// SetSpace sets this reactive's collision space to the given reactive space,
// updating it's collision tree to include it.
func (r *Reactive) SetSpace(sp *collision.ReactiveSpace) {
	r.Tree.Remove(r.RSpace.Space)
	r.RSpace = sp
	r.Tree.Add(r.RSpace.Space)
}

// GetSpace returns this reactive's space underlying its RSpace
func (r *Reactive) GetSpace() *collision.Space {
	return r.RSpace.Space
}

// GetReactiveSpace returns this reactive's RSpace
func (r *Reactive) GetReactiveSpace() *collision.ReactiveSpace {
	return r.RSpace
}

// Overwrites

// Init satisfies event.Entity
func (r *Reactive) Init() event.CID {
	r.CID = event.NextID(r)
	return r.CID
}

// ShiftPos acts like SetPos if given r.X()+x, r.Y()+y
func (r *Reactive) ShiftPos(x, y float64) {
	r.SetPos(r.X()+x, r.Y()+y)
}

// SetPos sets this reactive's logical, renderable, and collision position to be x,y
func (r *Reactive) SetPos(x, y float64) {
	r.SetLogicPos(x, y)
	r.R.SetPos(x, y)
	r.Tree.UpdateSpace(r.X(), r.Y(), r.W, r.H, r.RSpace.Space)
}

// Destroy destroys this reactive's doodad component and removes its space
// from it's collision tree
func (r *Reactive) Destroy() {
	r.Tree.Remove(r.RSpace.Space)
	r.Doodad.Destroy()
}

func (r *Reactive) String() string {
	st := "Reactive:\n{"
	st += r.Doodad.String()
	st += " }, \n"
	w := strconv.FormatFloat(r.W, 'f', 2, 32)
	h := strconv.FormatFloat(r.H, 'f', 2, 32)
	st += "W: " + w + ", H: " + h
	st += ",\nS:{ "
	st += r.RSpace.String()
	st += "}"
	return st
}
<|endoftext|>


<commit_before>package dicebot

import (
	"fmt"
	"github.com/arkie/hackyslack2"
	"github.com/arkie/hackyslack2/dicebot/roll"
	"testing"
)

var FormatTests = []string{
	"",
	"d6",
	"10d10-5",
	"123456789d123456789+123456789",
	"1d1 2d2 3d3",
	"10d10>5",
	"10d10<5",
	"10d10k5",
	"2d6 / 2d6",
	"mini 2d6 / 2d6",
}

func TestRollFormat(t *testing.T) {
	username := "TestUser"
	for _, test := range FormatTests {
		rolls := roll.Parse(test)
		verify := fmt.Sprint("@", username, " rolled ")
		sum := 0
		for i, result := range rolls {
			result.Roll()
			// Use the last roll as the final.
			total := result.Total
			switch result.Operator {
			case "+":
				sum += result.Total
			case "-":
				sum -= result.Total
			case "*":
				sum *= result.Total
			case "/":
				sum /= result.Total
			}
			if i == 0 {
				verify += fmt.Sprint("*", total, "*")
			} else {
				verify += fmt.Sprint(" ", result.Operator, " *", total, "*")
			}
		}
		if len(rolls) > 1 {
			verify += fmt.Sprint(" = *", sum, "*")
		}
		resp := formatRoll(username, false, rolls)
		if resp["response_type"] != "in_channel" {
			t.Error("Incorrect response type", resp["response_type"])
		}
		attach := resp["attachments"].([]hackyslack.D)[0]
		if attach["color"] == "" {
			t.Error("Missing color")
		}
		if attach["text"] != verify {
			t.Error("Incorrect response text", attach["text"], "instead of", verify)
		}
	}
}
<commit_msg>Add test for previous panic case<commit_after>package dicebot

import (
	"fmt"
	"github.com/arkie/hackyslack2"
	"github.com/arkie/hackyslack2/dicebot/roll"
	"testing"
)

var FormatTests = []string{
	"",
	"d6",
	"10d10-5",
	"123456789d123456789+123456789",
	"1d1 2d2 3d3",
	"10d10>5",
	"10d10<5",
	"10d10k5",
	"2d6 / 2d6",
	"mini 2d6 / 2d6",
}

func TestRollFormat(t *testing.T) {
	username := "TestUser"
	for _, test := range FormatTests {
		rolls := roll.Parse(test)
		verify := fmt.Sprint("@", username, " rolled ")
		sum := 0
		for i, result := range rolls {
			result.Roll()
			// Use the last roll as the final.
			total := result.Total
			switch result.Operator {
			case "+":
				sum += result.Total
			case "-":
				sum -= result.Total
			case "*":
				sum *= result.Total
			case "/":
				sum /= result.Total
			}
			if i == 0 {
				verify += fmt.Sprint("*", total, "*")
			} else {
				verify += fmt.Sprint(" ", result.Operator, " *", total, "*")
			}
		}
		if len(rolls) > 1 {
			verify += fmt.Sprint(" = *", sum, "*")
		}
		resp := formatRoll(username, false, rolls)
		if resp["response_type"] != "in_channel" {
			t.Error("Incorrect response type", resp["response_type"])
		}
		attach := resp["attachments"].([]hackyslack.D)[0]
		if attach["color"] == "" {
			t.Error("Missing color")
		}
		if attach["text"] != verify {
			t.Error("Incorrect response text", attach["text"], "instead of", verify)
		}
	}
}

// Test for panics.
func TestCommand(t *testing.T) {
	for _, test := range FormatTests {
		resp := command(hackyslack.Args{
			Text:     test,
			UserName: "CommandTest",
		})
		if resp["response_type"] != "in_channel" {
			t.Error("Incorrect response type", resp["response_type"])
		}
	}
}
<|endoftext|>