<commit_before>/*
Package promise provides a complete promise and future implementation.
A quick start sample:

fu := Start(func()(resp interface{}, err error){
    resp, err := http.Get("http://example.com/")
    return
})
//do somthing...
resp, err := fu.Get()
*/
package promise

import (
	"errors"
	"fmt"
	"sync/atomic"
	"time"
	"unsafe"
)

type callbackType int

const (
	CALLBACK_DONE callbackType = iota
	CALLBACK_FAIL
	CALLBACK_ALWAYS
	CALLBACK_CANCEL
)

//pipe presents a promise that will be chain call
type pipe struct {
	pipeDoneTask, pipeFailTask func(v interface{}) *Future
	pipePromise                *Promise
}

//getPipe returns piped Future task function and pipe Promise by the status of current Promise.
func (this *pipe) getPipe(isResolved bool) (func(v interface{}) *Future, *Promise) {
	if isResolved {
		return this.pipeDoneTask, this.pipePromise
	} else {
		return this.pipeFailTask, this.pipePromise
	}
}

//Canceller is used to check if the Promise be requested to cancel and cancel the Promise
//It usually be passed to the real act function for letting act function can cancel the execution.
type Canceller interface {
	IsCancelled() bool
	Cancel()
}

//canceller provides an implement of Canceller interface.
//It will be passed to Future task function as paramter of function
type canceller struct {
	f *Future
}

//RequestCancel is obsolete. It is replaced by Cancel() method.
//It don't mean the promise be surely cancelled.
//If Future task detects Cancelled status, the execution can be stopped.
func (this *canceller) RequestCancel() {
	this.Cancel()
}

//Cancel sets Future task to CANCELLED status
func (this *canceller) Cancel() {
	this.f.Cancel()
}

//IsCancelled returns true if Future task is cancelld, otherwise false.
func (this *canceller) IsCancelled() (r bool) {
	return atomic.LoadInt32(&this.f.cancelStatus) == 2
}

//futureVal stores the internal state of Future.
type futureVal struct {
	dones, fails, always []func(v interface{})
	cancels              []func()
	pipes                []*pipe
	r                    *PromiseResult
}

//Future provides a read-only view of promise,
//the value is set by using Resolve, Reject and Cancel methods of related Promise
type Future struct {
	Id    int //Id can be used as identity of Future
	chOut chan *PromiseResult
	chEnd chan struct{}
	//val point to futureVal that stores status of future
	//if need to change the status of future, must copy a new futureVal and modify it,
	//then use CAS to put the pointer of new futureVal
	val          unsafe.Pointer
	cancelStatus int32
}

//Canceller returns a canceller related to future.
//If Canceller return nil, the futrue cannot be cancelled.
func (this *Future) Canceller() Canceller {
	ccstatus := atomic.LoadInt32(&this.cancelStatus)
	if ccstatus >= 0 {
		return &canceller{this}
	} else {
		return nil
	}
}

//RequestCancel is obsolete. It is replaced by Cancel() method.
func (this *Future) RequestCancel() (e error) {
	return this.Cancel()
}

//IsCancelled returns true if the promise is cancelled, otherwise false
func (this *Future) IsCancelled() bool {
	ccstatus := atomic.LoadInt32(&this.cancelStatus)
	return ccstatus == 2
}

//Cancel sets the status of promise to RESULT_CANCELLED.
//If promise is cancelled, Get() will return nil and CANCELLED error.
//All callback functions will be not called if Promise is cancalled.
func (this *Future) SetTimeout(mm int) *Future {
	if mm == 0 {
		mm = 10
	} else {
		mm = mm * 1000 * 1000
	}

	go func() {
		<-time.After((time.Duration)(mm) * time.Nanosecond)
		this.Cancel()
	}()
	return this
}

//GetChan returns a channel than can be used to receive result of Promise
func (this *Future) GetChan() chan *PromiseResult {
	return this.chOut
}

//Get will block current goroutines until the Future is resolved/rejected/cancelled.
//If Future is resolved, value and nil will be returned
//If Future is rejected, nil and error will be returned.
//If Future is cancelled, nil and CANCELLED error will be returned.
func (this *Future) Get() (val interface{}, err error) {
	<-this.chEnd
	return getFutureReturnVal(this.loadResult())
}

//GetOrTimeout is similar to Get(), but GetOrTimeout will not block after timeout.
//If GetOrTimeout returns with a timeout, timeout value will be true in return values.
//The unit of paramter is millisecond.
func (this *Future) GetOrTimeout(mm uint) (val interface{}, err error, timout bool) {
	if mm == 0 {
		mm = 10
	} else {
		mm = mm * 1000 * 1000
	}

	select {
	case <-time.After((time.Duration)(mm) * time.Nanosecond):
		return nil, nil, true
	case <-this.chEnd:
		r, err := getFutureReturnVal(this.loadResult())
		return r, err, false
	}
}

//Cancel sets the status of promise to RESULT_CANCELLED.
//If promise is cancelled, Get() will return nil and CANCELLED error.
//All callback functions will be not called if Promise is cancalled.
func (this *Future) Cancel() (e error) {
	atomic.StoreInt32(&this.cancelStatus, 2)
	return this.setResult(&PromiseResult{CANCELLED, RESULT_CANCELLED})
}

//OnSuccess registers a callback function that will be called when Promise is resolved.
//If promise is already resolved, the callback will immediately called.
//The value of Promise will be paramter of Done callback function.
func (this *Future) OnSuccess(callback func(v interface{})) *Future {
	this.addCallback(callback, CALLBACK_DONE)
	return this
}

//OnFailure registers a callback function that will be called when Promise is rejected.
//If promise is already rejected, the callback will immediately called.
//The error of Promise will be paramter of Fail callback function.
func (this *Future) OnFailure(callback func(v interface{})) *Future {
	this.addCallback(callback, CALLBACK_FAIL)
	return this
}

//OnComplete register a callback function that will be called when Promise is rejected or resolved.
//If promise is already rejected or resolved, the callback will immediately called.
//According to the status of Promise, value or error will be paramter of Always callback function.
//Value is the paramter if Promise is resolved, or error is the paramter if Promise is rejected.
//Always callback will be not called if Promise be called.
func (this *Future) OnComplete(callback func(v interface{})) *Future {
	this.addCallback(callback, CALLBACK_ALWAYS)
	return this
}

//OnCancel registers a callback function that will be called when Promise is cancelled.
//If promise is already cancelled, the callback will immediately called.
func (this *Future) OnCancel(callback func()) *Future {
	this.addCallback(callback, CALLBACK_CANCEL)
	return this
}

//Pipe registers one or two functions that returns a Future, and returns a proxy of pipeline Future.
//First function will be called when Future is resolved, the returned Future will be as pipeline Future.
//Secondary function will be called when Futrue is rejected, the returned Future will be as pipeline Future.
func (this *Future) Pipe(callbacks ...interface{}) (result *Future, ok bool) {
	if len(callbacks) == 0 ||
		(len(callbacks) == 1 && callbacks[0] == nil) ||
		(len(callbacks) > 1 && callbacks[0] == nil && callbacks[1] == nil) {
		result = this
		return
	}

	//ensure all callback functions match the spec "func(v interface{}) *Future"
	cs := make([]func(v interface{}) *Future, len(callbacks), len(callbacks))
	for i, callback := range callbacks {
		if c, ok1 := callback.(func(v interface{}) *Future); ok1 {
			cs[i] = c
		} else if c, ok1 := callback.(func() *Future); ok1 {
			cs[i] = func(v interface{}) *Future {
				return c()
			}
		} else if c, ok1 := callback.(func(v interface{})); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() {
					c(v)
				})
			}
		} else if c, ok1 := callback.(func(v interface{}) (r interface{}, err error)); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() (r interface{}, err error) {
					r, err = c(v)
					return
				})
			}
		} else if c, ok1 := callback.(func()); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() {
					c()
				})
			}
		} else if c, ok1 := callback.(func() (r interface{}, err error)); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() (r interface{}, err error) {
					r, err = c()
					return
				})
			}
		} else {
			ok = false
			return
		}
	}

	for {
		v := this.loadVal()
		r := v.r
		if r != nil {
			result = this
			if r.Typ == RESULT_SUCCESS && cs[0] != nil {
				result = (cs[0](r.Result))
			} else if r.Typ == RESULT_FAILURE && len(cs) > 1 && cs[1] != nil {
				result = (cs[1](r.Result))
			}
		} else {
			newPipe := &pipe{}
			newPipe.pipeDoneTask = cs[0]
			if len(cs) > 1 {
				newPipe.pipeFailTask = cs[1]
			}
			newPipe.pipePromise = NewPromise()

			newVal := *v
			newVal.pipes = append(newVal.pipes, newPipe)

			//use CAS to ensure that the state of Future is not changed,
			//if the state is changed, will retry CAS operation.
			if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) {
				result = newPipe.pipePromise.Future
				break
			}
		}
	}
	ok = true

	return
}

//result uses Atomic load to return result of the Future
func (this *Future) loadResult() *PromiseResult {
	val := this.loadVal()
	return val.r
}

//val uses Atomic load to return state value of the Future
func (this *Future) loadVal() *futureVal {
	r := atomic.LoadPointer(&this.val)
	return (*futureVal)(r)
}

//setResult sets the value and final status of Promise, it will only be executed for once
func (this *Future) setResult(r *PromiseResult) (e error) { //r *PromiseResult) {
	defer func() {
		if err := getError(recover()); err != nil {
			e = err
			fmt.Println("\nerror in setResult():", err)
		}
	}()

	e = errors.New("Cannot resolve/reject/cancel more than once")
	v := this.loadVal()
	if v.r != nil {
		return
	}

	for {
		newVal := *v
		newVal.r = r

		//Use CAS operation to ensure that the state of Promise isn't changed.
		//If the state is changed, must get latest state and try to call CAS again.
		//No ABA issue in this case because address of all objects are different.
		if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) {
			//chOut will be returned in GetChan(), so send the result to chOut
			this.chOut <- r

			//Close chEnd then all Get() and GetOrTimeout() will be unblocked
			close(this.chEnd)

			//call callback functions and start the Promise pipeline
			if len(v.dones) > 0 || len(v.fails) > 0 || len(v.always) > 0 || len(v.cancels) > 0 {
				go func() {
					execCallback(r, v.dones, v.fails, v.always, v.cancels)
				}()
			}

			//start the pipeline
			if len(v.pipes) > 0 {
				go func() {
					for _, pipe := range v.pipes {
						pipeTask, pipePromise := pipe.getPipe(r.Typ == RESULT_SUCCESS)
						startPipe(r, pipeTask, pipePromise)
					}
				}()
			}
			e = nil
			break
		}
		v = this.loadVal()
	}
	return
}

//handleOneCallback registers a callback function
func (this *Future) addCallback(callback interface{}, t callbackType) {
	if callback == nil {
		return
	}
	if (t == CALLBACK_DONE) ||
		(t == CALLBACK_FAIL) ||
		(t == CALLBACK_ALWAYS) {
		if _, ok := callback.(func(v interface{})); !ok {
			panic(errors.New("Callback function spec must be func(v interface{})"))
		}
	} else if t == CALLBACK_CANCEL {
		if _, ok := callback.(func()); !ok {
			panic(errors.New("Callback function spec must be func()"))
		}
	}

	for {
		v := this.loadVal()
		r := v.r
		if r == nil {
			newVal := *v
			switch t {
			case CALLBACK_DONE:
				newVal.dones = append(newVal.dones, callback.(func(v interface{})))
			case CALLBACK_FAIL:
				newVal.fails = append(newVal.fails, callback.(func(v interface{})))
			case CALLBACK_ALWAYS:
				newVal.always = append(newVal.always, callback.(func(v interface{})))
			case CALLBACK_CANCEL:
				newVal.cancels = append(newVal.cancels, callback.(func()))
			}

			//use CAS to ensure that the state of Future is not changed,
			//if the state is changed, will retry CAS operation.
			if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) {
				break
			}
		} else {
			if (t == CALLBACK_DONE && r.Typ == RESULT_SUCCESS) ||
				(t == CALLBACK_FAIL && r.Typ == RESULT_FAILURE) ||
				(t == CALLBACK_ALWAYS && r.Typ != RESULT_CANCELLED) {
				callbackFunc := callback.(func(v interface{}))
				callbackFunc(r.Result)
			} else if t == CALLBACK_CANCEL && r.Typ == RESULT_CANCELLED {
				callbackFunc := callback.(func())
				callbackFunc()
			}
			break
		}
	}
}
<commit_msg>fix a issue that miss the detection for checking if future is completed or cancelled in CAS loop<commit_after>/*
Package promise provides a complete promise and future implementation.
A quick start sample:

fu := Start(func()(resp interface{}, err error){
    resp, err := http.Get("http://example.com/")
    return
})
//do somthing...
resp, err := fu.Get()
*/
package promise

