<commit_before>package QesyDb

import (
	"database/sql"
	"fmt"
	"github.com/Qesy/QesyGo"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var Db *sql.DB
var OpenLog int = 0

type Model struct {
	Cond      interface{}
	Insert    map[string]string
	InsertArr []map[string]string
	Update    map[string]string
	Field     string
	Table     string
	Index     string
	Limit     interface{}
	Sort      string
	GroupBy   string
	IsDeug    int
	Tx        *sql.Tx
}

// Connect  is a method with a sql.
func Connect(connStr string) {
	sqlDb, err := sql.Open("mysql", connStr)
	//defer sqlDb.Close()
	sqlDb.SetConnMaxLifetime(1800)
	sqlDb.SetMaxIdleConns(0)
	sqlDb.SetMaxOpenConns(600)
	if err != nil {
		log.Fatal("mysql connect error")
	}
	err = sqlDb.Ping()
	if err != nil {
		log.Fatal("mysql ping error")
	}
	QesyGo.Println("mysql connect sueccss")
	Db = sqlDb
}

func Begin() (*sql.Tx, error) {
	return Db.Begin()
}

func Rollback(tx *sql.Tx) error {
	return tx.Rollback()
}

func Commit(tx *sql.Tx) error {
	return tx.Commit()
}

// ExecSelectIndex  is a method with a sql.
func (m *Model) ExecSelectIndex() (map[string]map[string]string, error) {
	resultsSlice, _ := m.execSelect()
	retArr := map[string]map[string]string{}
	for _, v := range resultsSlice {
		if v[m.Index] == "" {
			continue
		}
		retArr[v[m.Index]] = v
	}
	m.Clean()
	return retArr, nil
}

func (m *Model) ExecSelect() ([]map[string]string, error) {
	ret, err := m.execSelect()
	m.Clean()
	return ret, err
}

// ExecSelect is a method with a sql.
func (m *Model) execSelect() ([]map[string]string, error) {
	cond := m.getSQLCond()
	field := m.getSQLField()
	sort := m.getSort()
	groupby := m.getGroupBy()
	limit := m.getSQLLimite()
	sqlStr := "SELECT " + field + " FROM " + m.Table + cond + groupby + sort + limit + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	resultsSlice := []map[string]string{}
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
		if err != nil {
			logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
			return resultsSlice, err
		}
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
		if err != nil {
			logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
			return resultsSlice, err
		}
	}
	//defer stmt.Close()

	rows, err := stmt.Query()
	defer rows.Close()
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return resultsSlice, err
	}
	fields, err := rows.Columns()
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return resultsSlice, err
	}

	for rows.Next() {
		result := make(map[string]string)
		var scanResultContainers []interface{}
		for i := 0; i < len(fields); i++ {
			var scanResultContainer interface{}
			scanResultContainers = append(scanResultContainers, &scanResultContainer)
		}
		if err := rows.Scan(scanResultContainers...); err != nil {
			return resultsSlice, err
		}
		for k, v := range fields {
			rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[k]))
			if rawValue.Interface() == nil {
				continue
			}
			rawType := reflect.TypeOf(rawValue.Interface())
			rawVal := reflect.ValueOf(rawValue.Interface())
			var str string
			switch rawType.Kind() {
			case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				str = strconv.FormatInt(rawVal.Int(), 10)
				result[v] = str
			case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				str = strconv.FormatUint(rawVal.Uint(), 10)
				result[v] = str
			case reflect.Float32, reflect.Float64:
				str = strconv.FormatFloat(rawVal.Float(), 'f', -1, 64)
				result[v] = str
			case reflect.Slice:
				if rawType.Elem().Kind() == reflect.Uint8 {
					result[v] = string(rawVal.Interface().([]byte))
					break
				}
			case reflect.String:
				str = rawVal.String()
				result[v] = str
			case reflect.Struct:
				str = rawVal.Interface().(time.Time).Format("2006-01-02 15:04:05.000 -0700")
				result[v] = str
			case reflect.Bool:
				if rawVal.Bool() {
					result[v] = "1"
				} else {
					result[v] = "0"
				}
			}
		}
		resultsSlice = append(resultsSlice, result)
	}
	return resultsSlice, nil
}

func (m *Model) ExecSelectOne() (map[string]string, error) {
	m.SetLimit([2]int{0, 1})
	resultsSlice, err := m.ExecSelect()
	if len(resultsSlice) == 0 {
		return map[string]string{}, err
	}
	return resultsSlice[0], nil
}

