<commit_before>package domfinder

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"strconv"
	"strings"
)

const (
	kernHostname = "/proc/sys/kernel/hostname"
	kernDomain   = "/proc/sys/kernel/domainname"
)

// getHostname returns the servers hostname which we should compare against webserver
// vhost entries.
func getHostname() string {
	host, herr := ioutil.ReadFile(kernHostname)
	domain, derr := ioutil.ReadFile(kernDomain)
	if herr != nil || derr != nil {
		return "unknown"
	}

	if strings.Contains(string(domain), "none") {
		return strings.Replace(string(host), "\n", "", 1)
	}

	return strings.Replace(string(host), "\n", "", 1) + "." + strings.Replace(string(domain), "\n", "", 1)
}

// stripDups strips all domains that have the same resulting URL
func stripDups(domains *[]*Domain) {
	var tmp []*Domain

	for _, dom := range *domains {
		isIn := false
		for _, other := range tmp {
			if dom.URL.String() == other.URL.String() {
				isIn = true
				break
			}
		}
		if !isIn {
			tmp = append(tmp, dom)
		}
	}

	*domains = tmp

	return
}

// isDomainURL should validate the data we are obtaining from the webservers to
// ensure it is a proper hostname and/or port (within reason. custom configs are
// custom)
func isDomainURL(host string, port string) (*url.URL, Err) {
	if port != "443" && port != "80" {
		host = fmt.Sprintf("%s:%s", host, port)
	}

	intport, err := strconv.Atoi(port)
	if err != nil {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}
	strport := strconv.Itoa(intport)
	if strport != port {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}

	// lets try and determine the scheme we need. Best solution would like be:
	//   - 443 -- https
	//   - anything else -- http
	var scheme string
	if port == "443" {
		scheme = "https://"
	} else {
		scheme = "http://"
	}
	host = scheme + host

	if strings.Contains(host, " ") {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}

	uri, err := url.Parse(host)

	if err != nil {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}

	return uri, nil
}
<commit_msg>add MustURL() into domfinder<commit_after>package domfinder

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"strconv"
	"strings"
)

const (
	kernHostname = "/proc/sys/kernel/hostname"
	kernDomain   = "/proc/sys/kernel/domainname"
)

// getHostname returns the servers hostname which we should compare against webserver
// vhost entries.
func getHostname() string {
	host, herr := ioutil.ReadFile(kernHostname)
	domain, derr := ioutil.ReadFile(kernDomain)
	if herr != nil || derr != nil {
		return "unknown"
	}

	if strings.Contains(string(domain), "none") {
		return strings.Replace(string(host), "\n", "", 1)
	}

	return strings.Replace(string(host), "\n", "", 1) + "." + strings.Replace(string(domain), "\n", "", 1)
}

// stripDups strips all domains that have the same resulting URL
func stripDups(domains *[]*Domain) {
	var tmp []*Domain

	for _, dom := range *domains {
		isIn := false
		for _, other := range tmp {
			if dom.URL.String() == other.URL.String() {
				isIn = true
				break
			}
		}
		if !isIn {
			tmp = append(tmp, dom)
		}
	}

	*domains = tmp

	return
}

// isDomainURL should validate the data we are obtaining from the webservers to
// ensure it is a proper hostname and/or port (within reason. custom configs are
// custom)
func isDomainURL(host, port string) (*url.URL, Err) {
	if port != "443" && port != "80" {
		host = fmt.Sprintf("%s:%s", host, port)
	}

	intport, err := strconv.Atoi(port)
	if err != nil {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}
	strport := strconv.Itoa(intport)
	if strport != port {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}

	// lets try and determine the scheme we need. Best solution would like be:
	//   - 443 -- https
	//   - anything else -- http
	var scheme string
	if port == "443" {
		scheme = "https://"
	} else {
		scheme = "http://"
	}
	host = scheme + host

	if strings.Contains(host, " ") {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}

	uri, err := url.Parse(host)

	if err != nil {
		return nil, &NewErr{Code: ErrInvalidURL, value: fmt.Sprintf("%s (port: %s)", host, port)}
	}

	return uri, nil
}

// MustURL is much like isDomainURL, however will panic on error (useful for tests).
func MustURL(host, port string) *url.URL {
	uri, err := isDomainURL(host, port)
	if err != nil {
		panic(err)
	}

	return uri
}
<|endoftext|>


<commit_before>package goriak

import (
	"errors"
	"reflect"
	"strconv"
	"time"

	riak "github.com/basho/riak-go-client"
)

func decodeInterface(data *riak.FetchMapResponse, output interface{}, riakRequest requestData) error {
	return transMapToStruct(
		data.Map,
		reflect.ValueOf(output).Elem(),
		reflect.TypeOf(output).Elem(),
		data.Context,
		[]string{}, // Start with an empty path
		riakRequest,
	)
}

// Assings values from a Riak Map to a receiving Go struct
func transMapToStruct(data *riak.Map, rValue reflect.Value, rType reflect.Type, riakContext []byte, path []string, riakRequest requestData) error {

	num := rType.NumField()

	for i := 0; i < num; i++ {

		field := rType.Field(i)
		fieldVal := rValue.Field(i)
		registerName := field.Name
		tag := field.Tag.Get("goriak")

		// goriakcontext is a reserved keyword.
		// Use the tag `goriak:"goriakcontext"` to get the Riak context necessary for certaion Riak operations,
		// such as removing items from a Set.
		if tag == "goriakcontext" {
			rValue.Field(i).SetBytes(riakContext)
			continue
		}

		// Ignore this value
		if tag == "-" {
			continue
		}

		if len(tag) > 0 {
			registerName = tag
		}

		switch field.Type.Kind() {
		case reflect.String:
			if val, ok := data.Registers[registerName]; ok {
				fieldVal.SetString(string(val))
			}

		case reflect.Array:
			// []byte
			if fieldVal.Type().Elem().Kind() == reflect.Uint8 {
				if val, ok := data.Registers[registerName]; ok {
					for ii := 0; ii < fieldVal.Len(); ii++ {
						fieldVal.Index(ii).SetUint(uint64(val[ii]))
					}
				}
			}

		// Integer types
		case reflect.Int:
			fallthrough
		case reflect.Int8:
			fallthrough
		case reflect.Int16:
			fallthrough
		case reflect.Int32:
			fallthrough
		case reflect.Int64:
			fallthrough
		case reflect.Uint:
			fallthrough
		case reflect.Uint8:
			fallthrough
		case reflect.Uint16:
			fallthrough
		case reflect.Uint32:
			fallthrough
		case reflect.Uint64:
			if val, ok := data.Registers[registerName]; ok {
				if newVal, err := bytesToValue(val, field.Type); err == nil {
					fieldVal.Set(newVal)
				}
			}

		case reflect.Bool:
			if val, ok := data.Flags[registerName]; ok {
				fieldVal.SetBool(val)
			}

		case reflect.Slice:
			err := transRiakToSlice(rValue.Field(i), registerName, data)

			if err != nil {
				return err
			}

		case reflect.Map:
			if subMap, ok := data.Maps[registerName]; ok {
				err := transMapToMap(rValue.Field(i), subMap)

				if err != nil {
					return err
				}
			}

		case reflect.Struct:
			done := false

			// time.Time
			if bin, ok := data.Registers[registerName]; ok {
				if ts, ok := fieldVal.Interface().(time.Time); ok {
					err := ts.UnmarshalBinary(bin)

					if err != nil {
						return err
					}

					fieldVal.Set(reflect.ValueOf(ts))
					done = true
				}
			}

			if !done {

				if subMap, ok := data.Maps[registerName]; ok {
					// Struct
					newPath := append(path, registerName)

					err := transMapToStruct(subMap, fieldVal, fieldVal.Type(), riakContext, newPath, riakRequest)

					if err != nil {
						return err
					}
				}
			}

		case reflect.Ptr:

			helperPathData := helper{
				name:    registerName,
				path:    path,
				key:     riakRequest,
				context: riakContext,
			}

			switch fieldVal.Type().String() {
			case "*goriak.Counter":
				var counterValue int64

				if val, ok := data.Counters[registerName]; ok {
					counterValue = val
				}

				resCounter := &Counter{
					helper: helperPathData,
					val:    counterValue,
				}

				fieldVal.Set(reflect.ValueOf(resCounter))

			case "*goriak.Set":

				var setValue [][]byte

				if val, ok := data.Sets[registerName]; ok {
					setValue = val
				}

				resSet := &Set{
					helper: helperPathData,
					value:  setValue,
				}

				fieldVal.Set(reflect.ValueOf(resSet))

			case "*goriak.Flag":

				var flagValue bool

				if val, ok := data.Flags[registerName]; ok {
					flagValue = val
				}

				resFlag := &Flag{
					helper: helperPathData,
					val:    flagValue,
				}

				fieldVal.Set(reflect.ValueOf(resFlag))

			case "*goriak.Register":

				var registerValue []byte

				if val, ok := data.Registers[registerName]; ok {
					registerValue = val
				}

				resRegister := &Register{
					helper: helperPathData,
					val:    registerValue,
				}

				fieldVal.Set(reflect.ValueOf(resRegister))

			default:
				return errors.New("Unexpected ptr type: " + fieldVal.Type().String())
			}

		default:
			return errors.New("Unknown type: " + field.Type.Kind().String())
		}
	}

	return nil
}

// Converts Riak objects (can be either Sets or Registers) to Golang Slices
func transRiakToSlice(sliceValue reflect.Value, registerName string, data *riak.Map) error {

	switch sliceValue.Type().Elem().Kind() {

	// []int
	case reflect.Int:
		if setVal, ok := data.Sets[registerName]; ok {
			result := make([]int, len(setVal))

			for i, v := range setVal {
				intVal, err := strconv.ParseInt(string(v), 10, 64)

				if err != nil {
					return err
				}

				result[i] = int(intVal)
			}

			// Success!
			sliceValue.Set(reflect.ValueOf(result))
		}

	// []string
	case reflect.String:
		if setVal, ok := data.Sets[registerName]; ok {
			result := make([]string, len(setVal))

			for i, v := range setVal {
				result[i] = string(v)
			}

			// Success!
			sliceValue.Set(reflect.ValueOf(result))
		}

	// []byte
	case reflect.Uint8:
		if val, ok := data.Registers[registerName]; ok {
			sliceValue.SetBytes(val)
		}

	// [][]byte
	case reflect.Slice:

		if sliceValue.Type().Elem().Elem().Kind() == reflect.Uint8 {
			if val, ok := data.Sets[registerName]; ok {
				sliceValue.Set(reflect.ValueOf(val))
			}

			return nil
		}

		return errors.New("Unknown slice slice type: " + sliceValue.Type().Elem().Elem().Kind().String())

	// [][n]byte
	case reflect.Array:
		if sliceValue.Type().Elem().Elem().Kind() == reflect.Uint8 {
			if values, ok := data.Sets[registerName]; ok {

				lengthOfExpectedArray := sliceValue.Type().Elem().Len()

				// The type of the inner array
				arrayType := sliceValue.Type().Elem()

				// A slice with array Type items
				// The length is set to the amount of values in the Set from Riak
				sliceType := reflect.SliceOf(arrayType)
				finalSliceValue := reflect.MakeSlice(sliceType, len(values), len(values))

				for valueIndex, value := range values {

					// Create the array from Riak data
					newArray := reflect.New(arrayType).Elem()

					for i := 0; i < lengthOfExpectedArray; i++ {
						newArray.Index(i).Set(reflect.ValueOf(value[i]))
					}

					// Add array to slice
					finalSliceValue.Index(valueIndex).Set(newArray)
				}

				// Override the Slice from "Userland"
				sliceValue.Set(finalSliceValue)

			}

			return nil
		}

		return errors.New("Unknown slice array type: " + sliceValue.Type().Elem().Elem().Kind().String())

	default:
		return errors.New("Unknown slice type: " + sliceValue.Type().Elem().Kind().String())
	}

	return nil
}