import (
	"errors"
	"fmt"
	"sync/atomic"
	"time"
	"unsafe"
)

type callbackType int

const (
	CALLBACK_DONE callbackType = iota
	CALLBACK_FAIL
	CALLBACK_ALWAYS
	CALLBACK_CANCEL
)

//pipe presents a promise that will be chain call
type pipe struct {
	pipeDoneTask, pipeFailTask func(v interface{}) *Future
	pipePromise                *Promise
}

//getPipe returns piped Future task function and pipe Promise by the status of current Promise.
func (this *pipe) getPipe(isResolved bool) (func(v interface{}) *Future, *Promise) {
	if isResolved {
		return this.pipeDoneTask, this.pipePromise
	} else {
		return this.pipeFailTask, this.pipePromise
	}
}

//Canceller is used to check if the Promise be requested to cancel and cancel the Promise
//It usually be passed to the real act function for letting act function can cancel the execution.
type Canceller interface {
	IsCancelled() bool
	Cancel()
}

//canceller provides an implement of Canceller interface.
//It will be passed to Future task function as paramter of function
type canceller struct {
	f *Future
}

//RequestCancel is obsolete. It is replaced by Cancel() method.
//It don't mean the promise be surely cancelled.
//If Future task detects Cancelled status, the execution can be stopped.
func (this *canceller) RequestCancel() {
	this.Cancel()
}

//Cancel sets Future task to CANCELLED status
func (this *canceller) Cancel() {
	this.f.Cancel()
}

//IsCancelled returns true if Future task is cancelld, otherwise false.
func (this *canceller) IsCancelled() (r bool) {
	return atomic.LoadInt32(&this.f.cancelStatus) == 2
}

//futureVal stores the internal state of Future.
type futureVal struct {
	dones, fails, always []func(v interface{})
	cancels              []func()
	pipes                []*pipe
	r                    *PromiseResult
}

//Future provides a read-only view of promise,
//the value is set by using Resolve, Reject and Cancel methods of related Promise
type Future struct {
	Id    int //Id can be used as identity of Future
	chOut chan *PromiseResult
	chEnd chan struct{}
	//val point to futureVal that stores status of future
	//if need to change the status of future, must copy a new futureVal and modify it,
	//then use CAS to put the pointer of new futureVal
	val          unsafe.Pointer
	cancelStatus int32
}

//Canceller returns a canceller related to future.
//If Canceller return nil, the futrue cannot be cancelled.
func (this *Future) Canceller() Canceller {
	ccstatus := atomic.LoadInt32(&this.cancelStatus)
	if ccstatus >= 0 {
		return &canceller{this}
	} else {
		return nil
	}
}

//RequestCancel is obsolete. It is replaced by Cancel() method.
func (this *Future) RequestCancel() (e error) {
	return this.Cancel()
}

//IsCancelled returns true if the promise is cancelled, otherwise false
func (this *Future) IsCancelled() bool {
	ccstatus := atomic.LoadInt32(&this.cancelStatus)
	return ccstatus == 2
}

//Cancel sets the status of promise to RESULT_CANCELLED.
//If promise is cancelled, Get() will return nil and CANCELLED error.
//All callback functions will be not called if Promise is cancalled.
func (this *Future) SetTimeout(mm int) *Future {
	if mm == 0 {
		mm = 10
	} else {
		mm = mm * 1000 * 1000
	}

	go func() {
		<-time.After((time.Duration)(mm) * time.Nanosecond)
		this.Cancel()
	}()
	return this
}

//GetChan returns a channel than can be used to receive result of Promise
func (this *Future) GetChan() chan *PromiseResult {
	return this.chOut
}

//Get will block current goroutines until the Future is resolved/rejected/cancelled.
//If Future is resolved, value and nil will be returned
//If Future is rejected, nil and error will be returned.
//If Future is cancelled, nil and CANCELLED error will be returned.
func (this *Future) Get() (val interface{}, err error) {
	<-this.chEnd
	return getFutureReturnVal(this.loadResult())
}

//GetOrTimeout is similar to Get(), but GetOrTimeout will not block after timeout.
//If GetOrTimeout returns with a timeout, timeout value will be true in return values.
//The unit of paramter is millisecond.
func (this *Future) GetOrTimeout(mm uint) (val interface{}, err error, timout bool) {
	if mm == 0 {
		mm = 10
	} else {
		mm = mm * 1000 * 1000
	}

	select {
	case <-time.After((time.Duration)(mm) * time.Nanosecond):
		return nil, nil, true
	case <-this.chEnd:
		r, err := getFutureReturnVal(this.loadResult())
		return r, err, false
	}
}

//Cancel sets the status of promise to RESULT_CANCELLED.
//If promise is cancelled, Get() will return nil and CANCELLED error.
//All callback functions will be not called if Promise is cancalled.
func (this *Future) Cancel() (e error) {
	atomic.StoreInt32(&this.cancelStatus, 2)
	return this.setResult(&PromiseResult{CANCELLED, RESULT_CANCELLED})
}

//OnSuccess registers a callback function that will be called when Promise is resolved.
//If promise is already resolved, the callback will immediately called.
//The value of Promise will be paramter of Done callback function.
func (this *Future) OnSuccess(callback func(v interface{})) *Future {
	this.addCallback(callback, CALLBACK_DONE)
	return this
}

//OnFailure registers a callback function that will be called when Promise is rejected.
//If promise is already rejected, the callback will immediately called.
//The error of Promise will be paramter of Fail callback function.
func (this *Future) OnFailure(callback func(v interface{})) *Future {
	this.addCallback(callback, CALLBACK_FAIL)
	return this
}

//OnComplete register a callback function that will be called when Promise is rejected or resolved.
//If promise is already rejected or resolved, the callback will immediately called.
//According to the status of Promise, value or error will be paramter of Always callback function.
//Value is the paramter if Promise is resolved, or error is the paramter if Promise is rejected.
//Always callback will be not called if Promise be called.
func (this *Future) OnComplete(callback func(v interface{})) *Future {
	this.addCallback(callback, CALLBACK_ALWAYS)
	return this
}

//OnCancel registers a callback function that will be called when Promise is cancelled.
//If promise is already cancelled, the callback will immediately called.
func (this *Future) OnCancel(callback func()) *Future {
	this.addCallback(callback, CALLBACK_CANCEL)
	return this
}

//Pipe registers one or two functions that returns a Future, and returns a proxy of pipeline Future.
//First function will be called when Future is resolved, the returned Future will be as pipeline Future.
//Secondary function will be called when Futrue is rejected, the returned Future will be as pipeline Future.
func (this *Future) Pipe(callbacks ...interface{}) (result *Future, ok bool) {
	if len(callbacks) == 0 ||
		(len(callbacks) == 1 && callbacks[0] == nil) ||
		(len(callbacks) > 1 && callbacks[0] == nil && callbacks[1] == nil) {
		result = this
		return
	}

	//ensure all callback functions match the spec "func(v interface{}) *Future"
	cs := make([]func(v interface{}) *Future, len(callbacks), len(callbacks))
	for i, callback := range callbacks {
		if c, ok1 := callback.(func(v interface{}) *Future); ok1 {
			cs[i] = c
		} else if c, ok1 := callback.(func() *Future); ok1 {
			cs[i] = func(v interface{}) *Future {
				return c()
			}
		} else if c, ok1 := callback.(func(v interface{})); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() {
					c(v)
				})
			}
		} else if c, ok1 := callback.(func(v interface{}) (r interface{}, err error)); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() (r interface{}, err error) {
					r, err = c(v)
					return
				})
			}
		} else if c, ok1 := callback.(func()); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() {
					c()
				})
			}
		} else if c, ok1 := callback.(func() (r interface{}, err error)); ok1 {
			cs[i] = func(v interface{}) *Future {
				return Start(func() (r interface{}, err error) {
					r, err = c()
					return
				})
			}
		} else {
			ok = false
			return
		}
	}

	for {
		v := this.loadVal()
		r := v.r
		if r != nil {
			result = this
			if r.Typ == RESULT_SUCCESS && cs[0] != nil {
				result = (cs[0](r.Result))
			} else if r.Typ == RESULT_FAILURE && len(cs) > 1 && cs[1] != nil {
				result = (cs[1](r.Result))
			}
		} else {
			newPipe := &pipe{}
			newPipe.pipeDoneTask = cs[0]
			if len(cs) > 1 {
				newPipe.pipeFailTask = cs[1]
			}
			newPipe.pipePromise = NewPromise()

			newVal := *v
			newVal.pipes = append(newVal.pipes, newPipe)

			//use CAS to ensure that the state of Future is not changed,
			//if the state is changed, will retry CAS operation.
			if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) {
				result = newPipe.pipePromise.Future
				break
			}
		}
	}
	ok = true

	return
}

//result uses Atomic load to return result of the Future
func (this *Future) loadResult() *PromiseResult {
	val := this.loadVal()
	return val.r
}

//val uses Atomic load to return state value of the Future
func (this *Future) loadVal() *futureVal {
	r := atomic.LoadPointer(&this.val)
	return (*futureVal)(r)
}

//setResult sets the value and final status of Promise, it will only be executed for once
func (this *Future) setResult(r *PromiseResult) (e error) { //r *PromiseResult) {
	defer func() {
		if err := getError(recover()); err != nil {
			e = err
			fmt.Println("\nerror in setResult():", err)
		}
	}()

	e = errors.New("Cannot resolve/reject/cancel more than once")

	for {
		v := this.loadVal()
		if v.r != nil {
			return
		}
		newVal := *v
		newVal.r = r

		//Use CAS operation to ensure that the state of Promise isn't changed.
		//If the state is changed, must get latest state and try to call CAS again.
		//No ABA issue in this case because address of all objects are different.
		if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) {
			//chOut will be returned in GetChan(), so send the result to chOut
			this.chOut <- r

			//Close chEnd then all Get() and GetOrTimeout() will be unblocked
			close(this.chEnd)

			//call callback functions and start the Promise pipeline
			if len(v.dones) > 0 || len(v.fails) > 0 || len(v.always) > 0 || len(v.cancels) > 0 {
				go func() {
					execCallback(r, v.dones, v.fails, v.always, v.cancels)
				}()
			}

			//start the pipeline
			if len(v.pipes) > 0 {
				go func() {
					for _, pipe := range v.pipes {
						pipeTask, pipePromise := pipe.getPipe(r.Typ == RESULT_SUCCESS)
						startPipe(r, pipeTask, pipePromise)
					}
				}()
			}
			e = nil
			break
		}
	}
	return
}

