<commit_before>package stringsutil

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
	"unicode"

	"github.com/huandu/xstrings"
)

const (
	StringToLower     = "StringToLower"
	SpaceToHyphen     = "SpaceToHyphen"
	SpaceToUnderscore = "SpaceToUnderscore"
	lowerAZ           = "abcdefghijklmnopqrstuvwxyz"
	upperAZ           = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	lowerUpper        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	upperLower        = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
)

var (
	rxControl = regexp.MustCompile(`[[:cntrl:]]`)
	rxSpaces  = regexp.MustCompile(`\s+`)
)

// PadLeft prepends a string to a base string until the string
// length is greater or equal to the desired length.
func PadLeft(str string, pad string, length int) string {
	for {
		str = pad + str
		if len(str) >= length {
			return str[0:length]
		}
	}
}

// PadRight appends a string to a base string until the string
// length is greater or equal to the desired length.
func PadRight(str string, pad string, length int) string {
	for {
		str += pad
		if len(str) > length {
			return str[0:length]
		}
	}
}

// Capitalize returns a string with the first character
// capitalized and the rest lower cased.
func Capitalize(s1 string) string {
	return ToUpperFirst(s1, true)
}

// ToLowerFirst lower cases the first letter in the string
func ToLowerFirst(s1 string) string {
	a1 := []rune(s1)
	a1[0] = unicode.ToLower(a1[0])
	return string(a1)
	/*
		if s == "" {
			return ""
		}
		r, n := utf8.DecodeRuneInString(s)
		return string(unicode.ToLower(r)) + s[n:]
	*/
}

// ToUpperFirst upper cases the first letter in the string
func ToUpperFirst(s1 string, lowerRest bool) string {
	if lowerRest {
		s1 = strings.ToLower(s1)
	}
	a1 := []rune(s1)
	a1[0] = unicode.ToUpper(a1[0])
	return string(a1)
	/*
			if s == "" {
			return ""
		}
		r, n := utf8.DecodeRuneInString(s)
		return string(unicode.ToUpper(r)) + s[n:]
	*/
}

// ToBool converts a string to a boolean value
// looking for the string "true" in any case.
func ToBool(v string) bool {
	if strings.ToLower(strings.TrimSpace(v)) == "true" {
		return true
	}
	return false
}

func SubstringIsSuffix(s1, s2 string) bool {
	len1 := len(s1)
	len2 := len(s2)
	idx := strings.Index(s1, s2)
	if len1 >= len2 && idx > -1 && idx == (len1-len2) {
		return true
	}
	return false
}

var rxSpace = regexp.MustCompile(`\s+`)

// RemoveSpaces eliminates all spaces in a string.
func RemoveSpaces(input string) string {
	return rxSpace.ReplaceAllString(input, "")
}

// SplitCondenseSpace splits a string and trims spaces on
// remaining elements, removing empty elements.
func SplitCondenseSpace(s, sep string) []string {
	split := strings.Split(s, sep)
	strs := []string{}
	for _, str := range split {
		str = strings.TrimSpace(str)
		if len(str) > 0 {
			strs = append(strs, str)
		}
	}
	return strs
}

// CondenseString trims whitespace at the ends of the string
// as well as in between.
func CondenseString(content string, join_lines bool) string {
	if join_lines {
		content = regexp.MustCompile(`\n`).ReplaceAllString(content, " ")
	}
	// Beginning
	content = regexp.MustCompile(`^\s+`).ReplaceAllString(content, "")
	// End
	content = regexp.MustCompile(`\s+$`).ReplaceAllString(content, "")
	// Middle
	content = regexp.MustCompile(`\n[\s\t\r]*\n`).ReplaceAllString(content, "\n")
	// Indentation
	content = regexp.MustCompile(`\n[\s\t\r]*`).ReplaceAllString(content, "\n")
	// Collapse
	content = regexp.MustCompile(`\s+`).ReplaceAllString(content, " ")
	return strings.TrimSpace(content)
}

// CondenseStringSimple removes extra spaces.
func CondenseStringSimple(s string) string {
	return strings.Join(strings.Fields(s), " ")
}

func StripControl(s string) string { return rxControl.ReplaceAllString(s, "") }

// TrimSentenceLength trims a string by a max length at word boundaries.
func TrimSentenceLength(sentenceInput string, maxLength int) string {
	if len(sentenceInput) <= maxLength {
		return sentenceInput
	}
	sentenceLen := string(sentenceInput[0:maxLength]) // first350 := string(s[0:350])
	rx_end := regexp.MustCompile(`[[:punct:]][^[[:punct:]]]*$`)
	sentencePunct := rx_end.ReplaceAllString(sentenceLen, "")
	if len(sentencePunct) >= 2 {
		return sentencePunct
	}
	return sentenceLen
}

func JoinTrimSpace(strs []string) string {
	return rxSpaces.ReplaceAllString(strings.Join(strs, " "), " ")
}

// JoinInterface joins an interface and returns a string. It takes
// a join separator, boolean to replace the join separator in the
// string parts and a separator alternate. `stripEmbeddedSep` strips
// separator string found within parts. `stripRepeatedSep` strips
// repeating separators. This flexibility is designed to support
// joining data for both CSVs and paths.
func JoinInterface(arr []interface{}, sep string, stripRepeatedSep bool, stripEmbeddedSep bool, altSep string) string {
	parts := []string{}
	rx := regexp.MustCompile(sep)
	for _, el := range arr {
		part := fmt.Sprintf("%v", el)
		if stripEmbeddedSep {
			part = rx.ReplaceAllString(part, altSep)
		}
		parts = append(parts, part)
	}
	joined := strings.Join(parts, sep)
	if stripRepeatedSep {
		joined = regexp.MustCompile(fmt.Sprintf("%s+", sep)).
			ReplaceAllString(joined, sep)
	}
	return joined
}

func JoinLiterary(slice []string, sep, joinWord string) string {
	switch len(slice) {
	case 0:
		return ""
	case 1:
		return slice[0]
	case 2:
		return slice[0] + " " + joinWord + " " + slice[1]
	default:
		last, rest := slice[len(slice)-1], slice[:len(slice)-1]
		rest = append(rest, joinWord+" "+last)
		return strings.Join(rest, sep+" ")
	}
}

func JoinLiteraryQuote(slice []string, leftQuote, rightQuote, sep, joinWord string) string {
	newSlice := SliceCondenseAndQuoteSpace(slice, leftQuote, rightQuote)
	switch len(newSlice) {
	case 0:
		return ""
	case 1:
		return newSlice[0]
	case 2:
		return newSlice[0] + " " + joinWord + " " + newSlice[1]
	default:
		last, rest := newSlice[len(newSlice)-1], newSlice[:len(newSlice)-1]
		rest = append(rest, joinWord+" "+last)
		return strings.Join(rest, sep+" ")
	}
}

func JoinStringsTrimSpaceToLowerSort(strs []string, sep string) string {
	wip := []string{}
	for _, s := range strs {
		s = strings.ToLower(strings.TrimSpace(s))
		if len(s) > 0 {
			wip = append(wip, s)
		}
	}
	sort.Strings(wip)
	return strings.Join(wip, sep)
}

func FormatString(s string, options []string) string {
	for _, opt := range options {
		switch strings.TrimSpace(opt) {
		case StringToLower:
			s = strings.ToLower(s)
		case SpaceToHyphen:
			s = regexp.MustCompile(`[\s-]+`).ReplaceAllString(s, "-")
		case SpaceToUnderscore:
			s = regexp.MustCompile(`[\s_]+`).ReplaceAllString(s, "_")
		}
	}
	return s
}

// CommonInitialisms is the listed by Go Lint.
const CommonInitialisms = "ACL,API,ASCII,CPU,CSS,DNS,EOF,GUID,HTML,HTTP,HTTPS,ID,IP,JSON,LHS,QPS,RAM,RHS,RPC,SLA,SMTP,SQL,SSH,TCP,TLS,TTL,UDP,UI,UID,UUID,URI,URL,UTF8,VM,XML,XMPP,XSRF,XSS"

// CommonInitialismsMap returns map[string]bool of upper case initialisms.
func CommonInitialismsMap() map[string]bool {
	ciMap := map[string]bool{}
	commonInitialisms := strings.Split(CommonInitialisms, ",")
	for _, ci := range commonInitialisms {
		ciMap[ci] = true
	}
	return ciMap
}

// StringToConstant is used to generate constant names for code generation.
// It uses the commonInitialisms in Go Lint.
func StringToConstant(s string) string {
	newParts := []string{}
	parts := strings.Split(s, "_")
	ciMap := CommonInitialismsMap()
	for _, p := range parts {
		pUp := strings.ToUpper(p)
		if _, ok := ciMap[pUp]; ok {
			newParts = append(newParts, pUp)
		} else {
			newParts = append(newParts, ToUpperFirst(p, true))
		}
	}
	return strings.Join(newParts, "")
}

func ToOpposite(s string) string {
	return xstrings.Translate(s, lowerUpper, upperLower)
}

var (
	rxRN = regexp.MustCompile(`\r\n`)
	rxR  = regexp.MustCompile(`\r`)
)

func NewlineToLinux(input string) string {
	return rxR.ReplaceAllString(rxRN.ReplaceAllString(input, "\n"), "\n")
}
<commit_msg>feat: stringsutil: add TrimSpaceWithDefault()<commit_after>package stringsutil

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
	"unicode"

	"github.com/huandu/xstrings"
)

const (
	StringToLower     = "StringToLower"
	SpaceToHyphen     = "SpaceToHyphen"
	SpaceToUnderscore = "SpaceToUnderscore"
	lowerAZ           = "abcdefghijklmnopqrstuvwxyz"
	upperAZ           = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	lowerUpper        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	upperLower        = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
)

var (
	rxControl = regexp.MustCompile(`[[:cntrl:]]`)
	rxSpaces  = regexp.MustCompile(`\s+`)
)

// PadLeft prepends a string to a base string until the string
// length is greater or equal to the desired length.
func PadLeft(str string, pad string, length int) string {
	for {
		str = pad + str
		if len(str) >= length {
			return str[0:length]
		}
	}
}

// PadRight appends a string to a base string until the string
// length is greater or equal to the desired length.
func PadRight(str string, pad string, length int) string {
	for {
		str += pad
		if len(str) > length {
			return str[0:length]
		}
	}
}

// Capitalize returns a string with the first character
// capitalized and the rest lower cased.
func Capitalize(s1 string) string {
	return ToUpperFirst(s1, true)
}

// ToLowerFirst lower cases the first letter in the string
func ToLowerFirst(s1 string) string {
	a1 := []rune(s1)
	a1[0] = unicode.ToLower(a1[0])
	return string(a1)
	/*
		if s == "" {
			return ""
		}
		r, n := utf8.DecodeRuneInString(s)
		return string(unicode.ToLower(r)) + s[n:]
	*/
}

// ToUpperFirst upper cases the first letter in the string
func ToUpperFirst(s1 string, lowerRest bool) string {
	if lowerRest {
		s1 = strings.ToLower(s1)
	}
	a1 := []rune(s1)
	a1[0] = unicode.ToUpper(a1[0])
	return string(a1)
	/*
			if s == "" {
			return ""
		}
		r, n := utf8.DecodeRuneInString(s)
		return string(unicode.ToUpper(r)) + s[n:]
	*/
}

// ToBool converts a string to a boolean value
// looking for the string "true" in any case.
func ToBool(v string) bool {
	if strings.ToLower(strings.TrimSpace(v)) == "true" {
		return true
	}
	return false
}

func SubstringIsSuffix(s1, s2 string) bool {
	len1 := len(s1)
	len2 := len(s2)
	idx := strings.Index(s1, s2)
	if len1 >= len2 && idx > -1 && idx == (len1-len2) {
		return true
	}
	return false
}

