<commit_before>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package connpool

import (
	"sync"
	"time"

	"golang.org/x/net/context"

	"vitess.io/vitess/go/trace"
	"vitess.io/vitess/go/mysql"
	"vitess.io/vitess/go/pools"
	"vitess.io/vitess/go/stats"
	"vitess.io/vitess/go/vt/callerid"
	"vitess.io/vitess/go/vt/dbconnpool"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv"

	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
)

// ErrConnPoolClosed is returned when the connection pool is closed.
var ErrConnPoolClosed = vterrors.New(vtrpcpb.Code_INTERNAL, "internal error: unexpected: conn pool is closed")

// usedNames is for preventing expvar from panicking. Tests
// create pool objects multiple time. If a name was previously
// used, expvar initialization is skipped.
// TODO(sougou): Find a way to still crash if this happened
// through non-test code.
var usedNames = make(map[string]bool)

// MySQLChecker defines the CheckMySQL interface that lower
// level objects can use to call back into TabletServer.
type MySQLChecker interface {
	CheckMySQL()
}

// Pool implements a custom connection pool for tabletserver.
// It's similar to dbconnpool.ConnPool, but the connections it creates
// come with built-in ability to kill in-flight queries. These connections
// also trigger a CheckMySQL call if we fail to connect to MySQL.
// Other than the connection type, ConnPool maintains an additional
// pool of dba connections that are used to kill connections.
type Pool struct {
	mu             sync.Mutex
	connections    *pools.ResourcePool
	capacity       int
	idleTimeout    time.Duration
	dbaPool        *dbconnpool.ConnectionPool
	checker        MySQLChecker
	appDebugParams *mysql.ConnParams
}

// New creates a new Pool. The name is used
// to publish stats only.
func New(
	name string,
	capacity int,
	idleTimeout time.Duration,
	checker MySQLChecker) *Pool {
	cp := &Pool{
		capacity:    capacity,
		idleTimeout: idleTimeout,
		dbaPool:     dbconnpool.NewConnectionPool("", 1, idleTimeout),
		checker:     checker,
	}
	if name == "" || usedNames[name] {
		return cp
	}
	usedNames[name] = true
	stats.NewGaugeFunc(name+"Capacity", "Tablet server conn pool capacity", cp.Capacity)
	stats.NewGaugeFunc(name+"Available", "Tablet server conn pool available", cp.Available)
	stats.NewGaugeFunc(name+"Active", "Tablet server conn pool active", cp.Active)
	stats.NewGaugeFunc(name+"InUse", "Tablet server conn pool in use", cp.InUse)
	stats.NewGaugeFunc(name+"MaxCap", "Tablet server conn pool max cap", cp.MaxCap)
	stats.NewCounterFunc(name+"WaitCount", "Tablet server conn pool wait count", cp.WaitCount)
	stats.NewCounterDurationFunc(name+"WaitTime", "Tablet server wait time", cp.WaitTime)
	stats.NewGaugeDurationFunc(name+"IdleTimeout", "Tablet server idle timeout", cp.IdleTimeout)
	stats.NewCounterFunc(name+"IdleClosed", "Tablet server conn pool idle closed", cp.IdleClosed)
	return cp
}

func (cp *Pool) pool() (p *pools.ResourcePool) {
	cp.mu.Lock()
	p = cp.connections
	cp.mu.Unlock()
	return p
}

// Open must be called before starting to use the pool.
func (cp *Pool) Open(appParams, dbaParams, appDebugParams *mysql.ConnParams) {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	f := func() (pools.Resource, error) {
		return NewDBConn(cp, appParams)
	}
	cp.connections = pools.NewResourcePool(f, cp.capacity, cp.capacity, cp.idleTimeout)
	cp.appDebugParams = appDebugParams

	cp.dbaPool.Open(dbaParams, tabletenv.MySQLStats)
}

// Close will close the pool and wait for connections to be returned before
// exiting.
func (cp *Pool) Close() {
	p := cp.pool()
	if p == nil {
		return
	}
	// We should not hold the lock while calling Close
	// because it waits for connections to be returned.
	p.Close()
	cp.mu.Lock()
	cp.connections = nil
	cp.mu.Unlock()
	cp.dbaPool.Close()
}

// Get returns a connection.
// You must call Recycle on DBConn once done.
func (cp *Pool) Get(ctx context.Context) (*DBConn, error) {
	span, ctx := trace.NewSpan(ctx, "Pool.Get")
	defer span.Finish()

	if cp.isCallerIDAppDebug(ctx) {
		return NewDBConnNoPool(cp.appDebugParams, cp.dbaPool)
	}
	p := cp.pool()
	if p == nil {
		return nil, ErrConnPoolClosed
	}
	r, err := p.Get(ctx)
	if err != nil {
		return nil, err
	}
	return r.(*DBConn), nil
}

// Put puts a connection into the pool.
func (cp *Pool) Put(conn *DBConn) {
	p := cp.pool()
	if p == nil {
		panic(ErrConnPoolClosed)
	}
	if conn == nil {
		p.Put(nil)
	} else {
		p.Put(conn)
	}
}

// SetCapacity alters the size of the pool at runtime.
func (cp *Pool) SetCapacity(capacity int) (err error) {
	cp.mu.Lock()
	defer cp.mu.Unlock()
	if cp.connections != nil {
		err = cp.connections.SetCapacity(capacity)
		if err != nil {
			return err
		}
	}
	cp.capacity = capacity
	return nil
}

// SetIdleTimeout sets the idleTimeout on the pool.
func (cp *Pool) SetIdleTimeout(idleTimeout time.Duration) {
	cp.mu.Lock()
	defer cp.mu.Unlock()
	if cp.connections != nil {
		cp.connections.SetIdleTimeout(idleTimeout)
	}
	cp.dbaPool.SetIdleTimeout(idleTimeout)
	cp.idleTimeout = idleTimeout
}

// StatsJSON returns the pool stats as a JSON object.
func (cp *Pool) StatsJSON() string {
	p := cp.pool()
	if p == nil {
		return "{}"
	}
	return p.StatsJSON()
}

// Capacity returns the pool capacity.
func (cp *Pool) Capacity() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.Capacity()
}

// Available returns the number of available connections in the pool
func (cp *Pool) Available() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.Available()
}

// Active returns the number of active connections in the pool
func (cp *Pool) Active() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.Active()
}

// InUse returns the number of in-use connections in the pool
func (cp *Pool) InUse() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.InUse()
}

// MaxCap returns the maximum size of the pool
func (cp *Pool) MaxCap() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.MaxCap()
}

// WaitCount returns how many clients are waiting for a connection
func (cp *Pool) WaitCount() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.WaitCount()
}

// WaitTime return the pool WaitTime.
func (cp *Pool) WaitTime() time.Duration {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.WaitTime()
}

// IdleTimeout returns the idle timeout for the pool.
func (cp *Pool) IdleTimeout() time.Duration {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.IdleTimeout()
}

// IdleClosed returns the number of closed connections for the pool.
func (cp *Pool) IdleClosed() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.IdleClosed()
}

func (cp *Pool) isCallerIDAppDebug(ctx context.Context) bool {
	if cp.appDebugParams == nil || cp.appDebugParams.Uname == "" {
		return false
	}
	callerID := callerid.ImmediateCallerIDFromContext(ctx)
	return callerID != nil && callerID.Username == cp.appDebugParams.Uname
}
<commit_msg>Move pool trace stats up a level.<commit_after>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package connpool

import (
	"sync"
	"time"

	"golang.org/x/net/context"

	"vitess.io/vitess/go/mysql"
	"vitess.io/vitess/go/pools"
	"vitess.io/vitess/go/stats"
	"vitess.io/vitess/go/trace"
	"vitess.io/vitess/go/vt/callerid"
	"vitess.io/vitess/go/vt/dbconnpool"
	"vitess.io/vitess/go/vt/vterrors"
	"vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv"

	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
)

// ErrConnPoolClosed is returned when the connection pool is closed.
var ErrConnPoolClosed = vterrors.New(vtrpcpb.Code_INTERNAL, "internal error: unexpected: conn pool is closed")

// usedNames is for preventing expvar from panicking. Tests
// create pool objects multiple time. If a name was previously
// used, expvar initialization is skipped.
// TODO(sougou): Find a way to still crash if this happened
// through non-test code.
var usedNames = make(map[string]bool)

// MySQLChecker defines the CheckMySQL interface that lower
// level objects can use to call back into TabletServer.
type MySQLChecker interface {
	CheckMySQL()
}

// Pool implements a custom connection pool for tabletserver.
// It's similar to dbconnpool.ConnPool, but the connections it creates
// come with built-in ability to kill in-flight queries. These connections
// also trigger a CheckMySQL call if we fail to connect to MySQL.
// Other than the connection type, ConnPool maintains an additional
// pool of dba connections that are used to kill connections.
type Pool struct {
	mu             sync.Mutex
	connections    *pools.ResourcePool
	capacity       int
	idleTimeout    time.Duration
	dbaPool        *dbconnpool.ConnectionPool
	checker        MySQLChecker
	appDebugParams *mysql.ConnParams
}

// New creates a new Pool. The name is used
// to publish stats only.
func New(
	name string,
	capacity int,
	idleTimeout time.Duration,
	checker MySQLChecker) *Pool {
	cp := &Pool{
		capacity:    capacity,
		idleTimeout: idleTimeout,
		dbaPool:     dbconnpool.NewConnectionPool("", 1, idleTimeout),
		checker:     checker,
	}
	if name == "" || usedNames[name] {
		return cp
	}
	usedNames[name] = true
	stats.NewGaugeFunc(name+"Capacity", "Tablet server conn pool capacity", cp.Capacity)
	stats.NewGaugeFunc(name+"Available", "Tablet server conn pool available", cp.Available)
	stats.NewGaugeFunc(name+"Active", "Tablet server conn pool active", cp.Active)
	stats.NewGaugeFunc(name+"InUse", "Tablet server conn pool in use", cp.InUse)
	stats.NewGaugeFunc(name+"MaxCap", "Tablet server conn pool max cap", cp.MaxCap)
	stats.NewCounterFunc(name+"WaitCount", "Tablet server conn pool wait count", cp.WaitCount)
	stats.NewCounterDurationFunc(name+"WaitTime", "Tablet server wait time", cp.WaitTime)
	stats.NewGaugeDurationFunc(name+"IdleTimeout", "Tablet server idle timeout", cp.IdleTimeout)
	stats.NewCounterFunc(name+"IdleClosed", "Tablet server conn pool idle closed", cp.IdleClosed)
	return cp
}

func (cp *Pool) pool() (p *pools.ResourcePool) {
	cp.mu.Lock()
	p = cp.connections
	cp.mu.Unlock()
	return p
}

// Open must be called before starting to use the pool.
func (cp *Pool) Open(appParams, dbaParams, appDebugParams *mysql.ConnParams) {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	f := func() (pools.Resource, error) {
		return NewDBConn(cp, appParams)
	}
	cp.connections = pools.NewResourcePool(f, cp.capacity, cp.capacity, cp.idleTimeout)
	cp.appDebugParams = appDebugParams

	cp.dbaPool.Open(dbaParams, tabletenv.MySQLStats)
}

// Close will close the pool and wait for connections to be returned before
// exiting.
func (cp *Pool) Close() {
	p := cp.pool()
	if p == nil {
		return
	}
	// We should not hold the lock while calling Close
	// because it waits for connections to be returned.
	p.Close()
	cp.mu.Lock()
	cp.connections = nil
	cp.mu.Unlock()
	cp.dbaPool.Close()
}

// Get returns a connection.
// You must call Recycle on DBConn once done.
func (cp *Pool) Get(ctx context.Context) (*DBConn, error) {
	span, ctx := trace.NewSpan(ctx, "Pool.Get")
	defer span.Finish()

	if cp.isCallerIDAppDebug(ctx) {
		return NewDBConnNoPool(cp.appDebugParams, cp.dbaPool)
	}
	p := cp.pool()
	if p == nil {
		return nil, ErrConnPoolClosed
	}
	span.Annotate("capacity", p.Capacity())
	span.Annotate("in_use", p.InUse())
	span.Annotate("available", p.Available())
	span.Annotate("active", p.Active())

	r, err := p.Get(ctx)
	if err != nil {
		return nil, err
	}
	return r.(*DBConn), nil
}

// Put puts a connection into the pool.
func (cp *Pool) Put(conn *DBConn) {
	p := cp.pool()
	if p == nil {
		panic(ErrConnPoolClosed)
	}
	if conn == nil {
		p.Put(nil)
	} else {
		p.Put(conn)
	}
}