func bytesToValue(input []byte, outputType reflect.Type) (reflect.Value, error) {

	outputKind := outputType.Kind()

	switch outputKind {
	case reflect.String:
		return reflect.ValueOf(string(input)), nil

	case reflect.Int:
		if i, err := strconv.ParseInt(string(input), 10, 0); err == nil {
			return reflect.ValueOf(int(i)), nil
		}

	case reflect.Int8:
		if i, err := strconv.ParseInt(string(input), 10, 8); err == nil {
			return reflect.ValueOf(int8(i)), nil
		}

	case reflect.Int16:
		if i, err := strconv.ParseInt(string(input), 10, 16); err == nil {
			return reflect.ValueOf(int16(i)), nil
		}

	case reflect.Int32:
		if i, err := strconv.ParseInt(string(input), 10, 32); err == nil {
			return reflect.ValueOf(int32(i)), nil
		}

	case reflect.Int64:
		if i, err := strconv.ParseInt(string(input), 10, 64); err == nil {
			return reflect.ValueOf(int64(i)), nil
		}

	case reflect.Uint:
		if i, err := strconv.ParseUint(string(input), 10, 0); err == nil {
			return reflect.ValueOf(uint(i)), nil
		}

	case reflect.Uint8:
		if i, err := strconv.ParseUint(string(input), 10, 8); err == nil {
			return reflect.ValueOf(uint8(i)), nil
		}

	case reflect.Uint16:
		if i, err := strconv.ParseUint(string(input), 10, 16); err == nil {
			return reflect.ValueOf(uint16(i)), nil
		}

	case reflect.Uint32:
		if i, err := strconv.ParseUint(string(input), 10, 32); err == nil {
			return reflect.ValueOf(uint32(i)), nil
		}

	case reflect.Uint64:
		if i, err := strconv.ParseUint(string(input), 10, 64); err == nil {
			return reflect.ValueOf(uint64(i)), nil
		}

	case reflect.Slice:
		sliceItemType := outputType.Elem().Kind()

		switch sliceItemType {
		case reflect.Uint8:
			return reflect.ValueOf(input), nil
		}

	case reflect.Array:

		// Create new array of the expected type
		newArray := reflect.New(outputType).Elem()
		lengthOfExpectedArray := outputType.Len()
		arrayItemType := outputType.Elem().Kind()

		switch arrayItemType {
		// Byte array
		case reflect.Uint8:

			// Copy bytes
			for i := 0; i < lengthOfExpectedArray; i++ {
				newArray.Index(i).Set(reflect.ValueOf(input[i]))
			}

			return newArray, nil
		}
	}

	return reflect.ValueOf(nil), errors.New("Invalid input type: " + outputType.String())
}

// Converts a Riak Map to a Go Map
func transMapToMap(mapValue reflect.Value, data *riak.Map) error {

	mapKeyType := mapValue.Type().Key().Kind()

	// Initialize the map
	newMap := reflect.MakeMap(mapValue.Type())
	mapValue.Set(newMap)

	for key, val := range data.Registers {

		// Convert key (a string) to the correct reflect.Value
		keyValue, err := bytesToValue([]byte(key), mapValue.Type().Key())

		if err != nil {
			return errors.New("Unknown map key type: " + mapKeyType.String())
		}

		valValue, err := bytesToValue(val, mapValue.Type().Elem())

		if err != nil {
			return errors.New("Unknown map value type")
		}

		// Save value to the Go map
		mapValue.SetMapIndex(keyValue, valValue)
	}

	return nil
}
<commit_msg>Removed more duplicate code<commit_after>package goriak

import (
	"errors"
	"reflect"
	"strconv"
	"time"

	riak "github.com/basho/riak-go-client"
)

func decodeInterface(data *riak.FetchMapResponse, output interface{}, riakRequest requestData) error {
	return transMapToStruct(
		data.Map,
		reflect.ValueOf(output).Elem(),
		reflect.TypeOf(output).Elem(),
		data.Context,
		[]string{}, // Start with an empty path
		riakRequest,
	)
}

// Assings values from a Riak Map to a receiving Go struct
func transMapToStruct(data *riak.Map, rValue reflect.Value, rType reflect.Type, riakContext []byte, path []string, riakRequest requestData) error {

	num := rType.NumField()

	for i := 0; i < num; i++ {

		field := rType.Field(i)
		fieldVal := rValue.Field(i)
		registerName := field.Name
		tag := field.Tag.Get("goriak")

		// goriakcontext is a reserved keyword.
		// Use the tag `goriak:"goriakcontext"` to get the Riak context necessary for certaion Riak operations,
		// such as removing items from a Set.
		if tag == "goriakcontext" {
			rValue.Field(i).SetBytes(riakContext)
			continue
		}

		// Ignore this value
		if tag == "-" {
			continue
		}

		if len(tag) > 0 {
			registerName = tag
		}

		switch field.Type.Kind() {
		case reflect.Array:
			fallthrough
		case reflect.String:
			fallthrough
		case reflect.Int:
			fallthrough
		case reflect.Int8:
			fallthrough
		case reflect.Int16:
			fallthrough
		case reflect.Int32:
			fallthrough
		case reflect.Int64:
			fallthrough
		case reflect.Uint:
			fallthrough
		case reflect.Uint8:
			fallthrough
		case reflect.Uint16:
			fallthrough
		case reflect.Uint32:
			fallthrough
		case reflect.Uint64:
			if val, ok := data.Registers[registerName]; ok {
				if newVal, err := bytesToValue(val, field.Type); err == nil {
					fieldVal.Set(newVal)
				}
			}

		case reflect.Bool:
			if val, ok := data.Flags[registerName]; ok {
				fieldVal.SetBool(val)
			}

		case reflect.Slice:
			err := transRiakToSlice(rValue.Field(i), registerName, data)

			if err != nil {
				return err
			}

		case reflect.Map:
			if subMap, ok := data.Maps[registerName]; ok {
				err := transMapToMap(rValue.Field(i), subMap)

				if err != nil {
					return err
				}
			}

		case reflect.Struct:
			done := false

			// time.Time
			if bin, ok := data.Registers[registerName]; ok {
				if ts, ok := fieldVal.Interface().(time.Time); ok {
					err := ts.UnmarshalBinary(bin)

					if err != nil {
						return err
					}

					fieldVal.Set(reflect.ValueOf(ts))
					done = true
				}
			}

			if !done {

				if subMap, ok := data.Maps[registerName]; ok {
					// Struct
					newPath := append(path, registerName)

					err := transMapToStruct(subMap, fieldVal, fieldVal.Type(), riakContext, newPath, riakRequest)

					if err != nil {
						return err
					}
				}
			}

		case reflect.Ptr:

			helperPathData := helper{
				name:    registerName,
				path:    path,
				key:     riakRequest,
				context: riakContext,
			}

			switch fieldVal.Type().String() {
			case "*goriak.Counter":
				var counterValue int64

				if val, ok := data.Counters[registerName]; ok {
					counterValue = val
				}

				resCounter := &Counter{
					helper: helperPathData,
					val:    counterValue,
				}

				fieldVal.Set(reflect.ValueOf(resCounter))

			case "*goriak.Set":

				var setValue [][]byte

				if val, ok := data.Sets[registerName]; ok {
					setValue = val
				}

				resSet := &Set{
					helper: helperPathData,
					value:  setValue,
				}

				fieldVal.Set(reflect.ValueOf(resSet))

			case "*goriak.Flag":

				var flagValue bool

				if val, ok := data.Flags[registerName]; ok {
					flagValue = val
				}

				resFlag := &Flag{
					helper: helperPathData,
					val:    flagValue,
				}

				fieldVal.Set(reflect.ValueOf(resFlag))

			case "*goriak.Register":

				var registerValue []byte

				if val, ok := data.Registers[registerName]; ok {
					registerValue = val
				}

				resRegister := &Register{
					helper: helperPathData,
					val:    registerValue,
				}

				fieldVal.Set(reflect.ValueOf(resRegister))

			default:
				return errors.New("Unexpected ptr type: " + fieldVal.Type().String())
			}

		default:
			return errors.New("Unknown type: " + field.Type.Kind().String())
		}
	}

	return nil
}

// Converts Riak objects (can be either Sets or Registers) to Golang Slices
func transRiakToSlice(sliceValue reflect.Value, registerName string, data *riak.Map) error {

	switch sliceValue.Type().Elem().Kind() {

	// []int
	case reflect.Int:
		if setVal, ok := data.Sets[registerName]; ok {
			result := make([]int, len(setVal))

			for i, v := range setVal {
				intVal, err := strconv.ParseInt(string(v), 10, 64)

				if err != nil {
					return err
				}

				result[i] = int(intVal)
			}

			// Success!
			sliceValue.Set(reflect.ValueOf(result))
		}

	// []string
	case reflect.String:
		if setVal, ok := data.Sets[registerName]; ok {
			result := make([]string, len(setVal))

			for i, v := range setVal {
				result[i] = string(v)
			}

			// Success!
			sliceValue.Set(reflect.ValueOf(result))
		}

	// []byte
	case reflect.Uint8:
		if val, ok := data.Registers[registerName]; ok {
			sliceValue.SetBytes(val)
		}

	// [][]byte
	case reflect.Slice:

		if sliceValue.Type().Elem().Elem().Kind() == reflect.Uint8 {
			if val, ok := data.Sets[registerName]; ok {
				sliceValue.Set(reflect.ValueOf(val))
			}

			return nil
		}

		return errors.New("Unknown slice slice type: " + sliceValue.Type().Elem().Elem().Kind().String())

	// [][n]byte
	case reflect.Array:
		if sliceValue.Type().Elem().Elem().Kind() == reflect.Uint8 {
			if values, ok := data.Sets[registerName]; ok {

				lengthOfExpectedArray := sliceValue.Type().Elem().Len()

				// The type of the inner array
				arrayType := sliceValue.Type().Elem()

				// A slice with array Type items
				// The length is set to the amount of values in the Set from Riak
				sliceType := reflect.SliceOf(arrayType)
				finalSliceValue := reflect.MakeSlice(sliceType, len(values), len(values))

				for valueIndex, value := range values {

					// Create the array from Riak data
					newArray := reflect.New(arrayType).Elem()

					for i := 0; i < lengthOfExpectedArray; i++ {
						newArray.Index(i).Set(reflect.ValueOf(value[i]))
					}

					// Add array to slice
					finalSliceValue.Index(valueIndex).Set(newArray)
				}

				// Override the Slice from "Userland"
				sliceValue.Set(finalSliceValue)

			}

			return nil
		}

		return errors.New("Unknown slice array type: " + sliceValue.Type().Elem().Elem().Kind().String())

	default:
		return errors.New("Unknown slice type: " + sliceValue.Type().Elem().Kind().String())
	}

	return nil
}

func bytesToValue(input []byte, outputType reflect.Type) (reflect.Value, error) {

	outputKind := outputType.Kind()

	switch outputKind {
	case reflect.String:
		return reflect.ValueOf(string(input)), nil

	case reflect.Int:
		if i, err := strconv.ParseInt(string(input), 10, 0); err == nil {
			return reflect.ValueOf(int(i)), nil
		}

	case reflect.Int8:
		if i, err := strconv.ParseInt(string(input), 10, 8); err == nil {
			return reflect.ValueOf(int8(i)), nil
		}

	case reflect.Int16:
		if i, err := strconv.ParseInt(string(input), 10, 16); err == nil {
			return reflect.ValueOf(int16(i)), nil
		}

	case reflect.Int32:
		if i, err := strconv.ParseInt(string(input), 10, 32); err == nil {
			return reflect.ValueOf(int32(i)), nil
		}

	case reflect.Int64:
		if i, err := strconv.ParseInt(string(input), 10, 64); err == nil {
			return reflect.ValueOf(int64(i)), nil
		}

	case reflect.Uint:
		if i, err := strconv.ParseUint(string(input), 10, 0); err == nil {
			return reflect.ValueOf(uint(i)), nil
		}

	case reflect.Uint8:
		if i, err := strconv.ParseUint(string(input), 10, 8); err == nil {
			return reflect.ValueOf(uint8(i)), nil
		}

	case reflect.Uint16:
		if i, err := strconv.ParseUint(string(input), 10, 16); err == nil {
			return reflect.ValueOf(uint16(i)), nil
		}

	case reflect.Uint32:
		if i, err := strconv.ParseUint(string(input), 10, 32); err == nil {
			return reflect.ValueOf(uint32(i)), nil
		}

	case reflect.Uint64:
		if i, err := strconv.ParseUint(string(input), 10, 64); err == nil {
			return reflect.ValueOf(uint64(i)), nil
		}

	case reflect.Slice:
		sliceItemType := outputType.Elem().Kind()

		switch sliceItemType {
		case reflect.Uint8:
			return reflect.ValueOf(input), nil
		}

	case reflect.Array:

		// Create new array of the expected type
		newArray := reflect.New(outputType).Elem()
		lengthOfExpectedArray := outputType.Len()
		arrayItemType := outputType.Elem().Kind()

		switch arrayItemType {
		// Byte array
		case reflect.Uint8:

			// Copy bytes
			for i := 0; i < lengthOfExpectedArray; i++ {
				newArray.Index(i).Set(reflect.ValueOf(input[i]))
			}

			return newArray, nil
		}
	}

	return reflect.ValueOf(nil), errors.New("Invalid input type: " + outputType.String())
}

