<commit_before>package sqlr

import "database/sql"

// A SchemaOption provides optional configuration and is supplied when
// creating a new Schema, or cloning a Schema.
type SchemaOption func(schema *Schema)

// ForDB creates an option that sets the dialect for the open DB handle.
func ForDB(db *sql.DB) SchemaOption {
	return func(schema *Schema) {
		schema.dialect = dialectFor(db)
		schema.cache.clear()
	}
}

// WithDialect provides an option that sets the schema's dialect.
func WithDialect(dialect Dialect) SchemaOption {
	return func(schema *Schema) {
		schema.dialect = dialect
		schema.cache.clear()
	}
}

// WithNamingConvention creates and option that sets the schema's naming convention.
func WithNamingConvention(convention NamingConvention) SchemaOption {
	return func(schema *Schema) {
		schema.convention = convention
		schema.cache.clear()
	}
}

// WithField creates an option that maps a Go field name to a
// database column name.
//
// It is more common to override column names in the struct tag of
// the field, but there are some cases where it makes sense to
// declare column name overrides directly with the schema. One situation
// is with fields within embedded structures. For example, with the following
// structures:
//  type UserRow struct {
//      Name string
//      HomeAddress Address
//      WorkAddress Address
//  }
//
//  type Address struct {
//      Street   string
//      Locality string
//      State    string
//  }
//
// If the column name for HomeAddress.Locality is called "home_suburb" for historical
// reasons, then it is not possible to specify a rename in the structure tag
// without also affecting the WorkAddress.Locality field. In this situation it is only
// possible to specify the column name override using the WithField option:
//  schema := NewSchema(
//      WithField("HomeAddress.Locality", "home_suburb"),
//  )
//
func WithField(fieldName string, columnName string) SchemaOption {
	return func(schema *Schema) {
		if schema.fieldMap == nil {
			schema.fieldMap = newFieldMap(schema.fieldMap)
		}
		schema.fieldMap.add(fieldName, columnName)
	}
}

// WithIdentifier creates an option that performs a global rename
// of an identifier when preparing SQL queries. This option is not
// needed very often: its main purpose is for helping a program
// operate against two different database schemas where table and
// column names follow a different naming convention.
//
// The example shows a situation where a program operates against
// an SQL Server database where a table is named "[User]", but the
// same table is named "users" in the Postgres schema.
func WithIdentifier(identifier string, meaning string) SchemaOption {
	return func(schema *Schema) {
		if schema.identMap == nil {
			schema.identMap = newIdentMap(schema.identMap)
		}
		schema.identMap.add(meaning, identifier)
	}
}
<commit_msg>Implement WithTagKey schema option.<commit_after>package sqlr

import "database/sql"

// A SchemaOption provides optional configuration and is supplied when
// creating a new Schema, or cloning a Schema.
type SchemaOption func(schema *Schema)

// ForDB creates an option that sets the dialect for the open DB handle.
func ForDB(db *sql.DB) SchemaOption {
	return func(schema *Schema) {
		schema.dialect = dialectFor(db)
		schema.cache.clear()
	}
}

// WithDialect provides an option that sets the schema's dialect.
func WithDialect(dialect Dialect) SchemaOption {
	return func(schema *Schema) {
		schema.dialect = dialect
		schema.cache.clear()
	}
}

// WithNamingConvention creates and option that sets the schema's naming convention.
func WithNamingConvention(convention NamingConvention) SchemaOption {
	return func(schema *Schema) {
		schema.convention = convention
		schema.cache.clear()
	}
}

// WithField creates an option that maps a Go field name to a
// database column name.
//
// It is more common to override column names in the struct tag of
// the field, but there are some cases where it makes sense to
// declare column name overrides directly with the schema. One situation
// is with fields within embedded structures. For example, with the following
// structures:
//  type UserRow struct {
//      Name string
//      HomeAddress Address
//      WorkAddress Address
//  }
//
//  type Address struct {
//      Street   string
//      Locality string
//      State    string
//  }
//
// If the column name for HomeAddress.Locality is called "home_suburb" for historical
// reasons, then it is not possible to specify a rename in the structure tag
// without also affecting the WorkAddress.Locality field. In this situation it is only
// possible to specify the column name override using the WithField option:
//  schema := NewSchema(
//      WithField("HomeAddress.Locality", "home_suburb"),
//  )
//
func WithField(fieldName string, columnName string) SchemaOption {
	return func(schema *Schema) {
		if schema.fieldMap == nil {
			schema.fieldMap = newFieldMap(schema.fieldMap)
		}
		schema.fieldMap.add(fieldName, columnName)
	}
}

// WithIdentifier creates an option that performs a global rename
// of an identifier when preparing SQL queries. This option is not
// needed very often: its main purpose is for helping a program
// operate against two different database schemas where table and
// column names follow a different naming convention.
//
// The example shows a situation where a program operates against
// an SQL Server database where a table is named "[User]", but the
// same table is named "users" in the Postgres schema.
func WithIdentifier(identifier string, meaning string) SchemaOption {
	return func(schema *Schema) {
		if schema.identMap == nil {
			schema.identMap = newIdentMap(schema.identMap)
		}
		schema.identMap.add(meaning, identifier)
	}
}

// WithTagKey creates an option that associates the schema
// with a key in struct field tags. This option is not needed
// very often: its main purpose is for helping a program operate
// against two different database schemas.
func WithTagKey(key string) SchemaOption {
	return func(schema *Schema) {
		schema.key = key
	}
}
<|endoftext|>


<commit_before>package sender

import (
	"bytes"
	"encoding/json"
	"errors"
	"sync/atomic"
	"time"

	"github.com/qiniu/log"
	"github.com/qiniu/logkit/conf"
	"github.com/qiniu/logkit/queue"
	"github.com/qiniu/logkit/utils"
	"github.com/qiniu/pandora-go-sdk/base/reqerr"
)

const (
	mb                = 1024 * 1024 // 1MB
	defaultWriteLimit = 10          // 默认写速限制为10MB
	maxBytesPerFile   = 100 * mb
	qNameSuffix       = "_local_save"
	memoryChanSuffix  = "_memory"
	defaultMaxProcs   = 1 // 默认没有并发
)

// 可选参数 fault_tolerant 为true的话，以下必填
const (
	KeyFtSyncEvery         = "ft_sync_every"    // 该参数设置多少次写入会同步一次offset log
	KeyFtSaveLogPath       = "ft_save_log_path" // disk queue 数据日志路径
	KeyFtWriteLimit        = "ft_write_limit"   // 写入速度限制，单位MB
	KeyFtStrategy          = "ft_strategy"      // ft 的策略
	KeyFtProcs             = "ft_procs"         // ft并发数，当always_save 策略时启用
	KeyFtMemoryChannel     = "ft_memory_channel"
	KeyFtMemoryChannelSize = "ft_memory_channel_size"
)

// ft 策略
const (
	// KeyFtStrategyBackupOnly 只在失败的时候进行容错
	KeyFtStrategyBackupOnly = "backup_only"
	// KeyFtStrategyAlwaysSave 所有数据都进行容错
	KeyFtStrategyAlwaysSave = "always_save"
)

// FtSender fault tolerance sender wrapper
type FtSender struct {
	stopped     int32
	exitChan    chan struct{}
	innerSender Sender
	logQueue    queue.BackendQueue
	backupQueue queue.BackendQueue
	writeLimit  int  // 写入速度限制，单位MB
	backupOnly  bool // 是否只使用backup queue
	procs       int  //发送并发数
	runnerName  string
	opt         *FtOption
	stats       utils.StatsInfo
}

type FtOption struct {
	saveLogPath       string
	syncEvery         int64
	writeLimit        int
	backupOnly        bool
	procs             int
	memoryChannel     bool
	memoryChannelSize int
}

type datasContext struct {
	Datas []Data `json:"datas"`
}

// NewFtSender Fault tolerant sender constructor
func NewFtSender(sender Sender, conf conf.MapConf) (*FtSender, error) {
	memoryChannel, _ := conf.GetBoolOr(KeyFtMemoryChannel, false)
	memoryChannelSize, _ := conf.GetIntOr(KeyFtMemoryChannelSize, 100)

	logpath, err := conf.GetString(KeyFtSaveLogPath)
	if err != nil {
		return nil, err
	}
	syncEvery, _ := conf.GetIntOr(KeyFtSyncEvery, DefaultFtSyncEvery)
	writeLimit, _ := conf.GetIntOr(KeyFtWriteLimit, defaultWriteLimit)
	strategy, _ := conf.GetStringOr(KeyFtStrategy, KeyFtStrategyAlwaysSave)
	procs, _ := conf.GetIntOr(KeyFtProcs, defaultMaxProcs)
	runnerName, _ := conf.GetStringOr(KeyRunnerName, UnderfinedRunnerName)

	opt := &FtOption{
		saveLogPath:       logpath,
		syncEvery:         int64(syncEvery),
		writeLimit:        writeLimit,
		backupOnly:        strategy == KeyFtStrategyBackupOnly,
		procs:             procs,
		memoryChannel:     memoryChannel,
		memoryChannelSize: memoryChannelSize,
	}

	return newFtSender(sender, runnerName, opt)
}

func newFtSender(innerSender Sender, runnerName string, opt *FtOption) (*FtSender, error) {
	var lq, bq queue.BackendQueue
	err := utils.CreateDirIfNotExist(opt.saveLogPath)
	if err != nil {
		return nil, err
	}
	if !opt.memoryChannel {
		lq = queue.NewDiskQueue("stream"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, false, 0)
		bq = queue.NewDiskQueue("backup"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, false, 0)
	} else {
		err := utils.CreateDirIfNotExist(opt.saveLogPath)
		if err != nil {
			return nil, err
		}
		lq = queue.NewDiskQueue("stream"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, true, opt.memoryChannelSize)
		bq = queue.NewDiskQueue("backup"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, true, opt.memoryChannelSize)
	}
	ftSender := FtSender{
		exitChan:    make(chan struct{}),
		innerSender: innerSender,
		logQueue:    lq,
		backupQueue: bq,
		writeLimit:  opt.writeLimit,
		backupOnly:  opt.backupOnly,
		procs:       opt.procs,
		runnerName:  runnerName,
	}
	go ftSender.asyncSendLogFromDiskQueue()
	return &ftSender, nil
}

func (ft *FtSender) Name() string {
	return ft.innerSender.Name() + "(ft)"
}

func (ft *FtSender) Send(datas []Data) error {
	se := &utils.StatsError{Ft: true}
	if ft.backupOnly {
		// 尝试直接发送数据，当数据失败的时候会加入到本地重试队列。外部不需要重试
		backDataContext, err := ft.trySendDatas(datas, 1)
		if err != nil {
			log.Warnf("Runner[%v] Sender[%v] try Send Datas err: %v", ft.runnerName, ft.innerSender.Name(), err)
		}
		// 容错队列会保证重试，此处不向外部暴露发送错误信息
		se.ErrorDetail = nil
		se.Ftlag = ft.backupQueue.Depth()
		if backDataContext != nil {
			var nowDatas []Data
			for _, v := range backDataContext {
				nowDatas = append(nowDatas, v.Datas...)
			}
			if nowDatas != nil {
				se.ErrorDetail = reqerr.NewSendError("save data to backend queue error", ConvertDatasBack(nowDatas), reqerr.TypeDefault)
				ft.stats.Errors += int64(len(nowDatas))
				ft.stats.LastError = se.ErrorDetail
			}
		}
	} else {
		err := ft.saveToFile(datas)
		if err != nil {
			se.ErrorDetail = err
			ft.stats.LastError = err
			ft.stats.Errors += int64(len(datas))
		} else {
			se.ErrorDetail = nil
		}
		se.Ftlag = ft.backupQueue.Depth() + ft.logQueue.Depth()
	}
	return se
}

func (ft *FtSender) Stats() utils.StatsInfo {
	return ft.stats
}

func (ft *FtSender) Close() error {
	atomic.AddInt32(&ft.stopped, 1)
	log.Warnf("Runner[%v] wait for Sender[%v] to completely exit", ft.runnerName, ft.Name())
	// 等待错误恢复流程退出
	<-ft.exitChan
	// 等待正常发送流程退出
	for i := 0; i < ft.procs; i++ {
		<-ft.exitChan
	}

	log.Warnf("Runner[%v] Sender[%v] has been completely exited", ft.runnerName, ft.Name())

	// persist queue's meta data
	ft.logQueue.Close()
	ft.backupQueue.Close()

	return ft.innerSender.Close()
}

// marshalData 将数据序列化
func (ft *FtSender) marshalData(datas []Data) (bs []byte, err error) {
	ctx := new(datasContext)
	ctx.Datas = datas
	bs, err = json.Marshal(ctx)
	if err != nil {
		err = reqerr.NewSendError("Cannot marshal data :"+err.Error(), ConvertDatasBack(datas), reqerr.TypeDefault)
		return
	}
	return
}

// unmarshalData 如何将数据从磁盘中反序列化出来
func (ft *FtSender) unmarshalData(dat []byte) (datas []Data, err error) {
	ctx := new(datasContext)
	d := json.NewDecoder(bytes.NewReader(dat))
	d.UseNumber()
	err = d.Decode(&ctx)
	if err != nil {
		return
	}
	datas = ctx.Datas
	return
}

