<commit_before>// Copyright 2015 Joel Wu
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package redis

import (
	"fmt"
	"errors"
	"strconv"
	"reflect"
)

// ErrNil indicates that a reply value is nil.
var ErrNil = errors.New("nil reply")

// Int is a helper that converts a command reply to an integer. If err is not
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
// reply to an int as follows:
//
//  Reply type    Result
//  integer       int(reply), nil
//  bulk string   parsed reply, nil
//  nil           0, ErrNil
//  other         0, error
func Int(reply interface{}, err error) (int, error) {
    if err != nil {
	return 0, err
    }
    switch reply := reply.(type) {
    case int64:
	x := int(reply)
	if int64(x) != reply {
	    return 0, strconv.ErrRange
	}
	return x, nil
    case []byte:
	n, err := strconv.ParseInt(string(reply), 10, 0)
	return int(n), err
    case nil:
	return 0, ErrNil
    case redisError:
	return 0, reply
    }
    return 0, fmt.Errorf("unexpected type %T for Int", reply)
}

// Int64 is a helper that converts a command reply to 64 bit integer. If err is
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
// reply to an int64 as follows:
//
//  Reply type    Result
//  integer       reply, nil
//  bulk string   parsed reply, nil
//  nil           0, ErrNil
//  other         0, error
func Int64(reply interface{}, err error) (int64, error) {
    if err != nil {
	return 0, err
    }
    switch reply := reply.(type) {
    case int64:
	return reply, nil
    case []byte:
	n, err := strconv.ParseInt(string(reply), 10, 64)
	return n, err
    case nil:
	return 0, ErrNil
    case redisError:
	return 0, reply
    }
    return 0, fmt.Errorf("unexpected type %T for Int64", reply)
}

// Float64 is a helper that converts a command reply to 64 bit float. If err is
// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
// the reply to an int as follows:
//
//  Reply type    Result
//  bulk string   parsed reply, nil
//  nil           0, ErrNil
//  other         0, error
func Float64(reply interface{}, err error) (float64, error) {
    if err != nil {
	return 0, err
    }
    switch reply := reply.(type) {
    case []byte:
	n, err := strconv.ParseFloat(string(reply), 64)
	return n, err
    case nil:
	return 0, ErrNil
    case redisError:
	return 0, reply
    }
    return 0, fmt.Errorf("unexpected type %T for Float64", reply)
}

// String is a helper that converts a command reply to a string. If err is not
// equal to nil, then String returns "", err. Otherwise String converts the
// reply to a string as follows:
//
//  Reply type      Result
//  bulk string     string(reply), nil
//  simple string   reply, nil
//  nil             "",  ErrNil
//  other           "",  error
func String(reply interface{}, err error) (string, error) {
    if err != nil {
	return "", err
    }
    switch reply := reply.(type) {
    case []byte:
	return string(reply), nil
    case string:
	return reply, nil
    case nil:
	return "", ErrNil
    case redisError:
	return "", reply
    }
    return "", fmt.Errorf("unexpected type %T for String", reply)
}

// Bytes is a helper that converts a command reply to a slice of bytes. If err
// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
// the reply to a slice of bytes as follows:
//
//  Reply type      Result
//  bulk string     reply, nil
//  simple string   []byte(reply), nil
//  nil             nil, ErrNil
//  other           nil, error
func Bytes(reply interface{}, err error) ([]byte, error) {
    if err != nil {
	return nil, err
    }
    switch reply := reply.(type) {
    case []byte:
	return reply, nil
    case string:
	return []byte(reply), nil
    case nil:
	return nil, ErrNil
    case redisError:
	return nil, reply
    }
    return nil, fmt.Errorf("unexpected type %T for Bytes", reply)
}

// Bool is a helper that converts a command reply to a boolean. If err is not
// equal to nil, then Bool returns false, err. Otherwise Bool converts the
// reply to boolean as follows:
//
//  Reply type      Result
//  integer         value != 0, nil
//  bulk string     strconv.ParseBool(reply)
//  nil             false, ErrNil
//  other           false, error
func Bool(reply interface{}, err error) (bool, error) {
    if err != nil {
	return false, err
    }
    switch reply := reply.(type) {
    case int64:
	return reply != 0, nil
    case []byte:
	return strconv.ParseBool(string(reply))
    case nil:
	return false, ErrNil
    case redisError:
	return false, reply
    }
    return false, fmt.Errorf("unexpected type %T for Bool", reply)
}

// Values is a helper that converts an array command reply to a []interface{}.
// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
// converts the reply as follows:
//
//  Reply type      Result
//  array           reply, nil
//  nil             nil, ErrNil
//  other           nil, error
func Values(reply interface{}, err error) ([]interface{}, error) {
    if err != nil {
	return nil, err
    }
    switch reply := reply.(type) {
    case []interface{}:
	return reply, nil
    case nil:
	return nil, ErrNil
    case redisError:
	return nil, reply
    }
    return nil, fmt.Errorf("unexpected type %T for Values", reply)
}

// Ints is a helper that converts an array command reply to a []int.
// If err is not equal to nil, then Ints returns nil, err.
func Ints(reply interface{}, err error) ([]int, error) {
    values, err := Values(reply, err)
    if err != nil {
	return nil, err
    }

    ints := make([]int, len(values))
    slice := make([]interface{}, len(values))
    for i, _ := range ints {
	slice[i] = &ints[i]
    }

    if _, err = Scan(values, slice...); err != nil {
	return nil, err
    }

    return ints, nil
}

// Strings is a helper that converts an array command reply to a []string. If
// err is not equal to nil, then Strings returns nil, err. Nil array items are
// converted to "" in the output slice. Strings returns an error if an array
// item is not a bulk string or nil.
func Strings(reply interface{}, err error) ([]string, error) {
    values, err := Values(reply, err)
    if err != nil {
	return nil, err
    }

    strings := make([]string, len(values))
    slice := make([]interface{}, len(values))
    for i, _ := range strings {
	slice[i] = &strings[i]
    }

    if _, err = Scan(values, slice...); err != nil {
	return nil, err
    }

    return strings, nil
}

// StringMap is a helper that converts an array of strings (alternating key, value)
// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
// Requires an even number of values in result.
func StringMap(result interface{}, err error) (map[string]string, error) {
    values, err := Values(result, err)
    if err != nil {
	return nil, err
    }
    if len(values) % 2 != 0 {
	return nil, errors.New("expect even number elements for StringMap")
    }

    m := make(map[string]string, len(values) / 2)
    for i := 0; i < len(values); i += 2 {
	key, okKey := values[i].([]byte)
	value, okValue := values[i + 1].([]byte)
	if !okKey || !okValue {
	    return nil, errors.New("expect bulk string for StringMap")
	}
	m[string(key)] = string(value)
    }

    return m, nil
}

// Scan copies from src to the values pointed at by dest.
//
// The values pointed at by dest must be an integer, float, boolean, string,
// []byte, interface{} or slices of these types. Scan uses the standard strconv
// package to convert bulk strings to numeric and boolean types.
//
// If a dest value is nil, then the corresponding src value is skipped.
//
// If a src element is nil, then the corresponding dest value is not modified.
//
// To enable easy use of Scan in a loop, Scan returns the slice of src
// following the copied values.
func Scan(src []interface{}, dst ...interface{}) ([]interface{}, error) {
    if len(src) < len(dst) {
	return nil, errors.New("mismatch length of source and dest")
    }
    var err error
    for i, d := range dst {
	err = convertAssign(d, src[i])
	if err != nil {
	    break
	}
    }
    return src[len(dst):], err
}

func ensureLen(d reflect.Value, n int) {
    if n > d.Cap() {
	d.Set(reflect.MakeSlice(d.Type(), n, n))
    } else {
	d.SetLen(n)
    }
}

func cannotConvert(d reflect.Value, s interface{}) error {
    return fmt.Errorf("redigo: Scan cannot convert from %s to %s",
	reflect.TypeOf(s), d.Type())
}

func convertAssignBytes(d reflect.Value, s []byte) (err error) {
    switch d.Type().Kind() {
    case reflect.Float32, reflect.Float64:
	var x float64
	x, err = strconv.ParseFloat(string(s), d.Type().Bits())
	d.SetFloat(x)
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
	var x int64
	x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
	d.SetInt(x)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
	var x uint64
	x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
	d.SetUint(x)
    case reflect.Bool:
	var x bool
	x, err = strconv.ParseBool(string(s))
	d.SetBool(x)
    case reflect.String:
	d.SetString(string(s))
    case reflect.Slice:
	if d.Type().Elem().Kind() != reflect.Uint8 {
	    err = cannotConvert(d, s)
	} else {
	    d.SetBytes(s)
	}
    default:
	err = cannotConvert(d, s)
    }
    return
}

func convertAssignInt(d reflect.Value, s int64) (err error) {
    switch d.Type().Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
	d.SetInt(s)
	if d.Int() != s {
	    err = strconv.ErrRange
	    d.SetInt(0)
	}
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
	if s < 0 {
	    err = strconv.ErrRange
	} else {
	    x := uint64(s)
	    d.SetUint(x)
	    if d.Uint() != x {
		err = strconv.ErrRange
		d.SetUint(0)
	    }
	}
    case reflect.Bool:
	d.SetBool(s != 0)
    default:
	err = cannotConvert(d, s)
    }
    return
}

func convertAssignValue(d reflect.Value, s interface{}) (err error) {
    switch s := s.(type) {
    case []byte:
	err = convertAssignBytes(d, s)
    case int64:
	err = convertAssignInt(d, s)
    default:
	err = cannotConvert(d, s)
    }
    return err
}

func convertAssignValues(d reflect.Value, s []interface{}) error {
    if d.Type().Kind() != reflect.Slice {
	return cannotConvert(d, s)
    }
    ensureLen(d, len(s))
    for i := 0; i < len(s); i++ {
	if err := convertAssignValue(d.Index(i), s[i]); err != nil {
	    return err
	}
    }
    return nil
}

func convertAssign(d interface{}, s interface{}) (err error) {
    // Handle the most common destination types using type switches and
    // fall back to reflection for all other types.
    switch s := s.(type) {
    case nil:
	// ingore
    case []byte:
	switch d := d.(type) {
	case *string:
	    *d = string(s)
	case *int:
	    *d, err = strconv.Atoi(string(s))
	case *bool:
	    *d, err = strconv.ParseBool(string(s))
	case *[]byte:
	    *d = s
	case *interface{}:
	    *d = s
	case nil:
	    // skip value
	default:
	    if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
		err = cannotConvert(d, s)
	    } else {
		err = convertAssignBytes(d.Elem(), s)
	    }
	}
    case int64:
	switch d := d.(type) {
	case *int:
	    x := int(s)
	    if int64(x) != s {
		err = strconv.ErrRange
		x = 0
	    }
	    *d = x
	case *bool:
	    *d = s != 0
	case *interface{}:
	    *d = s
	case nil:
	    // skip value
	default:
	    if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
		err = cannotConvert(d, s)
	    } else {
		err = convertAssignInt(d.Elem(), s)
	    }
	}
    case []interface{}:
	switch d := d.(type) {
	case *[]interface{}:
	    *d = s
	case *interface{}:
	    *d = s
	case nil:
	    // skip value
	default:
	    if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
		err = cannotConvert(d, s)
	    } else {
		err = convertAssignValues(d.Elem(), s)
	    }
	}
    case redisError:
	err = s
    default:
	err = cannotConvert(reflect.ValueOf(d), s)
    }
    return
}
<commit_msg>add support for int64 convertion<commit_after>// Copyright 2015 Joel Wu
// Copyright 2012 Gary Burd
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package redis