func (m *Model) ExecUpdate() (sql.Result, error) {
	updateStr := m.getSQLUpdate()
	condStr := m.getSQLCond()
	sqlStr := "UPDATE " + m.Table + " SET " + updateStr + condStr + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecInsert() (sql.Result, error) {
	insert := m.getSQLInsert()
	sqlStr := "INSERT INTO " + m.Table + " " + insert + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecInsertBatch() (sql.Result, error) {
	insert := m.getSQLInsertArr()
	sqlStr := "INSERT INTO " + m.Table + " " + insert + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecReplace() (sql.Result, error) {
	insert := m.getSQLInsert()
	sqlStr := "REPLACE INTO " + m.Table + " " + insert + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecDelete() (sql.Result, error) {
	condStr := m.getSQLCond()
	sqlStr := "DELETE FROM " + m.Table + condStr + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) Exec(sqlStr string) (sql.Result, error) {
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func GetLastInsertId(result sql.Result) (int64, error) {
	return result.LastInsertId()
}

func GetRowsAffected(result sql.Result) (int64, error) {
	return result.RowsAffected()
}

func (m *Model) getSQLCond() string {
	if str, ok := m.Cond.(string); ok {
		return str
	}
	var strArr []string
	if arr, ok := m.Cond.(map[string]string); ok {
		var strArr []string
		for k, v := range arr {
			strArr = append(strArr, k+"='"+v+"'")
		}
		if len(strArr) == 0 {
			return ""
		}
		return " WHERE " + strings.Join(strArr, " && ")
	}
	if arr, ok := m.Cond.(map[string]interface{}); ok {
		for k, v := range arr {
			if isStr, ok := v.(string); ok {
				strArr = append(strArr, k+"='"+isStr+"'")
			}
			if isStrArrTmp, ok := v.([]string); ok {
				isStrArr := make([]string, len(isStrArrTmp))
				copy(isStrArr, isStrArrTmp)
				for k, v := range isStrArr {
					isStrArr[k] = "'" + v + "'"
				}
				if len(isStrArr) == 0 {
					continue
				}
				strArr = append(strArr, k+" in ("+strings.Join(isStrArr, ",")+")")
			}
		}
		if len(strArr) == 0 {
			return ""
		}
		return " WHERE " + strings.Join(strArr, " && ")
	}
	return ""
}

func (m *Model) getSQLField() string {
	if m.Field != "" {
		return m.Field
	}
	return "*"
}

func (m *Model) getSort() string {
	if m.Sort != "" {
		return " ORDER BY " + m.Sort + " "
	}
	return ""
}

func (m *Model) getGroupBy() string {
	if m.GroupBy != "" {
		return " GROUP BY " + m.GroupBy + " "
	}
	return ""
}

func (m *Model) getSQLUpdate() string {
	var strArr []string
	for k, v := range m.Update {
		strArr = append(strArr, k+"='"+v+"'")
	}
	return strings.Join(strArr, ",")
}

func (m *Model) getSQLInsert() string {
	var fieldArr, valueArr []string
	for k, v := range m.Insert {
		fieldArr = append(fieldArr, k)
		valueArr = append(valueArr, "'"+v+"'")
	}
	return "(" + strings.Join(fieldArr, ",") + ") values (" + strings.Join(valueArr, ",") + ")"
}

func (m *Model) getSQLInsertArr() string {
	fieldArr, valuesArr := []string{}, []string{}
	for k, _ := range m.InsertArr[0] {
		fieldArr = append(fieldArr, k)
	}
	for _, value := range m.InsertArr {
		var valueArr []string
		for _, v := range fieldArr {
			valueArr = append(valueArr, "'"+value[v]+"'")
		}
		valuesArr = append(valuesArr, "("+strings.Join(valueArr, ",")+")")
	}
	return "(" + strings.Join(fieldArr, ",") + ")" + " values " + strings.Join(valuesArr, ",")
}

func (m *Model) getSQLLimite() string {
	if strArr, ok := m.Limit.([2]int); ok {
		return " LIMIT " + fmt.Sprintf("%d", strArr[0]) + ", " + fmt.Sprintf("%d", strArr[1])
	}
	if strArr, ok := m.Limit.([]int); ok {
		return " LIMIT " + fmt.Sprintf("%d", strArr[0]) + ", " + fmt.Sprintf("%d", strArr[1])
	}
	return ""
}

func (m *Model) Clean() {
	m.Cond = nil
	m.Insert = nil
	m.InsertArr = nil
	m.Update = nil
	m.Field = ""
	m.Table = ""
	m.Index = ""
	m.Limit = nil
	m.Sort = ""
	m.GroupBy = ""
	m.IsDeug = 0
}

func (m *Model) Debug(sql string) {
	if m.IsDeug == 1 {
		fmt.Println(sql)
	}
}

func logRecord(str string) {
	if OpenLog == 0 {
		return
	}
	QesyGo.Log(str, "error")
}
<commit_msg>fix bugs<commit_after>package QesyDb

import (
	"database/sql"
	"fmt"
	"github.com/Qesy/QesyGo"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var Db *sql.DB
var OpenLog int = 0

type Model struct {
	Cond      interface{}
	Insert    map[string]string
	InsertArr []map[string]string
	Update    map[string]string
	Field     string
	Table     string
	Index     string
	Limit     interface{}
	Sort      string
	GroupBy   string
	IsDeug    int
	Tx        *sql.Tx
}

// Connect  is a method with a sql.
func Connect(connStr string) {
	sqlDb, err := sql.Open("mysql", connStr)
	//defer sqlDb.Close()
	sqlDb.SetConnMaxLifetime(1800)
	sqlDb.SetMaxIdleConns(0)
	sqlDb.SetMaxOpenConns(600)
	if err != nil {
		log.Fatal("mysql connect error")
	}
	err = sqlDb.Ping()
	if err != nil {
		log.Fatal("mysql ping error")
	}
	QesyGo.Println("mysql connect sueccss")
	Db = sqlDb
}

func Begin() (*sql.Tx, error) {
	return Db.Begin()
}

func Rollback(tx *sql.Tx) error {
	return tx.Rollback()
}

func Commit(tx *sql.Tx) error {
	return tx.Commit()
}

// ExecSelectIndex  is a method with a sql.
func (m *Model) ExecSelectIndex() (map[string]map[string]string, error) {
	resultsSlice, _ := m.execSelect()
	retArr := map[string]map[string]string{}
	for _, v := range resultsSlice {
		if v[m.Index] == "" {
			continue
		}
		retArr[v[m.Index]] = v
	}
	m.Clean()
	return retArr, nil
}

func (m *Model) ExecSelect() ([]map[string]string, error) {
	ret, err := m.execSelect()
	m.Clean()
	return ret, err
}

// ExecSelect is a method with a sql.
func (m *Model) execSelect() ([]map[string]string, error) {
	cond := m.getSQLCond()
	field := m.getSQLField()
	sort := m.getSort()
	groupby := m.getGroupBy()
	limit := m.getSQLLimite()
	sqlStr := "SELECT " + field + " FROM " + m.Table + cond + groupby + sort + limit + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	resultsSlice := []map[string]string{}
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
		if err != nil {
			logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
			return resultsSlice, err
		}
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
		if err != nil {
			logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
			return resultsSlice, err
		}
	}
	//defer stmt.Close()

	rows, err := stmt.Query()
	defer rows.Close()
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return resultsSlice, err
	}
	fields, err := rows.Columns()
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return resultsSlice, err
	}

	for rows.Next() {
		result := make(map[string]string)
		var scanResultContainers []interface{}
		for i := 0; i < len(fields); i++ {
			var scanResultContainer interface{}
			scanResultContainers = append(scanResultContainers, &scanResultContainer)
		}
		if err := rows.Scan(scanResultContainers...); err != nil {
			return resultsSlice, err
		}
		for k, v := range fields {
			rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[k]))
			if rawValue.Interface() == nil {
				continue
			}
			rawType := reflect.TypeOf(rawValue.Interface())
			rawVal := reflect.ValueOf(rawValue.Interface())
			var str string
			switch rawType.Kind() {
			case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				str = strconv.FormatInt(rawVal.Int(), 10)
				result[v] = str
			case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				str = strconv.FormatUint(rawVal.Uint(), 10)
				result[v] = str
			case reflect.Float32, reflect.Float64:
				str = strconv.FormatFloat(rawVal.Float(), 'f', -1, 64)
				result[v] = str
			case reflect.Slice:
				if rawType.Elem().Kind() == reflect.Uint8 {
					result[v] = string(rawVal.Interface().([]byte))
					break
				}
			case reflect.String:
				str = rawVal.String()
				result[v] = str
			case reflect.Struct:
				str = rawVal.Interface().(time.Time).Format("2006-01-02 15:04:05.000 -0700")
				result[v] = str
			case reflect.Bool:
				if rawVal.Bool() {
					result[v] = "1"
				} else {
					result[v] = "0"
				}
			}
		}
		resultsSlice = append(resultsSlice, result)
	}
	return resultsSlice, nil
}

func (m *Model) ExecSelectOne() (map[string]string, error) {
	m.SetLimit([2]int{0, 1})
	resultsSlice, err := m.ExecSelect()
	if len(resultsSlice) == 0 {
		return map[string]string{}, err
	}
	return resultsSlice[0], nil
}

func (m *Model) ExecUpdate() (sql.Result, error) {
	updateStr := m.getSQLUpdate()
	condStr := m.getSQLCond()
	sqlStr := "UPDATE " + m.Table + " SET " + updateStr + condStr + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecInsert() (sql.Result, error) {
	insert := m.getSQLInsert()
	sqlStr := "INSERT INTO " + m.Table + " " + insert + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecInsertBatch() (sql.Result, error) {
	insert := m.getSQLInsertArr()
	sqlStr := "INSERT INTO " + m.Table + " " + insert + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecReplace() (sql.Result, error) {
	insert := m.getSQLInsert()
	sqlStr := "REPLACE INTO " + m.Table + " " + insert + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) ExecDelete() (sql.Result, error) {
	condStr := m.getSQLCond()
	sqlStr := "DELETE FROM " + m.Table + condStr + ";"
	m.Debug(sqlStr)
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func (m *Model) Exec(sqlStr string) (sql.Result, error) {
	var err error
	var stmt *sql.Stmt
	if m.Tx == nil {
		stmt, err = Db.Prepare(sqlStr)
		defer stmt.Close()
	} else {
		stmt, err = m.Tx.Prepare(sqlStr)
	}
	if err != nil {
		logRecord("ERR:" + err.Error() + "SQL:" + sqlStr)
		return nil, err
	}
	result, err := stmt.Exec()
	m.Clean()
	return result, err
}

func GetLastInsertId(result sql.Result) (int64, error) {
	return result.LastInsertId()
}

func GetRowsAffected(result sql.Result) (int64, error) {
	return result.RowsAffected()
}

func (m *Model) getSQLCond() string {
	if str, ok := m.Cond.(string); ok {
		return str
	}
	var strArr []string
	if arr, ok := m.Cond.(map[string]string); ok {
		var strArr []string
		for k, v := range arr {
			strArr = append(strArr, k+"='"+v+"'")
		}
		if len(strArr) == 0 {
			return ""
		}
		return " WHERE " + strings.Join(strArr, " && ")
	}
	if arr, ok := m.Cond.(map[string]interface{}); ok {
		for k, v := range arr {
			if isStr, ok := v.(string); ok {
				strArr = append(strArr, k+"='"+isStr+"'")
			} else if isStrArrTmp, ok := v.([]string); ok {
				isStrArr := make([]string, len(isStrArrTmp))
				copy(isStrArr, isStrArrTmp)
				for k, v := range isStrArr {
					isStrArr[k] = "'" + v + "'"
				}
				if len(isStrArr) == 0 {
					continue
				}
				strArr = append(strArr, k+" in ("+strings.Join(isStrArr, ",")+")")
			} else {
				panic("In must ([]string")
			}
		}
		if len(strArr) == 0 {
			return ""
		}
		return " WHERE " + strings.Join(strArr, " && ")
	}
	return ""
}

func (m *Model) getSQLField() string {
	if m.Field != "" {
		return m.Field
	}
	return "*"
}

func (m *Model) getSort() string {
	if m.Sort != "" {
		return " ORDER BY " + m.Sort + " "
	}
	return ""
}

func (m *Model) getGroupBy() string {
	if m.GroupBy != "" {
		return " GROUP BY " + m.GroupBy + " "
	}
	return ""
}

func (m *Model) getSQLUpdate() string {
	var strArr []string
	for k, v := range m.Update {
		strArr = append(strArr, k+"='"+v+"'")
	}
	return strings.Join(strArr, ",")
}

func (m *Model) getSQLInsert() string {
	var fieldArr, valueArr []string
	for k, v := range m.Insert {
		fieldArr = append(fieldArr, k)
		valueArr = append(valueArr, "'"+v+"'")
	}
	return "(" + strings.Join(fieldArr, ",") + ") values (" + strings.Join(valueArr, ",") + ")"
}

func (m *Model) getSQLInsertArr() string {
	fieldArr, valuesArr := []string{}, []string{}
	for k, _ := range m.InsertArr[0] {
		fieldArr = append(fieldArr, k)
	}
	for _, value := range m.InsertArr {
		var valueArr []string
		for _, v := range fieldArr {
			valueArr = append(valueArr, "'"+value[v]+"'")
		}
		valuesArr = append(valuesArr, "("+strings.Join(valueArr, ",")+")")
	}
	return "(" + strings.Join(fieldArr, ",") + ")" + " values " + strings.Join(valuesArr, ",")
}

func (m *Model) getSQLLimite() string {
	if strArr, ok := m.Limit.([2]int); ok {
		return " LIMIT " + fmt.Sprintf("%d", strArr[0]) + ", " + fmt.Sprintf("%d", strArr[1])
	}
	if strArr, ok := m.Limit.([]int); ok {
		return " LIMIT " + fmt.Sprintf("%d", strArr[0]) + ", " + fmt.Sprintf("%d", strArr[1])
	}
	return ""
}

func (m *Model) Clean() {
	m.Cond = nil
	m.Insert = nil
	m.InsertArr = nil
	m.Update = nil
	m.Field = ""
	m.Table = ""
	m.Index = ""
	m.Limit = nil
	m.Sort = ""
	m.GroupBy = ""
	m.IsDeug = 0
}

func (m *Model) Debug(sql string) {
	if m.IsDeug == 1 {
		fmt.Println(sql)
	}
}

func logRecord(str string) {
	if OpenLog == 0 {
		return
	}
	QesyGo.Log(str, "error")
}
<|endoftext|>


<commit_before>package parser

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/url"
)

// Parser parses stats from uWSGI server.
type Parser interface {
	// Parse stats from the given URL.
	Parse(url url.URL) (map[string]interface{}, error)
}

// StatsParser is a Parser that parses uWSGI stats server.
type StatsParser struct {
}

// NewStatsParser constructs a StatsParser.
func NewStatsParser() Parser {
	return &StatsParser{}
}

// Parse uWSGI stats from the given URL.
func (p *StatsParser) Parse(url url.URL) (map[string]interface{}, error) {
	var reader io.Reader

	switch url.Scheme {
	case "tcp":
		conn, err := net.Dial(url.Scheme, url.Host)
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		reader = conn

	default:
		return nil, fmt.Errorf("%v is a unsupported protocol", url.Scheme)
	}

	// uWSGI stats is expected to be a JSON document.
	// Ref - http://uwsgi-docs.readthedocs.org/en/latest/StatsServer.html
	body, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}

	var payload map[string]interface{}
	if err := json.Unmarshal([]byte(body), &payload); err != nil {
		return nil, err
	}

	// Convert float to int values.
	stats := ftoi(payload)

	return stats, nil
}

// ftoi returns a copy of in where float values are casted to int values.
func ftoi(in map[string]interface{}) map[string]interface{} {
	out := map[string]interface{}{}

	for k, v := range in {
		switch v.(type) {
		case float64:
			vt := v.(float64)
			out[k] = int(vt)
		case map[string]interface{}:
			vt := v.(map[string]interface{})
			out[k] = ftoi(vt)
		case []interface{}:
			vt := v.([]interface{})
			l := len(vt)
			a := make([]interface{}, l)
			for i := 0; i < l; i++ {
				e := vt[i]
				switch e.(type) {
				case float64:
					et := e.(float64)
					a[i] = int(et)
				case map[string]interface{}:
					et := e.(map[string]interface{})
					a[i] = ftoi(et)
				default:
					a[i] = e
				}
			}
			out[k] = a
		default:
			out[k] = v
		}
	}

	return out
}
<commit_msg>Prevent using variable name 'url' since it is also a stdlib package name<commit_after>package parser

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/url"
)

// Parser parses stats from uWSGI server.
type Parser interface {
	// Parse stats from the given URL.
	Parse(u url.URL) (map[string]interface{}, error)
}

// StatsParser is a Parser that parses uWSGI stats server.
type StatsParser struct {
}

// NewStatsParser constructs a StatsParser.
func NewStatsParser() Parser {
	return &StatsParser{}
}

// Parse uWSGI stats from the given URL.
func (p *StatsParser) Parse(u url.URL) (map[string]interface{}, error) {
	var reader io.Reader

	switch u.Scheme {
	case "tcp":
		conn, err := net.Dial(u.Scheme, u.Host)
		if err != nil {
			return nil, err
		}
		defer conn.Close()
		reader = conn

	default:
		return nil, fmt.Errorf("%v is a unsupported protocol", u.Scheme)
	}

	// uWSGI stats is expected to be a JSON document.
	// Ref - http://uwsgi-docs.readthedocs.org/en/latest/StatsServer.html
	body, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}

	var payload map[string]interface{}
	if err := json.Unmarshal([]byte(body), &payload); err != nil {
		return nil, err
	}

	// Convert float to int values.
	stats := ftoi(payload)

	return stats, nil
}

// ftoi returns a copy of in where float values are casted to int values.
func ftoi(in map[string]interface{}) map[string]interface{} {
	out := map[string]interface{}{}

	for k, v := range in {
		switch v.(type) {
		case float64:
			vt := v.(float64)
			out[k] = int(vt)
		case map[string]interface{}:
			vt := v.(map[string]interface{})
			out[k] = ftoi(vt)
		case []interface{}:
			vt := v.([]interface{})
			l := len(vt)
			a := make([]interface{}, l)
			for i := 0; i < l; i++ {
				e := vt[i]
				switch e.(type) {
				case float64:
					et := e.(float64)
					a[i] = int(et)
				case map[string]interface{}:
					et := e.(map[string]interface{})
					a[i] = ftoi(et)
				default:
					a[i] = e
				}
			}
			out[k] = a
		default:
			out[k] = v
		}
	}

	return out
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: ISC
// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package payment

import (
	"sync"

	"github.com/bitmark-inc/bitmarkd/background"
	"github.com/bitmark-inc/bitmarkd/currency"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/logger"
)

const (
	// MinimumNonceLength - least number of bytes in NONCE
	MinimumNonceLength = 8
	// MaximumNonceLength - most number of bytes in NONCE
	MaximumNonceLength = 64
)
const (
	requiredConfirmations = 1
	maximumBlockRate      = 500.0 // blocks per second
)

type P2PCache struct {
	BtcDirectory string `gluamapper:"btc_directory" json:"btc_directory"`
	LtcDirectory string `gluamapper:"ltc_directory" json:"ltc_directory"`
}

// Configuration - structure for configuration file
type Configuration struct {
	Mode           string                      `gluamapper:"mode" hcl:"mode" json:"mode"`
	P2PCache       P2PCache                    `gluamapper:"p2p_cache" json:"p2p_cache"`
	BootstrapNodes bootstrapNodesConfiguration `gluamapper:"bootstrap_nodes" hcl:"bootstrap_nodes" json:"bootstrap_nodes"`
	Discovery      *discoveryConfiguration     `gluamapper:"discovery" hcl:"discovery" json:"discovery"`
	Bitcoin        *currencyConfiguration      `gluamapper:"bitcoin" hcl:"bitcoin" json:"bitcoin"`
	Litecoin       *currencyConfiguration      `gluamapper:"litecoin" hcl:"litecoin" json:"litecoin"`
}

type bootstrapNodesConfiguration struct {
	Bitcoin  []string `gluamapper:"bitcoin" hcl:"bitcoin" json:"bitcoin"`
	Litecoin []string `gluamapper:"litecoin" hcl:"litecoin" json:"litecoin"`
}

type discoveryConfiguration struct {
	ReqEndpoint string `gluamapper:"req_endpoint" hcl:"req_endpoint" json:"req_endpoint"`
	SubEndpoint string `gluamapper:"sub_endpoint" hcl:"sub_endpoint" json:"sub_endpoint"`
}

type currencyConfiguration struct {
	URL string `gluamapper:"url" json:"url"`
}

type globalDataType struct {
	sync.RWMutex // to allow locking

	log        *logger.L
	handlers   map[string]currencyHandler
	background *background.T

	// set once during initialise
	initialised bool
}

var globalData globalDataType

// Initialise - setup the payment system
func Initialise(configuration *Configuration) error {
	globalData.Lock()
	defer globalData.Unlock()

	// no need to start if already started
	if globalData.initialised {
		return fault.AlreadyInitialised
	}

	globalData.log = logger.New("payment")
	globalData.log.Info("starting…")

	if configuration.Mode == "noverify" {
		globalData.initialised = true
		return nil
	}

	// initialise the handler for each currency
	globalData.handlers = make(map[string]currencyHandler)
	if configuration.Mode != "p2p" {
		useDiscovery := configuration.Mode == "discovery"
		for c := currency.First; c <= currency.Last; c++ {
			switch c {
			case currency.Bitcoin:
				if nil == configuration.Bitcoin {
					return fault.MissingPaymentBitcoinSection
				}
				handler, err := newBitcoinHandler(useDiscovery, configuration.Bitcoin)
				if err != nil {
					return err
				}
				globalData.handlers[currency.Bitcoin.String()] = handler
			case currency.Litecoin:
				if nil == configuration.Litecoin {
					return fault.MissingPaymentLitecoinSection
				}
				handler, err := newLitecoinHandler(useDiscovery, configuration.Litecoin)
				if err != nil {
					return err
				}
				globalData.handlers[currency.Litecoin.String()] = handler
			default: // only fails if new module not correctly installed
				logger.Panicf("missing payment initialiser for Currency: %s", c.String())
			}
		}
	}

	// start background processes
	globalData.log.Info("start background…")

	processes := background.Processes{}

	switch configuration.Mode {
	case "p2p":
		globalData.log.Info("p2p watcher…")

		btcP2pWatcher, err := newP2pWatcher(currency.Bitcoin,
			configuration.P2PCache.BtcDirectory,
			configuration.BootstrapNodes.Bitcoin)
		if err != nil {
			return err
		}
		ltcP2pWatcher, err := newP2pWatcher(currency.Litecoin,
			configuration.P2PCache.LtcDirectory,
			configuration.BootstrapNodes.Litecoin)
		if err != nil {
			return err
		}
		processes = append(processes, btcP2pWatcher, ltcP2pWatcher)
	case "discovery":
		globalData.log.Info("discovery…")
		if nil == configuration.Discovery {
			return fault.MissingPaymentDiscoverySection
		}
		discoverer, err := newDiscoverer(configuration.Discovery.SubEndpoint, configuration.Discovery.ReqEndpoint)
		if err != nil {
			return err
		}
		processes = append(processes, discoverer)
	case "rest":
		globalData.log.Info("checker…")
		processes = append(processes, &checker{})
	default:
		logger.Panicf("unsupported payment verification mode: %s", configuration.Mode)
	}

	// all data initialised
	globalData.initialised = true

	// start background
	globalData.background = background.Start(processes, globalData.log)

	return nil
}

// Finalise - stop all background tasks
func Finalise() error {
	if !globalData.initialised {
		return fault.NotInitialised
	}

	globalData.log.Info("shutting down…")
	globalData.log.Flush()

	// stop background
	globalData.background.StopAndWait()

	// finally...
	globalData.initialised = false

	globalData.log.Info("finished")
	globalData.log.Flush()

	return nil
}
<commit_msg>[payment] only stop background process if one was started<commit_after>// SPDX-License-Identifier: ISC
// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package payment

import (
	"sync"

	"github.com/bitmark-inc/bitmarkd/background"
	"github.com/bitmark-inc/bitmarkd/currency"
	"github.com/bitmark-inc/bitmarkd/fault"
	"github.com/bitmark-inc/logger"
)

const (
	// MinimumNonceLength - least number of bytes in NONCE
	MinimumNonceLength = 8
	// MaximumNonceLength - most number of bytes in NONCE
	MaximumNonceLength = 64
)
const (
	requiredConfirmations = 1
	maximumBlockRate      = 500.0 // blocks per second
)

type P2PCache struct {
	BtcDirectory string `gluamapper:"btc_directory" json:"btc_directory"`
	LtcDirectory string `gluamapper:"ltc_directory" json:"ltc_directory"`
}

// Configuration - structure for configuration file
type Configuration struct {
	Mode           string                      `gluamapper:"mode" hcl:"mode" json:"mode"`
	P2PCache       P2PCache                    `gluamapper:"p2p_cache" json:"p2p_cache"`
	BootstrapNodes bootstrapNodesConfiguration `gluamapper:"bootstrap_nodes" hcl:"bootstrap_nodes" json:"bootstrap_nodes"`
	Discovery      *discoveryConfiguration     `gluamapper:"discovery" hcl:"discovery" json:"discovery"`
	Bitcoin        *currencyConfiguration      `gluamapper:"bitcoin" hcl:"bitcoin" json:"bitcoin"`
	Litecoin       *currencyConfiguration      `gluamapper:"litecoin" hcl:"litecoin" json:"litecoin"`
}

type bootstrapNodesConfiguration struct {
	Bitcoin  []string `gluamapper:"bitcoin" hcl:"bitcoin" json:"bitcoin"`
	Litecoin []string `gluamapper:"litecoin" hcl:"litecoin" json:"litecoin"`
}

type discoveryConfiguration struct {
	ReqEndpoint string `gluamapper:"req_endpoint" hcl:"req_endpoint" json:"req_endpoint"`
	SubEndpoint string `gluamapper:"sub_endpoint" hcl:"sub_endpoint" json:"sub_endpoint"`
}

type currencyConfiguration struct {
	URL string `gluamapper:"url" json:"url"`
}

type globalDataType struct {
	sync.RWMutex // to allow locking

	log        *logger.L
	handlers   map[string]currencyHandler
	background *background.T

	// set once during initialise
	initialised bool
}

var globalData globalDataType

// Initialise - setup the payment system
func Initialise(configuration *Configuration) error {
	globalData.Lock()
	defer globalData.Unlock()

	// no need to start if already started
	if globalData.initialised {
		return fault.AlreadyInitialised
	}

	globalData.log = logger.New("payment")
	globalData.log.Info("starting…")

	if configuration.Mode == "noverify" {
		globalData.initialised = true
		return nil
	}

	// initialise the handler for each currency
	globalData.handlers = make(map[string]currencyHandler)
	if configuration.Mode != "p2p" {
		useDiscovery := configuration.Mode == "discovery"
		for c := currency.First; c <= currency.Last; c++ {
			switch c {
			case currency.Bitcoin:
				if nil == configuration.Bitcoin {
					return fault.MissingPaymentBitcoinSection
				}
				handler, err := newBitcoinHandler(useDiscovery, configuration.Bitcoin)
				if err != nil {
					return err
				}
				globalData.handlers[currency.Bitcoin.String()] = handler
			case currency.Litecoin:
				if nil == configuration.Litecoin {
					return fault.MissingPaymentLitecoinSection
				}
				handler, err := newLitecoinHandler(useDiscovery, configuration.Litecoin)
				if err != nil {
					return err
				}
				globalData.handlers[currency.Litecoin.String()] = handler
			default: // only fails if new module not correctly installed
				logger.Panicf("missing payment initialiser for Currency: %s", c.String())
			}
		}
	}

	// start background processes
	globalData.log.Info("start background…")

	processes := background.Processes{}

	switch configuration.Mode {
	case "p2p":
		globalData.log.Info("p2p watcher…")

		btcP2pWatcher, err := newP2pWatcher(currency.Bitcoin,
			configuration.P2PCache.BtcDirectory,
			configuration.BootstrapNodes.Bitcoin)
		if err != nil {
			return err
		}
		ltcP2pWatcher, err := newP2pWatcher(currency.Litecoin,
			configuration.P2PCache.LtcDirectory,
			configuration.BootstrapNodes.Litecoin)
		if err != nil {
			return err
		}
		processes = append(processes, btcP2pWatcher, ltcP2pWatcher)
	case "discovery":
		globalData.log.Info("discovery…")
		if nil == configuration.Discovery {
			return fault.MissingPaymentDiscoverySection
		}
		discoverer, err := newDiscoverer(configuration.Discovery.SubEndpoint, configuration.Discovery.ReqEndpoint)
		if err != nil {
			return err
		}
		processes = append(processes, discoverer)
	case "rest":
		globalData.log.Info("checker…")
		processes = append(processes, &checker{})
	default:
		logger.Panicf("unsupported payment verification mode: %s", configuration.Mode)
	}

	// all data initialised
	globalData.initialised = true

	// start background
	globalData.background = background.Start(processes, globalData.log)

	return nil
}

// Finalise - stop all background tasks
func Finalise() error {
	if !globalData.initialised {
		return fault.NotInitialised
	}

	globalData.log.Info("shutting down…")
	globalData.log.Flush()

	// stop background if one was started
	if nil != globalData.background {
		globalData.background.StopAndWait()
	}

	// finally...
	globalData.initialised = false

	globalData.log.Info("finished")
	globalData.log.Flush()

	return nil
}
<|endoftext|>


<commit_before>package stripe

import (
	"encoding/json"
)

// PaymentIntentCancellationReason is the list of allowed values for the cancelation reason.
type PaymentIntentCancellationReason string

// List of values that PaymentIntentCancellationReason can take.
const (
	PaymentIntentCancellationReasonAbandoned           PaymentIntentCancellationReason = "abandoned"
	PaymentIntentCancellationReasonAutomatic           PaymentIntentCancellationReason = "automatic"
	PaymentIntentCancellationReasonDuplicate           PaymentIntentCancellationReason = "duplicate"
	PaymentIntentCancellationReasonFailedInvoice       PaymentIntentCancellationReason = "failed_invoice"
	PaymentIntentCancellationReasonFraudulent          PaymentIntentCancellationReason = "fraudulent"
	PaymentIntentCancellationReasonRequestedByCustomer PaymentIntentCancellationReason = "requested_by_customer"
	PaymentIntentCancellationReasonVoidInvoice         PaymentIntentCancellationReason = "void_invoice"
)

// PaymentIntentCaptureMethod is the list of allowed values for the capture method.
type PaymentIntentCaptureMethod string

// List of values that PaymentIntentCaptureMethod can take.
const (
	PaymentIntentCaptureMethodAutomatic PaymentIntentCaptureMethod = "automatic"
	PaymentIntentCaptureMethodManual    PaymentIntentCaptureMethod = "manual"
)

// PaymentIntentConfirmationMethod is the list of allowed values for the confirmation method.
type PaymentIntentConfirmationMethod string

// List of values that PaymentIntentConfirmationMethod can take.
const (
	PaymentIntentConfirmationMethodAutomatic PaymentIntentConfirmationMethod = "automatic"
	PaymentIntentConfirmationMethodManual    PaymentIntentConfirmationMethod = "manual"
	// The following constants are considered deprecated
	PaymentIntentConfirmationMethodPublishable PaymentIntentConfirmationMethod = "publishable"
	PaymentIntentConfirmationMethodSecret      PaymentIntentConfirmationMethod = "secret"
)

// PaymentIntentNextActionType is the list of allowed values for the next action's type.
type PaymentIntentNextActionType string

// List of values that PaymentIntentNextActionType can take.
const (
	PaymentIntentNextActionTypeRedirectToURL PaymentIntentNextActionType = "redirect_to_url"
)

// PaymentIntentOffSession is the list of allowed values for types of off-session.
type PaymentIntentOffSession string

// List of values that PaymentIntentOffSession can take.
const (
	PaymentIntentOffSessionOneOff    PaymentIntentOffSession = "one_off"
	PaymentIntentOffSessionRecurring PaymentIntentOffSession = "recurring"
)

// PaymentIntentSetupFutureUsage is the list of allowed values for SetupFutureUsage.
type PaymentIntentSetupFutureUsage string

// List of values that PaymentIntentSetupFutureUsage can take.
const (
	PaymentIntentSetupFutureUsageOffSession PaymentIntentSetupFutureUsage = "off_session"
	PaymentIntentSetupFutureUsageOnSession  PaymentIntentSetupFutureUsage = "on_session"
)

// PaymentIntentStatus is the list of allowed values for the payment intent's status.
type PaymentIntentStatus string

// List of values that PaymentIntentStatus can take.
const (
	PaymentIntentStatusCanceled              PaymentIntentStatus = "canceled"
	PaymentIntentStatusProcessing            PaymentIntentStatus = "processing"
	PaymentIntentStatusRequiresAction        PaymentIntentStatus = "requires_action"
	PaymentIntentStatusRequiresCapture       PaymentIntentStatus = "requires_capture"
	PaymentIntentStatusRequiresConfirmation  PaymentIntentStatus = "requires_confirmation"
	PaymentIntentStatusRequiresPaymentMethod PaymentIntentStatus = "requires_payment_method"
	PaymentIntentStatusSucceeded             PaymentIntentStatus = "succeeded"
)

// PaymentIntentCancelParams is the set of parameters that can be used when canceling a payment intent.
type PaymentIntentCancelParams struct {
	Params             `form:"*"`
	CancellationReason *string `form:"cancellation_reason"`
}

// PaymentIntentCaptureParams is the set of parameters that can be used when capturing a payment intent.
type PaymentIntentCaptureParams struct {
	Params               `form:"*"`
	AmountToCapture      *int64                           `form:"amount_to_capture"`
	ApplicationFeeAmount *int64                           `form:"application_fee_amount"`
	TransferData         *PaymentIntentTransferDataParams `form:"transfer_data"`
}

// PaymentIntentConfirmParams is the set of parameters that can be used when confirming a payment intent.
type PaymentIntentConfirmParams struct {
	Params `form:"*"`
	// This parameter expects a boolean but used to take an enum so we're adding support for both
	// until the next major version (TODO).
	OffSession        interface{}            `form:"off_session"`
	PaymentMethod     *string                `form:"payment_method"`
	ReceiptEmail      *string                `form:"receipt_email"`
	ReturnURL         *string                `form:"return_url"`
	SavePaymentMethod *bool                  `form:"save_payment_method"`
	Shipping          *ShippingDetailsParams `form:"shipping"`
	Source            *string                `form:"source"`
}

// PaymentIntentTransferDataParams is the set of parameters allowed for the transfer hash.
type PaymentIntentTransferDataParams struct {
	Amount      *int64  `form:"amount"`
	Destination *string `form:"destination"`
}

// PaymentIntentParams is the set of parameters that can be used when handling a payment intent.
type PaymentIntentParams struct {
	Params               `form:"*"`
	Amount               *int64                           `form:"amount"`
	ApplicationFeeAmount *int64                           `form:"application_fee_amount"`
	CaptureMethod        *string                          `form:"capture_method"`
	Confirm              *bool                            `form:"confirm"`
	ConfirmationMethod   *string                          `form:"confirmation_method"`
	Currency             *string                          `form:"currency"`
	Customer             *string                          `form:"customer"`
	Description          *string                          `form:"description"`
	OnBehalfOf           *string                          `form:"on_behalf_of"`
	PaymentMethod        *string                          `form:"payment_method"`
	PaymentMethodTypes   []*string                        `form:"payment_method_types"`
	ReceiptEmail         *string                          `form:"receipt_email"`
	ReturnURL            *string                          `form:"return_url"`
	SavePaymentMethod    *bool                            `form:"save_payment_method"`
	SetupFutureUsage     *string                          `form:"setup_future_usage"`
	Shipping             *ShippingDetailsParams           `form:"shipping"`
	Source               *string                          `form:"source"`
	StatementDescriptor  *string                          `form:"statement_descriptor"`
	TransferData         *PaymentIntentTransferDataParams `form:"transfer_data"`
	TransferGroup        *string                          `form:"transfer_group"`

	// This parameter only works if you confirm on creation. It also expects a boolean but used to
	// take an enum so we're adding support for both until the next major version (TODO).
	OffSession interface{} `form:"off_session"`
}

// PaymentIntentListParams is the set of parameters that can be used when listing payment intents.
// For more details see https://stripe.com/docs/api#list_payouts.
type PaymentIntentListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	Customer     *string           `form:"customer"`
}

// PaymentIntentLastPaymentError represents the last error happening on a payment intent.
type PaymentIntentLastPaymentError struct {
	Charge        string         `json:"charge"`
	Code          string         `json:"code"`
	DeclineCode   string         `json:"decline_code"`
	DocURL        string         `json:"doc_url"`
	Message       string         `json:"message"`
	Param         string         `json:"param"`
	PaymentIntent *PaymentIntent `json:"payment_intent"`
	PaymentMethod *PaymentMethod `json:"payment_method"`
	Source        *PaymentSource `json:"source"`
	Type          ErrorType      `json:"type"`
}

// PaymentIntentNextActionRedirectToURL represents the resource for the next action of type
// "redirect_to_url".
type PaymentIntentNextActionRedirectToURL struct {
	ReturnURL string `json:"return_url"`
	URL       string `json:"url"`
}

// PaymentIntentNextAction represents the type of action to take on a payment intent.
type PaymentIntentNextAction struct {
	RedirectToURL *PaymentIntentNextActionRedirectToURL `json:"redirect_to_url"`
	Type          PaymentIntentNextActionType           `json:"type"`
}

// PaymentIntentTransferData represents the information for the transfer associated with a payment intent.
type PaymentIntentTransferData struct {
	Amount      int64    `json:"amount"`
	Destination *Account `json:"destination"`
}

// PaymentIntent is the resource representing a Stripe payout.
// For more details see https://stripe.com/docs/api#payment_intents.
type PaymentIntent struct {
	Amount              int64                           `json:"amount"`
	AmountCapturable    int64                           `json:"amount_capturable"`
	AmountReceived      int64                           `json:"amount_received"`
	Application         *Application                    `json:"application"`
	ApplicationFee      int64                           `json:"application_fee"`
	CanceledAt          int64                           `json:"canceled_at"`
	CancellationReason  PaymentIntentCancellationReason `json:"cancellation_reason"`
	CaptureMethod       PaymentIntentCaptureMethod      `json:"capture_method"`
	Charges             *ChargeList                     `json:"charges"`
	ClientSecret        string                          `json:"client_secret"`
	ConfirmationMethod  PaymentIntentConfirmationMethod `json:"confirmation_method"`
	Created             int64                           `json:"created"`
	Currency            string                          `json:"currency"`
	Customer            *Customer                       `json:"customer"`
	Description         string                          `json:"description"`
	Invoice             *Invoice                        `json:"invoice"`
	LastPaymentError    *PaymentIntentLastPaymentError  `json:"last_payment_error"`
	Livemode            bool                            `json:"livemode"`
	ID                  string                          `json:"id"`
	Metadata            map[string]string               `json:"metadata"`
	NextAction          *PaymentIntentNextAction        `json:"next_action"`
	OnBehalfOf          *Account                        `json:"on_behalf_of"`
	PaymentMethod       *PaymentMethod                  `json:"payment_method"`
	PaymentMethodTypes  []string                        `json:"payment_method_types"`
	ReceiptEmail        string                          `json:"receipt_email"`
	Review              *Review                         `json:"review"`
	SetupFutureUsage    PaymentIntentSetupFutureUsage   `json:"setup_future_usage"`
	Shipping            ShippingDetails                 `json:"shipping"`
	Source              *PaymentSource                  `json:"source"`
	StatementDescriptor string                          `json:"statement_descriptor"`
	Status              PaymentIntentStatus             `json:"status"`
	TransferData        *PaymentIntentTransferData      `json:"transfer_data"`
	TransferGroup       string                          `json:"transfer_group"`
}

// PaymentIntentList is a list of payment intents as retrieved from a list endpoint.
type PaymentIntentList struct {
	ListMeta
	Data []*PaymentIntent `json:"data"`
}

// UnmarshalJSON handles deserialization of a Payment Intent.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (p *PaymentIntent) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		p.ID = id
		return nil
	}

	type paymentintent PaymentIntent
	var v paymentintent
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*p = PaymentIntent(v)
	return nil
}
<commit_msg>Add support for `SetupFutureUsage` on `PaymentIntent` update/confirm<commit_after>package stripe