func (ft *FtSender) saveToFile(datas []Data) error {
	bs, err := ft.marshalData(datas)
	if err != nil {
		return err
	}
	err = ft.logQueue.Put(bs)
	if err != nil {
		return reqerr.NewSendError(ft.innerSender.Name()+" Cannot put data into backendQueue: "+err.Error(), ConvertDatasBack(datas), reqerr.TypeDefault)
	}
	return nil
}

func (ft *FtSender) asyncSendLogFromDiskQueue() {
	for i := 0; i < ft.procs; i++ {
		go ft.sendFromQueue(ft.logQueue)
	}
	go ft.sendFromQueue(ft.backupQueue)
}

// trySend 从bytes反序列化数据后尝试发送数据
func (ft *FtSender) trySendBytes(dat []byte, failSleep int) (backDataContext []*datasContext, err error) {
	datas, err := ft.unmarshalData(dat)
	if err != nil {
		return
	}
	return ft.trySendDatas(datas, failSleep)
}

func ConvertDatas(ins []map[string]interface{}) []Data {
	var datas []Data
	for _, v := range ins {
		datas = append(datas, Data(v))
	}
	return datas
}
func ConvertDatasBack(ins []Data) []map[string]interface{} {
	var datas []map[string]interface{}
	for _, v := range ins {
		datas = append(datas, map[string]interface{}(v))
	}
	return datas
}

// trySendDatas 尝试发送数据，如果失败，将失败数据加入backup queue，并睡眠指定时间。返回结果为是否正常发送
func (ft *FtSender) trySendDatas(datas []Data, failSleep int) (backDataContext []*datasContext, err error) {
	err = ft.innerSender.Send(datas)
	if c, ok := err.(*utils.StatsError); ok {
		err = c.ErrorDetail
		ft.stats.Errors += c.Errors
		ft.stats.Success += c.Success
	}
	if err != nil {
		retDatasContext := ft.handleSendError(err, datas)
		for _, v := range retDatasContext {
			nnBytes, _ := json.Marshal(v)
			qErr := ft.backupQueue.Put(nnBytes)
			if qErr != nil {
				log.Errorf("Runner[%v] Sender[%v] cannot write points back to queue %v: %v", ft.runnerName, ft.innerSender.Name(), ft.backupQueue.Name(), qErr)
				backDataContext = append(backDataContext, v)
			}
		}
		time.Sleep(time.Second * time.Duration(failSleep))
	}
	return
}

func (ft *FtSender) handleSendError(err error, datas []Data) (retDatasContext []*datasContext) {

	failCtx := new(datasContext)
	var binaryUnpack bool
	se, succ := err.(*reqerr.SendError)
	if !succ {
		// 如果不是SendError 默认所有的数据都发送失败
		log.Infof("Runner[%v] Sender[%v] error type is not *SendError! reSend all datas by default", ft.runnerName, ft.innerSender.Name())
		failCtx.Datas = datas
		ft.stats.LastError = err
	} else {
		failCtx.Datas = ConvertDatas(se.GetFailDatas())
		if se.ErrorType == reqerr.TypeBinaryUnpack {
			binaryUnpack = true
		}
		ft.stats.LastError = errors.New(se.Error())
	}
	log.Errorf("Runner[%v] Sender[%v] cannot write points: %v, failDatas size: %v", ft.runnerName, ft.innerSender.Name(), err, len(failCtx.Datas))
	log.Debugf("Runner[%v] Sender[%v] failed datas [[%v]]", ft.runnerName, ft.innerSender.Name(), failCtx.Datas)
	if binaryUnpack {
		lens := len(failCtx.Datas) / 2
		if lens > 0 {
			newFailCtx := new(datasContext)
			newFailCtx.Datas = failCtx.Datas[0:lens]
			failCtx.Datas = failCtx.Datas[lens:]
			retDatasContext = append(retDatasContext, newFailCtx)
		}
	}
	retDatasContext = append(retDatasContext, failCtx)
	return
}

func (ft *FtSender) sendFromQueue(queue queue.BackendQueue) {
	readChan := queue.ReadChan()
	timer := time.NewTicker(time.Second)
	waitCnt := 1
	var curDataContext, otherDataContext []*datasContext
	var curIdx int
	var backDataContext []*datasContext
	var err error
	for {
		if atomic.LoadInt32(&ft.stopped) > 0 {
			ft.exitChan <- struct{}{}
			return
		}
		if curIdx < len(curDataContext) {
			backDataContext, err = ft.trySendDatas(curDataContext[curIdx].Datas, waitCnt)
			curIdx++
		} else {
			select {
			case dat := <-readChan:
				backDataContext, err = ft.trySendBytes(dat, waitCnt)
			case <-timer.C:
				continue
			}
		}
		if err == nil {
			waitCnt = 1
			//此处的成功发送没有被stats统计
		} else {
			log.Errorf("Runner[%v] Sender[%v] cannot send points from queue %v, error is %v", ft.runnerName, ft.innerSender.Name(), queue.Name(), err)
			//此处的发送错误没有被stats统计
			waitCnt++
			if waitCnt > 10 {
				waitCnt = 10
			}
		}
		if backDataContext != nil {
			otherDataContext = append(otherDataContext, backDataContext...)
		}
		if curIdx == len(curDataContext) {
			curDataContext = otherDataContext
			otherDataContext = make([]*datasContext, 0)
			curIdx = 0
		}
	}
}
<commit_msg>always use disk queue as backup queue<commit_after>package sender

import (
	"bytes"
	"encoding/json"
	"errors"
	"sync/atomic"
	"time"

	"github.com/qiniu/log"
	"github.com/qiniu/logkit/conf"
	"github.com/qiniu/logkit/queue"
	"github.com/qiniu/logkit/utils"
	"github.com/qiniu/pandora-go-sdk/base/reqerr"
)

const (
	mb                = 1024 * 1024 // 1MB
	defaultWriteLimit = 10          // 默认写速限制为10MB
	maxBytesPerFile   = 100 * mb
	qNameSuffix       = "_local_save"
	memoryChanSuffix  = "_memory"
	defaultMaxProcs   = 1 // 默认没有并发
)

// 可选参数 fault_tolerant 为true的话，以下必填
const (
	KeyFtSyncEvery         = "ft_sync_every"    // 该参数设置多少次写入会同步一次offset log
	KeyFtSaveLogPath       = "ft_save_log_path" // disk queue 数据日志路径
	KeyFtWriteLimit        = "ft_write_limit"   // 写入速度限制，单位MB
	KeyFtStrategy          = "ft_strategy"      // ft 的策略
	KeyFtProcs             = "ft_procs"         // ft并发数，当always_save 策略时启用
	KeyFtMemoryChannel     = "ft_memory_channel"
	KeyFtMemoryChannelSize = "ft_memory_channel_size"
)

// ft 策略
const (
	// KeyFtStrategyBackupOnly 只在失败的时候进行容错
	KeyFtStrategyBackupOnly = "backup_only"
	// KeyFtStrategyAlwaysSave 所有数据都进行容错
	KeyFtStrategyAlwaysSave = "always_save"
)

// FtSender fault tolerance sender wrapper
type FtSender struct {
	stopped     int32
	exitChan    chan struct{}
	innerSender Sender
	logQueue    queue.BackendQueue
	backupQueue queue.BackendQueue
	writeLimit  int  // 写入速度限制，单位MB
	backupOnly  bool // 是否只使用backup queue
	procs       int  //发送并发数
	runnerName  string
	opt         *FtOption
	stats       utils.StatsInfo
}

type FtOption struct {
	saveLogPath       string
	syncEvery         int64
	writeLimit        int
	backupOnly        bool
	procs             int
	memoryChannel     bool
	memoryChannelSize int
}

type datasContext struct {
	Datas []Data `json:"datas"`
}

// NewFtSender Fault tolerant sender constructor
func NewFtSender(sender Sender, conf conf.MapConf) (*FtSender, error) {
	memoryChannel, _ := conf.GetBoolOr(KeyFtMemoryChannel, false)
	memoryChannelSize, _ := conf.GetIntOr(KeyFtMemoryChannelSize, 100)

	logpath, err := conf.GetString(KeyFtSaveLogPath)
	if err != nil {
		return nil, err
	}
	syncEvery, _ := conf.GetIntOr(KeyFtSyncEvery, DefaultFtSyncEvery)
	writeLimit, _ := conf.GetIntOr(KeyFtWriteLimit, defaultWriteLimit)
	strategy, _ := conf.GetStringOr(KeyFtStrategy, KeyFtStrategyAlwaysSave)
	procs, _ := conf.GetIntOr(KeyFtProcs, defaultMaxProcs)
	runnerName, _ := conf.GetStringOr(KeyRunnerName, UnderfinedRunnerName)

	opt := &FtOption{
		saveLogPath:       logpath,
		syncEvery:         int64(syncEvery),
		writeLimit:        writeLimit,
		backupOnly:        strategy == KeyFtStrategyBackupOnly,
		procs:             procs,
		memoryChannel:     memoryChannel,
		memoryChannelSize: memoryChannelSize,
	}

	return newFtSender(sender, runnerName, opt)
}

func newFtSender(innerSender Sender, runnerName string, opt *FtOption) (*FtSender, error) {
	var lq, bq queue.BackendQueue
	err := utils.CreateDirIfNotExist(opt.saveLogPath)
	if err != nil {
		return nil, err
	}
	if !opt.memoryChannel {
		lq = queue.NewDiskQueue("stream"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, false, 0)
	} else {
		lq = queue.NewDiskQueue("stream"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, true, opt.memoryChannelSize)
	}
	bq = queue.NewDiskQueue("backup"+qNameSuffix, opt.saveLogPath, maxBytesPerFile, 0, maxBytesPerFile, opt.syncEvery, opt.syncEvery, time.Second*2, opt.writeLimit*mb, false, 0)
	ftSender := FtSender{
		exitChan:    make(chan struct{}),
		innerSender: innerSender,
		logQueue:    lq,
		backupQueue: bq,
		writeLimit:  opt.writeLimit,
		backupOnly:  opt.backupOnly,
		procs:       opt.procs,
		runnerName:  runnerName,
	}
	go ftSender.asyncSendLogFromDiskQueue()
	return &ftSender, nil
}

func (ft *FtSender) Name() string {
	return ft.innerSender.Name() + "(ft)"
}

func (ft *FtSender) Send(datas []Data) error {
	se := &utils.StatsError{Ft: true}
	if ft.backupOnly {
		// 尝试直接发送数据，当数据失败的时候会加入到本地重试队列。外部不需要重试
		backDataContext, err := ft.trySendDatas(datas, 1)
		if err != nil {
			log.Warnf("Runner[%v] Sender[%v] try Send Datas err: %v", ft.runnerName, ft.innerSender.Name(), err)
		}
		// 容错队列会保证重试，此处不向外部暴露发送错误信息
		se.ErrorDetail = nil
		se.Ftlag = ft.backupQueue.Depth()
		if backDataContext != nil {
			var nowDatas []Data
			for _, v := range backDataContext {
				nowDatas = append(nowDatas, v.Datas...)
			}
			if nowDatas != nil {
				se.ErrorDetail = reqerr.NewSendError("save data to backend queue error", ConvertDatasBack(nowDatas), reqerr.TypeDefault)
				ft.stats.Errors += int64(len(nowDatas))
				ft.stats.LastError = se.ErrorDetail
			}
		}
	} else {
		err := ft.saveToFile(datas)
		if err != nil {
			se.ErrorDetail = err
			ft.stats.LastError = err
			ft.stats.Errors += int64(len(datas))
		} else {
			se.ErrorDetail = nil
		}
		se.Ftlag = ft.backupQueue.Depth() + ft.logQueue.Depth()
	}
	return se
}

func (ft *FtSender) Stats() utils.StatsInfo {
	return ft.stats
}

func (ft *FtSender) Close() error {
	atomic.AddInt32(&ft.stopped, 1)
	log.Warnf("Runner[%v] wait for Sender[%v] to completely exit", ft.runnerName, ft.Name())
	// 等待错误恢复流程退出
	<-ft.exitChan
	// 等待正常发送流程退出
	for i := 0; i < ft.procs; i++ {
		<-ft.exitChan
	}

	log.Warnf("Runner[%v] Sender[%v] has been completely exited", ft.runnerName, ft.Name())

	// persist queue's meta data
	ft.logQueue.Close()
	ft.backupQueue.Close()

	return ft.innerSender.Close()
}

// marshalData 将数据序列化
func (ft *FtSender) marshalData(datas []Data) (bs []byte, err error) {
	ctx := new(datasContext)
	ctx.Datas = datas
	bs, err = json.Marshal(ctx)
	if err != nil {
		err = reqerr.NewSendError("Cannot marshal data :"+err.Error(), ConvertDatasBack(datas), reqerr.TypeDefault)
		return
	}
	return
}

// unmarshalData 如何将数据从磁盘中反序列化出来
func (ft *FtSender) unmarshalData(dat []byte) (datas []Data, err error) {
	ctx := new(datasContext)
	d := json.NewDecoder(bytes.NewReader(dat))
	d.UseNumber()
	err = d.Decode(&ctx)
	if err != nil {
		return
	}
	datas = ctx.Datas
	return
}

func (ft *FtSender) saveToFile(datas []Data) error {
	bs, err := ft.marshalData(datas)
	if err != nil {
		return err
	}
	err = ft.logQueue.Put(bs)
	if err != nil {
		return reqerr.NewSendError(ft.innerSender.Name()+" Cannot put data into backendQueue: "+err.Error(), ConvertDatasBack(datas), reqerr.TypeDefault)
	}
	return nil
}