import (
	"fmt"
	"errors"
	"strconv"
	"reflect"
)

// ErrNil indicates that a reply value is nil.
var ErrNil = errors.New("nil reply")

// Int is a helper that converts a command reply to an integer. If err is not
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
// reply to an int as follows:
//
//  Reply type    Result
//  integer       int(reply), nil
//  bulk string   parsed reply, nil
//  nil           0, ErrNil
//  other         0, error
func Int(reply interface{}, err error) (int, error) {
    if err != nil {
	return 0, err
    }
    switch reply := reply.(type) {
    case int64:
	x := int(reply)
	if int64(x) != reply {
	    return 0, strconv.ErrRange
	}
	return x, nil
    case []byte:
	n, err := strconv.ParseInt(string(reply), 10, 0)
	return int(n), err
    case nil:
	return 0, ErrNil
    case redisError:
	return 0, reply
    }
    return 0, fmt.Errorf("unexpected type %T for Int", reply)
}

// Int64 is a helper that converts a command reply to 64 bit integer. If err is
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
// reply to an int64 as follows:
//
//  Reply type    Result
//  integer       reply, nil
//  bulk string   parsed reply, nil
//  nil           0, ErrNil
//  other         0, error
func Int64(reply interface{}, err error) (int64, error) {
    if err != nil {
	return 0, err
    }
    switch reply := reply.(type) {
    case int64:
	return reply, nil
    case []byte:
	n, err := strconv.ParseInt(string(reply), 10, 64)
	return n, err
    case nil:
	return 0, ErrNil
    case redisError:
	return 0, reply
    }
    return 0, fmt.Errorf("unexpected type %T for Int64", reply)
}

// Float64 is a helper that converts a command reply to 64 bit float. If err is
// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
// the reply to an int as follows:
//
//  Reply type    Result
//  bulk string   parsed reply, nil
//  nil           0, ErrNil
//  other         0, error
func Float64(reply interface{}, err error) (float64, error) {
    if err != nil {
	return 0, err
    }
    switch reply := reply.(type) {
    case []byte:
	n, err := strconv.ParseFloat(string(reply), 64)
	return n, err
    case nil:
	return 0, ErrNil
    case redisError:
	return 0, reply
    }
    return 0, fmt.Errorf("unexpected type %T for Float64", reply)
}

// String is a helper that converts a command reply to a string. If err is not
// equal to nil, then String returns "", err. Otherwise String converts the
// reply to a string as follows:
//
//  Reply type      Result
//  bulk string     string(reply), nil
//  simple string   reply, nil
//  nil             "",  ErrNil
//  other           "",  error
func String(reply interface{}, err error) (string, error) {
    if err != nil {
	return "", err
    }
    switch reply := reply.(type) {
    case []byte:
	return string(reply), nil
    case string:
	return reply, nil
    case nil:
	return "", ErrNil
    case redisError:
	return "", reply
    }
    return "", fmt.Errorf("unexpected type %T for String", reply)
}

// Bytes is a helper that converts a command reply to a slice of bytes. If err
// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
// the reply to a slice of bytes as follows:
//
//  Reply type      Result
//  bulk string     reply, nil
//  simple string   []byte(reply), nil
//  nil             nil, ErrNil
//  other           nil, error
func Bytes(reply interface{}, err error) ([]byte, error) {
    if err != nil {
	return nil, err
    }
    switch reply := reply.(type) {
    case []byte:
	return reply, nil
    case string:
	return []byte(reply), nil
    case nil:
	return nil, ErrNil
    case redisError:
	return nil, reply
    }
    return nil, fmt.Errorf("unexpected type %T for Bytes", reply)
}

// Bool is a helper that converts a command reply to a boolean. If err is not
// equal to nil, then Bool returns false, err. Otherwise Bool converts the
// reply to boolean as follows:
//
//  Reply type      Result
//  integer         value != 0, nil
//  bulk string     strconv.ParseBool(reply)
//  nil             false, ErrNil
//  other           false, error
func Bool(reply interface{}, err error) (bool, error) {
    if err != nil {
	return false, err
    }
    switch reply := reply.(type) {
    case int64:
	return reply != 0, nil
    case []byte:
	return strconv.ParseBool(string(reply))
    case nil:
	return false, ErrNil
    case redisError:
	return false, reply
    }
    return false, fmt.Errorf("unexpected type %T for Bool", reply)
}

// Values is a helper that converts an array command reply to a []interface{}.
// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
// converts the reply as follows:
//
//  Reply type      Result
//  array           reply, nil
//  nil             nil, ErrNil
//  other           nil, error
func Values(reply interface{}, err error) ([]interface{}, error) {
    if err != nil {
	return nil, err
    }
    switch reply := reply.(type) {
    case []interface{}:
	return reply, nil
    case nil:
	return nil, ErrNil
    case redisError:
	return nil, reply
    }
    return nil, fmt.Errorf("unexpected type %T for Values", reply)
}

// Ints is a helper that converts an array command reply to a []int.
// If err is not equal to nil, then Ints returns nil, err.
func Ints(reply interface{}, err error) ([]int, error) {
    values, err := Values(reply, err)
    if err != nil {
	return nil, err
    }

    ints := make([]int, len(values))
    slice := make([]interface{}, len(values))
    for i, _ := range ints {
	slice[i] = &ints[i]
    }

    if _, err = Scan(values, slice...); err != nil {
	return nil, err
    }

    return ints, nil
}

// Strings is a helper that converts an array command reply to a []string. If
// err is not equal to nil, then Strings returns nil, err. Nil array items are
// converted to "" in the output slice. Strings returns an error if an array
// item is not a bulk string or nil.
func Strings(reply interface{}, err error) ([]string, error) {
    values, err := Values(reply, err)
    if err != nil {
	return nil, err
    }

    strings := make([]string, len(values))
    slice := make([]interface{}, len(values))
    for i, _ := range strings {
	slice[i] = &strings[i]
    }

    if _, err = Scan(values, slice...); err != nil {
	return nil, err
    }

    return strings, nil
}

// StringMap is a helper that converts an array of strings (alternating key, value)
// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
// Requires an even number of values in result.
func StringMap(result interface{}, err error) (map[string]string, error) {
    values, err := Values(result, err)
    if err != nil {
	return nil, err
    }
    if len(values) % 2 != 0 {
	return nil, errors.New("expect even number elements for StringMap")
    }

    m := make(map[string]string, len(values) / 2)
    for i := 0; i < len(values); i += 2 {
	key, okKey := values[i].([]byte)
	value, okValue := values[i + 1].([]byte)
	if !okKey || !okValue {
	    return nil, errors.New("expect bulk string for StringMap")
	}
	m[string(key)] = string(value)
    }

    return m, nil
}

// Scan copies from src to the values pointed at by dest.
//
// The values pointed at by dest must be an integer, float, boolean, string,
// []byte, interface{} or slices of these types. Scan uses the standard strconv
// package to convert bulk strings to numeric and boolean types.
//
// If a dest value is nil, then the corresponding src value is skipped.
//
// If a src element is nil, then the corresponding dest value is not modified.
//
// To enable easy use of Scan in a loop, Scan returns the slice of src
// following the copied values.
func Scan(src []interface{}, dst ...interface{}) ([]interface{}, error) {
    if len(src) < len(dst) {
	return nil, errors.New("mismatch length of source and dest")
    }
    var err error
    for i, d := range dst {
	err = convertAssign(d, src[i])
	if err != nil {
	    break
	}
    }
    return src[len(dst):], err
}

func ensureLen(d reflect.Value, n int) {
    if n > d.Cap() {
	d.Set(reflect.MakeSlice(d.Type(), n, n))
    } else {
	d.SetLen(n)
    }
}

func cannotConvert(d reflect.Value, s interface{}) error {
    return fmt.Errorf("redigo: Scan cannot convert from %s to %s",
	reflect.TypeOf(s), d.Type())
}

func convertAssignBytes(d reflect.Value, s []byte) (err error) {
    switch d.Type().Kind() {
    case reflect.Float32, reflect.Float64:
	var x float64
	x, err = strconv.ParseFloat(string(s), d.Type().Bits())
	d.SetFloat(x)
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
	var x int64
	x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
	d.SetInt(x)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
	var x uint64
	x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
	d.SetUint(x)
    case reflect.Bool:
	var x bool
	x, err = strconv.ParseBool(string(s))
	d.SetBool(x)
    case reflect.String:
	d.SetString(string(s))
    case reflect.Slice:
	if d.Type().Elem().Kind() != reflect.Uint8 {
	    err = cannotConvert(d, s)
	} else {
	    d.SetBytes(s)
	}
    default:
	err = cannotConvert(d, s)
    }
    return
}

func convertAssignInt(d reflect.Value, s int64) (err error) {
    switch d.Type().Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
	d.SetInt(s)
	if d.Int() != s {
	    err = strconv.ErrRange
	    d.SetInt(0)
	}
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
	if s < 0 {
	    err = strconv.ErrRange
	} else {
	    x := uint64(s)
	    d.SetUint(x)
	    if d.Uint() != x {
		err = strconv.ErrRange
		d.SetUint(0)
	    }
	}
    case reflect.Bool:
	d.SetBool(s != 0)
    default:
	err = cannotConvert(d, s)
    }
    return
}

func convertAssignValue(d reflect.Value, s interface{}) (err error) {
    switch s := s.(type) {
    case []byte:
	err = convertAssignBytes(d, s)
    case int64:
	err = convertAssignInt(d, s)
    default:
	err = cannotConvert(d, s)
    }
    return err
}

func convertAssignValues(d reflect.Value, s []interface{}) error {
    if d.Type().Kind() != reflect.Slice {
	return cannotConvert(d, s)
    }
    ensureLen(d, len(s))
    for i := 0; i < len(s); i++ {
	if err := convertAssignValue(d.Index(i), s[i]); err != nil {
	    return err
	}
    }
    return nil
}

func convertAssign(d interface{}, s interface{}) (err error) {
    // Handle the most common destination types using type switches and
    // fall back to reflection for all other types.
    switch s := s.(type) {
    case nil:
	// ingore
    case []byte:
	switch d := d.(type) {
	case *string:
	    *d = string(s)
	case *int:
	    *d, err = strconv.Atoi(string(s))
	case *int64:
	    *d, err = strconv.ParseInt(string(s), 10, 64)
	case *bool:
	    *d, err = strconv.ParseBool(string(s))
	case *[]byte:
	    *d = s
	case *interface{}:
	    *d = s
	case nil:
	    // skip value
	default:
	    if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
		err = cannotConvert(d, s)
	    } else {
		err = convertAssignBytes(d.Elem(), s)
	    }
	}
    case int64:
	switch d := d.(type) {
	case *int:
	    x := int(s)
	    if int64(x) != s {
		err = strconv.ErrRange
		x = 0
	    }
	    *d = x
	case *int64:
	    *d = s
	case *bool:
	    *d = s != 0
	case *interface{}:
	    *d = s
	case nil:
	    // skip value
	default:
	    if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
		err = cannotConvert(d, s)
	    } else {
		err = convertAssignInt(d.Elem(), s)
	    }
	}
    case []interface{}:
	switch d := d.(type) {
	case *[]interface{}:
	    *d = s
	case *interface{}:
	    *d = s
	case nil:
	    // skip value
	default:
	    if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
		err = cannotConvert(d, s)
	    } else {
		err = convertAssignValues(d.Elem(), s)
	    }
	}
    case redisError:
	err = s
    default:
	err = cannotConvert(reflect.ValueOf(d), s)
    }
    return
}
<|endoftext|>