//handleOneCallback registers a callback function
func (this *Future) addCallback(callback interface{}, t callbackType) {
	if callback == nil {
		return
	}
	if (t == CALLBACK_DONE) ||
		(t == CALLBACK_FAIL) ||
		(t == CALLBACK_ALWAYS) {
		if _, ok := callback.(func(v interface{})); !ok {
			panic(errors.New("Callback function spec must be func(v interface{})"))
		}
	} else if t == CALLBACK_CANCEL {
		if _, ok := callback.(func()); !ok {
			panic(errors.New("Callback function spec must be func()"))
		}
	}

	for {
		v := this.loadVal()
		r := v.r
		if r == nil {
			newVal := *v
			switch t {
			case CALLBACK_DONE:
				newVal.dones = append(newVal.dones, callback.(func(v interface{})))
			case CALLBACK_FAIL:
				newVal.fails = append(newVal.fails, callback.(func(v interface{})))
			case CALLBACK_ALWAYS:
				newVal.always = append(newVal.always, callback.(func(v interface{})))
			case CALLBACK_CANCEL:
				newVal.cancels = append(newVal.cancels, callback.(func()))
			}

			//use CAS to ensure that the state of Future is not changed,
			//if the state is changed, will retry CAS operation.
			if atomic.CompareAndSwapPointer(&this.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) {
				break
			}
		} else {
			if (t == CALLBACK_DONE && r.Typ == RESULT_SUCCESS) ||
				(t == CALLBACK_FAIL && r.Typ == RESULT_FAILURE) ||
				(t == CALLBACK_ALWAYS && r.Typ != RESULT_CANCELLED) {
				callbackFunc := callback.(func(v interface{}))
				callbackFunc(r.Result)
			} else if t == CALLBACK_CANCEL && r.Typ == RESULT_CANCELLED {
				callbackFunc := callback.(func())
				callbackFunc()
			}
			break
		}
	}
}
<|endoftext|>


<commit_before>package middleware

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/carbonsrv/carbon/modules/glue"
	"github.com/carbonsrv/carbon/modules/helpers"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/carbonsrv/carbon/modules/static"
	"github.com/fzzy/radix/redis"
	"github.com/gin-gonic/gin"
	"github.com/pmylund/go-cache"
	"github.com/shurcooL/github_flavored_markdown"
	"github.com/vifino/contrib/gzip"
	"github.com/vifino/golua/lua"
	"github.com/vifino/luar"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"time"
)

func Bind(L *lua.State) {
	BindCarbon(L)
	BindMiddleware(L)
	BindRedis(L)
	BindKVStore(L)
	BindPhysFS(L)
	BindIOEnhancements(L)
	BindOSEnhancements(L)
	BindThread(L)
	BindNet(L)
	BindConversions(L)
	BindComs(L)
	BindMarkdown(L)
	BindOther(L)
}

func BindCarbon(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{ // Carbon specific API
		"glue": glue.GetGlue,
	})
}

func BindEngine(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{
		"_gin_new": gin.New,
	})
}

func BindMiddleware(L *lua.State) {
	luar.Register(L, "mw", luar.Map{
		// Essentials
		"Logger":   gin.Logger,
		"Recovery": gin.Recovery,

		// Lua related stuff
		"Lua":       Lua,
		"DLR_NS":    DLR_NS,
		"DLR_RUS":   DLR_RUS,
		"DLRWS_RUS": DLRWS_RUS,

		// Custom sub-routers.
		"ExtRoute": (func(plan map[string]interface{}) func(*gin.Context) {
			newplan := make(Plan, len(plan))
			for k, v := range plan {
				newplan[k] = v.(func(*gin.Context))
			}
			return ExtRoute(newplan)
		}),
		"VHOST": (func(plan map[string]interface{}) func(*gin.Context) {
			newplan := make(Plan, len(plan))
			for k, v := range plan {
				newplan[k] = v.(func(*gin.Context))
			}
			return VHOST(newplan)
		}),
		"VHOST_Middleware": (func(plan map[string]interface{}) gin.HandlerFunc {
			newplan := make(Plan, len(plan))
			for k, v := range plan {
				newplan[k] = v.(gin.HandlerFunc)
			}
			return VHOST_Middleware(newplan)
		}),

		// To run or not to run, that is the question!
		"if_regex":       If_Regexp,
		"if_written":     If_Written,
		"if_status":      If_Status,
		"if_not_regex":   If_Not_Regexp,
		"if_not_written": If_Not_Written,
		"if_not_status":  If_Not_Status,

		// Modification stuff.
		"GZip": func() func(*gin.Context) {
			return gzip.Gzip(gzip.DefaultCompression)
		},

		// Basic
		"Echo":     EchoHTML,
		"EchoText": Echo,
	})
	luar.Register(L, "carbon", luar.Map{
		"_mw_CGI": CGI, // Run CGI Apps!
		"_mw_combine": (func(middlewares []interface{}) func(*gin.Context) { // Combine routes, doesn't properly route like middleware or anything.
			newmiddlewares := make([]func(*gin.Context), len(middlewares))
			for k, v := range middlewares {
				newmiddlewares[k] = v.(func(*gin.Context))
			}
			return Combine(newmiddlewares)
		}),
	})
	L.DoString(glue.RouteGlue())
}

func BindPhysFS(L *lua.State) {
	luar.Register(L, "fs", luar.Map{ // PhysFS
		"mount":       physfs.Mount,
		"exits":       physfs.Exists,
		"getFS":       physfs.FileSystem,
		"mkdir":       physfs.Mkdir,
		"umount":      physfs.RemoveFromSearchPath,
		"delete":      physfs.Delete,
		"setWriteDir": physfs.SetWriteDir,
		"getWriteDir": physfs.GetWriteDir,
	})
}

func BindIOEnhancements(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{ // Small enhancements to the io stuff.
		"_io_list": (func(path string) ([]string, error) {
			files, err := ioutil.ReadDir(path)
			if err != nil {
				return make([]string, 1), err
			} else {
				list := make([]string, len(files))
				for i := range files {
					list[i] = files[i].Name()
				}
				return list, nil
			}
		}),
		"_io_glob": filepath.Glob,
		"_io_modtime": (func(path string) (int, error) {
			info, err := os.Stat(path)
			if err != nil {
				return -1, err
			} else {
				return int(info.ModTime().UTC().Unix()), nil
			}
		}),
	})
}

func BindOSEnhancements(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{ // Small enhancements to the io stuff.
		"_os_exists": (func(path string) bool {
			if _, err := os.Stat(path); err == nil {
				return true
			} else {
				return false
			}
		}),
		"_os_chdir":   os.Chdir,
		"_os_abspath": filepath.Abs,
	})
}

func BindRedis(L *lua.State) {
	luar.Register(L, "redis", luar.Map{
		"connectTimeout": (func(host string, timeout int) (*redis.Client, error) {
			return redis.DialTimeout("tcp", host, time.Duration(timeout)*time.Second)
		}),
		"connect": (func(host string) (*redis.Client, error) {
			return redis.Dial("tcp", host)
		}),
	})
}

func BindKVStore(L *lua.State) { // Thread safe Key Value Store that doesn't persist.
	luar.Register(L, "kvstore", luar.Map{
		"_set": (func(k string, v interface{}) {
			kvstore.Set(k, v, -1)
		}),
		"_del": (func(k string) {
			kvstore.Delete(k)
		}),
		"_get": (func(k string) interface{} {
			res, found := kvstore.Get(k)
			if found {
				return res
			} else {
				return nil
			}
		}),
		"_inc": (func(k string, n int64) error {
			return kvstore.Increment(k, n)
		}),
		"_dec": (func(k string, n int64) error {
			return kvstore.Decrement(k, n)
		}),
	})
}

func BindThread(L *lua.State) {
	luar.Register(L, "thread", luar.Map{
		"_spawn": (func(bcode string, dobind bool, vals map[string]interface{}) error {
			L := luar.Init()
			Bind(L)
			err := L.DoString(glue.MainGlue())
			if err != nil {
				panic(err)
			}

			if dobind {
				luar.Register(L, "", vals)
			}

			if L.LoadBuffer(bcode, len(bcode), "thread") != 0 {
				return errors.New(L.ToString(-1))
			}

			scheduler.Add(func() {
				if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
					// Silently error because reasons. ._.
				}
			})
			return nil
		}),
	})
}

func BindComs(L *lua.State) {
	luar.Register(L, "com", luar.Map{
		"create": (func() chan interface{} {
			return make(chan interface{})
		}),
		"createBuffered": (func(buffer int) chan interface{} {
			return make(chan interface{}, buffer)
		}),
		"receive": (func(c chan interface{}) interface{} {
			return <-c
		}),
		"send": (func(c chan interface{}, val interface{}) {
			c <- val
		}),
	})
}

func BindNet(L *lua.State) {
	luar.Register(L, "net", luar.Map{
		"dial": net.Dial,
		"write": (func(con net.Conn, str string) {
			fmt.Fprintf(con, str)
		}),
		"readline": (func(con net.Conn) (string, error) {
			return bufio.NewReader(con).ReadString('\n')
		}),
	})
}

func BindConversions(L *lua.State) {
	luar.Register(L, "convert", luar.Map{
		"stringtocharslice": (func(x string) []byte {
			return []byte(x)
		}),
		"charslicetostring": (func(x []byte) string {
			return string(x)
		}),
	})
}

func BindContext(L *lua.State, context *gin.Context) {
	luar.Register(L, "", luar.Map{
		"context": context,
		"req":     context.Request,

		"host":   context.Request.URL.Host,
		"path":   context.Request.URL.Path,
		"scheme": context.Request.URL.Scheme,
	})
	luar.Register(L, "carbon", luar.Map{
		"_header_set": context.Header,
		"_header_get": context.Request.Header().Get,
		"_paramfunc":  context.Param,
		"_formfunc":   context.PostForm,
		"_queryfunc":  context.Query,
	})
}
func BindStatic(L *lua.State, cfe *cache.Cache) {
	luar.Register(L, "carbon", luar.Map{
		"_staticserve": (func(path, prefix string) func(*gin.Context) {
			return staticServe.ServeCached(prefix, staticServe.PhysFS(path, prefix, true, true), cfe)
		}),
	})
}

func BindMarkdown(L *lua.State) {
	luar.Register(L, "markdown", luar.Map{
		"github": (func(source string) string {
			return string(github_flavored_markdown.Markdown([]byte(source)))
		}),
	})
}

func BindOther(L *lua.State) {
	luar.Register(L, "", luar.Map{
		"unixtime": (func() int {
			return int(time.Now().UTC().Unix())
		}),
	})
	luar.Register(L, "carbon", luar.Map{
		"_syntaxhl": helpers.SyntaxHL,
	})
}
<commit_msg>Alright, I can do this.<commit_after>package middleware

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/carbonsrv/carbon/modules/glue"
	"github.com/carbonsrv/carbon/modules/helpers"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/carbonsrv/carbon/modules/static"
	"github.com/fzzy/radix/redis"
	"github.com/gin-gonic/gin"
	"github.com/pmylund/go-cache"
	"github.com/shurcooL/github_flavored_markdown"
	"github.com/vifino/contrib/gzip"
	"github.com/vifino/golua/lua"
	"github.com/vifino/luar"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"time"
)

func Bind(L *lua.State) {
	BindCarbon(L)
	BindMiddleware(L)
	BindRedis(L)
	BindKVStore(L)
	BindPhysFS(L)
	BindIOEnhancements(L)
	BindOSEnhancements(L)
	BindThread(L)
	BindNet(L)
	BindConversions(L)
	BindComs(L)
	BindMarkdown(L)
	BindOther(L)
}

func BindCarbon(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{ // Carbon specific API
		"glue": glue.GetGlue,
	})
}

func BindEngine(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{
		"_gin_new": gin.New,
	})
}