var rxSpace = regexp.MustCompile(`\s+`)

// RemoveSpaces eliminates all spaces in a string.
func RemoveSpaces(input string) string {
	return rxSpace.ReplaceAllString(input, "")
}

// SplitCondenseSpace splits a string and trims spaces on
// remaining elements, removing empty elements.
func SplitCondenseSpace(s, sep string) []string {
	split := strings.Split(s, sep)
	strs := []string{}
	for _, str := range split {
		str = strings.TrimSpace(str)
		if len(str) > 0 {
			strs = append(strs, str)
		}
	}
	return strs
}

// CondenseString trims whitespace at the ends of the string
// as well as in between.
func CondenseString(content string, join_lines bool) string {
	if join_lines {
		content = regexp.MustCompile(`\n`).ReplaceAllString(content, " ")
	}
	// Beginning
	content = regexp.MustCompile(`^\s+`).ReplaceAllString(content, "")
	// End
	content = regexp.MustCompile(`\s+$`).ReplaceAllString(content, "")
	// Middle
	content = regexp.MustCompile(`\n[\s\t\r]*\n`).ReplaceAllString(content, "\n")
	// Indentation
	content = regexp.MustCompile(`\n[\s\t\r]*`).ReplaceAllString(content, "\n")
	// Collapse
	content = regexp.MustCompile(`\s+`).ReplaceAllString(content, " ")
	return strings.TrimSpace(content)
}

// CondenseStringSimple removes extra spaces.
func CondenseStringSimple(s string) string {
	return strings.Join(strings.Fields(s), " ")
}

func StripControl(s string) string { return rxControl.ReplaceAllString(s, "") }

// TrimSpaceWithDefault trims spaces and replaces default value if
// result is empty string.
func TrimSpaceWithDefault(str string, defaultValue string) string {
	str = strings.TrimSpace(str)
	if len(str) == 0 {
		str = defaultValue
	}
	return str
}

// TrimSentenceLength trims a string by a max length at word boundaries.
func TrimSentenceLength(sentenceInput string, maxLength int) string {
	if len(sentenceInput) <= maxLength {
		return sentenceInput
	}
	sentenceLen := string(sentenceInput[0:maxLength]) // first350 := string(s[0:350])
	rx_end := regexp.MustCompile(`[[:punct:]][^[[:punct:]]]*$`)
	sentencePunct := rx_end.ReplaceAllString(sentenceLen, "")
	if len(sentencePunct) >= 2 {
		return sentencePunct
	}
	return sentenceLen
}

func JoinTrimSpace(strs []string) string {
	return rxSpaces.ReplaceAllString(strings.Join(strs, " "), " ")
}

// JoinInterface joins an interface and returns a string. It takes
// a join separator, boolean to replace the join separator in the
// string parts and a separator alternate. `stripEmbeddedSep` strips
// separator string found within parts. `stripRepeatedSep` strips
// repeating separators. This flexibility is designed to support
// joining data for both CSVs and paths.
func JoinInterface(arr []interface{}, sep string, stripRepeatedSep bool, stripEmbeddedSep bool, altSep string) string {
	parts := []string{}
	rx := regexp.MustCompile(sep)
	for _, el := range arr {
		part := fmt.Sprintf("%v", el)
		if stripEmbeddedSep {
			part = rx.ReplaceAllString(part, altSep)
		}
		parts = append(parts, part)
	}
	joined := strings.Join(parts, sep)
	if stripRepeatedSep {
		joined = regexp.MustCompile(fmt.Sprintf("%s+", sep)).
			ReplaceAllString(joined, sep)
	}
	return joined
}

func JoinLiterary(slice []string, sep, joinWord string) string {
	switch len(slice) {
	case 0:
		return ""
	case 1:
		return slice[0]
	case 2:
		return slice[0] + " " + joinWord + " " + slice[1]
	default:
		last, rest := slice[len(slice)-1], slice[:len(slice)-1]
		rest = append(rest, joinWord+" "+last)
		return strings.Join(rest, sep+" ")
	}
}

func JoinLiteraryQuote(slice []string, leftQuote, rightQuote, sep, joinWord string) string {
	newSlice := SliceCondenseAndQuoteSpace(slice, leftQuote, rightQuote)
	switch len(newSlice) {
	case 0:
		return ""
	case 1:
		return newSlice[0]
	case 2:
		return newSlice[0] + " " + joinWord + " " + newSlice[1]
	default:
		last, rest := newSlice[len(newSlice)-1], newSlice[:len(newSlice)-1]
		rest = append(rest, joinWord+" "+last)
		return strings.Join(rest, sep+" ")
	}
}

func JoinStringsTrimSpaceToLowerSort(strs []string, sep string) string {
	wip := []string{}
	for _, s := range strs {
		s = strings.ToLower(strings.TrimSpace(s))
		if len(s) > 0 {
			wip = append(wip, s)
		}
	}
	sort.Strings(wip)
	return strings.Join(wip, sep)
}

func FormatString(s string, options []string) string {
	for _, opt := range options {
		switch strings.TrimSpace(opt) {
		case StringToLower:
			s = strings.ToLower(s)
		case SpaceToHyphen:
			s = regexp.MustCompile(`[\s-]+`).ReplaceAllString(s, "-")
		case SpaceToUnderscore:
			s = regexp.MustCompile(`[\s_]+`).ReplaceAllString(s, "_")
		}
	}
	return s
}

// CommonInitialisms is the listed by Go Lint.
const CommonInitialisms = "ACL,API,ASCII,CPU,CSS,DNS,EOF,GUID,HTML,HTTP,HTTPS,ID,IP,JSON,LHS,QPS,RAM,RHS,RPC,SLA,SMTP,SQL,SSH,TCP,TLS,TTL,UDP,UI,UID,UUID,URI,URL,UTF8,VM,XML,XMPP,XSRF,XSS"

// CommonInitialismsMap returns map[string]bool of upper case initialisms.
func CommonInitialismsMap() map[string]bool {
	ciMap := map[string]bool{}
	commonInitialisms := strings.Split(CommonInitialisms, ",")
	for _, ci := range commonInitialisms {
		ciMap[ci] = true
	}
	return ciMap
}

// StringToConstant is used to generate constant names for code generation.
// It uses the commonInitialisms in Go Lint.
func StringToConstant(s string) string {
	newParts := []string{}
	parts := strings.Split(s, "_")
	ciMap := CommonInitialismsMap()
	for _, p := range parts {
		pUp := strings.ToUpper(p)
		if _, ok := ciMap[pUp]; ok {
			newParts = append(newParts, pUp)
		} else {
			newParts = append(newParts, ToUpperFirst(p, true))
		}
	}
	return strings.Join(newParts, "")
}

func ToOpposite(s string) string {
	return xstrings.Translate(s, lowerUpper, upperLower)
}

var (
	rxRN = regexp.MustCompile(`\r\n`)
	rxR  = regexp.MustCompile(`\r`)
)

func NewlineToLinux(input string) string {
	return rxR.ReplaceAllString(rxRN.ReplaceAllString(input, "\n"), "\n")
}
<|endoftext|>


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

package blockchain

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

const (
	// Version api client
	Version = "1.0"

	// UserAgent is the header string used to identify this package.
	UserAgent = "blockchain-api-v1-client-go/" + Version + " (go; github; +https://git.io/v5dN0)"

	// BasePath the root address in the network
	BasePath = "https://blockchain.info"

	// BasePathTor the root address in the tor network
	BasePathTor = "https://blockchainbdgpzk.onion"
)

// Errors it is a set of errors returned when working with the package
var (
	ErrAIW = errors.New("address is wrong")
	ErrBEW = errors.New("block height is wrong")
	ErrBHW = errors.New("block hash is wrong")
	ErrCGD = errors.New("cannot get data on url")
	ErrCRR = errors.New("could not read answer response")
	ErrIRS = errors.New("incorrect response status")
	ErrNAP = errors.New("no address(es) provided")
	ErrRPE = errors.New("response parsing error")
	ErrTHW = errors.New("transaction hash is wrong")
)

// MaxAddressesCount the maximum number of addresses that can be checked at a time using
// the address checking function or the balance.
var MaxAddressesCount = 100

// Options map contains the default settings for requests to the api
var Options = map[string]string{"format": "json"}

func init() {
	MaxAddressesCount = len(addressesForTestings)
}

// Client specifies the mechanism by which individual API requests are made.
type Client struct {
	client *http.Client

	APIKey    string // API access key
	BasePath  string // API endpoint base URL
	UserAgent string // optional additional User-Agent fragment
}

func (c *Client) userAgent() string {
	if c.UserAgent == "" {
		return UserAgent
	}

	return UserAgent + " " + c.UserAgent
}

func (c *Client) setErrorOne(errorMain error) error {
	return c.setError(errorMain, nil, nil, nil)
}

func (c *Client) setErrorTwo(errorMain error, errorExec error) error {
	return c.setError(errorMain, errorExec, nil, nil)
}

func (c *Client) setErrorThree(errorMain error, errorExec error, response *http.Response) error {
	return c.setError(errorMain, errorExec, response, nil)
}

func (c *Client) setError(errorMain error, errorExec error, response *http.Response, address *string) error {
	if errorMain == nil {
		return nil
	}

	return Error{
		ErrMain:  errorMain,
		ErrExec:  errorExec,
		Response: response,
		Address:  address,
	}
}

// Do to send an client request, which is then converted to the passed type.
func (c *Client) Do(path string, i interface{}, options map[string]string) error {
	options = ApproveOptions(options)
	options["format"] = "json"
	options["api_code"] = c.APIKey
	values := url.Values{}
	for k, v := range options {
		values.Set(k, v)
	}

	req, e := http.NewRequest("GET", c.BasePath+path+"?"+(values.Encode()), nil)
	if e != nil {
		return c.setErrorTwo(ErrCGD, e)
	}
	req.Header.Set("User-Agent", c.userAgent())

	resp, e := c.client.Do(req)
	if e != nil {
		return c.setErrorThree(ErrCGD, e, resp)
	}
	defer resp.Body.Close()

	bytes, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return c.setErrorThree(ErrCRR, e, resp)
	}

	if resp.Status[0] != '2' {
		return c.setErrorThree(ErrIRS, errors.New(string(bytes)), resp)
	}

	if e = json.Unmarshal(bytes, &i); e != nil {
		return c.setErrorThree(ErrRPE, e, resp)
	}

	return nil
}

// Error data structure describing the error
type Error struct {
	// Address wrong address
	Address *string
	// ErrMain error information from the standard package error set,
	ErrMain error
	// ErrExec information about the error that occurred during
	// the operation of the standard library or external packages
	ErrExec error
	// Response http response
	Response *http.Response
}

// Error compatibility with error interface
func (e Error) Error() string {
	return e.ErrMain.Error()
}

// ApproveOptions automatic check and substitution of options
func ApproveOptions(options map[string]string) map[string]string {
	if options == nil {
		return Options
	}
	return options
}

// New specifies the mechanism by create newClient client the network internet
func New() *Client {
	return &Client{client: &http.Client{}, BasePath: BasePath}
}

// NewTor specifies the mechanism by create newClient client the network internet
func NewTor() *Client {
	return &Client{client: &http.Client{}, BasePath: BasePathTor}
}

// SetHTTP http client setter
func (c *Client) SetHTTP(cli *http.Client) {
	c.client = cli
}
<commit_msg>chore: Change user agent<commit_after>// Copyright 2017-2018 Vasiliy Vasilyuk. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package blockchain

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