<commit_before>package logyard

import (
	"log"
	"time"
)

// Retryer is a simple retryer
type Retryer struct {
	recentAttempts int
	lastAttempt    time.Time
}

func NewRetryer() *Retryer {
	return new(Retryer)
}

// Wait appropriately waits until next try
func (retry *Retryer) Wait(msg string) {
	retry.recentAttempts += 1
	if retry.recentAttempts > 3 {
		log.Printf("%s; retrying after %d seconds...",
			msg, retry.recentAttempts)
		time.Sleep(time.Duration(retry.recentAttempts) * time.Second)
	} else {
		log.Printf("%s; retrying...", msg)
	}

	// if the retry happens after a long time, reset our stats
	if time.Now().Sub(retry.lastAttempt).Seconds() > 60 {
		log.Println("Resetting retry attempts; ", time.Now().Sub(retry.lastAttempt).Seconds())
		retry.recentAttempts = 0
	}

	retry.lastAttempt = time.Now()
}
<commit_msg>drain retry logic: set a bound on exponential wait time = 5 minutes<commit_after>package logyard

import (
	"log"
	"time"
)

type Retryer struct {
	recentAttempts int
	lastAttempt    time.Time
}

func NewRetryer() *Retryer {
	return new(Retryer)
}

const MAX_WAIT_SECONDS = 60 * 5 // 5 minutes

// Wait appropriately waits until next try (exponential backoff delay)
func (retry *Retryer) Wait(msg string) {
	retry.recentAttempts += 1
	if retry.recentAttempts > 3 {
		waitSeconds := retry.recentAttempts
		if waitSeconds > MAX_WAIT_SECONDS {
			waitSeconds = MAX_WAIT_SECONDS
		}
		log.Printf("%s; retrying after %d seconds...",
			msg, waitSeconds)
		time.Sleep(time.Duration(waitSeconds) * time.Second)
	} else {
		log.Printf("%s; retrying...", msg)
	}

	// reset our stats if there weren't any retry attempts in the last
	// minute.
	if time.Now().Sub(retry.lastAttempt).Seconds() > 60 {
		log.Println("Resetting retry attempts; ", time.Now().Sub(retry.lastAttempt).Seconds())
		retry.recentAttempts = 0
	}

	retry.lastAttempt = time.Now()
}
<|endoftext|>


<commit_before>package mirango

import (
	"strings"

	"github.com/mirango/validation"
)

type Route struct {
	mirango                 *Mirango
	parent                  *Route
	children                Routes
	operations              *Operations
	path                    string
	schemes                 []string
	accepts                 []string
	returns                 []string
	middleware              []Middleware
	params                  *Params
	routeNotFoundHandler    Handler
	methodNotAllowedHandler Handler
	panicHandler            Handler

	slices           []string
	paramIndices     map[int]int
	paramNames       []string
	containsWildCard bool
}

type Routes []*Route

func (r Routes) Len() int {
	return len(r)
}

func (r Routes) Swap(i int, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r Routes) Less(i int, j int) bool {
	if r[i].containsWildCard && !r[j].containsWildCard {
		return false
	}
	if !r[i].containsWildCard && r[j].containsWildCard {
		return true
	}
	if r[i].containsWildCard && r[j].containsWildCard {
		// check position of wildcard
	}
	return false
}

func NewRoute(path string) *Route {
	return &Route{
		path:         cleanPath(path),
		operations:   NewOperations(),
		params:       NewParams(),
		paramIndices: map[int]int{},
	}
}

type pathParam struct {
	Key   string
	Value string
}

type pathParams []*pathParam

func (r *Route) GetRoot() *Route {
	if r.parent != nil {
		return r.parent.GetRoot()
	}
	return r
}

func (r *Route) processPath() {
	//path := r.path
	r.paramNames = nil
	r.paramIndices = map[int]int{}
	slices := strings.Split(r.path[1:], "/")

	if len(slices) == 0 {
		panic("path is empty")
	}

	r.slices = slices

	// check that every var name has length more than 0

	for i, s := range slices {
		param := strings.LastIndex(s, ":")
		wildcardParam := strings.LastIndex(s, "*")
		if param > wildcardParam {
			r.paramNames = append(r.paramNames, s[param+1:])
			r.paramIndices[i] = param
		} else if param < wildcardParam && i == len(slices)-1 {
			r.paramNames = append(r.paramNames, s[wildcardParam+1:])
			r.paramIndices[i] = wildcardParam
			r.containsWildCard = true
			r.children = nil
		} else if param == -1 && wildcardParam == -1 {
			r.paramIndices[i] = -1
			continue
		}
	}

	// check all paths
}

func (r *Route) GetNotFoundHandler() interface{} {
	return nil
}

func (r *Route) match(path string) (nr *Route, p pathParams) {
	route := r

	params := 0

	if r.parent == nil {
		nPath := r.path
		if !strings.HasPrefix(path, nPath) {
			r = nil
			p = nil
			return
		}
		path = path[len(nPath):]
	}

	slices := strings.Split(path[1:], "/")

	param := -1
	wildcardParam := -1

look:
	for _, c := range route.children {
	walk:
		for j := range c.slices {
			param = -1
			wildcardParam = -1
			if c.containsWildCard {
				if j == len(c.slices)-1 {
					wildcardParam = c.paramIndices[j]
				}
			} else {
				param = c.paramIndices[j]
			}

			if param == -1 && wildcardParam == -1 {
				if c.slices[j] != slices[j] {
					if j == len(slices)-1 && j == len(c.slices)-1 {
						nr = nil
						p = nil
						return
					}
					continue look
				} else {
					if j == len(slices)-1 && j == len(c.slices)-1 {
						nr = c
						return
					} else if j == len(c.slices)-1 {
						route = c
						slices = slices[j+1:]
						params = 0
						goto look
					} else if j == len(slices)-1 {
						nr = nil
						p = nil
						return
					}
					continue walk
				}
			} else if (param > wildcardParam && (len(slices[j]) <= param || slices[j][:param] != c.slices[j][:param])) ||
				((param < wildcardParam) && (!c.containsWildCard || len(slices[j]) <= wildcardParam || slices[j][:wildcardParam] != c.slices[j][:wildcardParam])) {
				continue look
			} else if param > wildcardParam {
				p = append(p, &pathParam{c.paramNames[params], slices[j][param:]})
				params++
				if j == len(slices)-1 && j == len(c.slices)-1 {
					nr = c
					return
				} else if j == len(c.slices)-1 {
					route = c
					slices = slices[j+1:]
					params = 0
					goto look
				} else if j == len(slices)-1 {
					nr = nil
					p = nil
					return
				}
				continue walk
			} else if param < wildcardParam {
				p = append(p, &pathParam{c.paramNames[params], strings.TrimSuffix(slices[j][wildcardParam:]+"/"+strings.Join(slices[j+1:], "/"), "/")})
				nr = c
				return
			}
		}
	}
	return
}

func (r *Route) BuildPath(v ...interface{}) string {
	return ""
}

func (r *Route) setMirango() {
	rr := r.GetRoot()
	if rr != nil {
		r.mirango = rr.mirango
	}
}

func (r *Route) Sort() {

}

func (r *Route) GetPath() string {
	return r.path
}

func (r *Route) GetFullPath() string {
	if r.parent == nil {
		return r.path
	}
	return r.parent.GetFullPath() + r.path
}

func (r *Route) Path(path string) {
	r.path = cleanPath(path)
}

func (r *Route) Route(path string) *Route {
	nr := NewRoute(path)
	return r.AddRoute(nr)
}

func (r *Route) AddRoute(nr *Route) *Route {
	if nr == nil {
		panic("route is nil")
	}

	if nr.parent != nil {
		nr = nr.Clone()
	}

	if r.containsWildCard {
		panic("wildcard routes can not have sub-routes")
	}

	nr.parent = r

	nr.processPath()

	// check path

	r.children = append(r.children, nr)

	nr.setMirango()
	return nr
}