func BindMiddleware(L *lua.State) {
	luar.Register(L, "mw", luar.Map{
		// Essentials
		"Logger":   gin.Logger,
		"Recovery": gin.Recovery,

		// Lua related stuff
		"Lua":       Lua,
		"DLR_NS":    DLR_NS,
		"DLR_RUS":   DLR_RUS,
		"DLRWS_RUS": DLRWS_RUS,

		// Custom sub-routers.
		"ExtRoute": (func(plan map[string]interface{}) func(*gin.Context) {
			newplan := make(Plan, len(plan))
			for k, v := range plan {
				newplan[k] = v.(func(*gin.Context))
			}
			return ExtRoute(newplan)
		}),
		"VHOST": (func(plan map[string]interface{}) func(*gin.Context) {
			newplan := make(Plan, len(plan))
			for k, v := range plan {
				newplan[k] = v.(func(*gin.Context))
			}
			return VHOST(newplan)
		}),
		"VHOST_Middleware": (func(plan map[string]interface{}) gin.HandlerFunc {
			newplan := make(Plan, len(plan))
			for k, v := range plan {
				newplan[k] = v.(gin.HandlerFunc)
			}
			return VHOST_Middleware(newplan)
		}),

		// To run or not to run, that is the question!
		"if_regex":       If_Regexp,
		"if_written":     If_Written,
		"if_status":      If_Status,
		"if_not_regex":   If_Not_Regexp,
		"if_not_written": If_Not_Written,
		"if_not_status":  If_Not_Status,

		// Modification stuff.
		"GZip": func() func(*gin.Context) {
			return gzip.Gzip(gzip.DefaultCompression)
		},

		// Basic
		"Echo":     EchoHTML,
		"EchoText": Echo,
	})
	luar.Register(L, "carbon", luar.Map{
		"_mw_CGI": CGI, // Run CGI Apps!
		"_mw_combine": (func(middlewares []interface{}) func(*gin.Context) { // Combine routes, doesn't properly route like middleware or anything.
			newmiddlewares := make([]func(*gin.Context), len(middlewares))
			for k, v := range middlewares {
				newmiddlewares[k] = v.(func(*gin.Context))
			}
			return Combine(newmiddlewares)
		}),
	})
	L.DoString(glue.RouteGlue())
}

func BindPhysFS(L *lua.State) {
	luar.Register(L, "fs", luar.Map{ // PhysFS
		"mount":       physfs.Mount,
		"exits":       physfs.Exists,
		"getFS":       physfs.FileSystem,
		"mkdir":       physfs.Mkdir,
		"umount":      physfs.RemoveFromSearchPath,
		"delete":      physfs.Delete,
		"setWriteDir": physfs.SetWriteDir,
		"getWriteDir": physfs.GetWriteDir,
	})
}

func BindIOEnhancements(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{ // Small enhancements to the io stuff.
		"_io_list": (func(path string) ([]string, error) {
			files, err := ioutil.ReadDir(path)
			if err != nil {
				return make([]string, 1), err
			} else {
				list := make([]string, len(files))
				for i := range files {
					list[i] = files[i].Name()
				}
				return list, nil
			}
		}),
		"_io_glob": filepath.Glob,
		"_io_modtime": (func(path string) (int, error) {
			info, err := os.Stat(path)
			if err != nil {
				return -1, err
			} else {
				return int(info.ModTime().UTC().Unix()), nil
			}
		}),
	})
}

func BindOSEnhancements(L *lua.State) {
	luar.Register(L, "carbon", luar.Map{ // Small enhancements to the io stuff.
		"_os_exists": (func(path string) bool {
			if _, err := os.Stat(path); err == nil {
				return true
			} else {
				return false
			}
		}),
		"_os_chdir":   os.Chdir,
		"_os_abspath": filepath.Abs,
	})
}

func BindRedis(L *lua.State) {
	luar.Register(L, "redis", luar.Map{
		"connectTimeout": (func(host string, timeout int) (*redis.Client, error) {
			return redis.DialTimeout("tcp", host, time.Duration(timeout)*time.Second)
		}),
		"connect": (func(host string) (*redis.Client, error) {
			return redis.Dial("tcp", host)
		}),
	})
}

func BindKVStore(L *lua.State) { // Thread safe Key Value Store that doesn't persist.
	luar.Register(L, "kvstore", luar.Map{
		"_set": (func(k string, v interface{}) {
			kvstore.Set(k, v, -1)
		}),
		"_del": (func(k string) {
			kvstore.Delete(k)
		}),
		"_get": (func(k string) interface{} {
			res, found := kvstore.Get(k)
			if found {
				return res
			} else {
				return nil
			}
		}),
		"_inc": (func(k string, n int64) error {
			return kvstore.Increment(k, n)
		}),
		"_dec": (func(k string, n int64) error {
			return kvstore.Decrement(k, n)
		}),
	})
}

func BindThread(L *lua.State) {
	luar.Register(L, "thread", luar.Map{
		"_spawn": (func(bcode string, dobind bool, vals map[string]interface{}) error {
			L := luar.Init()
			Bind(L)
			err := L.DoString(glue.MainGlue())
			if err != nil {
				panic(err)
			}

			if dobind {
				luar.Register(L, "", vals)
			}

			if L.LoadBuffer(bcode, len(bcode), "thread") != 0 {
				return errors.New(L.ToString(-1))
			}

			scheduler.Add(func() {
				if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
					// Silently error because reasons. ._.
				}
			})
			return nil
		}),
	})
}

func BindComs(L *lua.State) {
	luar.Register(L, "com", luar.Map{
		"create": (func() chan interface{} {
			return make(chan interface{})
		}),
		"createBuffered": (func(buffer int) chan interface{} {
			return make(chan interface{}, buffer)
		}),
		"receive": (func(c chan interface{}) interface{} {
			return <-c
		}),
		"send": (func(c chan interface{}, val interface{}) {
			c <- val
		}),
	})
}

func BindNet(L *lua.State) {
	luar.Register(L, "net", luar.Map{
		"dial": net.Dial,
		"write": (func(con net.Conn, str string) {
			fmt.Fprintf(con, str)
		}),
		"readline": (func(con net.Conn) (string, error) {
			return bufio.NewReader(con).ReadString('\n')
		}),
	})
}

func BindConversions(L *lua.State) {
	luar.Register(L, "convert", luar.Map{
		"stringtocharslice": (func(x string) []byte {
			return []byte(x)
		}),
		"charslicetostring": (func(x []byte) string {
			return string(x)
		}),
	})
}

func BindContext(L *lua.State, context *gin.Context) {
	luar.Register(L, "", luar.Map{
		"context": context,
		"req":     context.Request,

		"host":   context.Request.URL.Host,
		"path":   context.Request.URL.Path,
		"scheme": context.Request.URL.Scheme,
	})
	luar.Register(L, "carbon", luar.Map{
		"_header_set": context.Header,
		"_header_get": context.Request.Header.Get,
		"_paramfunc":  context.Param,
		"_formfunc":   context.PostForm,
		"_queryfunc":  context.Query,
	})
}
func BindStatic(L *lua.State, cfe *cache.Cache) {
	luar.Register(L, "carbon", luar.Map{
		"_staticserve": (func(path, prefix string) func(*gin.Context) {
			return staticServe.ServeCached(prefix, staticServe.PhysFS(path, prefix, true, true), cfe)
		}),
	})
}

func BindMarkdown(L *lua.State) {
	luar.Register(L, "markdown", luar.Map{
		"github": (func(source string) string {
			return string(github_flavored_markdown.Markdown([]byte(source)))
		}),
	})
}

func BindOther(L *lua.State) {
	luar.Register(L, "", luar.Map{
		"unixtime": (func() int {
			return int(time.Now().UTC().Unix())
		}),
	})
	luar.Register(L, "carbon", luar.Map{
		"_syntaxhl": helpers.SyntaxHL,
	})
}
<|endoftext|>


<commit_before>// The chroot package is able to create an Outscale OMI without requiring
// the launch of a new instance for every build. It does this by attaching
// and mounting the root volume of another OMI and chrooting into that
// directory. It then creates an OMI from that attached drive.
package chroot