// SetCapacity alters the size of the pool at runtime.
func (cp *Pool) SetCapacity(capacity int) (err error) {
	cp.mu.Lock()
	defer cp.mu.Unlock()
	if cp.connections != nil {
		err = cp.connections.SetCapacity(capacity)
		if err != nil {
			return err
		}
	}
	cp.capacity = capacity
	return nil
}

// SetIdleTimeout sets the idleTimeout on the pool.
func (cp *Pool) SetIdleTimeout(idleTimeout time.Duration) {
	cp.mu.Lock()
	defer cp.mu.Unlock()
	if cp.connections != nil {
		cp.connections.SetIdleTimeout(idleTimeout)
	}
	cp.dbaPool.SetIdleTimeout(idleTimeout)
	cp.idleTimeout = idleTimeout
}

// StatsJSON returns the pool stats as a JSON object.
func (cp *Pool) StatsJSON() string {
	p := cp.pool()
	if p == nil {
		return "{}"
	}
	return p.StatsJSON()
}

// Capacity returns the pool capacity.
func (cp *Pool) Capacity() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.Capacity()
}

// Available returns the number of available connections in the pool
func (cp *Pool) Available() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.Available()
}

// Active returns the number of active connections in the pool
func (cp *Pool) Active() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.Active()
}

// InUse returns the number of in-use connections in the pool
func (cp *Pool) InUse() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.InUse()
}

// MaxCap returns the maximum size of the pool
func (cp *Pool) MaxCap() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.MaxCap()
}

// WaitCount returns how many clients are waiting for a connection
func (cp *Pool) WaitCount() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.WaitCount()
}

// WaitTime return the pool WaitTime.
func (cp *Pool) WaitTime() time.Duration {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.WaitTime()
}

// IdleTimeout returns the idle timeout for the pool.
func (cp *Pool) IdleTimeout() time.Duration {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.IdleTimeout()
}

// IdleClosed returns the number of closed connections for the pool.
func (cp *Pool) IdleClosed() int64 {
	p := cp.pool()
	if p == nil {
		return 0
	}
	return p.IdleClosed()
}

func (cp *Pool) isCallerIDAppDebug(ctx context.Context) bool {
	if cp.appDebugParams == nil || cp.appDebugParams.Uname == "" {
		return false
	}
	callerID := callerid.ImmediateCallerIDFromContext(ctx)
	return callerID != nil && callerID.Username == cp.appDebugParams.Uname
}
<|endoftext|>


<commit_before>package graph

import (
	"os"
	"time"
)

// A photo album.
type Album struct {
	// The photo album ID. Publicly available.
	ID string
	// The profile that created this album. Publicly available. Contains the id and name fields.
	From Object
	// The title of the album. Publicly available.
	Name string
	// The description of the album. Available to everyone in Facebook.
	Description string
	// The location of the album. Available to everyone on Facebook.
	Location string
	// A link to this album on Facebook. Publicly available. Contains a valid URL.
	Link string
	// The privacy settings for the album. Available to everyone on Facebook.
	Privacy string
	// The number of photos in this album. Publicly available.
	Count string
	// The time the photo album was initially created. Publicly available. Contains a IETF RFC 3339 datetime.
	CreatedTime *time.Time
	// The last time the photo album was updated. Publicly available. Contains a IETF RFC 3339 datetime.
	UpdatedTime *time.Time

	// TODO: Connections
}

func getAlbums(url string) (as []Album, err os.Error) {
	data, err := getData(url)
	if err != nil {
		return
	}
	as = make([]Album, len(data))
	for i, v := range data {
		as[i], err = parseAlbum(v.(map[string]interface{}))
		if err != nil {
			return
		}
	}
	return
}

// Parses Album data. Returns nil for err if no error appeared.
func parseAlbum(value map[string]interface{}) (a Album, err os.Error) {
	for key, val := range value {
		switch key {
		case "id":
			a.ID = val.(string)
		case "from":
			a.From = parseObject(val.(map[string]interface{}))
		case "name":
			a.Name = val.(string)
		case "description":
			a.Description = val.(string)
		case "location":
			a.Location = val.(string)
		case "link":
			a.Link = val.(string)
		case "privacy":
			a.Privacy = val.(string)
		case "count":
			a.Count = val.(string)
		case "created_time":
			a.CreatedTime, err = parseTime(val.(string))
		case "updated_time":
			a.UpdatedTime, err = parseTime(val.(string))
		}
	}
	return
}
<commit_msg>Add GetPhotos, GetComments and GetPicture to Album struct.<commit_after>package graph

import (
	"os"
	"time"
)

// A photo album.
type Album struct {
	// The photo album ID. Publicly available.
	ID string
	// The profile that created this album. Publicly available. Contains the id and name fields.
	From Object
	// The title of the album. Publicly available.
	Name string
	// The description of the album. Available to everyone in Facebook.
	Description string
	// The location of the album. Available to everyone on Facebook.
	Location string
	// A link to this album on Facebook. Publicly available. Contains a valid URL.
	Link string
	// The privacy settings for the album. Available to everyone on Facebook.
	Privacy string
	// The number of photos in this album. Publicly available.
	Count string
	// The time the photo album was initially created. Publicly available. Contains a IETF RFC 3339 datetime.
	CreatedTime *time.Time
	// The last time the photo album was updated. Publicly available. Contains a IETF RFC 3339 datetime.
	UpdatedTime *time.Time

	// Connections
	photos   string
	comments string
	picture  string
}

// Gets the photos contained in this album. Publicly available.
// Returns an array of Photo objects.
func (a *Album) GetPhotos() (ps []Photo, err os.Error) {
	if a.photos == "" {
		err = os.NewError("Error: Album.GetPhotos: The photos URL is empty.")
	}
	return getPhotos(a.photos)
}

// Gets the comments made on this album. Available to everyone on Facebook.
// Returns an array of objects containing id, from, message and created_time fields.
func (a *Album) GetComments() (cs []Comment, err os.Error) {
	if a.comments == "" {
		err = os.NewError("Error: Album.GetComments: The comments URL is empty.")
	}
	return getComments(a.comments)
}

// Gets the album's cover photo. Publicly available.
// Returns an HTTP 302 URL string with the location set to the picture URL.
func (a *Album) GetPicture() (pic *Picture, err os.Error) {
	if a.picture == "" {
		err = os.NewError("Error: Album.GetPicture: The picture URL is empty.")
	}
	return NewPicture(a.picture), err
}

func getAlbums(url string) (as []Album, err os.Error) {
	data, err := getData(url)
	if err != nil {
		return
	}
	as = make([]Album, len(data))
	for i, v := range data {
		as[i], err = parseAlbum(v.(map[string]interface{}))
		if err != nil {
			return
		}
	}
	return
}

// Parses Album data. Returns nil for err if no error appeared.
func parseAlbum(value map[string]interface{}) (a Album, err os.Error) {
	for key, val := range value {
		switch key {
		case "id":
			a.ID = val.(string)
		case "from":
			a.From = parseObject(val.(map[string]interface{}))
		case "name":
			a.Name = val.(string)
		case "description":
			a.Description = val.(string)
		case "location":
			a.Location = val.(string)
		case "link":
			a.Link = val.(string)
		case "privacy":
			a.Privacy = val.(string)
		case "count":
			a.Count = val.(string)
		case "created_time":
			a.CreatedTime, err = parseTime(val.(string))
		case "updated_time":
			a.UpdatedTime, err = parseTime(val.(string))
		}
	}
	return
}
<|endoftext|>


<commit_before>package kv

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

	"log"

	"github.com/willf/bloom"
	"golang.org/x/net/context"
	elastic "gopkg.in/olivere/elastic.v5"

	"github.com/movio/kasper/util"
)

const indexSettings = `{
	"index.translog.durability": "async",
	"index.translog.sync_interval": "60s",
	"index.translog.flush_threshold_size": "512m"
}`

const indexMapping = `{
	"_all" : {
		"enabled" : false
	},
	"dynamic_templates": [{
		"no_index": {
			"mapping": {
				"index": "no"
			},
			"match": "*"
		}
	}]
}`

type indexAndType struct {
	indexName string
	indexType string
}

// BloomFilterConfig contains estimates to configure the optional bloom filter.
// See https://godoc.org/github.com/willf/bloom#NewWithEstimates for more information.
type BloomFilterConfig struct {
	// An estimate size the entire data set
	SizeEstimate uint
	// An estimate of the desired false positive rate
	FalsePositiveRate float64
}

// ElasticsearchKeyValueStore is a key-value storage that uses ElasticSearch.
// In this key-value store, all keys must have the format "<index>/<type>/<_id>".
// For performance reasons, this implementation create indexes with async durability.
// You must call Flush() at appropriate times to ensure Elasticsearch syncs its translog to disk.
// See: https://www.elastic.co/products/elasticsearch
type ElasticsearchKeyValueStore struct {
	witness         *util.StructPtrWitness
	client          *elastic.Client
	context         context.Context
	existingIndexes []indexAndType
	bloomFilters    map[string]map[string]*bloom.BloomFilter
	bfConfig        *BloomFilterConfig
}

// NewESKeyValueStore creates new ElasticsearchKeyValueStore instance.
// Host must of the format hostname:port.
// StructPtr should be a pointer to struct type that is used.
// for serialization and deserialization of store values.
func NewESKeyValueStore(host string, structPtr interface{}) *ElasticsearchKeyValueStore {
	return NewESKeyValueStoreWithBloomFilter(host, structPtr,nil)
}

// NewESKeyValueStoreWithBloomFilter enables an optional bloom filter to optimize Get() heavy workloads.
func NewESKeyValueStoreWithBloomFilter(host string, structPtr interface{}, bfConfig *BloomFilterConfig) *ElasticsearchKeyValueStore {
	url := fmt.Sprintf("http://%s", host)
	client, err := elastic.NewClient(
		elastic.SetURL(url),
		elastic.SetSniff(false), // FIXME: workaround for issues with ES in docker
	)
	if err != nil {
		panic(fmt.Sprintf("Cannot create ElasticSearch Client to '%s': %s", url, err))
	}
	return &ElasticsearchKeyValueStore{
		witness:         util.NewStructPtrWitness(structPtr),
		client:          client,
		context:         context.Background(),
		existingIndexes: nil,
		bloomFilters:    make(map[string]map[string]*bloom.BloomFilter),
		bfConfig:        bfConfig,
	}
}

func (s *ElasticsearchKeyValueStore) getBloomFilter(indexName, indexType string) *bloom.BloomFilter {
	if s.bloomFilters[indexName] == nil {
		return nil
	}
	return s.bloomFilters[indexName][indexType]
}

func (s *ElasticsearchKeyValueStore) setBloomFilter(indexName, indexType string, bf *bloom.BloomFilter) {
	if s.bloomFilters[indexName] == nil {
		s.bloomFilters[indexName] = make(map[string]*bloom.BloomFilter)
	}
	s.bloomFilters[indexName][indexType] = bf
}

func (s *ElasticsearchKeyValueStore) newBloomFilter() *bloom.BloomFilter {
	if s.bfConfig == nil {
		return nil
	}
	return bloom.NewWithEstimates(s.bfConfig.SizeEstimate, s.bfConfig.FalsePositiveRate)
}

func (s *ElasticsearchKeyValueStore) removeBloomFilter(indexName, indexType string) {
	if s.bloomFilters[indexName] == nil {
		return
	}
	delete(s.bloomFilters[indexName], indexType)
}

func (s *ElasticsearchKeyValueStore) provenAbsentByBloomFilter(indexName, indexType, id string) bool {
	bf := s.getBloomFilter(indexName, indexType)
	if bf == nil {
		return false
	}
	return bf.TestString(id) == false
}

func (s *ElasticsearchKeyValueStore) addToBloomFilter(indexName, indexType, id string) {
	bf := s.getBloomFilter(indexName, indexType)
	if bf == nil {
		return
	}
	bf.AddString(id)
}

func (s *ElasticsearchKeyValueStore) checkOrCreateIndex(indexName string, indexType string) {
	for _, existing := range s.existingIndexes {
		if existing.indexName == indexName && existing.indexType == indexType {
			return
		}
	}
	exists, err := s.client.IndexExists(indexName).Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to check if index exists: %s", err))
	}
	if !exists {
		_, err = s.client.CreateIndex(indexName).BodyString(indexSettings).Do(s.context)
		if err != nil {
			panic(fmt.Sprintf("Failed to create index: %s", err))
		}
		s.putMapping(indexName, indexType)
		s.setBloomFilter(indexName, indexType, s.newBloomFilter())
	}

	s.existingIndexes = append(s.existingIndexes, indexAndType{indexName, indexType})
}