const (
	// Version api client
	Version = "1.0"

	// UserAgent is the header string used to identify this package.
	UserAgent = "blockchain-api-go-client/" + Version + " (go; github; +https://git.io/v5dN0)"

	// BasePath the root address in the network
	BasePath = "https://blockchain.info"

	// BasePathTor the root address in the tor network
	BasePathTor = "https://blockchainbdgpzk.onion"
)

// Errors it is a set of errors returned when working with the package
var (
	ErrAIW = errors.New("address is wrong")
	ErrBEW = errors.New("block height is wrong")
	ErrBHW = errors.New("block hash is wrong")
	ErrCGD = errors.New("cannot get data on url")
	ErrCRR = errors.New("could not read answer response")
	ErrIRS = errors.New("incorrect response status")
	ErrNAP = errors.New("no address(es) provided")
	ErrRPE = errors.New("response parsing error")
	ErrTHW = errors.New("transaction hash is wrong")
)

// MaxAddressesCount the maximum number of addresses that can be checked at a time using
// the address checking function or the balance.
var MaxAddressesCount = 100

// Options map contains the default settings for requests to the api
var Options = map[string]string{"format": "json"}

func init() {
	MaxAddressesCount = len(addressesForTestings)
}

// Client specifies the mechanism by which individual API requests are made.
type Client struct {
	client *http.Client

	APIKey    string // API access key
	BasePath  string // API endpoint base URL
	UserAgent string // optional additional User-Agent fragment
}

func (c *Client) userAgent() string {
	if c.UserAgent == "" {
		return UserAgent
	}

	return UserAgent + " " + c.UserAgent
}

func (c *Client) setErrorOne(errorMain error) error {
	return c.setError(errorMain, nil, nil, nil)
}

func (c *Client) setErrorTwo(errorMain error, errorExec error) error {
	return c.setError(errorMain, errorExec, nil, nil)
}

func (c *Client) setErrorThree(errorMain error, errorExec error, response *http.Response) error {
	return c.setError(errorMain, errorExec, response, nil)
}

func (c *Client) setError(errorMain error, errorExec error, response *http.Response, address *string) error {
	if errorMain == nil {
		return nil
	}

	return Error{
		ErrMain:  errorMain,
		ErrExec:  errorExec,
		Response: response,
		Address:  address,
	}
}

// Do to send an client request, which is then converted to the passed type.
func (c *Client) Do(path string, i interface{}, options map[string]string) error {
	options = ApproveOptions(options)
	options["format"] = "json"
	options["api_code"] = c.APIKey
	values := url.Values{}
	for k, v := range options {
		values.Set(k, v)
	}

	req, e := http.NewRequest("GET", c.BasePath+path+"?"+(values.Encode()), nil)
	if e != nil {
		return c.setErrorTwo(ErrCGD, e)
	}
	req.Header.Set("User-Agent", c.userAgent())

	resp, e := c.client.Do(req)
	if e != nil {
		return c.setErrorThree(ErrCGD, e, resp)
	}
	defer resp.Body.Close()

	bytes, e := ioutil.ReadAll(resp.Body)
	if e != nil {
		return c.setErrorThree(ErrCRR, e, resp)
	}

	if resp.Status[0] != '2' {
		return c.setErrorThree(ErrIRS, errors.New(string(bytes)), resp)
	}

	if e = json.Unmarshal(bytes, &i); e != nil {
		return c.setErrorThree(ErrRPE, e, resp)
	}

	return nil
}

// Error data structure describing the error
type Error struct {
	// Address wrong address
	Address *string
	// ErrMain error information from the standard package error set,
	ErrMain error
	// ErrExec information about the error that occurred during
	// the operation of the standard library or external packages
	ErrExec error
	// Response http response
	Response *http.Response
}

// Error compatibility with error interface
func (e Error) Error() string {
	return e.ErrMain.Error()
}

// ApproveOptions automatic check and substitution of options
func ApproveOptions(options map[string]string) map[string]string {
	if options == nil {
		return Options
	}
	return options
}

// New specifies the mechanism by create newClient client the network internet
func New() *Client {
	return &Client{client: &http.Client{}, BasePath: BasePath}
}

// NewTor specifies the mechanism by create newClient client the network internet
func NewTor() *Client {
	return &Client{client: &http.Client{}, BasePath: BasePathTor}
}

// SetHTTP http client setter
func (c *Client) SetHTTP(cli *http.Client) {
	c.client = cli
}
<|endoftext|>


<commit_before>package consultant

import (
	"errors"
	"fmt"
	"github.com/hashicorp/consul/api"
	"github.com/renstrom/shortuuid"
	"math/rand"
	"net/url"
	"os"
	"sort"
	"strings"
)

type Client struct {
	*api.Client

	config api.Config

	myAddr string
	myHost string

	myNode string

	logSlug      string
	logSlugSlice []interface{}
}

type TagsOption int

const (
	// TagsAll means all tags passed must be present, though other tags are okay
	TagsAll TagsOption = iota

	// TagsAny means any service having at least one of the tags are returned
	TagsAny

	// TagsExactly means that the service tags must match those passed exactly
	TagsExactly

	// TagsExclude means skip services that match any tags passed
	TagsExclude
)

// NewClient constructs a new consultant client.
func NewClient(conf *api.Config) (*Client, error) {
	var err error

	if nil == conf {
		return nil, errors.New("config cannot be nil")
	}

	client := &Client{
		config:       *conf,
		logSlug:      "[consultant-client]",
		logSlugSlice: []interface{}{"[consultant-client]"},
	}

	client.Client, err = api.NewClient(conf)
	if err != nil {
		return nil, fmt.Errorf("unable to create Consul API Client: %s", err)
	}

	if client.myHost, err = os.Hostname(); err != nil {
		client.logPrintf("Unable to determine hostname: %s", err)
	}

	if client.myAddr, err = GetMyAddress(); err != nil {
		client.logPrintf("Unable to determine ip address: %s", err)
	}

	if client.myNode, err = client.Agent().NodeName(); err != nil {
		return nil, fmt.Errorf("unable to determine local Consul node name: %s", err)
	}

	return client, nil
}

// NewDefaultClient creates a new client with default configuration values
func NewDefaultClient() (*Client, error) {
	return NewClient(api.DefaultConfig())
}

// Config returns the API Client configuration struct as it was at time of construction
func (c *Client) Config() api.Config {
	return c.config
}

// MyAddr returns either the self-determine or set IP address of our host
func (c *Client) MyAddr() string {
	return c.myAddr
}

// SetMyAddr allows you to manually specify the IP address of our host
func (c *Client) SetMyAddr(myAddr string) {
	c.myAddr = myAddr
}

// MyHost returns either the self-determined or set name of our host
func (c *Client) MyHost() string {
	return c.myHost
}

// SetMyHost allows you to to manually specify the name of our host
func (c *Client) SetMyHost(myHost string) {
	c.myHost = myHost
}

// MyNode returns the name of the Consul Node this client is connected to
func (c *Client) MyNode() string {
	return c.myNode
}

// EnsureKey will fetch a key/value and ensure the key is present.  The value may still be empty.
func (c *Client) EnsureKey(key string, options *api.QueryOptions) (*api.KVPair, *api.QueryMeta, error) {
	kvp, qm, err := c.KV().Get(key, options)
	if err != nil {
		return nil, nil, err
	}
	if kvp != nil {
		return kvp, qm, nil
	}
	return nil, nil, errors.New("key not found")
}

// PickService will attempt to locate any registered service with a name + tag combination and return one at random from
// the resulting list
func (c *Client) PickService(service, tag string, passingOnly bool, options *api.QueryOptions) (*api.ServiceEntry, *api.QueryMeta, error) {
	svcs, qm, err := c.Health().Service(service, tag, passingOnly, options)
	if nil != err {
		return nil, nil, err
	}

	svcLen := len(svcs)
	if 0 < svcLen {
		return svcs[rand.Intn(svcLen)], qm, nil
	}

	return nil, qm, nil
}

// ServiceByTags - this wraps the consul Health().Service() call, adding the tagsOption parameter and accepting a
// slice of tags.  tagsOption should be one of the following:
//
//     TagsAll - this will return only services that have all the specified tags present.
//     TagsExactly - like TagsAll, but will return only services that match exactly the tags specified, no more.
//     TagsAny - this will return services that match any of the tags specified.
//     TagsExclude - this will return services don't have any of the tags specified.
func (c *Client) ServiceByTags(service string, tags []string, tagsOption TagsOption, passingOnly bool, options *api.QueryOptions) ([]*api.ServiceEntry, *api.QueryMeta, error) {
	var (
		retv, tmpv []*api.ServiceEntry
		qm         *api.QueryMeta
		err        error
	)
	var tag string

	// Grab the initial payload
	switch tagsOption {
	case TagsAll, TagsExactly:
		if len(tags) > 0 {
			tag = tags[0]
		}
		fallthrough
	case TagsAny, TagsExclude:
		tmpv, qm, err = c.Health().Service(service, tag, passingOnly, options)
	default:
		return nil, nil, fmt.Errorf("invalid value for tagsOption: %d", tagsOption)
	}

	if err != nil {
		return retv, qm, err
	}
	tagsMap := make(map[string]bool)
	for _, t := range tags {
		tagsMap[t] = true
	}

	// Filter payload.
OUTER:
	for _, se := range tmpv {
		switch tagsOption {
		case TagsExactly:
			if !strSlicesEqual(tags, se.Service.Tags) {
				continue OUTER
			}
		case TagsAll:
			if len(se.Service.Tags) < len(tags) {
				continue OUTER
			}
			// Consul allows duplicate tags, so this workaround.  Boo.
			mc := 0
			dedupeMap := make(map[string]bool)
			for _, t := range se.Service.Tags {
				if tagsMap[t] && !dedupeMap[t] {
					mc++
					dedupeMap[t] = true
				}
			}
			if mc != len(tagsMap) {
				continue OUTER
			}
		case TagsAny, TagsExclude:
			found := false
			for _, t := range se.Service.Tags {
				if tagsMap[t] {
					found = true
					break
				}
			}
			if tagsOption == TagsAny {
				if !found && len(tags) > 0 {
					continue OUTER
				}
			} else if found { // TagsExclude
				continue OUTER
			}

		}
		retv = append(retv, se)
	}

	return retv, qm, err
}

// determines if a and b contain the same elements (order doesn't matter)
func strSlicesEqual(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	ac := make([]string, len(a))
	bc := make([]string, len(b))
	copy(ac, a)
	copy(bc, b)
	sort.Strings(ac)
	sort.Strings(bc)
	for i, v := range ac {
		if bc[i] != v {
			return false
		}
	}
	return true
}

// BuildServiceURL will attempt to locate a healthy instance of the specified service name + tag combination, then
// attempt to construct a *net.URL from the resulting service information
func (c *Client) BuildServiceURL(protocol, serviceName, tag string, passingOnly bool, options *api.QueryOptions) (*url.URL, error) {
	svc, _, err := c.PickService(serviceName, tag, passingOnly, options)
	if nil != err {
		return nil, err
	}
	if nil == svc {
		return nil, fmt.Errorf("no services registered as \"%s\" with tag \"%s\" found", serviceName, tag)
	}

	return url.Parse(fmt.Sprintf("%s://%s:%d", protocol, svc.Service.Address, svc.Service.Port))
}

// SimpleServiceRegistration describes a service that we want to register
type SimpleServiceRegistration struct {
	Name string // [required] name to register service under
	Port int    // [required] external port to advertise for service consumers

	ID                string   // [optional] specific id for service, will be generated if not set
	RandomID          bool     // [optional] if ID is not set, use a random uuid if true, or hostname if false
	Address           string   // [optional] determined automatically by Register() if not set
	Tags              []string // [optional] desired tags: Register() adds serviceId
	CheckTCP          bool     // [optional] if true register a TCP check
	CheckPath         string   // [optional] register an http check with this path if set
	CheckScheme       string   // [optional] override the http check scheme (default: http)
	CheckPort         int      // [optional] if set, this is the port that the health check lives at
	Interval          string   // [optional] check interval
	EnableTagOverride bool     // [optional] whether we should allow tag overriding (new in 0.6+)
}