import (
	"errors"
	"runtime"

	osccommon "github.com/hashicorp/packer/builder/osc/common"
	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

// The unique ID for this builder
const BuilderId = "oapi.outscale.chroot"

// Config is the configuration that is chained through the steps and
// settable from the template.
type Config struct {
	common.PackerConfig       `mapstructure:",squash"`
	osccommon.OMIBlockDevices `mapstructure:",squash"`
	osccommon.OMIConfig       `mapstructure:",squash"`
	osccommon.AccessConfig    `mapstructure:",squash"`

	ChrootMounts      [][]string                 `mapstructure:"chroot_mounts"`
	CommandWrapper    string                     `mapstructure:"command_wrapper"`
	CopyFiles         []string                   `mapstructure:"copy_files"`
	DevicePath        string                     `mapstructure:"device_path"`
	NVMEDevicePath    string                     `mapstructure:"nvme_device_path"`
	FromScratch       bool                       `mapstructure:"from_scratch"`
	MountOptions      []string                   `mapstructure:"mount_options"`
	MountPartition    string                     `mapstructure:"mount_partition"`
	MountPath         string                     `mapstructure:"mount_path"`
	PostMountCommands []string                   `mapstructure:"post_mount_commands"`
	PreMountCommands  []string                   `mapstructure:"pre_mount_commands"`
	RootDeviceName    string                     `mapstructure:"root_device_name"`
	RootVolumeSize    int64                      `mapstructure:"root_volume_size"`
	RootVolumeType    string                     `mapstructure:"root_volume_type"`
	SourceOMI         string                     `mapstructure:"source_omi"`
	SourceOMIFilter   osccommon.OmiFilterOptions `mapstructure:"source_omi_filter"`
	RootVolumeTags    osccommon.TagMap           `mapstructure:"root_volume_tags"`

	ctx interpolate.Context
}

type wrappedCommandTemplate struct {
	Command string
}

type Builder struct {
	config Config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
	return nil, nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	if runtime.GOOS != "linux" {
		return nil, errors.New("The outscale-chroot builder only works on Linux environments.")
	}
	return nil, nil
}

func (b *Builder) Cancel() {
}
<commit_msg>feature: add chroot build cancel function implementation<commit_after>// The chroot package is able to create an Outscale OMI without requiring
// the launch of a new instance for every build. It does this by attaching
// and mounting the root volume of another OMI and chrooting into that
// directory. It then creates an OMI from that attached drive.
package chroot

import (
	"errors"
	"log"
	"runtime"

	osccommon "github.com/hashicorp/packer/builder/osc/common"
	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

// The unique ID for this builder
const BuilderId = "oapi.outscale.chroot"

// Config is the configuration that is chained through the steps and
// settable from the template.
type Config struct {
	common.PackerConfig       `mapstructure:",squash"`
	osccommon.OMIBlockDevices `mapstructure:",squash"`
	osccommon.OMIConfig       `mapstructure:",squash"`
	osccommon.AccessConfig    `mapstructure:",squash"`

	ChrootMounts      [][]string                 `mapstructure:"chroot_mounts"`
	CommandWrapper    string                     `mapstructure:"command_wrapper"`
	CopyFiles         []string                   `mapstructure:"copy_files"`
	DevicePath        string                     `mapstructure:"device_path"`
	NVMEDevicePath    string                     `mapstructure:"nvme_device_path"`
	FromScratch       bool                       `mapstructure:"from_scratch"`
	MountOptions      []string                   `mapstructure:"mount_options"`
	MountPartition    string                     `mapstructure:"mount_partition"`
	MountPath         string                     `mapstructure:"mount_path"`
	PostMountCommands []string                   `mapstructure:"post_mount_commands"`
	PreMountCommands  []string                   `mapstructure:"pre_mount_commands"`
	RootDeviceName    string                     `mapstructure:"root_device_name"`
	RootVolumeSize    int64                      `mapstructure:"root_volume_size"`
	RootVolumeType    string                     `mapstructure:"root_volume_type"`
	SourceOMI         string                     `mapstructure:"source_omi"`
	SourceOMIFilter   osccommon.OmiFilterOptions `mapstructure:"source_omi_filter"`
	RootVolumeTags    osccommon.TagMap           `mapstructure:"root_volume_tags"`

	ctx interpolate.Context
}

type wrappedCommandTemplate struct {
	Command string
}

type Builder struct {
	config Config
	runner multistep.Runner
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
	return nil, nil
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
	if runtime.GOOS != "linux" {
		return nil, errors.New("The outscale-chroot builder only works on Linux environments.")
	}
	return nil, nil
}

func (b *Builder) Cancel() {
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"eaciit/gdrj/model"
	"eaciit/gdrj/modules"
	"os"

	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

var conn dbox.IConnection
var count int
var ratioTableName string

var (
	ratiototrf      = float64(0.392)
	sourcetablename = "salespls-summary"
	calctablename   = "salespls-summary"
	desttablename   = "salespls-summary"
	t0              time.Time
	masters         = toolkit.M{}
	sgaalloc        = map[string]float64{
		"EXP": 0.08,
		"I4":  0.08,
		"I6":  0.105,
	}
)

type plalloc struct {
	ID                     string `bson:"_id" json:"_id"`
	Key                    string
	Key1, Key2, Key3       string
	Ref1                   float64
	Current                float64
	Expect                 float64
	Absorbed               float64
	Ratio1, Ratio2, Ratio3 float64
}

type allocmap map[string]*plalloc

var (
	rdfiscals    = allocmap{}
	branchtotals = allocmap{}
	trxsrc       = "nakulrd"
)

func main() {
	setinitialconnection()
	conn.NewQuery().From(desttablename).
		Where(dbox.Eq("key.trxsrc", trxsrc)).
		Delete().
		Exec(nil)

	prepmastercalc()
	//buildratio()
	processTable()
}

func processTable() {
	connsave, _ := modules.GetDboxIConnection("db_godrej")
	defer connsave.Close()
	qsave := connsave.NewQuery().SetConfig("multiexec", true).From(desttablename).Save()

	connselect, _ := modules.GetDboxIConnection("db_godrej")
	defer connselect.Close()

	cursor, _ := connselect.NewQuery().
		From(calctablename).
		Where(dbox.Ne("key.trxsrc", trxsrc)).
		Select().Cursor(nil)
	defer cursor.Close()

	i := 0
	count := cursor.Count()
	mstone := 0
	t0 = time.Now()
	for {
		mr := toolkit.M{}
		e := cursor.Fetch(&mr, 1, false)
		if e != nil || i >= count {
			break
		}
		i++
		makeProgressLog("Processing", i, count, 5, &mstone, t0)

		mrid := mr.GetString("_id")
		key := mr.Get("key", toolkit.M{}).(toolkit.M)
		channelid := key.GetString("customer_channelid")
		branchid := key.GetString("customer_branchid")
		//branchname := key.GetString("customer_branchname")

		if channelid != "I1" {
			discount := mr.GetFloat64("PL7A") * ratiototrf
			spg := mr.GetFloat64("PL31") * ratiototrf
			promo := mr.GetFloat64("Pl29A") * ratiototrf

			if discount != 0 || spg != 0 || promo != 0 {
				if branchid != "CD02" || branchid != "CD04" || branchid != "CD11" ||
					branchid != "CD09" {
					branchid = "CD02"
					//branchname = "Jakarta"
				}

				mreverse := toolkit.M{}
				mreverse.Set("_id", mrid+"_rd_reverse")
				mreversekey := toolkit.M{}
				for k, v := range key {
					mreversekey.Set(k, v)
				}
				mreversekey.Set("customer_customergroup", "")
				mreversekey.Set("customer_customergroupname", "")
				mreversekey.Set("trxsrc", trxsrc)
				mreverse.Set("key", mreversekey)
				//mreverse.Set("PL7A", -discount)
				//mreverse.Set("PL29A32", -spg)
				//mreverse.Set("PL31C", -promo)
				gdrj.CalcSum(mreverse, masters)
				esave := qsave.Exec(toolkit.M{}.Set("data", mreverse))
				if esave != nil {
					toolkit.Printfn("Error: " + esave.Error())
					os.Exit(100)
				}

				/*
					                mrd := toolkit.M{}
									mrd.Set("_id", mrid+"_rd")
									mrdkey := toolkit.M{}
									for k, v := range key {
										mrdkey.Set(k, v)
									}
									mrdkey.Set("customer_channelid", "I1")
									mrdkey.Set("customer_reportchannel", "RD")
									mrdkey.Set("customer_customertype", "RD")
									mrdkey.Set("customer_channelname", "RD")
									mrdkey.Set("customer_branchid", branchid)
									mrdkey.Set("customer_branchname", branchname)
									mrdkey.Set("customer_branchgroup", branchname)
									mrdkey.Set("customer_reportsubchannel", "SPG & Promo Allocation")
									mrdkey.Set("customer_customergroup", "")
									mrdkey.Set("customer_customergroupname", "")
									mrdkey.Set("trxsrc", trxsrc)

									mrd.Set("key", mrdkey)
									//mrd.Set("PL7A", discount)
									//mrd.Set("PL29A32", spg)
									//mrd.Set("PL31C", promo)
									gdrj.CalcSum(mrd, masters)
									esave = qsave.Exec(toolkit.M{}.Set("data", mrd))
									if esave != nil {
										toolkit.Printfn("Error: " + esave.Error())
										os.Exit(100)
									}
				*/
			}
		}
	}
}

func adjustAllocs(allocsmap *allocmap, key string, current, expect, absorbed, ref1 float64) {
	allocs := *allocsmap
	alloc := allocs[key]
	if alloc == nil {
		alloc = new(plalloc)
		alloc.Key = key
		alloc.ID = key
	}
	alloc.Current += current
	alloc.Expect += expect
	alloc.Ref1 += ref1
	alloc.Absorbed += absorbed
	allocs[key] = alloc
	*allocsmap = allocs
}

func makeProgressLog(reference string, i, count, step int, current *int, tstart time.Time) int {
	perstep := count * step / 100
	icurrent := *current
	if icurrent == 0 {
		icurrent = perstep
	}
	pct := i * 100 / count
	if i >= icurrent {
		toolkit.Printfn("%s, %d of %d [%d pct] in %s",
			reference, i, count, pct, time.Since(tstart).String())
		icurrent += perstep
	}
	*current = icurrent
	return icurrent
}

func buildmap(holder interface{},
	fnModel func() orm.IModel,
	filter *dbox.Filter,
	fnIter func(holder interface{}, obj interface{})) interface{} {
	crx, ecrx := gdrj.Find(fnModel(), filter, nil)
	if ecrx != nil {
		toolkit.Printfn("Cursor Error: %s", ecrx.Error())
		os.Exit(100)
	}
	defer crx.Close()
	for {
		s := fnModel()
		e := crx.Fetch(s, 1, false)
		if e != nil {
			break
		}
		fnIter(holder, s)
	}
	return holder
}

func prepmastercalc() {
	toolkit.Println("--> PL MODEL")
	masters.Set("plmodel", buildmap(map[string]*gdrj.PLModel{},
		func() orm.IModel {
			return new(gdrj.PLModel)
		},
		nil,
		func(holder, obj interface{}) {
			h := holder.(map[string]*gdrj.PLModel)
			o := obj.(*gdrj.PLModel)
			h[o.ID] = o
		}).(map[string]*gdrj.PLModel))
}

func setinitialconnection() {
	var err error
	conn, err = modules.GetDboxIConnection("db_godrej")

	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}

	err = gdrj.SetDb(conn)
	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}
}
<commit_msg>asdasda<commit_after>package main

import (
	"eaciit/gdrj/model"
	"eaciit/gdrj/modules"
	"os"

	"time"

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
)

var conn dbox.IConnection
var count int
var ratioTableName string

var (
	ratiototrf      = float64(0.392)
	sourcetablename = "salespls-summary"
	calctablename   = "salespls-summary"
	desttablename   = "salespls-summary"
	t0              time.Time
	masters         = toolkit.M{}
	sgaalloc        = map[string]float64{
		"EXP": 0.08,
		"I4":  0.08,
		"I6":  0.105,
	}
)

type plalloc struct {
	ID                     string `bson:"_id" json:"_id"`
	Key                    string
	Key1, Key2, Key3       string
	Ref1                   float64
	Current                float64
	Expect                 float64
	Absorbed               float64
	Ratio1, Ratio2, Ratio3 float64
}

type allocmap map[string]*plalloc

var (
	rdfiscals    = allocmap{}
	branchtotals = allocmap{}
	trxsrc       = "nakulrd"
)

func main() {
	setinitialconnection()
	conn.NewQuery().From(desttablename).
		Where(dbox.Eq("key.trxsrc", trxsrc)).
		Delete().
		Exec(nil)

	prepmastercalc()
	//buildratio()
	processTable()
}

func processTable() {
	connsave, _ := modules.GetDboxIConnection("db_godrej")
	defer connsave.Close()
	qsave := connsave.NewQuery().SetConfig("multiexec", true).From(desttablename).Save()

	connselect, _ := modules.GetDboxIConnection("db_godrej")
	defer connselect.Close()

	cursor, _ := connselect.NewQuery().
		From(calctablename).
		Where(dbox.Ne("key.trxsrc", trxsrc)).
		Select().Cursor(nil)
	defer cursor.Close()

	i := 0
	count := cursor.Count()
	mstone := 0
	t0 = time.Now()
	for {
		mr := toolkit.M{}
		e := cursor.Fetch(&mr, 1, false)
		if e != nil || i >= count {
			break
		}
		i++
		makeProgressLog("Processing", i, count, 5, &mstone, t0)

		mrid := mr.GetString("_id")
		key := mr.Get("key", toolkit.M{}).(toolkit.M)
		channelid := key.GetString("customer_channelid")
		branchid := key.GetString("customer_branchid")
		branchname := key.GetString("customer_branchname")

		if channelid != "I1" {
			discount := mr.GetFloat64("PL7A") * ratiototrf
			spg := mr.GetFloat64("PL31") * ratiototrf
			promo := mr.GetFloat64("Pl29A") * ratiototrf

			if discount != 0 || spg != 0 || promo != 0 {
				if branchid != "CD02" || branchid != "CD04" || branchid != "CD11" ||
					branchid != "CD09" {
					branchid = "CD02"
					//branchname = "Jakarta"
				}

				mreverse := toolkit.M{}
				mreverse.Set("_id", mrid+"_"+trxsrc+"_reverse")
				mreversekey := toolkit.M{}
				for k, v := range key {
					mreversekey.Set(k, v)
				}
				mreversekey.Set("customer_customergroup", "")
				mreversekey.Set("customer_customergroupname", "")
				mreversekey.Set("trxsrc", trxsrc)
				mreverse.Set("key", mreversekey)
				mreverse.Set("PL7A", -discount)
				mreverse.Set("PL29A32", -spg)
				mreverse.Set("PL31C", -promo)
				gdrj.CalcSum(mreverse, masters)
				esave := qsave.Exec(toolkit.M{}.Set("data", mreverse))
				if esave != nil {
					toolkit.Printfn("Error: " + esave.Error())
					os.Exit(100)
				}

				mrd := toolkit.M{}
				mrd.Set("_id", mrid+"_"+trxsrc)
				mrdkey := toolkit.M{}
				for k, v := range key {
					mrdkey.Set(k, v)
				}
				mrdkey.Set("customer_channelid", "I1")
				mrdkey.Set("customer_reportchannel", "RD")
				mrdkey.Set("customer_customertype", "RD")
				mrdkey.Set("customer_channelname", "RD")
				mrdkey.Set("customer_branchid", branchid)
				mrdkey.Set("customer_branchname", branchname)
				mrdkey.Set("customer_branchgroup", branchname)
				mrdkey.Set("customer_reportsubchannel", "SPG & Promo Allocation")
				mrdkey.Set("customer_customergroup", "")
				mrdkey.Set("customer_customergroupname", "")
				mrdkey.Set("trxsrc", trxsrc)
				mrd.Set("key", mrdkey)
				mrd.Set("PL7A", discount)
				mrd.Set("PL29A32", spg)
				mrd.Set("PL31C", promo)
				gdrj.CalcSum(mrd, masters)
				esave = qsave.Exec(toolkit.M{}.Set("data", mrd))
				if esave != nil {
					toolkit.Printfn("Error: " + esave.Error())
					os.Exit(100)
				}
			}
		}
	}
}

func adjustAllocs(allocsmap *allocmap, key string, current, expect, absorbed, ref1 float64) {
	allocs := *allocsmap
	alloc := allocs[key]
	if alloc == nil {
		alloc = new(plalloc)
		alloc.Key = key
		alloc.ID = key
	}
	alloc.Current += current
	alloc.Expect += expect
	alloc.Ref1 += ref1
	alloc.Absorbed += absorbed
	allocs[key] = alloc
	*allocsmap = allocs
}

func makeProgressLog(reference string, i, count, step int, current *int, tstart time.Time) int {
	perstep := count * step / 100
	icurrent := *current
	if icurrent == 0 {
		icurrent = perstep
	}
	pct := i * 100 / count
	if i >= icurrent {
		toolkit.Printfn("%s, %d of %d [%d pct] in %s",
			reference, i, count, pct, time.Since(tstart).String())
		icurrent += perstep
	}
	*current = icurrent
	return icurrent
}

func buildmap(holder interface{},
	fnModel func() orm.IModel,
	filter *dbox.Filter,
	fnIter func(holder interface{}, obj interface{})) interface{} {
	crx, ecrx := gdrj.Find(fnModel(), filter, nil)
	if ecrx != nil {
		toolkit.Printfn("Cursor Error: %s", ecrx.Error())
		os.Exit(100)
	}
	defer crx.Close()
	for {
		s := fnModel()
		e := crx.Fetch(s, 1, false)
		if e != nil {
			break
		}
		fnIter(holder, s)
	}
	return holder
}

func prepmastercalc() {
	toolkit.Println("--> PL MODEL")
	masters.Set("plmodel", buildmap(map[string]*gdrj.PLModel{},
		func() orm.IModel {
			return new(gdrj.PLModel)
		},
		nil,
		func(holder, obj interface{}) {
			h := holder.(map[string]*gdrj.PLModel)
			o := obj.(*gdrj.PLModel)
			h[o.ID] = o
		}).(map[string]*gdrj.PLModel))
}

func setinitialconnection() {
	var err error
	conn, err = modules.GetDboxIConnection("db_godrej")

	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}

	err = gdrj.SetDb(conn)
	if err != nil {
		toolkit.Println("Initial connection found : ", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

package reputation

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/jteeuwen/ircb/plugin"
	"github.com/jteeuwen/ircb/proto"
	"log"
	"os"
	"regexp"
	"strings"
)

type RepChange struct {
	entity string
	delta  int
}

var reputation map[string]int

var reputationChanges chan RepChange

var red redis.Conn

const WHOIS_DB = "2"
const WHOIS_SUFFIX = "-whois" // TODO remove this hack

func init() { plugin.Register(New) }

type Plugin struct {
	*plugin.Base

	// Each entry holds a regex pattern which should be excluded
	// from the url-title-lookup.
	exclude []*regexp.Regexp

	// Pattern which recognizes s-expressions.
	// TODO use proper parser
	sexpr *regexp.Regexp
}

func New(profile string) plugin.Plugin {
	p := new(Plugin)
	p.Base = plugin.New(profile, "url")
	p.sexpr = regexp.MustCompile(`(\((whois) (.*?)\)|\((is) (.*?) (.*?)\))`)
	return p
}

// Init initializes the plugin. it loads configuration data and binds
// commands and protocol handlers.
func (p *Plugin) Load(c *proto.Client) (err error) {
	err = p.Base.Load(c)
	if err != nil {
		return
	}

	c.Bind(proto.CmdPrivMsg, func(c *proto.Client, m *proto.Message) {
		p.parseSexpr(c, m)
	})

	ini := p.LoadConfig()
	if ini == nil {
		return
	}

	s := ini.Section("exclude")
	list := s.List("url")
	p.exclude = make([]*regexp.Regexp, len(list))

	for i := range list {
		p.exclude[i], err = regexp.Compile(list[i])

		if err != nil {
			return
		}
	}

	return
}

// parseSexpr looks for s-expressions embedded in incoming messages.
// If they are valid s-expressions and not in the exclude list,
// we use them to fetch page titles from the internet.
func (p *Plugin) parseSexpr(c *proto.Client, m *proto.Message) {
	list := p.sexpr.FindAllStringSubmatch(m.Data, -1)
	if len(list) == 0 {
		return
	}

	for _, sexpr := range list {
		if !p.excluded(sexpr[0]) {
			go whoIs(c, m, sexpr)
		}
	}
}

// excluded returns true if the given url is part of the exclusion list.
func (p *Plugin) excluded(url string) bool {
	for _, excl := range p.exclude {
		if excl.MatchString(url) {
			return true
		}
	}

	return false
}

// fetchTitle attempts to retrieve the title element for a given url.
func whoIs(c *proto.Client, m *proto.Message, match []string) {
	log.Printf("Whois: %+v", match)
	entity := strings.ToLower(match[3])
	action := match[2]
	response := "no idea who or what that is"
	switch action {
	case "whois":
		descriptor_b, err := red.Do("GET", entity+WHOIS_SUFFIX)
		if err != nil {
			log.Print(err)
			break
		}

		// Will be nil if not yet in the database
		if descriptor_b == nil {
			break
		}
		descriptor, ok := descriptor_b.([]byte)
		if !ok {
			fmt.Printf("ERROR: not a byte slice type: %+v", descriptor_b)
			break
		}

		response = fmt.Sprintf("%s is %s", entity, string(descriptor))
	default:
		if match[4] != "is" {
			log.Printf("action %s not supported", match[3])
			return
		}
		entity = match[5]
		descriptor := match[6]
		_, err := red.Do("SET", entity+WHOIS_SUFFIX, descriptor)
		if err != nil {
			log.Print(err)
			return
		}
		response = fmt.Sprintf("%s is %s", entity, descriptor)
	}

	c.PrivMsg(m.Receiver, response)
}

func init() {
	var err error
	red, err = redis.Dial(os.Getenv("REDIS_NETWORK"), os.Getenv("REDIS_ADDRESS"))
	if err != nil {
		log.Printf("ERROR: Failed to connect to redis database - reputation plugin will NOT load")
		panic(err)
	}
	_, err = red.Do("AUTH", os.Getenv("REDIS_PASSWORD"))
	if err != nil {
		panic(err)
	}
}
<commit_msg>Make whois append-only<commit_after>// This file is subject to a 1-clause BSD license.
// Its contents can be found in the enclosed LICENSE file.

package reputation

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/jteeuwen/ircb/plugin"
	"github.com/jteeuwen/ircb/proto"
	"log"
	"os"
	"regexp"
	"strings"
)

type RepChange struct {
	entity string
	delta  int
}

var reputation map[string]int

var reputationChanges chan RepChange

var red redis.Conn

const WHOIS_DB = "2"
const WHOIS_SUFFIX = "-whois" // TODO remove this hack

func init() { plugin.Register(New) }

type Plugin struct {
	*plugin.Base

	// Each entry holds a regex pattern which should be excluded
	// from the url-title-lookup.
	exclude []*regexp.Regexp

	// Pattern which recognizes s-expressions.
	// TODO use proper parser
	sexpr *regexp.Regexp
}

func New(profile string) plugin.Plugin {
	p := new(Plugin)
	p.Base = plugin.New(profile, "url")
	p.sexpr = regexp.MustCompile(`(\((whois) (.*?)\)|\((is) (.*?) (.*?)\))`)
	return p
}

// Init initializes the plugin. it loads configuration data and binds
// commands and protocol handlers.
func (p *Plugin) Load(c *proto.Client) (err error) {
	err = p.Base.Load(c)
	if err != nil {
		return
	}

	c.Bind(proto.CmdPrivMsg, func(c *proto.Client, m *proto.Message) {
		p.parseSexpr(c, m)
	})

	ini := p.LoadConfig()
	if ini == nil {
		return
	}

	s := ini.Section("exclude")
	list := s.List("url")
	p.exclude = make([]*regexp.Regexp, len(list))

	for i := range list {
		p.exclude[i], err = regexp.Compile(list[i])

		if err != nil {
			return
		}
	}

	return
}

// parseSexpr looks for s-expressions embedded in incoming messages.
// If they are valid s-expressions and not in the exclude list,
// we use them to fetch page titles from the internet.
func (p *Plugin) parseSexpr(c *proto.Client, m *proto.Message) {
	list := p.sexpr.FindAllStringSubmatch(m.Data, -1)
	if len(list) == 0 {
		return
	}

	for _, sexpr := range list {
		if !p.excluded(sexpr[0]) {
			go whoIs(c, m, sexpr)
		}
	}
}

// excluded returns true if the given url is part of the exclusion list.
func (p *Plugin) excluded(url string) bool {
	for _, excl := range p.exclude {
		if excl.MatchString(url) {
			return true
		}
	}

	return false
}

// fetchTitle attempts to retrieve the title element for a given url.
func whoIs(c *proto.Client, m *proto.Message, match []string) {
	log.Printf("Whois: %+v", match)
	entity := strings.ToLower(match[3])
	action := match[2]
	response := "no idea who or what that is"
	switch action {
	case "whois":
		descriptor_b, err := red.Do("GET", entity+WHOIS_SUFFIX)
		if err != nil {
			log.Print(err)
			break
		}

		// Will be nil if not yet in the database
		if descriptor_b == nil {
			break
		}
		descriptor, ok := descriptor_b.([]byte)
		if !ok {
			fmt.Printf("ERROR: not a byte slice type: %+v", descriptor_b)
			break
		}

		response = fmt.Sprintf("%s is %s", entity, string(descriptor)+"...")
	default:
		if match[4] != "is" {
			log.Printf("action %s not supported", match[3])
			return
		}
		entity = match[5]
		descriptor := match[6]
		_, err := red.Do("APPEND", entity+WHOIS_SUFFIX, descriptor+", ")
		if err != nil {
			log.Print(err)
			return
		}
		response = fmt.Sprintf("%s is %s", entity, descriptor)
	}

	c.PrivMsg(m.Receiver, response)
}

func init() {
	var err error
	red, err = redis.Dial(os.Getenv("REDIS_NETWORK"), os.Getenv("REDIS_ADDRESS"))
	if err != nil {
		log.Printf("ERROR: Failed to connect to redis database - reputation plugin will NOT load")
		panic(err)
	}
	_, err = red.Do("AUTH", os.Getenv("REDIS_PASSWORD"))
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 wechat-go Authors. All Rights Reserved.
MIT License

Copyright (c) 2017

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

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

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

package za

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/jmoiron/jsonq"
	"github.com/songtianyi/rrframework/logs"
	"github.com/songtianyi/wechat-go/wxweb"
)

var (
	ZA_CMD_PREFIX = "\u624e "
)

// Register plugin
func Register(session *wxweb.Session) {
	session.HandlerRegister.Add(wxweb.MSG_TEXT, wxweb.Handler(za_giphy), "za", ZA_CMD_PREFIX)
	if err := session.HandlerRegister.EnableByName("za"); err != nil {
		logs.Error(err)
	}
}

func getGifUrl(url string) (string, error) {
	// Load the URL
	res, e := http.Get(url)
	if e != nil {
		return "", e
	}

	if res == nil {
		return "", errors.New("Response is nil")
	}

	defer res.Body.Close()
	if res.Request == nil {
		return "", errors.New("Response.Request is nil")
	}

	frame := map[string]interface{}{}
	json.NewDecoder(res.Body).Decode(&frame)
	jq := jsonq.NewQuery(frame)

	gifUrl, e := jq.String("data", "images", "original", "url")
	if e != nil {
		return "", e
	}

	return gifUrl, nil
}

func za_giphy(session *wxweb.Session, msg *wxweb.ReceivedMessage) {
	if !strings.HasPrefix(msg.Content, ZA_CMD_PREFIX) {
		return
	}
	content := strings.TrimPrefix(msg.Content, ZA_CMD_PREFIX)
	uri := "http://api.giphy.com/v1/gifs/translate?api_key=dc6zaTOxFJmzC&rating=r&s=" + url.QueryEscape(content)
	logs.Info("giphy translate url: %v", uri)
	gif, err := getGifUrl(uri)
	if err != nil {
		logs.Error(err)
		return
	}
	resp, err := http.Get(gif)
	if err != nil {
		logs.Error(err)
		return
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	if msg.FromUserName == session.Bot.UserName {
		session.SendEmotionFromBytes(body, session.Bot.UserName, msg.ToUserName)
	} else {
		session.SendEmotionFromBytes(body, session.Bot.UserName, msg.FromUserName)
	}
}
<commit_msg>za: Refactor and improve the search precision of giphy.<commit_after>/*
Copyright 2017 wechat-go Authors. All Rights Reserved.
MIT License

Copyright (c) 2017

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

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

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

package za

import (
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/songtianyi/rrframework/logs"
	"github.com/songtianyi/wechat-go/wxweb"
)

var (
	ZA_CMD_PREFIX = "\u624e "
)

// Register plugin
func Register(session *wxweb.Session) {
	session.HandlerRegister.Add(wxweb.MSG_TEXT, wxweb.Handler(za_giphy), "za", ZA_CMD_PREFIX)
	if err := session.HandlerRegister.EnableByName("za"); err != nil {
		logs.Error(err)
	}
}

func za_giphy(session *wxweb.Session, msg *wxweb.ReceivedMessage) {
	if !strings.HasPrefix(msg.Content, ZA_CMD_PREFIX) {
		return
	}
	content := strings.TrimPrefix(msg.Content, ZA_CMD_PREFIX)

	search, err := http.Get("https://giphy.com/search/" + url.QueryEscape(content))
	if err != nil {
		logs.Error(err)
		return
	}
	defer search.Body.Close()
	searchBody, err := ioutil.ReadAll(search.Body)
	if err != nil {
		logs.Error(err)
		return
	}
	// Crappy RE for extracting image source from search.Body.
	// One caveat, the WeChat gif upload limit is 1MiB.  So the best
	// effor is to use the 'downsampled' gif.
	crappyRE := regexp.MustCompile(`"fixed_width_downsampled": {"url": "([^"']+)",`)
	matches := crappyRE.FindAll(searchBody, -1)
	if len(matches) < 1 {
		logs.Error("cannot get giphy image links")
		return
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	gifUrl := crappyRE.FindStringSubmatch(string(matches[r.Intn(len(matches))%20][:]))[1]
	logs.Info("gifUrl: ", gifUrl)
	resp, err := http.Get(gifUrl)
	if err != nil {
		logs.Error(err)
		return
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logs.Error(err)
		return
	}
	// XXX. Report sending error, normally caused by sending larger than 1MiB data.
	if msg.FromUserName == session.Bot.UserName {
		session.SendEmotionFromBytes(b, session.Bot.UserName, msg.ToUserName)
	} else {
		session.SendEmotionFromBytes(b, session.Bot.UserName, msg.FromUserName)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/yuin/gopher-lua"
	"strconv"
)

type Int64 int64

func (i Int64) register(L *lua.LState) {
	mt := L.NewTypeMetatable("int64")
	L.SetGlobal("int64", mt)
	// static attributes
	L.SetField(mt, "new", L.NewFunction(i.newInt64))
	// meta-methods
	L.SetFuncs(mt, map[string]lua.LGFunction{
		"__add":      i.add,
		"__sub":      i.sub,
		"__mul":      i.mul,
		"__div":      i.div,
		"__mod":      i.mod,
		"__unm":      i.unm,
		"__eq":       i.eq,
		"__lt":       i.lt,
		"__le":       i.le,
		"__tostring": i.tostring,
	})

	L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"bxor":   i.xor,
		"band":   i.and,
		"bor":    i.or,
		"lshift": i.lshift,
		"rshift": i.rshift,
	}))
}

func (i Int64) newInt64(L *lua.LState) int {
	x, err := strconv.ParseInt(L.CheckString(1), 0, 64)
	if err == nil {
		ud := L.NewUserData()
		ud.Value = Int64(x)
		L.SetMetatable(ud, L.GetTypeMetatable("int64"))
		L.Push(ud)
		return 1
	} else {
		L.ArgError(1, err.Error())
		return 0
	}
}

func (i Int64) add(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x + y })
}

func (i Int64) sub(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x - y })
}

func (i Int64) mul(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x * y })
}

func (i Int64) div(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x / y })
}

func (i Int64) mod(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x % y })
}

func (i Int64) unm(L *lua.LState) int {
	return i.unaryop(L, func(x int64) int64 { return -x })
}

func (i Int64) eq(L *lua.LState) int {
	return i.boolop(L, func(x, y int64) bool { return x == y })
}

func (i Int64) lt(L *lua.LState) int {
	return i.boolop(L, func(x, y int64) bool { return x < y })
}

func (i Int64) le(L *lua.LState) int {
	return i.boolop(L, func(x, y int64) bool { return x <= y })
}

func (i Int64) xor(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x ^ y })
}

func (i Int64) and(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x & y })
}

func (i Int64) or(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x | y })
}

func (i Int64) lshift(L *lua.LState) int {
	a := L.CheckUserData(1).Value.(Int64)
	shift := uint(L.CheckInt(2))
	ud := L.NewUserData()
	ud.Value = Int64(int64(a) << shift)
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) rshift(L *lua.LState) int {
	a := L.CheckUserData(1).Value.(Int64)
	shift := uint(L.CheckInt(2))
	ud := L.NewUserData()
	ud.Value = Int64(int64(a) >> shift)
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) tostring(L *lua.LState) int {
	x := L.CheckUserData(1).Value.(Int64)
	L.Push(lua.LString(fmt.Sprint(x)))
	return 1
}

func (i Int64) binop(L *lua.LState, f func(x, y int64) int64) int {
	a := L.CheckUserData(1).Value.(Int64)
	b := L.CheckUserData(2).Value.(Int64)
	ud := L.NewUserData()
	ud.Value = Int64(f(int64(a), int64(b)))
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) boolop(L *lua.LState, f func(x, y int64) bool) int {
	a := L.CheckUserData(1).Value.(Int64)
	b := L.CheckUserData(2).Value.(Int64)
	L.Push(lua.LBool(f(int64(a), int64(b))))
	return 1
}

func (i Int64) unaryop(L *lua.LState, f func(x int64) int64) int {
	a := L.CheckUserData(1).Value.(Int64)
	ud := L.NewUserData()
	ud.Value = Int64(f(int64(a)))
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}
<commit_msg>add support for bnot<commit_after>package main

import (
	"fmt"
	"github.com/yuin/gopher-lua"
	"strconv"
)

type Int64 int64

func (i Int64) register(L *lua.LState) {
	mt := L.NewTypeMetatable("int64")
	L.SetGlobal("int64", mt)
	// static attributes
	L.SetField(mt, "new", L.NewFunction(i.newInt64))
	// meta-methods
	L.SetFuncs(mt, map[string]lua.LGFunction{
		"__add":      i.add,
		"__sub":      i.sub,
		"__mul":      i.mul,
		"__div":      i.div,
		"__mod":      i.mod,
		"__unm":      i.unm,
		"__eq":       i.eq,
		"__lt":       i.lt,
		"__le":       i.le,
		"__tostring": i.tostring,
	})

	L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"bxor":   i.xor,
		"band":   i.and,
		"bor":    i.or,
		"bnot":   i.not,
		"lshift": i.lshift,
		"rshift": i.rshift,
	}))
}

func (i Int64) newInt64(L *lua.LState) int {
	x, err := strconv.ParseInt(L.CheckString(1), 0, 64)
	if err == nil {
		ud := L.NewUserData()
		ud.Value = Int64(x)
		L.SetMetatable(ud, L.GetTypeMetatable("int64"))
		L.Push(ud)
		return 1
	} else {
		L.ArgError(1, err.Error())
		return 0
	}
}

func (i Int64) add(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x + y })
}

func (i Int64) sub(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x - y })
}

func (i Int64) mul(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x * y })
}

func (i Int64) div(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x / y })
}

func (i Int64) mod(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x % y })
}

func (i Int64) unm(L *lua.LState) int {
	return i.unaryop(L, func(x int64) int64 { return -x })
}

func (i Int64) eq(L *lua.LState) int {
	return i.boolop(L, func(x, y int64) bool { return x == y })
}

func (i Int64) lt(L *lua.LState) int {
	return i.boolop(L, func(x, y int64) bool { return x < y })
}

func (i Int64) le(L *lua.LState) int {
	return i.boolop(L, func(x, y int64) bool { return x <= y })
}

func (i Int64) xor(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x ^ y })
}

func (i Int64) and(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x & y })
}

func (i Int64) or(L *lua.LState) int {
	return i.binop(L, func(x, y int64) int64 { return x | y })
}

func (i Int64) not(L *lua.LState) int {
	a := L.CheckUserData(1).Value.(Int64)
	ud := L.NewUserData()
	ud.Value = Int64(int64(a) ^ (1<<63 - 1))
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) lshift(L *lua.LState) int {
	a := L.CheckUserData(1).Value.(Int64)
	shift := uint(L.CheckInt(2))
	ud := L.NewUserData()
	ud.Value = Int64(int64(a) << shift)
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) rshift(L *lua.LState) int {
	a := L.CheckUserData(1).Value.(Int64)
	shift := uint(L.CheckInt(2))
	ud := L.NewUserData()
	ud.Value = Int64(int64(a) >> shift)
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) tostring(L *lua.LState) int {
	x := L.CheckUserData(1).Value.(Int64)
	L.Push(lua.LString(fmt.Sprint(x)))
	return 1
}

func (i Int64) binop(L *lua.LState, f func(x, y int64) int64) int {
	a := L.CheckUserData(1).Value.(Int64)
	b := L.CheckUserData(2).Value.(Int64)
	ud := L.NewUserData()
	ud.Value = Int64(f(int64(a), int64(b)))
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}

func (i Int64) boolop(L *lua.LState, f func(x, y int64) bool) int {
	a := L.CheckUserData(1).Value.(Int64)
	b := L.CheckUserData(2).Value.(Int64)
	L.Push(lua.LBool(f(int64(a), int64(b))))
	return 1
}

func (i Int64) unaryop(L *lua.LState, f func(x int64) int64) int {
	a := L.CheckUserData(1).Value.(Int64)
	ud := L.NewUserData()
	ud.Value = Int64(f(int64(a)))
	L.SetMetatable(ud, L.GetTypeMetatable("int64"))
	L.Push(ud)
	return 1
}
<|endoftext|>


<commit_before>package log

import (
	"io"
	"strings"

	"github.com/fatih/color"
)

type Logger struct {
	Stdout       io.Writer
	Stderr       io.Writer
	Level        Level
	DefaultLevel Level
	Color        bool
	MarshalFunc  func(interface{}) ([]byte, error)
}

func (l *Logger) Log(m *Message) {
	if m.Level > l.Level {
		return
	}
	var bs []byte
	if l.MarshalFunc != nil {
		var err error
		_m := *m
		if strings.HasSuffix(m.Text, "\n") {
			_m.Text = m.Text[:len(m.Text)-1]
		}
		bs, err = l.MarshalFunc(_m)
		if err != nil {
			panic(err)
		}
		bs = append(bs, '\n')
	} else {
		if l.Color {
			switch m.Level {
			case LevelCritical, LevelFatal:
				bs = []byte(color.RedString(m.String()))
			case LevelError:
				bs = []byte(color.RedString(m.String()))
			case LevelWarning:
				bs = []byte(color.YellowString(m.String()))
			case LevelInfo:
				bs = []byte(color.GreenString(m.String()))
			case LevelDebug:
				bs = []byte(m.String())
			default:
				bs = []byte(m.String())
			}
		} else {
			bs = []byte(m.String())
		}
	}
	if m.Level < LevelInfo {
		_, err := l.Stderr.Write(bs)
		if err != nil {
			panic(err)
		}
	} else {
		_, err := l.Stdout.Write(bs)
		if err != nil {
			panic(err)
		}
	}
}

// Write always returns 0, nil
func (l *Logger) Write(bs []byte) (int, error) {
	l.Log(NewMessage(l.DefaultLevel, string(bs)))
	return 0, nil
}
<commit_msg>return error instead of panic<commit_after>package log

import (
	"io"
	"strings"

	"github.com/fatih/color"
)

type LoggerFunc func(m *Message) error
type MarshalFunc func(interface{}) ([]byte, error)

type Logger struct {
	Stdout       io.Writer
	Stderr       io.Writer
	Level        Level
	DefaultLevel Level
	Color        bool
	Marshal      MarshalFunc
	LogFunc      LoggerFunc
}

func (l *Logger) Log(m *Message) error {
	if m.Level > l.Level {
		return nil
	}
	if l.LogFunc != nil {
		return l.LogFunc(m)
	}
	return l.DefaultLogFunc(m)
}

func (l *Logger) DefaultLogFunc(m *Message) error {
	var bs []byte
	if l.Marshal != nil {
		var err error
		_m := *m
		if strings.HasSuffix(m.Text, "\n") {
			_m.Text = m.Text[:len(m.Text)-1]
		}
		bs, err = l.Marshal(_m)
		if err != nil {
			return err
		}
		bs = append(bs, '\n')
	} else {
		if l.Color {
			switch m.Level {
			case LevelCritical, LevelFatal:
				bs = []byte(color.RedString(m.String()))
			case LevelError:
				bs = []byte(color.RedString(m.String()))
			case LevelWarning:
				bs = []byte(color.YellowString(m.String()))
			case LevelInfo:
				bs = []byte(color.GreenString(m.String()))
			case LevelDebug:
				bs = []byte(m.String())
			default:
				bs = []byte(m.String())
			}
		} else {
			bs = []byte(m.String())
		}
	}
	if m.Level < LevelInfo {
		_, err := l.Stderr.Write(bs)
		if err != nil {
			return err
		}
	} else {
		_, err := l.Stdout.Write(bs)
		if err != nil {
			return err
		}
	}
	return nil
}

// Write always returns 0, nil
func (l *Logger) Write(bs []byte) (int, error) {
	l.Log(NewMessage(l.DefaultLevel, string(bs)))
	return 0, nil
}
<|endoftext|>


<commit_before>package logger

import (
	"github.com/daviddengcn/go-colortext"
	"fmt"
	"time"
	"io"
	"os"
	"runtime"
)

//LogLevel
type Level int

//Logger
type Logger struct {
	Name string
	Level Level
	Colorful bool
	Output io.Writer
	CallStackDepth int
}

//LogLevel constants
const (
	LevelDebug = iota
	LevelInfo
	LevelWarning
	LevelError
	LevelFatal
)

const (
	defaultCallStackDepth = 2
)

//global logger
//
// The global logger is named "root" and it is a colorful logger with level DEBUG and log to os.Stdout
var gLogger = NewLogger(LevelDebug, true, "", nil)
var gLoggers = make(map[string]*Logger)
var gLogFileAndLine = true

//debug log
func Debug(fmt string, v ...interface{}) {
	gLogger.Debug(fmt, v...)
}

//info log
func Info(fmt string, v ...interface{}) {
	gLogger.Info(fmt, v...)
}

//warning log
func Warn(fmt string, v ...interface{}) {
	gLogger.Warn(fmt, v...)
}

//error log
func Error(fmt string, v ...interface{}) {
	gLogger.Error(fmt, v...)
}

//fatal error log
func Fatal(fmt string, v ...interface{}) {
	gLogger.Fatal(fmt, v...)
}

//set log level of the global logger
func SetLevel(level Level) {
	gLogger.Level = level
}

//set colorful of the global logger
func SetColorful(b bool) {
	gLogger.Colorful = b
}

//set where to write the log text of the global logger
func SetOutput(output io.Writer) {
	gLogger.Output = output
}

//set call stack depth of the global logger
func SetCallStackDepth(depth int) {
	gLogger.SetCallStackDepth(depth)
}

//global switch of file name and line number
//
//production applications should turn off file name and line number output to improve performance
func SetLogFileNameAndLineNumber(b bool) {
	gLogFileAndLine = b
}

//get a logger from logger pool, if cooresponding logger is not found, a simple logger is created and registered then return
//
//it is recommended that libraries call this function to initialize a library-inner-logger and pass library full name
//to the "name" argument
func Get(name string) *Logger {
	l := gLoggers[name]
	if(l == nil) {
		l = NewSimpleLogger(name)
		Register(l)
	}
	return l
}

//register a logger instance to logger pool
func Register(l * Logger)  {
	gLoggers[l.Name] = l
}

//create a logger
func NewLogger(level Level, colorful bool, name string, output io.Writer) *Logger {
	if(output == nil) {
		output = os.Stdout
	}
	if(name == "") {
		name = "root"
	}
	return &Logger{name, level, colorful, output, defaultCallStackDepth}
}

//create a named logger
//
//simple logger is a colorful logger with level DEBUG and log to os.Stdout
func NewSimpleLogger(name string) *Logger{
	return NewLogger(LevelDebug, true, name, nil)
}

//debug log
func (logger *Logger) Debug(fmt string, v ...interface{}) {
	if(logger.Level > LevelDebug) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Cyan, false, ct.None, false)
	}
	logger.logText(logger.Output, "TRACE:", fmt, v...)
}

//info log
func (logger *Logger) Info(fmt string, v ...interface{}) {
	if(logger.Level > LevelInfo) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Green, false, ct.None, false)
	}
	logger.logText(logger.Output, "INFO:", fmt, v...)
}

//warning log
func (logger *Logger) Warn(fmt string, v ...interface{}) {
	if(logger.Level > LevelWarning) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Yellow, false, ct.None, false)
	}
	logger.logText(logger.Output, "***WARN***:", fmt, v...)
}

//error log
func (logger *Logger) Error(fmt string, v ...interface{}) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	}
	logger.logText(logger.Output, "***ERROR***:", fmt, v...)
}

//fatal error log
func (logger *Logger) Fatal(fmt string, v ...interface{}) {
	if(logger.Level > LevelFatal) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	}
	logger.logText(logger.Output, "***FATAL***:", fmt, v...)
}

//set caller depth, this is used to print the file name and line number where calling log function
//
//set this value to 0 will cause no file name and line number outputting
//range of depth will be restricted from 0 to 10
func (logger *Logger) SetCallStackDepth(depth int) {
	if(depth < 0) {
		depth = 0
	}
	if(depth > 10) {
		depth = 10
	}
}

//******************************************************************************************//
//******************************************************************************************//
func (logger *Logger) logText(output io.Writer, levelFlagString, formatString string, v ...interface{}) {
	caller := " "
	if(gLogFileAndLine) {
		if (logger.CallStackDepth > 0) {
			_, file, line, ok := runtime.Caller(logger.CallStackDepth)
			if (ok == true) {
				caller = fmt.Sprintf(" - %s:%d -", file, line)
			}
		}
	}
	t := time.Now()
	fmt.Fprintf(output, "%d-%02d-%02d %02d:%02d:%02d.%03d [%s] %s%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond() / 1000000, logger.Name, levelFlagString, caller)
	fmt.Fprintf(output, formatString, v...)
	fmt.Fprintln(output)
}<commit_msg>bug fix: concurrency error<commit_after>package logger

import (
	"fmt"
	"io"
	"time"
	"os"
	"runtime"

	"github.com/daviddengcn/go-colortext"
)

//LogLevel
type Level int

//Logger
type Logger struct {
	// Logger name
	Name string
	// Log level
	Level Level
	// Colorful output, different log levels use different text colors
	Colorful bool
	// Where to write output stream
	Output io.Writer
	// Find which code, which file and which line number, called log functions
	CallStackDepth int
}

//LogLevel constants
const (
	LevelDebug = iota
	LevelInfo
	LevelWarning
	LevelError
	LevelFatal
)

const (
	defaultCallStackDepth = 2
)

//global logger
//
// The global logger is named "root" and it is a colorful logger with level DEBUG and log to os.Stdout
var gLogger = NewLogger(LevelDebug, true, "", nil)
var gLoggers = make(map[string]*Logger)
var gLogFileAndLine = true

//debug log
func Debug(fmt string, v ...interface{}) {
	gLogger.Debug(fmt, v...)
}

//info log
func Info(fmt string, v ...interface{}) {
	gLogger.Info(fmt, v...)
}

//warning log
func Warn(fmt string, v ...interface{}) {
	gLogger.Warn(fmt, v...)
}

//error log
func Error(fmt string, v ...interface{}) {
	gLogger.Error(fmt, v...)
}

//fatal error log
func Fatal(fmt string, v ...interface{}) {
	gLogger.Fatal(fmt, v...)
}

//set log level of the global logger
func SetLevel(level Level) {
	gLogger.Level = level
}

//set colorful of the global logger
func SetColorful(b bool) {
	gLogger.Colorful = b
}

//set where to write the log text of the global logger
func SetOutput(output io.Writer) {
	gLogger.Output = output
}

//set call stack depth of the global logger
func SetCallStackDepth(depth int) {
	gLogger.SetCallStackDepth(depth)
}

//global switch of file name and line number
//
//production applications should turn off file name and line number output to improve performance
func SetLogFileNameAndLineNumber(b bool) {
	gLogFileAndLine = b
}

//get a logger from logger pool, if cooresponding logger is not found, a simple logger is created and registered then return
//
//it is recommended that libraries call this function to initialize a library-inner-logger and pass library full name
//to the "name" argument
func Get(name string) *Logger {
	l := gLoggers[name]
	if(l == nil) {
		l = NewSimpleLogger(name)
		Register(l)
	}
	return l
}

//register a logger instance to logger pool
func Register(l * Logger)  {
	gLoggers[l.Name] = l
}

//create a logger
func NewLogger(level Level, colorful bool, name string, output io.Writer) *Logger {
	if(output == nil) {
		output = os.Stdout
	}
	if(name == "") {
		name = "root"
	}
	ret := &Logger{name, level, colorful, output, defaultCallStackDepth}
	return ret
}

//create a named logger
//
//simple logger is a colorful logger with level DEBUG and log to os.Stdout
func NewSimpleLogger(name string) *Logger{
	return NewLogger(LevelDebug, true, name, nil)
}

//debug log
func (logger *Logger) Debug(fmt string, v ...interface{}) {
	if(logger.Level > LevelDebug) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Cyan, false, ct.None, false)
	}
	logger.logText("TRACE:", fmt, v...)
}

//info log
func (logger *Logger) Info(fmt string, v ...interface{}) {
	if(logger.Level > LevelInfo) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Green, false, ct.None, false)
	}
	logger.logText("INFO:", fmt, v...)
}