func (ft *FtSender) asyncSendLogFromDiskQueue() {
	for i := 0; i < ft.procs; i++ {
		go ft.sendFromQueue(ft.logQueue)
	}
	go ft.sendFromQueue(ft.backupQueue)
}

// trySend 从bytes反序列化数据后尝试发送数据
func (ft *FtSender) trySendBytes(dat []byte, failSleep int) (backDataContext []*datasContext, err error) {
	datas, err := ft.unmarshalData(dat)
	if err != nil {
		return
	}
	return ft.trySendDatas(datas, failSleep)
}

func ConvertDatas(ins []map[string]interface{}) []Data {
	var datas []Data
	for _, v := range ins {
		datas = append(datas, Data(v))
	}
	return datas
}
func ConvertDatasBack(ins []Data) []map[string]interface{} {
	var datas []map[string]interface{}
	for _, v := range ins {
		datas = append(datas, map[string]interface{}(v))
	}
	return datas
}

// trySendDatas 尝试发送数据，如果失败，将失败数据加入backup queue，并睡眠指定时间。返回结果为是否正常发送
func (ft *FtSender) trySendDatas(datas []Data, failSleep int) (backDataContext []*datasContext, err error) {
	err = ft.innerSender.Send(datas)
	if c, ok := err.(*utils.StatsError); ok {
		err = c.ErrorDetail
		ft.stats.Errors += c.Errors
		ft.stats.Success += c.Success
	}
	if err != nil {
		retDatasContext := ft.handleSendError(err, datas)
		for _, v := range retDatasContext {
			nnBytes, _ := json.Marshal(v)
			qErr := ft.backupQueue.Put(nnBytes)
			if qErr != nil {
				log.Errorf("Runner[%v] Sender[%v] cannot write points back to queue %v: %v", ft.runnerName, ft.innerSender.Name(), ft.backupQueue.Name(), qErr)
				backDataContext = append(backDataContext, v)
			}
		}
		time.Sleep(time.Second * time.Duration(failSleep))
	}
	return
}

func (ft *FtSender) handleSendError(err error, datas []Data) (retDatasContext []*datasContext) {

	failCtx := new(datasContext)
	var binaryUnpack bool
	se, succ := err.(*reqerr.SendError)
	if !succ {
		// 如果不是SendError 默认所有的数据都发送失败
		log.Infof("Runner[%v] Sender[%v] error type is not *SendError! reSend all datas by default", ft.runnerName, ft.innerSender.Name())
		failCtx.Datas = datas
		ft.stats.LastError = err
	} else {
		failCtx.Datas = ConvertDatas(se.GetFailDatas())
		if se.ErrorType == reqerr.TypeBinaryUnpack {
			binaryUnpack = true
		}
		ft.stats.LastError = errors.New(se.Error())
	}
	log.Errorf("Runner[%v] Sender[%v] cannot write points: %v, failDatas size: %v", ft.runnerName, ft.innerSender.Name(), err, len(failCtx.Datas))
	log.Debugf("Runner[%v] Sender[%v] failed datas [[%v]]", ft.runnerName, ft.innerSender.Name(), failCtx.Datas)
	if binaryUnpack {
		lens := len(failCtx.Datas) / 2
		if lens > 0 {
			newFailCtx := new(datasContext)
			newFailCtx.Datas = failCtx.Datas[0:lens]
			failCtx.Datas = failCtx.Datas[lens:]
			retDatasContext = append(retDatasContext, newFailCtx)
		}
	}
	retDatasContext = append(retDatasContext, failCtx)
	return
}

func (ft *FtSender) sendFromQueue(queue queue.BackendQueue) {
	readChan := queue.ReadChan()
	timer := time.NewTicker(time.Second)
	waitCnt := 1
	var curDataContext, otherDataContext []*datasContext
	var curIdx int
	var backDataContext []*datasContext
	var err error
	for {
		if atomic.LoadInt32(&ft.stopped) > 0 {
			ft.exitChan <- struct{}{}
			return
		}
		if curIdx < len(curDataContext) {
			backDataContext, err = ft.trySendDatas(curDataContext[curIdx].Datas, waitCnt)
			curIdx++
		} else {
			select {
			case dat := <-readChan:
				backDataContext, err = ft.trySendBytes(dat, waitCnt)
			case <-timer.C:
				continue
			}
		}
		if err == nil {
			waitCnt = 1
			//此处的成功发送没有被stats统计
		} else {
			log.Errorf("Runner[%v] Sender[%v] cannot send points from queue %v, error is %v", ft.runnerName, ft.innerSender.Name(), queue.Name(), err)
			//此处的发送错误没有被stats统计
			waitCnt++
			if waitCnt > 10 {
				waitCnt = 10
			}
		}
		if backDataContext != nil {
			otherDataContext = append(otherDataContext, backDataContext...)
		}
		if curIdx == len(curDataContext) {
			curDataContext = otherDataContext
			otherDataContext = make([]*datasContext, 0)
			curIdx = 0
		}
	}
}
<|endoftext|>


<commit_before>package astar

type Cost int
type Node interface{}

type Graph interface {
	Nbs(Node) []Node
	Dist(n1, n2 Node) Cost
	H(n1, n2 Node) Cost
}

func Astar(graph Graph, node0, goal Node) (path []Node) {
	closedset := map[Node]struct{}{}
	parents := map[Node]Node{}
	g := map[Node]Cost{node0: 0}
	f0 := graph.H(node0, goal)
	openq := new(OpenQS)
	openq.Init()
	openq.Add(node0, f0)
	path = nil

	for openq.Len() > 0 {
		x := openq.Pop()
		if x == goal {
			consPath(goal, parents, &path)
			return path
		}

		closedset[x] = struct{}{}

		for _, y := range graph.Nbs(x) {
			if _, ok := closedset[y]; ok {
				continue
			}

			est_g := g[x] + graph.Dist(x, y)

			var updating bool
			var y_queued *qitem

			if y_queued, updating = openq.Item(y); updating {
				if est_g >= g[y] {
					continue
				}
			}

			parents[y] = x
			g[y] = est_g
			fy := graph.H(y, goal) + est_g
			if updating {
				openq.Update(y_queued, fy)
			} else {
				openq.Add(y, fy)
			}
		}
	}
	return
}

func consPath(node Node, parents map[Node]Node, path *[]Node) {
	for {
		parent, ok := parents[node]
		if ok {
			*path = append(*path, node)
			node = parent
		} else {
			break
		}
	}
	reverse(*path)
}

func reverse(path []Node) {
	for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
		path[i], path[j] = path[j], path[i]
	}
}
<commit_msg>cosmetic: rid of var decls, unroll op+if<commit_after>package astar

type Cost int
type Node interface{}

type Graph interface {
	Nbs(Node) []Node
	Dist(n1, n2 Node) Cost
	H(n1, n2 Node) Cost
}

func Astar(graph Graph, node0, goal Node) (path []Node) {
	closedset := map[Node]struct{}{}
	parents := map[Node]Node{}
	g := map[Node]Cost{node0: 0}
	f0 := graph.H(node0, goal)
	openq := new(OpenQS)
	openq.Init()
	openq.Add(node0, f0)
	path = nil

	for openq.Len() > 0 {
		x := openq.Pop()
		if x == goal {
			consPath(goal, parents, &path)
			return path
		}

		closedset[x] = struct{}{}

		for _, y := range graph.Nbs(x) {
			if _, ok := closedset[y]; ok {
				continue
			}

			est_g := g[x] + graph.Dist(x, y)

			y_queued, updating := openq.Item(y)

			if updating {
				if est_g >= g[y] {
					continue
				}
			}

			parents[y] = x
			g[y] = est_g
			fy := graph.H(y, goal) + est_g

			if updating {
				openq.Update(y_queued, fy)
			} else {
				openq.Add(y, fy)
			}
		}
	}
	return
}

func consPath(node Node, parents map[Node]Node, path *[]Node) {
	for {
		parent, ok := parents[node]
		if ok {
			*path = append(*path, node)
			node = parent
		} else {
			break
		}
	}
	reverse(*path)
}

func reverse(path []Node) {
	for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
		path[i], path[j] = path[j], path[i]
	}
}
<|endoftext|>


<commit_before>package scene

import (
	"image"
	"math"

	"github.com/pankona/gomo-simra/simra"
)

type sampleUnit struct {
	*unitBase
	sprite simra.Sprite
}

func (u *sampleUnit) Initialize() {
	simra.GetInstance().AddSprite("player.png",
		image.Rect(0, 0, 384, 384),
		&u.sprite)
}

func (u *sampleUnit) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandSpawn:
		d, ok := c.data.(*sampleUnit)
		if !ok {
			// unhandled event. ignore.
			return
		}
		if u.id != d.GetID() {
			// this spawn event is not for me. nop.
			return
		}
		u.action = newAction(actionSpawn, d)

	default:
		// nop
	}
}

func (u *sampleUnit) DoAction() {
	a := u.action
	if a == nil {
		// idle
		return
	}

	switch a.actiontype {
	case actionSpawn:
		d := a.data.(*sampleUnit)
		u.sprite.W = 64
		u.sprite.H = 64
		u.sprite.X = (float32)(d.position.x)
		u.sprite.Y = (float32)(d.position.y)
		simra.LogDebug("@@@@@@ [SPAWN] i'm %s", u.GetID())

		// start moving to target
		u.action = newAction(actionMoveToNearestTarget, u)

	case actionMoveToNearestTarget:
		// get my position
		ux, uy := u.sprite.X, u.sprite.Y

		// get target (player's) position
		p := u.game.player.(*player)
		px, py := p.sprite.X, p.sprite.Y

		// calculate which way to go
		// move speed is temporary
		moveSpeed := 1
		dx, dy := px-ux, py-uy
		newx := (float64)(moveSpeed) / math.Sqrt((float64)(dx*dx+dy*dy)) * (float64)(dx)
		newy := (float64)(moveSpeed) / math.Sqrt((float64)(dx*dx+dy*dy)) * (float64)(dy)
		u.sprite.X += (float32)(newx)
		u.sprite.Y += (float32)(newy)

	default:
		// nop
	}
}
<commit_msg>declare function for moveToNearestTarget<commit_after>package scene

import (
	"image"
	"math"

	"github.com/pankona/gomo-simra/simra"
)

type sampleUnit struct {
	*unitBase
	sprite simra.Sprite
}

func (u *sampleUnit) Initialize() {
	simra.GetInstance().AddSprite("player.png",
		image.Rect(0, 0, 384, 384),
		&u.sprite)
}

func (u *sampleUnit) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandSpawn:
		d, ok := c.data.(*sampleUnit)
		if !ok {
			// unhandled event. ignore.
			return
		}
		if u.id != d.GetID() {
			// this spawn event is not for me. nop.
			return
		}
		u.action = newAction(actionSpawn, d)

	default:
		// nop
	}
}

func (u *sampleUnit) DoAction() {
	a := u.action
	if a == nil {
		// idle
		return
	}

	switch a.actiontype {
	case actionSpawn:
		d := a.data.(*sampleUnit)
		u.sprite.W = 64
		u.sprite.H = 64
		u.sprite.X = (float32)(d.position.x)
		u.sprite.Y = (float32)(d.position.y)
		simra.LogDebug("@@@@@@ [SPAWN] i'm %s", u.GetID())

		// start moving to target
		u.action = newAction(actionMoveToNearestTarget, u)

	case actionMoveToNearestTarget:
		u.moveToNearestTarget()

	default:
		// nop
	}
}

func (u *sampleUnit) moveToNearestTarget() {
	// get my position
	ux, uy := u.sprite.X, u.sprite.Y

	// get target (player's) position
	p := u.game.player.(*player)
	px, py := p.sprite.X, p.sprite.Y

	// calculate which way to go
	// move speed is temporary
	moveSpeed := 0.3
	dx, dy := px-ux, py-uy
	newx := (float64)(moveSpeed) / math.Sqrt((float64)(dx*dx+dy*dy)) * (float64)(dx)
	newy := (float64)(moveSpeed) / math.Sqrt((float64)(dx*dx+dy*dy)) * (float64)(dy)
	u.sprite.X += (float32)(newx)
	u.sprite.Y += (float32)(newy)
}
<|endoftext|>


<commit_before>package e2e

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/route"
	"github.com/sclevine/agouti"
)

var server *httptest.Server
var client = http.DefaultClient
var db *gorm.DB

func TestMain(m *testing.M) {
	dbURL := model.ReplaceToTestDBURL(os.Getenv("DB_URL"))
	if err := os.Setenv("DB_URL", dbURL); err != nil {
		// TODO: Not use panic
		panic(err)
	}
	bootstrap.CheckServerEnvVars()

	var accessLogBuffer, appLogBuffer bytes.Buffer
	logger.InitializeAccessLogger(&accessLogBuffer)
	logger.InitializeAppLogger(&appLogBuffer)

	var err error
	db, err = model.OpenDB(dbURL, 1, true) // TODO: env
	if err != nil {
		panic(err)
	}
	if err := model.TruncateAllTables(db, model.GetDBName(dbURL)); err != nil {
		panic(err)
	}

	port := config.ListenPort()
	routes := route.Create()
	port += 1
	server = newTestServer(routes, port)
	fmt.Printf("Test HTTP server created: port=%d, url=%s\n", port, server.URL)
	defer server.Close()

	client.Timeout = 5 * time.Second
	os.Chdir("../")
	status := m.Run()
	defer os.Exit(status)
}

