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

//go:generate stringer -type RoundingMode

package number

import (
	"math"
	"strconv"
)

// RoundingMode determines how a number is rounded to the desired precision.
type RoundingMode byte

const (
	ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant.
	ToNearestZero                     // towards the nearest integer, or towards zero if equidistant.
	ToNearestAway                     // towards the nearest integer, or away from zero if equidistant.
	ToPositiveInf                     // towards infinity
	ToNegativeInf                     // towards negative infinity
	ToZero                            // towards zero
	AwayFromZero                      // away from zero
	numModes
)

// A RoundingContext indicates how a number should be converted to digits.
type RoundingContext struct {
	Mode      RoundingMode
	Increment int32 // if > 0, round to Increment * 10^-Scale

	Precision int32 // maximum number of significant digits.
	Scale     int32 // maximum number of decimals after the dot.
}

const maxIntDigits = 20

// A Decimal represents floating point number represented in digits of the base
// in which a number is to be displayed. Digits represents a number [0, 1.0),
// and the absolute value represented by Decimal is Digits * 10^Exp.
// Leading and trailing zeros may be omitted and Exp may point outside a valid
// position in Digits.
//
// Examples:
//      Number     Decimal
//      12345      Digits: [1, 2, 3, 4, 5], Exp: 5
//      12.345     Digits: [1, 2, 3, 4, 5], Exp: 2
//      12000      Digits: [1, 2],          Exp: 5
//      0.00123    Digits: [1, 2, 3],       Exp: -2
type Decimal struct {
	Digits []byte // mantissa digits, big-endian
	Exp    int32  // exponent
	Neg    bool
	Inf    bool // Takes precedence over Digits and Exp.
	NaN    bool // Takes precedence over Inf.

	buf [maxIntDigits]byte
}

// normalize retuns a new Decimal with leading and trailing zeros removed.
func (d *Decimal) normalize() (n Decimal) {
	n = *d
	b := n.Digits
	// Strip leading zeros. Resulting number of digits is significant digits.
	for len(b) > 0 && b[0] == 0 {
		b = b[1:]
		n.Exp--
	}
	// Strip trailing zeros
	for len(b) > 0 && b[len(b)-1] == 0 {
		b = b[:len(b)-1]
	}
	if len(b) == 0 {
		n.Exp = 0
	}
	n.Digits = b
	return n
}

func (d *Decimal) clear() {
	b := d.Digits
	if b == nil {
		b = d.buf[:0]
	}
	*d = Decimal{}
	d.Digits = b[:0]
}

func (x *Decimal) String() string {
	if x.NaN {
		return "NaN"
	}
	var buf []byte
	if x.Neg {
		buf = append(buf, '-')
	}
	if x.Inf {
		buf = append(buf, "Inf"...)
		return string(buf)
	}
	if len(x.Digits) == 0 {
		return "0"
	}
	switch {
	case x.Exp <= 0:
		// 0.00ddd
		buf = append(buf, "0."...)
		buf = appendZeros(buf, -int(x.Exp))
		buf = appendDigits(buf, x.Digits)

	case /* 0 < */ int(x.Exp) < len(x.Digits):
		// dd.ddd
		buf = appendDigits(buf, x.Digits[:x.Exp])
		buf = append(buf, '.')
		buf = appendDigits(buf, x.Digits[x.Exp:])

	default: // len(x.Digits) <= x.Exp
		// ddd00
		buf = appendDigits(buf, x.Digits)
		buf = appendZeros(buf, int(x.Exp)-len(x.Digits))
	}
	return string(buf)
}

func appendDigits(buf []byte, digits []byte) []byte {
	for _, c := range digits {
		buf = append(buf, c+'0')
	}
	return buf
}

// appendZeros appends n 0 digits to buf and returns buf.
func appendZeros(buf []byte, n int) []byte {
	for ; n > 0; n-- {
		buf = append(buf, '0')
	}
	return buf
}

func (d *Decimal) round(mode RoundingMode, n int) {
	if n >= len(d.Digits) {
		return
	}
	// Make rounding decision: The result mantissa is truncated ("rounded down")
	// by default. Decide if we need to increment, or "round up", the (unsigned)
	// mantissa.
	inc := false
	switch mode {
	case ToNegativeInf:
		inc = d.Neg
	case ToPositiveInf:
		inc = !d.Neg
	case ToZero:
		// nothing to do
	case AwayFromZero:
		inc = true
	case ToNearestEven:
		inc = d.Digits[n] > 5 || d.Digits[n] == 5 &&
			(len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0)
	case ToNearestAway:
		inc = d.Digits[n] >= 5
	case ToNearestZero:
		inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1
	default:
		panic("unreachable")
	}
	if inc {
		d.roundUp(n)
	} else {
		d.roundDown(n)
	}
}

// roundFloat rounds a floating point number.
func (r RoundingMode) roundFloat(x float64) float64 {
	// Make rounding decision: The result mantissa is truncated ("rounded down")
	// by default. Decide if we need to increment, or "round up", the (unsigned)
	// mantissa.
	abs := x
	if x < 0 {
		abs = -x
	}
	i, f := math.Modf(abs)
	if f == 0.0 {
		return x
	}
	inc := false
	switch r {
	case ToNegativeInf:
		inc = x < 0
	case ToPositiveInf:
		inc = x >= 0
	case ToZero:
		// nothing to do
	case AwayFromZero:
		inc = true
	case ToNearestEven:
		// TODO: check overflow
		inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0
	case ToNearestAway:
		inc = f >= 0.5
	case ToNearestZero:
		inc = f > 0.5
	default:
		panic("unreachable")
	}
	if inc {
		i += 1
	}
	if abs != x {
		i = -i
	}
	return i
}

func (x *Decimal) roundUp(n int) {
	if n < 0 || n >= len(x.Digits) {
		return // nothing to do
	}
	// find first digit < 9
	for n > 0 && x.Digits[n-1] >= 9 {
		n--
	}

	if n == 0 {
		// all digits are 9s => round up to 1 and update exponent
		x.Digits[0] = 1 // ok since len(x.Digits) > n
		x.Digits = x.Digits[:1]
		x.Exp++
		return
	}
	x.Digits[n-1]++
	x.Digits = x.Digits[:n]
	// x already trimmed
}

func (x *Decimal) roundDown(n int) {
	if n < 0 || n >= len(x.Digits) {
		return // nothing to do
	}
	x.Digits = x.Digits[:n]
	trim(x)
}

// trim cuts off any trailing zeros from x's mantissa;
// they are meaningless for the value of x.
func trim(x *Decimal) {
	i := len(x.Digits)
	for i > 0 && x.Digits[i-1] == 0 {
		i--
	}
	x.Digits = x.Digits[:i]
	if i == 0 {
		x.Exp = 0
	}
}

// A Converter converts a number into decimals according to the given rounding
// criteria.
type Converter interface {
	Convert(d *Decimal, r *RoundingContext)
}

const (
	signed   = true
	unsigned = false
)

// Convert converts the given number to the decimal representation using the
// supplied RoundingContext.
func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
	switch f := number.(type) {
	case Converter:
		d.clear()
		f.Convert(d, r)
	case float32:
		d.ConvertFloat(r, float64(f), 32)
	case float64:
		d.ConvertFloat(r, f, 64)
	case int:
		d.ConvertInt(r, signed, uint64(f))
	case int8:
		d.ConvertInt(r, signed, uint64(f))
	case int16:
		d.ConvertInt(r, signed, uint64(f))
	case int32:
		d.ConvertInt(r, signed, uint64(f))
	case int64:
		d.ConvertInt(r, signed, uint64(f))
	case uint:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint8:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint16:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint32:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint64:
		d.ConvertInt(r, unsigned, f)

		// TODO:
		// case string: if produced by strconv, allows for easy arbitrary pos.
		// case reflect.Value:
		// case big.Float
		// case big.Int
		// case big.Rat?
		// catch underlyings using reflect or will this already be done by the
		//    message package?
	}
}

// ConvertInt converts an integer to decimals.
func (d *Decimal) ConvertInt(r *RoundingContext, signed bool, x uint64) {
	if r.Increment > 0 {
		// TODO: if uint64 is too large, fall back to float64
		if signed {
			d.ConvertFloat(r, float64(int64(x)), 64)
		} else {
			d.ConvertFloat(r, float64(x), 64)
		}
		return
	}
	d.clear()
	if signed && int64(x) < 0 {
		x = uint64(-int64(x))
		d.Neg = true
	}
	d.fillIntDigits(x)
	d.Exp = int32(len(d.Digits))
}

// ConvertFloat converts a floating point number to decimals.
func (d *Decimal) ConvertFloat(r *RoundingContext, x float64, size int) {
	d.clear()
	if math.IsNaN(x) {
		d.NaN = true
		return
	}
	abs := x
	if x < 0 {
		d.Neg = true
		abs = -x
	}
	if math.IsInf(abs, 1) {
		d.Inf = true
		return
	}
	// Simple case: decimal notation
	if r.Scale > 0 || r.Increment > 0 && r.Scale == 0 {
		if int(r.Scale) > len(scales) {
			x *= math.Pow(10, float64(r.Scale))
		} else {
			x *= scales[r.Scale]
		}
		if r.Increment > 0 {
			inc := float64(r.Increment)
			x /= float64(inc)
			x = r.Mode.roundFloat(x)
			x *= inc
		} else {
			x = r.Mode.roundFloat(x)
		}
		d.fillIntDigits(uint64(math.Abs(x)))
		d.Exp = int32(len(d.Digits)) - r.Scale
		return
	}

	// Nasty case (for non-decimal notation).
	// Asides from being inefficient, this result is also wrong as it will
	// apply ToNearestEven rounding regardless of the user setting.
	// TODO: expose functionality in strconv so we can avoid this hack.
	//   Something like this would work:
	//   AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)

	prec := int(r.Precision)
	if prec > 0 {
		prec--
	}
	b := strconv.AppendFloat(d.Digits, abs, 'e', prec, size)
	i := 0
	k := 0
	// No need to check i < len(b) as we always have an 'e'.
	for {
		if c := b[i]; '0' <= c && c <= '9' {
			b[k] = c - '0'
			k++
		} else if c != '.' {
			break
		}
		i++
	}
	d.Digits = b[:k]
	i += len("e")
	pSign := i
	exp := 0
	for i++; i < len(b); i++ {
		exp *= 10
		exp += int(b[i] - '0')
	}
	if b[pSign] == '-' {
		exp = -exp
	}
	d.Exp = int32(exp) + 1
}

func (d *Decimal) fillIntDigits(x uint64) {
	if cap(d.Digits) < maxIntDigits {
		d.Digits = d.buf[:]
	} else {
		d.Digits = d.buf[:maxIntDigits]
	}
	i := 0
	for ; x > 0; x /= 10 {
		d.Digits[i] = byte(x % 10)
		i++
	}
	d.Digits = d.Digits[:i]
	for p := 0; p < i; p++ {
		i--
		d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p]
	}
}

var scales [70]float64

func init() {
	x := 1.0
	for i := range scales {
		scales[i] = x
		x *= 10
	}
}
<commit_msg>internal/number: enable the fast case more often<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate stringer -type RoundingMode

package number

import (
	"math"
	"strconv"
)

// RoundingMode determines how a number is rounded to the desired precision.
type RoundingMode byte

const (
	ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant.
	ToNearestZero                     // towards the nearest integer, or towards zero if equidistant.
	ToNearestAway                     // towards the nearest integer, or away from zero if equidistant.
	ToPositiveInf                     // towards infinity
	ToNegativeInf                     // towards negative infinity
	ToZero                            // towards zero
	AwayFromZero                      // away from zero
	numModes
)

// A RoundingContext indicates how a number should be converted to digits.
type RoundingContext struct {
	Mode      RoundingMode
	Increment int32 // if > 0, round to Increment * 10^-Scale

	Precision int32 // maximum number of significant digits.
	Scale     int32 // maximum number of decimals after the dot.
}

const maxIntDigits = 20

// A Decimal represents floating point number represented in digits of the base
// in which a number is to be displayed. Digits represents a number [0, 1.0),
// and the absolute value represented by Decimal is Digits * 10^Exp.
// Leading and trailing zeros may be omitted and Exp may point outside a valid
// position in Digits.
//
// Examples:
//      Number     Decimal
//      12345      Digits: [1, 2, 3, 4, 5], Exp: 5
//      12.345     Digits: [1, 2, 3, 4, 5], Exp: 2
//      12000      Digits: [1, 2],          Exp: 5
//      0.00123    Digits: [1, 2, 3],       Exp: -2
type Decimal struct {
	Digits []byte // mantissa digits, big-endian
	Exp    int32  // exponent
	Neg    bool
	Inf    bool // Takes precedence over Digits and Exp.
	NaN    bool // Takes precedence over Inf.

	buf [maxIntDigits]byte
}