// Converts a Riak Map to a Go Map
func transMapToMap(mapValue reflect.Value, data *riak.Map) error {

	mapKeyType := mapValue.Type().Key().Kind()

	// Initialize the map
	newMap := reflect.MakeMap(mapValue.Type())
	mapValue.Set(newMap)

	for key, val := range data.Registers {

		// Convert key (a string) to the correct reflect.Value
		keyValue, err := bytesToValue([]byte(key), mapValue.Type().Key())

		if err != nil {
			return errors.New("Unknown map key type: " + mapKeyType.String())
		}

		valValue, err := bytesToValue(val, mapValue.Type().Elem())

		if err != nil {
			return errors.New("Unknown map value type")
		}

		// Save value to the Go map
		mapValue.SetMapIndex(keyValue, valValue)
	}

	return nil
}
<|endoftext|>


<commit_before>package action

import (
	"fmt"
	"os"

	"github.com/atotto/clipboard"
	"github.com/fatih/color"
	"github.com/justwatchcom/gopass/qrcon"
	"github.com/justwatchcom/gopass/store"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

// Show the content of a secret file
func (s *Action) Show(c *cli.Context) error {
	name := c.Args().First()
	key := c.Args().Get(1)

	clip := c.Bool("clip")
	force := c.Bool("force")
	qr := c.Bool("qr")

	if err := s.show(c, name, key, clip, force, qr); err != nil {
		return s.exitError(ExitDecrypt, err, "%s", err)
	}
	return nil
}

func (s *Action) show(c *cli.Context, name, key string, clip, force, qr bool) error {
	if name == "" {
		return s.exitError(ExitUsage, nil, "Usage: %s show [name]", s.Name)
	}

	if s.Store.IsDir(name) {
		return s.List(c)
	}

	// auto-fallback to binary files with b64 suffix, if unique
	if !s.Store.Exists(name) && s.Store.Exists(name+BinarySuffix) {
		name += BinarySuffix
	}

	var content []byte
	var err error

	switch {
	case key != "":
		content, err = s.Store.GetKey(name, key)
		if err != nil {
			if errors.Cause(err) == store.ErrYAMLValueUnsupported {
				return s.exitError(ExitUnsupported, err, "Can not show nested key directly. Use 'gopass show %s'", name)
			}
			return s.exitError(ExitUnknown, err, "failed to retrieve key '%s' from '%s': %s", key, name, err)
		}
		if clip {
			return s.copyToClipboard(name, content)
		}
	case qr:
		content, err = s.Store.GetFirstLine(name)
		if err != nil {
			return s.exitError(ExitDecrypt, err, "failed to retrieve secret '%s': %s", name, err)
		}
		qr, err := qrcon.QRCode(string(content))
		if err != nil {
			return s.exitError(ExitUnknown, err, "failed to encode '%s' as QR: %s", name, err)
		}
		fmt.Println(qr)
		return nil
	case clip:
		content, err = s.Store.GetFirstLine(name)
		if err != nil {
			return s.exitError(ExitDecrypt, err, "failed to retrieve secret '%s': %s", name, err)
		}
		return s.copyToClipboard(name, content)
	default:
		if s.Store.SafeContent() && !force {
			content, err = s.Store.GetBody(name)
		} else {
			content, err = s.Store.Get(name)
		}
		if err != nil {
			if err != store.ErrNotFound {
				return s.exitError(ExitUnknown, err, "failed to retrieve secret '%s': %s", name, err)
			}
			color.Yellow("Entry '%s' not found. Starting search...", name)
			if err := s.Find(c); err != nil {
				return s.exitError(ExitNotFound, err, "%s", err)
			}
			os.Exit(ExitNotFound)
		}
	}

	fmt.Println(color.YellowString(string(content)))

	return nil
}

func (s *Action) copyToClipboard(name string, content []byte) error {
	if err := clipboard.WriteAll(string(content)); err != nil {
		return errors.Wrapf(err, "failed to write to clipboard")
	}

	if err := clearClipboard(content, s.Store.ClipTimeout()); err != nil {
		return errors.Wrapf(err, "failed to clear clipboard")
	}

	fmt.Printf("Copied %s to clipboard. Will clear in %d seconds.\n", color.YellowString(name), s.Store.ClipTimeout())
	return nil
}
<commit_msg>Trim any trailing newlines or carriage returns in show output (#296)<commit_after>package action

import (
	"fmt"
	"os"
	"strings"

	"github.com/atotto/clipboard"
	"github.com/fatih/color"
	"github.com/justwatchcom/gopass/qrcon"
	"github.com/justwatchcom/gopass/store"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)

// Show the content of a secret file
func (s *Action) Show(c *cli.Context) error {
	name := c.Args().First()
	key := c.Args().Get(1)

	clip := c.Bool("clip")
	force := c.Bool("force")
	qr := c.Bool("qr")

	if err := s.show(c, name, key, clip, force, qr); err != nil {
		return s.exitError(ExitDecrypt, err, "%s", err)
	}
	return nil
}

func (s *Action) show(c *cli.Context, name, key string, clip, force, qr bool) error {
	if name == "" {
		return s.exitError(ExitUsage, nil, "Usage: %s show [name]", s.Name)
	}

	if s.Store.IsDir(name) {
		return s.List(c)
	}

	// auto-fallback to binary files with b64 suffix, if unique
	if !s.Store.Exists(name) && s.Store.Exists(name+BinarySuffix) {
		name += BinarySuffix
	}

	var content []byte
	var err error

	switch {
	case key != "":
		content, err = s.Store.GetKey(name, key)
		if err != nil {
			if errors.Cause(err) == store.ErrYAMLValueUnsupported {
				return s.exitError(ExitUnsupported, err, "Can not show nested key directly. Use 'gopass show %s'", name)
			}
			return s.exitError(ExitUnknown, err, "failed to retrieve key '%s' from '%s': %s", key, name, err)
		}
		if clip {
			return s.copyToClipboard(name, content)
		}
	case qr:
		content, err = s.Store.GetFirstLine(name)
		if err != nil {
			return s.exitError(ExitDecrypt, err, "failed to retrieve secret '%s': %s", name, err)
		}
		qr, err := qrcon.QRCode(string(content))
		if err != nil {
			return s.exitError(ExitUnknown, err, "failed to encode '%s' as QR: %s", name, err)
		}
		fmt.Println(qr)
		return nil
	case clip:
		content, err = s.Store.GetFirstLine(name)
		if err != nil {
			return s.exitError(ExitDecrypt, err, "failed to retrieve secret '%s': %s", name, err)
		}
		return s.copyToClipboard(name, content)
	default:
		if s.Store.SafeContent() && !force {
			content, err = s.Store.GetBody(name)
		} else {
			content, err = s.Store.Get(name)
		}
		if err != nil {
			if err != store.ErrNotFound {
				return s.exitError(ExitUnknown, err, "failed to retrieve secret '%s': %s", name, err)
			}
			color.Yellow("Entry '%s' not found. Starting search...", name)
			if err := s.Find(c); err != nil {
				return s.exitError(ExitNotFound, err, "%s", err)
			}
			os.Exit(ExitNotFound)
		}
	}

	fmt.Println(color.YellowString(strings.TrimRight(string(content), "\r\n")))

	return nil
}

func (s *Action) copyToClipboard(name string, content []byte) error {
	if err := clipboard.WriteAll(string(content)); err != nil {
		return errors.Wrapf(err, "failed to write to clipboard")
	}

	if err := clearClipboard(content, s.Store.ClipTimeout()); err != nil {
		return errors.Wrapf(err, "failed to clear clipboard")
	}

	fmt.Printf("Copied %s to clipboard. Will clear in %d seconds.\n", color.YellowString(name), s.Store.ClipTimeout())
	return nil
}
<|endoftext|>


<commit_before>package yum

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
)

// global registry of known backends
var g_backends = make(map[string]func(repo *Repository) (Backend, error))

// NewBackend returns a new backend of type "backend"
func NewBackend(backend string, repo *Repository) (Backend, error) {
	factory, ok := g_backends[backend]
	if !ok {
		return nil, fmt.Errorf("yum: no such backend [%s]", backend)
	}
	return factory(repo)
}

// Backend queries a YUM DB repository
type Backend interface {

	// YumDataType returns the ID for the data type as used in the repomd.xml file
	YumDataType() string

	// Download the DB from server
	GetLatestDB(url string) error

	// Check whether the DB is there
	HasDB() bool

	// Load loads the DB
	LoadDB() error

	// FindLatestMatchingName locats a package by name, returns the latest available version.
	FindLatestMatchingName(name, version, release string) (*Package, error)

	// FindLatestMatchingRequire locates a package providing a given functionality.
	FindLatestMatchingRequire(requirement string) (*Package, error)

	// GetPackages returns all the packages known by a YUM repository
	GetPackages() []*Package
}

// Repository represents a YUM repository with all associated metadata.
type Repository struct {
	msg            *logger.Logger
	Name           string
	RepoUrl        string
	RepoMdUrl      string
	LocalRepoMdXml string
	CacheDir       string
	Backends       []string
	Backend        Backend
}

// NewRepository create a new Repository with name and from url.
func NewRepository(name, url, cachedir string, backends []string, setupBackend, checkForUpdates bool) (*Repository, error) {

	repo := Repository{
		msg:            logger.NewLogger("yum", logger.INFO, os.Stdout),
		Name:           name,
		RepoUrl:        url,
		RepoMdUrl:      url + "/repodata/repomd.xml",
		LocalRepoMdXml: filepath.Join(cachedir, "repomd.xml"),
		CacheDir:       cachedir,
		Backends:       make([]string, len(backends)),
	}
	copy(repo.Backends, backends)

	err := os.MkdirAll(cachedir, 0644)
	if err != nil {
		return nil, err
	}

	// load appropriate backend if requested
	if setupBackend {
		if checkForUpdates {
			err = repo.setupBackendFromRemote()
			if err != nil {
				return nil, err
			}
		} else {
			err = repo.setupBackendFromLocal()
			if err != nil {
				return nil, err
			}
		}
	}
	return &repo, err
}

// FindLatestMatchingName locats a package by name, returns the latest available version.
func (repo *Repository) FindLatestMatchingName(name, version, release string) (*Package, error) {
	return repo.Backend.FindLatestMatchingName(name, version, release)
}

// FindLatestMatchingRequire locates a package providing a given functionality.
func (repo *Repository) FindLatestMatchingRequire(requirement string) (*Package, error) {
	return repo.Backend.FindLatestMatchingRequire(requirement)
}

// GetPackages returns all the packages known by a YUM repository
func (repo *Repository) GetPackages() []*Package {
	return repo.Backend.GetPackages()
}

// setupBackendFromRemote checks which backend should be used and updates the DB files.
func (repo *Repository) setupBackendFromRemote() error {
	repo.msg.Infof("setupBackendFromRemote...\n")
	var err error
	var backend Backend
	// get repo metadata with list of available files
	remotedata, err := repo.remoteMetadata()
	if err != nil {
		return err
	}

	remotemd, err := repo.checkRepoMD(remotedata)
	if err != nil {
		return err
	}

	localdata, err := repo.localMetadata()
	if err != nil {
		return err
	}

	localmd, err := repo.checkRepoMD(localdata)
	if err != nil {
		return err
	}

	for _, bname := range repo.Backends {
		repo.msg.Infof("checking availability of backend [%s]\n", bname)
		ba, err := NewBackend(bname, repo)
		if err != nil {
			continue
		}

		rrepomd, ok := remotemd[ba.YumDataType()]
		if !ok {
			repo.msg.Warnf("remote repository does not provide [%s] DB\n", bname)
			continue
		}

		// a priori a match
		backend = ba
		repo.Backend = backend

		lrepomd, ok := localmd[ba.YumDataType()]
		if !ok {
			// doesn't matter, we download the DB in any case
		}

		if !repo.Backend.HasDB() || rrepomd.Timestamp.After(lrepomd.Timestamp) {
			// we need to update the DB
			url := repo.RepoUrl + "/" + rrepomd.Location
			repo.msg.Infof("updating the RPM database for %s\n", bname)
			err = repo.Backend.GetLatestDB(url)
			if err != nil {
				repo.msg.Warnf("problem updating RPM database for backend [%s]: %v\n", bname, err)
				err = nil
				backend = nil
				repo.Backend = nil
				continue
			}
			// save metadata to local repomd file
			err = ioutil.WriteFile(repo.LocalRepoMdXml, remotedata, 0644)
			if err != nil {
				repo.msg.Warnf("problem updating local repomd.xml file for backend [%s]: %v\n", bname, err)
				err = nil
				backend = nil
				repo.Backend = nil
				continue
			}
		}

		// load data necessary for the backend
		err = repo.Backend.LoadDB()
		if err != nil {
			repo.msg.Warnf("problem loading data for backend [%s]: %v\n", bname, err)
			err = nil
			backend = nil
			repo.Backend = nil
			continue
		}

		// stop at first one found
		break
	}

	if backend == nil {
		repo.msg.Errorf("No valid backend found\n")
		return fmt.Errorf("No valid backend found")
	}

	repo.msg.Infof("repository [%s] - chosen backend [%T]\n", repo.Name, repo.Backend)
	return err
}

func (repo *Repository) setupBackendFromLocal() error {
	repo.msg.Infof("setupBackendFromLocal...\n")
	var err error
	data, err := repo.localMetadata()
	if err != nil {
		return err
	}

	md, err := repo.checkRepoMD(data)
	if err != nil {
		return err
	}

	var backend Backend
	for _, bname := range repo.Backends {
		repo.msg.Infof("checking availability of backend [%s]\n", bname)
		ba, err := NewBackend(bname, repo)
		if err != nil {
			continue
		}
		_ /*repomd*/, ok := md[ba.YumDataType()]
		if !ok {
			repo.msg.Warnf("local repository does not provide [%s] DB\n", bname)
			continue
		}

		// a priori a match
		backend = ba
		repo.Backend = backend

		// loading data necessary for the backend
		err = repo.Backend.LoadDB()
		if err != nil {
			repo.msg.Warnf("problem loading data for backend [%s]: %v\n", bname, err)
			err = nil
			backend = nil
			repo.Backend = nil
			continue
		}

		// stop at first one found.
		break
	}

	if backend == nil {
		repo.msg.Errorf("No valid backend found\n")
		return fmt.Errorf("No valid backend found")
	}

	repo.msg.Infof("repository [%s] - chosen backend [%T]\n", repo.Name, repo.Backend)
	return err
}

// remoteMetadata retrieves the repo metadata file content
func (repo *Repository) remoteMetadata() ([]byte, error) {
	resp, err := http.Get(repo.RepoMdUrl)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, resp.Body)
	if err != nil && err != io.EOF {
		return nil, err
	}
	return buf.Bytes(), err
}