// SimpleServiceRegister is a helper method to ease consul service registration
func (c *Client) SimpleServiceRegister(reg *SimpleServiceRegistration) (string, error) {
	var err error                        // generic error holder
	var serviceID string                 // local service identifier
	var address string                   // service host address
	var interval string                  // check interval
	var checkHTTP *api.AgentServiceCheck // http type check
	var serviceName string               // service registration name

	// Perform some basic service name cleanup and validation
	serviceName = strings.TrimSpace(reg.Name)
	if serviceName == "" {
		return "", errors.New("\"Name\" cannot be blank")
	}
	if strings.Contains(serviceName, " ") {
		return "", fmt.Errorf("name \"%s\" is invalid, service names cannot contain spaces", serviceName)
	}

	// Come on, guys...valid ports plz...
	if reg.Port <= 0 {
		return "", fmt.Errorf("%d is not a valid port", reg.Port)
	}

	if address = reg.Address; address == "" {
		address = c.myAddr
	}

	if serviceID = reg.ID; serviceID == "" {
		// Form a unique service id
		var tail string
		if reg.RandomID {
			tail = shortuuid.New()
		} else {
			tail = strings.ToLower(c.myHost)
		}
		serviceID = fmt.Sprintf("%s-%s", serviceName, tail)
	}

	if interval = reg.Interval; interval == "" {
		// set a default interval
		interval = "30s"
	}

	// The serviceID is added in order to ensure detection in ServiceMonitor()
	tags := append(reg.Tags, serviceID)

	// Set up the service registration struct
	asr := &api.AgentServiceRegistration{
		ID:                serviceID,
		Name:              serviceName,
		Tags:              tags,
		Port:              reg.Port,
		Address:           address,
		Checks:            api.AgentServiceChecks{},
		EnableTagOverride: reg.EnableTagOverride,
	}

	// allow port override
	checkPort := reg.CheckPort
	if checkPort <= 0 {
		checkPort = reg.Port
	}

	// build http check if specified
	if reg.CheckPath != "" {

		// allow scheme override
		checkScheme := reg.CheckScheme
		if checkScheme == "" {
			checkScheme = "http"
		}

		// build check url
		checkURL := &url.URL{
			Scheme: checkScheme,
			Host:   fmt.Sprintf("%s:%d", address, checkPort),
			Path:   reg.CheckPath,
		}

		// build check
		checkHTTP = &api.AgentServiceCheck{
			HTTP:     checkURL.String(),
			Interval: interval,
		}

		// add http check
		asr.Checks = append(asr.Checks, checkHTTP)
	}

	// build tcp check if specified
	if reg.CheckTCP {
		// create tcp check definition
		asr.Checks = append(asr.Checks, &api.AgentServiceCheck{
			TCP:      fmt.Sprintf("%s:%d", address, checkPort),
			Interval: interval,
		})
	}

	// register and check error
	if err = c.Agent().ServiceRegister(asr); err != nil {
		return "", err
	}

	// return registered service
	return serviceID, nil
}

func (c *Client) logPrintf(format string, v ...interface{}) {
	log.Printf(fmt.Sprintf("%s %s", c.logSlug, format), v...)
}

func (c *Client) logPrint(v ...interface{}) {
	log.Print(append(c.logSlugSlice, v...)...)
}
<commit_msg>minor cleanup<commit_after>package consultant

import (
	"errors"
	"fmt"
	"github.com/hashicorp/consul/api"
	"github.com/renstrom/shortuuid"
	"math/rand"
	"net/url"
	"os"
	"sort"
	"strings"
)

type Client struct {
	*api.Client

	config api.Config

	myAddr string
	myHost string

	myNode string

	logSlug      string
	logSlugSlice []interface{}
}

type TagsOption int

const (
	// TagsAll means all tags passed must be present, though other tags are okay
	TagsAll TagsOption = iota

	// TagsAny means any service having at least one of the tags are returned
	TagsAny

	// TagsExactly means that the service tags must match those passed exactly
	TagsExactly

	// TagsExclude means skip services that match any tags passed
	TagsExclude
)

// NewClient constructs a new consultant client.
func NewClient(conf *api.Config) (*Client, error) {
	var err error

	if nil == conf {
		return nil, errors.New("config cannot be nil")
	}

	client := &Client{
		config:       *conf,
		logSlug:      "[consultant-client]",
		logSlugSlice: []interface{}{"[consultant-client]"},
	}

	client.Client, err = api.NewClient(conf)
	if err != nil {
		return nil, fmt.Errorf("unable to create Consul API Client: %s", err)
	}

	if client.myHost, err = os.Hostname(); err != nil {
		client.logPrintf("Unable to determine hostname: %s", err)
	}

	if client.myAddr, err = GetMyAddress(); err != nil {
		client.logPrintf("Unable to determine ip address: %s", err)
	}

	if client.myNode, err = client.Agent().NodeName(); err != nil {
		return nil, fmt.Errorf("unable to determine local Consul node name: %s", err)
	}

	return client, nil
}

// NewDefaultClient creates a new client with default configuration values
func NewDefaultClient() (*Client, error) {
	return NewClient(api.DefaultConfig())
}

// Config returns the API Client configuration struct as it was at time of construction
func (c *Client) Config() api.Config {
	return c.config
}

// MyAddr returns either the self-determine or set IP address of our host
func (c *Client) MyAddr() string {
	return c.myAddr
}

// SetMyAddr allows you to manually specify the IP address of our host
func (c *Client) SetMyAddr(myAddr string) {
	c.myAddr = myAddr
}

// MyHost returns either the self-determined or set name of our host
func (c *Client) MyHost() string {
	return c.myHost
}

// SetMyHost allows you to to manually specify the name of our host
func (c *Client) SetMyHost(myHost string) {
	c.myHost = myHost
}

// MyNode returns the name of the Consul Node this client is connected to
func (c *Client) MyNode() string {
	return c.myNode
}

// EnsureKey will fetch a key/value and ensure the key is present.  The value may still be empty.
func (c *Client) EnsureKey(key string, options *api.QueryOptions) (*api.KVPair, *api.QueryMeta, error) {
	kvp, qm, err := c.KV().Get(key, options)
	if err != nil {
		return nil, nil, err
	}
	if kvp != nil {
		return kvp, qm, nil
	}
	return nil, nil, errors.New("key not found")
}

// PickService will attempt to locate any registered service with a name + tag combination and return one at random from
// the resulting list
func (c *Client) PickService(service, tag string, passingOnly bool, options *api.QueryOptions) (*api.ServiceEntry, *api.QueryMeta, error) {
	svcs, qm, err := c.Health().Service(service, tag, passingOnly, options)
	if nil != err {
		return nil, nil, err
	}

	svcLen := len(svcs)
	if 0 < svcLen {
		return svcs[rand.Intn(svcLen)], qm, nil
	}

	return nil, qm, nil
}

// ServiceByTags - this wraps the consul Health().Service() call, adding the tagsOption parameter and accepting a
// slice of tags.  tagsOption should be one of the following:
//
//     TagsAll - this will return only services that have all the specified tags present.
//     TagsExactly - like TagsAll, but will return only services that match exactly the tags specified, no more.
//     TagsAny - this will return services that match any of the tags specified.
//     TagsExclude - this will return services don't have any of the tags specified.
func (c *Client) ServiceByTags(service string, tags []string, tagsOption TagsOption, passingOnly bool, options *api.QueryOptions) ([]*api.ServiceEntry, *api.QueryMeta, error) {
	var (
		retv, tmpv []*api.ServiceEntry
		qm         *api.QueryMeta
		err        error
	)

	tag := ""

	// Grab the initial payload
	switch tagsOption {
	case TagsAll, TagsExactly:
		if len(tags) > 0 {
			tag = tags[0]
		}
		fallthrough
	case TagsAny, TagsExclude:
		tmpv, qm, err = c.Health().Service(service, tag, passingOnly, options)
	default:
		return nil, nil, fmt.Errorf("invalid value for tagsOption: %d", tagsOption)
	}

	if err != nil {
		return retv, qm, err
	}

	var tagsMap map[string]bool
	if tagsOption != TagsExactly {
		tagsMap = make(map[string]bool)
		for _, t := range tags {
			tagsMap[t] = true
		}
	}

	// Filter payload.
OUTER:
	for _, se := range tmpv {
		switch tagsOption {
		case TagsExactly:
			if !strSlicesEqual(tags, se.Service.Tags) {
				continue OUTER
			}
		case TagsAll:
			if len(se.Service.Tags) < len(tags) {
				continue OUTER
			}
			// Consul allows duplicate tags, so this workaround.  Boo.
			mc := 0
			dedupeMap := make(map[string]bool)
			for _, t := range se.Service.Tags {
				if tagsMap[t] && !dedupeMap[t] {
					mc++
					dedupeMap[t] = true
				}
			}
			if mc != len(tagsMap) {
				continue OUTER
			}
		case TagsAny, TagsExclude:
			found := false
			for _, t := range se.Service.Tags {
				if tagsMap[t] {
					found = true
					break
				}
			}
			if tagsOption == TagsAny {
				if !found && len(tags) > 0 {
					continue OUTER
				}
			} else if found { // TagsExclude
				continue OUTER
			}

		}
		retv = append(retv, se)
	}

	return retv, qm, err
}

// determines if a and b contain the same elements (order doesn't matter)
func strSlicesEqual(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	ac := make([]string, len(a))
	bc := make([]string, len(b))
	copy(ac, a)
	copy(bc, b)
	sort.Strings(ac)
	sort.Strings(bc)
	for i, v := range ac {
		if bc[i] != v {
			return false
		}
	}
	return true
}

// BuildServiceURL will attempt to locate a healthy instance of the specified service name + tag combination, then
// attempt to construct a *net.URL from the resulting service information
func (c *Client) BuildServiceURL(protocol, serviceName, tag string, passingOnly bool, options *api.QueryOptions) (*url.URL, error) {
	svc, _, err := c.PickService(serviceName, tag, passingOnly, options)
	if nil != err {
		return nil, err
	}
	if nil == svc {
		return nil, fmt.Errorf("no services registered as \"%s\" with tag \"%s\" found", serviceName, tag)
	}

	return url.Parse(fmt.Sprintf("%s://%s:%d", protocol, svc.Service.Address, svc.Service.Port))
}

// SimpleServiceRegistration describes a service that we want to register
type SimpleServiceRegistration struct {
	Name string // [required] name to register service under
	Port int    // [required] external port to advertise for service consumers

	ID                string   // [optional] specific id for service, will be generated if not set
	RandomID          bool     // [optional] if ID is not set, use a random uuid if true, or hostname if false
	Address           string   // [optional] determined automatically by Register() if not set
	Tags              []string // [optional] desired tags: Register() adds serviceId
	CheckTCP          bool     // [optional] if true register a TCP check
	CheckPath         string   // [optional] register an http check with this path if set
	CheckScheme       string   // [optional] override the http check scheme (default: http)
	CheckPort         int      // [optional] if set, this is the port that the health check lives at
	Interval          string   // [optional] check interval
	EnableTagOverride bool     // [optional] whether we should allow tag overriding (new in 0.6+)
}