// normalize retuns a new Decimal with leading and trailing zeros removed.
func (d *Decimal) normalize() (n Decimal) {
	n = *d
	b := n.Digits
	// Strip leading zeros. Resulting number of digits is significant digits.
	for len(b) > 0 && b[0] == 0 {
		b = b[1:]
		n.Exp--
	}
	// Strip trailing zeros
	for len(b) > 0 && b[len(b)-1] == 0 {
		b = b[:len(b)-1]
	}
	if len(b) == 0 {
		n.Exp = 0
	}
	n.Digits = b
	return n
}

func (d *Decimal) clear() {
	b := d.Digits
	if b == nil {
		b = d.buf[:0]
	}
	*d = Decimal{}
	d.Digits = b[:0]
}

func (x *Decimal) String() string {
	if x.NaN {
		return "NaN"
	}
	var buf []byte
	if x.Neg {
		buf = append(buf, '-')
	}
	if x.Inf {
		buf = append(buf, "Inf"...)
		return string(buf)
	}
	if len(x.Digits) == 0 {
		return "0"
	}
	switch {
	case x.Exp <= 0:
		// 0.00ddd
		buf = append(buf, "0."...)
		buf = appendZeros(buf, -int(x.Exp))
		buf = appendDigits(buf, x.Digits)

	case /* 0 < */ int(x.Exp) < len(x.Digits):
		// dd.ddd
		buf = appendDigits(buf, x.Digits[:x.Exp])
		buf = append(buf, '.')
		buf = appendDigits(buf, x.Digits[x.Exp:])

	default: // len(x.Digits) <= x.Exp
		// ddd00
		buf = appendDigits(buf, x.Digits)
		buf = appendZeros(buf, int(x.Exp)-len(x.Digits))
	}
	return string(buf)
}

func appendDigits(buf []byte, digits []byte) []byte {
	for _, c := range digits {
		buf = append(buf, c+'0')
	}
	return buf
}

// appendZeros appends n 0 digits to buf and returns buf.
func appendZeros(buf []byte, n int) []byte {
	for ; n > 0; n-- {
		buf = append(buf, '0')
	}
	return buf
}

func (d *Decimal) round(mode RoundingMode, n int) {
	if n >= len(d.Digits) {
		return
	}
	// Make rounding decision: The result mantissa is truncated ("rounded down")
	// by default. Decide if we need to increment, or "round up", the (unsigned)
	// mantissa.
	inc := false
	switch mode {
	case ToNegativeInf:
		inc = d.Neg
	case ToPositiveInf:
		inc = !d.Neg
	case ToZero:
		// nothing to do
	case AwayFromZero:
		inc = true
	case ToNearestEven:
		inc = d.Digits[n] > 5 || d.Digits[n] == 5 &&
			(len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0)
	case ToNearestAway:
		inc = d.Digits[n] >= 5
	case ToNearestZero:
		inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1
	default:
		panic("unreachable")
	}
	if inc {
		d.roundUp(n)
	} else {
		d.roundDown(n)
	}
}

// roundFloat rounds a floating point number.
func (r RoundingMode) roundFloat(x float64) float64 {
	// Make rounding decision: The result mantissa is truncated ("rounded down")
	// by default. Decide if we need to increment, or "round up", the (unsigned)
	// mantissa.
	abs := x
	if x < 0 {
		abs = -x
	}
	i, f := math.Modf(abs)
	if f == 0.0 {
		return x
	}
	inc := false
	switch r {
	case ToNegativeInf:
		inc = x < 0
	case ToPositiveInf:
		inc = x >= 0
	case ToZero:
		// nothing to do
	case AwayFromZero:
		inc = true
	case ToNearestEven:
		// TODO: check overflow
		inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0
	case ToNearestAway:
		inc = f >= 0.5
	case ToNearestZero:
		inc = f > 0.5
	default:
		panic("unreachable")
	}
	if inc {
		i += 1
	}
	if abs != x {
		i = -i
	}
	return i
}

func (x *Decimal) roundUp(n int) {
	if n < 0 || n >= len(x.Digits) {
		return // nothing to do
	}
	// find first digit < 9
	for n > 0 && x.Digits[n-1] >= 9 {
		n--
	}

	if n == 0 {
		// all digits are 9s => round up to 1 and update exponent
		x.Digits[0] = 1 // ok since len(x.Digits) > n
		x.Digits = x.Digits[:1]
		x.Exp++
		return
	}
	x.Digits[n-1]++
	x.Digits = x.Digits[:n]
	// x already trimmed
}

func (x *Decimal) roundDown(n int) {
	if n < 0 || n >= len(x.Digits) {
		return // nothing to do
	}
	x.Digits = x.Digits[:n]
	trim(x)
}

// trim cuts off any trailing zeros from x's mantissa;
// they are meaningless for the value of x.
func trim(x *Decimal) {
	i := len(x.Digits)
	for i > 0 && x.Digits[i-1] == 0 {
		i--
	}
	x.Digits = x.Digits[:i]
	if i == 0 {
		x.Exp = 0
	}
}

// A Converter converts a number into decimals according to the given rounding
// criteria.
type Converter interface {
	Convert(d *Decimal, r *RoundingContext)
}

const (
	signed   = true
	unsigned = false
)

// Convert converts the given number to the decimal representation using the
// supplied RoundingContext.
func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
	switch f := number.(type) {
	case Converter:
		d.clear()
		f.Convert(d, r)
	case float32:
		d.ConvertFloat(r, float64(f), 32)
	case float64:
		d.ConvertFloat(r, f, 64)
	case int:
		d.ConvertInt(r, signed, uint64(f))
	case int8:
		d.ConvertInt(r, signed, uint64(f))
	case int16:
		d.ConvertInt(r, signed, uint64(f))
	case int32:
		d.ConvertInt(r, signed, uint64(f))
	case int64:
		d.ConvertInt(r, signed, uint64(f))
	case uint:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint8:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint16:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint32:
		d.ConvertInt(r, unsigned, uint64(f))
	case uint64:
		d.ConvertInt(r, unsigned, f)

		// TODO:
		// case string: if produced by strconv, allows for easy arbitrary pos.
		// case reflect.Value:
		// case big.Float
		// case big.Int
		// case big.Rat?
		// catch underlyings using reflect or will this already be done by the
		//    message package?
	}
}

// ConvertInt converts an integer to decimals.
func (d *Decimal) ConvertInt(r *RoundingContext, signed bool, x uint64) {
	if r.Increment > 0 {
		// TODO: if uint64 is too large, fall back to float64
		if signed {
			d.ConvertFloat(r, float64(int64(x)), 64)
		} else {
			d.ConvertFloat(r, float64(x), 64)
		}
		return
	}
	d.clear()
	if signed && int64(x) < 0 {
		x = uint64(-int64(x))
		d.Neg = true
	}
	d.fillIntDigits(x)
	d.Exp = int32(len(d.Digits))
}

// ConvertFloat converts a floating point number to decimals.
func (d *Decimal) ConvertFloat(r *RoundingContext, x float64, size int) {
	d.clear()
	if math.IsNaN(x) {
		d.NaN = true
		return
	}
	abs := x
	if x < 0 {
		d.Neg = true
		abs = -x
	}
	if math.IsInf(abs, 1) {
		d.Inf = true
		return
	}
	// Simple case: decimal notation
	if r.Scale > 0 || r.Increment > 0 || r.Precision == 0 {
		if int(r.Scale) > len(scales) {
			x *= math.Pow(10, float64(r.Scale))
		} else {
			x *= scales[r.Scale]
		}
		if r.Increment > 0 {
			inc := float64(r.Increment)
			x /= float64(inc)
			x = r.Mode.roundFloat(x)
			x *= inc
		} else {
			x = r.Mode.roundFloat(x)
		}
		d.fillIntDigits(uint64(math.Abs(x)))
		d.Exp = int32(len(d.Digits)) - r.Scale
		return
	}

	// Nasty case (for non-decimal notation).
	// Asides from being inefficient, this result is also wrong as it will
	// apply ToNearestEven rounding regardless of the user setting.
	// TODO: expose functionality in strconv so we can avoid this hack.
	//   Something like this would work:
	//   AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
	// TODO: This only supports the nearest even rounding mode.

	prec := int(r.Precision)
	if prec > 0 {
		prec--
	}
	b := strconv.AppendFloat(d.Digits, abs, 'e', prec, size)
	i := 0
	k := 0
	// No need to check i < len(b) as we always have an 'e'.
	for {
		if c := b[i]; '0' <= c && c <= '9' {
			b[k] = c - '0'
			k++
		} else if c != '.' {
			break
		}
		i++
	}
	d.Digits = b[:k]
	i += len("e")
	pSign := i
	exp := 0
	for i++; i < len(b); i++ {
		exp *= 10
		exp += int(b[i] - '0')
	}
	if b[pSign] == '-' {
		exp = -exp
	}
	d.Exp = int32(exp) + 1
}

func (d *Decimal) fillIntDigits(x uint64) {
	if cap(d.Digits) < maxIntDigits {
		d.Digits = d.buf[:]
	} else {
		d.Digits = d.buf[:maxIntDigits]
	}
	i := 0
	for ; x > 0; x /= 10 {
		d.Digits[i] = byte(x % 10)
		i++
	}
	d.Digits = d.Digits[:i]
	for p := 0; p < i; p++ {
		i--
		d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p]
	}
}

var scales [70]float64

func init() {
	x := 1.0
	for i := range scales {
		scales[i] = x
		x *= 10
	}
}
<|endoftext|>


<commit_before>package interpreter

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"sync"
	"syscall"

	"github.com/zetamatta/go-findfile"

	"../dos"
)

const FLAG_AMP2NEWCONSOLE = false

var WildCardExpansionAlways = false

type CommandNotFound struct {
	Name string
	Err  error
}

// from "TDM-GCC-64/x86_64-w64-mingw32/include/winbase.h"
const (
	CREATE_NEW_CONSOLE       = 0x10
	CREATE_NEW_PROCESS_GROUP = 0x200
)

func (this CommandNotFound) Stringer() string {
	return fmt.Sprintf("'%s' is not recognized as an internal or external command,\noperable program or batch file", this.Name)
}

func (this CommandNotFound) Error() string {
	return this.Stringer()
}

type Interpreter struct {
	exec.Cmd
	Stdio        [3]*os.File
	HookCount    int
	Tag          interface{}
	PipeSeq      [2]uint
	IsBackGround bool
	RawArgs      []string

	OnClone func(*Interpreter) error
	Closers []io.Closer
}

func (this *Interpreter) Close() {
	if this.Closers != nil {
		for _, c := range this.Closers {
			c.Close()
		}
		this.Closers = nil
	}
}

func New() *Interpreter {
	this := Interpreter{
		Stdio: [3]*os.File{os.Stdin, os.Stdout, os.Stderr},
	}
	this.Stdin = os.Stdin
	this.Stdout = os.Stdout
	this.Stderr = os.Stderr
	this.PipeSeq[0] = pipeSeq
	this.PipeSeq[1] = 0
	this.Tag = nil
	return &this
}

func (this *Interpreter) SetStdin(f *os.File) {
	this.Stdio[0] = f
	this.Stdin = f
}
func (this *Interpreter) SetStdout(f *os.File) {
	this.Stdio[1] = f
	this.Stdout = f
}
func (this *Interpreter) SetStderr(f *os.File) {
	this.Stdio[2] = f
	this.Stderr = f
}

func (this *Interpreter) Clone() (*Interpreter, error) {
	rv := new(Interpreter)
	rv.Stdio[0] = this.Stdio[0]
	rv.Stdio[1] = this.Stdio[1]
	rv.Stdio[2] = this.Stdio[2]
	rv.Stdin = this.Stdin
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.HookCount = this.HookCount
	rv.Tag = this.Tag
	rv.PipeSeq = this.PipeSeq
	rv.Closers = nil
	rv.OnClone = this.OnClone
	if this.OnClone != nil {
		if err := this.OnClone(rv); err != nil {
			return nil, err
		}
	}
	return rv, nil
}

type ArgsHookT func(it *Interpreter, args []string) ([]string, error)