func (s *ElasticsearchKeyValueStore) putMapping(indexName string, indexType string) {
	resp, err := s.client.PutMapping().Index(indexName).Type(indexType).BodyString(indexMapping).Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to put mapping for index: %s/%s: %s", indexName, indexType, err))
	}
	if resp == nil {
		panic(fmt.Sprintf("Expected put mapping response; got: %v", resp))
	}
	if !resp.Acknowledged {
		panic(fmt.Sprintf("Expected put mapping ack; got: %v", resp.Acknowledged))
	}
}

// Get gets value by key from store
func (s *ElasticsearchKeyValueStore) Get(key string) (interface{}, error) {
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return nil, fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)
	if s.provenAbsentByBloomFilter(indexName, indexType, valueID) {
		return s.witness.Nil(), nil
	}

	rawValue, err := s.client.Get().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if fmt.Sprintf("%s", err) == "elastic: Error 404 (Not Found)" {
		return s.witness.Nil(), nil
	}

	if err != nil {
		return s.witness.Nil(), err
	}

	if !rawValue.Found {
		return s.witness.Nil(), nil
	}

	structPtr := s.witness.Allocate()
	err = json.Unmarshal(*rawValue.Source, structPtr)
	if err != nil {
		return s.witness.Nil(), err
	}
	return structPtr, nil
}

// Put updates key in store with serialized value
func (s *ElasticsearchKeyValueStore) Put(key string, structPtr interface{}) error {
	s.witness.Assert(structPtr)
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)
	s.addToBloomFilter(indexName, indexType, valueID)

	_, err := s.client.Index().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		BodyJson(structPtr).
		Do(s.context)

	return err
}

// PutAll bulk executes Put operation for several entries
func (s *ElasticsearchKeyValueStore) PutAll(entries []*Entry) error {
	if len(entries) == 0 {
		return nil
	}
	bulk := s.client.Bulk()
	for _, entry := range entries {
		keyParts := strings.Split(entry.key, "/")
		if len(keyParts) != 3 {
			return fmt.Errorf("invalid key: '%s'", entry.key)
		}
		indexName := keyParts[0]
		indexType := keyParts[1]
		valueID := keyParts[2]

		s.witness.Assert(entry.value)
		s.checkOrCreateIndex(indexName, indexType)
		s.addToBloomFilter(indexName, indexType, valueID)

		bulk.Add(elastic.NewBulkIndexRequest().
			Index(indexName).
			Type(indexType).
			Id(valueID).
			Doc(entry.value),
		)
	}
	_, err := bulk.Do(s.context)
	return err
}

// Delete removes key from store
func (s *ElasticsearchKeyValueStore) Delete(key string) error {
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)
	s.removeBloomFilter(indexName, indexType)

	_, err := s.client.Delete().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	return err
}

// Flush the Elasticsearch translog to disk
func (s *ElasticsearchKeyValueStore) Flush() error {
	log.Println("Flusing ES indexes...")
	indexNames := []string{}
	for _, existing := range s.existingIndexes {
		indexNames = append(indexNames, existing.indexName)
	}
	_, err := s.client.Flush(indexNames...).
		WaitIfOngoing(true).
		Do(s.context)
	log.Println("Done flusing ES indexes.")
	return err
}
<commit_msg>404 on Delete should not be an error<commit_after>package kv

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

	"log"

	"github.com/willf/bloom"
	"golang.org/x/net/context"
	elastic "gopkg.in/olivere/elastic.v5"

	"github.com/movio/kasper/util"
)

const indexSettings = `{
	"index.translog.durability": "async",
	"index.translog.sync_interval": "60s",
	"index.translog.flush_threshold_size": "512m"
}`

const indexMapping = `{
	"_all" : {
		"enabled" : false
	},
	"dynamic_templates": [{
		"no_index": {
			"mapping": {
				"index": "no"
			},
			"match": "*"
		}
	}]
}`

type indexAndType struct {
	indexName string
	indexType string
}

// BloomFilterConfig contains estimates to configure the optional bloom filter.
// See https://godoc.org/github.com/willf/bloom#NewWithEstimates for more information.
type BloomFilterConfig struct {
	// An estimate size the entire data set
	SizeEstimate uint
	// An estimate of the desired false positive rate
	FalsePositiveRate float64
}

// ElasticsearchKeyValueStore is a key-value storage that uses ElasticSearch.
// In this key-value store, all keys must have the format "<index>/<type>/<_id>".
// For performance reasons, this implementation create indexes with async durability.
// You must call Flush() at appropriate times to ensure Elasticsearch syncs its translog to disk.
// See: https://www.elastic.co/products/elasticsearch
type ElasticsearchKeyValueStore struct {
	witness         *util.StructPtrWitness
	client          *elastic.Client
	context         context.Context
	existingIndexes []indexAndType
	bloomFilters    map[string]map[string]*bloom.BloomFilter
	bfConfig        *BloomFilterConfig
}

// NewESKeyValueStore creates new ElasticsearchKeyValueStore instance.
// Host must of the format hostname:port.
// StructPtr should be a pointer to struct type that is used.
// for serialization and deserialization of store values.
func NewESKeyValueStore(host string, structPtr interface{}) *ElasticsearchKeyValueStore {
	return NewESKeyValueStoreWithBloomFilter(host, structPtr, nil)
}

// NewESKeyValueStoreWithBloomFilter enables an optional bloom filter to optimize Get() heavy workloads.
func NewESKeyValueStoreWithBloomFilter(host string, structPtr interface{}, bfConfig *BloomFilterConfig) *ElasticsearchKeyValueStore {
	url := fmt.Sprintf("http://%s", host)
	client, err := elastic.NewClient(
		elastic.SetURL(url),
		elastic.SetSniff(false), // FIXME: workaround for issues with ES in docker
	)
	if err != nil {
		panic(fmt.Sprintf("Cannot create ElasticSearch Client to '%s': %s", url, err))
	}
	return &ElasticsearchKeyValueStore{
		witness:         util.NewStructPtrWitness(structPtr),
		client:          client,
		context:         context.Background(),
		existingIndexes: nil,
		bloomFilters:    make(map[string]map[string]*bloom.BloomFilter),
		bfConfig:        bfConfig,
	}
}

func (s *ElasticsearchKeyValueStore) getBloomFilter(indexName, indexType string) *bloom.BloomFilter {
	if s.bloomFilters[indexName] == nil {
		return nil
	}
	return s.bloomFilters[indexName][indexType]
}

func (s *ElasticsearchKeyValueStore) setBloomFilter(indexName, indexType string, bf *bloom.BloomFilter) {
	if s.bloomFilters[indexName] == nil {
		s.bloomFilters[indexName] = make(map[string]*bloom.BloomFilter)
	}
	s.bloomFilters[indexName][indexType] = bf
}

func (s *ElasticsearchKeyValueStore) newBloomFilter() *bloom.BloomFilter {
	if s.bfConfig == nil {
		return nil
	}
	return bloom.NewWithEstimates(s.bfConfig.SizeEstimate, s.bfConfig.FalsePositiveRate)
}

func (s *ElasticsearchKeyValueStore) removeBloomFilter(indexName, indexType string) {
	if s.bloomFilters[indexName] == nil {
		return
	}
	delete(s.bloomFilters[indexName], indexType)
}

func (s *ElasticsearchKeyValueStore) provenAbsentByBloomFilter(indexName, indexType, id string) bool {
	bf := s.getBloomFilter(indexName, indexType)
	if bf == nil {
		return false
	}
	return bf.TestString(id) == false
}

func (s *ElasticsearchKeyValueStore) addToBloomFilter(indexName, indexType, id string) {
	bf := s.getBloomFilter(indexName, indexType)
	if bf == nil {
		return
	}
	bf.AddString(id)
}

func (s *ElasticsearchKeyValueStore) checkOrCreateIndex(indexName string, indexType string) {
	for _, existing := range s.existingIndexes {
		if existing.indexName == indexName && existing.indexType == indexType {
			return
		}
	}
	exists, err := s.client.IndexExists(indexName).Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to check if index exists: %s", err))
	}
	if !exists {
		_, err = s.client.CreateIndex(indexName).BodyString(indexSettings).Do(s.context)
		if err != nil {
			panic(fmt.Sprintf("Failed to create index: %s", err))
		}
		s.putMapping(indexName, indexType)
		s.setBloomFilter(indexName, indexType, s.newBloomFilter())
	}

	s.existingIndexes = append(s.existingIndexes, indexAndType{indexName, indexType})
}

func (s *ElasticsearchKeyValueStore) putMapping(indexName string, indexType string) {
	resp, err := s.client.PutMapping().Index(indexName).Type(indexType).BodyString(indexMapping).Do(s.context)
	if err != nil {
		panic(fmt.Sprintf("Failed to put mapping for index: %s/%s: %s", indexName, indexType, err))
	}
	if resp == nil {
		panic(fmt.Sprintf("Expected put mapping response; got: %v", resp))
	}
	if !resp.Acknowledged {
		panic(fmt.Sprintf("Expected put mapping ack; got: %v", resp.Acknowledged))
	}
}

// Get gets value by key from store
func (s *ElasticsearchKeyValueStore) Get(key string) (interface{}, error) {
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return nil, fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)
	if s.provenAbsentByBloomFilter(indexName, indexType, valueID) {
		return s.witness.Nil(), nil
	}

	rawValue, err := s.client.Get().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if fmt.Sprintf("%s", err) == "elastic: Error 404 (Not Found)" {
		return s.witness.Nil(), nil
	}

	if err != nil {
		return s.witness.Nil(), err
	}

	if !rawValue.Found {
		return s.witness.Nil(), nil
	}

	structPtr := s.witness.Allocate()
	err = json.Unmarshal(*rawValue.Source, structPtr)
	if err != nil {
		return s.witness.Nil(), err
	}
	return structPtr, nil
}

// Put updates key in store with serialized value
func (s *ElasticsearchKeyValueStore) Put(key string, structPtr interface{}) error {
	s.witness.Assert(structPtr)
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)
	s.addToBloomFilter(indexName, indexType, valueID)

	_, err := s.client.Index().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		BodyJson(structPtr).
		Do(s.context)

	return err
}

// PutAll bulk executes Put operation for several entries
func (s *ElasticsearchKeyValueStore) PutAll(entries []*Entry) error {
	if len(entries) == 0 {
		return nil
	}
	bulk := s.client.Bulk()
	for _, entry := range entries {
		keyParts := strings.Split(entry.key, "/")
		if len(keyParts) != 3 {
			return fmt.Errorf("invalid key: '%s'", entry.key)
		}
		indexName := keyParts[0]
		indexType := keyParts[1]
		valueID := keyParts[2]

		s.witness.Assert(entry.value)
		s.checkOrCreateIndex(indexName, indexType)
		s.addToBloomFilter(indexName, indexType, valueID)

		bulk.Add(elastic.NewBulkIndexRequest().
			Index(indexName).
			Type(indexType).
			Id(valueID).
			Doc(entry.value),
		)
	}
	_, err := bulk.Do(s.context)
	return err
}

// Delete removes key from store
func (s *ElasticsearchKeyValueStore) Delete(key string) error {
	keyParts := strings.Split(key, "/")
	if len(keyParts) != 3 {
		return fmt.Errorf("invalid key: '%s'", key)
	}
	indexName := keyParts[0]
	indexType := keyParts[1]
	valueID := keyParts[2]

	s.checkOrCreateIndex(indexName, indexType)
	s.removeBloomFilter(indexName, indexType)

	response, err := s.client.Delete().
		Index(indexName).
		Type(indexType).
		Id(valueID).
		Do(s.context)

	if !response.Found {
		return nil
	}

	return err
}

// Flush the Elasticsearch translog to disk
func (s *ElasticsearchKeyValueStore) Flush() error {
	log.Println("Flusing ES indexes...")
	indexNames := []string{}
	for _, existing := range s.existingIndexes {
		indexNames = append(indexNames, existing.indexName)
	}
	_, err := s.client.Flush(indexNames...).
		WaitIfOngoing(true).
		Do(s.context)
	log.Println("Done flusing ES indexes.")
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gdamore/tcell"
	"io/ioutil"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"
)

// FileTypeRules represents a complete set of syntax rules for a filetype
type FileTypeRules struct {
	filetype string
	rules    []SyntaxRule
}

// SyntaxRule represents a regex to highlight in a certain style
type SyntaxRule struct {
	// What to highlight
	regex *regexp.Regexp
	// Any flags
	flags string
	// Whether this regex is a start=... end=... regex
	startend bool
	// How to highlight it
	style tcell.Style
}

var syntaxFiles map[[2]*regexp.Regexp]FileTypeRules