// SimpleServiceRegister is a helper method to ease consul service registration
func (c *Client) SimpleServiceRegister(reg *SimpleServiceRegistration) (string, error) {
	var err error                        // generic error holder
	var serviceID string                 // local service identifier
	var address string                   // service host address
	var interval string                  // check interval
	var checkHTTP *api.AgentServiceCheck // http type check
	var serviceName string               // service registration name

	// Perform some basic service name cleanup and validation
	serviceName = strings.TrimSpace(reg.Name)
	if serviceName == "" {
		return "", errors.New("\"Name\" cannot be blank")
	}
	if strings.Contains(serviceName, " ") {
		return "", fmt.Errorf("name \"%s\" is invalid, service names cannot contain spaces", serviceName)
	}

	// Come on, guys...valid ports plz...
	if reg.Port <= 0 {
		return "", fmt.Errorf("%d is not a valid port", reg.Port)
	}

	if address = reg.Address; address == "" {
		address = c.myAddr
	}

	if serviceID = reg.ID; serviceID == "" {
		// Form a unique service id
		var tail string
		if reg.RandomID {
			tail = shortuuid.New()
		} else {
			tail = strings.ToLower(c.myHost)
		}
		serviceID = fmt.Sprintf("%s-%s", serviceName, tail)
	}

	if interval = reg.Interval; interval == "" {
		// set a default interval
		interval = "30s"
	}

	// The serviceID is added in order to ensure detection in ServiceMonitor()
	tags := append(reg.Tags, serviceID)

	// Set up the service registration struct
	asr := &api.AgentServiceRegistration{
		ID:                serviceID,
		Name:              serviceName,
		Tags:              tags,
		Port:              reg.Port,
		Address:           address,
		Checks:            api.AgentServiceChecks{},
		EnableTagOverride: reg.EnableTagOverride,
	}

	// allow port override
	checkPort := reg.CheckPort
	if checkPort <= 0 {
		checkPort = reg.Port
	}

	// build http check if specified
	if reg.CheckPath != "" {

		// allow scheme override
		checkScheme := reg.CheckScheme
		if checkScheme == "" {
			checkScheme = "http"
		}

		// build check url
		checkURL := &url.URL{
			Scheme: checkScheme,
			Host:   fmt.Sprintf("%s:%d", address, checkPort),
			Path:   reg.CheckPath,
		}

		// build check
		checkHTTP = &api.AgentServiceCheck{
			HTTP:     checkURL.String(),
			Interval: interval,
		}

		// add http check
		asr.Checks = append(asr.Checks, checkHTTP)
	}

	// build tcp check if specified
	if reg.CheckTCP {
		// create tcp check definition
		asr.Checks = append(asr.Checks, &api.AgentServiceCheck{
			TCP:      fmt.Sprintf("%s:%d", address, checkPort),
			Interval: interval,
		})
	}

	// register and check error
	if err = c.Agent().ServiceRegister(asr); err != nil {
		return "", err
	}

	// return registered service
	return serviceID, nil
}

func (c *Client) logPrintf(format string, v ...interface{}) {
	log.Printf(fmt.Sprintf("%s %s", c.logSlug, format), v...)
}

func (c *Client) logPrint(v ...interface{}) {
	log.Print(append(c.logSlugSlice, v...)...)
}
<|endoftext|>


<commit_before>package honeybadger

type Payload interface {
	toJSON() []byte
}

type Backend interface {
	Notify(feature Feature, payload Payload) error
}

type Client struct {
	Config  *Config
	Backend Backend
}

func (c Client) Notify(err interface{}) string {
	notice := newNotice(c.Config, newError(err, 1))
	if notify_err := c.Backend.Notify(Notices, notice); notify_err != nil {
		panic(notify_err)
	}
	return notice.Token
}

func NewClient(config Config) Client {
	defaultConfig := Config{
		APIKey:   getEnv("HONEYBADGER_API_KEY"),
		Env:      getEnv("HONEYBADGER_ENV"),
		Hostname: getHostname(),
		Endpoint: "https://api.honeybadger.io",
	}.merge(config)
	backend := Server{URL: &defaultConfig.Endpoint, APIKey: &defaultConfig.APIKey}
	return Client{
		Config:  &defaultConfig,
		Backend: backend,
	}
}
<commit_msg>Notify in goroutine.<commit_after>package honeybadger

type Payload interface {
	toJSON() []byte
}

type Backend interface {
	Notify(feature Feature, payload Payload) error
}

type Client struct {
	Config  *Config
	Backend Backend
}

func (c Client) Notify(err interface{}) string {
	notice := newNotice(c.Config, newError(err, 1))
	go c.notify(notice)
	return notice.Token
}

func (c Client) notify(notice *Notice) {
	if err := c.Backend.Notify(Notices, notice); err != nil {
		panic(err)
	}
}

func NewClient(config Config) Client {
	defaultConfig := Config{
		APIKey:   getEnv("HONEYBADGER_API_KEY"),
		Env:      getEnv("HONEYBADGER_ENV"),
		Hostname: getHostname(),
		Endpoint: "https://api.honeybadger.io",
	}.merge(config)
	backend := Server{URL: &defaultConfig.Endpoint, APIKey: &defaultConfig.APIKey}
	return Client{
		Config:  &defaultConfig,
		Backend: backend,
	}
}
<|endoftext|>


<commit_before>package pilosa

import (
	"context"
	"io"
)

// Bit represents the intersection of a row and a column. It can be specifed by
// integer ids or string keys.
type Bit struct {
	RowID     uint64
	ColumnID  uint64
	RowKey    string
	ColumnKey string
	Timestamp int64
}

// FieldValue represents the value for a column within a
// range-encoded field.
type FieldValue struct {
	ColumnID uint64
	Value    int64
}

// InternalClient should be implemented by any struct that enables any transport between nodes
// TODO: Refactor
// Note from Travis: Typically an interface containing more than two or three methods is an indication that
// something hasn't been architected correctly.
// While I understand that putting the entire Client behind an interface might require this many methods,
// I don't want to let it go unquestioned.
type InternalClient interface {
	MaxShardByIndex(ctx context.Context) (map[string]uint64, error)
	Schema(ctx context.Context) ([]*IndexInfo, error)
	CreateIndex(ctx context.Context, index string, opt IndexOptions) error
	FragmentNodes(ctx context.Context, index string, shard uint64) ([]*Node, error)
	Query(ctx context.Context, index string, queryRequest *QueryRequest) (*QueryResponse, error)
	QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error)
	Import(ctx context.Context, index, field string, shard uint64, bits []Bit) error
	ImportK(ctx context.Context, index, field string, bits []Bit) error
	EnsureIndex(ctx context.Context, name string, options IndexOptions) error
	EnsureField(ctx context.Context, indexName string, fieldName string) error
	ImportValue(ctx context.Context, index, field string, shard uint64, vals []FieldValue) error
	ExportCSV(ctx context.Context, index, field string, shard uint64, w io.Writer) error
	CreateField(ctx context.Context, index, field string) error
	FragmentBlocks(ctx context.Context, uri *URI, index, field string, shard uint64) ([]FragmentBlock, error)
	BlockData(ctx context.Context, uri *URI, index, field string, shard uint64, block int) ([]uint64, []uint64, error)
	ColumnAttrDiff(ctx context.Context, uri *URI, index string, blks []AttrBlock) (map[uint64]map[string]interface{}, error)
	RowAttrDiff(ctx context.Context, uri *URI, index, field string, blks []AttrBlock) (map[uint64]map[string]interface{}, error)
	SendMessage(ctx context.Context, uri *URI, msg []byte) error
	RetrieveShardFromURI(ctx context.Context, index, field string, shard uint64, uri URI) (io.ReadCloser, error)
}

//===============

type InternalQueryClient interface {
	QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error)
}

type NopInternalQueryClient struct{}

func (n *NopInternalQueryClient) QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error) {
	return nil, nil
}

func newNopInternalQueryClient() *NopInternalQueryClient {
	return &NopInternalQueryClient{}
}

var _ InternalQueryClient = newNopInternalQueryClient()

//===============

type NopInternalClient struct{}

func newNopInternalClient() NopInternalClient {
	return NopInternalClient{}
}

var _ InternalClient = newNopInternalClient()

func (n NopInternalClient) MaxShardByIndex(context.Context) (map[string]uint64, error) {
	return nil, nil
}
func (n NopInternalClient) Schema(ctx context.Context) ([]*IndexInfo, error) { return nil, nil }
func (n NopInternalClient) CreateIndex(ctx context.Context, index string, opt IndexOptions) error {
	return nil
}
func (n NopInternalClient) FragmentNodes(ctx context.Context, index string, shard uint64) ([]*Node, error) {
	return nil, nil
}
func (n NopInternalClient) Query(ctx context.Context, index string, queryRequest *QueryRequest) (*QueryResponse, error) {
	return nil, nil
}
func (n NopInternalClient) QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error) {
	return nil, nil
}
func (n NopInternalClient) Import(ctx context.Context, index, field string, shard uint64, bits []Bit) error {
	return nil
}
func (n NopInternalClient) ImportK(ctx context.Context, index, field string, bits []Bit) error {
	return nil
}
func (n NopInternalClient) EnsureIndex(ctx context.Context, name string, options IndexOptions) error {
	return nil
}
func (n NopInternalClient) EnsureField(ctx context.Context, indexName string, fieldName string) error {
	return nil
}
func (n NopInternalClient) ImportValue(ctx context.Context, index, field string, shard uint64, vals []FieldValue) error {
	return nil
}
func (n NopInternalClient) ExportCSV(ctx context.Context, index, field string, shard uint64, w io.Writer) error {
	return nil
}
func (n NopInternalClient) CreateField(ctx context.Context, index, field string) error { return nil }
func (n NopInternalClient) FragmentBlocks(ctx context.Context, uri *URI, index, field string, shard uint64) ([]FragmentBlock, error) {
	return nil, nil
}
func (n NopInternalClient) BlockData(ctx context.Context, uri *URI, index, field string, shard uint64, block int) ([]uint64, []uint64, error) {
	return nil, nil, nil
}
func (n NopInternalClient) ColumnAttrDiff(ctx context.Context, uri *URI, index string, blks []AttrBlock) (map[uint64]map[string]interface{}, error) {
	return nil, nil
}
func (n NopInternalClient) RowAttrDiff(ctx context.Context, uri *URI, index, field string, blks []AttrBlock) (map[uint64]map[string]interface{}, error) {
	return nil, nil
}
func (n NopInternalClient) SendMessage(ctx context.Context, uri *URI, msg []byte) error {
	return nil
}
func (n NopInternalClient) RetrieveShardFromURI(ctx context.Context, index, field string, shard uint64, uri URI) (io.ReadCloser, error) {
	return nil, nil
}
<commit_msg>Unexport NopInternalClient<commit_after>package pilosa

import (
	"context"
	"io"
)

// Bit represents the intersection of a row and a column. It can be specifed by
// integer ids or string keys.
type Bit struct {
	RowID     uint64
	ColumnID  uint64
	RowKey    string
	ColumnKey string
	Timestamp int64
}

// FieldValue represents the value for a column within a
// range-encoded field.
type FieldValue struct {
	ColumnID uint64
	Value    int64
}