import (
	"encoding/json"
)

// PaymentIntentCancellationReason is the list of allowed values for the cancelation reason.
type PaymentIntentCancellationReason string

// List of values that PaymentIntentCancellationReason can take.
const (
	PaymentIntentCancellationReasonAbandoned           PaymentIntentCancellationReason = "abandoned"
	PaymentIntentCancellationReasonAutomatic           PaymentIntentCancellationReason = "automatic"
	PaymentIntentCancellationReasonDuplicate           PaymentIntentCancellationReason = "duplicate"
	PaymentIntentCancellationReasonFailedInvoice       PaymentIntentCancellationReason = "failed_invoice"
	PaymentIntentCancellationReasonFraudulent          PaymentIntentCancellationReason = "fraudulent"
	PaymentIntentCancellationReasonRequestedByCustomer PaymentIntentCancellationReason = "requested_by_customer"
	PaymentIntentCancellationReasonVoidInvoice         PaymentIntentCancellationReason = "void_invoice"
)

// PaymentIntentCaptureMethod is the list of allowed values for the capture method.
type PaymentIntentCaptureMethod string

// List of values that PaymentIntentCaptureMethod can take.
const (
	PaymentIntentCaptureMethodAutomatic PaymentIntentCaptureMethod = "automatic"
	PaymentIntentCaptureMethodManual    PaymentIntentCaptureMethod = "manual"
)