// localMetadata retrieves the repo metadata from the repomd file
func (repo *Repository) localMetadata() ([]byte, error) {
	if !path_exists(repo.LocalRepoMdXml) {
		return nil, nil
	}
	f, err := os.Open(repo.LocalRepoMdXml)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, f)
	if err != nil && err != io.EOF {
		return nil, err
	}
	return buf.Bytes(), err
}

// checkRepoMD parses the Repository metadata XML content
func (repo *Repository) checkRepoMD(data []byte) (map[string]RepoMD, error) {

	if len(data) <= 0 {
		return nil, nil
	}

	type xmlTree struct {
		XMLName xml.Name `xml:"repomd"`
		Data    []struct {
			Type     string `xml:"type,attr"`
			Checksum string `xml:"checksum"`
			Location struct {
				Href string `xml:"href,attr"`
			} `xml:"location"`
			Timestamp float64 `xml:"timestamp"`
		}
	}

	var tree xmlTree
	err := xml.Unmarshal(data, &tree)
	if err != nil {
		return nil, err
	}

	db := make(map[string]RepoMD)
	for _, data := range tree.Data {
		sec := int64(math.Floor(data.Timestamp))
		nsec := int64((data.Timestamp - float64(sec)) * 1e9)
		db[data.Type] = RepoMD{
			Checksum:  data.Checksum,
			Timestamp: time.Unix(sec, nsec),
			Location:  data.Location.Href,
		}
		repo.msg.Infof(">>> %s: %v\n", data.Type, db[data.Type])
	}
	return db, err
}

type RepoMD struct {
	Checksum  string
	Timestamp time.Time
	Location  string
}

// EOF
<commit_msg>yum: debug printouts<commit_after>package yum

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gonuts/logger"
)

// global registry of known backends
var g_backends = make(map[string]func(repo *Repository) (Backend, error))

// NewBackend returns a new backend of type "backend"
func NewBackend(backend string, repo *Repository) (Backend, error) {
	factory, ok := g_backends[backend]
	if !ok {
		return nil, fmt.Errorf("yum: no such backend [%s]", backend)
	}
	return factory(repo)
}

// Backend queries a YUM DB repository
type Backend interface {

	// YumDataType returns the ID for the data type as used in the repomd.xml file
	YumDataType() string

	// Download the DB from server
	GetLatestDB(url string) error

	// Check whether the DB is there
	HasDB() bool

	// Load loads the DB
	LoadDB() error

	// FindLatestMatchingName locats a package by name, returns the latest available version.
	FindLatestMatchingName(name, version, release string) (*Package, error)

	// FindLatestMatchingRequire locates a package providing a given functionality.
	FindLatestMatchingRequire(requirement string) (*Package, error)

	// GetPackages returns all the packages known by a YUM repository
	GetPackages() []*Package
}

// Repository represents a YUM repository with all associated metadata.
type Repository struct {
	msg            *logger.Logger
	Name           string
	RepoUrl        string
	RepoMdUrl      string
	LocalRepoMdXml string
	CacheDir       string
	Backends       []string
	Backend        Backend
}

// NewRepository create a new Repository with name and from url.
func NewRepository(name, url, cachedir string, backends []string, setupBackend, checkForUpdates bool) (*Repository, error) {

	repo := Repository{
		msg:            logger.NewLogger("yum", logger.INFO, os.Stdout),
		Name:           name,
		RepoUrl:        url,
		RepoMdUrl:      url + "/repodata/repomd.xml",
		LocalRepoMdXml: filepath.Join(cachedir, "repomd.xml"),
		CacheDir:       cachedir,
		Backends:       make([]string, len(backends)),
	}
	copy(repo.Backends, backends)

	err := os.MkdirAll(cachedir, 0644)
	if err != nil {
		return nil, err
	}

	// load appropriate backend if requested
	if setupBackend {
		if checkForUpdates {
			err = repo.setupBackendFromRemote()
			if err != nil {
				return nil, err
			}
		} else {
			err = repo.setupBackendFromLocal()
			if err != nil {
				return nil, err
			}
		}
	}
	return &repo, err
}

// FindLatestMatchingName locats a package by name, returns the latest available version.
func (repo *Repository) FindLatestMatchingName(name, version, release string) (*Package, error) {
	return repo.Backend.FindLatestMatchingName(name, version, release)
}

// FindLatestMatchingRequire locates a package providing a given functionality.
func (repo *Repository) FindLatestMatchingRequire(requirement string) (*Package, error) {
	return repo.Backend.FindLatestMatchingRequire(requirement)
}

// GetPackages returns all the packages known by a YUM repository
func (repo *Repository) GetPackages() []*Package {
	return repo.Backend.GetPackages()
}

// setupBackendFromRemote checks which backend should be used and updates the DB files.
func (repo *Repository) setupBackendFromRemote() error {
	repo.msg.Infof("setupBackendFromRemote...\n")
	var err error
	var backend Backend
	// get repo metadata with list of available files
	remotedata, err := repo.remoteMetadata()
	if err != nil {
		return err
	}

	repo.msg.Infof("...checkRepoMD [remote]...\n")
	remotemd, err := repo.checkRepoMD(remotedata)
	if err != nil {
		return err
	}
	repo.msg.Infof("...checkRepoMD [remote]...: %v\n", remotemd)

	localdata, err := repo.localMetadata()
	if err != nil {
		return err
	}

	localmd, err := repo.checkRepoMD(localdata)
	if err != nil {
		return err
	}

	for _, bname := range repo.Backends {
		repo.msg.Infof("checking availability of backend [%s]\n", bname)
		ba, err := NewBackend(bname, repo)
		if err != nil {
			continue
		}

		rrepomd, ok := remotemd[ba.YumDataType()]
		if !ok {
			repo.msg.Warnf("remote repository does not provide [%s] DB\n", bname)
			continue
		}

		// a priori a match
		backend = ba
		repo.Backend = backend

		lrepomd, ok := localmd[ba.YumDataType()]
		if !ok {
			// doesn't matter, we download the DB in any case
		}

		if !repo.Backend.HasDB() || rrepomd.Timestamp.After(lrepomd.Timestamp) {
			// we need to update the DB
			url := repo.RepoUrl + "/" + rrepomd.Location
			repo.msg.Infof("updating the RPM database for %s\n", bname)
			err = repo.Backend.GetLatestDB(url)
			if err != nil {
				repo.msg.Warnf("problem updating RPM database for backend [%s]: %v\n", bname, err)
				err = nil
				backend = nil
				repo.Backend = nil
				continue
			}
			// save metadata to local repomd file
			err = ioutil.WriteFile(repo.LocalRepoMdXml, remotedata, 0644)
			if err != nil {
				repo.msg.Warnf("problem updating local repomd.xml file for backend [%s]: %v\n", bname, err)
				err = nil
				backend = nil
				repo.Backend = nil
				continue
			}
		}

		// load data necessary for the backend
		err = repo.Backend.LoadDB()
		if err != nil {
			repo.msg.Warnf("problem loading data for backend [%s]: %v\n", bname, err)
			err = nil
			backend = nil
			repo.Backend = nil
			continue
		}

		// stop at first one found
		break
	}

	if backend == nil {
		repo.msg.Errorf("No valid backend found\n")
		return fmt.Errorf("No valid backend found")
	}

	repo.msg.Infof("repository [%s] - chosen backend [%T]\n", repo.Name, repo.Backend)
	return err
}

func (repo *Repository) setupBackendFromLocal() error {
	repo.msg.Infof("setupBackendFromLocal...\n")
	var err error
	data, err := repo.localMetadata()
	if err != nil {
		return err
	}

	md, err := repo.checkRepoMD(data)
	if err != nil {
		return err
	}

	var backend Backend
	for _, bname := range repo.Backends {
		repo.msg.Infof("checking availability of backend [%s]\n", bname)
		ba, err := NewBackend(bname, repo)
		if err != nil {
			continue
		}
		_ /*repomd*/, ok := md[ba.YumDataType()]
		if !ok {
			repo.msg.Warnf("local repository does not provide [%s] DB\n", bname)
			continue
		}

		// a priori a match
		backend = ba
		repo.Backend = backend

		// loading data necessary for the backend
		err = repo.Backend.LoadDB()
		if err != nil {
			repo.msg.Warnf("problem loading data for backend [%s]: %v\n", bname, err)
			err = nil
			backend = nil
			repo.Backend = nil
			continue
		}

		// stop at first one found.
		break
	}

	if backend == nil {
		repo.msg.Errorf("No valid backend found\n")
		return fmt.Errorf("No valid backend found")
	}

	repo.msg.Infof("repository [%s] - chosen backend [%T]\n", repo.Name, repo.Backend)
	return err
}