//warning log
func (logger *Logger) Warn(fmt string, v ...interface{}) {
	if(logger.Level > LevelWarning) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Yellow, false, ct.None, false)
	}
	logger.logText("***WARN***:", fmt, v...)
}

//error log
func (logger *Logger) Error(fmt string, v ...interface{}) {
	if(logger.Level > LevelError) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	}
	logger.logText("***ERROR***:", fmt, v...)
}

//fatal error log
func (logger *Logger) Fatal(fmt string, v ...interface{}) {
	if(logger.Level > LevelFatal) {
		return
	}
	if(logger.Colorful == true) {
		ct.ChangeColor(ct.Red, false, ct.None, false)
	}
	logger.logText("***FATAL***:", fmt, v...)
}

//set caller depth, this is used to print the file name and line number where calling log function
//
//set this value to 0 will cause no file name and line number outputting
//range of depth will be restricted from 0 to 10
func (logger *Logger) SetCallStackDepth(depth int) {
	if(depth < 0) {
		depth = 0
	}
	if(depth > 10) {
		depth = 10
	}
}

//******************************************************************************************//
//******************************************************************************************//
func (logger *Logger) logText(levelFlagString, formatString string, v ...interface{}) {
	caller := " "
	if(gLogFileAndLine) {
		if (logger.CallStackDepth > 0) {
			_, file, line, ok := runtime.Caller(logger.CallStackDepth)
			if (ok == true) {
				caller = fmt.Sprintf(" - %s:%d -", file, line)
			}
		}
	}
	t := time.Now()
	s1 := fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d.%03d [%s] %s%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond() / 1000000, logger.Name, levelFlagString, caller)
	s2 := fmt.Sprintf(formatString, v...)
	fmt.Fprintf(logger.Output, "%s%s\n", s1, s2)
}<|endoftext|>