var argsHook = func(it *Interpreter, args []string) ([]string, error) {
	return args, nil
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(*Interpreter) (int, bool, error)

var hook = func(*Interpreter) (int, bool, error) {
	return 0, false, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var OnCommandNotFound = func(this *Interpreter, err error) error {
	err = &CommandNotFound{this.Args[0], err}
	return err
}

var ErrorLevelStr string

func nvl(a *os.File, b *os.File) *os.File {
	if a != nil {
		return a
	} else {
		return b
	}
}

func (this *Interpreter) spawnvp_noerrmsg() (int, error) {
	// command is empty.
	if len(this.Args) <= 0 {
		return 0, nil
	}
	if dbg {
		print("spawnvp_noerrmsg('", this.Args[0], "')\n")
	}

	// aliases and lua-commands
	if errorlevel, done, err := hook(this); done || err != nil {
		return errorlevel, err
	}

	// command not found hook
	var err error
	this.Path = dos.LookPath(this.Args[0])
	if this.Path == "" {
		return 255, OnCommandNotFound(this, err)
	}
	if dbg {
		print("exec.LookPath(", this.Args[0], ")==", this.Path, "\n")
	}

	if WildCardExpansionAlways {
		this.Args = findfile.Globs(this.Args)
	}

	// executable-file
	if FLAG_AMP2NEWCONSOLE {
		if this.SysProcAttr != nil && (this.SysProcAttr.CreationFlags&CREATE_NEW_CONSOLE) != 0 {
			err = this.Start()
			return 0, err
		}
	}
	err = this.Run()

	errorlevel, errorlevelOk := dos.GetErrorLevel(&this.Cmd)
	if errorlevelOk {
		return errorlevel, err
	} else {
		return 255, err
	}
}

type AlreadyReportedError struct {
	Err error
}

func (this AlreadyReportedError) Error() string {
	return ""
}

func IsAlreadyReported(err error) bool {
	_, ok := err.(AlreadyReportedError)
	return ok
}

func (this *Interpreter) Spawnvp() (int, error) {
	errorlevel, err := this.spawnvp_noerrmsg()
	if err != nil && err != io.EOF && !IsAlreadyReported(err) {
		if dbg {
			val := reflect.ValueOf(err)
			fmt.Fprintf(this.Stderr, "error-type=%s\n", val.Type())
		}
		fmt.Fprintln(this.Stderr, err.Error())
		err = AlreadyReportedError{err}
	}
	return errorlevel, err
}

var pipeSeq uint = 0

func (this *Interpreter) Interpret(text string) (errorlevel int, err error) {
	if dbg {
		print("Interpret('", text, "')\n")
	}
	if this == nil {
		return 255, errors.New("Fatal Error: Interpret: instance is nil")
	}
	errorlevel = 0
	err = nil

	statements, statementsErr := Parse(text)
	if statementsErr != nil {
		if dbg {
			print("Parse Error:", statementsErr.Error(), "\n")
		}
		return 0, statementsErr
	}
	if argsHook != nil {
		if dbg {
			print("call argsHook\n")
		}
		for _, pipeline := range statements {
			for _, state := range pipeline {
				state.Args, err = argsHook(this, state.Args)
				if err != nil {
					return 255, err
				}
			}
		}
		if dbg {
			print("done argsHook\n")
		}
	}
	for _, pipeline := range statements {
		for i, state := range pipeline {
			if state.Term == "|" && (i+1 >= len(pipeline) || len(pipeline[i+1].Args) <= 0) {
				return 255, errors.New("The syntax of the command is incorrect.")
			}
		}
	}

	for _, pipeline := range statements {

		var pipeIn *os.File = nil
		pipeSeq++
		isBackGround := this.IsBackGround
		for _, state := range pipeline {
			if state.Term == "&" {
				isBackGround = true
				break
			}
		}
		var wg sync.WaitGroup
		for i, state := range pipeline {
			if dbg {
				print(i, ": pipeline loop(", state.Args[0], ")\n")
			}
			cmd := new(Interpreter)
			cmd.PipeSeq[0] = pipeSeq
			cmd.PipeSeq[1] = uint(1 + i)
			cmd.IsBackGround = isBackGround
			cmd.Tag = this.Tag
			cmd.HookCount = this.HookCount
			cmd.SetStdin(nvl(this.Stdio[0], os.Stdin))
			cmd.SetStdout(nvl(this.Stdio[1], os.Stdout))
			cmd.SetStderr(nvl(this.Stdio[2], os.Stderr))
			cmd.OnClone = this.OnClone
			if this.OnClone != nil {
				if err := this.OnClone(cmd); err != nil {
					return 255, err
				}
			}

			if pipeIn != nil {
				cmd.SetStdin(pipeIn)
				cmd.Closers = append(cmd.Closers, pipeIn)
				pipeIn = nil
			}

			if state.Term[0] == '|' {
				var pipeOut *os.File
				pipeIn, pipeOut, err = os.Pipe()
				cmd.SetStdout(pipeOut)
				if state.Term == "|&" {
					cmd.SetStderr(pipeOut)
				}
				cmd.Closers = append(cmd.Closers, pipeOut)
			}

			for _, red := range state.Redirect {
				var fd *os.File
				fd, err = red.OpenOn(cmd)
				if err != nil {
					return 0, err
				}
				defer fd.Close()
			}

			cmd.Args = state.Args
			cmd.RawArgs = state.RawArgs
			if i > 0 {
				cmd.IsBackGround = true
			}
			if i == len(pipeline)-1 && state.Term != "&" {
				errorlevel, err = cmd.Spawnvp()
				ErrorLevelStr = fmt.Sprintf("%d", errorlevel)
				cmd.Close()
			} else {
				if !isBackGround {
					wg.Add(1)
				}
				go func(cmd1 *Interpreter) {
					if isBackGround {
						if FLAG_AMP2NEWCONSOLE {
							if len(pipeline) == 1 {
								cmd1.SysProcAttr = &syscall.SysProcAttr{
									CreationFlags: CREATE_NEW_CONSOLE |
										CREATE_NEW_PROCESS_GROUP,
								}
							}
						}
					} else {
						defer wg.Done()
					}
					cmd1.Spawnvp()
					cmd1.Close()
				}(cmd)
			}
		}
		if !isBackGround {
			wg.Wait()
			if len(pipeline) > 0 {
				switch pipeline[len(pipeline)-1].Term {
				case "&&":
					if errorlevel != 0 {
						return errorlevel, nil
					}
				case "||":
					if errorlevel == 0 {
						return errorlevel, nil
					}
				}
			}
		}
	}
	return
}
<commit_msg>Fix: trivial miss on Command not found<commit_after>package interpreter

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"reflect"
	"sync"
	"syscall"

	"github.com/zetamatta/go-findfile"

	"../dos"
)

const FLAG_AMP2NEWCONSOLE = false

var WildCardExpansionAlways = false

type CommandNotFound struct {
	Name string
	Err  error
}

// from "TDM-GCC-64/x86_64-w64-mingw32/include/winbase.h"
const (
	CREATE_NEW_CONSOLE       = 0x10
	CREATE_NEW_PROCESS_GROUP = 0x200
)

func (this CommandNotFound) Stringer() string {
	return fmt.Sprintf("'%s' is not recognized as an internal or external command,\noperable program or batch file", this.Name)
}

func (this CommandNotFound) Error() string {
	return this.Stringer()
}

type Interpreter struct {
	exec.Cmd
	Stdio        [3]*os.File
	HookCount    int
	Tag          interface{}
	PipeSeq      [2]uint
	IsBackGround bool
	RawArgs      []string

	OnClone func(*Interpreter) error
	Closers []io.Closer
}

func (this *Interpreter) Close() {
	if this.Closers != nil {
		for _, c := range this.Closers {
			c.Close()
		}
		this.Closers = nil
	}
}

func New() *Interpreter {
	this := Interpreter{
		Stdio: [3]*os.File{os.Stdin, os.Stdout, os.Stderr},
	}
	this.Stdin = os.Stdin
	this.Stdout = os.Stdout
	this.Stderr = os.Stderr
	this.PipeSeq[0] = pipeSeq
	this.PipeSeq[1] = 0
	this.Tag = nil
	return &this
}

func (this *Interpreter) SetStdin(f *os.File) {
	this.Stdio[0] = f
	this.Stdin = f
}
func (this *Interpreter) SetStdout(f *os.File) {
	this.Stdio[1] = f
	this.Stdout = f
}
func (this *Interpreter) SetStderr(f *os.File) {
	this.Stdio[2] = f
	this.Stderr = f
}

func (this *Interpreter) Clone() (*Interpreter, error) {
	rv := new(Interpreter)
	rv.Stdio[0] = this.Stdio[0]
	rv.Stdio[1] = this.Stdio[1]
	rv.Stdio[2] = this.Stdio[2]
	rv.Stdin = this.Stdin
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.HookCount = this.HookCount
	rv.Tag = this.Tag
	rv.PipeSeq = this.PipeSeq
	rv.Closers = nil
	rv.OnClone = this.OnClone
	if this.OnClone != nil {
		if err := this.OnClone(rv); err != nil {
			return nil, err
		}
	}
	return rv, nil
}

type ArgsHookT func(it *Interpreter, args []string) ([]string, error)

var argsHook = func(it *Interpreter, args []string) ([]string, error) {
	return args, nil
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(*Interpreter) (int, bool, error)

var hook = func(*Interpreter) (int, bool, error) {
	return 0, false, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var OnCommandNotFound = func(this *Interpreter, err error) error {
	err = &CommandNotFound{this.Args[0], err}
	return err
}

var ErrorLevelStr string

func nvl(a *os.File, b *os.File) *os.File {
	if a != nil {
		return a
	} else {
		return b
	}
}

func (this *Interpreter) spawnvp_noerrmsg() (int, error) {
	// command is empty.
	if len(this.Args) <= 0 {
		return 0, nil
	}
	if dbg {
		print("spawnvp_noerrmsg('", this.Args[0], "')\n")
	}

	// aliases and lua-commands
	if errorlevel, done, err := hook(this); done || err != nil {
		return errorlevel, err
	}

	// command not found hook
	var err error
	this.Path = dos.LookPath(this.Args[0])
	if this.Path == "" {
		return 255, OnCommandNotFound(this, os.ErrNotExist)
	}
	if dbg {
		print("exec.LookPath(", this.Args[0], ")==", this.Path, "\n")
	}

	if WildCardExpansionAlways {
		this.Args = findfile.Globs(this.Args)
	}

	// executable-file
	if FLAG_AMP2NEWCONSOLE {
		if this.SysProcAttr != nil && (this.SysProcAttr.CreationFlags&CREATE_NEW_CONSOLE) != 0 {
			err = this.Start()
			return 0, err
		}
	}
	err = this.Run()

	errorlevel, errorlevelOk := dos.GetErrorLevel(&this.Cmd)
	if errorlevelOk {
		return errorlevel, err
	} else {
		return 255, err
	}
}

type AlreadyReportedError struct {
	Err error
}

func (this AlreadyReportedError) Error() string {
	return ""
}

func IsAlreadyReported(err error) bool {
	_, ok := err.(AlreadyReportedError)
	return ok
}

func (this *Interpreter) Spawnvp() (int, error) {
	errorlevel, err := this.spawnvp_noerrmsg()
	if err != nil && err != io.EOF && !IsAlreadyReported(err) {
		if dbg {
			val := reflect.ValueOf(err)
			fmt.Fprintf(this.Stderr, "error-type=%s\n", val.Type())
		}
		fmt.Fprintln(this.Stderr, err.Error())
		err = AlreadyReportedError{err}
	}
	return errorlevel, err
}

var pipeSeq uint = 0

func (this *Interpreter) Interpret(text string) (errorlevel int, err error) {
	if dbg {
		print("Interpret('", text, "')\n")
	}
	if this == nil {
		return 255, errors.New("Fatal Error: Interpret: instance is nil")
	}
	errorlevel = 0
	err = nil

	statements, statementsErr := Parse(text)
	if statementsErr != nil {
		if dbg {
			print("Parse Error:", statementsErr.Error(), "\n")
		}
		return 0, statementsErr
	}
	if argsHook != nil {
		if dbg {
			print("call argsHook\n")
		}
		for _, pipeline := range statements {
			for _, state := range pipeline {
				state.Args, err = argsHook(this, state.Args)
				if err != nil {
					return 255, err
				}
			}
		}
		if dbg {
			print("done argsHook\n")
		}
	}
	for _, pipeline := range statements {
		for i, state := range pipeline {
			if state.Term == "|" && (i+1 >= len(pipeline) || len(pipeline[i+1].Args) <= 0) {
				return 255, errors.New("The syntax of the command is incorrect.")
			}
		}
	}

	for _, pipeline := range statements {

		var pipeIn *os.File = nil
		pipeSeq++
		isBackGround := this.IsBackGround
		for _, state := range pipeline {
			if state.Term == "&" {
				isBackGround = true
				break
			}
		}
		var wg sync.WaitGroup
		for i, state := range pipeline {
			if dbg {
				print(i, ": pipeline loop(", state.Args[0], ")\n")
			}
			cmd := new(Interpreter)
			cmd.PipeSeq[0] = pipeSeq
			cmd.PipeSeq[1] = uint(1 + i)
			cmd.IsBackGround = isBackGround
			cmd.Tag = this.Tag
			cmd.HookCount = this.HookCount
			cmd.SetStdin(nvl(this.Stdio[0], os.Stdin))
			cmd.SetStdout(nvl(this.Stdio[1], os.Stdout))
			cmd.SetStderr(nvl(this.Stdio[2], os.Stderr))
			cmd.OnClone = this.OnClone
			if this.OnClone != nil {
				if err := this.OnClone(cmd); err != nil {
					return 255, err
				}
			}

			if pipeIn != nil {
				cmd.SetStdin(pipeIn)
				cmd.Closers = append(cmd.Closers, pipeIn)
				pipeIn = nil
			}

			if state.Term[0] == '|' {
				var pipeOut *os.File
				pipeIn, pipeOut, err = os.Pipe()
				cmd.SetStdout(pipeOut)
				if state.Term == "|&" {
					cmd.SetStderr(pipeOut)
				}
				cmd.Closers = append(cmd.Closers, pipeOut)
			}

			for _, red := range state.Redirect {
				var fd *os.File
				fd, err = red.OpenOn(cmd)
				if err != nil {
					return 0, err
				}
				defer fd.Close()
			}

			cmd.Args = state.Args
			cmd.RawArgs = state.RawArgs
			if i > 0 {
				cmd.IsBackGround = true
			}
			if i == len(pipeline)-1 && state.Term != "&" {
				errorlevel, err = cmd.Spawnvp()
				ErrorLevelStr = fmt.Sprintf("%d", errorlevel)
				cmd.Close()
			} else {
				if !isBackGround {
					wg.Add(1)
				}
				go func(cmd1 *Interpreter) {
					if isBackGround {
						if FLAG_AMP2NEWCONSOLE {
							if len(pipeline) == 1 {
								cmd1.SysProcAttr = &syscall.SysProcAttr{
									CreationFlags: CREATE_NEW_CONSOLE |
										CREATE_NEW_PROCESS_GROUP,
								}
							}
						}
					} else {
						defer wg.Done()
					}
					cmd1.Spawnvp()
					cmd1.Close()
				}(cmd)
			}
		}
		if !isBackGround {
			wg.Wait()
			if len(pipeline) > 0 {
				switch pipeline[len(pipeline)-1].Term {
				case "&&":
					if errorlevel != 0 {
						return errorlevel, nil
					}
				case "||":
					if errorlevel == 0 {
						return errorlevel, nil
					}
				}
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package kvpaxos

import "net/rpc"
import crand "crypto/rand"
import "math/big"
import mrand "math/rand"

import "time"
import "fmt"
import "log"

type Clerk struct {
	servers []string
	// You will have to modify this struct.
}

func nrand() int64 {
	max := big.NewInt(int64(1) << 62)
	bigx, _ := crand.Int(crand.Reader, max)
	x := bigx.Int64()
	return x
}

func MakeClerk(servers []string) *Clerk {
	ck := new(Clerk)
	ck.servers = servers
	// You'll have to add code here.
	return ck
}

//
// call() sends an RPC to the rpcname handler on server srv
// with arguments args, waits for the reply, and leaves the
// reply in reply. the reply argument should be a pointer
// to a reply structure.
//
// the return value is true if the server responded, and false
// if call() was not able to contact the server. in particular,
// the reply's contents are only valid if call() returned true.
//
// you should assume that call() will return an
// error after a while if the server is dead.
// don't provide your own time-out mechanism.
//
// please use call() to send all RPCs, in client.go and server.go.
// please don't change this function.
//
func call(srv string, rpcname string,
	args interface{}, reply interface{}) bool {
	c, errx := rpc.Dial("unix", srv)
	if errx != nil {
		return false
	}
	defer c.Close()

	err := c.Call(rpcname, args, reply)
	if err == nil {
		return true
	}

	fmt.Println(err)
	return false
}

//
// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
func (ck *Clerk) Get(key string) string {
	// You will have to modify this function.
	log.Println("Clerk.Get", "key:", key)
	var reply = &GetReply{}

	defer func() {
		log.Println("Clerk.Get", "key:", key, "value:", reply.Value)
	}()
	args := GetArgs{}
	args.Key = key
	args.RandID = nrand()

	for {
		for i := 0; i < len(ck.servers); i++ {
			r := call(ck.servers[i], "KVPaxos.Get", args, reply)
			if !r {
				r := mrand.Int() % 100
				time.Sleep(time.Duration(r) * time.Millisecond)
				continue
			}
			if reply.Err != OK {
				continue
			}

			return reply.Value
		}
	}

	return ""
}

//
// shared by Put and Append.
//
func (ck *Clerk) PutAppend(key string, value string, op string) {
	// You will have to modify this function.
	args := PutAppendArgs{}
	args.Key = key
	args.Value = value
	args.Op = op
	args.RandID = nrand()

	log.Println("Clerk.PutAppend", "key:", key, "value:", value, "op:", op, "RandID:", args.RandID)

	var reply = &PutAppendReply{}
	for {
		b := false
		for i := 0; i < len(ck.servers); i++ {
			r := call(ck.servers[i], "KVPaxos.PutAppend", args, reply)
			if !r {
				r := mrand.Int() % 100
				time.Sleep(time.Duration(r) * time.Millisecond)
				continue
			}
			if reply.Err != OK {
				continue
			}
			b = true
			break
		}
		if b {
			break
		}
	}
}

func (ck *Clerk) Put(key string, value string) {
	ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
	ck.PutAppend(key, value, "Append")
}
<commit_msg>draft<commit_after>package kvpaxos

import "net/rpc"
import crand "crypto/rand"
import "math/big"
import mrand "math/rand"

import "time"
import "fmt"
import "log"

type Clerk struct {
	servers []string
	// You will have to modify this struct.
}

func nrand() int64 {
	max := big.NewInt(int64(1) << 62)
	bigx, _ := crand.Int(crand.Reader, max)
	x := bigx.Int64()
	return x
}

func MakeClerk(servers []string) *Clerk {
	ck := new(Clerk)
	ck.servers = servers
	// You'll have to add code here.
	return ck
}

//
// call() sends an RPC to the rpcname handler on server srv
// with arguments args, waits for the reply, and leaves the
// reply in reply. the reply argument should be a pointer
// to a reply structure.
//
// the return value is true if the server responded, and false
// if call() was not able to contact the server. in particular,
// the reply's contents are only valid if call() returned true.
//
// you should assume that call() will return an
// error after a while if the server is dead.
// don't provide your own time-out mechanism.
//
// please use call() to send all RPCs, in client.go and server.go.
// please don't change this function.
//
func call(srv string, rpcname string,
	args interface{}, reply interface{}) bool {
	c, errx := rpc.Dial("unix", srv)
	if errx != nil {
		return false
	}
	defer c.Close()

	err := c.Call(rpcname, args, reply)
	if err == nil {
		return true
	}

	fmt.Println(err)
	return false
}

//
// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
func (ck *Clerk) Get(key string) string {
	// You will have to modify this function.
	log.Println("Clerk.Get", "key:", key)
	var reply = &GetReply{}

	defer func() {
		log.Println("Clerk.Get", "key:", key, "value:", reply.Value)
	}()
	args := GetArgs{}
	args.Key = key
	args.RandID = nrand()

	for {
		for i := 0; i < len(ck.servers); i++ {
			r := call(ck.servers[i], "KVPaxos.Get", args, reply)
			if !r {
				time.Sleep(time.Duration(mrand.Int()%100) * time.Millisecond)
				continue
			}
			if reply.Err != OK {
				time.Sleep(time.Duration(mrand.Int()%100) * time.Millisecond)
				continue
			}

			return reply.Value
		}
	}

	return ""
}

//
// shared by Put and Append.
//
func (ck *Clerk) PutAppend(key string, value string, op string) {
	// You will have to modify this function.
	args := PutAppendArgs{}
	args.Key = key
	args.Value = value
	args.Op = op
	args.RandID = nrand()

	log.Println("Clerk.PutAppend", "key:", key, "value:", value, "op:", op, "RandID:", args.RandID)

	var reply = &PutAppendReply{}
	for {
		b := false
		for i := 0; i < len(ck.servers); i++ {
			r := call(ck.servers[i], "KVPaxos.PutAppend", args, reply)
			if !r {
				time.Sleep(time.Duration(mrand.Int()%100) * time.Millisecond)
				continue
			}
			if reply.Err != OK {
				time.Sleep(time.Duration(mrand.Int()%100) * time.Millisecond)
				continue
			}
			b = true
			break
		}
		if b {
			break
		}
	}
}

func (ck *Clerk) Put(key string, value string) {
	ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
	ck.PutAppend(key, value, "Append")
}
<|endoftext|>


<commit_before>package sender

import (
	"fmt"
	"socialapi/models"
	"socialapi/workers/email/chatemail/common"
	"socialapi/workers/email/emailmodels"
	"strconv"
	"sync"
	"time"

	"github.com/koding/logging"
	"github.com/koding/metrics"
	"github.com/koding/redis"
	"github.com/robfig/cron"
	"github.com/streadway/amqp"
)

var cronJob *cron.Cron

const (
	Schedule     = "0 * * * * *"
	MessageLimit = 3
	Subject      = "[Koding] Chat notifications for %s"
	DateLayout   = "Jan 2, 2006"
	MAXROUTINES  = 4
)

type Controller struct {
	log       logging.Logger
	redisConn *redis.RedisSession
	settings  *emailmodels.EmailSettings
	metrics   *metrics.Metrics

	ready chan struct{}
}

func (c *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	c.log.Error("an error occurred: %s", err)
	delivery.Ack(false)

	return false
}

func New(redisConn *redis.RedisSession, log logging.Logger, es *emailmodels.EmailSettings, metrics *metrics.Metrics) (*Controller, error) {
	c := &Controller{
		log:       log,
		redisConn: redisConn,
		settings:  es,
		ready:     make(chan struct{}, 1),
		metrics:   metrics,
	}

	return c, c.initCron()
}

// initCron initializes the cron job with given schedule, and Send closure
func (c *Controller) initCron() error {
	cronJob = cron.New()
	if err := cronJob.AddFunc(Schedule, c.Run); err != nil {
		return err
	}

	cronJob.Start()

	c.ready <- struct{}{}

	return nil
}

// Shutdown stops the cron job
func (c *Controller) Shutdown() {
	cronJob.Stop()
}

// Run send account emails in current time period
func (c *Controller) Run() {
	select {
	case <-c.ready:
		c.log.Debug("Starting next mailing period")
		c.SendEmails()
	case <-time.After(10 * time.Second):
		c.log.Critical("Need some more private message email sender workers")
		return
	}
}

func (c *Controller) SendEmails() {
	currentPeriod := common.GetCurrentMailPeriod()
	defer func() { c.ready <- struct{}{} }()

	var wg sync.WaitGroup
	for i := 0; i < MAXROUTINES; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			c.StartWorker(currentPeriod)
		}()
	}

}

func (c *Controller) StartWorker(currentPeriod int) {
	for {
		// Fetch Account
		account, err := c.NextAccount(strconv.Itoa(currentPeriod))
		// no more pending notifications
		if err == models.ErrAccountNotFound {
			c.log.Info("All accounts are notified")
			return
		}

		if err != nil {
			c.log.Error("Could not fetch account: %s", err)
			return
		}

		// Fetch channel summary data
		channels, err := c.FetchChannelSummaries(account, strconv.Itoa(currentPeriod))
		if err != nil {
			c.log.Error("Could not fetch messages for rendering: %s", err)
			continue
		}

		// maybe an error occurred while fething summaries, or they are already glanced
		// who knows
		if len(channels) == 0 {
			continue
		}

		// Decorate channel data
		es := emailmodels.NewEmailSummary(channels)

		// Render body
		body, err := es.Render()
		if err != nil {
			c.log.Error("Could not render body for account %d: %s", account.Id, err)
		}

		// Send
		subject := fmt.Sprintf(Subject, time.Now().Format(DateLayout))
		mailer, err := emailmodels.NewMailer(account, body, subject, c.settings)
		if err != nil {
			c.log.Error("Could not create mailer for account %d: %s", account.Id, err)
			continue
		}

		if err := mailer.SendMail("chat"); err != nil {
			c.log.Error("Could not send email for account: %d: %s", account.Id, err)
		}
	}
}

// NextAccount pops a random account element from set, deletes its next period
// from AccountNextPeriod hash set and returns the popped account
func (c *Controller) NextAccount(period string) (*models.Account, error) {
	key := common.PeriodAccountSetKey(period)
	val, err := c.redisConn.PopSetMember(key)
	if err == redis.ErrNil {
		return nil, models.ErrAccountNotFound
	}

	if err != nil {
		return nil, err
	}

	accountId, err := strconv.ParseInt(val, 10, 64)
	if err != nil {
		return nil, err
	}
	a := models.NewAccount()
	a.Id = accountId

	// directyle delete it from AccountNextPeriod hash set for sending further e-mails
	if err := common.ResetMailingPeriodForAccount(c.redisConn, a); err != nil {
		return nil, err
	}

	return a, nil
}

func (c *Controller) FetchChannelSummaries(a *models.Account, period string) ([]*emailmodels.ChannelSummary, error) {
	// fetch value from redis
	key := common.AccountChannelHashSetKey(a.Id, period)
	defer func() {
		if _, err := c.redisConn.Del(key); err != nil {
			c.log.Error("Could not delete pending channels for account", err)
		}
	}()

	channels := make([]*emailmodels.ChannelSummary, 0)
	vals, err := c.redisConn.HashGetAll(key)
	if err != nil {
		return channels, err
	}

	// maybe all channels are already glanced
	if len(vals) == 0 {
		return channels, nil
	}

	for i := 0; i < len(vals); i += 2 {
		ch, awayTime, err := c.parseValues(vals[i], vals[i+1])
		if err != nil {
			c.log.Error("Could not fetch channel messages: %s", err)
			continue
		}

		cs, err := emailmodels.NewChannelSummary(a, ch, awayTime)
		if err != nil {
			c.log.Error("Could not decorate channel summary: %s", err)
			continue
		}

		channels = append(channels, cs)
	}

	return channels, nil
}

func (c *Controller) parseValues(field, value interface{}) (*models.Channel, time.Time, error) {
	// string value of channelId
	channelId, err := c.redisConn.String(field)
	if err != nil {
		return nil, time.Time{}, err
	}

	// convert channel id to int64
	id, err := strconv.ParseInt(channelId, 10, 64)
	if err != nil {
		return nil, time.Time{}, err
	}

	ch := models.NewChannel()
	ch.Id = id

	awaySince, err := c.redisConn.String(value)
	if err != nil {
		return nil, time.Time{}, err
	}

	awayTime, err := strconv.ParseInt(awaySince, 10, 64)
	if err != nil {
		return nil, time.Time{}, err
	}

	t := time.Unix(0, awayTime)

	return ch, t, nil
}
<commit_msg>email: prevent email sending when total message count is 0<commit_after>package sender

import (
	"fmt"
	"socialapi/models"
	"socialapi/workers/email/chatemail/common"
	"socialapi/workers/email/emailmodels"
	"strconv"
	"sync"
	"time"

	"github.com/koding/logging"
	"github.com/koding/metrics"
	"github.com/koding/redis"
	"github.com/robfig/cron"
	"github.com/streadway/amqp"
)

var cronJob *cron.Cron

const (
	Schedule     = "0 * * * * *"
	MessageLimit = 3
	Subject      = "[Koding] Chat notifications for %s"
	DateLayout   = "Jan 2, 2006"
	MAXROUTINES  = 4
)

type Controller struct {
	log       logging.Logger
	redisConn *redis.RedisSession
	settings  *emailmodels.EmailSettings
	metrics   *metrics.Metrics

	ready chan struct{}
}

func (c *Controller) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	c.log.Error("an error occurred: %s", err)
	delivery.Ack(false)

	return false
}

func New(redisConn *redis.RedisSession, log logging.Logger, es *emailmodels.EmailSettings, metrics *metrics.Metrics) (*Controller, error) {
	c := &Controller{
		log:       log,
		redisConn: redisConn,
		settings:  es,
		ready:     make(chan struct{}, 1),
		metrics:   metrics,
	}

	return c, c.initCron()
}

// initCron initializes the cron job with given schedule, and Send closure
func (c *Controller) initCron() error {
	cronJob = cron.New()
	if err := cronJob.AddFunc(Schedule, c.Run); err != nil {
		return err
	}

	cronJob.Start()

	c.ready <- struct{}{}

	return nil
}

// Shutdown stops the cron job
func (c *Controller) Shutdown() {
	cronJob.Stop()
}

// Run send account emails in current time period
func (c *Controller) Run() {
	select {
	case <-c.ready:
		c.log.Debug("Starting next mailing period")
		c.SendEmails()
	case <-time.After(10 * time.Second):
		c.log.Critical("Need some more private message email sender workers")
		return
	}
}

func (c *Controller) SendEmails() {
	currentPeriod := common.GetCurrentMailPeriod()
	defer func() { c.ready <- struct{}{} }()

	var wg sync.WaitGroup
	for i := 0; i < MAXROUTINES; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			c.StartWorker(currentPeriod)
		}()
	}

}

func (c *Controller) StartWorker(currentPeriod int) {
	for {
		// Fetch Account
		account, err := c.NextAccount(strconv.Itoa(currentPeriod))
		// no more pending notifications
		if err == models.ErrAccountNotFound {
			c.log.Info("All accounts are notified")
			return
		}

		if err != nil {
			c.log.Error("Could not fetch account: %s", err)
			return
		}

		// Fetch channel summary data
		channels, err := c.FetchChannelSummaries(account, strconv.Itoa(currentPeriod))
		if err != nil {
			c.log.Error("Could not fetch messages for rendering: %s", err)
			continue
		}

		// maybe an error occurred while fething summaries, or they are already glanced
		// who knows
		if len(channels) == 0 {
			continue
		}

		// Decorate channel data
		es := emailmodels.NewEmailSummary(channels)
		if es.MessageCount == 0 {
			c.log.Error("Private message notification email for account %d does not have any messages", account.Id)
			continue
		}

		// Render body
		body, err := es.Render()
		if err != nil {
			c.log.Error("Could not render body for account %d: %s", account.Id, err)
			continue
		}

		// Send
		subject := fmt.Sprintf(Subject, time.Now().Format(DateLayout))
		mailer, err := emailmodels.NewMailer(account, body, subject, c.settings)
		if err != nil {
			c.log.Error("Could not create mailer for account %d: %s", account.Id, err)
			continue
		}

		if err := mailer.SendMail("chat"); err != nil {
			c.log.Error("Could not send email for account: %d: %s", account.Id, err)
		}
	}
}

// NextAccount pops a random account element from set, deletes its next period
// from AccountNextPeriod hash set and returns the popped account
func (c *Controller) NextAccount(period string) (*models.Account, error) {
	key := common.PeriodAccountSetKey(period)
	val, err := c.redisConn.PopSetMember(key)
	if err == redis.ErrNil {
		return nil, models.ErrAccountNotFound
	}

	if err != nil {
		return nil, err
	}

	accountId, err := strconv.ParseInt(val, 10, 64)
	if err != nil {
		return nil, err
	}
	a := models.NewAccount()
	a.Id = accountId

	// directyle delete it from AccountNextPeriod hash set for sending further e-mails
	if err := common.ResetMailingPeriodForAccount(c.redisConn, a); err != nil {
		return nil, err
	}

	return a, nil
}

func (c *Controller) FetchChannelSummaries(a *models.Account, period string) ([]*emailmodels.ChannelSummary, error) {
	// fetch value from redis
	key := common.AccountChannelHashSetKey(a.Id, period)
	defer func() {
		if _, err := c.redisConn.Del(key); err != nil {
			c.log.Error("Could not delete pending channels for account", err)
		}
	}()

	channels := make([]*emailmodels.ChannelSummary, 0)
	vals, err := c.redisConn.HashGetAll(key)
	if err != nil {
		return channels, err
	}

	// maybe all channels are already glanced
	if len(vals) == 0 {
		return channels, nil
	}

	for i := 0; i < len(vals); i += 2 {
		ch, awayTime, err := c.parseValues(vals[i], vals[i+1])
		if err != nil {
			c.log.Error("Could not fetch channel messages: %s", err)
			continue
		}

		cs, err := emailmodels.NewChannelSummary(a, ch, awayTime)
		if err != nil {
			c.log.Error("Could not decorate channel summary: %s", err)
			continue
		}

		channels = append(channels, cs)
	}

	return channels, nil
}

func (c *Controller) parseValues(field, value interface{}) (*models.Channel, time.Time, error) {
	// string value of channelId
	channelId, err := c.redisConn.String(field)
	if err != nil {
		return nil, time.Time{}, err
	}

	// convert channel id to int64
	id, err := strconv.ParseInt(channelId, 10, 64)
	if err != nil {
		return nil, time.Time{}, err
	}

	ch := models.NewChannel()
	ch.Id = id

	awaySince, err := c.redisConn.String(value)
	if err != nil {
		return nil, time.Time{}, err
	}

	awayTime, err := strconv.ParseInt(awaySince, 10, 64)
	if err != nil {
		return nil, time.Time{}, err
	}

	t := time.Unix(0, awayTime)

	return ch, t, nil
}
<|endoftext|>


<commit_before>package main

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

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

var conn dbox.IConnection

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)
	}
}