func (r *Route) GET(h interface{}) *Operation {
	o := GET(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) POST(h interface{}) *Operation {
	o := POST(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) PUT(h interface{}) *Operation {
	o := PUT(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) DELETE(h interface{}) *Operation {
	o := DELETE(r, h)
	r.operations.Append(o)
	return o
}

// Copy returns a pointer to a copy of the route.
// It does not copy parent, operations, nor deep-copy the params.
func (r *Route) Clone() *Route {
	route := NewRoute(r.path)
	// for _, cr := range rs {
	// 	route.AddRoute(cr.Copy())
	// }
	// route.path = r.path
	// route.operations = r.operations
	// route.params = r.params
	// route.middleware = r.middleware
	return route
}

func (r *Route) Params(params ...*Param) *Route {
	r.params.Set(params...)
	return r
}

func (r *Route) GetMiddleware() []Middleware {
	return r.middleware
}

func (r *Route) GetAllMiddleware() []Middleware {
	if r.parent != nil {
		return middlewareUnion(r.middleware, r.parent.GetAllMiddleware())
	}
	return r.middleware
}

func (r *Route) GetParams() *Params {
	return r.params
}

func (r *Route) GetAllParams() *Params {
	params := r.params.Clone()
	if r.parent != nil {
		params.Union(r.parent.GetAllParams())
	}
	return params
}

func (r *Route) GetSchemes() []string {
	return r.schemes
}

func (r *Route) GetAllSchemes() []string {
	if r.parent != nil {
		return stringsUnion(r.schemes, r.parent.GetAllSchemes())
	}
	return r.schemes
}

func (r *Route) GetAccepts() []string {
	return r.accepts
}

func (r *Route) GetAllAccepts() []string {
	if r.parent != nil {
		return stringsUnion(r.accepts, r.parent.GetAllAccepts())
	}
	return r.accepts
}

func (r *Route) GetReturns() []string {
	return r.returns
}

func (r *Route) GetAllReturns() []string {
	if r.parent != nil {
		return stringsUnion(r.returns, r.parent.GetAllReturns())
	}
	return r.returns
}

func (r *Route) ServeHTTP(c *Context, params pathParams) interface{} {
	for _, o := range r.operations.operations {
		for _, m := range o.methods {
			if m == c.Request.Request.Method {
				err := setPathParams(c, o.params, params)
				if err != nil {
					return err
				}
				return o.ServeHTTP(c)
			}
		}
	}
	// method not allowed
	return nil
}

func setPathParams(c *Context, params *Params, pathParams pathParams) *Error {
	for _, p := range params.Get() {
		var pv *validation.Value
		if p.IsIn(IN_PATH) {
			var v string
			for i, par := range pathParams {
				if par.Key == p.name {
					v = par.Value
					pathParams = append(pathParams[:i], pathParams[i+1:]...)
					break
				}
			}
			if v == "" {
				return nil //error
			}
			pv = validation.NewValue(p.name, v, "path", p.GetAs())
			c.Input[p.name] = pv
		}
	}
	return nil
}

func cleanPath(path string) string {
	path = strings.ToLower(path)
	path = strings.Trim(path, "/")
	slices := strings.Split(path, "/")
	nPath := ""
	for _, s := range slices {
		if len(s) > 0 {
			nPath = nPath + "/" + s
		}
	}
	return nPath

}
<commit_msg>Defined 2 new functions to append Operation and Middleware for Route struct<commit_after>package mirango

import (
	"strings"

	"github.com/mirango/validation"
)

type Route struct {
	mirango                 *Mirango
	parent                  *Route
	children                Routes
	operations              *Operations
	path                    string
	schemes                 []string
	accepts                 []string
	returns                 []string
	middleware              []Middleware
	params                  *Params
	routeNotFoundHandler    Handler
	methodNotAllowedHandler Handler
	panicHandler            Handler

	slices           []string
	paramIndices     map[int]int
	paramNames       []string
	containsWildCard bool
}

type Routes []*Route

func (r Routes) Len() int {
	return len(r)
}

func (r Routes) Swap(i int, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r Routes) Less(i int, j int) bool {
	if r[i].containsWildCard && !r[j].containsWildCard {
		return false
	}
	if !r[i].containsWildCard && r[j].containsWildCard {
		return true
	}
	if r[i].containsWildCard && r[j].containsWildCard {
		// check position of wildcard
	}
	return false
}

func NewRoute(path string) *Route {
	return &Route{
		path:         cleanPath(path),
		operations:   NewOperations(),
		params:       NewParams(),
		paramIndices: map[int]int{},
	}
}

type pathParam struct {
	Key   string
	Value string
}

type pathParams []*pathParam

func (r *Route) GetRoot() *Route {
	if r.parent != nil {
		return r.parent.GetRoot()
	}
	return r
}

func (r *Route) processPath() {
	//path := r.path
	r.paramNames = nil
	r.paramIndices = map[int]int{}
	slices := strings.Split(r.path[1:], "/")

	if len(slices) == 0 {
		panic("path is empty")
	}

	r.slices = slices

	// check that every var name has length more than 0

	for i, s := range slices {
		param := strings.LastIndex(s, ":")
		wildcardParam := strings.LastIndex(s, "*")
		if param > wildcardParam {
			r.paramNames = append(r.paramNames, s[param+1:])
			r.paramIndices[i] = param
		} else if param < wildcardParam && i == len(slices)-1 {
			r.paramNames = append(r.paramNames, s[wildcardParam+1:])
			r.paramIndices[i] = wildcardParam
			r.containsWildCard = true
			r.children = nil
		} else if param == -1 && wildcardParam == -1 {
			r.paramIndices[i] = -1
			continue
		}
	}

	// check all paths
}

func (r *Route) GetNotFoundHandler() interface{} {
	return nil
}

func (r *Route) match(path string) (nr *Route, p pathParams) {
	route := r

	params := 0

	if r.parent == nil {
		nPath := r.path
		if !strings.HasPrefix(path, nPath) {
			r = nil
			p = nil
			return
		}
		path = path[len(nPath):]
	}

	slices := strings.Split(path[1:], "/")

	param := -1
	wildcardParam := -1

look:
	for _, c := range route.children {
	walk:
		for j := range c.slices {
			param = -1
			wildcardParam = -1
			if c.containsWildCard {
				if j == len(c.slices)-1 {
					wildcardParam = c.paramIndices[j]
				}
			} else {
				param = c.paramIndices[j]
			}

			if param == -1 && wildcardParam == -1 {
				if c.slices[j] != slices[j] {
					if j == len(slices)-1 && j == len(c.slices)-1 {
						nr = nil
						p = nil
						return
					}
					continue look
				} else {
					if j == len(slices)-1 && j == len(c.slices)-1 {
						nr = c
						return
					} else if j == len(c.slices)-1 {
						route = c
						slices = slices[j+1:]
						params = 0
						goto look
					} else if j == len(slices)-1 {
						nr = nil
						p = nil
						return
					}
					continue walk
				}
			} else if (param > wildcardParam && (len(slices[j]) <= param || slices[j][:param] != c.slices[j][:param])) ||
				((param < wildcardParam) && (!c.containsWildCard || len(slices[j]) <= wildcardParam || slices[j][:wildcardParam] != c.slices[j][:wildcardParam])) {
				continue look
			} else if param > wildcardParam {
				p = append(p, &pathParam{c.paramNames[params], slices[j][param:]})
				params++
				if j == len(slices)-1 && j == len(c.slices)-1 {
					nr = c
					return
				} else if j == len(c.slices)-1 {
					route = c
					slices = slices[j+1:]
					params = 0
					goto look
				} else if j == len(slices)-1 {
					nr = nil
					p = nil
					return
				}
				continue walk
			} else if param < wildcardParam {
				p = append(p, &pathParam{c.paramNames[params], strings.TrimSuffix(slices[j][wildcardParam:]+"/"+strings.Join(slices[j+1:], "/"), "/")})
				nr = c
				return
			}
		}
	}
	return
}

func (r *Route) BuildPath(v ...interface{}) string {
	return ""
}

func (r *Route) setMirango() {
	rr := r.GetRoot()
	if rr != nil {
		r.mirango = rr.mirango
	}
}

func (r *Route) Sort() {

}

func (r *Route) GetPath() string {
	return r.path
}

func (r *Route) GetFullPath() string {
	if r.parent == nil {
		return r.path
	}
	return r.parent.GetFullPath() + r.path
}

func (r *Route) Path(path string) {
	r.path = cleanPath(path)
}

func (r *Route) Route(path string) *Route {
	nr := NewRoute(path)
	return r.AddRoute(nr)
}

func (r *Route) AddRoute(nr *Route) *Route {
	if nr == nil {
		panic("route is nil")
	}

	if nr.parent != nil {
		nr = nr.Clone()
	}

	if r.containsWildCard {
		panic("wildcard routes can not have sub-routes")
	}

	nr.parent = r

	nr.processPath()

	// check path

	r.children = append(r.children, nr)

	nr.setMirango()
	return nr
}

func (r *Route) GET(h interface{}) *Operation {
	o := GET(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) POST(h interface{}) *Operation {
	o := POST(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) PUT(h interface{}) *Operation {
	o := PUT(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) DELETE(h interface{}) *Operation {
	o := DELETE(r, h)
	r.operations.Append(o)
	return o
}

func (r *Route) Operations(ops ...*Operation) *Route {
	for _, o := range ops {
		o = o.Clone()
		o.route = r
		r.operations.Append(o)
	}
	return r
}

// Copy returns a pointer to a copy of the route.
// It does not copy parent, operations, nor deep-copy the params.
func (r *Route) Clone() *Route {
	route := NewRoute(r.path)
	// for _, cr := range rs {
	// 	route.AddRoute(cr.Copy())
	// }
	// route.path = r.path
	// route.operations = r.operations
	// route.params = r.params
	// route.middleware = r.middleware
	return route
}

func (r *Route) Params(params ...*Param) *Route {
	r.params.Set(params...)
	return r
}

func (r *Route) With(mw ...interface{}) *Route {
	for i := 0; i < len(mw); i++ {
		switch t := mw[i].(type) {
		case Middleware:
			r.middleware = append(r.middleware, t)
		case MiddlewareFunc:
			r.middleware = append(r.middleware, t)
		case func(Handler) Handler:
			r.middleware = append(r.middleware, MiddlewareFunc(t))
		}
	}
	return r
}

func (r *Route) GetMiddleware() []Middleware {
	return r.middleware
}

func (r *Route) GetAllMiddleware() []Middleware {
	if r.parent != nil {
		return middlewareUnion(r.middleware, r.parent.GetAllMiddleware())
	}
	return r.middleware
}

func (r *Route) GetParams() *Params {
	return r.params
}

func (r *Route) GetAllParams() *Params {
	params := r.params.Clone()
	if r.parent != nil {
		params.Union(r.parent.GetAllParams())
	}
	return params
}

func (r *Route) GetSchemes() []string {
	return r.schemes
}

func (r *Route) GetAllSchemes() []string {
	if r.parent != nil {
		return stringsUnion(r.schemes, r.parent.GetAllSchemes())
	}
	return r.schemes
}

func (r *Route) GetAccepts() []string {
	return r.accepts
}

func (r *Route) GetAllAccepts() []string {
	if r.parent != nil {
		return stringsUnion(r.accepts, r.parent.GetAllAccepts())
	}
	return r.accepts
}

func (r *Route) GetReturns() []string {
	return r.returns
}

func (r *Route) GetAllReturns() []string {
	if r.parent != nil {
		return stringsUnion(r.returns, r.parent.GetAllReturns())
	}
	return r.returns
}

func (r *Route) ServeHTTP(c *Context, params pathParams) interface{} {
	for _, o := range r.operations.operations {
		for _, m := range o.methods {
			if m == c.Request.Request.Method {
				err := setPathParams(c, o.params, params)
				if err != nil {
					return err
				}
				return o.ServeHTTP(c)
			}
		}
	}
	// method not allowed
	return nil
}

func setPathParams(c *Context, params *Params, pathParams pathParams) *Error {
	for _, p := range params.Get() {
		var pv *validation.Value
		if p.IsIn(IN_PATH) {
			var v string
			for i, par := range pathParams {
				if par.Key == p.name {
					v = par.Value
					pathParams = append(pathParams[:i], pathParams[i+1:]...)
					break
				}
			}
			if v == "" {
				return nil //error
			}
			pv = validation.NewValue(p.name, v, "path", p.GetAs())
			c.Input[p.name] = pv
		}
	}
	return nil
}

func cleanPath(path string) string {
	path = strings.ToLower(path)
	path = strings.Trim(path, "/")
	slices := strings.Split(path, "/")
	nPath := ""
	for _, s := range slices {
		if len(s) > 0 {
			nPath = nPath + "/" + s
		}
	}
	return nPath

}
<|endoftext|>


<commit_before>package gobot

import (
	"fmt"
	"log"
	"os"
	"os/signal"

	multierror "github.com/hashicorp/go-multierror"
)

// JSONRobot a JSON representation of a Robot.
type JSONRobot struct {
	Name        string            `json:"name"`
	Commands    []string          `json:"commands"`
	Connections []*JSONConnection `json:"connections"`
	Devices     []*JSONDevice     `json:"devices"`
}

// NewJSONRobot returns a JSONRobot given a Robot.
func NewJSONRobot(robot *Robot) *JSONRobot {
	jsonRobot := &JSONRobot{
		Name:        robot.Name,
		Commands:    []string{},
		Connections: []*JSONConnection{},
		Devices:     []*JSONDevice{},
	}

	for command := range robot.Commands() {
		jsonRobot.Commands = append(jsonRobot.Commands, command)
	}

	robot.Devices().Each(func(device Device) {
		jsonDevice := NewJSONDevice(device)
		jsonRobot.Connections = append(jsonRobot.Connections, NewJSONConnection(robot.Connection(jsonDevice.Connection)))
		jsonRobot.Devices = append(jsonRobot.Devices, jsonDevice)
	})
	return jsonRobot
}

// Robot is a named entity that manages a collection of connections and devices.
// It contains its own work routine and a collection of
// custom commands to control a robot remotely via the Gobot api.
type Robot struct {
	Name        string
	Work        func()
	connections *Connections
	devices     *Devices
	trap        func(chan os.Signal)
	AutoRun     bool
	done        chan bool
	Commander
	Eventer
}

// Robots is a collection of Robot
type Robots []*Robot

// Len returns the amount of Robots in the collection.
func (r *Robots) Len() int {
	return len(*r)
}

// Start calls the Start method of each Robot in the collection
func (r *Robots) Start(args ...interface{}) (err error) {
	autoRun := true
	if args[0] != nil {
		autoRun = args[0].(bool)
	}
	for _, robot := range *r {
		if rerr := robot.Start(autoRun); rerr != nil {
			err = multierror.Append(err, rerr)
			return
		}
	}
	return
}

// Stop calls the Stop method of each Robot in the collection
func (r *Robots) Stop() (err error) {
	for _, robot := range *r {
		if rerr := robot.Stop(); rerr != nil {
			err = multierror.Append(err, rerr)
			return
		}
	}
	return
}

// Each enumerates through the Robots and calls specified callback function.
func (r *Robots) Each(f func(*Robot)) {
	for _, robot := range *r {
		f(robot)
	}
}

// NewRobot returns a new Robot given optional accepts:
//
// 	[]Connection: Connections which are automatically started and stopped with the robot
//	[]Device: Devices which are automatically started and stopped with the robot
//	func(): The work routine the robot will execute once all devices and connections have been initialized and started
// A name will be automaically generated if no name is supplied.
func NewRobot(v ...interface{}) *Robot {
	r := &Robot{
		Name:        fmt.Sprintf("%X", Rand(int(^uint(0)>>1))),
		connections: &Connections{},
		devices:     &Devices{},
		done:        make(chan bool, 1),
		trap: func(c chan os.Signal) {
			signal.Notify(c, os.Interrupt)
		},
		AutoRun:   true,
		Work:      nil,
		Eventer:   NewEventer(),
		Commander: NewCommander(),
	}

	for i := range v {
		switch v[i].(type) {
		case string:
			r.Name = v[i].(string)
		case []Connection:
			log.Println("Initializing connections...")
			for _, connection := range v[i].([]Connection) {
				c := r.AddConnection(connection)
				log.Println("Initializing connection", c.Name(), "...")
			}
		case []Device:
			log.Println("Initializing devices...")
			for _, device := range v[i].([]Device) {
				d := r.AddDevice(device)
				log.Println("Initializing device", d.Name(), "...")
			}
		case func():
			r.Work = v[i].(func())
		}
	}

	log.Println("Robot", r.Name, "initialized.")

	return r
}

// Start a Robot's Connections, Devices, and work.
func (r *Robot) Start(args ...interface{}) (err error) {
	if len(args) > 0 && args[0] != nil {
		r.AutoRun = args[0].(bool)
	}
	log.Println("Starting Robot", r.Name, "...")
	if cerr := r.Connections().Start(); cerr != nil {
		err = multierror.Append(err, cerr)
		return
	}
	if derr := r.Devices().Start(); derr != nil {
		err = multierror.Append(err, derr)
		return
	}
	if r.Work == nil {
		r.Work = func() {}
	}

	log.Println("Starting work...")
	go func() {
		r.Work()
		<-r.done
	}()

	if r.AutoRun {
		c := make(chan os.Signal, 1)
		r.trap(c)
		if err != nil {
			// there was an error during start, so we immediately pass the interrupt
			// in order to disconnect the initialized robots, connections and devices
			c <- os.Interrupt
		}

		// waiting for interrupt coming on the channel
		<-c

		// Stop calls the Stop method on itself, if we are "auto-running".
		r.Stop()
	}

	return
}

// Stop stops a Robot's connections and Devices
func (r *Robot) Stop() (err error) {
	log.Println("Stopping Robot", r.Name, "...")
	err = r.Devices().Halt()
	err = r.Connections().Finalize()
	r.done <- true
	return err
}

// Devices returns all devices associated with this Robot.
func (r *Robot) Devices() *Devices {
	return r.devices
}

// AddDevice adds a new Device to the robots collection of devices. Returns the
// added device.
func (r *Robot) AddDevice(d Device) Device {
	*r.devices = append(*r.Devices(), d)
	return d
}

// Device returns a device given a name. Returns nil if the Device does not exist.
func (r *Robot) Device(name string) Device {
	if r == nil {
		return nil
	}
	for _, device := range *r.devices {
		if device.Name() == name {
			return device
		}
	}
	return nil
}

// Connections returns all connections associated with this robot.
func (r *Robot) Connections() *Connections {
	return r.connections
}

// AddConnection adds a new connection to the robots collection of connections.
// Returns the added connection.
func (r *Robot) AddConnection(c Connection) Connection {
	*r.connections = append(*r.Connections(), c)
	return c
}

// Connection returns a connection given a name. Returns nil if the Connection
// does not exist.
func (r *Robot) Connection(name string) Connection {
	if r == nil {
		return nil
	}
	for _, connection := range *r.connections {
		if connection.Name() == name {
			return connection
		}
	}
	return nil
}
<commit_msg>core: use multierror when handling Robot Stop<commit_after>package gobot

import (
	"fmt"
	"log"
	"os"
	"os/signal"

	multierror "github.com/hashicorp/go-multierror"
)

// JSONRobot a JSON representation of a Robot.
type JSONRobot struct {
	Name        string            `json:"name"`
	Commands    []string          `json:"commands"`
	Connections []*JSONConnection `json:"connections"`
	Devices     []*JSONDevice     `json:"devices"`
}

// NewJSONRobot returns a JSONRobot given a Robot.
func NewJSONRobot(robot *Robot) *JSONRobot {
	jsonRobot := &JSONRobot{
		Name:        robot.Name,
		Commands:    []string{},
		Connections: []*JSONConnection{},
		Devices:     []*JSONDevice{},
	}

	for command := range robot.Commands() {
		jsonRobot.Commands = append(jsonRobot.Commands, command)
	}

	robot.Devices().Each(func(device Device) {
		jsonDevice := NewJSONDevice(device)
		jsonRobot.Connections = append(jsonRobot.Connections, NewJSONConnection(robot.Connection(jsonDevice.Connection)))
		jsonRobot.Devices = append(jsonRobot.Devices, jsonDevice)
	})
	return jsonRobot
}

// Robot is a named entity that manages a collection of connections and devices.
// It contains its own work routine and a collection of
// custom commands to control a robot remotely via the Gobot api.
type Robot struct {
	Name        string
	Work        func()
	connections *Connections
	devices     *Devices
	trap        func(chan os.Signal)
	AutoRun     bool
	done        chan bool
	Commander
	Eventer
}

// Robots is a collection of Robot
type Robots []*Robot

// Len returns the amount of Robots in the collection.
func (r *Robots) Len() int {
	return len(*r)
}

// Start calls the Start method of each Robot in the collection
func (r *Robots) Start(args ...interface{}) (err error) {
	autoRun := true
	if args[0] != nil {
		autoRun = args[0].(bool)
	}
	for _, robot := range *r {
		if rerr := robot.Start(autoRun); rerr != nil {
			err = multierror.Append(err, rerr)
			return
		}
	}
	return
}

// Stop calls the Stop method of each Robot in the collection
func (r *Robots) Stop() (err error) {
	for _, robot := range *r {
		if rerr := robot.Stop(); rerr != nil {
			err = multierror.Append(err, rerr)
			return
		}
	}
	return
}

// Each enumerates through the Robots and calls specified callback function.
func (r *Robots) Each(f func(*Robot)) {
	for _, robot := range *r {
		f(robot)
	}
}

// NewRobot returns a new Robot given optional accepts:
//
// 	[]Connection: Connections which are automatically started and stopped with the robot
//	[]Device: Devices which are automatically started and stopped with the robot
//	func(): The work routine the robot will execute once all devices and connections have been initialized and started
// A name will be automaically generated if no name is supplied.
func NewRobot(v ...interface{}) *Robot {
	r := &Robot{
		Name:        fmt.Sprintf("%X", Rand(int(^uint(0)>>1))),
		connections: &Connections{},
		devices:     &Devices{},
		done:        make(chan bool, 1),
		trap: func(c chan os.Signal) {
			signal.Notify(c, os.Interrupt)
		},
		AutoRun:   true,
		Work:      nil,
		Eventer:   NewEventer(),
		Commander: NewCommander(),
	}

	for i := range v {
		switch v[i].(type) {
		case string:
			r.Name = v[i].(string)
		case []Connection:
			log.Println("Initializing connections...")
			for _, connection := range v[i].([]Connection) {
				c := r.AddConnection(connection)
				log.Println("Initializing connection", c.Name(), "...")
			}
		case []Device:
			log.Println("Initializing devices...")
			for _, device := range v[i].([]Device) {
				d := r.AddDevice(device)
				log.Println("Initializing device", d.Name(), "...")
			}
		case func():
			r.Work = v[i].(func())
		}
	}

	log.Println("Robot", r.Name, "initialized.")

	return r
}

// Start a Robot's Connections, Devices, and work.
func (r *Robot) Start(args ...interface{}) (err error) {
	if len(args) > 0 && args[0] != nil {
		r.AutoRun = args[0].(bool)
	}
	log.Println("Starting Robot", r.Name, "...")
	if cerr := r.Connections().Start(); cerr != nil {
		err = multierror.Append(err, cerr)
		return
	}
	if derr := r.Devices().Start(); derr != nil {
		err = multierror.Append(err, derr)
		return
	}
	if r.Work == nil {
		r.Work = func() {}
	}

	log.Println("Starting work...")
	go func() {
		r.Work()
		<-r.done
	}()

	if r.AutoRun {
		c := make(chan os.Signal, 1)
		r.trap(c)
		if err != nil {
			// there was an error during start, so we immediately pass the interrupt
			// in order to disconnect the initialized robots, connections and devices
			c <- os.Interrupt
		}

		// waiting for interrupt coming on the channel
		<-c

		// Stop calls the Stop method on itself, if we are "auto-running".
		r.Stop()
	}

	return
}

// Stop stops a Robot's connections and Devices
func (r *Robot) Stop() error {
	var result error
	log.Println("Stopping Robot", r.Name, "...")
	err := r.Devices().Halt()
	if err != nil {
		result = multierror.Append(result, err)
	}
	err = r.Connections().Finalize()
	if err != nil {
		result = multierror.Append(result, err)
	}

	r.done <- true
	return result
}

// Devices returns all devices associated with this Robot.
func (r *Robot) Devices() *Devices {
	return r.devices
}

// AddDevice adds a new Device to the robots collection of devices. Returns the
// added device.
func (r *Robot) AddDevice(d Device) Device {
	*r.devices = append(*r.Devices(), d)
	return d
}

// Device returns a device given a name. Returns nil if the Device does not exist.
func (r *Robot) Device(name string) Device {
	if r == nil {
		return nil
	}
	for _, device := range *r.devices {
		if device.Name() == name {
			return device
		}
	}
	return nil
}

// Connections returns all connections associated with this robot.
func (r *Robot) Connections() *Connections {
	return r.connections
}

// AddConnection adds a new connection to the robots collection of connections.
// Returns the added connection.
func (r *Robot) AddConnection(c Connection) Connection {
	*r.connections = append(*r.Connections(), c)
	return c
}

// Connection returns a connection given a name. Returns nil if the Connection
// does not exist.
func (r *Robot) Connection(name string) Connection {
	if r == nil {
		return nil
	}
	for _, connection := range *r.connections {
		if connection.Name() == name {
			return connection
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package mango

import (
	"fmt"
	"net/http"
	"path"
	"regexp"
	"strings"
	"time"
)

type H map[string]interface{}

type Router struct {
	// Prepare
	PreHandler Handler
	// 404
	NotFoundHandler Handler
	// 500
	ErrorHandler Handler
	// See Router.StrictSlash(). This defines the flag for new routes.
	strictSlash bool
	//routers     map[*regexp.Regexp]interface{}
	routers map[*regexp.Regexp]map[string]Handler
}

// NewRouter returns a new router instance.
func NewRouter(urls map[string]map[string]Handler, n Handler, e Handler, p Handler) *Router {
	route := make(map[*regexp.Regexp]map[string]Handler)

	for k, _ := range urls {
		re, err := regexp.Compile("^" + k + "$")
		if err != nil {
			fmt.Println("url is error:", k)
		}
		route[re] = urls[k]
	}
	return &Router{routers: route, NotFoundHandler: n, ErrorHandler: e, PreHandler: p}
}

func (self *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Clean path to canonical form and redirect.
	var ctx *HTTPRequest
	start := time.Now()
	var st, l int
	defer func() {
		if err := recover(); err != nil {
			st = http.StatusInternalServerError
			var e = fmt.Sprintf("%s", err)
			fmt.Println(e)
			if self.ErrorHandler == nil {
				w.WriteHeader(http.StatusInternalServerError)
				if Debug {
					w.Write([]byte(e))
				} else {
					w.Write([]byte("Internal Server Error"))
				}
			} else {
				self.ErrorHandler(ctx)
			}
		}
		use := float64(time.Now().UnixNano() - start.UnixNano())

		var ip string
		if ctx != nil {
			st = ctx.StatusCode
			l = ctx.Length
			ip = ctx.RemoteAddr
		} else {
			if st == 0 {
				st = 200
			}
			l = 0
			ip = strings.Split(req.RemoteAddr, ":")[0]
		}

		fmt.Printf("%s - - [%d-%02d-%02d %02d:%02d:%02d] \"%s %s \" %d %d %.6f\n",
			ip, start.Year(), start.Month(), start.Day(),
			start.Hour(), start.Minute(), start.Second(),
			req.Method, req.URL.Path, st, l, use/1000000)
	}()

	if p := cleanPath(req.URL.Path); p != req.URL.Path {
		w.Header().Set("Location", p)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}
	flag := false
	for k, _ := range self.routers {
		m := k.FindStringSubmatch(req.URL.Path)

		if len(m) != 0 {
			ctx = &HTTPRequest{}
			ctx.Init(w, req, m)
			if self.PreHandler != nil {
				self.PreHandler(ctx)
			}

			if f, ok := self.routers[k][req.Method]; ok {
				f(ctx)
			} else {
				http.Error(w, "Method Not Allowed", 405)
			}
			return
		}
	}
	if !flag {
		st = http.StatusNotFound
		if self.NotFoundHandler == nil {
			http.NotFoundHandler().ServeHTTP(w, req)
		} else {
			ctx := &HTTPRequest{}
			ctx.Init(w, req, []string{})
			self.NotFoundHandler(ctx)
		}
	}
}

func cleanPath(p string) string {
	if p == "" {
		return "/"
	}
	if p[0] != '/' {
		p = "/" + p
	}
	np := path.Clean(p)
	// path.Clean removes trailing slash except for root;
	// put the trailing slash back if necessary.
	if p[len(p)-1] == '/' && np != "/" {
		np += "/"
	}
	return np
}
<commit_msg>optimize http method query<commit_after>package mango

import (
	"fmt"
	"net/http"
	"path"
	"regexp"
	"strings"
	"time"
)

type H map[string]interface{}

type Router struct {
	// Prepare
	PreHandler Handler
	// 404
	NotFoundHandler Handler
	// 500
	ErrorHandler Handler
	// See Router.StrictSlash(). This defines the flag for new routes.
	strictSlash bool
	//routers     map[*regexp.Regexp]interface{}
	routers map[*regexp.Regexp]map[string]Handler
}

// NewRouter returns a new router instance.
func NewRouter(urls map[string]map[string]Handler, n Handler, e Handler, p Handler) *Router {
	route := make(map[*regexp.Regexp]map[string]Handler)

	for k, _ := range urls {
		re, err := regexp.Compile("^" + k + "$")
		if err != nil {
			fmt.Println("url is error:", k)
		}
		route[re] = urls[k]
	}
	return &Router{routers: route, NotFoundHandler: n, ErrorHandler: e, PreHandler: p}
}

func (self *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// Clean path to canonical form and redirect.
	var ctx *HTTPRequest
	start := time.Now()
	var st, l int
	defer func() {
		if err := recover(); err != nil {
			st = http.StatusInternalServerError
			var e = fmt.Sprintf("%s", err)
			fmt.Println(e)
			if self.ErrorHandler == nil {
				w.WriteHeader(http.StatusInternalServerError)
				if Debug {
					w.Write([]byte(e))
				} else {
					w.Write([]byte("Internal Server Error"))
				}
			} else {
				self.ErrorHandler(ctx)
			}
		}
		use := float64(time.Now().UnixNano() - start.UnixNano())

		var ip string
		if ctx != nil {
			st = ctx.StatusCode
			l = ctx.Length
			ip = ctx.RemoteAddr
		} else {
			if st == 0 {
				st = 200
			}
			l = 0
			ip = strings.Split(req.RemoteAddr, ":")[0]
		}

		fmt.Printf("%s - - [%d-%02d-%02d %02d:%02d:%02d] \"%s %s \" %d %d %.6f\n",
			ip, start.Year(), start.Month(), start.Day(),
			start.Hour(), start.Minute(), start.Second(),
			req.Method, req.URL.Path, st, l, use/1000000)
	}()

	if p := cleanPath(req.URL.Path); p != req.URL.Path {
		w.Header().Set("Location", p)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}
	flag := false
	for k, v := range self.routers {
		m := k.FindStringSubmatch(req.URL.Path)

		if len(m) != 0 {
			ctx = &HTTPRequest{}
			ctx.Init(w, req, m)
			if self.PreHandler != nil {
				self.PreHandler(ctx)
			}

			if f, ok := v[req.Method]; ok {
				f(ctx)
			} else {
				http.Error(w, "Method Not Allowed", 405)
			}
			return
		}
	}
	if !flag {
		st = http.StatusNotFound
		if self.NotFoundHandler == nil {
			http.NotFoundHandler().ServeHTTP(w, req)
		} else {
			ctx := &HTTPRequest{}
			ctx.Init(w, req, []string{})
			self.NotFoundHandler(ctx)
		}
	}
}

func cleanPath(p string) string {
	if p == "" {
		return "/"
	}
	if p[0] != '/' {
		p = "/" + p
	}
	np := path.Clean(p)
	// path.Clean removes trailing slash except for root;
	// put the trailing slash back if necessary.
	if p[len(p)-1] == '/' && np != "/" {
		np += "/"
	}
	return np
}
<|endoftext|>


<commit_before>package braspag

const (
	URLDEV  = "https://homologacao.pagador.com.br"
	URLPROD = "https://www.pagador.com.br"
	//
	SERVICE_QUERY       = "/services/pagadorQuery.asmx"
	SERVICE_TRANSACTION = "/webservice/pagadorTransaction.asmx"
	//
	SOAPACTION_AUTHORIZE_TRANSACTION  = "https://www.pagador.com.br/webservice/pagador/AuthorizeTransaction"
	SOAPACTION_CAPTURE_CC_TRANSACTION = "https://www.pagador.com.br/webservice/pagador/CaptureCreditCardTransaction"
	SOAPACTION_REFUND_CC_TRANSACTION  = "https://www.pagador.com.br/webservice/pagador/RefundCreditCardTransaction"
	SOAPACTION_VOID_CC_TRANSACTION    = "https://www.pagador.com.br/webservice/pagador/VoidCreditCardTransaction"
	//
	SOAPACTION_QUERY_GETBOLETODATA = "https://www.pagador.com.br/query/pagadorquery/GetBoletoData"
	SOAPACTION_QUERY_GETORDERDATA  = "https://www.pagador.com.br/query/pagadorquery/GetOrderData"
	//
	PM_BOLETO_BRADESCO              = 6
	PM_BOLETO_CAIXA                 = 7
	PM_BOLETO_HSBC                  = 8
	PM_BOLETO_BANCODOBRASIL         = 9
	PM_BOLETO_BANCOREAL             = 10
	PM_BOLETO_CITIBANK              = 13
	PM_BOLETO_ITAU                  = 14
	PM_BOLETO_SANTANDER             = 124
	PM_CIELO_VISAELECTRON           = 123
	PM_CIELO_VISA                   = 500
	PM_CIELO_MASTERCARD             = 501
	PM_CIELO_AMEX                   = 502
	PM_CIELO_DINERS                 = 503
	PM_CIELO_ELO                    = 504
	PM_BANORTE_VISA                 = 505
	PM_BANORTE_MASTERCARD           = 506
	PM_BANORTE_DINERS               = 507
	PM_BANORTE_AMEX                 = 508
	PM_REDECARD_VISA                = 509
	PM_REDECARD_MASTERCARD          = 510
	PM_REDECARD_DINERS              = 511
	PM_PAGOSONLINE_VISA             = 512
	PM_PAGOSONLINE_MASTERCARD       = 513
	PM_PAGOSONLINE_AMEX             = 514
	PM_PAGOSONLINE_DINERS           = 515
	PM_PAYVISION_VISA               = 516
	PM_PAYVISION_MASTERCARD         = 517
	PM_PAYVISION_DINERS             = 518
	PM_PAYVISION_AMEX               = 519
	PM_BANORTECARGOSAUTO_VISA       = 520
	PM_BANORTECARGOSAUTO_MASTERCARD = 521
	PM_BANORTECARGOSAUTO_DINERS     = 522
	PM_AMEX_2P                      = 523
	PM_SITEF_VISA                   = 524
	PM_SITEF_MASTERCARD             = 525
	PM_SITEF_AMEX                   = 526
	PM_SITEF_DINERS                 = 527
	PM_SITEF_HIPERCARD              = 528
	PM_SITEF_LEADER                 = 529
	PM_SITEF_AURA                   = 530
	PM_SITEF_SANTANDERVISA          = 531
	PM_SITEF_SANTANDERMASTERCARD    = 532
	PM_SIMULATED_USD                = 995
	PM_SIMULATED_EUR                = 996
	PM_SIMULATED_BRL                = 997
	//
	CCDRSTAT_CAPTURED       = 0 // byte? // Transação Capturada
	CCDRSTAT_AUTHORIZED     = 1 // byte? // Transação Autorizada, pendente de captura.
	CCDRSTAT_NOT_AUTHORIZED = 2 // byte? // Transação não Autorizada, pela Adquirente.
	CCDRSTAT_DEQUAL_ERROR   = 3 // byte? // Transação com erro Desqualificante.
	CCDRSTAT_WAITING        = 4 // byte? // Transação aguardando resposta.
	//
	PAYMENTPLAN_AVISTA                      = 0
	PAYMENTPLAN_PARCEL_ESTABELECIMENTO      = 1
	PAYMENTPLAN_PARCEL_EMISSOR              = 2
	PAYMENTPLAN_IATA_PARCEL_ESTABELECIMENTO = 3 // SOMENTE COMPANHIAS AÉREAS
	PAYMENTPLAN_IATA_PARCEL_EMISSOR         = 4 // SOMENTE COMPANHIAS AÉREAS
	PAYMENTPLAN_IATA_AVISTA                 = 5 // SOMENTE COMPANHIAS AÉREAS
	//
	TRTYPE_INVALID           = 0
	TRTYPE_PRE               = 1
	TRTYPE_AUTO              = 2
	TRTYPE_PRE_AUTHENTICATE  = 3 // 3DS (?)
	TRTYPE_AUTO_AUTHENTICATE = 4
	TRTYPE_PRE_RECURRING     = 5
	TRTYPE_AUTO_RECURRING    = 6
)
<commit_msg>more paym methods<commit_after>package braspag

const (
	URLDEV  = "https://homologacao.pagador.com.br"
	URLPROD = "https://www.pagador.com.br"
	//
	SERVICE_QUERY       = "/services/pagadorQuery.asmx"
	SERVICE_TRANSACTION = "/webservice/pagadorTransaction.asmx"
	//
	SOAPACTION_AUTHORIZE_TRANSACTION  = "https://www.pagador.com.br/webservice/pagador/AuthorizeTransaction"
	SOAPACTION_CAPTURE_CC_TRANSACTION = "https://www.pagador.com.br/webservice/pagador/CaptureCreditCardTransaction"
	SOAPACTION_REFUND_CC_TRANSACTION  = "https://www.pagador.com.br/webservice/pagador/RefundCreditCardTransaction"
	SOAPACTION_VOID_CC_TRANSACTION    = "https://www.pagador.com.br/webservice/pagador/VoidCreditCardTransaction"
	//
	SOAPACTION_QUERY_GETBOLETODATA = "https://www.pagador.com.br/query/pagadorquery/GetBoletoData"
	SOAPACTION_QUERY_GETORDERDATA  = "https://www.pagador.com.br/query/pagadorquery/GetOrderData"
	//
	PM_BOLETO_BRADESCO              = 6
	PM_BOLETO_CAIXA                 = 7
	PM_BOLETO_HSBC                  = 8
	PM_BOLETO_BANCODOBRASIL         = 9
	PM_BOLETO_BANCOREAL             = 10
	PM_BOLETO_CITIBANK              = 13
	PM_BOLETO_ITAU                  = 14
	PM_BOLETO_SANTANDER             = 124
	PM_CIELO_VISAELECTRON           = 123
	PM_CIELO_VISA                   = 500
	PM_CIELO_MASTERCARD             = 501
	PM_CIELO_AMEX                   = 502
	PM_CIELO_DINERS                 = 503
	PM_CIELO_ELO                    = 504
	PM_CIELO_DISCOVER               = 543
	PM_CIELO_JCB                    = 544
	PM_CIELO_AURA                   = 545
	PM_BANORTE_VISA                 = 505
	PM_BANORTE_MASTERCARD           = 506
	PM_BANORTE_DINERS               = 507
	PM_BANORTE_AMEX                 = 508
	PM_REDECARD_VISA                = 509
	PM_REDECARD_MASTERCARD          = 510
	PM_REDECARD_DINERS              = 511
	PM_PAGOSONLINE_VISA             = 512
	PM_PAGOSONLINE_MASTERCARD       = 513
	PM_PAGOSONLINE_AMEX             = 514
	PM_PAGOSONLINE_DINERS           = 515
	PM_PAYVISION_VISA               = 516
	PM_PAYVISION_MASTERCARD         = 517
	PM_PAYVISION_DINERS             = 518
	PM_PAYVISION_AMEX               = 519
	PM_BANORTECARGOSAUTO_VISA       = 520
	PM_BANORTECARGOSAUTO_MASTERCARD = 521
	PM_BANORTECARGOSAUTO_DINERS     = 522
	PM_AMEX_2P                      = 523
	PM_SITEF_VISA                   = 524
	PM_SITEF_MASTERCARD             = 525
	PM_SITEF_AMEX                   = 526
	PM_SITEF_DINERS                 = 527
	PM_SITEF_HIPERCARD              = 528
	PM_SITEF_LEADER                 = 529
	PM_SITEF_AURA                   = 530
	PM_SITEF_SANTANDERVISA          = 531
	PM_SITEF_SANTANDERMASTERCARD    = 532
	PM_SIMULATED_USD                = 995
	PM_SIMULATED_EUR                = 996
	PM_SIMULATED_BRL                = 997
	//
	CCDRSTAT_CAPTURED       = 0 // byte? // Transação Capturada
	CCDRSTAT_AUTHORIZED     = 1 // byte? // Transação Autorizada, pendente de captura.
	CCDRSTAT_NOT_AUTHORIZED = 2 // byte? // Transação não Autorizada, pela Adquirente.
	CCDRSTAT_DEQUAL_ERROR   = 3 // byte? // Transação com erro Desqualificante.
	CCDRSTAT_WAITING        = 4 // byte? // Transação aguardando resposta.
	//
	PAYMENTPLAN_AVISTA                      = 0
	PAYMENTPLAN_PARCEL_ESTABELECIMENTO      = 1
	PAYMENTPLAN_PARCEL_EMISSOR              = 2
	PAYMENTPLAN_IATA_PARCEL_ESTABELECIMENTO = 3 // SOMENTE COMPANHIAS AÉREAS
	PAYMENTPLAN_IATA_PARCEL_EMISSOR         = 4 // SOMENTE COMPANHIAS AÉREAS
	PAYMENTPLAN_IATA_AVISTA                 = 5 // SOMENTE COMPANHIAS AÉREAS
	//
	TRTYPE_INVALID           = 0
	TRTYPE_PRE               = 1
	TRTYPE_AUTO              = 2
	TRTYPE_PRE_AUTHENTICATE  = 3 // 3DS (?)
	TRTYPE_AUTO_AUTHENTICATE = 4
	TRTYPE_PRE_RECURRING     = 5
	TRTYPE_AUTO_RECURRING    = 6
)
<|endoftext|>


<commit_before>// Package rpool provides a resource pool.
package rpool

import (
	"container/list"
	"errors"
	"io"
	"sync"
	"time"

	"github.com/facebookgo/stats"
)

var (
	errPoolClosed  = errors.New("rpool: pool has been closed")
	errCloseAgain  = errors.New("rpool: Pool.Close called more than once")
	closedSentinel = sentinelCloser(1)
	newSentinel    = sentinelCloser(2)
)

// Pool manages the life cycle of resources.
type Pool struct {
	// New is used to create a new resource when necessary.
	New func() (io.Closer, error)

	// CloseErrorHandler will be called when an error occurs closing a resource.
	CloseErrorHandler func(err error)

	// Stats is optional and allows for the pool to provide stats for various
	// interesting events in the proxy.
	Stats stats.Client

	// Max defines the maximum number of concurrently allocated resources.
	Max uint

	// MinIdle defines the number of minimum idle resources. These number of
	// resources are kept around when the idle cleanup kicks in.
	MinIdle uint

	// IdleTimeout defines the duration of idle time after which a resource will
	// be closed.
	IdleTimeout time.Duration

	// ClosePoolSize defines the number of concurrent goroutines that will close
	// resources.
	ClosePoolSize uint

	manageOnce sync.Once
	acquire    chan chan io.Closer
	release    chan io.Closer
	discard    chan struct{}
	close      chan chan error
	closers    chan io.Closer
}

// Acquire will pull a resource from the pool or create a new one if necessary.
func (p *Pool) Acquire() (io.Closer, error) {
	defer stats.BumpTime(p.Stats, "acquire.time").End()
	p.manageOnce.Do(p.goManage)
	r := make(chan io.Closer)
	p.acquire <- r
	c := <-r

	// sentinel value indicates the pool is closed
	if c == closedSentinel {
		return nil, errPoolClosed
	}

	// need to allocate a new resource
	if c == newSentinel {
		t := stats.BumpTime(p.Stats, "acquire.new.time")
		c, err := p.New()
		t.End()
		stats.BumpSum(p.Stats, "acquire.new", 1)
		if err != nil {
			stats.BumpSum(p.Stats, "acquire.error.new", 1)
			// discard our assumed checked out resource since we failed to New
			p.discard <- struct{}{}
		}
		return c, err
	}

	// successfully acquired from pool
	return c, nil
}

// Release puts the resource back into the pool.
func (p *Pool) Release(c io.Closer) {
	p.manageOnce.Do(p.goManage)
	p.release <- c
}

// Discard closes the resource and indicates we're throwing it away.
func (p *Pool) Discard(c io.Closer) {
	p.manageOnce.Do(p.goManage)
	p.closers <- c
	p.discard <- struct{}{}
}

// Close closes the pool and its resources. It waits until all acquired
// resources are released or discarded. It is an error to call Acquire after
// closing the pool.
func (p *Pool) Close() error {
	defer stats.BumpTime(p.Stats, "shutdown.time").End()
	p.manageOnce.Do(p.goManage)
	r := make(chan error)
	p.close <- r
	return <-r
}

func (p *Pool) goManage() {
	if p.Max == 0 {
		panic("no max configured")
	}
	if p.IdleTimeout.Nanoseconds() == 0 {
		panic("no idle timeout configured")
	}
	if p.ClosePoolSize == 0 {
		panic("no close pool size configured")
	}

	p.release = make(chan io.Closer)
	p.acquire = make(chan chan io.Closer)
	p.discard = make(chan struct{})
	p.close = make(chan chan error)
	p.closers = make(chan io.Closer, p.Max)
	go p.manage()
}

type entry struct {
	resource io.Closer
	use      time.Time
}

func (p *Pool) manage() {
	// setup goroutines to close resources
	var closeWG sync.WaitGroup
	closeWG.Add(int(p.ClosePoolSize))
	for i := uint(0); i < p.ClosePoolSize; i++ {
		go func() {
			defer closeWG.Done()
			for c := range p.closers {
				t := stats.BumpTime(p.Stats, "close.time")
				stats.BumpSum(p.Stats, "close", 1)
				if err := c.Close(); err != nil {
					stats.BumpSum(p.Stats, "close.error", 1)
					p.CloseErrorHandler(err)
				}
				t.End()
			}
		}()
	}

	// setup a ticker to report various averages every minute. if we don't have a
	// Stats implementation provided, we Stop it so it never ticks.
	statsTicker := time.NewTicker(time.Minute)
	if p.Stats == nil {
		statsTicker.Stop()
	}

	resources := []entry{}
	out := uint(0)
	waiting := list.New()
	idleTicker := time.NewTicker(p.IdleTimeout)
	closed := false
	var closeResponse chan error
	for {
		if closed && out == 0 && waiting.Len() == 0 {
			if p.Stats != nil {
				statsTicker.Stop()
			}

			// all waiting acquires are done, all resources have been released.
			// now just wait for all resources to close.
			close(p.closers)
			closeWG.Wait()

			// close internal channels.
			close(p.acquire)
			close(p.release)
			close(p.discard)
			close(p.close)

			// return a response to the original close.
			closeResponse <- nil

			return
		}

		select {
		case r := <-p.acquire:
			// if closed, new acquire calls are rejected
			if closed {
				r <- closedSentinel
				stats.BumpSum(p.Stats, "acquire.error.closed", 1)
				continue
			}

			// acquire from pool
			if cl := len(resources); cl > 0 {
				c := resources[cl-1]
				r <- c.resource
				resources = resources[:cl-1]
				out++
				stats.BumpSum(p.Stats, "acquire.pool", 1)
				continue
			}

			// max resources already in use, need to block & wait
			if out == p.Max {
				waiting.PushBack(r)
				stats.BumpSum(p.Stats, "acquire.waiting", 1)
				continue
			}

			// Make a new resource in the calling goroutine by sending it a
			// newSentinel. We assume it's checked out. Acquire will discard if
			// creating a new resource fails.
			out++
			r <- newSentinel
		case c := <-p.release:
			// pass it to someone who's waiting
			if e := waiting.Front(); e != nil {
				r := waiting.Remove(e).(chan io.Closer)
				r <- c
				continue
			}

			if out == 0 {
				panic("releasing more than acquired")
			}

			// no longer out
			out--

			// no one is waiting, and we're closed, schedule it to be closed
			if closed {
				p.closers <- c
				continue
			}

			// put it back in our pool
			resources = append(resources, entry{resource: c, use: time.Now()})
		case <-p.discard:
			// we can make a new one if someone is waiting. no need to decrement out
			// in this case since we assume this new one is checked out. Acquire will
			// discard if creating a new resource fails.
			if e := waiting.Front(); e != nil {
				r := waiting.Remove(e).(chan io.Closer)
				r <- newSentinel
				continue
			}

			if out == 0 {
				panic("discarding more than acquired")
			}

			// otherwise we lost a resource and dont need a new one right away
			out--
		case now := <-idleTicker.C:
			eligibleOffset := len(resources) - int(p.MinIdle)

			// less than min idle, nothing to do
			if eligibleOffset <= 0 {
				continue
			}

			t := stats.BumpTime(p.Stats, "idle.cleanup.time")

			// cleanup idle resources
			idleLen := 0
			for _, e := range resources[:eligibleOffset] {
				if now.Sub(e.use) < p.IdleTimeout {
					break
				}
				p.closers <- e.resource
				idleLen++
			}

			// move the remaining resources to the beginning
			resources = resources[:copy(resources, resources[idleLen:])]

			t.End()
			stats.BumpSum(p.Stats, "idle.closed", float64(idleLen))
		case <-statsTicker.C:
			// We can assume if we hit this then p.Stats is not nil
			p.Stats.BumpAvg("waiting", float64(waiting.Len()))
			p.Stats.BumpAvg("idle", float64(len(resources)))
			p.Stats.BumpAvg("out", float64(out))
			p.Stats.BumpAvg("alive", float64(uint(len(resources))+out))
		case r := <-p.close:
			// cant call close if already closing
			if closed {
				r <- errCloseAgain
				continue
			}

			closed = true
			idleTicker.Stop() // stop idle processing

			// close idle since if we have idle, implicitly no one is waiting
			for _, e := range resources {
				p.closers <- e.resource
			}

			closeResponse = r
		}
	}
}

type sentinelCloser int

func (s sentinelCloser) Close() error {
	panic("should never get called")
}
<commit_msg>doc nit<commit_after>// Package rpool provides a resource pool.
package rpool

import (
	"container/list"
	"errors"
	"io"
	"sync"
	"time"

	"github.com/facebookgo/stats"
)

var (
	errPoolClosed  = errors.New("rpool: pool has been closed")
	errCloseAgain  = errors.New("rpool: Pool.Close called more than once")
	closedSentinel = sentinelCloser(1)
	newSentinel    = sentinelCloser(2)
)

// Pool manages the life cycle of resources.
type Pool struct {
	// New is used to create a new resource when necessary.
	New func() (io.Closer, error)

	// CloseErrorHandler will be called when an error occurs closing a resource.
	CloseErrorHandler func(err error)

	// Stats is optional and allows for the pool to provide stats for various
	// interesting events in the pool.
	Stats stats.Client

	// Max defines the maximum number of concurrently allocated resources.
	Max uint

	// MinIdle defines the number of minimum idle resources. These number of
	// resources are kept around when the idle cleanup kicks in.
	MinIdle uint

	// IdleTimeout defines the duration of idle time after which a resource will
	// be closed.
	IdleTimeout time.Duration

	// ClosePoolSize defines the number of concurrent goroutines that will close
	// resources.
	ClosePoolSize uint

	manageOnce sync.Once
	acquire    chan chan io.Closer
	release    chan io.Closer
	discard    chan struct{}
	close      chan chan error
	closers    chan io.Closer
}

// Acquire will pull a resource from the pool or create a new one if necessary.
func (p *Pool) Acquire() (io.Closer, error) {
	defer stats.BumpTime(p.Stats, "acquire.time").End()
	p.manageOnce.Do(p.goManage)
	r := make(chan io.Closer)
	p.acquire <- r
	c := <-r

	// sentinel value indicates the pool is closed
	if c == closedSentinel {
		return nil, errPoolClosed
	}

	// need to allocate a new resource
	if c == newSentinel {
		t := stats.BumpTime(p.Stats, "acquire.new.time")
		c, err := p.New()
		t.End()
		stats.BumpSum(p.Stats, "acquire.new", 1)
		if err != nil {
			stats.BumpSum(p.Stats, "acquire.error.new", 1)
			// discard our assumed checked out resource since we failed to New
			p.discard <- struct{}{}
		}
		return c, err
	}

	// successfully acquired from pool
	return c, nil
}

// Release puts the resource back into the pool.
func (p *Pool) Release(c io.Closer) {
	p.manageOnce.Do(p.goManage)
	p.release <- c
}

// Discard closes the resource and indicates we're throwing it away.
func (p *Pool) Discard(c io.Closer) {
	p.manageOnce.Do(p.goManage)
	p.closers <- c
	p.discard <- struct{}{}
}

// Close closes the pool and its resources. It waits until all acquired
// resources are released or discarded. It is an error to call Acquire after
// closing the pool.
func (p *Pool) Close() error {
	defer stats.BumpTime(p.Stats, "shutdown.time").End()
	p.manageOnce.Do(p.goManage)
	r := make(chan error)
	p.close <- r
	return <-r
}

func (p *Pool) goManage() {
	if p.Max == 0 {
		panic("no max configured")
	}
	if p.IdleTimeout.Nanoseconds() == 0 {
		panic("no idle timeout configured")
	}
	if p.ClosePoolSize == 0 {
		panic("no close pool size configured")
	}

	p.release = make(chan io.Closer)
	p.acquire = make(chan chan io.Closer)
	p.discard = make(chan struct{})
	p.close = make(chan chan error)
	p.closers = make(chan io.Closer, p.Max)
	go p.manage()
}

type entry struct {
	resource io.Closer
	use      time.Time
}

func (p *Pool) manage() {
	// setup goroutines to close resources
	var closeWG sync.WaitGroup
	closeWG.Add(int(p.ClosePoolSize))
	for i := uint(0); i < p.ClosePoolSize; i++ {
		go func() {
			defer closeWG.Done()
			for c := range p.closers {
				t := stats.BumpTime(p.Stats, "close.time")
				stats.BumpSum(p.Stats, "close", 1)
				if err := c.Close(); err != nil {
					stats.BumpSum(p.Stats, "close.error", 1)
					p.CloseErrorHandler(err)
				}
				t.End()
			}
		}()
	}

	// setup a ticker to report various averages every minute. if we don't have a
	// Stats implementation provided, we Stop it so it never ticks.
	statsTicker := time.NewTicker(time.Minute)
	if p.Stats == nil {
		statsTicker.Stop()
	}

	resources := []entry{}
	out := uint(0)
	waiting := list.New()
	idleTicker := time.NewTicker(p.IdleTimeout)
	closed := false
	var closeResponse chan error
	for {
		if closed && out == 0 && waiting.Len() == 0 {
			if p.Stats != nil {
				statsTicker.Stop()
			}

			// all waiting acquires are done, all resources have been released.
			// now just wait for all resources to close.
			close(p.closers)
			closeWG.Wait()

			// close internal channels.
			close(p.acquire)
			close(p.release)
			close(p.discard)
			close(p.close)

			// return a response to the original close.
			closeResponse <- nil

			return
		}

		select {
		case r := <-p.acquire:
			// if closed, new acquire calls are rejected
			if closed {
				r <- closedSentinel
				stats.BumpSum(p.Stats, "acquire.error.closed", 1)
				continue
			}

			// acquire from pool
			if cl := len(resources); cl > 0 {
				c := resources[cl-1]
				r <- c.resource
				resources = resources[:cl-1]
				out++
				stats.BumpSum(p.Stats, "acquire.pool", 1)
				continue
			}

			// max resources already in use, need to block & wait
			if out == p.Max {
				waiting.PushBack(r)
				stats.BumpSum(p.Stats, "acquire.waiting", 1)
				continue
			}

			// Make a new resource in the calling goroutine by sending it a
			// newSentinel. We assume it's checked out. Acquire will discard if
			// creating a new resource fails.
			out++
			r <- newSentinel
		case c := <-p.release:
			// pass it to someone who's waiting
			if e := waiting.Front(); e != nil {
				r := waiting.Remove(e).(chan io.Closer)
				r <- c
				continue
			}

			if out == 0 {
				panic("releasing more than acquired")
			}

			// no longer out
			out--

			// no one is waiting, and we're closed, schedule it to be closed
			if closed {
				p.closers <- c
				continue
			}

			// put it back in our pool
			resources = append(resources, entry{resource: c, use: time.Now()})
		case <-p.discard:
			// we can make a new one if someone is waiting. no need to decrement out
			// in this case since we assume this new one is checked out. Acquire will
			// discard if creating a new resource fails.
			if e := waiting.Front(); e != nil {
				r := waiting.Remove(e).(chan io.Closer)
				r <- newSentinel
				continue
			}

			if out == 0 {
				panic("discarding more than acquired")
			}

			// otherwise we lost a resource and dont need a new one right away
			out--
		case now := <-idleTicker.C:
			eligibleOffset := len(resources) - int(p.MinIdle)

			// less than min idle, nothing to do
			if eligibleOffset <= 0 {
				continue
			}

			t := stats.BumpTime(p.Stats, "idle.cleanup.time")

			// cleanup idle resources
			idleLen := 0
			for _, e := range resources[:eligibleOffset] {
				if now.Sub(e.use) < p.IdleTimeout {
					break
				}
				p.closers <- e.resource
				idleLen++
			}

			// move the remaining resources to the beginning
			resources = resources[:copy(resources, resources[idleLen:])]

			t.End()
			stats.BumpSum(p.Stats, "idle.closed", float64(idleLen))
		case <-statsTicker.C:
			// We can assume if we hit this then p.Stats is not nil
			p.Stats.BumpAvg("waiting", float64(waiting.Len()))
			p.Stats.BumpAvg("idle", float64(len(resources)))
			p.Stats.BumpAvg("out", float64(out))
			p.Stats.BumpAvg("alive", float64(uint(len(resources))+out))
		case r := <-p.close:
			// cant call close if already closing
			if closed {
				r <- errCloseAgain
				continue
			}

			closed = true
			idleTicker.Stop() // stop idle processing

			// close idle since if we have idle, implicitly no one is waiting
			for _, e := range resources {
				p.closers <- e.resource
			}

			closeResponse = r
		}
	}
}

type sentinelCloser int

func (s sentinelCloser) Close() error {
	panic("should never get called")
}
<|endoftext|>