// PaymentIntentConfirmationMethod is the list of allowed values for the confirmation method.
type PaymentIntentConfirmationMethod string

// List of values that PaymentIntentConfirmationMethod can take.
const (
	PaymentIntentConfirmationMethodAutomatic PaymentIntentConfirmationMethod = "automatic"
	PaymentIntentConfirmationMethodManual    PaymentIntentConfirmationMethod = "manual"
	// The following constants are considered deprecated
	PaymentIntentConfirmationMethodPublishable PaymentIntentConfirmationMethod = "publishable"
	PaymentIntentConfirmationMethodSecret      PaymentIntentConfirmationMethod = "secret"
)

// PaymentIntentNextActionType is the list of allowed values for the next action's type.
type PaymentIntentNextActionType string

// List of values that PaymentIntentNextActionType can take.
const (
	PaymentIntentNextActionTypeRedirectToURL PaymentIntentNextActionType = "redirect_to_url"
)

// PaymentIntentOffSession is the list of allowed values for types of off-session.
type PaymentIntentOffSession string

// List of values that PaymentIntentOffSession can take.
const (
	PaymentIntentOffSessionOneOff    PaymentIntentOffSession = "one_off"
	PaymentIntentOffSessionRecurring PaymentIntentOffSession = "recurring"
)