// remoteMetadata retrieves the repo metadata file content
func (repo *Repository) remoteMetadata() ([]byte, error) {
	resp, err := http.Get(repo.RepoMdUrl)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, resp.Body)
	if err != nil && err != io.EOF {
		return nil, err
	}
	return buf.Bytes(), err
}

// localMetadata retrieves the repo metadata from the repomd file
func (repo *Repository) localMetadata() ([]byte, error) {
	if !path_exists(repo.LocalRepoMdXml) {
		return nil, nil
	}
	f, err := os.Open(repo.LocalRepoMdXml)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, f)
	if err != nil && err != io.EOF {
		return nil, err
	}
	return buf.Bytes(), err
}

// checkRepoMD parses the Repository metadata XML content
func (repo *Repository) checkRepoMD(data []byte) (map[string]RepoMD, error) {

	if len(data) <= 0 {
		return nil, nil
	}

	type xmlTree struct {
		XMLName xml.Name `xml:"repomd"`
		Data    []struct {
			Type     string `xml:"type,attr"`
			Checksum string `xml:"checksum"`
			Location struct {
				Href string `xml:"href,attr"`
			} `xml:"location"`
			Timestamp float64 `xml:"timestamp"`
		}
	}

	var tree xmlTree
	err := xml.Unmarshal(data, &tree)
	if err != nil {
		return nil, err
	}

	db := make(map[string]RepoMD)
	for _, data := range tree.Data {
		sec := int64(math.Floor(data.Timestamp))
		nsec := int64((data.Timestamp - float64(sec)) * 1e9)
		db[data.Type] = RepoMD{
			Checksum:  data.Checksum,
			Timestamp: time.Unix(sec, nsec),
			Location:  data.Location.Href,
		}
		repo.msg.Infof(">>> %s: %v\n", data.Type, db[data.Type])
	}
	return db, err
}

type RepoMD struct {
	Checksum  string
	Timestamp time.Time
	Location  string
}

// EOF
<|endoftext|>


<commit_before>package zapsentry

import (
	"fmt"
	"strings"

	"github.com/getsentry/raven-go"
	"go.uber.org/zap/zapcore"
)

//
// Levels
//

var zapLevelToRavenSeverity = map[zapcore.Level]raven.Severity{
	zapcore.DebugLevel:  raven.DEBUG,
	zapcore.InfoLevel:   raven.INFO,
	zapcore.WarnLevel:   raven.WARNING,
	zapcore.ErrorLevel:  raven.ERROR,
	zapcore.DPanicLevel: raven.FATAL,
	zapcore.PanicLevel:  raven.FATAL,
	zapcore.FatalLevel:  raven.FATAL,
}

//
// Environment
//

type Environment string

const (
	EnvDevelopment Environment = "development"
	EnvProduction  Environment = "production"
)

//
// Significant field keys
//

const TagPrefix = "#"

const (
	EventIDKey    = "event_id"
	ProjectKey    = "project"
	TimestampKey  = "timestamp"
	LoggerKey     = "logger"
	PlatformKey   = "platform"
	CulpritKey    = "culprit"
	ServerNameKey = "server_name"
	ErrorKey      = "error"
)

//
// Core options
//

type Option func(*Core)

func SetStackTraceSkip(skip int) Option {
	return func(core *Core) {
		core.stSkip = skip
	}
}

func SetStackTraceContext(context int) Option {
	return func(core *Core) {
		core.stContext = context
	}
}

func SetStackTracePackagePrefixes(prefixes []string) Option {
	return func(core *Core) {
		core.stPackagePrefixes = prefixes
	}
}

func SetWaitEnabler(enab zapcore.LevelEnabler) Option {
	return func(core *Core) {
		core.wait = enab
	}
}

//
// Core
//

const (
	DefaultEnvironment       = EnvProduction
	DefaultStackTraceContext = 5
	DefaultWaitEnabler       = zapcore.PanicLevel
)

type Core struct {
	zapcore.LevelEnabler

	client *raven.Client

	stSkip            int
	stContext         int
	stPackagePrefixes []string

	wait zapcore.LevelEnabler

	fields []zapcore.Field
}

func NewCore(enab zapcore.LevelEnabler, client *raven.Client, options ...Option) *Core {
	core := &Core{
		LevelEnabler: enab,
		client:       client,
		stContext:    DefaultStackTraceContext,
		wait:         DefaultWaitEnabler,
	}

	for _, opt := range options {
		opt(core)
	}

	return core
}

func (core *Core) With(fields []zapcore.Field) zapcore.Core {
	// Clone core.
	clone := *core

	// Clone and append fields.
	clone.fields = make([]zapcore.Field, len(core.fields)+len(fields))
	copy(clone.fields, core.fields)
	copy(clone.fields[len(core.fields):], fields)

	// Done.
	return &clone
}

func (core *Core) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
	if core.Enabled(entry.Level) {
		return checked.AddCore(entry, core)
	}
	return checked
}

func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
	// Create a Raven packet.
	packet := raven.NewPacket(entry.Message)

	// Process entry.
	packet.Level = zapLevelToRavenSeverity[entry.Level]
	packet.Timestamp = raven.Timestamp(entry.Time)
	packet.Logger = entry.LoggerName

	// Process fields.
	encoder := zapcore.NewMapObjectEncoder()
	var err error

	processField := func(field zapcore.Field) {
		// Check for significant keys.
		switch field.Key {
		case EventIDKey:
			packet.EventID = field.String

		case ProjectKey:
			packet.Project = field.String

		case PlatformKey:
			packet.Platform = field.String

		case CulpritKey:
			packet.Culprit = field.String

		case ServerNameKey:
			packet.ServerName = field.String

		case ErrorKey:
			if ex, ok := field.Interface.(error); ok {
				err = ex
			} else {
				field.AddTo(encoder)
			}

		default:
			// Add to the encoder in case this is not a significant key.
			field.AddTo(encoder)
		}
	}

	// Process core fields first.
	for _, field := range core.fields {
		processField(field)
	}

	// Then process the fields passed directly.
	// These can be then used to overwrite the core fields.
	for _, field := range fields {
		processField(field)
	}

	tags := make(map[string]string)
	extra := make(map[string]interface{})

	for key, value := range encoder.Fields {
		if strings.HasPrefix(key, TagPrefix) {
			key = key[len(TagPrefix):]
			if v, ok := value.(string); ok {
				tags[key] = v
			} else {
				tags[key] = fmt.Sprintf("%v", value)
			}
		} else {
			extra[key] = value
		}
	}

	if len(tags) != 0 {
		packet.AddTags(tags)
	}
	if len(extra) != 0 {
		packet.Extra = extra
	}

	// In case an error object is present, create an exception.
	// Capture the stack trace in any case.
	stackTrace := raven.NewStacktrace(core.stSkip, core.stContext, core.stPackagePrefixes)
	if err != nil {
		packet.Interfaces = append(packet.Interfaces, raven.NewException(err, stackTrace))
	} else {
		packet.Interfaces = append(packet.Interfaces, stackTrace)
	}

	// Capture the packet.
	_, errCh := core.client.Capture(packet, nil)

	if core.wait.Enabled(entry.Level) {
		return <-errCh
	}
	return nil
}

func (core *Core) Sync() error {
	core.client.Wait()
	return nil
}
<commit_msg>zapsentry: Add support for HTTP interface<commit_after>package zapsentry

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/getsentry/raven-go"
	"go.uber.org/zap/zapcore"
)

//
// Levels
//

var zapLevelToRavenSeverity = map[zapcore.Level]raven.Severity{
	zapcore.DebugLevel:  raven.DEBUG,
	zapcore.InfoLevel:   raven.INFO,
	zapcore.WarnLevel:   raven.WARNING,
	zapcore.ErrorLevel:  raven.ERROR,
	zapcore.DPanicLevel: raven.FATAL,
	zapcore.PanicLevel:  raven.FATAL,
	zapcore.FatalLevel:  raven.FATAL,
}

//
// Environment
//

type Environment string

const (
	EnvDevelopment Environment = "development"
	EnvProduction  Environment = "production"
)

//
// Significant field keys
//

const TagPrefix = "#"

const (
	EventIDKey     = "event_id"
	ProjectKey     = "project"
	TimestampKey   = "timestamp"
	LoggerKey      = "logger"
	PlatformKey    = "platform"
	CulpritKey     = "culprit"
	ServerNameKey  = "server_name"
	ErrorKey       = "error"
	HTTPRequestKey = "http_request"
)

//
// Core options
//

type Option func(*Core)

func SetStackTraceSkip(skip int) Option {
	return func(core *Core) {
		core.stSkip = skip
	}
}

func SetStackTraceContext(context int) Option {
	return func(core *Core) {
		core.stContext = context
	}
}

func SetStackTracePackagePrefixes(prefixes []string) Option {
	return func(core *Core) {
		core.stPackagePrefixes = prefixes
	}
}

func SetWaitEnabler(enab zapcore.LevelEnabler) Option {
	return func(core *Core) {
		core.wait = enab
	}
}

//
// Core
//

const (
	DefaultEnvironment       = EnvProduction
	DefaultStackTraceContext = 5
	DefaultWaitEnabler       = zapcore.PanicLevel
)

type Core struct {
	zapcore.LevelEnabler

	client *raven.Client

	stSkip            int
	stContext         int
	stPackagePrefixes []string

	wait zapcore.LevelEnabler

	fields []zapcore.Field
}

func NewCore(enab zapcore.LevelEnabler, client *raven.Client, options ...Option) *Core {
	core := &Core{
		LevelEnabler: enab,
		client:       client,
		stContext:    DefaultStackTraceContext,
		wait:         DefaultWaitEnabler,
	}

	for _, opt := range options {
		opt(core)
	}

	return core
}

func (core *Core) With(fields []zapcore.Field) zapcore.Core {
	// Clone core.
	clone := *core

	// Clone and append fields.
	clone.fields = make([]zapcore.Field, len(core.fields)+len(fields))
	copy(clone.fields, core.fields)
	copy(clone.fields[len(core.fields):], fields)

	// Done.
	return &clone
}

func (core *Core) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
	if core.Enabled(entry.Level) {
		return checked.AddCore(entry, core)
	}
	return checked
}

func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
	// Create a Raven packet.
	packet := raven.NewPacket(entry.Message)

	// Process entry.
	packet.Level = zapLevelToRavenSeverity[entry.Level]
	packet.Timestamp = raven.Timestamp(entry.Time)
	packet.Logger = entry.LoggerName

	// Process fields.
	encoder := zapcore.NewMapObjectEncoder()

	// When set, relevant Sentry interfaces are added.
	var (
		err error
		req *http.Request
	)

	processField := func(field zapcore.Field) {
		// Check for significant keys.
		switch field.Key {
		case EventIDKey:
			packet.EventID = field.String

		case ProjectKey:
			packet.Project = field.String

		case PlatformKey:
			packet.Platform = field.String

		case CulpritKey:
			packet.Culprit = field.String

		case ServerNameKey:
			packet.ServerName = field.String

		case ErrorKey:
			if ex, ok := field.Interface.(error); ok {
				err = ex
			} else {
				field.AddTo(encoder)
			}

		case HTTPRequestKey:
			if r, ok := field.Interface.(*http.Request); ok {
				req = r
			} else {
				field.AddTo(encoder)
			}

		default:
			// Add to the encoder in case this is not a significant key.
			field.AddTo(encoder)
		}
	}

	// Process core fields first.
	for _, field := range core.fields {
		processField(field)
	}

	// Then process the fields passed directly.
	// These can be then used to overwrite the core fields.
	for _, field := range fields {
		processField(field)
	}

	tags := make(map[string]string)
	extra := make(map[string]interface{})

	for key, value := range encoder.Fields {
		if strings.HasPrefix(key, TagPrefix) {
			key = key[len(TagPrefix):]
			if v, ok := value.(string); ok {
				tags[key] = v
			} else {
				tags[key] = fmt.Sprintf("%v", value)
			}
		} else {
			extra[key] = value
		}
	}

	if len(tags) != 0 {
		packet.AddTags(tags)
	}
	if len(extra) != 0 {
		packet.Extra = extra
	}

	// In case an error object is present, create an exception.
	// Capture the stack trace in any case.
	stackTrace := raven.NewStacktrace(core.stSkip, core.stContext, core.stPackagePrefixes)
	if err != nil {
		packet.Interfaces = append(packet.Interfaces, raven.NewException(err, stackTrace))
	} else {
		packet.Interfaces = append(packet.Interfaces, stackTrace)
	}

	// In case an HTTP request is present, add the HTTP interface.
	if req != nil {
		packet.Interfaces = append(packet.Interfaces, raven.NewHttp(req))
	}

	// Capture the packet.
	_, errCh := core.client.Capture(packet, nil)

	if core.wait.Enabled(entry.Level) {
		return <-errCh
	}
	return nil
}