// LoadSyntaxFiles loads the syntax files from the default directory ~/.micro
func LoadSyntaxFiles() {
	usr, _ := user.Current()
	dir := usr.HomeDir
	LoadSyntaxFilesFromDir(dir + "/.micro/syntax")
}

// JoinRule takes a syntax rule (which can be multiple regular expressions)
// and joins it into one regular expression by ORing everything together
func JoinRule(rule string) string {
	split := strings.Split(rule, `" "`)
	joined := strings.Join(split, ")|(")
	joined = "(" + joined + ")"
	return joined
}

// LoadSyntaxFilesFromDir loads the syntax files from a specified directory
// To load the syntax files, we must fill the `syntaxFiles` map
// This involves finding the regex for syntax and if it exists, the regex
// for the header. Then we must get the text for the file and the filetype.
func LoadSyntaxFilesFromDir(dir string) {
	InitColorscheme()

	syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules)
	files, _ := ioutil.ReadDir(dir)
	for _, f := range files {
		if filepath.Ext(f.Name()) == ".micro" {
			text, err := ioutil.ReadFile(dir + "/" + f.Name())
			filename := dir + "/" + f.Name()

			if err != nil {
				TermMessage("Error loading syntax files: " + err.Error())
				continue
			}
			lines := strings.Split(string(text), "\n")

			syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`)
			headerParser := regexp.MustCompile(`header "(.*)"`)

			ruleParser := regexp.MustCompile(`color (.*?)\s+(?:\((.*?)\)\s+)?"(.*)"`)
			ruleStartEndParser := regexp.MustCompile(`color (.*?)\s+(?:\((.*?)\)\s+)?start="(.*?)"\s+end="(.*?)"`)

			var syntaxRegex *regexp.Regexp
			var headerRegex *regexp.Regexp
			var filetype string
			var rules []SyntaxRule
			for lineNum, line := range lines {
				if strings.TrimSpace(line) == "" ||
					strings.TrimSpace(line)[0] == '#' {
					// Ignore this line
					continue
				}

				if strings.HasPrefix(line, "syntax") {
					syntaxMatches := syntaxParser.FindSubmatch([]byte(line))
					if len(syntaxMatches) == 3 {
						if syntaxRegex != nil {
							regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex}
							syntaxFiles[regexes] = FileTypeRules{filetype, rules}
						}
						rules = rules[:0]

						filetype = string(syntaxMatches[1])
						extensions := JoinRule(string(syntaxMatches[2]))

						syntaxRegex, err = regexp.Compile(extensions)
						if err != nil {
							TermError(filename, lineNum, err.Error())
							continue
						}
					} else {
						TermError(filename, lineNum, "Syntax statement is not valid: "+line)
						continue
					}
				} else if strings.HasPrefix(line, "header") {
					headerMatches := headerParser.FindSubmatch([]byte(line))
					if len(headerMatches) == 2 {
						header := JoinRule(string(headerMatches[1]))

						headerRegex, err = regexp.Compile(header)
						if err != nil {
							TermError(filename, lineNum, "Regex error: "+err.Error())
							continue
						}
					} else {
						TermError(filename, lineNum, "Header statement is not valid: "+line)
						continue
					}
				} else {
					if ruleParser.MatchString(line) {
						submatch := ruleParser.FindSubmatch([]byte(line))
						var color string
						var regexStr string
						var flags string
						if len(submatch) == 4 {
							color = string(submatch[1])
							flags = string(submatch[2])
							regexStr = "(?" + flags + ")" + JoinRule(string(submatch[3]))
						} else if len(submatch) == 3 {
							color = string(submatch[1])
							regexStr = JoinRule(string(submatch[2]))
						} else {
							TermError(filename, lineNum, "Invalid statement: "+line)
						}
						regex, err := regexp.Compile(regexStr)
						if err != nil {
							TermError(filename, lineNum, err.Error())
							continue
						}

						st := tcell.StyleDefault
						if _, ok := colorscheme[color]; ok {
							st = colorscheme[color]
						} else {
							st = StringToStyle(color)
						}
						rules = append(rules, SyntaxRule{regex, flags, false, st})
					} else if ruleStartEndParser.MatchString(line) {
						submatch := ruleStartEndParser.FindSubmatch([]byte(line))
						var color string
						var start string
						var end string
						// Use m and s flags by default
						flags := "ms"
						if len(submatch) == 5 {
							color = string(submatch[1])
							flags = string(submatch[2])
							start = string(submatch[3])
							end = string(submatch[4])
						} else if len(submatch) == 4 {
							color = string(submatch[1])
							start = string(submatch[2])
							end = string(submatch[3])
						} else {
							TermError(filename, lineNum, "Invalid statement: "+line)
						}

						regex, err := regexp.Compile("(?" + flags + ")" + "(" + start + ").*?(" + end + ")")
						if err != nil {
							TermError(filename, lineNum, err.Error())
							continue
						}

						st := tcell.StyleDefault
						if _, ok := colorscheme[color]; ok {
							st = colorscheme[color]
						} else {
							st = StringToStyle(color)
						}
						rules = append(rules, SyntaxRule{regex, flags, true, st})
					}
				}
			}
			if syntaxRegex != nil {
				regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex}
				syntaxFiles[regexes] = FileTypeRules{filetype, rules}
			}
		}
	}
}

// GetRules finds the syntax rules that should be used for the buffer
// and returns them. It also returns the filetype of the file
func GetRules(buf *Buffer) ([]SyntaxRule, string) {
	for r := range syntaxFiles {
		if r[0] != nil && r[0].MatchString(buf.path) {
			return syntaxFiles[r].rules, syntaxFiles[r].filetype
		} else if r[1] != nil && r[1].MatchString(buf.lines[0]) {
			return syntaxFiles[r].rules, syntaxFiles[r].filetype
		}
	}
	return nil, "Unknown"
}

// SyntaxMatches is an alias to a map from character numbers to styles,
// so map[3] represents the style of the third character
type SyntaxMatches map[int]tcell.Style

// Match takes a buffer and returns the syntax matches a map specifying how it should be syntax highlighted
func Match(rules []SyntaxRule, buf *Buffer, v *View) SyntaxMatches {
	m := make(SyntaxMatches)

	lineStart := v.updateLines[0]
	lineEnd := v.updateLines[1] + 1
	if lineStart < 0 {
		// Don't need to update syntax highlighting
		return m
	}

	totalStart := v.topline - synLinesUp
	totalEnd := v.topline + v.height + synLinesDown
	if totalStart < 0 {
		totalStart = 0
	}
	if totalEnd > len(buf.lines) {
		totalEnd = len(buf.lines)
	}

	if lineEnd > len(buf.lines) {
		lineEnd = len(buf.lines)
	}

	lines := buf.lines[lineStart:lineEnd]
	str := strings.Join(buf.lines[totalStart:totalEnd], "\n")
	startNum := v.cursor.loc + v.cursor.Distance(0, totalStart)
	toplineNum := v.cursor.loc + v.cursor.Distance(0, v.topline)
	for _, rule := range rules {
		if rule.startend && rule.regex.MatchString(str) {
			indicies := rule.regex.FindAllStringIndex(str, -1)
			for _, value := range indicies {
				value[0] += startNum
				value[1] += startNum
				for i := value[0]; i < value[1]; i++ {
					if i >= toplineNum {
						m[i] = rule.style
					}
				}
			}
		} else {
			for _, line := range lines {
				if rule.regex.MatchString(line) {
					indicies := rule.regex.FindAllStringIndex(str, -1)
					for _, value := range indicies {
						value[0] += toplineNum
						value[1] += toplineNum
						for i := value[0]; i < value[1]; i++ {
							m[i] = rule.style
						}
					}
				}
			}
		}
	}

	return m
}
<commit_msg>Use non cgo implementation of homedir<commit_after>package main

import (
	"github.com/gdamore/tcell"
	"github.com/mitchellh/go-homedir"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"
)

// FileTypeRules represents a complete set of syntax rules for a filetype
type FileTypeRules struct {
	filetype string
	rules    []SyntaxRule
}

// SyntaxRule represents a regex to highlight in a certain style
type SyntaxRule struct {
	// What to highlight
	regex *regexp.Regexp
	// Any flags
	flags string
	// Whether this regex is a start=... end=... regex
	startend bool
	// How to highlight it
	style tcell.Style
}

var syntaxFiles map[[2]*regexp.Regexp]FileTypeRules

// LoadSyntaxFiles loads the syntax files from the default directory ~/.micro
func LoadSyntaxFiles() {
	dir, err := homedir.Dir()
	if err != nil {
		TermMessage("Error finding your home directory\nCan't load runtime files")
		return
	}
	LoadSyntaxFilesFromDir(dir + "/.micro/syntax")
}

// JoinRule takes a syntax rule (which can be multiple regular expressions)
// and joins it into one regular expression by ORing everything together
func JoinRule(rule string) string {
	split := strings.Split(rule, `" "`)
	joined := strings.Join(split, ")|(")
	joined = "(" + joined + ")"
	return joined
}

// LoadSyntaxFilesFromDir loads the syntax files from a specified directory
// To load the syntax files, we must fill the `syntaxFiles` map
// This involves finding the regex for syntax and if it exists, the regex
// for the header. Then we must get the text for the file and the filetype.
func LoadSyntaxFilesFromDir(dir string) {
	InitColorscheme()

	syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules)
	files, _ := ioutil.ReadDir(dir)
	for _, f := range files {
		if filepath.Ext(f.Name()) == ".micro" {
			text, err := ioutil.ReadFile(dir + "/" + f.Name())
			filename := dir + "/" + f.Name()

			if err != nil {
				TermMessage("Error loading syntax files: " + err.Error())
				continue
			}
			lines := strings.Split(string(text), "\n")

			syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`)
			headerParser := regexp.MustCompile(`header "(.*)"`)

			ruleParser := regexp.MustCompile(`color (.*?)\s+(?:\((.*?)\)\s+)?"(.*)"`)
			ruleStartEndParser := regexp.MustCompile(`color (.*?)\s+(?:\((.*?)\)\s+)?start="(.*?)"\s+end="(.*?)"`)

			var syntaxRegex *regexp.Regexp
			var headerRegex *regexp.Regexp
			var filetype string
			var rules []SyntaxRule
			for lineNum, line := range lines {
				if strings.TrimSpace(line) == "" ||
					strings.TrimSpace(line)[0] == '#' {
					// Ignore this line
					continue
				}

				if strings.HasPrefix(line, "syntax") {
					syntaxMatches := syntaxParser.FindSubmatch([]byte(line))
					if len(syntaxMatches) == 3 {
						if syntaxRegex != nil {
							regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex}
							syntaxFiles[regexes] = FileTypeRules{filetype, rules}
						}
						rules = rules[:0]

						filetype = string(syntaxMatches[1])
						extensions := JoinRule(string(syntaxMatches[2]))

						syntaxRegex, err = regexp.Compile(extensions)
						if err != nil {
							TermError(filename, lineNum, err.Error())
							continue
						}
					} else {
						TermError(filename, lineNum, "Syntax statement is not valid: "+line)
						continue
					}
				} else if strings.HasPrefix(line, "header") {
					headerMatches := headerParser.FindSubmatch([]byte(line))
					if len(headerMatches) == 2 {
						header := JoinRule(string(headerMatches[1]))

						headerRegex, err = regexp.Compile(header)
						if err != nil {
							TermError(filename, lineNum, "Regex error: "+err.Error())
							continue
						}
					} else {
						TermError(filename, lineNum, "Header statement is not valid: "+line)
						continue
					}
				} else {
					if ruleParser.MatchString(line) {
						submatch := ruleParser.FindSubmatch([]byte(line))
						var color string
						var regexStr string
						var flags string
						if len(submatch) == 4 {
							color = string(submatch[1])
							flags = string(submatch[2])
							regexStr = "(?" + flags + ")" + JoinRule(string(submatch[3]))
						} else if len(submatch) == 3 {
							color = string(submatch[1])
							regexStr = JoinRule(string(submatch[2]))
						} else {
							TermError(filename, lineNum, "Invalid statement: "+line)
						}
						regex, err := regexp.Compile(regexStr)
						if err != nil {
							TermError(filename, lineNum, err.Error())
							continue
						}

						st := tcell.StyleDefault
						if _, ok := colorscheme[color]; ok {
							st = colorscheme[color]
						} else {
							st = StringToStyle(color)
						}
						rules = append(rules, SyntaxRule{regex, flags, false, st})
					} else if ruleStartEndParser.MatchString(line) {
						submatch := ruleStartEndParser.FindSubmatch([]byte(line))
						var color string
						var start string
						var end string
						// Use m and s flags by default
						flags := "ms"
						if len(submatch) == 5 {
							color = string(submatch[1])
							flags = string(submatch[2])
							start = string(submatch[3])
							end = string(submatch[4])
						} else if len(submatch) == 4 {
							color = string(submatch[1])
							start = string(submatch[2])
							end = string(submatch[3])
						} else {
							TermError(filename, lineNum, "Invalid statement: "+line)
						}

						regex, err := regexp.Compile("(?" + flags + ")" + "(" + start + ").*?(" + end + ")")
						if err != nil {
							TermError(filename, lineNum, err.Error())
							continue
						}

						st := tcell.StyleDefault
						if _, ok := colorscheme[color]; ok {
							st = colorscheme[color]
						} else {
							st = StringToStyle(color)
						}
						rules = append(rules, SyntaxRule{regex, flags, true, st})
					}
				}
			}
			if syntaxRegex != nil {
				regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex}
				syntaxFiles[regexes] = FileTypeRules{filetype, rules}
			}
		}
	}
}