var (
    pcs = toolkit.M{}
    ccs = toolkit.M{}
    ledgers = toolkit.M{}
    prods = toolkit.M{}
    custs = toolkit.M{}
	plmodels = toolkit.M{}
	brands = toolkit.M{}
	ratios = map[string][]gdrj.SalesRatio{}
)

func getCursor(obj orm.IModel)dbox.ICursor{
    c, e := gdrj.Find(obj,nil,nil)
    if e!=nil{
        return nil
    }
    return c
}

func prepMaster(){
    pc:=new(gdrj.ProfitCenter)
    cc:=new(gdrj.CostCenter)
    prod:=new(gdrj.Product)
    ledger:=new(gdrj.LedgerMaster)

    cpc := getCursor(pc)
    defer cpc.Close()
    var e error
    for e=cpc.Fetch(pc,1,false);e==nil;{
        pcs.Set(pc.ID,pc)
        pc =new(gdrj.ProfitCenter)
        e=cpc.Fetch(pc,1,false)
    }

    ccc:=getCursor(cc)
    defer ccc.Close()
    for e=ccc.Fetch(cc,1,false);e==nil;{
        ccs.Set(cc.ID,cc)
        cc = new(gdrj.CostCenter)
        e=ccc.Fetch(cc,1,false)
    }

    cprod:=getCursor(prod)
    defer cprod.Close()
    for e=cprod.Fetch(prod,1,false);e==nil;{
        prods.Set(prod.ID,prod)
        prod=new(gdrj.Product)
        e=cprod.Fetch(prod,1,false)
    }

    cledger:=getCursor(ledger)
    defer cledger.Close()
    for e=cledger.Fetch(ledger,1,false);e==nil;{
        ledgers.Set(ledger.ID,ledger)
        ledger=new(gdrj.LedgerMaster)
        e=cledger.Fetch(ledger,1,false)
    }

    cust := new(gdrj.Customer)
    ccust:=getCursor(cust)
    defer ccust.Close()
    for e=ccust.Fetch(cust,1,false);e==nil;{
        custs.Set(cust.ID,cust)
        cust=new(gdrj.Customer)
        e=ccust.Fetch(cust,1,false)
    }

	plmodel := new(gdrj.PLModel)
	cplmodel := getCursor(plmodel)
	defer cplmodel.Close()
	for e=cplmodel.Fetch(plmodel,1,false);e==nil;{
		plmodels.Set(plmodel.ID,plmodel)
		plmodel=new(gdrj.PLModel)
		e=cplmodel.Fetch(plmodel,1,false)
	}

	toolkit.Println("--> Brand")
	brand := new(gdrj.HBrandCategory)
	cbrand := getCursor(plmodel)
	defer cbrand.Close()
	for e=cbrand.Fetch(brand,1,false);e==nil;{
		brands.Set(brand.ID,brand)
		brand=new(gdrj.HBrandCategory)
		e=cbrand.Fetch(brand,1,false)
	}

	toolkit.Println("--> Sales Ratio")
	ratio := new(gdrj.SalesRatio)
	cratios := getCursor(ratio)
	defer cratios.Close()
	for {
		efetch := cratios.Fetch(ratio, 1, false)
		if efetch != nil {
			break
		}
		ratioid := toolkit.Sprintf("%d_%d_%s", ratio.Year, ratio.Month, ratio.BranchID)
		a, exist := ratios[ratioid]
		if !exist {
			a = []gdrj.SalesRatio{}
		}
		a=append(a, *ratio)
		ratio = new(gdrj.SalesRatio)
		ratios[ratioid] = a
	}
}