func (core *Core) Sync() error {
	core.client.Wait()
	return nil
}
<|endoftext|>


<commit_before>package lua

import (
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"
	"time"
)

func field(l *State, key string, def int) int {
	l.Field(-1, key)
	r, ok := l.ToInteger(-1)
	if !ok {
		if def < 0 {
			Errorf(l, "field '%s' missing in date table", key)
		}
		r = def
	}
	l.Pop(1)
	return r
}

var osLibrary = []RegistryFunction{
	{"clock", clock},
	// {"date", os_date},
	{"difftime", func(l *State) int {
		l.PushNumber(time.Unix(int64(CheckNumber(l, 1)), 0).Sub(time.Unix(int64(OptNumber(l, 2, 0)), 0)).Seconds())
		return 1
	}},

	// From the Lua manual:
	// "This function is equivalent to the ISO C function system"
	// https://www.lua.org/manual/5.2/manual.html#pdf-os.execute
	{"execute", func(l *State) int {
		c := OptString(l, 1, "")

		if c == "" {
			// Check whether "sh" is available on the system.
			err := exec.Command("sh").Run()
			l.PushBoolean(err == nil)
			return 1
		}

		terminatedSuccessfully := true
		terminationReason := "exit"
		terminationData := 0

		// Create the command.
		cmd := exec.Command("sh", "-c", c)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		// Run the command.
		if err := cmd.Run(); err != nil {
			if err != nil {
				terminatedSuccessfully = false
				terminationReason = "exit"
				terminationData = 1

				if exiterr, ok := err.(*exec.ExitError); ok {
					if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
						if status.Signaled() {
							terminationReason = "signal"
							terminationData = int(status.Signal())
						} else {
							terminationData = status.ExitStatus()
						}
					} else {
						// Unsupported system?
					}
				} else {
					// From man 3 system:
					// "If a child process could not be created, or its
					// status could not be retrieved, the return value
					// is -1."
					terminationData = -1
				}
			}
		}

		// Deal with the return values.
		if terminatedSuccessfully {
			l.PushBoolean(true)
		} else {
			l.PushNil()
		}

		l.PushString(terminationReason)
		l.PushInteger(terminationData)

		return 3
	}},
	{"exit", func(l *State) int {
		var status int
		if l.IsBoolean(1) {
			if !l.ToBoolean(1) {
				status = 1
			}
		} else {
			status = OptInteger(l, 1, status)
		}
		// if l.ToBoolean(2) {
		// 	Close(l)
		// }
		os.Exit(status)
		panic("unreachable")
	}},
	{"getenv", func(l *State) int { l.PushString(os.Getenv(CheckString(l, 1))); return 1 }},
	{"remove", func(l *State) int { name := CheckString(l, 1); return FileResult(l, os.Remove(name), name) }},
	{"rename", func(l *State) int { return FileResult(l, os.Rename(CheckString(l, 1), CheckString(l, 2)), "") }},
	// {"setlocale", func(l *State) int {
	// 	op := CheckOption(l, 2, "all", []string{"all", "collate", "ctype", "monetary", "numeric", "time"})
	// 	l.PushString(setlocale([]int{LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}, OptString(l, 1, "")))
	// 	return 1
	// }},
	{"time", func(l *State) int {
		if l.IsNoneOrNil(1) {
			l.PushNumber(float64(time.Now().Unix()))
		} else {
			CheckType(l, 1, TypeTable)
			l.SetTop(1)
			year := field(l, "year", -1) - 1900
			month := field(l, "month", -1) - 1
			day := field(l, "day", -1)
			hour := field(l, "hour", 12)
			min := field(l, "min", 0)
			sec := field(l, "sec", 0)
			// dst := boolField(l, "isdst") // TODO how to use dst?
			l.PushNumber(float64(time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local).Unix()))
		}
		return 1
	}},
	{"tmpname", func(l *State) int {
		f, err := ioutil.TempFile("", "lua_")
		if err != nil {
			Errorf(l, "unable to generate a unique filename")
		}
		defer f.Close()
		l.PushString(f.Name())
		return 1
	}},
}

// OSOpen opens the os library. Usually passed to Require.
func OSOpen(l *State) int {
	NewLibrary(l, osLibrary)
	return 1
}
<commit_msg>Remove redundant check in the os.execute code.<commit_after>package lua

import (
	"io/ioutil"
	"os"
	"os/exec"
	"syscall"
	"time"
)

func field(l *State, key string, def int) int {
	l.Field(-1, key)
	r, ok := l.ToInteger(-1)
	if !ok {
		if def < 0 {
			Errorf(l, "field '%s' missing in date table", key)
		}
		r = def
	}
	l.Pop(1)
	return r
}

var osLibrary = []RegistryFunction{
	{"clock", clock},
	// {"date", os_date},
	{"difftime", func(l *State) int {
		l.PushNumber(time.Unix(int64(CheckNumber(l, 1)), 0).Sub(time.Unix(int64(OptNumber(l, 2, 0)), 0)).Seconds())
		return 1
	}},

	// From the Lua manual:
	// "This function is equivalent to the ISO C function system"
	// https://www.lua.org/manual/5.2/manual.html#pdf-os.execute
	{"execute", func(l *State) int {
		c := OptString(l, 1, "")

		if c == "" {
			// Check whether "sh" is available on the system.
			err := exec.Command("sh").Run()
			l.PushBoolean(err == nil)
			return 1
		}

		terminatedSuccessfully := true
		terminationReason := "exit"
		terminationData := 0

		// Create the command.
		cmd := exec.Command("sh", "-c", c)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		// Run the command.
		if err := cmd.Run(); err != nil {
			terminatedSuccessfully = false
			terminationReason = "exit"
			terminationData = 1

			if exiterr, ok := err.(*exec.ExitError); ok {
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					if status.Signaled() {
						terminationReason = "signal"
						terminationData = int(status.Signal())
					} else {
						terminationData = status.ExitStatus()
					}
				} else {
					// Unsupported system?
				}
			} else {
				// From man 3 system:
				// "If a child process could not be created, or its
				// status could not be retrieved, the return value
				// is -1."
				terminationData = -1
			}
		}

		// Deal with the return values.
		if terminatedSuccessfully {
			l.PushBoolean(true)
		} else {
			l.PushNil()
		}

		l.PushString(terminationReason)
		l.PushInteger(terminationData)

		return 3
	}},
	{"exit", func(l *State) int {
		var status int
		if l.IsBoolean(1) {
			if !l.ToBoolean(1) {
				status = 1
			}
		} else {
			status = OptInteger(l, 1, status)
		}
		// if l.ToBoolean(2) {
		// 	Close(l)
		// }
		os.Exit(status)
		panic("unreachable")
	}},
	{"getenv", func(l *State) int { l.PushString(os.Getenv(CheckString(l, 1))); return 1 }},
	{"remove", func(l *State) int { name := CheckString(l, 1); return FileResult(l, os.Remove(name), name) }},
	{"rename", func(l *State) int { return FileResult(l, os.Rename(CheckString(l, 1), CheckString(l, 2)), "") }},
	// {"setlocale", func(l *State) int {
	// 	op := CheckOption(l, 2, "all", []string{"all", "collate", "ctype", "monetary", "numeric", "time"})
	// 	l.PushString(setlocale([]int{LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}, OptString(l, 1, "")))
	// 	return 1
	// }},
	{"time", func(l *State) int {
		if l.IsNoneOrNil(1) {
			l.PushNumber(float64(time.Now().Unix()))
		} else {
			CheckType(l, 1, TypeTable)
			l.SetTop(1)
			year := field(l, "year", -1) - 1900
			month := field(l, "month", -1) - 1
			day := field(l, "day", -1)
			hour := field(l, "hour", 12)
			min := field(l, "min", 0)
			sec := field(l, "sec", 0)
			// dst := boolField(l, "isdst") // TODO how to use dst?
			l.PushNumber(float64(time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local).Unix()))
		}
		return 1
	}},
	{"tmpname", func(l *State) int {
		f, err := ioutil.TempFile("", "lua_")
		if err != nil {
			Errorf(l, "unable to generate a unique filename")
		}
		defer f.Close()
		l.PushString(f.Name())
		return 1
	}},
}

// OSOpen opens the os library. Usually passed to Require.
func OSOpen(l *State) int {
	NewLibrary(l, osLibrary)
	return 1
}
<|endoftext|>


<commit_before>/*

config is a simple library that manages config set-up for boardgame-util and
friends, reading from config.json and config.SECRET.json files. See boardgame-
util/README.md for more on the structure of config.json files.

Although a number of the details are exposed in this package, generally you
just use Get() and then directly read the values of the returned Config's Dev
and Prod properties.

*/
package config

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

const (
	privateConfigFileName = "config.SECRET.json"
	publicConfigFileName  = "config.PUBLIC.json"
)

//FileNames returns the publicConfig filename and privateConfig filename to
//use given the search path. If dir is a config file itself, loads that (and
//any private component in same directory). Next it interprets dir as a
//directory to search within for any config files. If none are found, walks
//upwards in the directory hierarchy (as long as that's still in $GOPATH)
//until it finds a folder that appears to work. If dir is "", working
//directory is assumed.
func FileNames(dir string) (publicConfig, privateConfig string, err error) {

	if dir == "" {
		dir = "."
	}

	//Try to interpret it as a file
	if public, private, err := fileNamesToUseWithFile(dir); err == nil {
		return public, private, nil
	}

	//Guess it wasn't a file, try interpreting as a directory.

	goPath, err := filepath.Abs(os.Getenv("GOPATH"))

	if err != nil {
		//Gopath isn't set correctly
		return "", "", errors.New("Couldn't absolutize gopath: " + err.Error())
	}

	for {

		abs, err := filepath.Abs(dir)

		if err != nil {
			//Maybe fell off the end of what is a real file?
			return "", "", errors.New("Got err absolutizing search directory: " + dir + " : " + err.Error())
		}

		if !strings.HasPrefix(abs, goPath) {
			return "", "", errors.New("Fell out of gopath without finding config: " + abs)
		}

		public, private := fileNamesToUseInDir(dir)

		if public != "" || private != "" {
			return public, private, nil
		}

		dir = filepath.Join("..", dir)
	}

	return "", "", errors.New("Couldn't find a path")

}

//fileNamesToUseWithFile takes a filename of the public component. Returns the
//string to the publicComponent and also the private component if it exists in
//that folder.z
func fileNamesToUseWithFile(filename string) (publicConfig, privateConfig string, err error) {

	if info, err := os.Stat(filename); err != nil {
		return "", "", errors.New("That file does not exist: " + err.Error())
	} else {
		if info.IsDir() {
			return "", "", errors.New(filename + " points to a dir, not a file")
		}
	}

	//Check to see if there's a private config in that folder
	dir := filepath.Dir(filename)

	privatePath := filepath.Join(dir, privateConfigFileName)

	if _, err := os.Stat(privatePath); err != nil {
		// No private path I guess
		return filename, "", nil
	}

	return filename, privatePath, nil

}