// InternalClient should be implemented by any struct that enables any transport between nodes
// TODO: Refactor
// Note from Travis: Typically an interface containing more than two or three methods is an indication that
// something hasn't been architected correctly.
// While I understand that putting the entire Client behind an interface might require this many methods,
// I don't want to let it go unquestioned.
type InternalClient interface {
	MaxShardByIndex(ctx context.Context) (map[string]uint64, error)
	Schema(ctx context.Context) ([]*IndexInfo, error)
	CreateIndex(ctx context.Context, index string, opt IndexOptions) error
	FragmentNodes(ctx context.Context, index string, shard uint64) ([]*Node, error)
	Query(ctx context.Context, index string, queryRequest *QueryRequest) (*QueryResponse, error)
	QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error)
	Import(ctx context.Context, index, field string, shard uint64, bits []Bit) error
	ImportK(ctx context.Context, index, field string, bits []Bit) error
	EnsureIndex(ctx context.Context, name string, options IndexOptions) error
	EnsureField(ctx context.Context, indexName string, fieldName string) error
	ImportValue(ctx context.Context, index, field string, shard uint64, vals []FieldValue) error
	ExportCSV(ctx context.Context, index, field string, shard uint64, w io.Writer) error
	CreateField(ctx context.Context, index, field string) error
	FragmentBlocks(ctx context.Context, uri *URI, index, field string, shard uint64) ([]FragmentBlock, error)
	BlockData(ctx context.Context, uri *URI, index, field string, shard uint64, block int) ([]uint64, []uint64, error)
	ColumnAttrDiff(ctx context.Context, uri *URI, index string, blks []AttrBlock) (map[uint64]map[string]interface{}, error)
	RowAttrDiff(ctx context.Context, uri *URI, index, field string, blks []AttrBlock) (map[uint64]map[string]interface{}, error)
	SendMessage(ctx context.Context, uri *URI, msg []byte) error
	RetrieveShardFromURI(ctx context.Context, index, field string, shard uint64, uri URI) (io.ReadCloser, error)
}

//===============

type InternalQueryClient interface {
	QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error)
}

type NopInternalQueryClient struct{}

func (n *NopInternalQueryClient) QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error) {
	return nil, nil
}

func newNopInternalQueryClient() *NopInternalQueryClient {
	return &NopInternalQueryClient{}
}

var _ InternalQueryClient = newNopInternalQueryClient()

//===============

type nopInternalClient struct{}

func newNopInternalClient() nopInternalClient {
	return nopInternalClient{}
}

var _ InternalClient = newNopInternalClient()

func (n nopInternalClient) MaxShardByIndex(context.Context) (map[string]uint64, error) {
	return nil, nil
}
func (n nopInternalClient) Schema(ctx context.Context) ([]*IndexInfo, error) { return nil, nil }
func (n nopInternalClient) CreateIndex(ctx context.Context, index string, opt IndexOptions) error {
	return nil
}
func (n nopInternalClient) FragmentNodes(ctx context.Context, index string, shard uint64) ([]*Node, error) {
	return nil, nil
}
func (n nopInternalClient) Query(ctx context.Context, index string, queryRequest *QueryRequest) (*QueryResponse, error) {
	return nil, nil
}
func (n nopInternalClient) QueryNode(ctx context.Context, uri *URI, index string, queryRequest *QueryRequest) (*QueryResponse, error) {
	return nil, nil
}
func (n nopInternalClient) Import(ctx context.Context, index, field string, shard uint64, bits []Bit) error {
	return nil
}
func (n nopInternalClient) ImportK(ctx context.Context, index, field string, bits []Bit) error {
	return nil
}
func (n nopInternalClient) EnsureIndex(ctx context.Context, name string, options IndexOptions) error {
	return nil
}
func (n nopInternalClient) EnsureField(ctx context.Context, indexName string, fieldName string) error {
	return nil
}
func (n nopInternalClient) ImportValue(ctx context.Context, index, field string, shard uint64, vals []FieldValue) error {
	return nil
}
func (n nopInternalClient) ExportCSV(ctx context.Context, index, field string, shard uint64, w io.Writer) error {
	return nil
}
func (n nopInternalClient) CreateField(ctx context.Context, index, field string) error { return nil }
func (n nopInternalClient) FragmentBlocks(ctx context.Context, uri *URI, index, field string, shard uint64) ([]FragmentBlock, error) {
	return nil, nil
}
func (n nopInternalClient) BlockData(ctx context.Context, uri *URI, index, field string, shard uint64, block int) ([]uint64, []uint64, error) {
	return nil, nil, nil
}
func (n nopInternalClient) ColumnAttrDiff(ctx context.Context, uri *URI, index string, blks []AttrBlock) (map[uint64]map[string]interface{}, error) {
	return nil, nil
}
func (n nopInternalClient) RowAttrDiff(ctx context.Context, uri *URI, index, field string, blks []AttrBlock) (map[uint64]map[string]interface{}, error) {
	return nil, nil
}
func (n nopInternalClient) SendMessage(ctx context.Context, uri *URI, msg []byte) error {
	return nil
}
func (n nopInternalClient) RetrieveShardFromURI(ctx context.Context, index, field string, shard uint64, uri URI) (io.ReadCloser, error) {
	return nil, nil
}
<|endoftext|>


<commit_before>package urlcheck

import "errors"
import "io/ioutil"
import "net/http"
import "regexp"
import "strconv"
import "strings"

type Method int

const (
	GET Method = iota
	POST
)

type Test struct {
	Url           string
	Content       string
	Code          int
	Method        Method
	Data          string
	Headers       map[string]string
	SkipSSLVerify bool
}

func (t Test) Test() (err error) {
	resp, err := t.DoRequest()
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if err == nil {
		err = t.CheckCode(resp)
	}
	if err == nil {
		err = t.CheckContent(resp)
	}
	return
}

func (t Test) DoRequest() (resp *http.Response, err error) {
	req, err := http.NewRequest(t.MethodName(), t.Url, strings.NewReader(t.Data))
	if err != nil {
		return nil, err
	}

	for k, v := range t.Headers {
		req.Header.Add(k, v)
	}

	resp, err = client.Do(req)
	return resp, err
}

func (t Test) CheckCode(resp *http.Response) error {
	code := t.Code
	if code == 0 {
		code = 200
	}
	if resp.StatusCode != code {
		return t.NewError("Expected status code " + strconv.Itoa(code) + ", received " + strconv.Itoa(resp.StatusCode))
	}
	return nil
}

func (t Test) CheckContent(resp *http.Response) error {
	if t.Content == "" {
		return nil
	}

	rcvdbytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	rcvdcontent := string(rcvdbytes)

	match, err := regexp.MatchString(t.Content, rcvdcontent)
	if err != nil {
		return err
	}

	if !match {
		return t.NewError("Expected content '" + t.Content + "', not found in response (" + strconv.Itoa(len(rcvdbytes)) + " bytes)")
	}

	return nil
}

func (t Test) NewError(message string) error {
	return errors.New(strings.Title(strings.ToLower(t.MethodName())) + " " + t.Url + ": " + message)
}

func (t Test) MethodName() string {
	switch t.Method {
	case GET:
		return "GET"
	case POST:
		return "POST"
	default:
		return ""
	}
}
<commit_msg>Test: don't pass http.Response objects around, instead use code/body<commit_after>package urlcheck

import (
	"errors"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

type Method int

const (
	GET Method = iota
	POST
)

type Test struct {
	Url           string
	Content       string
	Code          int
	Method        Method
	Data          string
	Headers       map[string]string
	SkipSSLVerify bool
}

func (t Test) Test() (err error) {
	code, body, err := t.DoRequest()
	if err != nil {
		return err
	}

	if err == nil {
		err = t.CheckCode(code)
	}
	if err == nil {
		err = t.CheckContent(body)
	}
	return
}

func (t Test) DoRequest() (code int, body string, err error) {
	req, err := http.NewRequest(t.MethodName(), t.Url, strings.NewReader(t.Data))
	if err != nil {
		return 0, "", err
	}

	for k, v := range t.Headers {
		req.Header.Add(k, v)
	}

	resp, err := client.Do(req)
	if err != nil {
		return 0, "", err
	}

	rcvdbytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return 0, "", err
	}
	body = string(rcvdbytes)
	code = resp.StatusCode

	defer resp.Body.Close()

	return code, body, nil
}

func (t Test) CheckCode(code int) error {
	expect := t.Code
	if expect == 0 {
		expect = 200
	}
	if code != expect {
		return t.NewError("Expected status code " + strconv.Itoa(expect) + ", received " + strconv.Itoa(code))
	}
	return nil
}

func (t Test) CheckContent(body string) error {
	if t.Content == "" {
		return nil
	}

	match, err := regexp.MatchString(t.Content, body)
	if err != nil {
		return err
	}

	if !match {
		return t.NewError("Expected content '" + t.Content + "', not found in response (" + strconv.Itoa(len(body)) + " bytes)")
	}

	return nil
}

func (t Test) NewError(message string) error {
	return errors.New(strings.Title(strings.ToLower(t.MethodName())) + " " + t.Url + ": " + message)
}