func main() {
	//runtime.GOMAXPROCS(runtime.NumCPU())
	setinitialconnection()
	defer gdrj.CloseDb()

    toolkit.Println("Reading Master")
    prepMaster()

	pldm := new(gdrj.PLDataModel)
	toolkit.Println("Delete existing")
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","30052016SAP_EXPORT")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_FREIGHT")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_SUSEMI")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_APINTRA")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","30052016SAP_SGAPL")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_MEGASARI")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_SALESRD")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_DISC-RDJKT")).Delete().Exec(nil)

    toolkit.Println("START...")

	//for i, src := range arrstring {
	//dbf := dbox.Contains("src", src)
	crx, err := gdrj.Find(new(gdrj.RawDataPL), nil, toolkit.M{})
	if err != nil {
		toolkit.Println("Error Found : ", err.Error())
		os.Exit(1)
	}

    count := crx.Count()

	jobs := make(chan *gdrj.RawDataPL, count)
	result := make(chan string, count)

	for wi:=1;wi<10;wi++{
		go worker(wi, jobs, result)
	}

	t0 := time.Now()
	ci := 0
	iseof := false
	for !iseof {
		arrpl := []*gdrj.RawDataPL{}
		e := crx.Fetch(&arrpl, 1000, false)
		if e!=nil{
			iseof=true
			break
		}

		for _, v := range arrpl {
			jobs <- v
			ci++
		}

		toolkit.Printfn("Processing %d of %d in %s", ci, count, time.Since(t0).String())

		if len(arrpl) < 1000 {
			iseof = true
		}
	}

	toolkit.Println("Saving")
	step := count / 100
	limit := step
	for ri := 0; ri < count; ri++ {
		<-result
		if ri >= limit {
			toolkit.Printfn("Saving %d of %d (%dpct) in %s", ri, count, ri*100/count,
				time.Since(t0).String())
			limit += step
		}
	}
	toolkit.Printfn("Done %s", time.Since(t0).String())
}