//fileNamesToUseInDir looks for public/private values precisely in the given folder.
func fileNamesToUseInDir(dir string) (publicConfig, privateConfig string) {

	possiblePrivateConfig := filepath.Join(dir, privateConfigFileName)

	if _, err := os.Stat(possiblePrivateConfig); err == nil {
		privateConfig = possiblePrivateConfig
	}

	infos, err := ioutil.ReadDir(dir)

	if err != nil {
		return "", ""
	}

	foundNames := make(map[string]bool)

	for _, info := range infos {
		if info.Name() == privateConfigFileName {
			continue
		}
		if strings.HasPrefix(info.Name(), "config.") && strings.HasSuffix(info.Name(), ".json") {
			foundNames[info.Name()] = true
		}
	}

	prioritizedNames := []string{
		publicConfigFileName,
		"config.json",
	}

	for _, name := range prioritizedNames {
		if foundNames[name] {
			return filepath.Join(dir, name), privateConfig
		}
	}

	//Whatever, return the first one
	for name := range foundNames {
		return filepath.Join(dir, name), privateConfig
	}

	//None of the preferred names were found, just return whatever is in
	//publicConfig, privateConfig. publicConfig is "", privateConfig already
	//has the dir in it
	return

}

//GetConfig returns a Config for those two named files. publicConfig and
//privateConfig may both be "" without erroring.
func GetConfig(publicConfigFile, privateConfigFile string) (*Config, error) {
	publicConfig, err := NewRawConfig(publicConfigFile)

	if err != nil {
		return nil, errors.New("Couldn't get public config: " + err.Error())
	}

	privateConfig, err := NewRawConfig(privateConfigFile)

	if err != nil {
		return nil, errors.New("Couldn't get private config: " + err.Error())
	}

	return NewConfig(publicConfig, privateConfig), nil
}

//Get fetches a fully realized config. It is a simple convenience wrapper
//around FileNames and GetConfig.
func Get(dir string) (*Config, error) {
	publicConfigName, privateConfigName, err := FileNames(dir)

	if err != nil {
		return nil, errors.New("Couldn't get file names to use: " + err.Error())
	}

	return GetConfig(publicConfigName, privateConfigName)

}
<commit_msg>Define DefaultFileNames. Part of #655.<commit_after>/*

config is a simple library that manages config set-up for boardgame-util and
friends, reading from config.json and config.SECRET.json files. See boardgame-
util/README.md for more on the structure of config.json files.

Although a number of the details are exposed in this package, generally you
just use Get() and then directly read the values of the returned Config's Dev
and Prod properties.

*/
package config

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

const (
	privateConfigFileName = "config.SECRET.json"
	publicConfigFileName  = "config.PUBLIC.json"
)

//DefaultFileNames returns the publicConfig and privateConfig names for the
//given path, even if they don't exist. If dirOrFile ends in ".json" then that
//will be returned, with privateConfig being in the same folder. If it's a
//dir, it will be the default filenames in that folder.
func DefaultFileNames(dirOrFile string) (publicConfig, privateConfig string, err error) {
	if strings.HasSuffix(dirOrFile, ".json") {
		dir := filepath.Dir(dirOrFile)

		if _, err := os.Stat(dir); os.IsNotExist(err) {
			return "", "", errors.New("Dir " + dir + " does not exist.")
		}

		return dirOrFile, filepath.Join(dir, privateConfigFileName), nil
	}

	//OK, we'll interpret as Dir.

	if _, err := os.Stat(dirOrFile); os.IsNotExist(err) {
		return "", "", errors.New(dirOrFile + " is interpreted as a directory but does not exist")
	}
	return filepath.Join(dirOrFile, publicConfigFileName), filepath.Join(dirOrFile, privateConfigFileName), nil
}

//FileNames returns the publicConfig filename and privateConfig filename to
//use given the search path. If dir is a config file itself, loads that (and
//any private component in same directory). Next it interprets dir as a
//directory to search within for any config files. If none are found, walks
//upwards in the directory hierarchy (as long as that's still in $GOPATH)
//until it finds a folder that appears to work. If dir is "", working
//directory is assumed.
func FileNames(dir string) (publicConfig, privateConfig string, err error) {

	if dir == "" {
		dir = "."
	}

	//Try to interpret it as a file
	if public, private, err := fileNamesToUseWithFile(dir); err == nil {
		return public, private, nil
	}

	//Guess it wasn't a file, try interpreting as a directory.

	goPath, err := filepath.Abs(os.Getenv("GOPATH"))

	if err != nil {
		//Gopath isn't set correctly
		return "", "", errors.New("Couldn't absolutize gopath: " + err.Error())
	}

	for {

		abs, err := filepath.Abs(dir)

		if err != nil {
			//Maybe fell off the end of what is a real file?
			return "", "", errors.New("Got err absolutizing search directory: " + dir + " : " + err.Error())
		}

		if !strings.HasPrefix(abs, goPath) {
			return "", "", errors.New("Fell out of gopath without finding config: " + abs)
		}

		public, private := fileNamesToUseInDir(dir)

		if public != "" || private != "" {
			return public, private, nil
		}

		dir = filepath.Join("..", dir)
	}

	return "", "", errors.New("Couldn't find a path")

}

//fileNamesToUseWithFile takes a filename of the public component. Returns the
//string to the publicComponent and also the private component if it exists in
//that folder.z
func fileNamesToUseWithFile(filename string) (publicConfig, privateConfig string, err error) {

	if info, err := os.Stat(filename); err != nil {
		return "", "", errors.New("That file does not exist: " + err.Error())
	} else {
		if info.IsDir() {
			return "", "", errors.New(filename + " points to a dir, not a file")
		}
	}

	//Check to see if there's a private config in that folder
	dir := filepath.Dir(filename)

	privatePath := filepath.Join(dir, privateConfigFileName)

	if _, err := os.Stat(privatePath); err != nil {
		// No private path I guess
		return filename, "", nil
	}

	return filename, privatePath, nil

}

//fileNamesToUseInDir looks for public/private values precisely in the given folder.
func fileNamesToUseInDir(dir string) (publicConfig, privateConfig string) {

	possiblePrivateConfig := filepath.Join(dir, privateConfigFileName)

	if _, err := os.Stat(possiblePrivateConfig); err == nil {
		privateConfig = possiblePrivateConfig
	}

	infos, err := ioutil.ReadDir(dir)

	if err != nil {
		return "", ""
	}

	foundNames := make(map[string]bool)

	for _, info := range infos {
		if info.Name() == privateConfigFileName {
			continue
		}
		if strings.HasPrefix(info.Name(), "config.") && strings.HasSuffix(info.Name(), ".json") {
			foundNames[info.Name()] = true
		}
	}

	prioritizedNames := []string{
		publicConfigFileName,
		"config.json",
	}

	for _, name := range prioritizedNames {
		if foundNames[name] {
			return filepath.Join(dir, name), privateConfig
		}
	}

	//Whatever, return the first one
	for name := range foundNames {
		return filepath.Join(dir, name), privateConfig
	}

	//None of the preferred names were found, just return whatever is in
	//publicConfig, privateConfig. publicConfig is "", privateConfig already
	//has the dir in it
	return

}

//GetConfig returns a Config for those two named files. publicConfig and
//privateConfig may both be "" without erroring.
func GetConfig(publicConfigFile, privateConfigFile string) (*Config, error) {
	publicConfig, err := NewRawConfig(publicConfigFile)

	if err != nil {
		return nil, errors.New("Couldn't get public config: " + err.Error())
	}

	privateConfig, err := NewRawConfig(privateConfigFile)

	if err != nil {
		return nil, errors.New("Couldn't get private config: " + err.Error())
	}

	return NewConfig(publicConfig, privateConfig), nil
}

//Get fetches a fully realized config. It is a simple convenience wrapper
//around FileNames and GetConfig.
func Get(dir string) (*Config, error) {
	publicConfigName, privateConfigName, err := FileNames(dir)

	if err != nil {
		return nil, errors.New("Couldn't get file names to use: " + err.Error())
	}

	return GetConfig(publicConfigName, privateConfigName)

}
<|endoftext|>


<commit_before>/*

Package golden is a package designed to make it possible to compare a game to a
golden run for testing purposes. It takes a record saved in storage/filesystem
format and compares it.

Typical Use

Typically you generate a new set of goldens by cd'ing into the package
containing the game you want to test. Then you run `boardgame-util
create-golden`, which generates a stub server for that game. As you play and
create games, it will save the records as filesystem.record.Record, a format
that is designed to be easy to read and modify as JSON. `create-golden` also
will save a `golden_test.go` which will, when tests are run, compare the current
GameManager's operation to the states and moves saved in the golden json blobs.
These tests are a great way to verify that the behavior of your game does not
accidentlaly change.

The format of filesystem.record.Record is optimized to be able to be
hand-edited. It does a number of tricks to make sure that states and moves can
be spliced in easily. First and foremost, it typically stores subsequent states
not as full blobs but as diffs from the state before. This means that changing
one state doesn't require also modifying all subsequent states to have the same
values. The format also "relativizes" moves, setting their Version to -1,
signifying that when fetched via record.Move(version), it should just use the
version number it said it was. In addition, the Initiator field is stored as a
relative number for how many moves back in history to go to. Finally, the
Timestamp field is stored in a format that is as many seconds past the Unix
epoch as the move is from the Game's Created timestamp. All of these properties
mean that the format is (relatively) easy to tweak manually to add or remove
moves.

You can also add a "Description" top-level field in the json to describe what
the game is testing, which is useful to keep track of goldens that test various
edge cases.

*/
package golden

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/go-test/deep"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/storage/filesystem/record"
	"github.com/sirupsen/logrus"
	"github.com/yudai/gojsondiff"
	"github.com/yudai/gojsondiff/formatter"
)

//Compare is the primary method in the package. It takes a game delegate and a
//filename denoting a record to compare against. delegate shiould be a fresh
//delegate not yet affiliated with a manager. It compares every version and move
//in the history (ignoring things that shouldn't be the same, like timestamps)
//and reports the first place they divrge. Any time it finds a move not proposed
//by AdminPlayerIndex it will propose that move. As long as your game uses
//state.Rand() for all randomness and is otherwise deterministic then everything
//should work. If updateOnDifferent is true, instead of erroring, it will
//instead overwrite the existing golden with a new one. The boardgame-util
//create-goldens tool will output a test that will look for a `-update-golden`
//flag and pass in that variable here.
func Compare(delegate boardgame.GameDelegate, recFilename string, updateOnDifferent bool) error {

	storage := newStorageManager()

	manager, err := boardgame.NewGameManager(delegate, storage)

	if err != nil {
		return errors.New("Couldn't create new manager: " + err.Error())
	}

	storage.manager = manager

	rec, err := record.New(recFilename)

	if err != nil {
		return errors.New("Couldn't create record: " + err.Error())
	}

	return compare(manager, rec, storage, updateOnDifferent)

}

//CompareFolder is like Compare, except it will iterate through any file in
//recFolder that ends in .json. Errors if any of those files cannot be parsed
//into recs. See Compare for more documentation.
func CompareFolder(delegate boardgame.GameDelegate, recFolder string, updateOnDifferent bool) error {

	storage := newStorageManager()

	manager, err := boardgame.NewGameManager(delegate, storage)

	if err != nil {
		return errors.New("Couldn't create new manager: " + err.Error())
	}

	storage.manager = manager

	infos, err := ioutil.ReadDir(recFolder)

	if err != nil {
		return errors.New("Couldn't read folder: " + err.Error())
	}

	for _, info := range infos {
		if info.IsDir() {
			continue
		}

		if filepath.Ext(info.Name()) != ".json" {
			continue
		}

		rec, err := record.New(filepath.Join(recFolder, info.Name()))

		if err != nil {
			return errors.New("File with name " + info.Name() + " couldn't be loaded into rec: " + err.Error())
		}

		if err := compare(manager, rec, storage, updateOnDifferent); err != nil {
			return errors.New("File named " + info.Name() + " had compare error: " + err.Error())
		}

	}

	return nil
}

func newLogger() (*logrus.Logger, *bytes.Buffer) {
	result := logrus.New()
	buf := &bytes.Buffer{}
	result.Out = buf
	result.SetLevel(logrus.DebugLevel)
	return result, buf
}