// GetRules finds the syntax rules that should be used for the buffer
// and returns them. It also returns the filetype of the file
func GetRules(buf *Buffer) ([]SyntaxRule, string) {
	for r := range syntaxFiles {
		if r[0] != nil && r[0].MatchString(buf.path) {
			return syntaxFiles[r].rules, syntaxFiles[r].filetype
		} else if r[1] != nil && r[1].MatchString(buf.lines[0]) {
			return syntaxFiles[r].rules, syntaxFiles[r].filetype
		}
	}
	return nil, "Unknown"
}

// SyntaxMatches is an alias to a map from character numbers to styles,
// so map[3] represents the style of the third character
type SyntaxMatches map[int]tcell.Style

// Match takes a buffer and returns the syntax matches a map specifying how it should be syntax highlighted
func Match(rules []SyntaxRule, buf *Buffer, v *View) SyntaxMatches {
	m := make(SyntaxMatches)

	lineStart := v.updateLines[0]
	lineEnd := v.updateLines[1] + 1
	if lineStart < 0 {
		// Don't need to update syntax highlighting
		return m
	}

	totalStart := v.topline - synLinesUp
	totalEnd := v.topline + v.height + synLinesDown
	if totalStart < 0 {
		totalStart = 0
	}
	if totalEnd > len(buf.lines) {
		totalEnd = len(buf.lines)
	}

	if lineEnd > len(buf.lines) {
		lineEnd = len(buf.lines)
	}

	lines := buf.lines[lineStart:lineEnd]
	str := strings.Join(buf.lines[totalStart:totalEnd], "\n")
	startNum := v.cursor.loc + v.cursor.Distance(0, totalStart)
	toplineNum := v.cursor.loc + v.cursor.Distance(0, v.topline)
	for _, rule := range rules {
		if rule.startend && rule.regex.MatchString(str) {
			indicies := rule.regex.FindAllStringIndex(str, -1)
			for _, value := range indicies {
				value[0] += startNum
				value[1] += startNum
				for i := value[0]; i < value[1]; i++ {
					if i >= toplineNum {
						m[i] = rule.style
					}
				}
			}
		} else {
			for _, line := range lines {
				if rule.regex.MatchString(line) {
					indicies := rule.regex.FindAllStringIndex(str, -1)
					for _, value := range indicies {
						value[0] += toplineNum
						value[1] += toplineNum
						for i := value[0]; i < value[1]; i++ {
							m[i] = rule.style
						}
					}
				}
			}
		}
	}

	return m
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
)

type SingleHooke struct {
	K float64
}

func (sh SingleHooke) Accel(bs []Body, i int) (a Vector) {

	b := bs[i]

	return b.Xs[0].Scale(-sh.K / b.M)
}

type AnalyticSHM struct {
	K, M float64
	A    Vector
}

func (ashm AnalyticSHM) XVAt(t float64) (x, v Vector) {

	omega := math.Sqrt(ashm.K / ashm.M)

	x = ashm.A.Scale(math.Cos(omega * t))
	v = ashm.A.Negate().Scale(omega * math.Sin(omega*t))

	return
}

func (ashm AnalyticSHM) Force() Force {

	return Force(SingleHooke{K: ashm.K})
}

func (ashm AnalyticSHM) ForEuler() []Body {

	x0, v0 := ashm.XVAt(0)

	return []Body{
		Body{
			Xs: []Vector{x0},
			Vs: []Vector{v0},
			M:  ashm.M,
		},
	}
}

func (ashm AnalyticSHM) ForVerlet(dt float64) []Body {

	x0, v0 := ashm.XVAt(0)
	xPrev, vPrev := ashm.XVAt(-dt)

	bs := []Body{
		Body{
			Xs: []Vector{x0, xPrev},
			Vs: []Vector{v0, vPrev},
			M:  ashm.M,
		},
	}

	Step(Verlet, bs, ashm.Force(), dt)

	return bs
}

func (ashm AnalyticSHM) DataHeader() {

	//fmt.Printf("# ")
	fmt.Printf("t x v E Ek U ")
	fmt.Printf("x_e v_e E_e Ek_e U_e x_e_resid ")
	fmt.Printf("x_v v_e E_v Ek_v U_v x_v_resid ")
	fmt.Println()
}

func (ashm AnalyticSHM) Run(dt float64, steps int) {

	ashm.DataHeader()

	force := ashm.Force()

	eulerState := ashm.ForEuler()
	verletState := ashm.ForVerlet(dt)

	for t := 0.0; steps > 0; {

		fmt.Printf("%f ", t)

		x := ashm.Analytic(t)

		ashm.EulerFormat(eulerState, x)
		Step(Euler, eulerState, force, dt)

		ashm.VerletFormat(verletState, x)
		Step(Verlet, verletState, force, dt)

		fmt.Println()

		t += dt
		steps--
	}
}

var e_x = NewVector(1, 0, 0)

func (ashm AnalyticSHM) Analytic(t float64) (x Vector) {

	x, v := ashm.XVAt(t)

	kinetic := ashm.M * math.Pow(v.Norm(), 2) / 2
	potential := ashm.A.Norm() * math.Pow(x.Norm(), 2) / 2

	totalE := kinetic + potential

	fmt.Printf(
		"%f %f %f %f %f ",
		x.Dot(e_x), v.Dot(e_x), totalE, kinetic, potential,
	)

	return x
}

func Step(alg Integrator, bs []Body, f Force, dt float64) {

	as := make([]Vector, len(bs))

	for i, _ := range bs {

		as[i] = f.Accel(bs, i)
	}

	for i, body := range bs {

		alg(body.Xs, body.Vs, as[i], dt)
	}
}

func (ashm AnalyticSHM) EulerFormat(bs []Body, x Vector) {

	xE, vE := bs[0].Xs[0].Dot(e_x), bs[0].Vs[0].Dot(e_x)

	kinetic := ashm.M * math.Pow(vE, 2) / 2
	potential := ashm.K * math.Pow(xE, 2) / 2

	total := kinetic + potential

	residue := math.Abs(x.Dot(e_x) - xE)

	fmt.Printf(
		"%f %f %f %f %f %f ",
		xE, vE, total, kinetic, potential, residue,
	)
}

func (ashm AnalyticSHM) VerletFormat(bs []Body, x Vector) {

	xV, vV := bs[0].Xs[1].Dot(e_x), bs[0].Xs[1].Dot(e_x)

	kinetic := ashm.M * math.Pow(vV, 2) / 2
	potential := ashm.K * math.Pow(xV, 2) / 2

	total := kinetic + potential

	residue := math.Abs(x.Dot(e_x) - xV)

	fmt.Printf(
		"%f %f %f %f %f %f ",
		xV, vV, total, kinetic, potential, residue,
	)
}
<commit_msg>Fix Verlet formatting<commit_after>package main

import (
	"fmt"
	"math"
)

type SingleHooke struct {
	K float64
}

func (sh SingleHooke) Accel(bs []Body, i int) (a Vector) {

	b := bs[i]

	return b.Xs[0].Scale(-sh.K / b.M)
}

type AnalyticSHM struct {
	K, M float64
	A    Vector
}

func (ashm AnalyticSHM) XVAt(t float64) (x, v Vector) {

	omega := math.Sqrt(ashm.K / ashm.M)

	x = ashm.A.Scale(math.Cos(omega * t))
	v = ashm.A.Negate().Scale(omega * math.Sin(omega*t))

	return
}

func (ashm AnalyticSHM) Force() Force {

	return Force(SingleHooke{K: ashm.K})
}

func (ashm AnalyticSHM) ForEuler() []Body {

	x0, v0 := ashm.XVAt(0)

	return []Body{
		Body{
			Xs: []Vector{x0},
			Vs: []Vector{v0},
			M:  ashm.M,
		},
	}
}

func (ashm AnalyticSHM) ForVerlet(dt float64) []Body {

	x0, v0 := ashm.XVAt(0)
	xPrev, vPrev := ashm.XVAt(-dt)

	bs := []Body{
		Body{
			Xs: []Vector{x0, xPrev},
			Vs: []Vector{v0, vPrev},
			M:  ashm.M,
		},
	}

	Step(Verlet, bs, ashm.Force(), dt)

	return bs
}

func (ashm AnalyticSHM) DataHeader() {

	//fmt.Printf("# ")
	fmt.Printf("t x v E Ek U ")
	fmt.Printf("x_e v_e E_e Ek_e U_e x_e_resid ")
	fmt.Printf("x_v v_e E_v Ek_v U_v x_v_resid ")
	fmt.Println()
}

func (ashm AnalyticSHM) Run(dt float64, steps int) {

	ashm.DataHeader()

	force := ashm.Force()

	eulerState := ashm.ForEuler()
	verletState := ashm.ForVerlet(dt)

	for t := 0.0; steps > 0; {

		fmt.Printf("%f ", t)

		x := ashm.Analytic(t)

		ashm.EulerFormat(eulerState, x)
		Step(Euler, eulerState, force, dt)

		ashm.VerletFormat(verletState, x)
		Step(Verlet, verletState, force, dt)

		fmt.Println()

		t += dt
		steps--
	}
}

var e_x = NewVector(1, 0, 0)

func (ashm AnalyticSHM) Analytic(t float64) (x Vector) {

	x, v := ashm.XVAt(t)

	kinetic := ashm.M * math.Pow(v.Norm(), 2) / 2
	potential := ashm.A.Norm() * math.Pow(x.Norm(), 2) / 2

	totalE := kinetic + potential

	fmt.Printf(
		"%f %f %f %f %f ",
		x.Dot(e_x), v.Dot(e_x), totalE, kinetic, potential,
	)

	return x
}

func Step(alg Integrator, bs []Body, f Force, dt float64) {

	as := make([]Vector, len(bs))

	for i, _ := range bs {

		as[i] = f.Accel(bs, i)
	}

	for i, body := range bs {

		alg(body.Xs, body.Vs, as[i], dt)
	}
}

func (ashm AnalyticSHM) EulerFormat(bs []Body, x Vector) {

	xE, vE := bs[0].Xs[0].Dot(e_x), bs[0].Vs[0].Dot(e_x)

	kinetic := ashm.M * math.Pow(vE, 2) / 2
	potential := ashm.K * math.Pow(xE, 2) / 2

	total := kinetic + potential

	residue := math.Abs(x.Dot(e_x) - xE)

	fmt.Printf(
		"%f %f %f %f %f %f ",
		xE, vE, total, kinetic, potential, residue,
	)
}

func (ashm AnalyticSHM) VerletFormat(bs []Body, x Vector) {

	xV, vV := bs[0].Xs[1].Dot(e_x), bs[0].Vs[1].Dot(e_x)

	kinetic := ashm.M * math.Pow(vV, 2) / 2
	potential := ashm.K * math.Pow(xV, 2) / 2

	total := kinetic + potential

	residue := math.Abs(x.Dot(e_x) - xV)

	fmt.Printf(
		"%f %f %f %f %f %f ",
		xV, vV, total, kinetic, potential, residue,
	)
}
<|endoftext|>


<commit_before>/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>

*/
package cmd

import (
	"fmt"

	"github.com/manifoldco/promptui"
	"github.com/spf13/cobra"
)

// taskCmd represents the task command
var taskCmd = &cobra.Command{
	Use:   "task <ID>",
	Short: "This command creates new task",
	Long:  `task code01 -t="create new repo" -d=01-feb-2022`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			fmt.Println("Task ID must be provided")
			return
		}
		category := chooseCategory()
		fmt.Println(category)
		fmt.Println("Task ID: ", args[0])
		//name, _ := cmd.Flags().GetString("name")
		title, _ := cmd.Flags().GetString("title")
		due, _ := cmd.Flags().GetString("due")
		fmt.Println("Title of the task :" + title)
		fmt.Println("Due date of the task :" + due)
	},
}