var pldatas = map[string]*gdrj.PLDataModel{}

func worker(wi int, jobs <-chan *gdrj.RawDataPL, result chan<- string){
	workerconn, err := modules.GetDboxIConnection("db_godrej")
	defer workerconn.Close()

		for v:= range jobs{
			if v.Src=="31052016SAP_SALESRD" || v.Src=="31052016SAP_DISC-RDJKT" || v.Src=="" || v.AmountinIDR==0 {
				result <- "NOK"
				continue
			}

			tdate := time.Date(v.Year, time.Month(v.Period), 1, 0, 0, 0, 0, time.UTC).AddDate(0, 3, 0)

			ls := new(gdrj.PLDataModel)
			ls.CompanyCode = v.EntityID
			//ls.LedgerAccount = v.Account

			ls.Year = tdate.Year()
			ls.Month = int(tdate.Month())
			ls.Date = gdrj.NewDate(ls.Year, ls.Month, 1)

			ls.PCID = v.PCID
			if v.PCID != "" && pcs.Has(v.PCID) {
				ls.PC = pcs.Get(v.PCID).(*gdrj.ProfitCenter)
			}

			ls.CCID = v.CCID
			if v.CCID != "" && ccs.Has(v.CCID) {
				ls.CC = ccs.Get(v.CCID).(*gdrj.CostCenter)
			}

			ls.OutletID = v.OutletID
			if v.OutletID != "" && custs.Has(v.OutletID) {
				ls.Customer = custs.Get(v.OutletID).(*gdrj.Customer)
				//ls.Customer = gdrj.CustomerGetByID(v.OutletID)
			} else {
				c := new(gdrj.Customer)
				c.Name = v.OutletName
				c.BranchID = v.BusA
				c.ChannelID = "I3"
				c.ChannelName = "MT"
				c.CustType = "EXP"
				c.CustomerGroup = "EXP"
				c.Zone = "EXP"
				c.Region = "EXP"
				c.National = "EXP"
				c.AreaName = "EXP"
				c.CustomerGroupName = "Export"
				ls.Customer = c
			}

			ls.SKUID = v.SKUID
			if v.SKUID != "" && prods.Has(v.SKUID) {
				ls.Product = prods.Get(v.SKUID).(*gdrj.Product)
			} else if v.SKUID!="" {
				ls.Product = new(gdrj.Product)
				ls.Product.Name = v.ProductName
				ls.Product.BrandCategoryID = v.PCID[4:]
				if brands.Has(ls.Product.BrandCategoryID){
					ls.Product.Brand = brands.Get(ls.Product.BrandCategoryID).(*gdrj.HBrandCategory).BrandID
				} else {
					ls.Product.BrandCategoryID = "Common"
					ls.Product.Brand = "-"
				}
			}

			ls.Value1 = v.AmountinIDR
			//ls.Value2 = v.AmountinUSD

			tLedgerAccount := new(gdrj.LedgerMaster)
			if ledgers.Has(v.Account){
				tLedgerAccount = ledgers.Get(v.Account).(*gdrj.LedgerMaster)
			}
			if tLedgerAccount.PLCode==""{
				plm := plmodels.Get("PL34").(*gdrj.PLModel)
				ls.PLCode = plm.ID
				ls.PLOrder = plm.OrderIndex
				ls.PLGroup1 = plm.PLHeader1
				ls.PLGroup2 = plm.PLHeader2
				ls.PLGroup3 = plm.PLHeader3
			} else if v.Src=="30052016SAP_EXPORT"{
				plm := plmodels.Get("PL6").(*gdrj.PLModel)
				ls.PLCode = plm.ID
				ls.PLOrder = plm.OrderIndex
				ls.PLGroup1 = plm.PLHeader1
				ls.PLGroup2 = plm.PLHeader2
				ls.PLGroup3 = plm.PLHeader3
			} else  {
				ls.PLCode = tLedgerAccount.PLCode
				ls.PLOrder = tLedgerAccount.OrderIndex
				ls.PLGroup1 = tLedgerAccount.H1
				ls.PLGroup2 = tLedgerAccount.H2
				ls.PLGroup3 = tLedgerAccount.H3
			}

			ls.Date = gdrj.NewDate(ls.Year, int(ls.Month), 1)

			sources := strings.Split(v.Src,"_")
			if len(sources)==1{
				ls.Source = sources[1]
			} else if len(sources)>1{
				ls.Source = sources[1]
			} else {
				ls.Source="OTHER"
			}

			rs := []gdrj.SalesRatio{}
			if v.Src!="30052016SAP_EXPORT"{
				srid := toolkit.Sprintf("%d_%d_%s", ls.Year, ls.Month, ls.Customer.BranchID)
				a, exists := ratios[srid]
				if exists{
					rs=a
				}
			}

			if len(rs)==0{
				r := new(gdrj.SalesRatio)
				r.Year = ls.Year
				r.Month = ls.Month
				r.Ratio = 1
				rs = append(rs, *r)
			}

			total := float64(0)
			for _, r := range rs{
				total += r.Ratio
			}

			for _, r := range rs{
				lsexist := false
				rls := new(gdrj.PLDataModel)
				*rls = *ls
				rls.OutletID = r.OutletID
				rls.SKUID = r.SKUID
				rls.ID = rls.PrepareID().(string)
				rls, lsexist = pldatas[rls.ID]
				multiplier:=float64(1)
				if v.Src!="30052016SAP_EXPORT"{
					multiplier=-1
				}

				insert := true
				if !lsexist{
					//-- need to grand rls again
					rls = new(gdrj.PLDataModel)
					*rls = *ls
					rls.OutletID = r.OutletID
					rls.SKUID = r.SKUID
					rls.ID = rls.PrepareID().(string)
					//-- end

					//-- get existing values
					els := new(gdrj.PLDataModel)
					cls,_ := workerconn.NewQuery().From(ls.TableName()).
						Where(dbox.Eq("_id",rls.ID)).Cursor(nil)
					ecls:=cls.Fetch(els,1,false)
					if ecls==nil{
						rls.Value1=els.Value1
					}
					insert=false
					cls.Close()
				}

				rls.Value1 += ls.Value1 * r.Ratio/total * multiplier
				if insert{
					err = workerconn.NewQuery().
						SetConfig("multiexec",true).
						From(ls.TableName()).
						//Where(dbox.Eq("_id", rls.ID)).
						Insert().
						Exec(toolkit.M{}.Set("data",rls))
				} else {
					err = workerconn.NewQuery().
						SetConfig("multiexec",true).
						From(ls.TableName()).
						//Where(dbox.Eq("_id", rls.ID)).
						Update().
						Exec(toolkit.M{}.Set("data",rls))
				}
				if err != nil {
					toolkit.Println("Error Found : ", err.Error())
					os.Exit(1)
				}
				pldatas[rls.ID]=rls
			}
			result <- "OK"
		}
}
<commit_msg>add mutex<commit_after>package main

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

	"github.com/eaciit/dbox"
	"github.com/eaciit/orm/v1"
	"github.com/eaciit/toolkit"
    "time"
	"strings"
	"sync"
)

var mutex = new(sync.Mutex)
var conn dbox.IConnection

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)
	}
}

var (
    pcs = toolkit.M{}
    ccs = toolkit.M{}
    ledgers = toolkit.M{}
    prods = toolkit.M{}
    custs = toolkit.M{}
	plmodels = toolkit.M{}
	brands = toolkit.M{}
	ratios = map[string][]gdrj.SalesRatio{}
)

func getCursor(obj orm.IModel)dbox.ICursor{
    c, e := gdrj.Find(obj,nil,nil)
    if e!=nil{
        return nil
    }
    return c
}

func prepMaster(){
    pc:=new(gdrj.ProfitCenter)
    cc:=new(gdrj.CostCenter)
    prod:=new(gdrj.Product)
    ledger:=new(gdrj.LedgerMaster)

    cpc := getCursor(pc)
    defer cpc.Close()
    var e error
    for e=cpc.Fetch(pc,1,false);e==nil;{
        pcs.Set(pc.ID,pc)
        pc =new(gdrj.ProfitCenter)
        e=cpc.Fetch(pc,1,false)
    }

    ccc:=getCursor(cc)
    defer ccc.Close()
    for e=ccc.Fetch(cc,1,false);e==nil;{
        ccs.Set(cc.ID,cc)
        cc = new(gdrj.CostCenter)
        e=ccc.Fetch(cc,1,false)
    }

    cprod:=getCursor(prod)
    defer cprod.Close()
    for e=cprod.Fetch(prod,1,false);e==nil;{
        prods.Set(prod.ID,prod)
        prod=new(gdrj.Product)
        e=cprod.Fetch(prod,1,false)
    }

    cledger:=getCursor(ledger)
    defer cledger.Close()
    for e=cledger.Fetch(ledger,1,false);e==nil;{
        ledgers.Set(ledger.ID,ledger)
        ledger=new(gdrj.LedgerMaster)
        e=cledger.Fetch(ledger,1,false)
    }

    cust := new(gdrj.Customer)
    ccust:=getCursor(cust)
    defer ccust.Close()
    for e=ccust.Fetch(cust,1,false);e==nil;{
        custs.Set(cust.ID,cust)
        cust=new(gdrj.Customer)
        e=ccust.Fetch(cust,1,false)
    }

	plmodel := new(gdrj.PLModel)
	cplmodel := getCursor(plmodel)
	defer cplmodel.Close()
	for e=cplmodel.Fetch(plmodel,1,false);e==nil;{
		plmodels.Set(plmodel.ID,plmodel)
		plmodel=new(gdrj.PLModel)
		e=cplmodel.Fetch(plmodel,1,false)
	}

	toolkit.Println("--> Brand")
	brand := new(gdrj.HBrandCategory)
	cbrand := getCursor(plmodel)
	defer cbrand.Close()
	for e=cbrand.Fetch(brand,1,false);e==nil;{
		brands.Set(brand.ID,brand)
		brand=new(gdrj.HBrandCategory)
		e=cbrand.Fetch(brand,1,false)
	}

	toolkit.Println("--> Sales Ratio")
	ratio := new(gdrj.SalesRatio)
	cratios := getCursor(ratio)
	defer cratios.Close()
	for {
		efetch := cratios.Fetch(ratio, 1, false)
		if efetch != nil {
			break
		}
		ratioid := toolkit.Sprintf("%d_%d_%s", ratio.Year, ratio.Month, ratio.BranchID)
		a, exist := ratios[ratioid]
		if !exist {
			a = []gdrj.SalesRatio{}
		}
		a=append(a, *ratio)
		ratio = new(gdrj.SalesRatio)
		ratios[ratioid] = a
	}
}