// PaymentIntentSetupFutureUsage is the list of allowed values for SetupFutureUsage.
type PaymentIntentSetupFutureUsage string

// List of values that PaymentIntentSetupFutureUsage can take.
const (
	PaymentIntentSetupFutureUsageOffSession PaymentIntentSetupFutureUsage = "off_session"
	PaymentIntentSetupFutureUsageOnSession  PaymentIntentSetupFutureUsage = "on_session"
)

// PaymentIntentStatus is the list of allowed values for the payment intent's status.
type PaymentIntentStatus string

// List of values that PaymentIntentStatus can take.
const (
	PaymentIntentStatusCanceled              PaymentIntentStatus = "canceled"
	PaymentIntentStatusProcessing            PaymentIntentStatus = "processing"
	PaymentIntentStatusRequiresAction        PaymentIntentStatus = "requires_action"
	PaymentIntentStatusRequiresCapture       PaymentIntentStatus = "requires_capture"
	PaymentIntentStatusRequiresConfirmation  PaymentIntentStatus = "requires_confirmation"
	PaymentIntentStatusRequiresPaymentMethod PaymentIntentStatus = "requires_payment_method"
	PaymentIntentStatusSucceeded             PaymentIntentStatus = "succeeded"
)

// PaymentIntentCancelParams is the set of parameters that can be used when canceling a payment intent.
type PaymentIntentCancelParams struct {
	Params             `form:"*"`
	CancellationReason *string `form:"cancellation_reason"`
}

// PaymentIntentCaptureParams is the set of parameters that can be used when capturing a payment intent.
type PaymentIntentCaptureParams struct {
	Params               `form:"*"`
	AmountToCapture      *int64                           `form:"amount_to_capture"`
	ApplicationFeeAmount *int64                           `form:"application_fee_amount"`
	TransferData         *PaymentIntentTransferDataParams `form:"transfer_data"`
}

// PaymentIntentConfirmParams is the set of parameters that can be used when confirming a payment intent.
type PaymentIntentConfirmParams struct {
	Params `form:"*"`
	// This parameter expects a boolean but used to take an enum so we're adding support for both
	// until the next major version (TODO).
	OffSession        interface{}            `form:"off_session"`
	PaymentMethod     *string                `form:"payment_method"`
	ReceiptEmail      *string                `form:"receipt_email"`
	ReturnURL         *string                `form:"return_url"`
	SavePaymentMethod *bool                  `form:"save_payment_method"`
	SetupFutureUsage  *string                `form:"setup_future_usage"`
	Shipping          *ShippingDetailsParams `form:"shipping"`
	Source            *string                `form:"source"`
}

// PaymentIntentTransferDataParams is the set of parameters allowed for the transfer hash.
type PaymentIntentTransferDataParams struct {
	Amount      *int64  `form:"amount"`
	Destination *string `form:"destination"`
}

// PaymentIntentParams is the set of parameters that can be used when handling a payment intent.
type PaymentIntentParams struct {
	Params               `form:"*"`
	Amount               *int64                           `form:"amount"`
	ApplicationFeeAmount *int64                           `form:"application_fee_amount"`
	CaptureMethod        *string                          `form:"capture_method"`
	Confirm              *bool                            `form:"confirm"`
	ConfirmationMethod   *string                          `form:"confirmation_method"`
	Currency             *string                          `form:"currency"`
	Customer             *string                          `form:"customer"`
	Description          *string                          `form:"description"`
	OnBehalfOf           *string                          `form:"on_behalf_of"`
	PaymentMethod        *string                          `form:"payment_method"`
	PaymentMethodTypes   []*string                        `form:"payment_method_types"`
	ReceiptEmail         *string                          `form:"receipt_email"`
	ReturnURL            *string                          `form:"return_url"`
	SavePaymentMethod    *bool                            `form:"save_payment_method"`
	SetupFutureUsage     *string                          `form:"setup_future_usage"`
	Shipping             *ShippingDetailsParams           `form:"shipping"`
	Source               *string                          `form:"source"`
	StatementDescriptor  *string                          `form:"statement_descriptor"`
	TransferData         *PaymentIntentTransferDataParams `form:"transfer_data"`
	TransferGroup        *string                          `form:"transfer_group"`

	// This parameter only works if you confirm on creation. It also expects a boolean but used to
	// take an enum so we're adding support for both until the next major version (TODO).
	OffSession interface{} `form:"off_session"`
}

// PaymentIntentListParams is the set of parameters that can be used when listing payment intents.
// For more details see https://stripe.com/docs/api#list_payouts.
type PaymentIntentListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
	Customer     *string           `form:"customer"`
}

// PaymentIntentLastPaymentError represents the last error happening on a payment intent.
type PaymentIntentLastPaymentError struct {
	Charge        string         `json:"charge"`
	Code          string         `json:"code"`
	DeclineCode   string         `json:"decline_code"`
	DocURL        string         `json:"doc_url"`
	Message       string         `json:"message"`
	Param         string         `json:"param"`
	PaymentIntent *PaymentIntent `json:"payment_intent"`
	PaymentMethod *PaymentMethod `json:"payment_method"`
	Source        *PaymentSource `json:"source"`
	Type          ErrorType      `json:"type"`
}

// PaymentIntentNextActionRedirectToURL represents the resource for the next action of type
// "redirect_to_url".
type PaymentIntentNextActionRedirectToURL struct {
	ReturnURL string `json:"return_url"`
	URL       string `json:"url"`
}

// PaymentIntentNextAction represents the type of action to take on a payment intent.
type PaymentIntentNextAction struct {
	RedirectToURL *PaymentIntentNextActionRedirectToURL `json:"redirect_to_url"`
	Type          PaymentIntentNextActionType           `json:"type"`
}

// PaymentIntentTransferData represents the information for the transfer associated with a payment intent.
type PaymentIntentTransferData struct {
	Amount      int64    `json:"amount"`
	Destination *Account `json:"destination"`
}

// PaymentIntent is the resource representing a Stripe payout.
// For more details see https://stripe.com/docs/api#payment_intents.
type PaymentIntent struct {
	Amount              int64                           `json:"amount"`
	AmountCapturable    int64                           `json:"amount_capturable"`
	AmountReceived      int64                           `json:"amount_received"`
	Application         *Application                    `json:"application"`
	ApplicationFee      int64                           `json:"application_fee"`
	CanceledAt          int64                           `json:"canceled_at"`
	CancellationReason  PaymentIntentCancellationReason `json:"cancellation_reason"`
	CaptureMethod       PaymentIntentCaptureMethod      `json:"capture_method"`
	Charges             *ChargeList                     `json:"charges"`
	ClientSecret        string                          `json:"client_secret"`
	ConfirmationMethod  PaymentIntentConfirmationMethod `json:"confirmation_method"`
	Created             int64                           `json:"created"`
	Currency            string                          `json:"currency"`
	Customer            *Customer                       `json:"customer"`
	Description         string                          `json:"description"`
	Invoice             *Invoice                        `json:"invoice"`
	LastPaymentError    *PaymentIntentLastPaymentError  `json:"last_payment_error"`
	Livemode            bool                            `json:"livemode"`
	ID                  string                          `json:"id"`
	Metadata            map[string]string               `json:"metadata"`
	NextAction          *PaymentIntentNextAction        `json:"next_action"`
	OnBehalfOf          *Account                        `json:"on_behalf_of"`
	PaymentMethod       *PaymentMethod                  `json:"payment_method"`
	PaymentMethodTypes  []string                        `json:"payment_method_types"`
	ReceiptEmail        string                          `json:"receipt_email"`
	Review              *Review                         `json:"review"`
	SetupFutureUsage    PaymentIntentSetupFutureUsage   `json:"setup_future_usage"`
	Shipping            ShippingDetails                 `json:"shipping"`
	Source              *PaymentSource                  `json:"source"`
	StatementDescriptor string                          `json:"statement_descriptor"`
	Status              PaymentIntentStatus             `json:"status"`
	TransferData        *PaymentIntentTransferData      `json:"transfer_data"`
	TransferGroup       string                          `json:"transfer_group"`
}

// PaymentIntentList is a list of payment intents as retrieved from a list endpoint.
type PaymentIntentList struct {
	ListMeta
	Data []*PaymentIntent `json:"data"`
}

// UnmarshalJSON handles deserialization of a Payment Intent.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (p *PaymentIntent) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		p.ID = id
		return nil
	}

	type paymentintent PaymentIntent
	var v paymentintent
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*p = PaymentIntent(v)
	return nil
}
<|endoftext|>


<commit_before>package quandl

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

// ApiKey is used to set your api key before you make any call
var ApiKey = ""

// LastUrl will hold the last requested URL after each call
var LastUrl = ""

// CacheHandler is a reference to a struct that implements the Cacher interface.
// If set, it will use it to get documents from the cache or set to it.
// See the example cache handler in the quandl/cache package.
var CacheHandler Cacher

var urlTemplates map[string]string = map[string]string{
	"symbol":  "https://www.quandl.com/api/v1/datasets/%s.%s?%s",
	"symbols": "https://www.quandl.com/api/v1/multisets.%s?columns=%s&%s",
	"search":  "https://www.quandl.com/api/v1/datasets.%s?%s",
	"list":    "http://www.quandl.com/api/v2/datasets.%s?%s",
}

// Type Options is used to send additional parameters in the Quandl request
type Options url.Values

// Set registers a key=value pair to be sent in the Quandl request
func (o Options) Set(key, value string) {
	o[key] = []string{value}
}

type Cacher interface {
	Get(key string) []byte
	Set(key string, data []byte) error
}

// GetSymbol returns data for a given symbol
func GetSymbol(symbol string, params Options) (SymbolResponse, error) {
	raw, err := GetSymbolRaw(symbol, "json", params)
	var response SymbolResponse
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return response, marshallerError(raw, err)
	}
	return response, nil
}

// GetSymbols returns data for a symbols array
func GetSymbols(symbols []string, params Options) (SymbolsResponse, error) {
	raw, err := GetSymbolsRaw(symbols, "json", params)
	var response SymbolsResponse
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return response, marshallerError(raw, err)
	}
	return response, nil
}

// GetList returns a list of symbols for a source
func GetList(source string, page int, perPage int) (ListResponse, error) {
	raw, err := GetListRaw(source, "json", page, perPage)
	var response ListResponse
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return response, marshallerError(raw, err)
	}
	return response, nil
}

// GetSearch returns search results
func GetSearch(query string, page int, perPage int) (SearchResponse, error) {
	raw, err := GetSearchRaw(query, "json", page, perPage)
	var response SearchResponse
	if err != nil {
		return response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return response, marshallerError(raw, err)
	}
	return response, nil
}

// GetSymbolRaw returns CSV, JSON or XML data for a given symbol
func GetSymbolRaw(symbol string, format string, params Options) ([]byte, error) {
	url := getUrl("symbol", symbol, format, arrangeParams(params))
	return getData(url)
}

// GetSymbolsRaw returns CSV, JSON or XML data for multiple symbols
func GetSymbolsRaw(symbols []string, format string, params Options) ([]byte, error) {
	url := getUrl("symbols", format, symbolsToString(symbols), arrangeParams(params))
	return getData(url)
}