func init() {
	createCmd.AddCommand(taskCmd)
	taskCmd.PersistentFlags().StringP("title", "t", "", "Title of the task")
	taskCmd.PersistentFlags().StringP("due", "d", "", "Due Date")
	taskCmd.MarkPersistentFlagRequired("title")
}

// chooseCategory chooses category with promptui
func chooseCategory() string {
	items := []string{"Coding", "Learning", "Meeting", "Design", "R & D"}
	index := -1
	var result string
	var err error

	for index < 0 {
		prompt := promptui.SelectWithAdd{
			Label:    "Select the category to add a task",
			Items:    items,
			AddLabel: "Add your own category",
		}

		index, result, err = prompt.Run()

		if index == -1 {
			items = append(items, result)
		}
	}

	if err != nil {
		fmt.Printf("Prompt failed %v\n", err)
		return ""
	}

	return result
}
<commit_msg>Modified REST API Example with Testing<commit_after>/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>

*/
package cmd

import (
	"fmt"

	"github.com/manifoldco/promptui"
	"github.com/spf13/cobra"
)

// taskCmd represents the task command
var taskCmd = &cobra.Command{
	Use:   "task <ID>",
	Short: "This command creates new task",
	Long:  `task code01 -t="create new repo" -d=01-feb-2022`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			fmt.Println("Task ID must be provided")
			return
		}
		category := chooseCategory()
		fmt.Println("Category:", category)
		fmt.Println("Task ID: ", args[0])
		//name, _ := cmd.Flags().GetString("name")
		title, _ := cmd.Flags().GetString("title")
		due, _ := cmd.Flags().GetString("due")
		fmt.Println("Title of the task :" + title)
		fmt.Println("Due date of the task :" + due)
	},
}

func init() {
	createCmd.AddCommand(taskCmd)
	taskCmd.PersistentFlags().StringP("title", "t", "", "Title of the task")
	taskCmd.PersistentFlags().StringP("due", "d", "", "Due Date")
	taskCmd.MarkPersistentFlagRequired("title")
}

// chooseCategory chooses category with promptui
func chooseCategory() string {
	items := []string{"Coding", "Learning", "Meeting", "Design", "R & D"}
	index := -1
	var result string
	var err error

	for index < 0 {
		prompt := promptui.SelectWithAdd{
			Label:    "Select the category to add a task",
			Items:    items,
			AddLabel: "Add your own category",
		}

		index, result, err = prompt.Run()

		if index == -1 {
			items = append(items, result)
		}
	}

	if err != nil {
		fmt.Printf("Prompt failed %v\n", err)
		return ""
	}

	return result
}
<|endoftext|>


<commit_before>package downloader

import (
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type Downloader struct {
	Url        string
	ProgressCh chan int
	Size       int
}

func New(url string) *Downloader {
	return &Downloader{
		Url:        url,
		ProgressCh: make(chan int),
		Size:       0,
	}
}

func (d *Downloader) updateSize(header http.Header) error {
	strVal := header.Get("Content-Length")
	if len(strVal) > 0 {
		val, err := strconv.Atoi(strVal)
		if err != nil {
			return err
		}
		d.Size = val
		return nil
	}
	return nil
}

func (d *Downloader) updateSizeByHead() error {
	res, err := http.Head(d.Url)
	if err != nil {
		return err
	}
	d.updateSize(res.Header)
	return nil
}

// start download and save as file.
func (d *Downloader) Start() error {
	d.updateSizeByHead()

	dialFunc := func(network, addr string) (net.Conn, error) {
		conn, err := net.Dial(network, addr)
		if err != nil {
			return nil, err
		}
		return NewCountableConnection(conn, d.ProgressCh), nil
	}
	transport := &http.Transport{
		Dial: dialFunc,
	}
	client := &http.Client{Transport: transport}

	res, err := client.Get(d.Url)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if d.Size == 0 {
		d.updateSize(res.Header)
	}

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

	fileNameParts := strings.Split(d.Url, "/")
	fileName := fileNameParts[len(fileNameParts)-1]
	if fileName == "" {
		// TODO: tailling slash
		fileName = "a.downloaded.file"
	}
	if err := ioutil.WriteFile(fileName, data, os.FileMode(0666)); err != nil {
		return err
	}

	return nil
}
<commit_msg>fix comment<commit_after>package downloader

import (
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type Downloader struct {
	Url        string
	ProgressCh chan int
	Size       int
}

func New(url string) *Downloader {
	return &Downloader{
		Url:        url,
		ProgressCh: make(chan int),
		Size:       0,
	}
}

func (d *Downloader) updateSize(header http.Header) error {
	strVal := header.Get("Content-Length")
	if len(strVal) > 0 {
		val, err := strconv.Atoi(strVal)
		if err != nil {
			return err
		}
		d.Size = val
		return nil
	}
	return nil
}

func (d *Downloader) updateSizeByHead() error {
	res, err := http.Head(d.Url)
	if err != nil {
		return err
	}
	d.updateSize(res.Header)
	return nil
}

// start downloading and save response.Body as file.
func (d *Downloader) Start() error {
	d.updateSizeByHead()

	dialFunc := func(network, addr string) (net.Conn, error) {
		conn, err := net.Dial(network, addr)
		if err != nil {
			return nil, err
		}
		return NewCountableConnection(conn, d.ProgressCh), nil
	}
	transport := &http.Transport{
		Dial: dialFunc,
	}
	client := &http.Client{Transport: transport}

	res, err := client.Get(d.Url)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if d.Size == 0 {
		d.updateSize(res.Header)
	}

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

	fileNameParts := strings.Split(d.Url, "/")
	fileName := fileNameParts[len(fileNameParts)-1]
	if fileName == "" {
		// TODO: tailling slash
		fileName = "a.downloaded.file"
	}
	if err := ioutil.WriteFile(fileName, data, os.FileMode(0666)); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package ai

import (
	"math/rand"

	"context"

	"github.com/nelhage/taktician/tak"
)

type RandomAI struct {
	r *rand.Rand
}

func (r *RandomAI) GetMove(ctx context.Context, p *tak.Position) tak.Move {
	moves := p.AllMoves(nil)
	i := r.r.Int31n(int32(len(moves)))
	return moves[i]
}

func NewRandom(seed int64) TakPlayer {
	return &RandomAI{
		r: rand.New(rand.NewSource(seed)),
	}
}
<commit_msg>optimize ai.RandomAI<commit_after>package ai

import (
	"math/rand"

	"context"

	"github.com/nelhage/taktician/tak"
)

type RandomAI struct {
	r *rand.Rand
}

func (r *RandomAI) GetMove(ctx context.Context, p *tak.Position) tak.Move {
	var buffer [100]tak.Move
	moves := p.AllMoves(buffer[:0])
	i := r.r.Int31n(int32(len(moves)))
	return moves[i]
}

func NewRandom(seed int64) TakPlayer {
	return &RandomAI{
		r: rand.New(rand.NewSource(seed)),
	}
}
<|endoftext|>


<commit_before>package handler

import (
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/rs/zerolog"
)

// NewMuxRouter sets up the mux.Router and registers routes to URL paths
// using the available handlers
func NewMuxRouter(logger zerolog.Logger, handlers Handlers) *mux.Router {
	// I should take this as a dependency, but need to do some work with wire
	rtr := mux.NewRouter()

	// I should take this as a dependency, but need to do some work with wire
	c := alice.New()

	// add Standard Handler chain and zerolog logger to Context
	c = AddStandardHandlerChain(logger, c)

	// send Router through PathPrefix method to validate any standard
	// subroutes you may want for your APIs. e.g. I always want to be
	// sure that every request has "/api" as part of it's path prefix
	// without having to put it into every handle path in my various
	// routing functions
	rtr = rtr.PathPrefix("/api").Subrouter()

	// Match only POST requests at /api/v1/movies
	// with Content-Type header = application/json
	rtr.Handle("/v1/movies",
		c.Append(AccessTokenHandler).
			Then(handlers.CreateMovieHandler)).
		Methods("POST").
		Headers("Content-Type", "application/json")

	// Match only PUT requests having an ID at /api/v1/movies/{id}
	// with the Content-Type header = application/json
	rtr.Handle("/v1/movies/{id}",
		c.Append(AccessTokenHandler).
			Then(handlers.UpdateMovieHandler)).
		Methods("PUT").
		Headers("Content-Type", "application/json")

	// Match only DELETE requests having an ID at /api/v1/movies/{id}
	rtr.Handle("/v1/movies/{id}",
		c.Append(AccessTokenHandler).
			Then(handlers.DeleteMovieHandler)).
		Methods("DELETE")

	// Match only GET requests having an ID at /api/v1/movies/{id}
	rtr.Handle("/v1/movies/{id}",
		c.Append(AccessTokenHandler).
			Then(handlers.FindMovieByIDHandler)).
		Methods("GET")

	// Match only GET requests /api/v1/movies
	rtr.Handle("/v1/movies",
		c.Append(AccessTokenHandler).
			Then(handlers.FindAllMoviesHandler)).
		Methods("GET")

	// Match only GET requests at /api/v1/ping
	rtr.Handle("/v1/ping",
		c.Then(handlers.PingHandler)).
		Methods("GET")

	return rtr
}
<commit_msg>add JSONContentTypeHandler to middleware chain<commit_after>package handler

import (
	"github.com/gorilla/mux"
	"github.com/justinas/alice"
	"github.com/rs/zerolog"
)

// NewMuxRouter sets up the mux.Router and registers routes to URL paths
// using the available handlers
func NewMuxRouter(logger zerolog.Logger, handlers Handlers) *mux.Router {
	// I should take this as a dependency, but need to do some work with wire
	rtr := mux.NewRouter()

	// I should take this as a dependency, but need to do some work with wire
	c := alice.New()

	// add Standard Handler chain and zerolog logger to Context
	c = AddStandardHandlerChain(logger, c)

	// send Router through PathPrefix method to validate any standard
	// subroutes you may want for your APIs. e.g. I always want to be
	// sure that every request has "/api" as part of it's path prefix
	// without having to put it into every handle path in my various
	// routing functions
	rtr = rtr.PathPrefix("/api").Subrouter()

	// Match only POST requests at /api/v1/movies
	// with Content-Type header = application/json
	rtr.Handle("/v1/movies",
		c.Append(AccessTokenHandler).
			Append(JSONContentTypeHandler).
			Then(handlers.CreateMovieHandler)).
		Methods("POST").
		Headers("Content-Type", "application/json")

	// Match only PUT requests having an ID at /api/v1/movies/{id}
	// with the Content-Type header = application/json
	rtr.Handle("/v1/movies/{id}",
		c.Append(AccessTokenHandler).
			Append(JSONContentTypeHandler).
			Then(handlers.UpdateMovieHandler)).
		Methods("PUT").
		Headers("Content-Type", "application/json")

	// Match only DELETE requests having an ID at /api/v1/movies/{id}
	rtr.Handle("/v1/movies/{id}",
		c.Append(AccessTokenHandler).
			Append(JSONContentTypeHandler).
			Then(handlers.DeleteMovieHandler)).
		Methods("DELETE")

	// Match only GET requests having an ID at /api/v1/movies/{id}
	rtr.Handle("/v1/movies/{id}",
		c.Append(AccessTokenHandler).
			Append(JSONContentTypeHandler).
			Then(handlers.FindMovieByIDHandler)).
		Methods("GET")

	// Match only GET requests /api/v1/movies
	rtr.Handle("/v1/movies",
		c.Append(AccessTokenHandler).
			Append(JSONContentTypeHandler).
			Then(handlers.FindAllMoviesHandler)).
		Methods("GET")

	// Match only GET requests at /api/v1/ping
	rtr.Handle("/v1/ping",
		c.Append(JSONContentTypeHandler).
			Then(handlers.PingHandler)).
		Methods("GET")

	return rtr
}
<|endoftext|>


<commit_before>package central

// These options are output along with a response by System.Answer()
// There are choices for the user to respond to a system clarification question

type Options struct {
	keys []string
	values []string
}

func NewOptions() *Options {
	return &Options{}
}

func (options *Options) AddOption(key string, value string) {
	options.keys = append(options.keys, key)
	options.values = append(options.values, value)
}

func (options *Options) GetKeys() []string {
	return options.keys
}

func (options *Options) GetValues() []string {
	return options.values
}

func (options *Options) HasOptions() bool {
	return len(options.keys) > 0
}

func (options *Options) String() string {
	string := ""

	for i := 0; i < len(options.keys); i++ {
		string += " [" + options.keys[i] + "] " + options.values[i]
	}

	return string
}<commit_msg>initialize OptionKeys and OptionValues<commit_after>package central

// These options are output along with a response by System.Answer()
// There are choices for the user to respond to a system clarification question

type Options struct {
	keys []string
	values []string
}

func NewOptions() *Options {
	return &Options{
		keys: []string{},
		values: []string{},
	}
}

func (options *Options) AddOption(key string, value string) {
	options.keys = append(options.keys, key)
	options.values = append(options.values, value)
}

func (options *Options) GetKeys() []string {
	return options.keys
}

func (options *Options) GetValues() []string {
	return options.values
}

func (options *Options) HasOptions() bool {
	return len(options.keys) > 0
}

func (options *Options) String() string {
	string := ""

	for i := 0; i < len(options.keys); i++ {
		string += " [" + options.keys[i] + "] " + options.values[i]
	}

	return string
}<|endoftext|>


<commit_before>package alg

import (
	"math"
	"math/rand"
)

// IntRange represents the ability
// to poll a struct and return an integer,
// distributed over some range dependant
// on the implementing struct.
type IntRange interface {
	Poll() int
}

// LinearIntRange polls on a linear scale
// between a minimum and a maximum
type LinearIntRange struct {
	Min, Max int
}

// Poll returns an integer distributed
// between lir.Min and lir.Max
func (lir LinearIntRange) Poll() int {
	return rand.Intn(lir.Max-lir.Min) + lir.Min
}

type BaseSpreadRangei struct {
	Base, Spread int
}

func (b BaseSpreadRangei) Poll() int {
	return b.Base + rand.Intn((b.Spread*2)+1) - b.Spread
}

// Constant implements IntRange as a poll
// which always returns the same integer.
type Constant int

// Poll returns c cast to an int
func (c Constant) Poll() int {
	return int(c)
}

type Infinite struct{}

func (inf Infinite) Poll() int {
	return math.MaxInt32
}

type FloatRange interface {
	Poll() float64
	Mult(f float64) FloatRange
}

type BaseSpreadRange struct {
	Base, Spread float64
}

func (b BaseSpreadRange) Poll() float64 {
	return b.Base + (b.Spread * 2 * rand.Float64()) - b.Spread
}

func (b BaseSpreadRange) Mult(f float64) FloatRange {
	b.Base = b.Base * f
	b.Spread = b.Spread * f
	return b
}

type Constantf float64

func (c Constantf) Poll() float64 {
	return float64(c)
}

func (c Constantf) Mult(f float64) FloatRange {
	c = Constantf(float64(c) * f)
	return c
}
<commit_msg>Amend to last commit-- range file is now intrange and floatrange<commit_after><|endoftext|>


<commit_before>package ali

import (
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"
)

func urlValues(c *Client, param PayParam) url.Values {
	values := url.Values{}
	values.Add("app_id", c.config.AppID)
	values.Add("method", param.URI())
	values.Add("format", "JSON")
	values.Add("charset", "utf-8")
	values.Add("sign_type", c.config.SignType)
	values.Add("timestamp", generateTimestampStr())
	values.Add("version", "1.0")
	values.Add("biz_content", param.BizContent())

	for k, v := range param.ExtraParams() {
		values.Add(k, v)
	}

	var keys []string
	for k := range values {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	values.Add("sign", signature(keys, values, c.config.AppPrivateKey, c.config.SignType))
	fmt.Printf("SIGN %#v\n", values)
	return values
}

func signature(keys []string, values url.Values, privateKey []byte, signType string) string {
	if values == nil {
		values = url.Values{}
	}

	var valueList []string
	for _, k := range keys {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}

	concat := strings.Join(valueList, "&")

	var sign string
	if signType == "RSA" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA1)
	} else if signType == "RSA2" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA256)
	}
	return sign
}