func main() {
	//runtime.GOMAXPROCS(runtime.NumCPU())
	setinitialconnection()
	defer gdrj.CloseDb()

    toolkit.Println("Reading Master")
    prepMaster()

	pldm := new(gdrj.PLDataModel)
	toolkit.Println("Delete existing")
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","30052016SAP_EXPORT")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_FREIGHT")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_SUSEMI")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_APINTRA")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","30052016SAP_SGAPL")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_MEGASARI")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_SALESRD")).Delete().Exec(nil)
    conn.NewQuery().From(pldm.TableName()).Where(dbox.Eq("source","31052016SAP_DISC-RDJKT")).Delete().Exec(nil)

    toolkit.Println("START...")

	//for i, src := range arrstring {
	//dbf := dbox.Contains("src", src)
	crx, err := gdrj.Find(new(gdrj.RawDataPL), nil, toolkit.M{})
	if err != nil {
		toolkit.Println("Error Found : ", err.Error())
		os.Exit(1)
	}

    count := crx.Count()

	jobs := make(chan *gdrj.RawDataPL, count)
	result := make(chan string, count)

	for wi:=1;wi<10;wi++{
		go worker(wi, jobs, result)
	}

	t0 := time.Now()
	ci := 0
	iseof := false
	for !iseof {
		arrpl := []*gdrj.RawDataPL{}
		e := crx.Fetch(&arrpl, 1000, false)
		if e!=nil{
			iseof=true
			break
		}

		for _, v := range arrpl {
			jobs <- v
			ci++
		}

		toolkit.Printfn("Processing %d of %d in %s", ci, count, time.Since(t0).String())

		if len(arrpl) < 1000 {
			iseof = true
		}
	}

	toolkit.Println("Saving")
	step := count / 100
	limit := step
	for ri := 0; ri < count; ri++ {
		<-result
		if ri >= limit {
			toolkit.Printfn("Saving %d of %d (%dpct) in %s", ri, count, ri*100/count,
				time.Since(t0).String())
			limit += step
		}
	}
	toolkit.Printfn("Done %s", time.Since(t0).String())
}

var pldatas = map[string]*gdrj.PLDataModel{}

func worker(wi int, jobs <-chan *gdrj.RawDataPL, result chan<- string){
	workerconn, err := modules.GetDboxIConnection("db_godrej")
	defer workerconn.Close()

		for v:= range jobs{
			if v.Src=="31052016SAP_SALESRD" || v.Src=="31052016SAP_DISC-RDJKT" || v.Src=="" || v.AmountinIDR==0 {
				result <- "NOK"
				continue
			}

			tdate := time.Date(v.Year, time.Month(v.Period), 1, 0, 0, 0, 0, time.UTC).AddDate(0, 3, 0)

			ls := new(gdrj.PLDataModel)
			ls.CompanyCode = v.EntityID
			//ls.LedgerAccount = v.Account

			ls.Year = tdate.Year()
			ls.Month = int(tdate.Month())
			ls.Date = gdrj.NewDate(ls.Year, ls.Month, 1)

			ls.PCID = v.PCID
			if v.PCID != "" && pcs.Has(v.PCID) {
				ls.PC = pcs.Get(v.PCID).(*gdrj.ProfitCenter)
			}

			ls.CCID = v.CCID
			if v.CCID != "" && ccs.Has(v.CCID) {
				ls.CC = ccs.Get(v.CCID).(*gdrj.CostCenter)
			}

			ls.OutletID = v.OutletID
			if v.OutletID != "" && custs.Has(v.OutletID) {
				ls.Customer = custs.Get(v.OutletID).(*gdrj.Customer)
				//ls.Customer = gdrj.CustomerGetByID(v.OutletID)
			} else {
				c := new(gdrj.Customer)
				c.Name = v.OutletName
				c.BranchID = v.BusA
				c.ChannelID = "I3"
				c.ChannelName = "MT"
				c.CustType = "EXP"
				c.CustomerGroup = "EXP"
				c.Zone = "EXP"
				c.Region = "EXP"
				c.National = "EXP"
				c.AreaName = "EXP"
				c.CustomerGroupName = "Export"
				ls.Customer = c
			}

			ls.SKUID = v.SKUID
			if v.SKUID != "" && prods.Has(v.SKUID) {
				ls.Product = prods.Get(v.SKUID).(*gdrj.Product)
			} else if v.SKUID!="" {
				ls.Product = new(gdrj.Product)
				ls.Product.Name = v.ProductName
				ls.Product.BrandCategoryID = v.PCID[4:]
				if brands.Has(ls.Product.BrandCategoryID){
					ls.Product.Brand = brands.Get(ls.Product.BrandCategoryID).(*gdrj.HBrandCategory).BrandID
				} else {
					ls.Product.BrandCategoryID = "Common"
					ls.Product.Brand = "-"
				}
			}

			ls.Value1 = v.AmountinIDR
			//ls.Value2 = v.AmountinUSD

			tLedgerAccount := new(gdrj.LedgerMaster)
			if ledgers.Has(v.Account){
				tLedgerAccount = ledgers.Get(v.Account).(*gdrj.LedgerMaster)
			}
			if tLedgerAccount.PLCode==""{
				plm := plmodels.Get("PL34").(*gdrj.PLModel)
				ls.PLCode = plm.ID
				ls.PLOrder = plm.OrderIndex
				ls.PLGroup1 = plm.PLHeader1
				ls.PLGroup2 = plm.PLHeader2
				ls.PLGroup3 = plm.PLHeader3
			} else if v.Src=="30052016SAP_EXPORT"{
				plm := plmodels.Get("PL6").(*gdrj.PLModel)
				ls.PLCode = plm.ID
				ls.PLOrder = plm.OrderIndex
				ls.PLGroup1 = plm.PLHeader1
				ls.PLGroup2 = plm.PLHeader2
				ls.PLGroup3 = plm.PLHeader3
			} else  {
				ls.PLCode = tLedgerAccount.PLCode
				ls.PLOrder = tLedgerAccount.OrderIndex
				ls.PLGroup1 = tLedgerAccount.H1
				ls.PLGroup2 = tLedgerAccount.H2
				ls.PLGroup3 = tLedgerAccount.H3
			}

			ls.Date = gdrj.NewDate(ls.Year, int(ls.Month), 1)

			sources := strings.Split(v.Src,"_")
			if len(sources)==1{
				ls.Source = sources[1]
			} else if len(sources)>1{
				ls.Source = sources[1]
			} else {
				ls.Source="OTHER"
			}

			rs := []gdrj.SalesRatio{}
			if v.Src!="30052016SAP_EXPORT"{
				srid := toolkit.Sprintf("%d_%d_%s", ls.Year, ls.Month, ls.Customer.BranchID)
				a, exists := ratios[srid]
				if exists{
					rs=a
				}
			}

			if len(rs)==0{
				r := new(gdrj.SalesRatio)
				r.Year = ls.Year
				r.Month = ls.Month
				r.Ratio = 1
				rs = append(rs, *r)
			}

			total := float64(0)
			for _, r := range rs{
				total += r.Ratio
			}

			for _, r := range rs{
				lsexist := false
				rls := new(gdrj.PLDataModel)
				*rls = *ls
				rls.OutletID = r.OutletID
				rls.SKUID = r.SKUID
				rls.ID = rls.PrepareID().(string)
				rls, lsexist = pldatas[rls.ID]
				multiplier:=float64(1)
				if v.Src!="30052016SAP_EXPORT"{
					multiplier=-1
				}

				mutex.Lock()
				insert := true
				if !lsexist{
					//-- need to grand rls again
					rls = new(gdrj.PLDataModel)
					*rls = *ls
					rls.OutletID = r.OutletID
					rls.SKUID = r.SKUID
					rls.ID = rls.PrepareID().(string)
					//-- end

					//-- get existing values
					els := new(gdrj.PLDataModel)
					cls,_ := workerconn.NewQuery().From(ls.TableName()).
						Where(dbox.Eq("_id",rls.ID)).Cursor(nil)
					ecls:=cls.Fetch(els,1,false)
					if ecls==nil{
						rls.Value1=els.Value1
					}
					insert=false
					cls.Close()
				}

				rls.Value1 += ls.Value1 * r.Ratio/total * multiplier
				if insert{
					err = workerconn.NewQuery().
						SetConfig("multiexec",true).
						From(ls.TableName()).
						//Where(dbox.Eq("_id", rls.ID)).
						Insert().
						Exec(toolkit.M{}.Set("data",rls))
				} else {
					err = workerconn.NewQuery().
						SetConfig("multiexec",true).
						From(ls.TableName()).
						//Where(dbox.Eq("_id", rls.ID)).
						Update().
						Exec(toolkit.M{}.Set("data",rls))
				}
				if err != nil {
					toolkit.Println("Error Found : ", err.Error())
					os.Exit(1)
				}
				pldatas[rls.ID]=rls
				mutex.Unlock()
			}
			result <- "OK"
		}
}
<|endoftext|>


<commit_before>package autonat

import (
	"context"
	"sync"
	"time"

	pb "github.com/libp2p/go-libp2p-autonat/pb"

	ggio "github.com/gogo/protobuf/io"
	libp2p "github.com/libp2p/go-libp2p"
	host "github.com/libp2p/go-libp2p-host"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	ma "github.com/multiformats/go-multiaddr"
)

const P_CIRCUIT = 290

var AutoNATServiceResetInterval = 1 * time.Minute

type AutoNATService struct {
	ctx    context.Context
	dialer host.Host
	peers  map[peer.ID]struct{}
	mx     sync.Mutex
}

func NewAutoNATService(ctx context.Context, h host.Host) (*AutoNATService, error) {
	dialer, err := libp2p.New(ctx)
	if err != nil {
		return nil, err
	}

	as := &AutoNATService{
		ctx:    ctx,
		dialer: dialer,
		peers:  make(map[peer.ID]struct{}),
	}
	h.SetStreamHandler(AutoNATProto, as.handleStream)

	go as.resetPeers()

	return as, nil
}

func (as *AutoNATService) handleStream(s inet.Stream) {
	defer s.Close()

	pid := s.Conn().RemotePeer()
	log.Debugf("New stream from %s", pid.Pretty())

	r := ggio.NewDelimitedReader(s, inet.MessageSizeMax)
	w := ggio.NewDelimitedWriter(s)

	var req pb.Message
	var res pb.Message

	err := r.ReadMsg(&req)
	if err != nil {
		s.Reset()
		return
	}

	t := req.GetType()
	if t != pb.Message_DIAL {
		log.Debugf("Unexpected message from: %s", t.String())
		s.Reset()
		return
	}

	dr := as.handleDial(pid, req.GetDial().GetPeer())
	res.Type = pb.Message_DIAL_RESPONSE.Enum()
	res.DialResponse = dr

	err = w.WriteMsg(&res)
	if err != nil {
		log.Debugf("Error writing response: %s", err.Error())
		s.Reset()
		return
	}
}

func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Message_DialResponse {
	if mpi == nil {
		return newDialResponseError(pb.Message_E_BAD_REQUEST, "missing peer info")
	}

	mpid := mpi.GetId()
	if mpid != nil {
		mp, err := peer.IDFromBytes(mpid)
		if err != nil {
			return newDialResponseError(pb.Message_E_BAD_REQUEST, "bad peer id")
		}

		if mp != p {
			return newDialResponseError(pb.Message_E_BAD_REQUEST, "peer id mismatch")
		}
	}

	addrs := make([]ma.Multiaddr, 0)
	for _, maddr := range mpi.GetAddrs() {
		addr, err := ma.NewMultiaddrBytes(maddr)
		if err != nil {
			log.Debugf("Error parsing multiaddr: %s", err.Error())
			continue
		}

		// skip relay addresses
		_, err = addr.ValueForProtocol(P_CIRCUIT)
		if err == nil {
			continue
		}

		// skip private network (unroutable) addresses
		if !isPublicAddr(addr) {
			continue
		}

		addrs = append(addrs, addr)
	}

	if len(addrs) == 0 {
		return newDialResponseError(pb.Message_E_DIAL_ERROR, "no dialable addresses")
	}

	return as.doDial(pstore.PeerInfo{ID: p, Addrs: addrs})
}