// GetListRaw returns a list of symbols for a source as CSV, JSON or XML
func GetListRaw(source string, format string, page int, perPage int) ([]byte, error) {
	params := Options{}

	params.Set("query", "*")
	params.Set("source_code", source)
	params.Set("per_page", strconv.Itoa(perPage))
	params.Set("page", strconv.Itoa(page))

	url := getUrl("list", format, arrangeParams(params))
	return getData(url)
}

// GetSearchRaw returns search results as JSON or XML
func GetSearchRaw(query string, format string, page int, perPage int) ([]byte, error) {
	params := Options{}

	if format == "csv" {
		format = "json"
	}

	params.Set("query", query)
	params.Set("per_page", strconv.Itoa(perPage))
	params.Set("page", strconv.Itoa(page))

	url := getUrl("search", format, arrangeParams(params))
	return getData(url)
}

// getData requests a URL from Quandl and returns the raw response string
func getData(url string) ([]byte, error) {
	if CacheHandler != nil {
		if response := CacheHandler.Get(url); response != nil {
			return response, nil
		}
	}

	response, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if CacheHandler != nil {
		if err := CacheHandler.Set(url, contents); err != nil {
			return nil, err
		}
	}

	return contents, nil
}

// getUrl receives a kind that points to a URL template and
// a variable number of strings, which will be replaced
// in the template.
func getUrl(kind string, args ...interface{}) string {
	template := urlTemplates[kind]
	LastUrl = strings.Trim(fmt.Sprintf(template, args...), "&?")
	return LastUrl
}

// arrangeParams takes an Options map and converts it to
// a query string. It will also append the api key as needed.
func arrangeParams(qs Options) string {
	if qs == nil {
		if ApiKey == "" {
			return ""
		}
		qs = Options{}
	}
	if ApiKey != "" {
		qs.Set("auth_token", ApiKey)
	}
	return url.Values(qs).Encode()
}

// symbolsToString converts an array of symbols to the format
// needed for a multiset Quandl request.
func symbolsToString(symbols []string) string {
	var result []string
	for _, symbol := range symbols {
		result = append(result, strings.Replace(symbol, "/", ".", -1))
	}
	return strings.Join(result, ",")
}

// marshallerError returns a formatted error that includes the response
func marshallerError(response []byte, err error) error {
	return errors.New("JSON Marshaller Error:\nRESPONSE:\n" + string(response) + "\n\nERROR:\n" + err.Error())
}
<commit_msg>Changed some return values to pointers<commit_after>package quandl

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

// ApiKey is used to set your api key before you make any call
var ApiKey = ""

// LastUrl will hold the last requested URL after each call
var LastUrl = ""

// CacheHandler is a reference to a struct that implements the Cacher interface.
// If set, it will use it to get documents from the cache or set to it.
// See the example cache handler in the quandl/cache package.
var CacheHandler Cacher

var urlTemplates map[string]string = map[string]string{
	"symbol":  "https://www.quandl.com/api/v1/datasets/%s.%s?%s",
	"symbols": "https://www.quandl.com/api/v1/multisets.%s?columns=%s&%s",
	"search":  "https://www.quandl.com/api/v1/datasets.%s?%s",
	"list":    "http://www.quandl.com/api/v2/datasets.%s?%s",
}

// Type Options is used to send additional parameters in the Quandl request
type Options url.Values

// Set registers a key=value pair to be sent in the Quandl request
func (o Options) Set(key, value string) {
	o[key] = []string{value}
}

type Cacher interface {
	Get(key string) []byte
	Set(key string, data []byte) error
}

// GetSymbol returns data for a given symbol
func GetSymbol(symbol string, params Options) (*SymbolResponse, error) {
	raw, err := GetSymbolRaw(symbol, "json", params)
	var response SymbolResponse
	if err != nil {
		return &response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return &response, marshallerError(raw, err)
	}
	return &response, nil
}

// GetSymbols returns data for a symbols array
func GetSymbols(symbols []string, params Options) (*SymbolsResponse, error) {
	raw, err := GetSymbolsRaw(symbols, "json", params)
	var response SymbolsResponse
	if err != nil {
		return &response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return &response, marshallerError(raw, err)
	}
	return &response, nil
}

// GetList returns a list of symbols for a source
func GetList(source string, page int, perPage int) (*ListResponse, error) {
	raw, err := GetListRaw(source, "json", page, perPage)
	var response ListResponse
	if err != nil {
		return &response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return &response, marshallerError(raw, err)
	}
	return &response, nil
}

// GetSearch returns search results
func GetSearch(query string, page int, perPage int) (*SearchResponse, error) {
	raw, err := GetSearchRaw(query, "json", page, perPage)
	var response SearchResponse
	if err != nil {
		return &response, err
	}

	err = json.Unmarshal(raw, &response)
	if err != nil {
		return &response, marshallerError(raw, err)
	}
	return &response, nil
}

// GetSymbolRaw returns CSV, JSON or XML data for a given symbol
func GetSymbolRaw(symbol string, format string, params Options) ([]byte, error) {
	url := getUrl("symbol", symbol, format, arrangeParams(params))
	return getData(url)
}

// GetSymbolsRaw returns CSV, JSON or XML data for multiple symbols
func GetSymbolsRaw(symbols []string, format string, params Options) ([]byte, error) {
	url := getUrl("symbols", format, symbolsToString(symbols), arrangeParams(params))
	return getData(url)
}

// GetListRaw returns a list of symbols for a source as CSV, JSON or XML
func GetListRaw(source string, format string, page int, perPage int) ([]byte, error) {
	params := Options{}

	params.Set("query", "*")
	params.Set("source_code", source)
	params.Set("per_page", strconv.Itoa(perPage))
	params.Set("page", strconv.Itoa(page))

	url := getUrl("list", format, arrangeParams(params))
	return getData(url)
}

// GetSearchRaw returns search results as JSON or XML
func GetSearchRaw(query string, format string, page int, perPage int) ([]byte, error) {
	params := Options{}

	if format == "csv" {
		format = "json"
	}

	params.Set("query", query)
	params.Set("per_page", strconv.Itoa(perPage))
	params.Set("page", strconv.Itoa(page))

	url := getUrl("search", format, arrangeParams(params))
	return getData(url)
}

// getData requests a URL from Quandl and returns the raw response string
func getData(url string) ([]byte, error) {
	if CacheHandler != nil {
		if response := CacheHandler.Get(url); response != nil {
			return response, nil
		}
	}

	response, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if CacheHandler != nil {
		if err := CacheHandler.Set(url, contents); err != nil {
			return nil, err
		}
	}

	return contents, nil
}

// getUrl receives a kind that points to a URL template and
// a variable number of strings, which will be replaced
// in the template.
func getUrl(kind string, args ...interface{}) string {
	template := urlTemplates[kind]
	LastUrl = strings.Trim(fmt.Sprintf(template, args...), "&?")
	return LastUrl
}

// arrangeParams takes an Options map and converts it to
// a query string. It will also append the api key as needed.
func arrangeParams(qs Options) string {
	if qs == nil {
		if ApiKey == "" {
			return ""
		}
		qs = Options{}
	}
	if ApiKey != "" {
		qs.Set("auth_token", ApiKey)
	}
	return url.Values(qs).Encode()
}

// symbolsToString converts an array of symbols to the format
// needed for a multiset Quandl request.
func symbolsToString(symbols []string) string {
	var result []string
	for _, symbol := range symbols {
		result = append(result, strings.Replace(symbol, "/", ".", -1))
	}
	return strings.Join(result, ",")
}

// marshallerError returns a formatted error that includes the response
func marshallerError(response []byte, err error) error {
	return errors.New("JSON Marshaller Error:\nRESPONSE:\n" + string(response) + "\n\nERROR:\n" + err.Error())
}
<|endoftext|>


<commit_before>package gobrake

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"
)

type QueueInfo struct {
	Queue string
	Func  string
	File  string
	Line  int
	Start time.Time
	End   time.Time
}

type queueKey struct {
	Queue string    `json:"queue"`
	Func  string    `json:"function"`
	File  string    `json:"file"`
	Line  int       `json:"line"`
	Time  time.Time `json:"time"`
}

type queueKeyStat struct {
	queueKey
	*routeStat
}

type queueStats struct {
	opt    *NotifierOptions
	apiURL string

	flushTimer *time.Timer
	addWG      *sync.WaitGroup

	mu sync.Mutex
	m  map[queueKey]*routeStat
}

func newQueueStats(opt *NotifierOptions) *queueStats {
	return &queueStats{
		opt: opt,
		apiURL: fmt.Sprintf("%s/api/v5/projects/%d/queues-stats",
			opt.Host, opt.ProjectId),
	}
}

func (s *queueStats) init() {
	if s.flushTimer == nil {
		s.flushTimer = time.AfterFunc(flushPeriod, s.flush)
		s.addWG = new(sync.WaitGroup)
		s.m = make(map[queueKey]*routeStat)
	}
}

func (s *queueStats) flush() {
	s.mu.Lock()

	s.flushTimer = nil
	addWG := s.addWG
	s.addWG = nil
	m := s.m
	s.m = nil

	s.mu.Unlock()

	addWG.Wait()
	err := s.send(m)
	if err != nil {
		logger.Printf("queueStats.send failed: %s", err)
	}
}

type queuesOut struct {
	Env    string         `json:"environment"`
	Queues []queueKeyStat `json:"queues"`
}

func (s *queueStats) send(m map[queueKey]*routeStat) error {
	var queues []queueKeyStat
	for k, v := range m {
		err := v.td.Compress()
		if err != nil {
			return err
		}

		b, err := v.td.AsBytes()
		if err != nil {
			return err
		}
		v.TDigest = b

		queues = append(queues, queueKeyStat{
			queueKey:  k,
			routeStat: v,
		})
	}

	buf := buffers.Get().(*bytes.Buffer)
	defer buffers.Put(buf)
	buf.Reset()

	out := queuesOut{
		Env:    s.opt.Environment,
		Queues: queues,
	}
	err := json.NewEncoder(buf).Encode(&out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", s.apiURL, buf)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", "Bearer "+s.opt.ProjectKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", userAgent)
	resp, err := s.opt.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	buf.Reset()
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}

	switch resp.StatusCode {
	case http.StatusUnauthorized:
		return errUnauthorized
	case httpStatusTooManyRequests:
		return errIPRateLimited
	}

	err = fmt.Errorf("got unexpected response status=%q", resp.Status)
	return err
}

func (s *queueStats) Notify(c context.Context, q *QueueInfo) error {
	key := queueKey{
		Queue: q.Queue,
		Func:  q.Func,
		File:  q.File,
		Line:  q.Line,
		Time:  q.Start.UTC().Truncate(time.Minute),
	}

	s.mu.Lock()
	s.init()
	stat, ok := s.m[key]
	if !ok {
		stat = &routeStat{}
		s.m[key] = stat
	}
	addWG := s.addWG
	s.addWG.Add(1)
	s.mu.Unlock()

	dur := q.End.Sub(q.Start)

	stat.mu.Lock()
	err := stat.Add(dur)
	addWG.Done()
	stat.mu.Unlock()

	return err
}
<commit_msg>Remove unused fields<commit_after>package gobrake

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"
)

type QueueInfo struct {
	Queue string
	Start time.Time
	End   time.Time
}

type queueKey struct {
	Queue string    `json:"queue"`
	Time  time.Time `json:"time"`
}

type queueKeyStat struct {
	queueKey
	*routeStat
}

type queueStats struct {
	opt    *NotifierOptions
	apiURL string

	flushTimer *time.Timer
	addWG      *sync.WaitGroup

	mu sync.Mutex
	m  map[queueKey]*routeStat
}

func newQueueStats(opt *NotifierOptions) *queueStats {
	return &queueStats{
		opt: opt,
		apiURL: fmt.Sprintf("%s/api/v5/projects/%d/queues-stats",
			opt.Host, opt.ProjectId),
	}
}