// newTestServer returns a new test Server with fixed port number.
func newTestServer(handler http.Handler, port int) *httptest.Server {
	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		if listener, err = net.Listen("tcp6", fmt.Sprintf("[::1]:%d", port)); err != nil {
			panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
		}
	}
	ts := &httptest.Server{
		Listener: listener,
		Config:   &http.Server{Handler: handler},
	}
	ts.Start()
	return ts
}

func newWebDriver() *agouti.WebDriver {
	e2eWebDriver := os.Getenv("E2E_WEB_DRIVER")
	var driver *agouti.WebDriver
	switch strings.ToLower(e2eWebDriver) {
	case "chromedriver":
		driver = agouti.ChromeDriver()
	case "phantomjs":
		driver = agouti.PhantomJS()
	default:
		driver = agouti.PhantomJS()
	}
	driver.HTTPClient = client
	return driver
}
<commit_msg>Fix E2E test failure<commit_after>package e2e

import (
	"bytes"
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/bootstrap"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/route"
	"github.com/sclevine/agouti"
)

var server *httptest.Server
var client = http.DefaultClient
var db *gorm.DB

func TestMain(m *testing.M) {
	bootstrap.CheckCLIEnvVars()
	dbURL := model.ReplaceToTestDBURL(bootstrap.CLIEnvVars.DBURL())
	if err := os.Setenv("MYSQL_DATABASE", "lekcije_test"); err != nil {
		// TODO: Not use panic
		panic(err)
	}
	bootstrap.CheckServerEnvVars()

	var accessLogBuffer, appLogBuffer bytes.Buffer
	logger.InitializeAccessLogger(&accessLogBuffer)
	logger.InitializeAppLogger(&appLogBuffer)

	var err error
	db, err = model.OpenDB(dbURL, 1, true) // TODO: env
	if err != nil {
		panic(err)
	}
	if err := model.TruncateAllTables(db, model.GetDBName(dbURL)); err != nil {
		panic(err)
	}

	port := config.ListenPort()
	routes := route.Create()
	port += 1
	server = newTestServer(routes, port)
	fmt.Printf("Test HTTP server created: port=%d, url=%s\n", port, server.URL)
	defer server.Close()

	client.Timeout = 5 * time.Second
	os.Chdir("../")
	status := m.Run()
	defer os.Exit(status)
}

// newTestServer returns a new test Server with fixed port number.
func newTestServer(handler http.Handler, port int) *httptest.Server {
	listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
	if err != nil {
		if listener, err = net.Listen("tcp6", fmt.Sprintf("[::1]:%d", port)); err != nil {
			panic(fmt.Sprintf("httptest: failed to listen on a port: %v", err))
		}
	}
	ts := &httptest.Server{
		Listener: listener,
		Config:   &http.Server{Handler: handler},
	}
	ts.Start()
	return ts
}

func newWebDriver() *agouti.WebDriver {
	e2eWebDriver := os.Getenv("E2E_WEB_DRIVER")
	var driver *agouti.WebDriver
	switch strings.ToLower(e2eWebDriver) {
	case "chromedriver":
		driver = agouti.ChromeDriver()
	case "phantomjs":
		driver = agouti.PhantomJS()
	default:
		driver = agouti.PhantomJS()
	}
	driver.HTTPClient = client
	return driver
}
<|endoftext|>


<commit_before>// API on Network

package ecs

import (
	"time"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/util"
)

type AllocatePublicIpAddressArgs struct {
	InstanceId string
}

type AllocatePublicIpAddressResponse struct {
	common.Response

	IpAddress string
}

// AllocatePublicIpAddress allocates Public Ip Address
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocatepublicipaddress
func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress string, err error) {
	args := AllocatePublicIpAddressArgs{
		InstanceId: instanceId,
	}
	response := AllocatePublicIpAddressResponse{}
	err = client.Invoke("AllocatePublicIpAddress", &args, &response)
	if err != nil {
		return "", err
	}
	return response.IpAddress, nil
}

type ModifyInstanceNetworkSpec struct {
	InstanceId              string
	InternetMaxBandwidthOut *int
	InternetMaxBandwidthIn  *int
	NetworkChargeType common.InternetChargeType
}

type ModifyInstanceNetworkSpecResponse struct {
	common.Response
}

// ModifyInstanceNetworkSpec modifies instance network spec
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyinstancenetworkspec
func (client *Client) ModifyInstanceNetworkSpec(args *ModifyInstanceNetworkSpec) error {

	response := ModifyInstanceNetworkSpecResponse{}
	return client.Invoke("ModifyInstanceNetworkSpec", args, &response)
}

type AllocateEipAddressArgs struct {
	RegionId           common.Region
	Bandwidth          int
	InternetChargeType common.InternetChargeType
	ISP                string
	ClientToken        string
}

type AllocateEipAddressResponse struct {
	common.Response
	EipAddress   string
	AllocationId string
}

// AllocateEipAddress allocates Eip Address
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocateeipaddress
func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddress string, AllocationId string, err error) {
	if args.Bandwidth == 0 {
		args.Bandwidth = 5
	}
	response := AllocateEipAddressResponse{}
	err = client.Invoke("AllocateEipAddress", args, &response)
	if err != nil {
		return "", "", err
	}
	return response.EipAddress, response.AllocationId, nil
}

type AssociateEipAddressArgs struct {
	AllocationId string
	InstanceId   string
}

type AssociateEipAddressResponse struct {
	common.Response
}

// AssociateEipAddress associates EIP address to VM instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&associateeipaddress
func (client *Client) AssociateEipAddress(allocationId string, instanceId string) error {
	args := AssociateEipAddressArgs{
		AllocationId: allocationId,
		InstanceId:   instanceId,
	}
	response := ModifyInstanceNetworkSpecResponse{}
	return client.Invoke("AssociateEipAddress", &args, &response)
}

// Status of disks
type EipStatus string

const (
	EipStatusAssociating   = EipStatus("Associating")
	EipStatusUnassociating = EipStatus("Unassociating")
	EipStatusInUse         = EipStatus("InUse")
	EipStatusAvailable     = EipStatus("Available")
)

type DescribeEipAddressesArgs struct {
	RegionId     common.Region
	Status       EipStatus //enum Associating | Unassociating | InUse | Available
	EipAddress   string
	AllocationId string
	common.Pagination
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddresssettype
type EipAddressSetType struct {
	RegionId           common.Region
	IpAddress          string
	AllocationId       string
	Status             EipStatus
	InstanceId         string
	InstanceType       string
	Bandwidth          string // Why string
	InternetChargeType common.InternetChargeType
	OperationLocks     OperationLocksType
	AllocationTime     util.ISO6801Time
}

type DescribeEipAddressesResponse struct {
	common.Response
	common.PaginationResult
	EipAddresses struct {
		EipAddress []EipAddressSetType
	}
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&describeeipaddresses
func (client *Client) DescribeEipAddresses(args *DescribeEipAddressesArgs) (eipAddresses []EipAddressSetType, pagination *common.PaginationResult, err error) {
	response, err := client.DescribeEipAddressesWithRaw(args)
	if err == nil {
		return response.EipAddresses.EipAddress, &response.PaginationResult, nil
	}

	return nil, nil, err
}

func (client *Client) DescribeEipAddressesWithRaw(args *DescribeEipAddressesArgs) (response *DescribeEipAddressesResponse, err error) {
	args.Validate()
	response = &DescribeEipAddressesResponse{}

	err = client.Invoke("DescribeEipAddresses", args, response)

	if err == nil {
		return response, nil
	}

	return nil, err
}

type ModifyEipAddressAttributeArgs struct {
	AllocationId string
	Bandwidth    int
}

type ModifyEipAddressAttributeResponse struct {
	common.Response
}

// ModifyEipAddressAttribute Modifies EIP attribute
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyeipaddressattribute
func (client *Client) ModifyEipAddressAttribute(allocationId string, bandwidth int) error {
	args := ModifyEipAddressAttributeArgs{
		AllocationId: allocationId,
		Bandwidth:    bandwidth,
	}
	response := ModifyEipAddressAttributeResponse{}
	return client.Invoke("ModifyEipAddressAttribute", &args, &response)
}

type UnallocateEipAddressArgs struct {
	AllocationId string
	InstanceId   string
}

type UnallocateEipAddressResponse struct {
	common.Response
}

// UnassociateEipAddress unallocates Eip Address from instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&unassociateeipaddress
func (client *Client) UnassociateEipAddress(allocationId string, instanceId string) error {
	args := UnallocateEipAddressArgs{
		AllocationId: allocationId,
		InstanceId:   instanceId,
	}
	response := UnallocateEipAddressResponse{}
	return client.Invoke("UnassociateEipAddress", &args, &response)
}

type ReleaseEipAddressArgs struct {
	AllocationId string
}

type ReleaseEipAddressResponse struct {
	common.Response
}

// ReleaseEipAddress releases Eip address
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&releaseeipaddress
func (client *Client) ReleaseEipAddress(allocationId string) error {
	args := ReleaseEipAddressArgs{
		AllocationId: allocationId,
	}
	response := ReleaseEipAddressResponse{}
	return client.Invoke("ReleaseEipAddress", &args, &response)
}

// WaitForVSwitchAvailable waits for VSwitch to given status
func (client *Client) WaitForEip(regionId common.Region, allocationId string, status EipStatus, timeout int) error {
	if timeout <= 0 {
		timeout = DefaultTimeout
	}
	args := DescribeEipAddressesArgs{
		RegionId:     regionId,
		AllocationId: allocationId,
	}
	for {
		eips, _, err := client.DescribeEipAddresses(&args)
		if err != nil {
			return err
		}
		if len(eips) == 0 {
			return common.GetClientErrorFromString("Not found")
		}
		if eips[0].Status == status {
			break
		}
		timeout = timeout - DefaultWaitForInterval
		if timeout <= 0 {
			return common.GetClientErrorFromString("Timeout")
		}
		time.Sleep(DefaultWaitForInterval * time.Second)
	}
	return nil
}
<commit_msg>Support to associate EIP with SLB and Nat Gateway<commit_after>// API on Network

package ecs

import (
	"time"

	"github.com/denverdino/aliyungo/common"
	"github.com/denverdino/aliyungo/util"
)

type AllocatePublicIpAddressArgs struct {
	InstanceId string
}

type AllocatePublicIpAddressResponse struct {
	common.Response

	IpAddress string
}

// AllocatePublicIpAddress allocates Public Ip Address
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocatepublicipaddress
func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress string, err error) {
	args := AllocatePublicIpAddressArgs{
		InstanceId: instanceId,
	}
	response := AllocatePublicIpAddressResponse{}
	err = client.Invoke("AllocatePublicIpAddress", &args, &response)
	if err != nil {
		return "", err
	}
	return response.IpAddress, nil
}

type ModifyInstanceNetworkSpec struct {
	InstanceId              string
	InternetMaxBandwidthOut *int
	InternetMaxBandwidthIn  *int
	NetworkChargeType common.InternetChargeType
}

type ModifyInstanceNetworkSpecResponse struct {
	common.Response
}

// ModifyInstanceNetworkSpec modifies instance network spec
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyinstancenetworkspec
func (client *Client) ModifyInstanceNetworkSpec(args *ModifyInstanceNetworkSpec) error {

	response := ModifyInstanceNetworkSpecResponse{}
	return client.Invoke("ModifyInstanceNetworkSpec", args, &response)
}

type AllocateEipAddressArgs struct {
	RegionId           common.Region
	Bandwidth          int
	InternetChargeType common.InternetChargeType
	ISP                string
	ClientToken        string
}

type AllocateEipAddressResponse struct {
	common.Response
	EipAddress   string
	AllocationId string
}

// AllocateEipAddress allocates Eip Address
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocateeipaddress
func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddress string, AllocationId string, err error) {
	if args.Bandwidth == 0 {
		args.Bandwidth = 5
	}
	response := AllocateEipAddressResponse{}
	err = client.Invoke("AllocateEipAddress", args, &response)
	if err != nil {
		return "", "", err
	}
	return response.EipAddress, response.AllocationId, nil
}

type EipInstanceType string

const (
	EcsInstance = "EcsInstance"
	SlbInstance = "SlbInstance"
	Nat = "Nat"
	HaVip = "HaVip"
)
type AssociateEipAddressArgs struct {
	AllocationId string
	InstanceId   string
	InstanceType EipInstanceType
}

type AssociateEipAddressResponse struct {
	common.Response
}

// AssociateEipAddress associates EIP address to VM instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&associateeipaddress
func (client *Client) AssociateEipAddress(allocationId string, instanceId string) error {
	args := AssociateEipAddressArgs{
		AllocationId: allocationId,
		InstanceId:   instanceId,
	}
	response := ModifyInstanceNetworkSpecResponse{}
	return client.Invoke("AssociateEipAddress", &args, &response)
}

func (client *Client) NewAssociateEipAddress(args *AssociateEipAddressArgs) error {
	response := ModifyInstanceNetworkSpecResponse{}
	return client.Invoke("AssociateEipAddress", args, &response)
}

// Status of disks
type EipStatus string