func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse {
	// rate limit check
	as.mx.Lock()
	_, ok := as.peers[pi.ID]
	if ok {
		as.mx.Unlock()
		return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials")
	}
	as.peers[pi.ID] = struct{}{}
	as.mx.Unlock()

	ctx, cancel := context.WithTimeout(as.ctx, 42*time.Second)
	defer cancel()

	err := as.dialer.Connect(ctx, pi)
	if err != nil {
		log.Debugf("error dialing %s: %s", pi.ID.Pretty(), err.Error())
		// wait for the context to timeout to avoid leaking timing information
		// this renders the service ineffective as a port scanner
		<-ctx.Done()
		return newDialResponseError(pb.Message_E_DIAL_ERROR, "dial failed")
	}

	conns := as.dialer.Network().ConnsToPeer(pi.ID)
	if len(conns) == 0 {
		log.Errorf("supposedly connected to %s, but no connection to peer", pi.ID.Pretty())
		return newDialResponseError(pb.Message_E_INTERNAL_ERROR, "internal service error")
	}

	ra := conns[0].RemoteMultiaddr()
	conns[0].Close()
	return newDialResponseOK(ra)
}

func (as *AutoNATService) resetPeers() {
	ticker := time.NewTicker(AutoNATServiceResetInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			as.mx.Lock()
			as.peers = make(map[peer.ID]struct{})
			as.mx.Unlock()

		case <-as.ctx.Done():
			return
		}
	}
}
<commit_msg>don't throw away read errors; log them.<commit_after>package autonat

import (
	"context"
	"sync"
	"time"

	pb "github.com/libp2p/go-libp2p-autonat/pb"

	ggio "github.com/gogo/protobuf/io"
	libp2p "github.com/libp2p/go-libp2p"
	host "github.com/libp2p/go-libp2p-host"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	ma "github.com/multiformats/go-multiaddr"
)

const P_CIRCUIT = 290

var AutoNATServiceResetInterval = 1 * time.Minute

type AutoNATService struct {
	ctx    context.Context
	dialer host.Host
	peers  map[peer.ID]struct{}
	mx     sync.Mutex
}

func NewAutoNATService(ctx context.Context, h host.Host) (*AutoNATService, error) {
	dialer, err := libp2p.New(ctx)
	if err != nil {
		return nil, err
	}

	as := &AutoNATService{
		ctx:    ctx,
		dialer: dialer,
		peers:  make(map[peer.ID]struct{}),
	}
	h.SetStreamHandler(AutoNATProto, as.handleStream)

	go as.resetPeers()

	return as, nil
}

func (as *AutoNATService) handleStream(s inet.Stream) {
	defer s.Close()

	pid := s.Conn().RemotePeer()
	log.Debugf("New stream from %s", pid.Pretty())

	r := ggio.NewDelimitedReader(s, inet.MessageSizeMax)
	w := ggio.NewDelimitedWriter(s)

	var req pb.Message
	var res pb.Message

	err := r.ReadMsg(&req)
	if err != nil {
		log.Debugf("Error reading message from %s: %s", pid.Pretty(), err.Error())
		s.Reset()
		return
	}

	t := req.GetType()
	if t != pb.Message_DIAL {
		log.Debugf("Unexpected message from %s: %s (%d)", pid.Pretty(), t.String(), t)
		s.Reset()
		return
	}

	dr := as.handleDial(pid, req.GetDial().GetPeer())
	res.Type = pb.Message_DIAL_RESPONSE.Enum()
	res.DialResponse = dr

	err = w.WriteMsg(&res)
	if err != nil {
		log.Debugf("Error writing response to %s: %s", pid.Pretty(), err.Error())
		s.Reset()
		return
	}
}

func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Message_DialResponse {
	if mpi == nil {
		return newDialResponseError(pb.Message_E_BAD_REQUEST, "missing peer info")
	}

	mpid := mpi.GetId()
	if mpid != nil {
		mp, err := peer.IDFromBytes(mpid)
		if err != nil {
			return newDialResponseError(pb.Message_E_BAD_REQUEST, "bad peer id")
		}

		if mp != p {
			return newDialResponseError(pb.Message_E_BAD_REQUEST, "peer id mismatch")
		}
	}

	addrs := make([]ma.Multiaddr, 0)
	for _, maddr := range mpi.GetAddrs() {
		addr, err := ma.NewMultiaddrBytes(maddr)
		if err != nil {
			log.Debugf("Error parsing multiaddr: %s", err.Error())
			continue
		}

		// skip relay addresses
		_, err = addr.ValueForProtocol(P_CIRCUIT)
		if err == nil {
			continue
		}

		// skip private network (unroutable) addresses
		if !isPublicAddr(addr) {
			continue
		}

		addrs = append(addrs, addr)
	}

	if len(addrs) == 0 {
		return newDialResponseError(pb.Message_E_DIAL_ERROR, "no dialable addresses")
	}

	return as.doDial(pstore.PeerInfo{ID: p, Addrs: addrs})
}

func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse {
	// rate limit check
	as.mx.Lock()
	_, ok := as.peers[pi.ID]
	if ok {
		as.mx.Unlock()
		return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials")
	}
	as.peers[pi.ID] = struct{}{}
	as.mx.Unlock()

	ctx, cancel := context.WithTimeout(as.ctx, 42*time.Second)
	defer cancel()

	err := as.dialer.Connect(ctx, pi)
	if err != nil {
		log.Debugf("error dialing %s: %s", pi.ID.Pretty(), err.Error())
		// wait for the context to timeout to avoid leaking timing information
		// this renders the service ineffective as a port scanner
		<-ctx.Done()
		return newDialResponseError(pb.Message_E_DIAL_ERROR, "dial failed")
	}

	conns := as.dialer.Network().ConnsToPeer(pi.ID)
	if len(conns) == 0 {
		log.Errorf("supposedly connected to %s, but no connection to peer", pi.ID.Pretty())
		return newDialResponseError(pb.Message_E_INTERNAL_ERROR, "internal service error")
	}

	ra := conns[0].RemoteMultiaddr()
	conns[0].Close()
	return newDialResponseOK(ra)
}

func (as *AutoNATService) resetPeers() {
	ticker := time.NewTicker(AutoNATServiceResetInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			as.mx.Lock()
			as.peers = make(map[peer.ID]struct{})
			as.mx.Unlock()

		case <-as.ctx.Done():
			return
		}
	}
}
<|endoftext|>


<commit_before>package config_test

import (
	"testing"

	. "github.com/github/git-lfs/config"
	"github.com/ttaylorr/assert"
)

func TestEnvironmentOfReturnsCorrectlyInitializedEnvironment(t *testing.T) {
	fetcher := new(MockFetcher)

	env := EnvironmentOf(fetcher)

	assert.Equal(t, fetcher, env.Fetcher)
}

func TestEnvironmentGetDelegatesToFetcher(t *testing.T) {
	var fetcher MockFetcher
	fetcher.On("Get", "foo").Return("bar").Once()

	env := EnvironmentOf(&fetcher)
	val := env.Get("foo")

	fetcher.AssertExpectations(t)
	assert.Equal(t, "bar", val)
}

func TestEnvironmentBoolTruthyConversion(t *testing.T) {
	for _, c := range []EnvironmentConversionTestCase{
		{"", true, GetBoolDefault(true)},
		{"", false, GetBoolDefault(false)},

		{"true", true, GetBoolDefault(false)},
		{"1", true, GetBoolDefault(false)},
		{"on", true, GetBoolDefault(false)},
		{"yes", true, GetBoolDefault(false)},
		{"t", true, GetBoolDefault(false)},

		{"false", false, GetBoolDefault(true)},
		{"0", false, GetBoolDefault(true)},
		{"off", false, GetBoolDefault(true)},
		{"no", false, GetBoolDefault(true)},
		{"f", false, GetBoolDefault(true)},
	} {
		c.Assert(t)
	}
}

func TestEnvironmentIntTestCases(t *testing.T) {
	for _, c := range []EnvironmentConversionTestCase{
		{"", 1, GetIntDefault(1)},

		{"1", 1, GetIntDefault(0)},
		{"3", 3, GetIntDefault(0)},

		{"malformed", 7, GetIntDefault(7)},
	} {
		c.Assert(t)
	}
}

type EnvironmentConversionTestCase struct {
	Val      string
	Expected interface{}

	GotFn func(env *Environment, key string) interface{}
}

var (
	GetBoolDefault = func(def bool) func(e *Environment, key string) interface{} {
		return func(e *Environment, key string) interface{} {
			return e.Bool(key, def)
		}
	}

	GetIntDefault = func(def int) func(e *Environment, key string) interface{} {
		return func(e *Environment, key string) interface{} {
			return e.Int(key, def)
		}
	}
)

func (c *EnvironmentConversionTestCase) Assert(t *testing.T) {
	var fetcher MockFetcher
	fetcher.On("Get", c.Val).Return(c.Val).Once()

	env := EnvironmentOf(&fetcher)
	got := c.GotFn(env, c.Val)

	if c.Expected != got {
		t.Errorf("lfs/config: expected val=%q to be %q (got: %q)", c.Val, c.Expected, got)
	}
	fetcher.AssertExpectations(t)
}
<commit_msg>config/environment_test: use testify/assert<commit_after>package config_test

import (
	"testing"

	. "github.com/github/git-lfs/config"
	"github.com/stretchr/testify/assert"
)

func TestEnvironmentOfReturnsCorrectlyInitializedEnvironment(t *testing.T) {
	fetcher := new(MockFetcher)

	env := EnvironmentOf(fetcher)

	assert.Equal(t, fetcher, env.Fetcher)
}

func TestEnvironmentGetDelegatesToFetcher(t *testing.T) {
	var fetcher MockFetcher
	fetcher.On("Get", "foo").Return("bar").Once()

	env := EnvironmentOf(&fetcher)
	val := env.Get("foo")

	fetcher.AssertExpectations(t)
	assert.Equal(t, "bar", val)
}

func TestEnvironmentBoolTruthyConversion(t *testing.T) {
	for _, c := range []EnvironmentConversionTestCase{
		{"", true, GetBoolDefault(true)},
		{"", false, GetBoolDefault(false)},

		{"true", true, GetBoolDefault(false)},
		{"1", true, GetBoolDefault(false)},
		{"on", true, GetBoolDefault(false)},
		{"yes", true, GetBoolDefault(false)},
		{"t", true, GetBoolDefault(false)},

		{"false", false, GetBoolDefault(true)},
		{"0", false, GetBoolDefault(true)},
		{"off", false, GetBoolDefault(true)},
		{"no", false, GetBoolDefault(true)},
		{"f", false, GetBoolDefault(true)},
	} {
		c.Assert(t)
	}
}

func TestEnvironmentIntTestCases(t *testing.T) {
	for _, c := range []EnvironmentConversionTestCase{
		{"", 1, GetIntDefault(1)},

		{"1", 1, GetIntDefault(0)},
		{"3", 3, GetIntDefault(0)},

		{"malformed", 7, GetIntDefault(7)},
	} {
		c.Assert(t)
	}
}

type EnvironmentConversionTestCase struct {
	Val      string
	Expected interface{}

	GotFn func(env *Environment, key string) interface{}
}

var (
	GetBoolDefault = func(def bool) func(e *Environment, key string) interface{} {
		return func(e *Environment, key string) interface{} {
			return e.Bool(key, def)
		}
	}

	GetIntDefault = func(def int) func(e *Environment, key string) interface{} {
		return func(e *Environment, key string) interface{} {
			return e.Int(key, def)
		}
	}
)

func (c *EnvironmentConversionTestCase) Assert(t *testing.T) {
	var fetcher MockFetcher
	fetcher.On("Get", c.Val).Return(c.Val).Once()

	env := EnvironmentOf(&fetcher)
	got := c.GotFn(env, c.Val)

	if c.Expected != got {
		t.Errorf("lfs/config: expected val=%q to be %q (got: %q)", c.Val, c.Expected, got)
	}
	fetcher.AssertExpectations(t)
}
<|endoftext|>