func (s *queueStats) init() {
	if s.flushTimer == nil {
		s.flushTimer = time.AfterFunc(flushPeriod, s.flush)
		s.addWG = new(sync.WaitGroup)
		s.m = make(map[queueKey]*routeStat)
	}
}

func (s *queueStats) flush() {
	s.mu.Lock()

	s.flushTimer = nil
	addWG := s.addWG
	s.addWG = nil
	m := s.m
	s.m = nil

	s.mu.Unlock()

	addWG.Wait()
	err := s.send(m)
	if err != nil {
		logger.Printf("queueStats.send failed: %s", err)
	}
}

type queuesOut struct {
	Env    string         `json:"environment"`
	Queues []queueKeyStat `json:"queues"`
}

func (s *queueStats) send(m map[queueKey]*routeStat) error {
	var queues []queueKeyStat
	for k, v := range m {
		err := v.td.Compress()
		if err != nil {
			return err
		}

		b, err := v.td.AsBytes()
		if err != nil {
			return err
		}
		v.TDigest = b

		queues = append(queues, queueKeyStat{
			queueKey:  k,
			routeStat: v,
		})
	}

	buf := buffers.Get().(*bytes.Buffer)
	defer buffers.Put(buf)
	buf.Reset()

	out := queuesOut{
		Env:    s.opt.Environment,
		Queues: queues,
	}
	err := json.NewEncoder(buf).Encode(&out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", s.apiURL, buf)
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", "Bearer "+s.opt.ProjectKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", userAgent)
	resp, err := s.opt.HTTPClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	buf.Reset()
	_, err = buf.ReadFrom(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return nil
	}

	switch resp.StatusCode {
	case http.StatusUnauthorized:
		return errUnauthorized
	case httpStatusTooManyRequests:
		return errIPRateLimited
	}

	err = fmt.Errorf("got unexpected response status=%q", resp.Status)
	return err
}

func (s *queueStats) Notify(c context.Context, q *QueueInfo) error {
	key := queueKey{
		Queue: q.Queue,
		Time:  q.Start.UTC().Truncate(time.Minute),
	}

	s.mu.Lock()
	s.init()
	stat, ok := s.m[key]
	if !ok {
		stat = &routeStat{}
		s.m[key] = stat
	}
	addWG := s.addWG
	s.addWG.Add(1)
	s.mu.Unlock()

	dur := q.End.Sub(q.Start)

	stat.mu.Lock()
	err := stat.Add(dur)
	addWG.Done()
	stat.mu.Unlock()

	return err
}
<|endoftext|>


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

package dns

// These raw* functions do not use reflection, they directly set the values
// in the buffer. There are faster than their reflection counterparts.

// RawSetId sets the message id in buf.
func rawSetId(msg []byte, i uint16) bool {
	if len(msg) < 2 {
		return false
	}
	msg[0], msg[1] = packUint16(i)
	return true
}

// rawSetQuestionLen sets the lenght of the question section.
func rawSetQuestionLen(msg []byte, i uint16) {
	if len(msg) < 6 {
		return false
	}
	msg[4], msg[5] = packUint16(i)
	return true
}

// rawSetAnswerLen sets the lenght of the answer section.
func rawSetAnswerLen(msg []byte, i uint16) {
	if len(msg) < 8 {
		return false
	}
	msg[6], msg[7] = packUint16(i)
	return true
}

// rawSetsNsLen sets the lenght of the authority section.
func rawSetNsLen(msg []byte, i uint16) {
	if len(msg) < 10 {
		return false
	}
	msg[8], msg[9] = packUint16(i)
	return true
}

// rawSetExtraLen sets the lenght of the additional section.
func rawSetExtraLen(msg []byte, i uint16) {
	if len(msg) < 12 {
		return false
	}
	msg[10], msg[11] = packUint16(i)
	return true
}

// rawSetRdlength sets the rdlength in the header of
// the RR. The offset 'off' must be positioned at the
// start of the header of the RR, 'end' must be the
// end of the RR.
func rawSetRdlength(msg []byte, off, end int) bool {
	l := len(msg)
Loop:
	for {
		if off+1 > l {
			return false
		}
		c := int(msg[off])
		off++
		switch c & 0xC0 {
		case 0x00:
			if c == 0x00 {
				// End of the domainname
				break Loop
			}

		case 0xC0:
			// pointer, next byte included, ends domainname
			off++
			break Loop
		}
	}
	// The domainname has been seen, we at the start of the fixed part in the header.
	// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
	off += 2 + 2 + 4
	if off+2 > l {
		return false
	}
	//off+1 is the end of the header, 'end' is the end of the rr
	//so 'end' - 'off+2' is the lenght of the rdata
	msg[off], msg[off+1] = packUint16(uint16(end - (off + 2)))
	return true
}
<commit_msg>make them return booleans<commit_after>// Copyright 2012 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dns

// These raw* functions do not use reflection, they directly set the values
// in the buffer. There are faster than their reflection counterparts.

// RawSetId sets the message id in buf.
func rawSetId(msg []byte, i uint16) bool {
	if len(msg) < 2 {
		return false
	}
	msg[0], msg[1] = packUint16(i)
	return true
}

// rawSetQuestionLen sets the lenght of the question section.
func rawSetQuestionLen(msg []byte, i uint16) bool {
	if len(msg) < 6 {
		return false
	}
	msg[4], msg[5] = packUint16(i)
	return true
}

// rawSetAnswerLen sets the lenght of the answer section.
func rawSetAnswerLen(msg []byte, i uint16) bool {
	if len(msg) < 8 {
		return false
	}
	msg[6], msg[7] = packUint16(i)
	return true
}

// rawSetsNsLen sets the lenght of the authority section.
func rawSetNsLen(msg []byte, i uint16) bool {
	if len(msg) < 10 {
		return false
	}
	msg[8], msg[9] = packUint16(i)
	return true
}

// rawSetExtraLen sets the lenght of the additional section.
func rawSetExtraLen(msg []byte, i uint16) bool {
	if len(msg) < 12 {
		return false
	}
	msg[10], msg[11] = packUint16(i)
	return true
}

// rawSetRdlength sets the rdlength in the header of
// the RR. The offset 'off' must be positioned at the
// start of the header of the RR, 'end' must be the
// end of the RR.
func rawSetRdlength(msg []byte, off, end int) bool {
	l := len(msg)
Loop:
	for {
		if off+1 > l {
			return false
		}
		c := int(msg[off])
		off++
		switch c & 0xC0 {
		case 0x00:
			if c == 0x00 {
				// End of the domainname
				break Loop
			}

		case 0xC0:
			// pointer, next byte included, ends domainname
			off++
			break Loop
		}
	}
	// The domainname has been seen, we at the start of the fixed part in the header.
	// Type is 2 bytes, class is 2 bytes, ttl 4 and then 2 bytes for the length.
	off += 2 + 2 + 4
	if off+2 > l {
		return false
	}
	//off+1 is the end of the header, 'end' is the end of the rr
	//so 'end' - 'off+2' is the lenght of the rdata
	msg[off], msg[off+1] = packUint16(uint16(end - (off + 2)))
	return true
}
<|endoftext|>


<commit_before>package rardecode

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"time"
)

// FileHeader HostOS types
const (
	HostOSUnknown = 0
	HostOSMSDOS   = 1
	HostOSOS2     = 2
	HostOSWindows = 3
	HostOSUnix    = 4
	HostOSMacOS   = 5
	HostOSBeOS    = 6
)

const (
	maxPassword = 128
)

var (
	errShortFile        = errors.New("rardecode: decoded file too short")
	errInvalidFileBlock = errors.New("rardecode: invalid file block")
	errUnexpectedArcEnd = errors.New("rardecode: unexpected end of archive")
	errBadFileChecksum  = errors.New("rardecode: bad file checksum")
)

type limitedReader struct {
	r        io.Reader
	n        int64 // bytes remaining
	shortErr error // error returned when r returns io.EOF with n > 0
}

func (l *limitedReader) Read(p []byte) (int, error) {
	if l.n <= 0 {
		return 0, io.EOF
	}
	if int64(len(p)) > l.n {
		p = p[0:l.n]
	}
	n, err := l.r.Read(p)
	l.n -= int64(n)
	if err == io.EOF && l.n > 0 {
		return n, l.shortErr
	}
	return n, err
}

// limitReader returns an io.Reader that reads from r and stops with
// io.EOF after n bytes.
// If r returns an io.EOF before reading n bytes, err is returned.
func limitReader(r io.Reader, n int64, err error) io.Reader {
	return &limitedReader{r, n, err}
}

// fileChecksum allows file checksum validations to be performed.
// File contents must first be written to fileChecksum. Then valid is
// called to perform the file checksum calculation to determine
// if the file contents are valid or not.
type fileChecksum interface {
	io.Writer
	valid() bool
}

// FileHeader represents a single file in a RAR archive.
type FileHeader struct {
	Name             string    // file name using '/' as the directory separator
	IsDir            bool      // is a directory
	HostOS           byte      // Host OS the archive was created on
	Attributes       int64     // file attributes
	PackedSize       int64     // packed file size (or first block if the file spans volumes)
	UnPackedSize     int64     // unpacked file size
	UnKnownSize      bool      // unpacked file size is not known
	ModificationTime time.Time // modification time (non-zero if set)
	CreationTime     time.Time // creation time (non-zero if set)
	AccessTime       time.Time // access time (non-zero if set)
	Version          int       // file version
}

// fileBlockHeader represents a file block in a RAR archive.
// Files may comprise one or more file blocks.
// Solid files retain decode tables and dictionary from previous solid files in the archive.
type fileBlockHeader struct {
	first   bool         // first block in file
	last    bool         // last block in file
	solid   bool         // file is solid
	winSize uint         // log base 2 of decode window size
	cksum   fileChecksum // file checksum
	decoder decoder      // decoder to use for file
	key     []byte       // key for AES, non-empty if file encrypted
	iv      []byte       // iv for AES, non-empty if file encrypted
	FileHeader
}

// fileBlockReader provides sequential access to file blocks in a RAR archive.
type fileBlockReader interface {
	io.Reader                        // Read's read data from the current file block
	next() (*fileBlockHeader, error) // advances to the next file block
	reset(r io.Reader)               // resets for new volume file
	isSolid() bool                   // is archive solid
	version() int                    // returns current archive format version
}

// packedFileReader provides sequential access to packed files in a RAR archive.
type packedFileReader struct {
	r fileBlockReader
	h *fileBlockHeader // current file header
}

// nextBlockInFile advances to the next file block in the current file, or returns
// an error if there is a problem.
// It is invalid to call this when already at the last block in the current file.
func (f *packedFileReader) nextBlockInFile() error {
	h, err := f.r.next()
	if err != nil {
		if err == io.EOF {
			// archive ended, but file hasn't
			return errUnexpectedArcEnd
		}
		return err
	}
	if h.first || h.Name != f.h.Name {
		return errInvalidFileBlock
	}
	f.h = h
	return nil
}

// next advances to the next packed file in the RAR archive.
func (f *packedFileReader) next() (*fileBlockHeader, error) {
	if f.h != nil {
		// skip to last block in current file
		for !f.h.last {
			if err := f.nextBlockInFile(); err != nil {
				return nil, err
			}
		}
	}
	var err error
	f.h, err = f.r.next() // get next file block
	if err != nil {
		return nil, err
	}
	if !f.h.first {
		return nil, errInvalidFileBlock
	}
	return f.h, nil
}

// Read reads the packed data for the current file into p.
func (f *packedFileReader) Read(p []byte) (int, error) {
	n, err := f.r.Read(p) // read current block data
	for err == io.EOF {   // current block empty
		if n > 0 {
			return n, nil
		}
		if f.h == nil || f.h.last {
			return 0, io.EOF // last block so end of file
		}
		if err := f.nextBlockInFile(); err != nil {
			return 0, err
		}
		n, err = f.r.Read(p) // read new block data
	}
	return n, err
}