func (t Test) MethodName() string {
	switch t.Method {
	case GET:
		return "GET"
	case POST:
		return "POST"
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package book

type Book struct {
	Author   string    `json:"author"`
	Name     string    `json:"name"`
	Chapters []Chapter `json:"chapters"`
}

type Chapter struct {
	Title   string `json:"title"`
	Content string `json:"content"`
}
<commit_msg>change Chapter.Content type string to []string<commit_after>package book

type Book struct {
	Author   string    `json:"author"`
	Name     string    `json:"name"`
	Chapters []Chapter `json:"chapters"`
}

type Chapter struct {
	Title   string `json:"title"`
	Content []string `json:"content"`
}
<|endoftext|>


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

package aerospike

import (
	"fmt"
	"reflect"
	"runtime"
	"sync"

	. "github.com/aerospike/aerospike-client-go/types"
	. "github.com/aerospike/aerospike-client-go/types/atomic"
)

type Result struct {
	Record *Record
	Err    error
}

// String implements the Stringer interface
func (res *Result) String() string {
	if res.Record != nil {
		return fmt.Sprintf("%v", res.Record)
	}
	return fmt.Sprintf("%v", res.Err)
}

// Objectset encapsulates the result of Scan and Query commands.
type objectset struct {
	// a reference to the object channel to close on end signal
	objChan reflect.Value

	// Errors is a channel on which all errors will be sent back.
	// NOTE: Do not use Errors directly. Range on channel returned by Results() instead.
	// This field is deprecated and will be unexported in the future
	Errors chan error

	wgGoroutines sync.WaitGroup
	goroutines   *AtomicInt

	active    *AtomicBool
	cancelled chan struct{}

	chanLock sync.Mutex

	taskId uint64
}

// TaskId returns the transactionId/jobId sent to the server for this recordset.
func (os *objectset) TaskId() uint64 {
	return os.taskId
}

// Recordset encapsulates the result of Scan and Query commands.
type Recordset struct {
	objectset

	// Records is a channel on which the resulting records will be sent back.
	// NOTE: Do not use Records directly. Range on channel returned by Results() instead.
	// Will be unexported in the future
	Records chan *Record
}

// makes sure the recordset is closed eventually, even if it is not consumed
func recordsetFinalizer(rs *Recordset) {
	rs.Close()
}

// newObjectset generates a new RecordSet instance.
func newObjectset(objChan reflect.Value, goroutines int, taskId uint64) *objectset {

	if objChan.Kind() != reflect.Chan ||
		objChan.Type().Elem().Kind() != reflect.Ptr ||
		objChan.Type().Elem().Elem().Kind() != reflect.Struct {
		panic("Scan/Query object channels should be of type `chan *T`")
	}

	rs := &objectset{
		objChan:    objChan,
		Errors:     make(chan error, goroutines),
		active:     NewAtomicBool(true),
		goroutines: NewAtomicInt(goroutines),
		cancelled:  make(chan struct{}),
		taskId:     taskId,
	}
	rs.wgGoroutines.Add(goroutines)

	return rs
}

// newRecordset generates a new RecordSet instance.
func newRecordset(recSize, goroutines int, taskId uint64) *Recordset {
	var nilChan chan *struct{}

	rs := &Recordset{
		Records:   make(chan *Record, recSize),
		objectset: *newObjectset(reflect.ValueOf(nilChan), goroutines, taskId),
	}

	runtime.SetFinalizer(rs, recordsetFinalizer)
	return rs
}

// IsActive returns true if the operation hasn't been finished or cancelled.
func (rcs *Recordset) IsActive() bool {
	return rcs.active.Get()
}

// Read reads the next record from the Recordset. If the Recordset has been
// closed, it returns ErrRecordsetClosed.
func (rcs *Recordset) Read() (record *Record, err error) {
	var ok bool

L:
	select {
	case record, ok = <-rcs.Records:
		if !ok {
			err = ErrRecordsetClosed
		}
	case err = <-rcs.Errors:
		if err == nil {
			// if err == nil, it means the Errors chan has been closed
			// we should not return nil as an error, so we should listen
			// to other chans again to determine either cancellation,
			// or normal EOR
			goto L
		}
	}

	return record, err
}

// Results returns a new receive-only channel with the results of the Scan/Query.
// This is a more idiomatic approach to the iterator pattern in getting the
// results back from the recordset, and doesn't require the user to write the
// ugly select in their code.
// Result contains a Record and an error reference.
//
// Example:
//
//  recordset, err := client.ScanAll(nil, namespace, set)
//  handleError(err)
//  for res := range recordset.Results() {
//    if res.Err != nil {
//      // handle error here
//    } else {
//      // process record here
//      fmt.Println(res.Record.Bins)
//    }
//  }
func (rcs *Recordset) Results() <-chan *Result {
	recCap := cap(rcs.Records)
	if recCap < 1 {
		recCap = 1
	}
	res := make(chan *Result, recCap)

	select {
	case <-rcs.cancelled:
		// Bail early and give the caller a channel for nothing -- it's
		// functionally wasted memory, but the caller did something
		// after close, so it's their own doing.
		close(res)
		return res
	default:
	}

	go func(cancelled <-chan struct{}) {
		defer close(res)
		for {
			record, err := rcs.Read()
			if err == ErrRecordsetClosed {
				return
			}

			result := &Result{Record: record, Err: err}
			select {
			case <-cancelled:
				return
			case res <- result:

			}
		}
	}(rcs.cancelled)

	return res
}

// Close all streams from different nodes. A successful close return nil,
// subsequent calls to the method will return ErrRecordsetClosed.
func (rcs *Recordset) Close() error {
	// do it only once
	if !rcs.active.CompareAndToggle(true) {
		return ErrRecordsetClosed
	}

	// check and close cancelled channel
	// this will broadcast to all commands listening to the channel
	select {
	case <-rcs.cancelled:
	default:
		close(rcs.cancelled)
	}

	// wait till all goroutines are done, and signalEnd is called by the scan command
	rcs.wgGoroutines.Wait()

	return nil
}

func (rcs *Recordset) signalEnd() {
	rcs.wgGoroutines.Done()
	if rcs.goroutines.DecrementAndGet() == 0 {
		// mark the recordset as closed
		rcs.active.Set(false)

		// check and close cancelled channel
		select {
		case <-rcs.cancelled:
		default:
			close(rcs.cancelled)
		}

		rcs.chanLock.Lock()
		defer rcs.chanLock.Unlock()

		if rcs.Records != nil {
			close(rcs.Records)
		} else if rcs.objChan.IsValid() {
			rcs.objChan.Close()
		}

		close(rcs.Errors)
	}
}

func (rcs *Recordset) sendError(err error) {
	rcs.chanLock.Lock()
	defer rcs.chanLock.Unlock()
	if rcs.IsActive() {
		rcs.Errors <- err
	}
}
<commit_msg>always check and close cancelled channel (exactly once though)<commit_after>// Copyright 2013-2016 Aerospike, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package aerospike

import (
	"fmt"
	"reflect"
	"runtime"
	"sync"

	. "github.com/aerospike/aerospike-client-go/types"
	. "github.com/aerospike/aerospike-client-go/types/atomic"
)

type Result struct {
	Record *Record
	Err    error
}

// String implements the Stringer interface
func (res *Result) String() string {
	if res.Record != nil {
		return fmt.Sprintf("%v", res.Record)
	}
	return fmt.Sprintf("%v", res.Err)
}

// Objectset encapsulates the result of Scan and Query commands.
type objectset struct {
	// a reference to the object channel to close on end signal
	objChan reflect.Value

	// Errors is a channel on which all errors will be sent back.
	// NOTE: Do not use Errors directly. Range on channel returned by Results() instead.
	// This field is deprecated and will be unexported in the future
	Errors chan error

	wgGoroutines sync.WaitGroup
	goroutines   *AtomicInt

	active    *AtomicBool
	cancelled chan struct{}

	chanLock sync.Mutex

	taskId uint64
}

// TaskId returns the transactionId/jobId sent to the server for this recordset.
func (os *objectset) TaskId() uint64 {
	return os.taskId
}

// Recordset encapsulates the result of Scan and Query commands.
type Recordset struct {
	objectset

	// Records is a channel on which the resulting records will be sent back.
	// NOTE: Do not use Records directly. Range on channel returned by Results() instead.
	// Will be unexported in the future
	Records chan *Record
}

// makes sure the recordset is closed eventually, even if it is not consumed
func recordsetFinalizer(rs *Recordset) {
	rs.Close()
}

// newObjectset generates a new RecordSet instance.
func newObjectset(objChan reflect.Value, goroutines int, taskId uint64) *objectset {

	if objChan.Kind() != reflect.Chan ||
		objChan.Type().Elem().Kind() != reflect.Ptr ||
		objChan.Type().Elem().Elem().Kind() != reflect.Struct {
		panic("Scan/Query object channels should be of type `chan *T`")
	}

	rs := &objectset{
		objChan:    objChan,
		Errors:     make(chan error, goroutines),
		active:     NewAtomicBool(true),
		goroutines: NewAtomicInt(goroutines),
		cancelled:  make(chan struct{}),
		taskId:     taskId,
	}
	rs.wgGoroutines.Add(goroutines)

	return rs
}

// newRecordset generates a new RecordSet instance.
func newRecordset(recSize, goroutines int, taskId uint64) *Recordset {
	var nilChan chan *struct{}

	rs := &Recordset{
		Records:   make(chan *Record, recSize),
		objectset: *newObjectset(reflect.ValueOf(nilChan), goroutines, taskId),
	}

	runtime.SetFinalizer(rs, recordsetFinalizer)
	return rs
}

// IsActive returns true if the operation hasn't been finished or cancelled.
func (rcs *Recordset) IsActive() bool {
	return rcs.active.Get()
}

// Read reads the next record from the Recordset. If the Recordset has been
// closed, it returns ErrRecordsetClosed.
func (rcs *Recordset) Read() (record *Record, err error) {
	var ok bool

L:
	select {
	case record, ok = <-rcs.Records:
		if !ok {
			err = ErrRecordsetClosed
		}
	case err = <-rcs.Errors:
		if err == nil {
			// if err == nil, it means the Errors chan has been closed
			// we should not return nil as an error, so we should listen
			// to other chans again to determine either cancellation,
			// or normal EOR
			goto L
		}
	}

	return record, err
}

// Results returns a new receive-only channel with the results of the Scan/Query.
// This is a more idiomatic approach to the iterator pattern in getting the
// results back from the recordset, and doesn't require the user to write the
// ugly select in their code.
// Result contains a Record and an error reference.
//
// Example:
//
//  recordset, err := client.ScanAll(nil, namespace, set)
//  handleError(err)
//  for res := range recordset.Results() {
//    if res.Err != nil {
//      // handle error here
//    } else {
//      // process record here
//      fmt.Println(res.Record.Bins)
//    }
//  }
func (rcs *Recordset) Results() <-chan *Result {
	recCap := cap(rcs.Records)
	if recCap < 1 {
		recCap = 1
	}
	res := make(chan *Result, recCap)

	select {
	case <-rcs.cancelled:
		// Bail early and give the caller a channel for nothing -- it's
		// functionally wasted memory, but the caller did something
		// after close, so it's their own doing.
		close(res)
		return res
	default:
	}

	go func(cancelled <-chan struct{}) {
		defer close(res)
		for {
			record, err := rcs.Read()
			if err == ErrRecordsetClosed {
				return
			}

			result := &Result{Record: record, Err: err}
			select {
			case <-cancelled:
				return
			case res <- result:

			}
		}
	}(rcs.cancelled)

	return res
}

// Close all streams from different nodes. A successful close return nil,
// subsequent calls to the method will return ErrRecordsetClosed.
func (rcs *Recordset) Close() error {
	// check and close cancelled channel
	// this will broadcast to all commands listening to the channel
	select {
	case <-rcs.cancelled:
	default:
		close(rcs.cancelled)
	}

	// do it only once
	if !rcs.active.CompareAndToggle(true) {
		return ErrRecordsetClosed
	}

	// wait till all goroutines are done, and signalEnd is called by the scan command
	rcs.wgGoroutines.Wait()

	return nil
}

func (rcs *Recordset) signalEnd() {
	rcs.wgGoroutines.Done()
	if rcs.goroutines.DecrementAndGet() == 0 {
		// mark the recordset as closed
		rcs.active.Set(false)

		rcs.chanLock.Lock()
		defer rcs.chanLock.Unlock()

		if rcs.Records != nil {
			close(rcs.Records)
		} else if rcs.objChan.IsValid() {
			rcs.objChan.Close()
		}

		close(rcs.Errors)
	}
}

func (rcs *Recordset) sendError(err error) {
	rcs.chanLock.Lock()
	defer rcs.chanLock.Unlock()
	if rcs.IsActive() {
		rcs.Errors <- err
	}
}
<|endoftext|>


<commit_before>// Package reflector extends standard package reflect with useful utilities.
package reflector

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

// Converts value to kind. Panics if it can't be done.
type Converter func(value interface{}, kind reflect.Kind) interface{}

// Converter: requires value to be exactly of specified kind.
func NoConvert(value interface{}, kind reflect.Kind) interface{} {
	switch kind {
	case reflect.Bool:
		return value.(bool)

	case reflect.Int:
		return int64(value.(int))
	case reflect.Int8:
		return int64(value.(int8))
	case reflect.Int16:
		return int64(value.(int16))
	case reflect.Int32:
		return int64(value.(int32))
	case reflect.Int64:
		return value.(int64)

	case reflect.Uint:
		return uint64(value.(uint))
	case reflect.Uint8:
		return uint64(value.(uint8))
	case reflect.Uint16:
		return uint64(value.(uint16))
	case reflect.Uint32:
		return uint64(value.(uint32))
	case reflect.Uint64:
		return value.(uint64)
	case reflect.Uintptr:
		return uint64(value.(uintptr))

	case reflect.Float32:
		return float64(value.(float32))
	case reflect.Float64:
		return value.(float64)

	case reflect.String:
		return value.(string)
	}

	panic(fmt.Errorf("NoConvert: can't convert %#v to %s", value, kind))
}

// Converter: uses strconv.Parse* functions.
func Strconv(value interface{}, kind reflect.Kind) (res interface{}) {
	e := fmt.Errorf("Strconv: can't convert %#v to %s", value, kind)
	s := fmt.Sprint(value)

	switch kind {
	case reflect.Bool:
		res, e = strconv.ParseBool(s)
		if e != nil {
			panic(e)
		}
		return

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		res, e = strconv.ParseInt(s, 10, 64)
		if e != nil {
			panic(e)
		}
		return

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		res, e = strconv.ParseUint(s, 10, 64)
		if e != nil {
			panic(e)
		}
		return

	case reflect.Float32, reflect.Float64:
		res, e = strconv.ParseFloat(s, 64)
		if e != nil {
			panic(e)
		}
		return

	case reflect.String:
		return s
	}

	panic(e)
}

// Converts a struct to map.
// First argument is a pointer to struct.
// Second argument is a not-nil map which will be modified.
// Only exported struct fields are used.
// Tag may be used to change mapping between struct field and map key.
// Currently supports bool, ints, uints, floats, strings.
// Panics in case of error.
func StructToMap(structPointer interface{}, m map[string]interface{}, tag string) {
	structPointerType := reflect.TypeOf(structPointer)
	if structPointerType.Kind() != reflect.Ptr {
		panic(fmt.Errorf("StructToMap: expected pointer to struct as first argument, got %s", structPointerType.Kind()))
	}

	structType := structPointerType.Elem()
	if structType.Kind() != reflect.Struct {
		panic(fmt.Errorf("StructToMap: expected pointer to struct as first argument, got pointer to %s", structType.Kind()))
	}

	s := reflect.ValueOf(structPointer).Elem()

	var name string
	for i := 0; i < structType.NumField(); i++ {
		f := s.Field(i)
		if !f.CanSet() {
			continue
		}

		stf := structType.Field(i)
		name = ""
		if tag != "" {
			name = stf.Tag.Get(tag)
		}
		if name == "" {
			name = stf.Name
		}

		m[name] = f.Interface()
	}
}

// Converts a map to struct using converter function.
// First argument is a map.
// Second argument is a not-nil pointer to struct which will be modified.
// Only exported struct fields are set. Omitted or extra values in map are ignored.
// Tag may be used to change mapping between struct field and map key.
// Currently supports bool, ints, uints, floats, strings.
// Panics in case of error.
func MapToStruct(m map[string]interface{}, structPointer interface{}, converter Converter, tag string) {
	structPointerType := reflect.TypeOf(structPointer)
	if structPointerType.Kind() != reflect.Ptr {
		panic(fmt.Errorf("MapToStruct: expected pointer to struct as second argument, got %s", structPointerType.Kind()))
	}

	structType := structPointerType.Elem()
	if structType.Kind() != reflect.Struct {
		panic(fmt.Errorf("MapToStruct: expected pointer to struct as second argument, got pointer to %s", structType.Kind()))
	}
	s := reflect.ValueOf(structPointer).Elem()

	var name string
	defer func() {
		e := recover()
		if e == nil {
			return
		}

		panic(fmt.Errorf("MapToStruct, field %s: %s", name, e))
	}()

	for i := 0; i < structType.NumField(); i++ {
		f := s.Field(i)
		if !f.CanSet() {
			continue
		}

		stf := structType.Field(i)
		name = ""
		if tag != "" {
			name = stf.Tag.Get(tag)
		}
		if name == "" {
			name = stf.Name
		}
		v, ok := m[name]
		if !ok {
			continue
		}

		kind := f.Kind()
		switch kind {
		case reflect.Bool:
			f.SetBool(converter(v, kind).(bool))

		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			f.SetInt(converter(v, kind).(int64))

		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			f.SetUint(converter(v, kind).(uint64))

		case reflect.Float32, reflect.Float64:
			f.SetFloat(converter(v, kind).(float64))

		case reflect.String:
			f.SetString(converter(v, kind).(string))

		default:
			// not implemented
		}
	}

	return
}

// Converts a slice of maps to a slice of structs. Uses MapToStruct().
// First argument is a slice of maps.
// Second argument is a pointer to (possibly nil) slice of structs which will be set.
func MapsToStructs(maps []map[string]interface{}, slicePointer interface{}, converter Converter, tag string) {
	slicePointerType := reflect.TypeOf(slicePointer)
	if slicePointerType.Kind() != reflect.Ptr {
		panic(fmt.Errorf("MapsToStructs: expected pointer to slice of structs as second argument, got %s", slicePointerType.Kind()))
	}

	sliceType := slicePointerType.Elem()
	if sliceType.Kind() != reflect.Slice {
		panic(fmt.Errorf("MapsToStructs: expected pointer to slice of structs as second argument, got pointer to %s", sliceType.Kind()))
	}

	structType := sliceType.Elem()
	if structType.Kind() != reflect.Struct {
		panic(fmt.Errorf("MapsToStructs: expected pointer to slice of structs as second argument, got pointer to slice of %s", structType.Kind()))
	}

	slice := reflect.MakeSlice(sliceType, 0, len(maps))
	for _, m := range maps {
		str := reflect.New(structType)
		MapToStruct(m, str.Interface(), converter, tag)
		slice = reflect.Append(slice, str.Elem())
	}
	reflect.ValueOf(slicePointer).Elem().Set(slice)
}

// Variant of MapsToStructs() with relaxed signature.
func MapsToStructs2(maps []interface{}, slicePointer interface{}, converter Converter, tag string) {
	m := make([]map[string]interface{}, len(maps))
	for index, i := range maps {
		m[index] = i.(map[string]interface{})
	}
	MapsToStructs(m, slicePointer, converter, tag)
}
<commit_msg>Better argument names.<commit_after>// Package reflector extends standard package reflect with useful utilities.
package reflector

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

// Converts value to kind. Panics if it can't be done.
type Converter func(value interface{}, kind reflect.Kind) interface{}

// Converter: requires value to be exactly of specified kind.
func NoConvert(value interface{}, kind reflect.Kind) interface{} {
	switch kind {
	case reflect.Bool:
		return value.(bool)

	case reflect.Int:
		return int64(value.(int))
	case reflect.Int8:
		return int64(value.(int8))
	case reflect.Int16:
		return int64(value.(int16))
	case reflect.Int32:
		return int64(value.(int32))
	case reflect.Int64:
		return value.(int64)

	case reflect.Uint:
		return uint64(value.(uint))
	case reflect.Uint8:
		return uint64(value.(uint8))
	case reflect.Uint16:
		return uint64(value.(uint16))
	case reflect.Uint32:
		return uint64(value.(uint32))
	case reflect.Uint64:
		return value.(uint64)
	case reflect.Uintptr:
		return uint64(value.(uintptr))

	case reflect.Float32:
		return float64(value.(float32))
	case reflect.Float64:
		return value.(float64)

	case reflect.String:
		return value.(string)
	}

	panic(fmt.Errorf("NoConvert: can't convert %#v to %s", value, kind))
}

// Converter: uses strconv.Parse* functions.
func Strconv(value interface{}, kind reflect.Kind) (res interface{}) {
	e := fmt.Errorf("Strconv: can't convert %#v to %s", value, kind)
	s := fmt.Sprint(value)

	switch kind {
	case reflect.Bool:
		res, e = strconv.ParseBool(s)
		if e != nil {
			panic(e)
		}
		return

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		res, e = strconv.ParseInt(s, 10, 64)
		if e != nil {
			panic(e)
		}
		return

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		res, e = strconv.ParseUint(s, 10, 64)
		if e != nil {
			panic(e)
		}
		return

	case reflect.Float32, reflect.Float64:
		res, e = strconv.ParseFloat(s, 64)
		if e != nil {
			panic(e)
		}
		return

	case reflect.String:
		return s
	}

	panic(e)
}

// Converts a struct to map.
// First argument is a pointer to struct.
// Second argument is a not-nil map which will be modified.
// Only exported struct fields are used.
// Tag may be used to change mapping between struct field and map key.
// Currently supports bool, ints, uints, floats, strings.
// Panics in case of error.
func StructToMap(StructPointer interface{}, Map map[string]interface{}, tag string) {
	structPointerType := reflect.TypeOf(StructPointer)
	if structPointerType.Kind() != reflect.Ptr {
		panic(fmt.Errorf("StructToMap: expected pointer to struct as first argument, got %s", structPointerType.Kind()))
	}

	structType := structPointerType.Elem()
	if structType.Kind() != reflect.Struct {
		panic(fmt.Errorf("StructToMap: expected pointer to struct as first argument, got pointer to %s", structType.Kind()))
	}

	s := reflect.ValueOf(StructPointer).Elem()

	var name string
	for i := 0; i < structType.NumField(); i++ {
		f := s.Field(i)
		if !f.CanSet() {
			continue
		}

		stf := structType.Field(i)
		name = ""
		if tag != "" {
			name = stf.Tag.Get(tag)
		}
		if name == "" {
			name = stf.Name
		}

		Map[name] = f.Interface()
	}
}

// Converts a map to struct using converter function.
// First argument is a map.
// Second argument is a not-nil pointer to struct which will be modified.
// Only exported struct fields are set. Omitted or extra values in map are ignored.
// Tag may be used to change mapping between struct field and map key.
// Currently supports bool, ints, uints, floats, strings.
// Panics in case of error.
func MapToStruct(Map map[string]interface{}, StructPointer interface{}, converter Converter, tag string) {
	structPointerType := reflect.TypeOf(StructPointer)
	if structPointerType.Kind() != reflect.Ptr {
		panic(fmt.Errorf("MapToStruct: expected pointer to struct as second argument, got %s", structPointerType.Kind()))
	}

	structType := structPointerType.Elem()
	if structType.Kind() != reflect.Struct {
		panic(fmt.Errorf("MapToStruct: expected pointer to struct as second argument, got pointer to %s", structType.Kind()))
	}
	s := reflect.ValueOf(StructPointer).Elem()

	var name string
	defer func() {
		e := recover()
		if e == nil {
			return
		}

		panic(fmt.Errorf("MapToStruct, field %s: %s", name, e))
	}()

	for i := 0; i < structType.NumField(); i++ {
		f := s.Field(i)
		if !f.CanSet() {
			continue
		}

		stf := structType.Field(i)
		name = ""
		if tag != "" {
			name = stf.Tag.Get(tag)
		}
		if name == "" {
			name = stf.Name
		}
		v, ok := Map[name]
		if !ok {
			continue
		}

		kind := f.Kind()
		switch kind {
		case reflect.Bool:
			f.SetBool(converter(v, kind).(bool))

		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			f.SetInt(converter(v, kind).(int64))

		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			f.SetUint(converter(v, kind).(uint64))

		case reflect.Float32, reflect.Float64:
			f.SetFloat(converter(v, kind).(float64))

		case reflect.String:
			f.SetString(converter(v, kind).(string))

		default:
			// not implemented
		}
	}

	return
}

// Converts a slice of maps to a slice of structs. Uses MapToStruct().
// First argument is a slice of maps.
// Second argument is a pointer to (possibly nil) slice of structs which will be set.
func MapsToStructs(Maps []map[string]interface{}, SlicePointer interface{}, converter Converter, tag string) {
	slicePointerType := reflect.TypeOf(SlicePointer)
	if slicePointerType.Kind() != reflect.Ptr {
		panic(fmt.Errorf("MapsToStructs: expected pointer to slice of structs as second argument, got %s", slicePointerType.Kind()))
	}

	sliceType := slicePointerType.Elem()
	if sliceType.Kind() != reflect.Slice {
		panic(fmt.Errorf("MapsToStructs: expected pointer to slice of structs as second argument, got pointer to %s", sliceType.Kind()))
	}

	structType := sliceType.Elem()
	if structType.Kind() != reflect.Struct {
		panic(fmt.Errorf("MapsToStructs: expected pointer to slice of structs as second argument, got pointer to slice of %s", structType.Kind()))
	}

	slice := reflect.MakeSlice(sliceType, 0, len(Maps))
	for _, m := range Maps {
		s := reflect.New(structType)
		MapToStruct(m, s.Interface(), converter, tag)
		slice = reflect.Append(slice, s.Elem())
	}
	reflect.ValueOf(SlicePointer).Elem().Set(slice)
}

// Variant of MapsToStructs() with relaxed signature.
func MapsToStructs2(Maps []interface{}, SlicePointer interface{}, converter Converter, tag string) {
	m := make([]map[string]interface{}, len(Maps))
	for index, i := range Maps {
		m[index] = i.(map[string]interface{})
	}
	MapsToStructs(m, SlicePointer, converter, tag)
}
<|endoftext|>