const (
	EipStatusAssociating   = EipStatus("Associating")
	EipStatusUnassociating = EipStatus("Unassociating")
	EipStatusInUse         = EipStatus("InUse")
	EipStatusAvailable     = EipStatus("Available")
)

type DescribeEipAddressesArgs struct {
	RegionId     common.Region
	Status       EipStatus //enum Associating | Unassociating | InUse | Available
	EipAddress   string
	AllocationId string
	common.Pagination
}

//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddresssettype
type EipAddressSetType struct {
	RegionId           common.Region
	IpAddress          string
	AllocationId       string
	Status             EipStatus
	InstanceId         string
	InstanceType       string
	Bandwidth          string // Why string
	InternetChargeType common.InternetChargeType
	OperationLocks     OperationLocksType
	AllocationTime     util.ISO6801Time
}

type DescribeEipAddressesResponse struct {
	common.Response
	common.PaginationResult
	EipAddresses struct {
		EipAddress []EipAddressSetType
	}
}

// DescribeInstanceStatus describes instance status
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&describeeipaddresses
func (client *Client) DescribeEipAddresses(args *DescribeEipAddressesArgs) (eipAddresses []EipAddressSetType, pagination *common.PaginationResult, err error) {
	response, err := client.DescribeEipAddressesWithRaw(args)
	if err == nil {
		return response.EipAddresses.EipAddress, &response.PaginationResult, nil
	}

	return nil, nil, err
}

func (client *Client) DescribeEipAddressesWithRaw(args *DescribeEipAddressesArgs) (response *DescribeEipAddressesResponse, err error) {
	args.Validate()
	response = &DescribeEipAddressesResponse{}

	err = client.Invoke("DescribeEipAddresses", args, response)

	if err == nil {
		return response, nil
	}

	return nil, err
}

type ModifyEipAddressAttributeArgs struct {
	AllocationId string
	Bandwidth    int
}

type ModifyEipAddressAttributeResponse struct {
	common.Response
}

// ModifyEipAddressAttribute Modifies EIP attribute
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyeipaddressattribute
func (client *Client) ModifyEipAddressAttribute(allocationId string, bandwidth int) error {
	args := ModifyEipAddressAttributeArgs{
		AllocationId: allocationId,
		Bandwidth:    bandwidth,
	}
	response := ModifyEipAddressAttributeResponse{}
	return client.Invoke("ModifyEipAddressAttribute", &args, &response)
}

type UnallocateEipAddressArgs struct {
	AllocationId string
	InstanceId   string
}

type UnallocateEipAddressResponse struct {
	common.Response
}

// UnassociateEipAddress unallocates Eip Address from instance
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&unassociateeipaddress
func (client *Client) UnassociateEipAddress(allocationId string, instanceId string) error {
	args := UnallocateEipAddressArgs{
		AllocationId: allocationId,
		InstanceId:   instanceId,
	}
	response := UnallocateEipAddressResponse{}
	return client.Invoke("UnassociateEipAddress", &args, &response)
}

type ReleaseEipAddressArgs struct {
	AllocationId string
}

type ReleaseEipAddressResponse struct {
	common.Response
}

// ReleaseEipAddress releases Eip address
//
// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&releaseeipaddress
func (client *Client) ReleaseEipAddress(allocationId string) error {
	args := ReleaseEipAddressArgs{
		AllocationId: allocationId,
	}
	response := ReleaseEipAddressResponse{}
	return client.Invoke("ReleaseEipAddress", &args, &response)
}

// WaitForVSwitchAvailable waits for VSwitch to given status
func (client *Client) WaitForEip(regionId common.Region, allocationId string, status EipStatus, timeout int) error {
	if timeout <= 0 {
		timeout = DefaultTimeout
	}
	args := DescribeEipAddressesArgs{
		RegionId:     regionId,
		AllocationId: allocationId,
	}
	for {
		eips, _, err := client.DescribeEipAddresses(&args)
		if err != nil {
			return err
		}
		if len(eips) == 0 {
			return common.GetClientErrorFromString("Not found")
		}
		if eips[0].Status == status {
			break
		}
		timeout = timeout - DefaultWaitForInterval
		if timeout <= 0 {
			return common.GetClientErrorFromString("Timeout")
		}
		time.Sleep(DefaultWaitForInterval * time.Second)
	}
	return nil
}
<|endoftext|>


<commit_before>package edit

// Command history subsystem.

type historyState struct {
	current int
	prefix  string
	line    string
}

func (h *historyState) jump(i int, line string) {
	h.current = i
	h.line = line
}

func (ed *Editor) appendHistory(line string) {
	if ed.store != nil {
		ed.store.AddCmd(line)
		// TODO(xiaq): Report possible error
	}
}