func signPKCS1v15(source, privateKey []byte, hash crypto.Hash) string {
	block, _ := pem.Decode(privateKey)
	if block == nil {
		return ""
	}

	rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return ""
	}

	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	s, err := rsa.SignPKCS1v15(nil, rsaPrivateKey, hash, hashed)
	if err != nil {
		return ""
	}
	return base64.StdEncoding.EncodeToString(s)
}

func verify(values url.Values, publicKey []byte, signType string) bool {
	decoded, err := base64.StdEncoding.DecodeString(values.Get("sign"))
	if err != nil {
		return false
	}

	var excluded []string
	for k, v := range values {
		if k == "sign" || k == "sign_type" {
			continue
		}
		if len(v) > 0 {
			excluded = append(excluded, k)
		}
	}

	sort.Strings(excluded)

	var valueList []string
	for _, k := range excluded {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}
	concat := strings.Join(valueList, "&")

	var ok bool
	if signType == "RSA" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA1)
	} else if signType == "RSA2" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA256)
	}
	return ok
}

func verifyPKCS1v15(source, sign, publicKey []byte, hash crypto.Hash) bool {
	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	block, _ := pem.Decode(publicKey)
	if block == nil {
		return false
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return false
	}

	rsaPublicKey := pub.(*rsa.PublicKey)
	err = rsa.VerifyPKCS1v15(rsaPublicKey, hash, hashed, sign)
	if err != nil {
		return false
	}
	return true
}

func generateTimestampStr() string {
	now := time.Now()
	year, month, day := now.Date()
	hour, min, sec := now.Clock()
	return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec)
}

func marshalJSON(val interface{}) string {
	data, err := json.Marshal(val)
	if err != nil {
		return ""
	}
	return string(data)
}
<commit_msg>add log<commit_after>package ali

import (
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"
)

func urlValues(c *Client, param PayParam) url.Values {
	values := url.Values{}
	values.Add("app_id", c.config.AppID)
	values.Add("method", param.URI())
	values.Add("format", "JSON")
	values.Add("charset", "utf-8")
	values.Add("sign_type", c.config.SignType)
	values.Add("timestamp", generateTimestampStr())
	values.Add("version", "1.0")
	values.Add("biz_content", param.BizContent())

	for k, v := range param.ExtraParams() {
		values.Add(k, v)
	}

	var keys []string
	for k := range values {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	values.Add("sign", signature(keys, values, c.config.AppPrivateKey, c.config.SignType))
	fmt.Printf("VALUES %v, CONFIG %v\n", values, c.config)
	return values
}

func signature(keys []string, values url.Values, privateKey []byte, signType string) string {
	if values == nil {
		values = url.Values{}
	}

	var valueList []string
	for _, k := range keys {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}

	concat := strings.Join(valueList, "&")

	var sign string
	if signType == "RSA" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA1)
	} else if signType == "RSA2" {
		sign = signPKCS1v15([]byte(concat), privateKey, crypto.SHA256)
	}
	return sign
}

func signPKCS1v15(source, privateKey []byte, hash crypto.Hash) string {
	block, _ := pem.Decode(privateKey)
	if block == nil {
		return ""
	}

	rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return ""
	}

	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	s, err := rsa.SignPKCS1v15(nil, rsaPrivateKey, hash, hashed)
	if err != nil {
		return ""
	}
	return base64.StdEncoding.EncodeToString(s)
}

func verify(values url.Values, publicKey []byte, signType string) bool {
	decoded, err := base64.StdEncoding.DecodeString(values.Get("sign"))
	if err != nil {
		return false
	}

	var excluded []string
	for k, v := range values {
		if k == "sign" || k == "sign_type" {
			continue
		}
		if len(v) > 0 {
			excluded = append(excluded, k)
		}
	}

	sort.Strings(excluded)

	var valueList []string
	for _, k := range excluded {
		v := strings.TrimSpace(values.Get(k))
		if v != "" {
			valueList = append(valueList, fmt.Sprintf("%s=%s", k, v))
		}
	}
	concat := strings.Join(valueList, "&")

	var ok bool
	if signType == "RSA" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA1)
	} else if signType == "RSA2" {
		ok = verifyPKCS1v15([]byte(concat), decoded, publicKey, crypto.SHA256)
	}
	return ok
}

func verifyPKCS1v15(source, sign, publicKey []byte, hash crypto.Hash) bool {
	h := hash.New()
	h.Write(source)
	hashed := h.Sum(nil)

	block, _ := pem.Decode(publicKey)
	if block == nil {
		return false
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return false
	}

	rsaPublicKey := pub.(*rsa.PublicKey)
	err = rsa.VerifyPKCS1v15(rsaPublicKey, hash, hashed, sign)
	if err != nil {
		return false
	}
	return true
}

func generateTimestampStr() string {
	now := time.Now()
	year, month, day := now.Date()
	hour, min, sec := now.Clock()
	return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec)
}

func marshalJSON(val interface{}) string {
	data, err := json.Marshal(val)
	if err != nil {
		return ""
	}
	return string(data)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
)

var analyticsPath = filepath.Join(HomeDir, ".heroku", "analytics.json")

// AnalyticsCommands represents the analytics file
type AnalyticsCommands []struct {
	Command   string `json:"command"`
	Timestamp int64  `json:"timestamp"`
	Version   string `json:"version"`
	Platform  string `json:"platform"`
	Language  string `json:"language"`
}

// RecordAnalytics records the commands users run
// For now the actual recording is done in the Ruby CLI,
// this just performs the submission
func RecordAnalytics(wg *sync.WaitGroup) {
	defer wg.Done()
	if skipAnalytics() {
		return
	}
	f, err := os.Open(analyticsPath)
	if err != nil {
		Logln(err)
		return
	}
	var analytics AnalyticsCommands
	if err := json.NewDecoder(f).Decode(&analytics); err != nil {
		Logln(err)
		return
	}
	if len(analytics) < 10 {
		// do not record if less than 10 analytics
		return
	}
	req := apiRequestBase("")
	req.Uri = "https://cli-analytics.heroku.com/record"
	req.Method = "POST"
	req.Body = struct {
		Commands AnalyticsCommands `json:"commands"`
		User     string            `json:"user"`
	}{
		Commands: analytics,
		User:     netrcLogin(),
	}
	resp, err := req.Do()
	if err != nil {
		Logln(err)
		return
	}
	if resp.StatusCode != 201 {
		Logln("analytics: HTTP " + resp.Status)
		return
	}
	os.Truncate(analyticsPath, 0)
}

func skipAnalytics() bool {
	skip, err := config.GetBool("skip_analytics")
	if err != nil {
		Logln(err)
		return true
	}
	return skip
}
<commit_msg>locking around analytics<commit_after>package main

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sync"

	"github.com/dickeyxxx/golock"
)

var analyticsPath = filepath.Join(HomeDir, ".heroku", "analytics.json")

// AnalyticsCommands represents the analytics file
type AnalyticsCommands []struct {
	Command   string `json:"command"`
	Timestamp int64  `json:"timestamp"`
	Version   string `json:"version"`
	Platform  string `json:"platform"`
	Language  string `json:"language"`
}

// RecordAnalytics records the commands users run
// For now the actual recording is done in the Ruby CLI,
// this just performs the submission
func RecordAnalytics(wg *sync.WaitGroup) {
	defer wg.Done()
	if skipAnalytics() {
		return
	}
	f, err := os.Open(analyticsPath)
	if err != nil {
		Logln(err)
		return
	}
	var analytics AnalyticsCommands
	if err := json.NewDecoder(f).Decode(&analytics); err != nil {
		Logln(err)
		return
	}
	if len(analytics) < 10 {
		// do not record if less than 10 analytics
		return
	}
	lockfile := filepath.Join(AppDir(), "analytics.lock")
	if locked, _ := golock.IsLocked(lockfile); locked {
		// skip if already updating
		return
	}
	golock.Lock(lockfile)
	defer golock.Unlock(lockfile)
	req := apiRequestBase("")
	req.Uri = "https://cli-analytics.heroku.com/record"
	req.Method = "POST"
	req.Body = struct {
		Commands AnalyticsCommands `json:"commands"`
		User     string            `json:"user"`
	}{
		Commands: analytics,
		User:     netrcLogin(),
	}
	resp, err := req.Do()
	if err != nil {
		Logln(err)
		return
	}
	if resp.StatusCode != 201 {
		Logln("analytics: HTTP " + resp.Status)
		return
	}
	os.Truncate(analyticsPath, 0)
}

func skipAnalytics() bool {
	skip, err := config.GetBool("skip_analytics")
	if err != nil {
		Logln(err)
		return true
	}
	return skip
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"
	"runtime"
	"time"
)

var (
	counter   int
	userlist  map[int]*User
	chanlist  map[string]*Channel
	max_users int
	epoch     time.Time
)