func compare(manager *boardgame.GameManager, rec *record.Record, storage *storageManager, updateOnDifferent bool) error {

	//TODO: get rid of this function once refactored
	comparer, err := newComparer(manager, rec, storage)

	if err != nil {
		return errors.New("Couldn't create comparer: " + err.Error())
	}

	if updateOnDifferent {
		fmt.Println("WARNING: overwriting old goldens, verify the diff looks sane before committing!")
		newGolden, err := comparer.RegenerateGolden()
		if err != nil {
			comparer.PrintDebug()
			return err
		}
		if err := newGolden.Save(rec.Path(), false); err != nil {
			return errors.New("Could not overwrite " + rec.Path() + ": " + err.Error())
		}
	} else {

		if err := comparer.Compare(); err != nil {
			comparer.PrintDebug()
			return err
		}
	}

	return nil
}

var differ = gojsondiff.New()

func compareJSONBlobs(one, two []byte) error {

	diff, err := differ.Compare(one, two)

	if err != nil {
		return errors.New("Couldn't diff: " + err.Error())
	}

	if diff.Modified() {

		var oneJSON map[string]interface{}

		if err := json.Unmarshal(one, &oneJSON); err != nil {
			return errors.New("Couldn't unmarshal left")
		}

		diffformatter := formatter.NewAsciiFormatter(oneJSON, formatter.AsciiFormatterConfig{
			Coloring: true,
		})

		str, err := diffformatter.Format(diff)

		if err != nil {
			return errors.New("Couldn't format diff: " + err.Error())
		}

		return errors.New("Diff: " + str)
	}

	return nil

}

func compareMoveStorageRecords(one, two boardgame.MoveStorageRecord, skipAbsoluteVersions bool) error {

	oneBlob := one.Blob
	twoBlob := two.Blob

	//Set the fields we know might differ to known values
	one.Blob = nil
	two.Blob = nil

	two.Timestamp = one.Timestamp

	if skipAbsoluteVersions {
		if one.Version >= 0 || two.Version >= 0 {
			two.Version = one.Version
			two.Initiator = one.Initiator
		}
	}

	if !reflect.DeepEqual(one, two) {
		return errors.New("Move storage records differed in base fields: " + strings.Join(deep.Equal(one, two), ", "))
	}

	return compareJSONBlobs(oneBlob, twoBlob)

}
<commit_msg>Add package doc to golden about remastering. Part of #701. Part of #755.<commit_after>/*

Package golden is a package designed to make it possible to compare a game to a
golden run for testing purposes. It takes a record saved in storage/filesystem
format and compares it.

Typical Use

Typically you generate a new set of goldens by cd'ing into the package
containing the game you want to test. Then you run `boardgame-util
create-golden`, which generates a stub server for that game. As you play and
create games, it will save the records as filesystem.record.Record, a format
that is designed to be easy to read and modify as JSON. `create-golden` also
will save a `golden_test.go` which will, when tests are run, compare the current
GameManager's operation to the states and moves saved in the golden json blobs.
These tests are a great way to verify that the behavior of your game does not
accidentlaly change.

The format of filesystem.record.Record is optimized to be able to be
hand-edited. It does a number of tricks to make sure that states and moves can
be spliced in easily. First and foremost, it typically stores subsequent states
not as full blobs but as diffs from the state before. This means that changing
one state doesn't require also modifying all subsequent states to have the same
values. The format also "relativizes" moves, setting their Version to -1,
signifying that when fetched via record.Move(version), it should just use the
version number it said it was. In addition, the Initiator field is stored as a
relative number for how many moves back in history to go to. Finally, the
Timestamp field is stored in a format that is as many seconds past the Unix
epoch as the move is from the Game's Created timestamp. All of these properties
mean that the format is (relatively) easy to tweak manually to add or remove
moves.

You can also add a "Description" top-level field in the json to describe what
the game is testing, which is useful to keep track of goldens that test various
edge cases.

Remastering Goldens

Typically you record a golden, and then every time you test the game package, it
will just verify the game logic still applies the same moves in the right order,
with the right state modifications. But every so often you want to 'remaster'
your golden. For example, perhaps the game logic has changed to have slightly
different behavior, or you want to update the format of the golden to ensure
it's canonical and up-to-date, to match changes in the underlying library.

It's possible to 'remaster' goldens, which means to re-record them and overwrite
the original. You do this by passing true as the last value to Compare or
CompareFolder. The `golden_test.go` that is generated for you will also
automatically add a flag that will be passed in, so the canonical way to
remaster is to run `go test -update-golden`, which instead of comparing the
golden, will remaster it, overwriting the original.

After you remaster, it's a good idea to sanity check by doing a normal test (`go
test`) to verify the game logic matches the new golden. You should also visually
inspect the diff of the golden before commiting to make sure there aren't any
unintended changes. The remastering process is designed to ensure that wherever
possible content doesn't change. The design of filesystem.record.Record
(described above) helps, but the remastering process also seeks to, for example,
use existing timestamps wherever possible and generate reasonable intermediate
timestamps for new moves that have been added.

*/
package golden

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/go-test/deep"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/storage/filesystem/record"
	"github.com/sirupsen/logrus"
	"github.com/yudai/gojsondiff"
	"github.com/yudai/gojsondiff/formatter"
)

//Compare is the primary method in the package. It takes a game delegate and a
//filename denoting a record to compare against. delegate shiould be a fresh
//delegate not yet affiliated with a manager. It compares every version and move
//in the history (ignoring things that shouldn't be the same, like timestamps)
//and reports the first place they divrge. Any time it finds a move not proposed
//by AdminPlayerIndex it will propose that move. As long as your game uses
//state.Rand() for all randomness and is otherwise deterministic then everything
//should work. If updateOnDifferent is true, instead of erroring, it will
//instead overwrite the existing golden with a new one. The boardgame-util
//create-goldens tool will output a test that will look for a `-update-golden`
//flag and pass in that variable here.
func Compare(delegate boardgame.GameDelegate, recFilename string, updateOnDifferent bool) error {

	storage := newStorageManager()

	manager, err := boardgame.NewGameManager(delegate, storage)

	if err != nil {
		return errors.New("Couldn't create new manager: " + err.Error())
	}

	storage.manager = manager

	rec, err := record.New(recFilename)

	if err != nil {
		return errors.New("Couldn't create record: " + err.Error())
	}

	return compare(manager, rec, storage, updateOnDifferent)

}

//CompareFolder is like Compare, except it will iterate through any file in
//recFolder that ends in .json. Errors if any of those files cannot be parsed
//into recs. See Compare for more documentation.
func CompareFolder(delegate boardgame.GameDelegate, recFolder string, updateOnDifferent bool) error {

	storage := newStorageManager()

	manager, err := boardgame.NewGameManager(delegate, storage)

	if err != nil {
		return errors.New("Couldn't create new manager: " + err.Error())
	}

	storage.manager = manager

	infos, err := ioutil.ReadDir(recFolder)

	if err != nil {
		return errors.New("Couldn't read folder: " + err.Error())
	}

	for _, info := range infos {
		if info.IsDir() {
			continue
		}

		if filepath.Ext(info.Name()) != ".json" {
			continue
		}

		rec, err := record.New(filepath.Join(recFolder, info.Name()))

		if err != nil {
			return errors.New("File with name " + info.Name() + " couldn't be loaded into rec: " + err.Error())
		}

		if err := compare(manager, rec, storage, updateOnDifferent); err != nil {
			return errors.New("File named " + info.Name() + " had compare error: " + err.Error())
		}

	}

	return nil
}

func newLogger() (*logrus.Logger, *bytes.Buffer) {
	result := logrus.New()
	buf := &bytes.Buffer{}
	result.Out = buf
	result.SetLevel(logrus.DebugLevel)
	return result, buf
}

func compare(manager *boardgame.GameManager, rec *record.Record, storage *storageManager, updateOnDifferent bool) error {

	//TODO: get rid of this function once refactored
	comparer, err := newComparer(manager, rec, storage)

	if err != nil {
		return errors.New("Couldn't create comparer: " + err.Error())
	}

	if updateOnDifferent {
		fmt.Println("WARNING: overwriting old goldens, verify the diff looks sane before committing!")
		newGolden, err := comparer.RegenerateGolden()
		if err != nil {
			comparer.PrintDebug()
			return err
		}
		if err := newGolden.Save(rec.Path(), false); err != nil {
			return errors.New("Could not overwrite " + rec.Path() + ": " + err.Error())
		}
	} else {

		if err := comparer.Compare(); err != nil {
			comparer.PrintDebug()
			return err
		}
	}

	return nil
}

var differ = gojsondiff.New()

func compareJSONBlobs(one, two []byte) error {

	diff, err := differ.Compare(one, two)

	if err != nil {
		return errors.New("Couldn't diff: " + err.Error())
	}

	if diff.Modified() {

		var oneJSON map[string]interface{}

		if err := json.Unmarshal(one, &oneJSON); err != nil {
			return errors.New("Couldn't unmarshal left")
		}

		diffformatter := formatter.NewAsciiFormatter(oneJSON, formatter.AsciiFormatterConfig{
			Coloring: true,
		})

		str, err := diffformatter.Format(diff)

		if err != nil {
			return errors.New("Couldn't format diff: " + err.Error())
		}

		return errors.New("Diff: " + str)
	}

	return nil

}

func compareMoveStorageRecords(one, two boardgame.MoveStorageRecord, skipAbsoluteVersions bool) error {

	oneBlob := one.Blob
	twoBlob := two.Blob

	//Set the fields we know might differ to known values
	one.Blob = nil
	two.Blob = nil

	two.Timestamp = one.Timestamp

	if skipAbsoluteVersions {
		if one.Version >= 0 || two.Version >= 0 {
			two.Version = one.Version
			two.Initiator = one.Initiator
		}
	}

	if !reflect.DeepEqual(one, two) {
		return errors.New("Move storage records differed in base fields: " + strings.Join(deep.Equal(one, two), ", "))
	}

	return compareJSONBlobs(oneBlob, twoBlob)

}
<|endoftext|>


<commit_before>package bracket_push

import (
	"testing"
)

var testCases = []struct {
	input    string
	expected bool
}{
	{
		input:    "",
		expected: true,
	},
	{
		input:    "{}",
		expected: true,
	},
	{
		input:    "{{",
		expected: false,
	},
	{
		input:    "{}[]",
		expected: true,
	},
	{
		input:    "{[]}",
		expected: true,
	},
	{
		input:    "{[)][]}",
		expected: false,
	},
	{
		input:    "{[]([()])}",
		expected: true,
	},
}

func TestBracket(t *testing.T) {
	for _, tt := range testCases {
		actual, err := Bracket(tt.input)
		// We don't expect errors for any of the test cases
		if err != nil {
			t.Fatalf("Bracket(%q) returned error %q.  Error not expected.", tt.input, err)
		}
		if actual != tt.expected {
			t.Fatalf("Bracket(%q) was expected to return %v but returned %v.",
				tt.input, tt.expected, actual)
		}
	}
}

func BenchmarkBracket(b *testing.B) {
	b.StopTimer()
	for _, tt := range testCases {
		b.StartTimer()
		for i := 0; i < b.N; i++ {
			Bracket(tt.input)
		}
		b.StopTimer()
	}
}
<commit_msg>bracket-push: Add out-of-order test<commit_after>package bracket_push

import (
	"testing"
)

var testCases = []struct {
	input    string
	expected bool
}{
	{
		input:    "",
		expected: true,
	},
	{
		input:    "{}",
		expected: true,
	},
	{
		input:    "{{",
		expected: false,
	},
	{
		input:    "}{",
		expected: false,
	},
	{
		input:    "{}[]",
		expected: true,
	},
	{
		input:    "{[]}",
		expected: true,
	},
	{
		input:    "{[)][]}",
		expected: false,
	},
	{
		input:    "{[]([()])}",
		expected: true,
	},
}

func TestBracket(t *testing.T) {
	for _, tt := range testCases {
		actual, err := Bracket(tt.input)
		// We don't expect errors for any of the test cases
		if err != nil {
			t.Fatalf("Bracket(%q) returned error %q.  Error not expected.", tt.input, err)
		}
		if actual != tt.expected {
			t.Fatalf("Bracket(%q) was expected to return %v but returned %v.",
				tt.input, tt.expected, actual)
		}
	}
}

func BenchmarkBracket(b *testing.B) {
	b.StopTimer()
	for _, tt := range testCases {
		b.StartTimer()
		for i := 0; i < b.N; i++ {
			Bracket(tt.input)
		}
		b.StopTimer()
	}
}
<|endoftext|>