func (ed *Editor) prevHistory() bool {
	if ed.store != nil {
		i, line, err := ed.store.LastCmd(ed.history.current, ed.history.prefix, true)
		if err == nil {
			ed.history.jump(i, line)
			return true
		}
		// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	}
	return false
}

func (ed *Editor) nextHistory() bool {
	if ed.store != nil {
		// Persistent history
		i, line, err := ed.store.FirstCmd(ed.history.current+1, ed.history.prefix, true)
		if err == nil {
			ed.history.jump(i, line)
			return true
		}
		// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	}

	return false
}

// acceptHistory accepts the currently selected history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.history.line
	ed.dot = len(ed.line)
}
<commit_msg>edit: Insert history in a separate goroutine.<commit_after>package edit

// Command history subsystem.

type historyState struct {
	current int
	prefix  string
	line    string
}

func (h *historyState) jump(i int, line string) {
	h.current = i
	h.line = line
}

func (ed *Editor) appendHistory(line string) {
	if ed.store != nil {
		go func() {
			ed.store.AddCmd(line)
			// TODO(xiaq): Report possible error
		}()
	}
}

func (ed *Editor) prevHistory() bool {
	if ed.store != nil {
		i, line, err := ed.store.LastCmd(ed.history.current, ed.history.prefix, true)
		if err == nil {
			ed.history.jump(i, line)
			return true
		}
		// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	}
	return false
}

func (ed *Editor) nextHistory() bool {
	if ed.store != nil {
		// Persistent history
		i, line, err := ed.store.FirstCmd(ed.history.current+1, ed.history.prefix, true)
		if err == nil {
			ed.history.jump(i, line)
			return true
		}
		// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	}

	return false
}

// acceptHistory accepts the currently selected history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.history.line
	ed.dot = len(ed.line)
}
<|endoftext|>


<commit_before>/**
 * Copyright 2017 Yannick Roffin
 *
 *   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 teleinfo_service

import (
	"bufio"
	"os"
	"sync"
	"syscall"
	"time"

	"github.com/spf13/viper"
	log "github.com/yroffin/jarvis-go-ext/logger"
)

// On linux
// apply: stty 1200 cs7 evenp cstopb -igncr -inlcr -brkint -icrnl -opost -isig -icanon -iexten -F /dev/ttyUSB0
// to configure teleinfo tty
// Cf. https://hallard.me/gestion-de-la-teleinfo-avec-un-raspberry-pi-et-une-carte-arduipi/

// Teleinfo : instance Teleinfo device struct
type TeleinfoService struct {
	Entries map[string]string
}

// Trame EDF
type TeleinfoTrame struct {
	etiquette string // ETIQUETTE (4 à 8 caractères)
	data      string // DATA (1 à 12 caractères)
	hecksum   string // CHECKSUM (caractère de contrôle : Somme de caractère)
}

var instance *TeleinfoService
var once sync.Once
var mutex = &sync.Mutex{}

// GetInstance : singleton instance
func Service() *TeleinfoService {
	once.Do(func() {
		instance = new(TeleinfoService)
		instance.init()
	})
	return instance
}

// declare canal
var canal = make(chan byte, 1024)

// handleReadFile : read file
func handleReadFile(device string) error {

	s, err := os.OpenFile(device, syscall.O_RDONLY, 0666)

	if err != nil {
		log.Default.Error("teleinfo", log.Fields{
			"error": err,
		})
	}

	log.Default.Info("teleinfo", log.Fields{
		"device": device,
	})

	reader := bufio.NewReader(s)

	// Receive reply
	for {
		buf, err := reader.ReadBytes(0x0d)
		if err != nil { // err will equal io.EOF
			// sleep while no bytes
			// to avoid system flood read
			time.Sleep(1000 * time.Millisecond)
		} else {
			// dispatch io
			for i := 0; i < len(buf); i++ {
				canal <- buf[i]
			}
		}
	}

	log.Default.Info("teleinfo", log.Fields{
		"status": "done",
	})

	return nil
}

/**
* phase1: LF (0x0A)
* phase2: ETIQUETTE (4 à 8 caractères)
* phase3: SP (0x20)
* phase4: DATA (1 à 12 caractères)
* phase5: SP (0x20)
* phase6: CHECKSUM (caractère de contrôle : Somme de caractère)
* CR (0x0D)
 */

// submit trame
func (that *TeleinfoService) submit(trame TeleinfoTrame) {
	mutex.Lock()
	that.Entries[trame.etiquette] = trame.data
	mutex.Unlock()
}

// single trame
func (that *TeleinfoService) handleTrame(trame string) {
	var espace int
	var send TeleinfoTrame
	for i := 0; i < len(trame); i++ {
		switch {
		case trame[i] == 0x20:
			espace++
			continue
		default:
			if espace == 0 {
				send.etiquette += string([]byte{trame[i]})
			}
			if espace == 1 {
				send.data += string([]byte{trame[i]})
			}
			continue
		}
	}
	// submit new value
	that.submit(send)
}

// all trames detection
func (that *TeleinfoService) handleTrames(trame string) {
	var send string
	for i := 0; i < len(trame); i++ {
		switch {
		case trame[i] == 0x0A:
			send = ""
			continue
		case trame[i] == 0x0D:
			that.handleTrame(send)
			continue
		default:
			send += string([]byte{trame[i]})
		}
	}
}

// worker to consume file
func worker(that *TeleinfoService) {
	var trame string
	var etx bool

	// wait for ETX 0x003
	for i := 0; etx == false; i++ {
		var x = <-canal
		if x == 0x03 {
			etx = true
		}
	}

	// daemon
	for {
		var x = <-canal
		switch {
		case x == 0x03:
			// wait for ETX 0x003
			that.handleTrames(trame)
			break
		case x == 0x02:
			// wait for STX 0x002
			trame = ""
			break
		case x != 0x02 && x != 0x03:
			// other
			trame += string([]byte{x})
			break
		}
	}
}

// GetEntries load entries
func (that *TeleinfoService) GetEntries(entries map[string]string) map[string]string {
	mutex.Lock()
	for key, value := range that.Entries {
		entries[key] = value
	}
	mutex.Unlock()
	return entries
}

// Get get values
func (that *TeleinfoService) Get(key string) string {
	var value string
	mutex.Lock()
	i, ex := that.Entries[key]
	if ex {
		value = i
	}
	mutex.Unlock()
	return value
}

// init initialize this module
func (that *TeleinfoService) init() {
	// add map
	that.Entries = make(map[string]string)

	// start worker
	go handleReadFile(viper.GetString("jarvis.option.teleinfo.file"))
	go worker(that)

	// log information
	log.Default.Info("teleinfo", log.Fields{
		"teleinfoFile": viper.GetString("jarvis.option.teleinfo.file"),
	})
}
<commit_msg>Correction<commit_after>/**
 * Copyright 2017 Yannick Roffin
 *
 *   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 teleinfo_service

import (
	"bufio"
	"os"
	"sync"
	"syscall"
	"time"

	"github.com/spf13/viper"
	log "github.com/yroffin/jarvis-go-ext/logger"
)

// On linux
// apply: stty 1200 cs7 evenp cstopb -igncr -inlcr -brkint -icrnl -opost -isig -icanon -iexten -F /dev/ttyUSB0
// to configure teleinfo tty
// Cf. https://hallard.me/gestion-de-la-teleinfo-avec-un-raspberry-pi-et-une-carte-arduipi/

// Teleinfo : instance Teleinfo device struct
type TeleinfoService struct {
	Entries map[string]string
}

// Trame EDF
type TeleinfoTrame struct {
	etiquette string // ETIQUETTE (4 à 8 caractères)
	data      string // DATA (1 à 12 caractères)
	hecksum   string // CHECKSUM (caractère de contrôle : Somme de caractère)
}

var instance *TeleinfoService
var once sync.Once
var mutex = &sync.Mutex{}

// GetInstance : singleton instance
func Service() *TeleinfoService {
	once.Do(func() {
		instance = new(TeleinfoService)
		instance.init()
	})
	return instance
}

// declare canal
var canal = make(chan byte, 1024)

// handleReadFile : read file
func handleReadFile(device string) error {

	s, err := os.OpenFile(device, syscall.O_RDONLY, 0666)

	if err != nil {
		log.Default.Error("teleinfo", log.Fields{
			"error": err,
		})
	}

	log.Default.Info("teleinfo", log.Fields{
		"device": device,
	})

	reader := bufio.NewReader(s)

	// Receive reply
	for {
		buf, err := reader.ReadBytes(0x0d)
		if err != nil { // err will equal io.EOF
			// sleep while no bytes
			// to avoid system flood read
			time.Sleep(2000 * time.Millisecond)
			log.Default.Error("teleinfo", log.Fields{
				"ReadBytes": err,
			})
		} else {
			// dispatch io
			log.Default.Info("teleinfo", log.Fields{
				"dispatch": string(buf),
			})
			for i := 0; i < len(buf); i++ {
				canal <- buf[i]
			}
		}
	}

	log.Default.Info("teleinfo", log.Fields{
		"status": "done",
	})

	return nil
}

/**
* phase1: LF (0x0A)
* phase2: ETIQUETTE (4 à 8 caractères)
* phase3: SP (0x20)
* phase4: DATA (1 à 12 caractères)
* phase5: SP (0x20)
* phase6: CHECKSUM (caractère de contrôle : Somme de caractère)
* CR (0x0D)
 */

// submit trame
func (that *TeleinfoService) submit(trame TeleinfoTrame) {
	mutex.Lock()
	that.Entries[trame.etiquette] = trame.data
	mutex.Unlock()
}

// single trame
func (that *TeleinfoService) handleTrame(trame string) {
	var espace int
	var send TeleinfoTrame
	for i := 0; i < len(trame); i++ {
		switch {
		case trame[i] == 0x20:
			espace++
			continue
		default:
			if espace == 0 {
				send.etiquette += string([]byte{trame[i]})
			}
			if espace == 1 {
				send.data += string([]byte{trame[i]})
			}
			continue
		}
	}
	// submit new value
	that.submit(send)
}

// all trames detection
func (that *TeleinfoService) handleTrames(trame string) {
	var send string
	for i := 0; i < len(trame); i++ {
		switch {
		case trame[i] == 0x0A:
			send = ""
			continue
		case trame[i] == 0x0D:
			that.handleTrame(send)
			continue
		default:
			send += string([]byte{trame[i]})
		}
	}
}

// worker to consume file
func worker(that *TeleinfoService) {
	var trame string
	var etx bool

	// wait for ETX 0x003
	for i := 0; etx == false; i++ {
		var x = <-canal
		if x == 0x03 {
			etx = true
		}
	}

	// daemon
	for {
		var x = <-canal
		switch {
		case x == 0x03:
			// wait for ETX 0x003
			that.handleTrames(trame)
			break
		case x == 0x02:
			// wait for STX 0x002
			trame = ""
			break
		case x != 0x02 && x != 0x03:
			// other
			trame += string([]byte{x})
			break
		}
	}
}

// GetEntries load entries
func (that *TeleinfoService) GetEntries(entries map[string]string) map[string]string {
	mutex.Lock()
	for key, value := range that.Entries {
		entries[key] = value
	}
	mutex.Unlock()
	return entries
}

// Get get values
func (that *TeleinfoService) Get(key string) string {
	var value string
	mutex.Lock()
	i, ex := that.Entries[key]
	if ex {
		value = i
	}
	mutex.Unlock()
	return value
}

// init initialize this module
func (that *TeleinfoService) init() {
	// add map
	that.Entries = make(map[string]string)

	// start worker
	go handleReadFile(viper.GetString("jarvis.option.teleinfo.file"))
	go worker(that)

	// log information
	log.Default.Info("teleinfo", log.Fields{
		"teleinfoFile": viper.GetString("jarvis.option.teleinfo.file"),
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 Apcera Inc. All rights reserved.

package graft

import (
	"errors"
	"sync"
)

var mu sync.Mutex
var peers map[string]*Node

func init() {
	peers = make(map[string]*Node)
}

func mockPeerCount() int {
	mu.Lock()
	defer mu.Unlock()
	return len(peers)
}

func mockPeers() []*Node {
	mu.Lock()
	defer mu.Unlock()
	nodes := make([]*Node, 0, len(peers))
	for _, p := range peers {
		nodes = append(nodes, p)
	}
	return nodes
}

func mockRegisterPeer(n *Node) {
	mu.Lock()
	defer mu.Unlock()
	peers[n.id] = n
}

func mockUnregisterPeer(id string) {
	mu.Lock()
	defer mu.Unlock()
	delete(peers, id)
}

// Membership designations for split network simulations.
const (
	NO_MEMBERSHIP = iota
	GRP_A
	GRP_B
)

// Handle a simulation of a split network.
func mockSplitNetwork(grp []*Node) {
	if len(grp) <= 0 {
		return
	}
	mu.Lock()
	defer mu.Unlock()
	// Reset all to other group, GrpB
	for _, p := range peers {
		rpc := p.rpc.(*MockRpcDriver)
		rpc.membership = GRP_B
	}
	// Set passed in nodes to GrpA
	for _, p := range grp {
		rpc := p.rpc.(*MockRpcDriver)
		rpc.membership = GRP_A
	}
}

// Restore network from a split.
func mockRestoreNetwork() {
	mu.Lock()
	defer mu.Unlock()
	for _, p := range peers {
		rpc := p.rpc.(*MockRpcDriver)
		rpc.membership = NO_MEMBERSHIP
	}
}

type MockRpcDriver struct {
	mu   sync.Mutex
	node *Node

	// For testing
	shouldFailInit bool
	closeCalled    bool
	shouldFailComm bool

	membership int
}

func NewMockRpc() *MockRpcDriver {
	return &MockRpcDriver{}
}

func (rpc *MockRpcDriver) Init(n *Node) error {
	if rpc.shouldFailInit {
		return errors.New("RPC Failed to Init")
	}
	// Redo the channels to be buffered since we could be
	// sending and block the select loops.
	cSize := n.ClusterInfo().Size
	n.VoteRequests = make(chan *VoteRequest, cSize)
	n.VoteResponses = make(chan *VoteResponse, cSize)
	n.HeartBeats = make(chan *Heartbeat, cSize)

	mockRegisterPeer(n)
	rpc.node = n
	return nil
}

func (rpc *MockRpcDriver) Close() {
	rpc.closeCalled = true
	if rpc.node != nil {
		mockUnregisterPeer(rpc.node.id)
	}
}

// Test if we can talk to a peer
func (rpc *MockRpcDriver) commAllowed(peer *Node) bool {
	peerRpc := peer.rpc.(*MockRpcDriver)
	// Check to see if we are in same group as peer
	return rpc.membership == peerRpc.membership
}

func (rpc *MockRpcDriver) RequestVote(vr *VoteRequest) error {
	if rpc.isCommBlocked() {
		// Silent failure
		return nil
	}
	for _, p := range mockPeers() {
		if p.id != rpc.node.id && rpc.commAllowed(p) {
			p.VoteRequests <- vr
		}
	}
	return nil
}

func (rpc *MockRpcDriver) HeartBeat(hb *Heartbeat) error {
	if rpc.isCommBlocked() {
		// Silent failure
		return nil
	}

	for _, p := range mockPeers() {
		if p.id != rpc.node.id && rpc.commAllowed(p) {
			p.HeartBeats <- hb
		}
	}
	return nil
}

func (rpc *MockRpcDriver) SendVoteResponse(candidate string, vresp *VoteResponse) error {
	if rpc.isCommBlocked() {
		// Silent failure
		return nil
	}

	mu.Lock()
	p := peers[candidate]
	mu.Unlock()

	if p != nil && p.isRunning() && rpc.commAllowed(p) {
		p.VoteResponses <- vresp
	}
	return nil
}

func (rpc *MockRpcDriver) setCommBlocked(block bool) {
	rpc.mu.Lock()
	defer rpc.mu.Unlock()
	rpc.shouldFailComm = block
}

func (rpc *MockRpcDriver) isCommBlocked() bool {
	rpc.mu.Lock()
	defer rpc.mu.Unlock()
	return rpc.shouldFailComm
}
<commit_msg>Fix infrequent race on membership<commit_after>// Copyright 2013 Apcera Inc. All rights reserved.

package graft

import (
	"errors"
	"sync"
	"sync/atomic"
)

var mu sync.Mutex
var peers map[string]*Node

func init() {
	peers = make(map[string]*Node)
}

func mockPeerCount() int {
	mu.Lock()
	defer mu.Unlock()
	return len(peers)
}

func mockPeers() []*Node {
	mu.Lock()
	defer mu.Unlock()
	nodes := make([]*Node, 0, len(peers))
	for _, p := range peers {
		nodes = append(nodes, p)
	}
	return nodes
}

func mockRegisterPeer(n *Node) {
	mu.Lock()
	defer mu.Unlock()
	peers[n.id] = n
}

func mockUnregisterPeer(id string) {
	mu.Lock()
	defer mu.Unlock()
	delete(peers, id)
}

// Membership designations for split network simulations.
const (
	NO_MEMBERSHIP = int32(iota)
	GRP_A
	GRP_B
)

// Handle a simulation of a split network.
func mockSplitNetwork(grp []*Node) {
	if len(grp) <= 0 {
		return
	}
	mu.Lock()
	defer mu.Unlock()
	// Reset all to other group, GrpB
	for _, p := range peers {
		rpc := p.rpc.(*MockRpcDriver)
		atomic.StoreInt32(&rpc.membership, GRP_B)
	}
	// Set passed in nodes to GrpA
	for _, p := range grp {
		rpc := p.rpc.(*MockRpcDriver)
		atomic.StoreInt32(&rpc.membership, GRP_A)
	}
}

// Restore network from a split.
func mockRestoreNetwork() {
	mu.Lock()
	defer mu.Unlock()
	for _, p := range peers {
		rpc := p.rpc.(*MockRpcDriver)
		atomic.StoreInt32(&rpc.membership, NO_MEMBERSHIP)
	}
}

type MockRpcDriver struct {
	mu   sync.Mutex
	node *Node

	// For testing
	shouldFailInit bool
	closeCalled    bool
	shouldFailComm bool
	membership     int32
}

func NewMockRpc() *MockRpcDriver {
	return &MockRpcDriver{}
}

func (rpc *MockRpcDriver) Init(n *Node) error {
	if rpc.shouldFailInit {
		return errors.New("RPC Failed to Init")
	}
	// Redo the channels to be buffered since we could be
	// sending and block the select loops.
	cSize := n.ClusterInfo().Size
	n.VoteRequests = make(chan *VoteRequest, cSize)
	n.VoteResponses = make(chan *VoteResponse, cSize)
	n.HeartBeats = make(chan *Heartbeat, cSize)

	mockRegisterPeer(n)
	rpc.node = n
	return nil
}

func (rpc *MockRpcDriver) Close() {
	rpc.closeCalled = true
	if rpc.node != nil {
		mockUnregisterPeer(rpc.node.id)
	}
}

// Test if we can talk to a peer
func (rpc *MockRpcDriver) commAllowed(peer *Node) bool {
	// Faked nodes
	if peer == nil || peer.rpc == nil {
		return true
	}
	peerRpc := peer.rpc.(*MockRpcDriver)

	// Check to see if we are in same group as peer
	m1 := atomic.LoadInt32(&rpc.membership)
	m2 := atomic.LoadInt32(&peerRpc.membership)
	return m1 == m2
}

func (rpc *MockRpcDriver) RequestVote(vr *VoteRequest) error {
	if rpc.isCommBlocked() {
		// Silent failure
		return nil
	}
	for _, p := range mockPeers() {
		if p.id != rpc.node.id && rpc.commAllowed(p) {
			p.VoteRequests <- vr
		}
	}
	return nil
}

func (rpc *MockRpcDriver) HeartBeat(hb *Heartbeat) error {
	if rpc.isCommBlocked() {
		// Silent failure
		return nil
	}

	for _, p := range mockPeers() {
		if p.id != rpc.node.id && rpc.commAllowed(p) {
			p.HeartBeats <- hb
		}
	}
	return nil
}

func (rpc *MockRpcDriver) SendVoteResponse(candidate string, vresp *VoteResponse) error {
	if rpc.isCommBlocked() {
		// Silent failure
		return nil
	}

	mu.Lock()
	p := peers[candidate]
	mu.Unlock()

	if p != nil && p.isRunning() && rpc.commAllowed(p) {
		p.VoteResponses <- vresp
	}
	return nil
}

func (rpc *MockRpcDriver) setCommBlocked(block bool) {
	rpc.mu.Lock()
	defer rpc.mu.Unlock()
	rpc.shouldFailComm = block
}

func (rpc *MockRpcDriver) isCommBlocked() bool {
	rpc.mu.Lock()
	defer rpc.mu.Unlock()
	return rpc.shouldFailComm
}
<|endoftext|>


<commit_before>package script

import (
    "fmt"
    "encoding/binary"

    pb "code.google.com/p/goprotobuf/proto"
    "github.com/aarzilli/golua/lua"

    "act/proto"
    "act/postman"
)

const (
    MAX_STATE = 2
)

type StateInPack struct {
    sid         float64
    uid         float64
    pname       string
    data        string
}

type LuaState struct {
    state               *lua.State
    stateInChan         chan *StateInPack
    stateOutChan        chan *proto.GateOutPack
    pm                  *postman.Postman
}

func NewLuaState(out chan *proto.GateOutPack) *LuaState {
    ls := &LuaState{lua.NewState(), make(chan *StateInPack, 10), out, postman.NewPostman()}
    ls.state.OpenLibs()
    ls.state.DoFile("./script/glue.lua")
    RegisterLuaFunction(ls)
    go ls.loop()
    return ls
}

func (this *LuaState) loop() {
    for {
        select {
            case pack := <-this.stateInChan:
                this.invokeLua(pack)
        }
    }
}

func (this *LuaState) invokeLua(pack *StateInPack) {
    fmt.Println("stateInChan")
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Lua Err:", r)
        }
    }()
    this.state.GetGlobal("dispatch")
    this.state.PushNumber(pack.sid)
    this.state.PushNumber(pack.uid)
    this.state.PushString(pack.pname)
    this.state.PushString(pack.data)
    if lerr := this.state.Call(4, 0); lerr != nil {
        this.state.RaiseError("---")
    }
}

type LuaMgr struct {
    hash2state       map[uint32]*LuaState
    recvChan        chan *proto.GateInPack
    sendChan        chan *proto.GateOutPack
}

func NewLuaMgr() *LuaMgr {
    mgr := &LuaMgr{hash2state: make(map[uint32]*LuaState)}
    return mgr
}

func (this *LuaMgr) Start(out chan *proto.GateOutPack, in chan *proto.GateInPack) {
    fmt.Println("LuaMgr running")
    this.sendChan, this.recvChan = out, in
    for {
        select {
            case pack := <-this.recvChan:
                fmt.Println("luamgr recv")
                h := pack.GetHeader()
                uid, sid := h.GetUid(), h.GetSid()
                pname := proto.URI2PROTO[pack.GetUri()].Name()[3:]
                state := this.GetLuaState(sid)
                state.stateInChan <- &StateInPack{float64(sid), float64(uid), pname,
                                        string(pack.GetBin())}
        }
    }
}

func (this *LuaMgr)GetLuaState(sid uint32) *LuaState {
     hash := sid % MAX_STATE
     state, exist := this.hash2state[sid]
     if !exist {
        state = NewLuaState(this.sendChan)
        this.hash2state[hash] = state
     }
     return state
}

func RegisterLuaFunction(LS *LuaState) {

    // arg1: uri        uint32
    // arg2: msg        string
    // arg3: sid        uint32
    // arg4: uids       []uint32
    Lua_SendMsg := func(L *lua.State) int {
        uri := uint32(L.ToInteger(1))
        msg := L.ToString(2)
        sid := uint32(L.ToInteger(3))
        len_uids := int(L.ObjLen(4))
        var uids []uint32
        for i:=1; i<=len_uids; i++ {
            L.RawGeti(4, i)
            uid := L.ToInteger(-1)
            uids = append(uids, uint32(uid))
            //L.Pop(1)
        }
        fmt.Println(len(msg), uids, sid, uri)

        uri_field := make([]byte, 4)
        binary.LittleEndian.PutUint32(uri_field, uri)
        data := []byte(msg)
        data = append(uri_field, data...)

        LS.stateOutChan <- &proto.GateOutPack{Uids: uids, Sid: pb.Uint32(sid), Bin: data}
        return 0
    }

    // arg1: post_string    string
    Lua_Post := func(L *lua.State) int {
        post_string := L.ToString(1)
        ret := LS.pm.Post(post_string)
        L.PushString(ret)
        return 1
    }

    LS.state.Register("GoSendMsg", Lua_SendMsg)
    LS.state.Register("GoPost", Lua_Post)
}

<commit_msg>second update & print lua err<commit_after>package script

import (
    "fmt"
    "time"
    "encoding/binary"

    pb "code.google.com/p/goprotobuf/proto"
    "github.com/aarzilli/golua/lua"

    "act/proto"
    "act/postman"
)

const (
    MAX_STATE = 2
)

type StateInPack struct {
    sid         float64
    uid         float64
    pname       string
    data        string
}

type LuaState struct {
    state               *lua.State
    stateInChan         chan *StateInPack
    stateOutChan        chan *proto.GateOutPack
    pm                  *postman.Postman
}

func NewLuaState(out chan *proto.GateOutPack) *LuaState {
    ls := &LuaState{lua.NewState(), make(chan *StateInPack, 10), out, postman.NewPostman()}
    ls.state.OpenLibs()
    if err := ls.state.DoFile("./script/glue.lua"); err != nil {
        ls.doPrintingErrors(err)
        panic("lua do file err")
    }
    RegisterLuaFunction(ls)
    go ls.loop()
    return ls
}

func (this *LuaState) doPrintingErrors(err error) {
    if err != nil {
        if lerr, ok := err.(*lua.LuaError); ok {
            fmt.Printf("Lua err: %s %d\n", lerr.Error(), len(lerr.StackTrace()))
            for _, e := range lerr.StackTrace() {
                fmt.Printf("\tat %s:%d in %s\n", e.ShortSource, e.CurrentLine, e.Name)
            }
        } else {
            fmt.Printf("Error executing lua code: %s\n", err.Error())
        }
    }
}

func (this *LuaState) loop() {
    ticker := time.Tick(1 * time.Second)
    for {
        select {
            case pack := <-this.stateInChan:
                this.invokeLua(pack)
            case <-ticker:
                this.update()
        }
    }
}

func (this *LuaState) invokeLua(pack *StateInPack) {
    fmt.Println("stateInChan")
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Lua Err:", r)
        }
    }()
    this.state.GetGlobal("dispatch")
    this.state.PushNumber(pack.sid)
    this.state.PushNumber(pack.uid)
    this.state.PushString(pack.pname)
    this.state.PushString(pack.data)
    if err := this.state.Call(4, 0); err != nil {
        this.doPrintingErrors(err)
    }
}

func (this *LuaState) update() {
    this.state.GetGlobal("update")
    if lerr := this.state.Call(0, 0); lerr != nil {
        this.doPrintingErrors(lerr)
    }
}

type LuaMgr struct {
    hash2state       map[uint32]*LuaState
    recvChan        chan *proto.GateInPack
    sendChan        chan *proto.GateOutPack
}

func NewLuaMgr() *LuaMgr {
    mgr := &LuaMgr{hash2state: make(map[uint32]*LuaState)}
    return mgr
}

func (this *LuaMgr) Start(out chan *proto.GateOutPack, in chan *proto.GateInPack) {
    fmt.Println("LuaMgr running")
    this.sendChan, this.recvChan = out, in
    for {
        select {
            case pack := <-this.recvChan:
                fmt.Println("luamgr recv")
                h := pack.GetHeader()
                uid, sid := h.GetUid(), h.GetSid()
                pname := proto.URI2PROTO[pack.GetUri()].Name()[3:]
                state := this.GetLuaState(sid)
                state.stateInChan <- &StateInPack{float64(sid), float64(uid), pname,
                                        string(pack.GetBin())}
        }
    }
}

func (this *LuaMgr)GetLuaState(sid uint32) *LuaState {
     hash := sid % MAX_STATE
     state, exist := this.hash2state[sid]
     if !exist {
        state = NewLuaState(this.sendChan)
        this.hash2state[hash] = state
     }
     return state
}

func RegisterLuaFunction(LS *LuaState) {

    // arg1: uri        uint32
    // arg2: msg        string
    // arg3: sid        uint32
    // arg4: uids       []uint32
    Lua_SendMsg := func(L *lua.State) int {
        uri := uint32(L.ToInteger(1))
        msg := L.ToString(2)
        sid := uint32(L.ToInteger(3))
        len_uids := int(L.ObjLen(4))
        var uids []uint32
        for i:=1; i<=len_uids; i++ {
            L.RawGeti(4, i)
            uid := L.ToInteger(-1)
            uids = append(uids, uint32(uid))
            //L.Pop(1)
        }
        fmt.Println(len(msg), uids, sid, uri)

        uri_field := make([]byte, 4)
        binary.LittleEndian.PutUint32(uri_field, uri)
        data := []byte(msg)
        data = append(uri_field, data...)

        LS.stateOutChan <- &proto.GateOutPack{Uids: uids, Sid: pb.Uint32(sid), Bin: data}
        return 0
    }

    // arg1: post_string    string
    Lua_Post := func(L *lua.State) int {
        post_string := L.ToString(1)
        ret := LS.pm.Post(post_string)
        L.PushString(ret)
        return 1
    }

    LS.state.Register("GoSendMsg", Lua_SendMsg)
    LS.state.Register("GoPost", Lua_Post)
}

<|endoftext|>


<commit_before>package battery

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"os"
	"strconv"

	"github.com/fatih/color"
)