func main() {
	epoch = time.Now()
	SetupNumerics()
	userlist = make(map[int]*User)
	chanlist = make(map[string]*Channel)
	// Listen for incoming connections.
	l, err := net.Listen("tcp", CONN_HOST+":"+CONN_PORT)
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		os.Exit(1)

	}
	// Close the listener when the application closes.
	defer l.Close()
	fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)
	go PeriodicStatusUpdate()
	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)

		}
		// Handle connections in a new goroutine.
		user := NewUser(conn)
		go CheckMaxUsers()
		go user.HandleRequests()
	}

}

func CheckMaxUsers() {
	if len(userlist) > max_users {
		max_users = len(userlist)
	}
}

func PeriodicStatusUpdate() {
	for {
		fmt.Println("Status:", len(userlist), "current users")
		fmt.Println("Status:", runtime.NumGoroutine(), "current Goroutines")
		fmt.Println("Status:", counter, "total connections")
		fmt.Println("Status:", max_users, "max users")
		time.Sleep(5 * time.Second)
	}
}
<commit_msg>better info in server status<commit_after>package main

import (
	"fmt"
	"net"
	"os"
	"runtime"
	"time"
)

var (
	counter   int
	userlist  map[int]*User
	chanlist  map[string]*Channel
	max_users int
	epoch     time.Time
)

func main() {
	epoch = time.Now()
	SetupNumerics()
	userlist = make(map[int]*User)
	chanlist = make(map[string]*Channel)
	// Listen for incoming connections.
	l, err := net.Listen("tcp", CONN_HOST+":"+CONN_PORT)
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		os.Exit(1)

	}
	// Close the listener when the application closes.
	defer l.Close()
	fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)
	go PeriodicStatusUpdate()
	for {
		// Listen for an incoming connection.
		conn, err := l.Accept()
		if err != nil {
			fmt.Println("Error accepting: ", err.Error())
			os.Exit(1)

		}
		// Handle connections in a new goroutine.
		user := NewUser(conn)
		go CheckMaxUsers()
		go user.HandleRequests()
	}

}

func CheckMaxUsers() {
	if len(userlist) > max_users {
		max_users = len(userlist)
	}
}

func PeriodicStatusUpdate() {
	for {
		fmt.Println("Status:", len(userlist), "current users")
		fmt.Println("Status:", len(chanlist), "current channels")
		fmt.Println("Status:", runtime.NumGoroutine(), "current Goroutines")
		time.Sleep(5 * time.Second)

	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strings"
	"time"
)

type User struct {
	nick       string
	user       string
	ident      string
	dead       bool
	nickset    bool
	connection net.Conn
	id         int
	realname   string
	userset    bool
	registered bool
	ip         string
	host       string
	epoch      time.Time
	chanlist   map[string]*Channel
}

func (user *User) Quit() {
	user.dead = true
	if user.connection != nil {
		user.connection.Close()
	}
	delete(userlist, user.id)
}

func (user *User) FireNumeric(numeric int, args ...interface{}) {
	msg := strcat(fmt.Sprintf(":%s %.3d %s ", sname, numeric, user.nick), fmt.Sprintf(NUM[numeric], args...))
	user.SendLine(msg)
}

func NewUser(conn net.Conn) *User {
	userip := GetIpFromConn(conn)
	fmt.Println("New connection from", userip)
	counter = counter + 1
	user := &User{id: counter, connection: conn, ip: userip, nick: "*"}
	user.chanlist = make(map[string]*Channel)
	user.host = user.ip
	user.epoch = time.Now()
	userlist[user.id] = user
	go user.UserHostLookup()
	return user
}

func (user *User) SendLine(msg string) {
	msg = fmt.Sprintf("%s\n", msg)
	if user.dead {
		return
	}
	_, err := user.connection.Write([]byte(msg))
	if err != nil {
		user.Quit()
		fmt.Printf("Error sending message to %s, disconnecting\n", user.nick)
	}
	fmt.Printf("Send to %s: %s", user.nick, msg)
}

func (user *User) HandleRequests() {
	b := bufio.NewReader(user.connection)
	for {
		if user.dead {
			break
		}
		line, err := b.ReadString('\n')
		if err != nil {
			fmt.Println("Error reading:", err.Error())
			user.Quit()
		}
		if line == "" {
			user.Quit()
			break
		}
		line = strings.TrimSpace(line)
		fmt.Println("Receive from", fmt.Sprintf("%s:", user.nick), line)
		ProcessLine(user, line)
	}
}
func (user *User) NickHandler(args []string) {
	if len(args) < 2 {
		user.FireNumeric(ERR_NONICKNAMEGIVEN)
		return
	}
	if CheckNickCollision(args[1]) != false {
		return //TODO handle properly
	}
	if !user.nickset {
		user.nickset = true
	}
	user.nick = args[1]
	fmt.Println("User changed name to", args[1])
	if !user.registered && user.userset {
		user.UserRegistrationFinished()
	}
}

func (user *User) UserHandler(args []string) {
	if len(args) < 5 {
		//ERR_NEEDMOREPARAMS
		return
	}
	user.ident = args[1]
	if strings.HasPrefix(args[4], ":") {
		args[4] = strings.Replace(args[4], ":", "", 1)
	}
	var buffer bytes.Buffer
	for i := 4; i < len(args); i++ {
		buffer.WriteString(args[i])
		buffer.WriteString(" ")
	}
	user.realname = strings.TrimSpace(buffer.String())
	user.userset = true
	if !user.registered && user.nickset {
		user.UserRegistrationFinished()
	}
}

func (user *User) UserRegistrationFinished() {
	user.registered = true
	fmt.Printf("User %d finished registration\n", user.id)
	user.FireNumeric(RPL_WELCOME, user.nick, user.ident, user.host)
	user.FireNumeric(RPL_YOURHOST, sname, software, softwarev)
	user.FireNumeric(RPL_CREATED, epoch)
	//TODO fire RPL_MYINFO when we actually have enough stuff to do it
}

func (user *User) UserHostLookup() {
	user.SendLine(fmt.Sprintf(":%s NOTICE %s :*** Looking up your hostname...", sname, user.nick))
	adds, err := net.LookupAddr(user.ip)
	if err != nil {
		user.SendLine(fmt.Sprintf("%s NOTICE %s :*** Unable to resolve your hostname", sname, user.nick))
		return
	}
	addstring := adds[0]
	adds, err = net.LookupHost(addstring)
	if err != nil {
		user.SendLine(fmt.Sprintf("%s NOTICE %s :*** Unable to resolve your hostname", sname, user.nick))
		return
	}
	for _, k := range adds {
		if user.ip == k {
			user.host = addstring
			user.SendLine(fmt.Sprintf(":%s NOTICE %s :*** Found your hostname", sname, user.nick))
			return
		}
	}
	user.SendLine(fmt.Sprintf(":%s NOTICE %s :*** Your forward and reverse DNS do not match, ignoring hostname", sname, user.nick))
}

func (user *User) CommandNotFound(args []string) {
	user.FireNumeric(ERR_UNKNOWNCOMMAND, args[0])
}

func (user *User) GetHostMask() string {
	return fmt.Sprintf("%s!%s@%s", user.nick, user.ident, user.host)
}

func (user *User) JoinHandler(args []string) {
	_, channel := GetChannelByName(args[1])
	channel.JoinUser(user)
	user.chanlist[channel.name] = channel
}
<commit_msg>provide feedback when a nick is changed<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"strings"
	"time"
)

type User struct {
	nick       string
	user       string
	ident      string
	dead       bool
	nickset    bool
	connection net.Conn
	id         int
	realname   string
	userset    bool
	registered bool
	ip         string
	host       string
	epoch      time.Time
	chanlist   map[string]*Channel
}

func (user *User) Quit() {
	user.dead = true
	if user.connection != nil {
		user.connection.Close()
	}
	delete(userlist, user.id)
}

func (user *User) FireNumeric(numeric int, args ...interface{}) {
	msg := strcat(fmt.Sprintf(":%s %.3d %s ", sname, numeric, user.nick), fmt.Sprintf(NUM[numeric], args...))
	user.SendLine(msg)
}

func NewUser(conn net.Conn) *User {
	userip := GetIpFromConn(conn)
	fmt.Println("New connection from", userip)
	counter = counter + 1
	user := &User{id: counter, connection: conn, ip: userip, nick: "*"}
	user.chanlist = make(map[string]*Channel)
	user.host = user.ip
	user.epoch = time.Now()
	userlist[user.id] = user
	go user.UserHostLookup()
	return user
}

func (user *User) SendLine(msg string) {
	msg = fmt.Sprintf("%s\n", msg)
	if user.dead {
		return
	}
	_, err := user.connection.Write([]byte(msg))
	if err != nil {
		user.Quit()
		fmt.Printf("Error sending message to %s, disconnecting\n", user.nick)
	}
	fmt.Printf("Send to %s: %s", user.nick, msg)
}

func (user *User) HandleRequests() {
	b := bufio.NewReader(user.connection)
	for {
		if user.dead {
			break
		}
		line, err := b.ReadString('\n')
		if err != nil {
			fmt.Println("Error reading:", err.Error())
			user.Quit()
		}
		if line == "" {
			user.Quit()
			break
		}
		line = strings.TrimSpace(line)
		fmt.Println("Receive from", fmt.Sprintf("%s:", user.nick), line)
		ProcessLine(user, line)
	}
}
func (user *User) NickHandler(args []string) {
	if len(args) < 2 {
		user.FireNumeric(ERR_NONICKNAMEGIVEN)
		return
	}
	if CheckNickCollision(args[1]) != false {
		user.FireNumeric(ERR_NICKNAMEINUSE, args[1])
    return
	}
	if !user.nickset {
		user.nickset = true
  } else {
    targets := []*User{}
    targets = append(targets,user)
    for _, k := range user.chanlist {
      targets = append(targets,k.GetUserList()...)
    }
    SendToMany(fmt.Sprintf(":%s NICK %s", user.GetHostMask(), args[1]), targets)
  }
	user.nick = args[1]
	fmt.Println("User changed name to", args[1])
	if !user.registered && user.userset {
		user.UserRegistrationFinished()
	}
}

func (user *User) UserHandler(args []string) {
	if len(args) < 5 {
		//ERR_NEEDMOREPARAMS
		return
	}
	user.ident = args[1]
	if strings.HasPrefix(args[4], ":") {
		args[4] = strings.Replace(args[4], ":", "", 1)
	}
	var buffer bytes.Buffer
	for i := 4; i < len(args); i++ {
		buffer.WriteString(args[i])
		buffer.WriteString(" ")
	}
	user.realname = strings.TrimSpace(buffer.String())
	user.userset = true
	if !user.registered && user.nickset {
		user.UserRegistrationFinished()
	}
}

func (user *User) UserRegistrationFinished() {
	user.registered = true
	fmt.Printf("User %d finished registration\n", user.id)
	user.FireNumeric(RPL_WELCOME, user.nick, user.ident, user.host)
	user.FireNumeric(RPL_YOURHOST, sname, software, softwarev)
	user.FireNumeric(RPL_CREATED, epoch)
	//TODO fire RPL_MYINFO when we actually have enough stuff to do it
}

func (user *User) UserHostLookup() {
	user.SendLine(fmt.Sprintf(":%s NOTICE %s :*** Looking up your hostname...", sname, user.nick))
	adds, err := net.LookupAddr(user.ip)
	if err != nil {
		user.SendLine(fmt.Sprintf("%s NOTICE %s :*** Unable to resolve your hostname", sname, user.nick))
		return
	}
	addstring := adds[0]
	adds, err = net.LookupHost(addstring)
	if err != nil {
		user.SendLine(fmt.Sprintf("%s NOTICE %s :*** Unable to resolve your hostname", sname, user.nick))
		return
	}
	for _, k := range adds {
		if user.ip == k {
			user.host = addstring
			user.SendLine(fmt.Sprintf(":%s NOTICE %s :*** Found your hostname", sname, user.nick))
			return
		}
	}
	user.SendLine(fmt.Sprintf(":%s NOTICE %s :*** Your forward and reverse DNS do not match, ignoring hostname", sname, user.nick))
}

func (user *User) CommandNotFound(args []string) {
	user.FireNumeric(ERR_UNKNOWNCOMMAND, args[0])
}

func (user *User) GetHostMask() string {
	return fmt.Sprintf("%s!%s@%s", user.nick, user.ident, user.host)
}

func (user *User) JoinHandler(args []string) {
	_, channel := GetChannelByName(args[1])
	channel.JoinUser(user)
	user.chanlist[channel.name] = channel
}
<|endoftext|>