// Reader provides sequential access to files in a RAR archive.
type Reader struct {
	r      io.Reader        // reader for current unpacked file
	pr     packedFileReader // reader for current packed file
	dr     decodeReader     // reader for decoding and filters if file is compressed
	cksum  fileChecksum     // current file checksum
	solidr io.Reader        // reader for solid file
}

// Read reads from the current file in the RAR archive.
func (r *Reader) Read(p []byte) (int, error) {
	n, err := r.r.Read(p)
	if err == io.EOF && r.cksum != nil && !r.cksum.valid() {
		return n, errBadFileChecksum
	}
	return n, err
}

// Next advances to the next file in the archive.
func (r *Reader) Next() (*FileHeader, error) {
	if r.solidr != nil {
		// solid files must be read fully to update decoder information
		if _, err := io.Copy(ioutil.Discard, r.solidr); err != nil {
			return nil, err
		}
	}

	h, err := r.pr.next() // skip to next file
	if err != nil {
		return nil, err
	}
	r.solidr = nil

	r.r = io.Reader(&r.pr) // start with packed file reader

	// check for encryption
	if len(h.key) > 0 && len(h.iv) > 0 {
		r.r = newAesDecryptReader(r.r, h.key, h.iv) // decrypt
	}
	// check for compression
	if h.decoder != nil {
		err = r.dr.init(r.r, h.decoder, h.winSize, !h.solid)
		if err != nil {
			return nil, err
		}
		r.r = &r.dr
		if r.pr.r.isSolid() {
			r.solidr = r.r
		}
	}
	if h.UnPackedSize >= 0 && !h.UnKnownSize {
		// Limit reading to UnPackedSize as there may be padding
		r.r = limitReader(r.r, h.UnPackedSize, errShortFile)
	}
	r.cksum = h.cksum
	if r.cksum != nil {
		r.r = io.TeeReader(r.r, h.cksum) // write file data to checksum as it is read
	}
	fh := new(FileHeader)
	*fh = h.FileHeader
	return fh, nil
}

func (r *Reader) init(fbr fileBlockReader) {
	r.r = bytes.NewReader(nil) // initial reads will always return EOF
	r.pr.r = fbr
}

// NewReader creates a Reader reading from r.
func NewReader(r io.Reader, password string) (*Reader, error) {
	fbr, err := newFileBlockReader(r, password)
	if err != nil {
		return nil, err
	}
	rr := new(Reader)
	rr.init(fbr)
	return rr, nil
}

type ReadCloser struct {
	v *volume
	Reader
}

// Close closes the rar file.
func (rc *ReadCloser) Close() error {
	return rc.v.Close()
}

// OpenReader opens a RAR archive specified by the name and returns a ReadCloser.
func OpenReader(name, password string) (*ReadCloser, error) {
	v, err := openVolume(name, password)
	if err != nil {
		return nil, err
	}
	rc := new(ReadCloser)
	rc.v = v
	rc.Reader.init(v)
	return rc, nil
}
<commit_msg>add Mode() method to FileHeader to return os.FileMode for the file.<commit_after>package rardecode

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"time"
)

// FileHeader HostOS types
const (
	HostOSUnknown = 0
	HostOSMSDOS   = 1
	HostOSOS2     = 2
	HostOSWindows = 3
	HostOSUnix    = 4
	HostOSMacOS   = 5
	HostOSBeOS    = 6
)

const (
	maxPassword = 128
)

var (
	errShortFile        = errors.New("rardecode: decoded file too short")
	errInvalidFileBlock = errors.New("rardecode: invalid file block")
	errUnexpectedArcEnd = errors.New("rardecode: unexpected end of archive")
	errBadFileChecksum  = errors.New("rardecode: bad file checksum")
)

type limitedReader struct {
	r        io.Reader
	n        int64 // bytes remaining
	shortErr error // error returned when r returns io.EOF with n > 0
}

func (l *limitedReader) Read(p []byte) (int, error) {
	if l.n <= 0 {
		return 0, io.EOF
	}
	if int64(len(p)) > l.n {
		p = p[0:l.n]
	}
	n, err := l.r.Read(p)
	l.n -= int64(n)
	if err == io.EOF && l.n > 0 {
		return n, l.shortErr
	}
	return n, err
}

// limitReader returns an io.Reader that reads from r and stops with
// io.EOF after n bytes.
// If r returns an io.EOF before reading n bytes, err is returned.
func limitReader(r io.Reader, n int64, err error) io.Reader {
	return &limitedReader{r, n, err}
}

// fileChecksum allows file checksum validations to be performed.
// File contents must first be written to fileChecksum. Then valid is
// called to perform the file checksum calculation to determine
// if the file contents are valid or not.
type fileChecksum interface {
	io.Writer
	valid() bool
}

// FileHeader represents a single file in a RAR archive.
type FileHeader struct {
	Name             string    // file name using '/' as the directory separator
	IsDir            bool      // is a directory
	HostOS           byte      // Host OS the archive was created on
	Attributes       int64     // Host OS specific file attributes
	PackedSize       int64     // packed file size (or first block if the file spans volumes)
	UnPackedSize     int64     // unpacked file size
	UnKnownSize      bool      // unpacked file size is not known
	ModificationTime time.Time // modification time (non-zero if set)
	CreationTime     time.Time // creation time (non-zero if set)
	AccessTime       time.Time // access time (non-zero if set)
	Version          int       // file version
}

// Mode returns an os.FileMode for the file, calculated from the Attributes field.
func (f *FileHeader) Mode() os.FileMode {
	var m os.FileMode

	if f.IsDir {
		m = os.ModeDir
	}
	if f.HostOS == HostOSWindows {
		if f.IsDir {
			m |= 0777
		} else if f.Attributes&1 > 0 {
			m |= 0444 // readonly
		} else {
			m |= 0666
		}
		return m
	}
	// assume unix perms for all remaining os types
	m |= os.FileMode(f.Attributes) & os.ModePerm

	// only check other bits on unix host created archives
	if f.HostOS != HostOSUnix {
		return m
	}

	if f.Attributes&0x200 != 0 {
		m |= os.ModeSticky
	}
	if f.Attributes&0x400 != 0 {
		m |= os.ModeSetgid
	}
	if f.Attributes&0x800 != 0 {
		m |= os.ModeSetuid
	}

	// Check for additional file types.
	if f.Attributes&0xF000 == 0xA000 {
		m |= os.ModeSymlink
	}
	return m
}

// fileBlockHeader represents a file block in a RAR archive.
// Files may comprise one or more file blocks.
// Solid files retain decode tables and dictionary from previous solid files in the archive.
type fileBlockHeader struct {
	first   bool         // first block in file
	last    bool         // last block in file
	solid   bool         // file is solid
	winSize uint         // log base 2 of decode window size
	cksum   fileChecksum // file checksum
	decoder decoder      // decoder to use for file
	key     []byte       // key for AES, non-empty if file encrypted
	iv      []byte       // iv for AES, non-empty if file encrypted
	FileHeader
}

// fileBlockReader provides sequential access to file blocks in a RAR archive.
type fileBlockReader interface {
	io.Reader                        // Read's read data from the current file block
	next() (*fileBlockHeader, error) // advances to the next file block
	reset(r io.Reader)               // resets for new volume file
	isSolid() bool                   // is archive solid
	version() int                    // returns current archive format version
}

// packedFileReader provides sequential access to packed files in a RAR archive.
type packedFileReader struct {
	r fileBlockReader
	h *fileBlockHeader // current file header
}

// nextBlockInFile advances to the next file block in the current file, or returns
// an error if there is a problem.
// It is invalid to call this when already at the last block in the current file.
func (f *packedFileReader) nextBlockInFile() error {
	h, err := f.r.next()
	if err != nil {
		if err == io.EOF {
			// archive ended, but file hasn't
			return errUnexpectedArcEnd
		}
		return err
	}
	if h.first || h.Name != f.h.Name {
		return errInvalidFileBlock
	}
	f.h = h
	return nil
}

// next advances to the next packed file in the RAR archive.
func (f *packedFileReader) next() (*fileBlockHeader, error) {
	if f.h != nil {
		// skip to last block in current file
		for !f.h.last {
			if err := f.nextBlockInFile(); err != nil {
				return nil, err
			}
		}
	}
	var err error
	f.h, err = f.r.next() // get next file block
	if err != nil {
		return nil, err
	}
	if !f.h.first {
		return nil, errInvalidFileBlock
	}
	return f.h, nil
}

// Read reads the packed data for the current file into p.
func (f *packedFileReader) Read(p []byte) (int, error) {
	n, err := f.r.Read(p) // read current block data
	for err == io.EOF {   // current block empty
		if n > 0 {
			return n, nil
		}
		if f.h == nil || f.h.last {
			return 0, io.EOF // last block so end of file
		}
		if err := f.nextBlockInFile(); err != nil {
			return 0, err
		}
		n, err = f.r.Read(p) // read new block data
	}
	return n, err
}

// Reader provides sequential access to files in a RAR archive.
type Reader struct {
	r      io.Reader        // reader for current unpacked file
	pr     packedFileReader // reader for current packed file
	dr     decodeReader     // reader for decoding and filters if file is compressed
	cksum  fileChecksum     // current file checksum
	solidr io.Reader        // reader for solid file
}

// Read reads from the current file in the RAR archive.
func (r *Reader) Read(p []byte) (int, error) {
	n, err := r.r.Read(p)
	if err == io.EOF && r.cksum != nil && !r.cksum.valid() {
		return n, errBadFileChecksum
	}
	return n, err
}

// Next advances to the next file in the archive.
func (r *Reader) Next() (*FileHeader, error) {
	if r.solidr != nil {
		// solid files must be read fully to update decoder information
		if _, err := io.Copy(ioutil.Discard, r.solidr); err != nil {
			return nil, err
		}
	}

	h, err := r.pr.next() // skip to next file
	if err != nil {
		return nil, err
	}
	r.solidr = nil

	r.r = io.Reader(&r.pr) // start with packed file reader

	// check for encryption
	if len(h.key) > 0 && len(h.iv) > 0 {
		r.r = newAesDecryptReader(r.r, h.key, h.iv) // decrypt
	}
	// check for compression
	if h.decoder != nil {
		err = r.dr.init(r.r, h.decoder, h.winSize, !h.solid)
		if err != nil {
			return nil, err
		}
		r.r = &r.dr
		if r.pr.r.isSolid() {
			r.solidr = r.r
		}
	}
	if h.UnPackedSize >= 0 && !h.UnKnownSize {
		// Limit reading to UnPackedSize as there may be padding
		r.r = limitReader(r.r, h.UnPackedSize, errShortFile)
	}
	r.cksum = h.cksum
	if r.cksum != nil {
		r.r = io.TeeReader(r.r, h.cksum) // write file data to checksum as it is read
	}
	fh := new(FileHeader)
	*fh = h.FileHeader
	return fh, nil
}

func (r *Reader) init(fbr fileBlockReader) {
	r.r = bytes.NewReader(nil) // initial reads will always return EOF
	r.pr.r = fbr
}

// NewReader creates a Reader reading from r.
func NewReader(r io.Reader, password string) (*Reader, error) {
	fbr, err := newFileBlockReader(r, password)
	if err != nil {
		return nil, err
	}
	rr := new(Reader)
	rr.init(fbr)
	return rr, nil
}

type ReadCloser struct {
	v *volume
	Reader
}

// Close closes the rar file.
func (rc *ReadCloser) Close() error {
	return rc.v.Close()
}

// OpenReader opens a RAR archive specified by the name and returns a ReadCloser.
func OpenReader(name, password string) (*ReadCloser, error) {
	v, err := openVolume(name, password)
	if err != nil {
		return nil, err
	}
	rc := new(ReadCloser)
	rc.v = v
	rc.Reader.init(v)
	return rc, nil
}
<|endoftext|>