// LENGTH: 8
var chars = []rune{
	'▏',
	'▎',
	'▍',
	'▌',
	'▋',
	'▊',
	'▉',
	'█',
}

type Bar struct {
	buffer      bytes.Buffer
	Gauge       []rune
	GaugeWidth  int
	width       int
	nowVal      int
	totalVal    int
	charLen     int
	format      string
	prefix      rune
	postfix     rune
	charge      string
	ShowPercent bool
	ShowCounter bool
	Showthunder bool
	EnableColor bool
	EnableTmux  bool
}

func digit(num int) string {
	return strconv.Itoa(int(math.Log10(float64(num))) + 1)
}

func New(total int) *Bar {
	if total <= 0 {
		panic(errors.New("Please specify total size that is greater than zero"))
	}
	bar := &Bar{
		buffer:      bytes.Buffer{},
		totalVal:    total,
		nowVal:      -1,
		charLen:     len(chars),
		format:      "%s",
		prefix:      '|',
		postfix:     '|',
		charge:      "⚡",
		ShowPercent: true,
		ShowCounter: true,
		Showthunder: false,
		EnableColor: false,
		EnableTmux:  true,
	}
	return bar.SetWidth(3)
}

func (bar *Bar) SetPrefix(char rune) *Bar {
	bar.prefix = char
	return bar
}

func (bar *Bar) SetPostfix(char rune) *Bar {
	bar.postfix = char
	return bar
}

func (bar *Bar) SetWidth(width int) *Bar {
	bar.width = width
	// +1 for postfix
	bar.GaugeWidth = width + 1
	// +1 for prefix
	bar.Gauge = make([]rune, bar.GaugeWidth+1, bar.GaugeWidth+1)
	return bar
}

func (bar *Bar) Set(set int) *Bar {
	bar.nowVal = set
	return bar
}

func (bar *Bar) Run() {
	bar.writer()
}

func (bar *Bar) writer() {
	if bar.ShowPercent {
		bar.format = "%3d%%" + bar.format
	}

	if bar.ShowCounter {
		digit := digit(bar.totalVal)
		bar.format += " %" + digit + "d/%" + digit + "d"
	}

	// for thunder
	bar.format = "%s" + bar.format

	if bar.nowVal <= bar.totalVal {
		bar.print()
	}

}

func (bar *Bar) print() {
	frac := float64(bar.nowVal) / float64(bar.totalVal)
	barLen, fracBarLen := bar.divmod(frac)

	// append prefix
	bar.Gauge[0] = bar.prefix

	// append full block
	for i := 1; i < barLen; i++ {
		bar.Gauge[i] = chars[bar.charLen-1]
	}

	// append lower block
	bar.Gauge[barLen] = chars[fracBarLen]

	// padding with whitespace
	for i := barLen + 1; i < bar.GaugeWidth; i++ {
		bar.Gauge[i] = ' '
	}

	// append postfix
	bar.Gauge[bar.GaugeWidth] = bar.postfix

	bar.write(frac)
}

func (bar *Bar) write(frac float64) {
	var args []interface{}
	percent := int(frac * 100)

	if bar.Showthunder {
		args = append(args, bar.charge)
	} else {
		args = append(args, " ")
	}

	if bar.ShowPercent {
		args = append(args, percent)
	}

	args = append(args, string(bar.Gauge))

	if bar.ShowCounter {
		args = append(args, bar.nowVal)
		args = append(args, bar.totalVal)
	}

	if bar.EnableColor {
		if bar.EnableTmux {
			bar.colorTmuxPrint(percent, args...)
		} else {
			bar.colorPrint(percent, args...)
		}
	} else {
		fmt.Fprintf(&bar.buffer, bar.format, args...)
	}
	os.Stdout.Write(bar.buffer.Bytes())
}

func (bar *Bar) colorTmuxPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		bar.format = "#[fg=green]" + bar.format
	} else if 20 <= percent && percent < 60 {
		bar.format = "#[fg=yellow]" + bar.format
	} else {
		bar.format = "#[fg=red]" + bar.format
	}
	fmt.Fprintf(&bar.buffer, bar.format+"#[default]", args...)
}

func (bar *Bar) colorPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		fmt.Fprintf(&bar.buffer, color.GreenString(bar.format), args...)
	} else if 20 <= percent && percent < 60 {
		fmt.Fprintf(&bar.buffer, color.YellowString(bar.format), args...)
	} else {
		fmt.Fprintf(&bar.buffer, color.RedString(bar.format), args...)
	}
}

func (bar *Bar) divmod(frac float64) (int, int) {
	// Over 100%
	if frac >= 1.0 {
		return bar.width, bar.charLen - 1
	}
	pre := int(frac * float64(bar.width) * float64(bar.charLen))
	return pre/bar.charLen + 1, pre % bar.charLen
}

func unshift() {

}
<commit_msg>Modified space<commit_after>package battery

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"os"
	"strconv"

	"github.com/fatih/color"
)

// LENGTH: 8
var chars = []rune{
	'▏',
	'▎',
	'▍',
	'▌',
	'▋',
	'▊',
	'▉',
	'█',
}

type Bar struct {
	buffer      bytes.Buffer
	Gauge       []rune
	GaugeWidth  int
	width       int
	nowVal      int
	totalVal    int
	charLen     int
	format      string
	prefix      rune
	postfix     rune
	charge      string
	ShowPercent bool
	ShowCounter bool
	Showthunder bool
	EnableColor bool
	EnableTmux  bool
}

func digit(num int) string {
	return strconv.Itoa(int(math.Log10(float64(num))) + 1)
}

func New(total int) *Bar {
	if total <= 0 {
		panic(errors.New("Please specify total size that is greater than zero"))
	}
	bar := &Bar{
		buffer:      bytes.Buffer{},
		totalVal:    total,
		nowVal:      -1,
		charLen:     len(chars),
		format:      "%s",
		prefix:      '|',
		postfix:     '|',
		charge:      "⚡",
		ShowPercent: true,
		ShowCounter: true,
		Showthunder: false,
		EnableColor: false,
		EnableTmux:  true,
	}
	return bar.SetWidth(3)
}

func (bar *Bar) SetPrefix(char rune) *Bar {
	bar.prefix = char
	return bar
}

func (bar *Bar) SetPostfix(char rune) *Bar {
	bar.postfix = char
	return bar
}

func (bar *Bar) SetWidth(width int) *Bar {
	bar.width = width
	// +1 for postfix
	bar.GaugeWidth = width + 1
	// +1 for prefix
	bar.Gauge = make([]rune, bar.GaugeWidth+1, bar.GaugeWidth+1)
	return bar
}

func (bar *Bar) Set(set int) *Bar {
	bar.nowVal = set
	return bar
}

func (bar *Bar) Run() {
	bar.writer()
}

func (bar *Bar) writer() {
	if bar.ShowPercent {
		bar.format = "%3d%%" + bar.format
	}

	if bar.ShowCounter {
		digit := digit(bar.totalVal)
		bar.format += " %" + digit + "d/%" + digit + "d"
	}

	// for thunder
	bar.format = "%s" + bar.format

	if bar.nowVal <= bar.totalVal {
		bar.print()
	}

}

func (bar *Bar) print() {
	frac := float64(bar.nowVal) / float64(bar.totalVal)
	barLen, fracBarLen := bar.divmod(frac)

	// append prefix
	bar.Gauge[0] = bar.prefix

	// append full block
	for i := 1; i < barLen; i++ {
		bar.Gauge[i] = chars[bar.charLen-1]
	}

	// append lower block
	bar.Gauge[barLen] = chars[fracBarLen]

	// padding with whitespace
	for i := barLen + 1; i < bar.GaugeWidth; i++ {
		bar.Gauge[i] = ' '
	}

	// append postfix
	bar.Gauge[bar.GaugeWidth] = bar.postfix

	bar.write(frac)
}

func (bar *Bar) write(frac float64) {
	var args []interface{}
	percent := int(frac * 100)

	if bar.Showthunder {
		args = append(args, bar.charge)
	} else {
		args = append(args, "　")
	}

	if bar.ShowPercent {
		args = append(args, percent)
	}

	args = append(args, string(bar.Gauge))

	if bar.ShowCounter {
		args = append(args, bar.nowVal)
		args = append(args, bar.totalVal)
	}

	if bar.EnableColor {
		if bar.EnableTmux {
			bar.colorTmuxPrint(percent, args...)
		} else {
			bar.colorPrint(percent, args...)
		}
	} else {
		fmt.Fprintf(&bar.buffer, bar.format, args...)
	}
	os.Stdout.Write(bar.buffer.Bytes())
}

func (bar *Bar) colorTmuxPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		bar.format = "#[fg=green]" + bar.format
	} else if 20 <= percent && percent < 60 {
		bar.format = "#[fg=yellow]" + bar.format
	} else {
		bar.format = "#[fg=red]" + bar.format
	}
	fmt.Fprintf(&bar.buffer, bar.format+"#[default]", args...)
}

func (bar *Bar) colorPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		fmt.Fprintf(&bar.buffer, color.GreenString(bar.format), args...)
	} else if 20 <= percent && percent < 60 {
		fmt.Fprintf(&bar.buffer, color.YellowString(bar.format), args...)
	} else {
		fmt.Fprintf(&bar.buffer, color.RedString(bar.format), args...)
	}
}

func (bar *Bar) divmod(frac float64) (int, int) {
	// Over 100%
	if frac >= 1.0 {
		return bar.width, bar.charLen - 1
	}
	pre := int(frac * float64(bar.width) * float64(bar.charLen))
	return pre/bar.charLen + 1, pre % bar.charLen
}

func unshift() {

}
<|endoftext|>


<commit_before>package token

import "strconv"

// Type is the type of a token
type Type int

const (
	// ILLEGAL is a type of token that is not specified in language spec (should not exist)
	ILLEGAL Type = iota
	EOF
	COMMENT

	LITERAL
	IDENTIFIER

	// consts
	INT_LITERAL
	FLOAT_LITERAL
	CHAR_LITERAL
	BOOLEAN_LITERAL
	STRING_LITERAL

	operatorStart
	// operators
	ADD
	SUB
	MUL
	DIV
	MOD
	POW

	LAND
	LOR
	LNOT

	ASSIGN
	ASSIGN_ADD
	ASSIGN_SUB
	ASSIGN_MUL
	ASSIGN_DIV
	ASSIGN_MOD
	ASSIGN_POW

	EQUAL
	NOT_EQUAL // not equal
	LESS
	LEQ // less or equal
	GREATER
	GEQ              // greater or equal
	TRIPLE_EQUAL     // deep equal
	TRIPLE_NOT_EQUAL // deep not equal

	LEFT_PAREN
	RIGHT_PAREN

	LEFT_CURLY_BRACE
	RIGHT_CURLY_BRACE

	QUESTION_MARK

	SEMI // SEMI: semi-colon (;)
	COLON
	COMMA
	operatorEnd

	keywordStart
	// keywords
	LET
	CONST

	IF
	ELSE

	WHILE
	FOR
	IN
	BREAK

	SWITCH
	CASE
	DEFAULT

	INT_KEYWORD
	FLOAT_KEYWORD
	CHAR_KEYWORD
	BOOL_KEYWORD
	STRING_KEYWORD

	TRUE
	FALSE

	PRINT
	keywordEnd
)

var tokens = [...]string{
	ILLEGAL: "ILLEGAL",
	EOF:     "EOF",
	COMMENT: "COMMENT",

	LITERAL:         "LITERAL",
	IDENTIFIER:      "IDENTIFIER",
	INT_LITERAL:     "INTLITERAL",
	FLOAT_LITERAL:   "FLOATLITERAL",
	CHAR_LITERAL:    "CHARLITERAL",
	BOOLEAN_LITERAL: "BOOLEANLITERAL",
	STRING_LITERAL:  "STRINGLITERAL",

	ADD: "+",
	SUB: "-",
	MUL: "*",
	DIV: "/",
	MOD: "%",
	POW: "^^",

	LAND: "&&", // logic and
	LOR:  "||", // logic or
	LNOT: "!",  // logic not

	ASSIGN:     "=",
	ASSIGN_ADD: "+=",
	ASSIGN_SUB: "-=",
	ASSIGN_MUL: "*=",
	ASSIGN_DIV: "/=",
	ASSIGN_MOD: "%=",
	ASSIGN_POW: "^^=",

	EQUAL:            "==",
	NOT_EQUAL:        "!=",
	LESS:             "<",
	LEQ:              "<=",
	GREATER:          ">",
	GEQ:              ">=",
	TRIPLE_EQUAL:     "===",
	TRIPLE_NOT_EQUAL: "!==",

	LEFT_PAREN:  "(",
	RIGHT_PAREN: ")",

	LEFT_CURLY_BRACE:  "{",
	RIGHT_CURLY_BRACE: "}",

	QUESTION_MARK: "?",

	SEMI:  ";",
	COLON: ":",
	COMMA: ",",

	LET:   "let",
	CONST: "const",

	IF:   "if",
	ELSE: "else",

	WHILE: "while",
	FOR:   "for",
	IN:    "in",
	BREAK: "break",

	SWITCH:  "switch",
	CASE:    "case",
	DEFAULT: "default",

	INT_KEYWORD:    "int",
	FLOAT_KEYWORD:  "float",
	CHAR_KEYWORD:   "char",
	BOOL_KEYWORD:   "bool",
	STRING_KEYWORD: "string",

	TRUE:  "true",
	FALSE: "false",

	PRINT: "print",
}

func (tokenType Type) String() string {
	if tokenType >= 0 && int(tokenType) < len(tokens) {
		return tokens[tokenType]
	}

	return "token(" + strconv.Itoa(int(tokenType)) + ")"
}

var keywords map[string]Type
var operators map[string]Type

func init() {
	// init keywords mapping
	keywords = make(map[string]Type)
	for i := keywordStart + 1; i < keywordEnd; i++ {
		keywords[tokens[i]] = i
	}

	operators = make(map[string]Type)
	for i := operatorStart + 1; i < operatorEnd; i++ {
		operators[tokens[i]] = i
	}
}

func GetKeywordsMapping() map[string]Type {
	return keywords
}

func GetOperatorsMapping() map[string]Type {
	return operators
}
<commit_msg>token type for increment/decrement<commit_after>package token

import "strconv"

// Type is the type of a token
type Type int

const (
	// ILLEGAL is a type of token that is not specified in language spec (should not exist)
	ILLEGAL Type = iota
	EOF
	COMMENT

	LITERAL
	IDENTIFIER

	// consts
	INT_LITERAL
	FLOAT_LITERAL
	CHAR_LITERAL
	BOOLEAN_LITERAL
	STRING_LITERAL

	operatorStart
	// operators
	ADD
	SUB
	MUL
	DIV
	MOD
	POW

	INCREMENT
	DECREMENT

	LAND
	LOR
	LNOT

	ASSIGN
	ASSIGN_ADD
	ASSIGN_SUB
	ASSIGN_MUL
	ASSIGN_DIV
	ASSIGN_MOD
	ASSIGN_POW

	EQUAL
	NOT_EQUAL // not equal
	LESS
	LEQ // less or equal
	GREATER
	GEQ              // greater or equal
	TRIPLE_EQUAL     // deep equal
	TRIPLE_NOT_EQUAL // deep not equal

	LEFT_PAREN
	RIGHT_PAREN

	LEFT_CURLY_BRACE
	RIGHT_CURLY_BRACE

	QUESTION_MARK

	SEMI // SEMI: semi-colon (;)
	COLON
	COMMA
	operatorEnd

	keywordStart
	// keywords
	LET
	CONST

	IF
	ELSE

	WHILE
	FOR
	IN
	BREAK

	SWITCH
	CASE
	DEFAULT

	INT_KEYWORD
	FLOAT_KEYWORD
	CHAR_KEYWORD
	BOOL_KEYWORD
	STRING_KEYWORD

	TRUE
	FALSE

	PRINT
	keywordEnd
)

var tokens = [...]string{
	ILLEGAL: "ILLEGAL",
	EOF:     "EOF",
	COMMENT: "COMMENT",

	LITERAL:         "LITERAL",
	IDENTIFIER:      "IDENTIFIER",
	INT_LITERAL:     "INTLITERAL",
	FLOAT_LITERAL:   "FLOATLITERAL",
	CHAR_LITERAL:    "CHARLITERAL",
	BOOLEAN_LITERAL: "BOOLEANLITERAL",
	STRING_LITERAL:  "STRINGLITERAL",

	ADD: "+",
	SUB: "-",
	MUL: "*",
	DIV: "/",
	MOD: "%",
	POW: "^^",

	INCREMENT: "++",
	DECREMENT: "--",

	LAND: "&&", // logic and
	LOR:  "||", // logic or
	LNOT: "!",  // logic not

	ASSIGN:     "=",
	ASSIGN_ADD: "+=",
	ASSIGN_SUB: "-=",
	ASSIGN_MUL: "*=",
	ASSIGN_DIV: "/=",
	ASSIGN_MOD: "%=",
	ASSIGN_POW: "^^=",

	EQUAL:            "==",
	NOT_EQUAL:        "!=",
	LESS:             "<",
	LEQ:              "<=",
	GREATER:          ">",
	GEQ:              ">=",
	TRIPLE_EQUAL:     "===",
	TRIPLE_NOT_EQUAL: "!==",

	LEFT_PAREN:  "(",
	RIGHT_PAREN: ")",

	LEFT_CURLY_BRACE:  "{",
	RIGHT_CURLY_BRACE: "}",

	QUESTION_MARK: "?",

	SEMI:  ";",
	COLON: ":",
	COMMA: ",",

	LET:   "let",
	CONST: "const",

	IF:   "if",
	ELSE: "else",

	WHILE: "while",
	FOR:   "for",
	IN:    "in",
	BREAK: "break",

	SWITCH:  "switch",
	CASE:    "case",
	DEFAULT: "default",

	INT_KEYWORD:    "int",
	FLOAT_KEYWORD:  "float",
	CHAR_KEYWORD:   "char",
	BOOL_KEYWORD:   "bool",
	STRING_KEYWORD: "string",

	TRUE:  "true",
	FALSE: "false",

	PRINT: "print",
}

func (tokenType Type) String() string {
	if tokenType >= 0 && int(tokenType) < len(tokens) {
		return tokens[tokenType]
	}

	return "token(" + strconv.Itoa(int(tokenType)) + ")"
}

var keywords map[string]Type
var operators map[string]Type

func init() {
	// init keywords mapping
	keywords = make(map[string]Type)
	for i := keywordStart + 1; i < keywordEnd; i++ {
		keywords[tokens[i]] = i
	}

	operators = make(map[string]Type)
	for i := operatorStart + 1; i < operatorEnd; i++ {
		operators[tokens[i]] = i
	}
}

func GetKeywordsMapping() map[string]Type {
	return keywords
}

func GetOperatorsMapping() map[string]Type {
	return operators
}
<|endoftext|>