<commit_before>// Copyright (c) 2017-2019 Snowflake Computing Inc. All right reserved.

package gosnowflake

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"context"

	"sync"
)

var random *rand.Rand

func init() {
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
}

// requestGUIDKey is attached to every request against Snowflake
const requestGUIDKey string = "request_guid"

// retryCounterKey is attached to query-request from the second time
const retryCounterKey string = "retryCounter"

// requestIDKey is attached to all requests to Snowflake
const requestIDKey string = "requestId"

// This class takes in an url during construction and replace the
// value of request_guid every time the replace() is called
// When the url does not contain request_guid, just return the original
// url
type requestGUIDReplacer interface {
	// replace the url with new ID
	replace() *url.URL
}

// Make requestGUIDReplacer given a url string
func newRequestGUIDReplace(urlPtr *url.URL) requestGUIDReplacer {
	values, err := url.ParseQuery(urlPtr.RawQuery)
	if err != nil {
		// nop if invalid query parameters
		return &transientReplace{urlPtr}
	}
	if len(values.Get(requestGUIDKey)) == 0 {
		// nop if no request_guid is included.
		return &transientReplace{urlPtr}
	}

	return &requestGUIDReplace{urlPtr, values}
}

// this replacer does nothing but replace the url
type transientReplace struct {
	urlPtr *url.URL
}

func (replacer *transientReplace) replace() *url.URL {
	return replacer.urlPtr
}

/*
requestGUIDReplacer is a one-shot object that is created out of the retry loop and
called with replace to change the retry_guid's value upon every retry
*/
type requestGUIDReplace struct {
	urlPtr    *url.URL
	urlValues url.Values
}

/**
This function would replace they value of the requestGUIDKey in a url with a newly
generated uuid
*/
func (replacer *requestGUIDReplace) replace() *url.URL {
	replacer.urlValues.Del(requestGUIDKey)
	replacer.urlValues.Add(requestGUIDKey, uuid.New().String())
	replacer.urlPtr.RawQuery = replacer.urlValues.Encode()
	return replacer.urlPtr
}

type retryCounterUpdater interface {
	replaceOrAdd(retry int) *url.URL
}

type retryCounterUpdate struct {
	urlPtr    *url.URL
	urlValues url.Values
}

// this replacer does nothing but replace the url
type transientReplaceOrAdd struct {
	urlPtr *url.URL
}

func (replaceOrAdder *transientReplaceOrAdd) replaceOrAdd(retry int) *url.URL {
	return replaceOrAdder.urlPtr
}

func (replacer *retryCounterUpdate) replaceOrAdd(retry int) *url.URL {
	replacer.urlValues.Del(retryCounterKey)
	replacer.urlValues.Add(retryCounterKey, strconv.Itoa(retry))
	replacer.urlPtr.RawQuery = replacer.urlValues.Encode()
	return replacer.urlPtr
}

func newRetryUpdate(urlPtr *url.URL) retryCounterUpdater {
	if !strings.HasPrefix(urlPtr.Path, queryRequestPath) {
		// nop if not query-request
		return &transientReplaceOrAdd{urlPtr}
	}
	values, err := url.ParseQuery(urlPtr.RawQuery)
	if err != nil {
		// nop if the URL is not valid
		return &transientReplaceOrAdd{urlPtr}
	}
	return &retryCounterUpdate{urlPtr, values}
}

type waitAlgo struct {
	mutex *sync.Mutex   // required for random.Int63n
	base  time.Duration // base wait time
	cap   time.Duration // maximum wait time
}

func randSecondDuration(n time.Duration) time.Duration {
	return time.Duration(random.Int63n(int64(n/time.Second))) * time.Second
}

// decorrelated jitter backoff
func (w *waitAlgo) decorr(attempt int, sleep time.Duration) time.Duration {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	t := 3*sleep - w.base
	switch {
	case t > 0:
		return durationMin(w.cap, randSecondDuration(t)+w.base)
	case t < 0:
		return durationMin(w.cap, randSecondDuration(-t)+3*sleep)
	}
	return w.base
}

var defaultWaitAlgo = &waitAlgo{
	mutex: &sync.Mutex{},
	base:  5 * time.Second,
	cap:   160 * time.Second,
}

type requestFunc func(method, urlStr string, body io.Reader) (*http.Request, error)

type clientInterface interface {
	Do(req *http.Request) (*http.Response, error)
}

type retryHTTP struct {
	ctx      context.Context
	client   clientInterface
	req      requestFunc
	method   string
	fullURL  *url.URL
	headers  map[string]string
	body     []byte
	timeout  time.Duration
	raise4XX bool
}

func newRetryHTTP(ctx context.Context,
	client clientInterface,
	req requestFunc,
	fullURL *url.URL,
	headers map[string]string,
	timeout time.Duration) *retryHTTP {
	instance := retryHTTP{}
	instance.ctx = ctx
	instance.client = client
	instance.req = req
	instance.method = "GET"
	instance.fullURL = fullURL
	instance.headers = headers
	instance.body = nil
	instance.timeout = timeout
	instance.raise4XX = false
	return &instance
}

func (r *retryHTTP) doRaise4XX(raise4XX bool) *retryHTTP {
	r.raise4XX = raise4XX
	return r
}

func (r *retryHTTP) doPost() *retryHTTP {
	r.method = "POST"
	return r
}

func (r *retryHTTP) setBody(body []byte) *retryHTTP {
	r.body = body
	return r
}

func (r *retryHTTP) execute() (res *http.Response, err error) {
	totalTimeout := r.timeout
	glog.V(2).Infof("retryHTTP.totalTimeout: %v", totalTimeout)
	retryCounter := 0
	sleepTime := time.Duration(0)

	var rIDReplacer requestGUIDReplacer
	var rUpdater retryCounterUpdater

	for {
		req, err := r.req(r.method, r.fullURL.String(), bytes.NewReader(r.body))
		if err != nil {
			return nil, err
		}
		if req != nil {
			// req can be nil in tests
			req = req.WithContext(r.ctx)
		}
		for k, v := range r.headers {
			req.Header.Set(k, v)
		}
		res, err = r.client.Do(req)
		// context cancel or timeout
		if err != nil {
			urlError, isURLError := err.(*url.Error)
			if isURLError &&
				(urlError.Err == context.DeadlineExceeded || urlError.Err == context.Canceled) {
				return res, urlError.Err
			}
			if isURLError {
				if driverError, ok := urlError.Err.(*SnowflakeError); ok {
					if driverError.Number == ErrOCSPStatusRevoked {
						return nil, err
					}
				}

			}
			// cannot just return 4xx and 5xx status as the error can be sporadic. run often helps.
			glog.V(2).Infof(
				"failed http connection. no response is returned. err: %v. retrying...\n", err)
		} else {
			if res.StatusCode == http.StatusOK || r.raise4XX && res != nil && res.StatusCode >= 400 && res.StatusCode < 500 {
				// exit if success
				// or
				// abort connection if raise4XX flag is enabled and the range of HTTP status code are 4XX.
				// This is currently used for Snowflake login. The caller must generate an error object based on HTTP status.
				break
			}
			glog.V(2).Infof(
				"failed http connection. HTTP Status: %v. retrying...\n", res.StatusCode)
			res.Body.Close()
		}
		// uses decorrelated jitter backoff
		sleepTime = defaultWaitAlgo.decorr(retryCounter, sleepTime)

		if totalTimeout > 0 {
			glog.V(2).Infof("to timeout: %v", totalTimeout)
			// if any timeout is set
			totalTimeout -= sleepTime
			if totalTimeout <= 0 {
				if err != nil {
					return nil, err
				}
				if res != nil {
					return nil, fmt.Errorf("timeout. HTTP Status: %v. Hanging?", res.StatusCode)
				}
				return nil, errors.New("timeout. Hanging?")
			}
		}
		retryCounter++
		if rIDReplacer == nil {
			rIDReplacer = newRequestGUIDReplace(r.fullURL)
		}
		r.fullURL = rIDReplacer.replace()
		if rUpdater == nil {
			rUpdater = newRetryUpdate(r.fullURL)
		}
		r.fullURL = rUpdater.replaceOrAdd(retryCounter)
		glog.V(2).Infof("sleeping %v. to timeout: %v. retrying", sleepTime, totalTimeout)

		await := time.NewTimer(sleepTime)
		select {
		case <-await.C:
			// retry the request
		case <-r.ctx.Done():
			await.Stop()
			return res, r.ctx.Err()
		}
	}
	return res, err
}
<commit_msg>SNOW-90775: no retry for expired certificate<commit_after>// Copyright (c) 2017-2019 Snowflake Computing Inc. All right reserved.

package gosnowflake

import (
	"bytes"
	"crypto/x509"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"context"

	"sync"
)

var random *rand.Rand

func init() {
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
}

// requestGUIDKey is attached to every request against Snowflake
const requestGUIDKey string = "request_guid"

// retryCounterKey is attached to query-request from the second time
const retryCounterKey string = "retryCounter"

// requestIDKey is attached to all requests to Snowflake
const requestIDKey string = "requestId"

// This class takes in an url during construction and replace the
// value of request_guid every time the replace() is called
// When the url does not contain request_guid, just return the original
// url
type requestGUIDReplacer interface {
	// replace the url with new ID
	replace() *url.URL
}

// Make requestGUIDReplacer given a url string
func newRequestGUIDReplace(urlPtr *url.URL) requestGUIDReplacer {
	values, err := url.ParseQuery(urlPtr.RawQuery)
	if err != nil {
		// nop if invalid query parameters
		return &transientReplace{urlPtr}
	}
	if len(values.Get(requestGUIDKey)) == 0 {
		// nop if no request_guid is included.
		return &transientReplace{urlPtr}
	}

	return &requestGUIDReplace{urlPtr, values}
}

// this replacer does nothing but replace the url
type transientReplace struct {
	urlPtr *url.URL
}

func (replacer *transientReplace) replace() *url.URL {
	return replacer.urlPtr
}

/*
requestGUIDReplacer is a one-shot object that is created out of the retry loop and
called with replace to change the retry_guid's value upon every retry
*/
type requestGUIDReplace struct {
	urlPtr    *url.URL
	urlValues url.Values
}

/**
This function would replace they value of the requestGUIDKey in a url with a newly
generated uuid
*/
func (replacer *requestGUIDReplace) replace() *url.URL {
	replacer.urlValues.Del(requestGUIDKey)
	replacer.urlValues.Add(requestGUIDKey, uuid.New().String())
	replacer.urlPtr.RawQuery = replacer.urlValues.Encode()
	return replacer.urlPtr
}

type retryCounterUpdater interface {
	replaceOrAdd(retry int) *url.URL
}

type retryCounterUpdate struct {
	urlPtr    *url.URL
	urlValues url.Values
}

// this replacer does nothing but replace the url
type transientReplaceOrAdd struct {
	urlPtr *url.URL
}

func (replaceOrAdder *transientReplaceOrAdd) replaceOrAdd(retry int) *url.URL {
	return replaceOrAdder.urlPtr
}

func (replacer *retryCounterUpdate) replaceOrAdd(retry int) *url.URL {
	replacer.urlValues.Del(retryCounterKey)
	replacer.urlValues.Add(retryCounterKey, strconv.Itoa(retry))
	replacer.urlPtr.RawQuery = replacer.urlValues.Encode()
	return replacer.urlPtr
}

func newRetryUpdate(urlPtr *url.URL) retryCounterUpdater {
	if !strings.HasPrefix(urlPtr.Path, queryRequestPath) {
		// nop if not query-request
		return &transientReplaceOrAdd{urlPtr}
	}
	values, err := url.ParseQuery(urlPtr.RawQuery)
	if err != nil {
		// nop if the URL is not valid
		return &transientReplaceOrAdd{urlPtr}
	}
	return &retryCounterUpdate{urlPtr, values}
}

type waitAlgo struct {
	mutex *sync.Mutex   // required for random.Int63n
	base  time.Duration // base wait time
	cap   time.Duration // maximum wait time
}

func randSecondDuration(n time.Duration) time.Duration {
	return time.Duration(random.Int63n(int64(n/time.Second))) * time.Second
}

// decorrelated jitter backoff
func (w *waitAlgo) decorr(attempt int, sleep time.Duration) time.Duration {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	t := 3*sleep - w.base
	switch {
	case t > 0:
		return durationMin(w.cap, randSecondDuration(t)+w.base)
	case t < 0:
		return durationMin(w.cap, randSecondDuration(-t)+3*sleep)
	}
	return w.base
}

var defaultWaitAlgo = &waitAlgo{
	mutex: &sync.Mutex{},
	base:  5 * time.Second,
	cap:   160 * time.Second,
}

type requestFunc func(method, urlStr string, body io.Reader) (*http.Request, error)

type clientInterface interface {
	Do(req *http.Request) (*http.Response, error)
}

type retryHTTP struct {
	ctx      context.Context
	client   clientInterface
	req      requestFunc
	method   string
	fullURL  *url.URL
	headers  map[string]string
	body     []byte
	timeout  time.Duration
	raise4XX bool
}

func newRetryHTTP(ctx context.Context,
	client clientInterface,
	req requestFunc,
	fullURL *url.URL,
	headers map[string]string,
	timeout time.Duration) *retryHTTP {
	instance := retryHTTP{}
	instance.ctx = ctx
	instance.client = client
	instance.req = req
	instance.method = "GET"
	instance.fullURL = fullURL
	instance.headers = headers
	instance.body = nil
	instance.timeout = timeout
	instance.raise4XX = false
	return &instance
}

func (r *retryHTTP) doRaise4XX(raise4XX bool) *retryHTTP {
	r.raise4XX = raise4XX
	return r
}

func (r *retryHTTP) doPost() *retryHTTP {
	r.method = "POST"
	return r
}

func (r *retryHTTP) setBody(body []byte) *retryHTTP {
	r.body = body
	return r
}

func (r *retryHTTP) execute() (res *http.Response, err error) {
	totalTimeout := r.timeout
	glog.V(2).Infof("retryHTTP.totalTimeout: %v", totalTimeout)
	retryCounter := 0
	sleepTime := time.Duration(0)

	var rIDReplacer requestGUIDReplacer
	var rUpdater retryCounterUpdater

	for {
		req, err := r.req(r.method, r.fullURL.String(), bytes.NewReader(r.body))
		if err != nil {
			return nil, err
		}
		if req != nil {
			// req can be nil in tests
			req = req.WithContext(r.ctx)
		}
		for k, v := range r.headers {
			req.Header.Set(k, v)
		}
		res, err = r.client.Do(req)
		if err != nil {
			// check if it can retry.
			doExit, err := r.isRetryableError(err)
			if doExit {
				return res, err
			}
			// cannot just return 4xx and 5xx status as the error can be sporadic. run often helps.
			glog.V(2).Infof(
				"failed http connection. no response is returned. err: %v. retrying...\n", err)
		} else {
			if res.StatusCode == http.StatusOK || r.raise4XX && res != nil && res.StatusCode >= 400 && res.StatusCode < 500 {
				// exit if success
				// or
				// abort connection if raise4XX flag is enabled and the range of HTTP status code are 4XX.
				// This is currently used for Snowflake login. The caller must generate an error object based on HTTP status.
				break
			}
			glog.V(2).Infof(
				"failed http connection. HTTP Status: %v. retrying...\n", res.StatusCode)
			res.Body.Close()
		}
		// uses decorrelated jitter backoff
		sleepTime = defaultWaitAlgo.decorr(retryCounter, sleepTime)

		if totalTimeout > 0 {
			glog.V(2).Infof("to timeout: %v", totalTimeout)
			// if any timeout is set
			totalTimeout -= sleepTime
			if totalTimeout <= 0 {
				if err != nil {
					return nil, err
				}
				if res != nil {
					return nil, fmt.Errorf("timeout. HTTP Status: %v. Hanging?", res.StatusCode)
				}
				return nil, errors.New("timeout. Hanging?")
			}
		}
		retryCounter++
		if rIDReplacer == nil {
			rIDReplacer = newRequestGUIDReplace(r.fullURL)
		}
		r.fullURL = rIDReplacer.replace()
		if rUpdater == nil {
			rUpdater = newRetryUpdate(r.fullURL)
		}
		r.fullURL = rUpdater.replaceOrAdd(retryCounter)
		glog.V(2).Infof("sleeping %v. to timeout: %v. retrying", sleepTime, totalTimeout)

		await := time.NewTimer(sleepTime)
		select {
		case <-await.C:
			// retry the request
		case <-r.ctx.Done():
			await.Stop()
			return res, r.ctx.Err()
		}
	}
	return res, err
}

func (r *retryHTTP) isRetryableError(err error) (bool, error) {
	urlError, isURLError := err.(*url.Error)
	if isURLError {
		// context cancel or timeout
		if urlError.Err == context.DeadlineExceeded || urlError.Err == context.Canceled {
			return true, urlError.Err
		}
		if driverError, ok := urlError.Err.(*SnowflakeError); ok {
			// Certificate Revoked
			if driverError.Number == ErrOCSPStatusRevoked {
				return true, err
			}
		}
		if _, ok := urlError.Err.(x509.CertificateInvalidError); ok {
			// Certificate is invalid
			return true, err
		}
		if _, ok := urlError.Err.(x509.UnknownAuthorityError); ok {
			// Certificate is self-signed
			return true, err
		}

	}
	return false, err
}
<|endoftext|>


<commit_before>package which

import (
	"debug/gosym"
	"errors"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// TODO(rjeczalik): support all platform types

func init() {
	// Add $GOROOT and $GOROOT_FINAL to the filtered paths.
	goroot := runtime.GOROOT()
	filtered[goroot] = struct{}{}
	if err := os.Setenv("GOROOT", ""); err == nil {
		filtered[runtime.GOROOT()] = struct{}{} // $GOROOT_FINAL
		os.Setenv("GOROOT", goroot)
	}
	// Make the order of file factory methods platform-specific.
	switch runtime.GOOS {
	case "darwin":
		alltbl = append(alltbl, newmacho, newelf, newpe)
	case "windows":
		alltbl = append(alltbl, newpe, newmacho, newelf)
	default:
		alltbl = append(alltbl, newelf, newmacho, newpe)
	}
}

type tabler interface {
	Close() error
	Pcln() ([]byte, error)
	Sym() ([]byte, error)
	Text() (uint64, error)
	Type() *PlatformType
}

// All supported symbol table builders.
var alltbl []func(string) (tabler, error)

// A path is discarded if it contains any of the filtered strings.
// TODO(rjeczalik): add $HOME/.gvm/gos?
var filtered = map[string]struct{}{
	"/tmp/makerelease":  {},
	"<autogenerated>":   {},
	"c:/go/src":         {},
	"/usr/local/go/src": {},
}

var (
	// ErrNotGoExec is an error.
	ErrNotGoExec = errors.New("which: not a Go executable")
	// ErrGuessFail is an error.
	ErrGuessFail = errors.New("which: unable to guess an import path of the main package")
)

// PlatformType represents the target platform of the executable.
type PlatformType struct {
	GOOS   string // target operating system
	GOARCH string // target architecture
}

// String gives Go platform string.
func (typ PlatformType) String() string {
	return typ.GOOS + "_" + typ.GOARCH
}

var (
	// PlatformDarwin386 represents the darwin_386 target arch.
	PlatformDarwin386 = &PlatformType{"darwin", "386"}
	// PlatformDarwinAMD64 represents the darwin_amd64 target arch.
	PlatformDarwinAMD64 = &PlatformType{"darwin", "amd64"}
	// PlatformFreeBSD386 represents the freebsd_386 target arch.
	PlatformFreeBSD386 = &PlatformType{"freebsd", "386"}
	// PlatformFreeBSDAMD64 represents the freebsd_amd64 target arch.
	PlatformFreeBSDAMD64 = &PlatformType{"freebsd", "amd64"}
	// PlatformLinux386 represents the linux_386 target arch.
	PlatformLinux386 = &PlatformType{"linux", "386"}
	// PlatformLinuxAMD64 represents the linux_amd64 target arch.
	PlatformLinuxAMD64 = &PlatformType{"linux", "amd64"}
	// PlatformWindows386 represents the windows_386 target arch.
	PlatformWindows386 = &PlatformType{"windows", "386"}
	// PlatformWindowsAMD64 represents the windows_amd64 target arch.
	PlatformWindowsAMD64 = &PlatformType{"windows", "amd64"}
)

// Exec represents a single Go executable file.
type Exec struct {
	Path  string        // Path to the executable.
	Type  *PlatformType // Fileutable file format.
	table *gosym.Table
}

// NewExec tries to detect executable type for the given path and returns
// a new executable. It fails if file does not exist, is not a Go executable or
// it's unable to parse the file format.
func NewExec(path string) (*Exec, error) {
	typ, symtab, pclntab, text, err := newtbl(path)
	if err != nil {
		return nil, err
	}
	lntab := gosym.NewLineTable(pclntab, text)
	if lntab == nil {
		return nil, ErrNotGoExec
	}
	tab, err := gosym.NewTable(symtab, lntab)
	if err != nil {
		return nil, ErrNotGoExec
	}
	return &Exec{Path: path, Type: typ, table: tab}, nil
}

// Import gives the import path of main package of given executable. It returns
// non-nil error when it fails to guess the exact path.
func (ex *Exec) Import() (string, error) {
	var dirs = make(map[string]struct{})
	for file, obj := range ex.table.Files {
		for i := range obj.Funcs {
			// main.main symbol is referenced by every file of each package
			// imported by the main package of the executable.
			if obj.Funcs[i].Sym.Name == "main.main" && !isfiltered(file) {
				dirs[filepath.Dir(file)] = struct{}{}
			}
		}
	}
	name := filepath.Base(ex.Path)
	if ex.Type == PlatformWindows386 || ex.Type == PlatformWindowsAMD64 {
		name = strings.TrimRight(name, ".exe")
	}
	if pkg, unique := guesspkg(name, dirs); unique && pkg != "" {
		return pkg, nil
	}
	return "", ErrGuessFail
}

// Import reads the import path of main package of the Go executable given by
// the path.
func Import(path string) (string, error) {
	ex, err := NewExec(path)
	if err != nil {
		return "", err
	}
	return ex.Import()
}

func newtbl(path string) (typ *PlatformType, symtab, pclntab []byte, text uint64, err error) {
	var tbl tabler
	fail := func() {
		err = errors.New("which: unable to read Go symbol table: " + err.Error())
		tbl.Close()
	}
	for _, newt := range alltbl {
		if tbl, err = newt(path); err != nil {
			err = ErrNotGoExec
			continue
		}
		if symtab, err = tbl.Sym(); err != nil {
			fail()
			continue
		}
		if pclntab, err = tbl.Pcln(); err != nil {
			fail()
			continue
		}
		if text, err = tbl.Text(); err != nil {
			fail()
			continue
		}
		typ = tbl.Type()
		tbl.Close()
		break
	}
	return
}

func isfiltered(file string) bool {
	for f := range filtered {
		if strings.Contains(file, f) {
			return true
		}
	}
	return false
}

func guesspkg(name string, dirs map[string]struct{}) (pkg string, unique bool) {
	for _, s := range []string{"cmd/" + name, name} {
		for dir := range dirs {
			if strings.Contains(dir, s) {
				if i := strings.LastIndex(dir, "/src/"); i != -1 {
					pkg = dir[i+len("/src/"):]
					if unique {
						unique = false
						return
					}
					unique = true
				}
			}
		}
		if unique {
			return
		}
	}
	return
}
<commit_msg>Fix paths for guesspkg<commit_after>package which

import (
	"debug/gosym"
	"errors"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// TODO(rjeczalik): support all platform types

func init() {
	// Add $GOROOT and $GOROOT_FINAL to the filtered paths.
	goroot := runtime.GOROOT()
	filtered[goroot] = struct{}{}
	if err := os.Setenv("GOROOT", ""); err == nil {
		filtered[runtime.GOROOT()] = struct{}{} // $GOROOT_FINAL
		os.Setenv("GOROOT", goroot)
	}
	// Make the order of file factory methods platform-specific.
	switch runtime.GOOS {
	case "darwin":
		alltbl = append(alltbl, newmacho, newelf, newpe)
	case "windows":
		alltbl = append(alltbl, newpe, newmacho, newelf)
	default:
		alltbl = append(alltbl, newelf, newmacho, newpe)
	}
}

type tabler interface {
	Close() error
	Pcln() ([]byte, error)
	Sym() ([]byte, error)
	Text() (uint64, error)
	Type() *PlatformType
}

// All supported symbol table builders.
var alltbl []func(string) (tabler, error)

// A path is discarded if it contains any of the filtered strings.
// TODO(rjeczalik): add $HOME/.gvm/gos?
var filtered = map[string]struct{}{
	filepath.FromSlash("/tmp/makerelease"):  {},
	filepath.FromSlash("<autogenerated>"):   {},
	filepath.FromSlash("c:/go/src"):         {},
	filepath.FromSlash("/usr/local/go/src"): {},
}

var (
	// ErrNotGoExec is an error.
	ErrNotGoExec = errors.New("which: not a Go executable")
	// ErrGuessFail is an error.
	ErrGuessFail = errors.New("which: unable to guess an import path of the main package")
)

// PlatformType represents the target platform of the executable.
type PlatformType struct {
	GOOS   string // target operating system
	GOARCH string // target architecture
}

// String gives Go platform string.
func (typ PlatformType) String() string {
	return typ.GOOS + "_" + typ.GOARCH
}

var (
	// PlatformDarwin386 represents the darwin_386 target arch.
	PlatformDarwin386 = &PlatformType{"darwin", "386"}
	// PlatformDarwinAMD64 represents the darwin_amd64 target arch.
	PlatformDarwinAMD64 = &PlatformType{"darwin", "amd64"}
	// PlatformFreeBSD386 represents the freebsd_386 target arch.
	PlatformFreeBSD386 = &PlatformType{"freebsd", "386"}
	// PlatformFreeBSDAMD64 represents the freebsd_amd64 target arch.
	PlatformFreeBSDAMD64 = &PlatformType{"freebsd", "amd64"}
	// PlatformLinux386 represents the linux_386 target arch.
	PlatformLinux386 = &PlatformType{"linux", "386"}
	// PlatformLinuxAMD64 represents the linux_amd64 target arch.
	PlatformLinuxAMD64 = &PlatformType{"linux", "amd64"}
	// PlatformWindows386 represents the windows_386 target arch.
	PlatformWindows386 = &PlatformType{"windows", "386"}
	// PlatformWindowsAMD64 represents the windows_amd64 target arch.
	PlatformWindowsAMD64 = &PlatformType{"windows", "amd64"}
)

// Exec represents a single Go executable file.
type Exec struct {
	Path  string        // Path to the executable.
	Type  *PlatformType // Fileutable file format.
	table *gosym.Table
}

// NewExec tries to detect executable type for the given path and returns
// a new executable. It fails if file does not exist, is not a Go executable or
// it's unable to parse the file format.
func NewExec(path string) (*Exec, error) {
	typ, symtab, pclntab, text, err := newtbl(path)
	if err != nil {
		return nil, err
	}
	lntab := gosym.NewLineTable(pclntab, text)
	if lntab == nil {
		return nil, ErrNotGoExec
	}
	tab, err := gosym.NewTable(symtab, lntab)
	if err != nil {
		return nil, ErrNotGoExec
	}
	return &Exec{Path: path, Type: typ, table: tab}, nil
}

// Import gives the import path of main package of given executable. It returns
// non-nil error when it fails to guess the exact path.
func (ex *Exec) Import() (string, error) {
	var dirs = make(map[string]struct{})
	for file, obj := range ex.table.Files {
		for i := range obj.Funcs {
			// main.main symbol is referenced by every file of each package
			// imported by the main package of the executable.
			if obj.Funcs[i].Sym.Name == "main.main" && !isfiltered(file) {
				dirs[filepath.Dir(file)] = struct{}{}
			}
		}
	}
	name := filepath.Base(ex.Path)
	if ex.Type == PlatformWindows386 || ex.Type == PlatformWindowsAMD64 {
		name = strings.TrimRight(name, ".exe")
	}
	if pkg, unique := guesspkg(name, dirs); unique && pkg != "" {
		return pkg, nil
	}
	return "", ErrGuessFail
}

// Import reads the import path of main package of the Go executable given by
// the path.
func Import(path string) (string, error) {
	ex, err := NewExec(path)
	if err != nil {
		return "", err
	}
	return ex.Import()
}

func newtbl(path string) (typ *PlatformType, symtab, pclntab []byte, text uint64, err error) {
	var tbl tabler
	fail := func() {
		err = errors.New("which: unable to read Go symbol table: " + err.Error())
		tbl.Close()
	}
	for _, newt := range alltbl {
		if tbl, err = newt(path); err != nil {
			err = ErrNotGoExec
			continue
		}
		if symtab, err = tbl.Sym(); err != nil {
			fail()
			continue
		}
		if pclntab, err = tbl.Pcln(); err != nil {
			fail()
			continue
		}
		if text, err = tbl.Text(); err != nil {
			fail()
			continue
		}
		typ = tbl.Type()
		tbl.Close()
		break
	}
	return
}

func isfiltered(file string) bool {
	for f := range filtered {
		if strings.Contains(file, f) {
			return true
		}
	}
	return false
}

var src = filepath.FromSlash("/src/")

func guesspkg(name string, dirs map[string]struct{}) (pkg string, unique bool) {
	defer func() {
		pkg = filepath.ToSlash(pkg)
	}()
	for _, s := range []string{"cmd" + string(os.PathSeparator) + name, name} {
		for dir := range dirs {
			if strings.Contains(dir, s) {
				if i := strings.LastIndex(dir, src); i != -1 {
					pkg = dir[i+len(src):]
					if unique {
						unique = false
						return
					}
					unique = true
				}
			}
		}
		if unique {
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"sync"
	"time"

	"go.uber.org/atomic"
)

// Facility is a destination for log entries. It can have pervasive fields
// added with With().
type Facility interface {
	LevelEnabler

	With([]Field) Facility
	Check(Entry, *CheckedEntry) *CheckedEntry
	Write(Entry, []Field) error
}

// WriterFacility creates a facility that writes logs to a WriteSyncer. By
// default, if w is nil, os.Stdout is used.
func WriterFacility(enc Encoder, ws WriteSyncer, enab LevelEnabler) Facility {
	return &ioFacility{
		LevelEnabler: enab,
		enc:          enc,
		out:          Lock(ws),
	}
}

type ioFacility struct {
	LevelEnabler
	enc Encoder
	out WriteSyncer
}

func (iof *ioFacility) With(fields []Field) Facility {
	clone := iof.clone()
	addFields(clone.enc, fields)
	return clone
}

func (iof *ioFacility) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
	if iof.Enabled(ent.Level) {
		return ce.AddFacility(ent, iof)
	}
	return ce
}

func (iof *ioFacility) Write(ent Entry, fields []Field) error {
	if err := iof.enc.WriteEntry(iof.out, ent, fields); err != nil {
		return err
	}
	if ent.Level > ErrorLevel {
		// Since we may be crashing the program, sync the output.
		return iof.out.Sync()
	}
	return nil
}

func (iof *ioFacility) clone() *ioFacility {
	return &ioFacility{
		LevelEnabler: iof.LevelEnabler,
		enc:          iof.enc.Clone(),
		out:          iof.out,
	}
}

// An ObservedLog is an encoding-agnostic representation of a log message.
type ObservedLog struct {
	Entry
	Context []Field
}

func (o ObservedLog) untimed() ObservedLog {
	o.Entry.Time = time.Time{}
	return o
}

// ObservedLogs is a concurrency-safe, ordered collection of observed logs. It
// has a fixed capacity, after which new logs begin overwriting the oldest
// observations.
type ObservedLogs struct {
	cap  int
	mu   sync.RWMutex
	logs []ObservedLog
}

// Add appends a new observed log to the collection.
func (o *ObservedLogs) Add(ent Entry, context []Field) {
	log := ObservedLog{
		Entry:   ent,
		Context: context,
	}
	o.mu.Lock()
	o.logs = append(o.logs, log)
	if len(o.logs) > o.cap {
		o.logs = o.logs[1:]
	}
	o.mu.Unlock()
}

// Len returns the number of items in the collection.
func (o *ObservedLogs) Len() int {
	o.mu.RLock()
	defer o.mu.RUnlock()
	return len(o.logs)
}

// All returns a copy of all the observed logs.
func (o *ObservedLogs) All() []ObservedLog {
	o.mu.RLock()
	ret := make([]ObservedLog, len(o.logs))
	for i := range o.logs {
		ret[i] = o.logs[i]
	}
	o.mu.RUnlock()
	return ret
}

// AllUntimed returns a copy of all the observed logs, but overwrites the
// observed timestamps with time.Time's zero value. This is useful when making
// assertions in tests.
func (o *ObservedLogs) AllUntimed() []ObservedLog {
	o.mu.RLock()
	ret := make([]ObservedLog, len(o.logs))
	for i := range o.logs {
		ret[i] = o.logs[i].untimed()
	}
	o.mu.RUnlock()
	return ret
}

type observerFacility struct {
	LevelEnabler
	sink    *ObservedLogs
	context []Field
}

// NewObserver creates a new facility that buffers logs in memory (without any
// encoding). It's particularly useful in tests, though it can serve a variety
// of other purposes as well. This constructor returns the facility itself and
// a function to retrieve the observed logs.
func NewObserver(enab LevelEnabler, cap int) (Facility, *ObservedLogs) {
	fac := &observerFacility{
		LevelEnabler: enab,
		sink:         &ObservedLogs{cap: cap, logs: make([]ObservedLog, 0, cap)},
	}
	return fac, fac.sink
}

func (o *observerFacility) With(fields []Field) Facility {
	return &observerFacility{
		LevelEnabler: o.LevelEnabler,
		sink:         o.sink,
		context:      append(o.context[:len(o.context):len(o.context)], fields...),
	}
}

func (o *observerFacility) Write(ent Entry, fields []Field) error {
	all := make([]Field, 0, len(fields)+len(o.context))
	all = append(all, o.context...)
	all = append(all, fields...)
	o.sink.Add(ent, all)
	return nil
}

func (o *observerFacility) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
	if o.Enabled(ent.Level) {
		ce = ce.AddFacility(ent, o)
	}
	return ce
}

type counters struct {
	sync.RWMutex
	counts map[string]*atomic.Uint64
}

func (c *counters) Inc(key string) uint64 {
	c.RLock()
	count, ok := c.counts[key]
	c.RUnlock()
	if ok {
		return count.Inc()
	}

	c.Lock()
	count, ok = c.counts[key]
	if ok {
		c.Unlock()
		return count.Inc()
	}

	c.counts[key] = atomic.NewUint64(1)
	c.Unlock()
	return 1
}

func (c *counters) Reset(key string) {
	c.Lock()
	count := c.counts[key]
	c.Unlock()
	count.Store(0)
}

// Sample creates a facility that samples incoming entries.
func Sample(fac Facility, tick time.Duration, first, thereafter int) Facility {
	return &sampler{
		Facility:   fac,
		tick:       tick,
		counts:     &counters{counts: make(map[string]*atomic.Uint64)},
		first:      uint64(first),
		thereafter: uint64(thereafter),
	}
}

type sampler struct {
	Facility

	tick       time.Duration
	counts     *counters
	first      uint64
	thereafter uint64
}

func (s *sampler) With(fields []Field) Facility {
	return &sampler{
		Facility:   s.Facility.With(fields),
		tick:       s.tick,
		counts:     s.counts,
		first:      s.first,
		thereafter: s.thereafter,
	}
}

func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
	if s.sampled(ent) {
		ce = s.Facility.Check(ent, ce)
	}
	return ce
}

func (s *sampler) sampled(ent Entry) bool {
	if !s.Facility.Enabled(ent.Level) {
		return false
	}
	n := s.counts.Inc(ent.Message)
	if n <= s.first {
		return true
	}
	if n == s.first+1 {
		time.AfterFunc(s.tick, func() { s.counts.Reset(ent.Message) })
	}
	return (n-s.first)%s.thereafter == 0
}
<commit_msg>sampler: simplify (#242)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zapcore

import (
	"sync"
	"time"

	"go.uber.org/atomic"
)

// Facility is a destination for log entries. It can have pervasive fields
// added with With().
type Facility interface {
	LevelEnabler

	With([]Field) Facility
	Check(Entry, *CheckedEntry) *CheckedEntry
	Write(Entry, []Field) error
}

// WriterFacility creates a facility that writes logs to a WriteSyncer. By
// default, if w is nil, os.Stdout is used.
func WriterFacility(enc Encoder, ws WriteSyncer, enab LevelEnabler) Facility {
	return &ioFacility{
		LevelEnabler: enab,
		enc:          enc,
		out:          Lock(ws),
	}
}

type ioFacility struct {
	LevelEnabler
	enc Encoder
	out WriteSyncer
}

func (iof *ioFacility) With(fields []Field) Facility {
	clone := iof.clone()
	addFields(clone.enc, fields)
	return clone
}

func (iof *ioFacility) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
	if iof.Enabled(ent.Level) {
		return ce.AddFacility(ent, iof)
	}
	return ce
}

func (iof *ioFacility) Write(ent Entry, fields []Field) error {
	if err := iof.enc.WriteEntry(iof.out, ent, fields); err != nil {
		return err
	}
	if ent.Level > ErrorLevel {
		// Since we may be crashing the program, sync the output.
		return iof.out.Sync()
	}
	return nil
}

func (iof *ioFacility) clone() *ioFacility {
	return &ioFacility{
		LevelEnabler: iof.LevelEnabler,
		enc:          iof.enc.Clone(),
		out:          iof.out,
	}
}

// An ObservedLog is an encoding-agnostic representation of a log message.
type ObservedLog struct {
	Entry
	Context []Field
}

func (o ObservedLog) untimed() ObservedLog {
	o.Entry.Time = time.Time{}
	return o
}

// ObservedLogs is a concurrency-safe, ordered collection of observed logs. It
// has a fixed capacity, after which new logs begin overwriting the oldest
// observations.
type ObservedLogs struct {
	cap  int
	mu   sync.RWMutex
	logs []ObservedLog
}

// Add appends a new observed log to the collection.
func (o *ObservedLogs) Add(ent Entry, context []Field) {
	log := ObservedLog{
		Entry:   ent,
		Context: context,
	}
	o.mu.Lock()
	o.logs = append(o.logs, log)
	if len(o.logs) > o.cap {
		o.logs = o.logs[1:]
	}
	o.mu.Unlock()
}

// Len returns the number of items in the collection.
func (o *ObservedLogs) Len() int {
	o.mu.RLock()
	defer o.mu.RUnlock()
	return len(o.logs)
}

// All returns a copy of all the observed logs.
func (o *ObservedLogs) All() []ObservedLog {
	o.mu.RLock()
	ret := make([]ObservedLog, len(o.logs))
	for i := range o.logs {
		ret[i] = o.logs[i]
	}
	o.mu.RUnlock()
	return ret
}

// AllUntimed returns a copy of all the observed logs, but overwrites the
// observed timestamps with time.Time's zero value. This is useful when making
// assertions in tests.
func (o *ObservedLogs) AllUntimed() []ObservedLog {
	o.mu.RLock()
	ret := make([]ObservedLog, len(o.logs))
	for i := range o.logs {
		ret[i] = o.logs[i].untimed()
	}
	o.mu.RUnlock()
	return ret
}

type observerFacility struct {
	LevelEnabler
	sink    *ObservedLogs
	context []Field
}

// NewObserver creates a new facility that buffers logs in memory (without any
// encoding). It's particularly useful in tests, though it can serve a variety
// of other purposes as well. This constructor returns the facility itself and
// a function to retrieve the observed logs.
func NewObserver(enab LevelEnabler, cap int) (Facility, *ObservedLogs) {
	fac := &observerFacility{
		LevelEnabler: enab,
		sink:         &ObservedLogs{cap: cap, logs: make([]ObservedLog, 0, cap)},
	}
	return fac, fac.sink
}

func (o *observerFacility) With(fields []Field) Facility {
	return &observerFacility{
		LevelEnabler: o.LevelEnabler,
		sink:         o.sink,
		context:      append(o.context[:len(o.context):len(o.context)], fields...),
	}
}

func (o *observerFacility) Write(ent Entry, fields []Field) error {
	all := make([]Field, 0, len(fields)+len(o.context))
	all = append(all, o.context...)
	all = append(all, fields...)
	o.sink.Add(ent, all)
	return nil
}

func (o *observerFacility) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
	if o.Enabled(ent.Level) {
		ce = ce.AddFacility(ent, o)
	}
	return ce
}

type counters struct {
	sync.RWMutex
	counts map[string]*atomic.Uint64
}

func (c *counters) Inc(key string) uint64 {
	c.RLock()
	count, ok := c.counts[key]
	c.RUnlock()
	if ok {
		return count.Inc()
	}

	c.Lock()
	count, ok = c.counts[key]
	if ok {
		c.Unlock()
		return count.Inc()
	}

	c.counts[key] = atomic.NewUint64(1)
	c.Unlock()
	return 1
}

func (c *counters) Reset(key string) {
	c.Lock()
	count := c.counts[key]
	c.Unlock()
	count.Store(0)
}

// Sample creates a facility that samples incoming entries.
func Sample(fac Facility, tick time.Duration, first, thereafter int) Facility {
	return &sampler{
		Facility:   fac,
		tick:       tick,
		counts:     &counters{counts: make(map[string]*atomic.Uint64)},
		first:      uint64(first),
		thereafter: uint64(thereafter),
	}
}

type sampler struct {
	Facility

	tick       time.Duration
	counts     *counters
	first      uint64
	thereafter uint64
}

func (s *sampler) With(fields []Field) Facility {
	return &sampler{
		Facility:   s.Facility.With(fields),
		tick:       s.tick,
		counts:     s.counts,
		first:      s.first,
		thereafter: s.thereafter,
	}
}

func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
	if !s.Enabled(ent.Level) {
		return ce
	}
	if n := s.counts.Inc(ent.Message); n > s.first {
		if n == s.first+1 {
			time.AfterFunc(s.tick, func() { s.counts.Reset(ent.Message) })
		}
		if (n-s.first)%s.thereafter != 0 {
			return ce
		}
	}
	return s.Facility.Check(ent, ce)
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"text/tabwriter"
	"os"
)

func (c *Command) PrintHead(){
	fmt.Printf("%s\n", c.Short.Text())
	//if c.Long != nil {
	//	fmt.Printf("\n%s\n", c.Long.Text())
	//}
}

func (c *Command) PrintUsage() {
	if c.Usage != "" {
		fmt.Printf("\nUsage:\n  %s\n", c.GetUsageWithParent())
	} else {
		c.PrintSubCommands()
	}
}

func (c *Command) PrintSample() {
	if c.Sample != "" {
		fmt.Printf("\nSample:\n  %s\n", c.Sample)
	}
}

func (c *Command) PrintSubCommands() {
	if len(c.subCommands) > 0 {
		fmt.Printf("\nCommands:\n")

		w := tabwriter.NewWriter(os.Stdout, 8, 0, 1, ' ', 0)
		for _, cmd := range c.subCommands {
			if cmd.Hidden {
				continue
			}
			fmt.Fprintf(w, "  %s\t%s\n", cmd.Name, cmd.Short.Text())
		}
		w.Flush()
	}
}

func (c *Command) PrintFlags(ctx *Context) {
	if len(c.flags.Flags()) == 0 {
		return
	}

	fmt.Printf("\nFlags:\n")
	w := tabwriter.NewWriter(os.Stdout, 8, 0, 1, ' ', 0)
	fs := c.Flags()
	if ctx != nil {
		fs = ctx.Flags()
	}
	for _, flag := range fs.Flags() {
		if flag.Hidden {
			continue
		}
		fmt.Fprintf(w, "  --%s\t%s\n", flag.Name, flag.Usage.Text())
	}
	w.Flush()
}

func (c *Command) PrintFailed(err error, suggestion string) {
	Errorf("ERROR: %v\n", err)
	fmt.Printf("%s\n", suggestion)
}

func (c *Command) PrintTail() {
	fmt.Printf("\nUse `%s --help` for more information.\n", c.Name)
}
<commit_msg>shorthand<commit_after>package cli

import (
	"fmt"
	"text/tabwriter"
	"os"
)

func (c *Command) PrintHead(){
	fmt.Printf("%s\n", c.Short.Text())
	//if c.Long != nil {
	//	fmt.Printf("\n%s\n", c.Long.Text())
	//}
}

func (c *Command) PrintUsage() {
	if c.Usage != "" {
		fmt.Printf("\nUsage:\n  %s\n", c.GetUsageWithParent())
	} else {
		c.PrintSubCommands()
	}
}

func (c *Command) PrintSample() {
	if c.Sample != "" {
		fmt.Printf("\nSample:\n  %s\n", c.Sample)
	}
}

func (c *Command) PrintSubCommands() {
	if len(c.subCommands) > 0 {
		fmt.Printf("\nCommands:\n")

		w := tabwriter.NewWriter(os.Stdout, 8, 0, 1, ' ', 0)
		for _, cmd := range c.subCommands {
			if cmd.Hidden {
				continue
			}
			fmt.Fprintf(w, "  %s\t%s\n", cmd.Name, cmd.Short.Text())
		}
		w.Flush()
	}
}

func (c *Command) PrintFlags(ctx *Context) {
	if len(c.flags.Flags()) == 0 {
		return
	}

	fmt.Printf("\nFlags:\n")
	w := tabwriter.NewWriter(os.Stdout, 8, 0, 1, ' ', 0)
	fs := c.Flags()
	if ctx != nil {
		fs = ctx.Flags()
	}
	for _, flag := range fs.Flags() {
		if flag.Hidden {
			continue
		}
		format := "--%s\t%s\n"
		if flag.Shorthand != "" {
			format = fmt.Sprintf("-%s, %s", flag.Shorthand, format)
		}
		fmt.Fprintf(w, "  " + format, flag.Name, flag.Usage.Text())
	}
	w.Flush()
}

func (c *Command) PrintFailed(err error, suggestion string) {
	Errorf("ERROR: %v\n", err)
	fmt.Printf("%s\n", suggestion)
}

func (c *Command) PrintTail() {
	fmt.Printf("\nUse `%s --help` for more information.\n", c.Name)
}
<|endoftext|>


<commit_before>package cliapp

import "github.com/mitchellh/cli"
import "github.com/bpicode/fritzctl/configurer"
import "github.com/bpicode/fritzctl/assert"

import "strings"

type configureCommand struct {
}

func (cmd *configureCommand) Help() string {
	return strings.Join([]string{
		"Walk through the configuration of fritzctl interactively.",
		"Configuration file is saved at the end of the survey.",
		"Run fritzctl with administrator privileges if the configuration file cannot be saved by a normal user.",
	}, "\n")
}

func (cmd *configureCommand) Synopsis() string {
	return "Configure fritzctl"
}

func (cmd *configureCommand) Run(args []string) int {
	cli := configurer.New()
	cli.ApplyDefaults(configurer.Defaults())
	cli.Greet()
	cli.Obtain()
	err := cli.Write()
	assert.NoError(err, "Error writing configuration file:", err)
	return 0
}

func configure() (cli.Command, error) {
	p := configureCommand{}
	return &p, nil
}
<commit_msg>lowercase synopsis and error message<commit_after>package cliapp

import "github.com/mitchellh/cli"
import "github.com/bpicode/fritzctl/configurer"
import "github.com/bpicode/fritzctl/assert"

import "strings"

type configureCommand struct {
}

func (cmd *configureCommand) Help() string {
	return strings.Join([]string{
		"Walk through the configuration of fritzctl interactively.",
		"Configuration file is saved at the end of the survey.",
		"Run fritzctl with administrator privileges if the configuration file cannot be saved by a normal user.",
	}, "\n")
}

func (cmd *configureCommand) Synopsis() string {
	return "configure fritzctl"
}

func (cmd *configureCommand) Run(args []string) int {
	cli := configurer.New()
	cli.ApplyDefaults(configurer.Defaults())
	cli.Greet()
	cli.Obtain()
	err := cli.Write()
	assert.NoError(err, "error writing configuration file:", err)
	return 0
}

func configure() (cli.Command, error) {
	p := configureCommand{}
	return &p, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package set

import (
	"fmt"
	"strings"
	"testing"

	"sigs.k8s.io/kustomize/api/filesys"
	testutils_test "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/testutils"
)

func TestSetImage(t *testing.T) {
	type given struct {
		args         []string
		infileImages []string
	}
	type expected struct {
		fileOutput []string
		err        error
	}
	testCases := []struct {
		description string
		given       given
		expected    expected
	}{
		{
			given: given{
				args: []string{"image1=my-image1:my-tag"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag",
				}},
		},
		{
			given: given{
				args: []string{"image1=my-image1:1234"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: \"1234\"",
				}},
		},
		{
			given: given{
				args: []string{"image1=my-image1:3.2e2"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: \"3.2e2\"",
				}},
		},
		{
			given: given{
				args: []string{"image1=my-image1@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"  name: image1",
					"  newName: my-image1",
				}},
		},
		{
			given: given{
				args: []string{"image1:my-tag"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newTag: my-tag",
				}},
		},
		{
			given: given{
				args: []string{"image1@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"  name: image1",
				}},
		},
		{
			description: "<image>=<image>",
			given: given{
				args: []string{"ngnix=localhost:5000/my-project/ngnix"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: ngnix",
					"  newName: localhost:5000/my-project/ngnix",
				}},
		},
		{
			given: given{
				args: []string{"ngnix=localhost:5000/my-project/ngnix:dev-01"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: ngnix",
					"  newName: localhost:5000/my-project/ngnix",
					"  newTag: dev-01",
				}},
		},
		{
			description: "override file",
			given: given{
				args: []string{"image1=foo.bar.foo:8800/foo/image1:foo-bar"},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: foo.bar.foo:8800/foo/image1",
					"  newTag: foo-bar",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
				}},
		},
		{
			description: "override new tag and new name with just a new tag",
			given: given{
				args: []string{"image1:v1"},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newTag: my-tag",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newTag: v1",
				}},
		},
		{
			description: "multiple args with multiple overrides",
			given: given{
				args: []string{
					"image1=foo.bar.foo:8800/foo/image1:foo-bar",
					"image2=my-image2@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"image3:my-tag",
				},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag1",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
					"- name: image3",
					"  newTag: my-tag",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: foo.bar.foo:8800/foo/image1",
					"  newTag: foo-bar",
					"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"  name: image2",
					"  newName: my-image2",
					"- name: image3",
					"  newTag: my-tag",
				}},
		},
		{
			description: "error: no args",
			expected: expected{
				err: errImageNoArgs,
			},
		},
		{
			description: "error: invalid args",
			given: given{
				args: []string{"bad", "args"},
			},
			expected: expected{
				err: errImageInvalidArgs,
			},
		},
	}

	for _, tc := range testCases {
		t.Run(fmt.Sprintf("%s%v", tc.description, tc.given.args), func(t *testing.T) {
			fSys := filesys.MakeFsInMemory()
			cmd := newCmdSetImage(fSys)

			if len(tc.given.infileImages) > 0 {
				// write file with infileImages
				testutils_test.WriteTestKustomizationWith(
					fSys,
					[]byte(strings.Join(tc.given.infileImages, "\n")))
			} else {
				testutils_test.WriteTestKustomization(fSys)
			}

			// act
			err := cmd.RunE(cmd, tc.given.args)

			// assert
			if err != tc.expected.err {
				t.Errorf("Unexpected error from set image command. Actual: %v\nExpected: %v", err, tc.expected.err)
				t.FailNow()
			}

			content, err := testutils_test.ReadTestKustomization(fSys)
			if err != nil {
				t.Errorf("unexpected read error: %v", err)
				t.FailNow()
			}
			expectedStr := strings.Join(tc.expected.fileOutput, "\n")
			if !strings.Contains(string(content), expectedStr) {
				t.Errorf("unexpected image in kustomization file. \nActual:\n%s\nExpected:\n%s", content, expectedStr)
			}
		})
	}
}
<commit_msg>Add set image test with patchesJson6902.<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package set

import (
	"fmt"
	"strings"
	"testing"

	"sigs.k8s.io/kustomize/api/filesys"
	testutils_test "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/testutils"
)

func TestSetImage(t *testing.T) {
	type given struct {
		args         []string
		infileImages []string
	}
	type expected struct {
		fileOutput []string
		err        error
	}
	testCases := []struct {
		description string
		given       given
		expected    expected
	}{
		{
			given: given{
				args: []string{"image1=my-image1:my-tag"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag",
				}},
		},
		{
			given: given{
				args: []string{"image1=my-image1:1234"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: \"1234\"",
				}},
		},
		{
			given: given{
				args: []string{"image1=my-image1:3.2e2"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: \"3.2e2\"",
				}},
		},
		{
			given: given{
				args: []string{"image1=my-image1@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"  name: image1",
					"  newName: my-image1",
				}},
		},
		{
			given: given{
				args: []string{"image1:my-tag"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newTag: my-tag",
				}},
		},
		{
			given: given{
				args: []string{"image1@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"  name: image1",
				}},
		},
		{
			description: "<image>=<image>",
			given: given{
				args: []string{"ngnix=localhost:5000/my-project/ngnix"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: ngnix",
					"  newName: localhost:5000/my-project/ngnix",
				}},
		},
		{
			given: given{
				args: []string{"ngnix=localhost:5000/my-project/ngnix:dev-01"},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: ngnix",
					"  newName: localhost:5000/my-project/ngnix",
					"  newTag: dev-01",
				}},
		},
		{
			description: "override file",
			given: given{
				args: []string{"image1=foo.bar.foo:8800/foo/image1:foo-bar"},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: foo.bar.foo:8800/foo/image1",
					"  newTag: foo-bar",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
				}},
		},
		{
			description: "override file with patch",
			given: given{
				args: []string{"image1=foo.bar.foo:8800/foo/image1:foo-bar"},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
					"patchesJson6902:",
					"- patch: |-",
					"    - op: remove",
					"      path: /spec/selector",
					"  target:",
					"    kind: Service",
					"    name: foo",
					"    version: v1",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: foo.bar.foo:8800/foo/image1",
					"  newTag: foo-bar",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
					"patchesJson6902:",
					"- patch: |-",
					"    - op: remove",
					"      path: /spec/selector",
					"  target:",
					"    kind: Service",
					"    name: foo",
					"    version: v1",
				}},
		},
		{
			description: "override new tag and new name with just a new tag",
			given: given{
				args: []string{"image1:v1"},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newTag: my-tag",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newTag: v1",
				}},
		},
		{
			description: "multiple args with multiple overrides",
			given: given{
				args: []string{
					"image1=foo.bar.foo:8800/foo/image1:foo-bar",
					"image2=my-image2@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"image3:my-tag",
				},
				infileImages: []string{
					"images:",
					"- name: image1",
					"  newName: my-image1",
					"  newTag: my-tag1",
					"- name: image2",
					"  newName: my-image2",
					"  newTag: my-tag2",
					"- name: image3",
					"  newTag: my-tag",
				},
			},
			expected: expected{
				fileOutput: []string{
					"images:",
					"- name: image1",
					"  newName: foo.bar.foo:8800/foo/image1",
					"  newTag: foo-bar",
					"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
					"  name: image2",
					"  newName: my-image2",
					"- name: image3",
					"  newTag: my-tag",
				}},
		},
		{
			description: "error: no args",
			expected: expected{
				err: errImageNoArgs,
			},
		},
		{
			description: "error: invalid args",
			given: given{
				args: []string{"bad", "args"},
			},
			expected: expected{
				err: errImageInvalidArgs,
			},
		},
	}

	for _, tc := range testCases {
		t.Run(fmt.Sprintf("%s%v", tc.description, tc.given.args), func(t *testing.T) {
			fSys := filesys.MakeFsInMemory()
			cmd := newCmdSetImage(fSys)

			if len(tc.given.infileImages) > 0 {
				// write file with infileImages
				testutils_test.WriteTestKustomizationWith(
					fSys,
					[]byte(strings.Join(tc.given.infileImages, "\n")))
			} else {
				testutils_test.WriteTestKustomization(fSys)
			}

			// act
			err := cmd.RunE(cmd, tc.given.args)

			// assert
			if err != tc.expected.err {
				t.Errorf("Unexpected error from set image command. Actual: %v\nExpected: %v", err, tc.expected.err)
				t.FailNow()
			}

			content, err := testutils_test.ReadTestKustomization(fSys)
			if err != nil {
				t.Errorf("unexpected read error: %v", err)
				t.FailNow()
			}
			expectedStr := strings.Join(tc.expected.fileOutput, "\n")
			if !strings.Contains(string(content), expectedStr) {
				t.Errorf("unexpected image in kustomization file. \nActual:\n%s\nExpected:\n%s", content, expectedStr)
			}
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"gmq/communication"
	"log"
	"net"
	"os"
)

var action, qname, payload, protocol, server, logfile, output string
var logger *log.Logger

func main() {

	flag.StringVar(&action, "a", "", "Action to be performed: P = Publish, S = Subscribe")
	flag.StringVar(&qname, "q", "", "Queue name")
	flag.StringVar(&payload, "m", "", "Base64 encoded payload to use (only needed with \"-a P\")")
	flag.StringVar(&protocol, "p", "tcp", "Protocol type: tcp4 or tcp6 (default to system settings)")
	flag.StringVar(&logfile, "l", "", "Log destination: if not set, defaults to STDOUT")
	flag.StringVar(&server, "s", "localhost:4567", "Server address: (DNS|IP)[:PORT] form ")
	flag.StringVar(&output, "o", "", "Output file, if empty defaults to STDOUT")
	flag.Parse()

	logger = initLog()

	out := configureOutput()
	logger.Printf("Printing output to %s\n", out.Name())
	mex := configureCall()
	resp := callServer(mex)
	if _, err := out.Write(resp); err != nil {
		logger.Printf("There was an error printing response from server to %s\n", out)
		logger.Println("Response:\n%s", string(resp))
	}

}

func initLog() *log.Logger {
	file, err := os.OpenFile(logfile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return log.New(os.Stdout, "[GMQ Client] ", log.LstdFlags)

	}
	return log.New(file, "[GMQ Client] ", log.LstdFlags)
}

func configureCall() *gmqnet.Message {
	if action == "" {
		logger.Fatalln("Action cannot be null! Set -a")
	}
	if qname == "" {
		logger.Fatalln("Queue name cannot be null! Set -q")
	}

	return &gmqnet.Message{
		Operation: action,
		Queue:     qname,
		Payload:   payload,
	}
}

func configureOutput() *os.File {
	if output == "" {
		return os.Stdout
	}
	out, err := os.OpenFile(output, os.O_RDWR|os.O_CREATE, 0660)
	if err != nil {
		logger.Fatalln("Error opening output file!")
	}
	return out
}

func callServer(mex *gmqnet.Message) []byte {
	var resp bytes.Buffer
	conn, err := net.Dial(protocol, server)
	if err != nil {
		logger.Fatalf("Error in call to server:\n%T\n%s\n", err, err.Error())
	}
	if _, err := conn.Write(gmqnet.WriteMessage(mex)); err != nil {
		logger.Fatalf("Error in call to server:\n%T\n%s\n", err, err.Error())
	}
	n, err := conn.Read(resp.Bytes())
	if err != nil {
		logger.Fatalf("Error in call to server:\n%T\n%s\n", err, err.Error())
	}
	return resp.Bytes()[:n]
}
<commit_msg>Fixing client<commit_after>package main

import (
	"flag"
	"gmq/communication"
	"io/ioutil"
	"log"
	"net"
	"os"
)

var action, qname, payload, protocol, server, logfile, output string
var logger *log.Logger

func main() {

	flag.StringVar(&action, "a", "", "Action to be performed: P = Publish, S = Subscribe, L = List")
	flag.StringVar(&qname, "q", "", "Queue name")
	flag.StringVar(&payload, "m", "", "Base64 encoded payload to use (only needed with \"-a P\")")
	flag.StringVar(&protocol, "p", "tcp", "Protocol type: tcp4 or tcp6 (default to system settings)")
	flag.StringVar(&logfile, "l", "", "Log destination: if not set, defaults to STDOUT")
	flag.StringVar(&server, "s", "localhost:4567", "Server address: (DNS|IP)[:PORT] form ")
	flag.StringVar(&output, "o", "", "Output file, if empty defaults to STDOUT")
	flag.Parse()

	logger = initLog()

	out := configureOutput()
	logger.Printf("Printing output to %s\n", out.Name())
	mex := configureCall()
	resp := callServer(mex)
	if _, err := out.Write(resp); err != nil {
		logger.Printf("There was an error printing response from server to %s\n", out)
		logger.Println("Response:\n%s", string(resp))
	}

}

func initLog() *log.Logger {
	file, err := os.OpenFile(logfile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return log.New(os.Stdout, "[GMQ Client] ", log.LstdFlags)

	}
	return log.New(file, "[GMQ Client] ", log.LstdFlags)
}

func configureCall() *gmqnet.Message {
	if action == "" {
		logger.Fatalln("Action cannot be null! Set -a")
	}
	if qname == "" {
		logger.Fatalln("Queue name cannot be null! Set -q")
	}

	return &gmqnet.Message{
		Operation: action,
		Queue:     qname,
		Payload:   payload,
	}
}

func configureOutput() *os.File {
	if output == "" {
		return os.Stdout
	}
	out, err := os.OpenFile(output, os.O_RDWR|os.O_CREATE, 0660)
	if err != nil {
		logger.Fatalln("Error opening output file!")
	}
	return out
}

func callServer(mex *gmqnet.Message) []byte {
	conn, err := net.Dial(protocol, server)
	defer conn.Close()
	if err != nil {
		logger.Fatalf("Error connecting to the server:\n%T\n%s\n", err, err.Error())
	}
	if _, err := conn.Write(gmqnet.WriteMessage(mex)); err != nil {
		logger.Fatalf("Error posting the message to server:\n%T\n%s\n", err, err.Error())
	}
	resp, err := ioutil.ReadAll(conn)
	if err != nil {
		logger.Fatalf("Error reading response from server:\n%T\n%s\n", err, err.Error())
	}
	return resp
}
<|endoftext|>


<commit_before>package postgres

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/caarlos0/env"
	"github.com/jmoiron/sqlx"
	// Used pg drive on sqlx
	_ "github.com/lib/pq"

	"database/sql"

	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/config"
)

const (
	pageNumberKey   = "_page"
	pageSizeKey     = "_page_size"
	defaultPageSize = "10"
)

// Conn connect on PostgreSQL
// Used sqlx
func Conn() (db *sqlx.DB) {
	cfg := config.Prest{}
	env.Parse(&cfg)

	db, err := sqlx.Connect("postgres", fmt.Sprintf("user=%s dbname=%s host=%s port=%v sslmode=disable", cfg.PGUser, cfg.PGDatabase, cfg.PGHost, cfg.PGPort))
	if err != nil {
		panic(fmt.Sprintf("Unable to connection to database: %v\n", err))
	}
	return
}

// WhereByRequest create interface for queries + where
func WhereByRequest(r *http.Request) (whereSyntax string) {
	u, _ := url.Parse(r.URL.String())
	where := []string{}
	for key, val := range u.Query() {
		if !strings.HasPrefix(key, "_") {
			keyInfo := strings.Split(key, ":")
			if len(keyInfo) > 1 {
				switch keyInfo[1] {
				case "jsonb":
					jsonField := strings.Split(keyInfo[0], "->>")
					where = append(where, fmt.Sprintf("%s->>'%s'='%s'", jsonField[0], jsonField[1], val[0]))
				default:
					where = append(where, fmt.Sprintf("%s='%s'", keyInfo[0], val[0]))

				}
				continue
			}
			where = append(where, fmt.Sprintf("%s='%s'", key, val[0]))
		}
	}

	whereSyntax = strings.Join(where, " and ")
	return
}

// Query process queries
func Query(SQL string, params ...interface{}) (jsonData []byte, err error) {
	db := Conn()
	rows, err := db.Queryx(SQL, params...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return nil, err
	}

	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rows.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	jsonData, err = json.Marshal(tableData)

	return
}

// PaginateIfPossible func
func PaginateIfPossible(r *http.Request) (paginatedQuery string) {
	u, _ := url.Parse(r.URL.String())
	values := u.Query()
	if _, ok := values[pageNumberKey]; !ok {
		paginatedQuery = ""
		return
	}
	pageNumber := values[pageNumberKey][0]
	pageSize := defaultPageSize
	if size, ok := values[pageSizeKey]; ok {
		pageSize = size[0]
	}
	paginatedQuery = fmt.Sprintf("LIMIT %s OFFSET(%s - 1) * %s", pageSize, pageNumber, pageSize)
	return
}

// Insert execute insert sql into a table
func Insert(database, schema, table string, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := make([]string, 0)
	values := make([]string, 0)
	for key, value := range body.Data {
		fields = append(fields, key)
		values = append(values, value)
	}
	colsName := strings.Join(fields, ", ")
	colsValue := strings.Join(values, "', '")
	sql := fmt.Sprintf("INSERT INTO %s.%s.%s (%s) VALUES ('%s')", database, schema, table, colsName, colsValue)

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Delete execute delete sql into a table
func Delete(database, schema, table, where string) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	sql := fmt.Sprintf("DELETE FROM %s.%s.%s", database, schema, table)
	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Update execute update sql into a table
func Update(database, schema, table, where string, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := []string{}
	for key, value := range body.Data {
		fields = append(fields, fmt.Sprintf("%s='%s'", key, value))
	}
	setSyntax := strings.Join(fields, ", ")

	sql := fmt.Sprintf("UPDATE %s.%s.%s SET %s", database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}
<commit_msg>WhereByRequest now return a array of values<commit_after>package postgres

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/caarlos0/env"
	"github.com/jmoiron/sqlx"
	// Used pg drive on sqlx
	_ "github.com/lib/pq"

	"database/sql"

	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/config"
)

const (
	pageNumberKey   = "_page"
	pageSizeKey     = "_page_size"
	defaultPageSize = "10"
)

// Conn connect on PostgreSQL
// Used sqlx
func Conn() (db *sqlx.DB) {
	cfg := config.Prest{}
	env.Parse(&cfg)

	db, err := sqlx.Connect("postgres", fmt.Sprintf("user=%s dbname=%s host=%s port=%v sslmode=disable", cfg.PGUser, cfg.PGDatabase, cfg.PGHost, cfg.PGPort))
	if err != nil {
		panic(fmt.Sprintf("Unable to connection to database: %v\n", err))
	}
	return
}

// WhereByRequest create interface for queries + where
func WhereByRequest(r *http.Request) (whereSyntax string, values []string) {
	u, _ := url.Parse(r.URL.String())
	where := []string{}
	for key, val := range u.Query() {
		if !strings.HasPrefix(key, "_") {
			keyInfo := strings.Split(key, ":")
			if len(keyInfo) > 1 {
				switch keyInfo[1] {
				case "jsonb":
					jsonField := strings.Split(keyInfo[0], "->>")
					where = append(where, fmt.Sprintf("%s->>'%s'=?", jsonField[0], jsonField[1]))
					values = append(values, val[0])
				default:
					where = append(where, fmt.Sprintf("%s=?", keyInfo[0]))
					values = append(values, val[0])
				}
				continue
			}
			where = append(where, fmt.Sprintf("%s=?", key))
			values = append(values, val[0])

		}
	}

	whereSyntax = strings.Join(where, " and ")
	return
}

// Query process queries
func Query(SQL string, params ...interface{}) (jsonData []byte, err error) {
	db := Conn()
	rows, err := db.Queryx(SQL, params...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return nil, err
	}

	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rows.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	jsonData, err = json.Marshal(tableData)

	return
}

// PaginateIfPossible func
func PaginateIfPossible(r *http.Request) (paginatedQuery string) {
	u, _ := url.Parse(r.URL.String())
	values := u.Query()
	if _, ok := values[pageNumberKey]; !ok {
		paginatedQuery = ""
		return
	}
	pageNumber := values[pageNumberKey][0]
	pageSize := defaultPageSize
	if size, ok := values[pageSizeKey]; ok {
		pageSize = size[0]
	}
	paginatedQuery = fmt.Sprintf("LIMIT %s OFFSET(%s - 1) * %s", pageSize, pageNumber, pageSize)
	return
}

// Insert execute insert sql into a table
func Insert(database, schema, table string, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := make([]string, 0)
	values := make([]string, 0)
	for key, value := range body.Data {
		fields = append(fields, key)
		values = append(values, value)
	}
	colsName := strings.Join(fields, ", ")
	colsValue := strings.Join(values, "', '")
	sql := fmt.Sprintf("INSERT INTO %s.%s.%s (%s) VALUES ('%s')", database, schema, table, colsName, colsValue)

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Delete execute delete sql into a table
func Delete(database, schema, table, where string) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	sql := fmt.Sprintf("DELETE FROM %s.%s.%s", database, schema, table)
	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Update execute update sql into a table
func Update(database, schema, table, where string, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := []string{}
	for key, value := range body.Data {
		fields = append(fields, fmt.Sprintf("%s='%s'", key, value))
	}
	setSyntax := strings.Join(fields, ", ")

	sql := fmt.Sprintf("UPDATE %s.%s.%s SET %s", database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/aybabtme/color/brush"
	"github.com/codegangsta/cli"
	gh "github.com/crosbymichael/octokat"
	"github.com/crosbymichael/pulls"
	"os"
	"path"
	"strings"
	"time"
)

const (
	defultTimeFormat = time.RFC822
)

var (
	m          *pulls.Maintainer
	configPath = path.Join(os.Getenv("HOME"), ".maintainercfg")
)

func listOpenPullsCmd(c *cli.Context) {
	var (
		pulls []*gh.PullRequest
		err   error
	)

	if c.Bool("no-merge") {
		pulls, err = m.GetNoMergePullRequests()
	} else {
		pulls, err = m.GetPullRequests("open")
	}
	if err != nil {
		writeError("Error getting pull reqeusts %s", err)
	}
	displayPullRequests(pulls)
}

func listClosedPullsCmd(c *cli.Context) {
	pulls, err := m.GetPullRequests("closed")
	if err != nil {
		writeError("Error getting pull reqeusts %s", err)
	}
	displayPullRequests(pulls)
}

func displayPullRequests(pulls []*gh.PullRequest) {
	w := newTabwriter()
	for _, p := range pulls {
		fmt.Fprintf(w, "%d\t%s\t%s\n", p.Number, truncate(p.Title), p.CreatedAt.Format(defultTimeFormat))
	}

	if err := w.Flush(); err != nil {
		writeError("%s", err)
	}
}

func showPullRequestCmd(c *cli.Context) {
	pr, err := m.GetPullRequest(c.Args()[0])
	if err != nil {
		writeError("%s", err)
	}
	displayPullRequest(pr)
}

func displayPullRequest(pr *gh.PullRequest) {
	fmt.Fprint(os.Stdout, brush.Green("Pull Request:"), "\n")
	fmt.Fprintf(os.Stdout, "No: %d\nTitle: %s\n\n", pr.Number, pr.Title)

	lines := strings.Split(pr.Body, "\n")
	for i, l := range lines {
		lines[i] = "\t" + l
	}
	fmt.Fprintf(os.Stdout, "Description:\n\n%s\n\n", strings.Join(lines, "\n"))

	comments, err := m.GetComments(pr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error getting comments: %s\n", err)
	} else {
		fmt.Fprintln(os.Stdout, "Comments:\n")
		for _, c := range comments {
			fmt.Fprintf(os.Stdout, "@%s %s\n%s\n", brush.Red(c.User.Login), c.CreatedAt.Format(defultTimeFormat), c.Body)
			fmt.Fprint(os.Stdout, "\n\n")
		}
	}
}

func repositoryInfoCmd(c *cli.Context) {
	r, err := m.Repository()
	if err != nil {
		writeError("%s", err)
	}
	fmt.Fprintf(os.Stdout, "Name: %s\nForks: %d\nStars: %d\nIssues: %d\n", r.Name, r.Forks, r.Watchers, r.OpenIssues)
}

func authCmd(c *cli.Context) {
	if token := c.String("add"); token != "" {
		if err := saveConfig(Config{token}); err != nil {
			writeError("%s", err)
		}
		return
	}
	// Display token and user information
	if config := loadConfig(); config.Token != "" {
		fmt.Fprintf(os.Stdout, "Token: %s\n", config.Token)
	} else {
		fmt.Fprintf(os.Stderr, "No token registered\n")
		os.Exit(1)
	}
}

func loadCommands(app *cli.App) {
	app.Commands = []cli.Command{
		{
			Name:      "open",
			ShortName: "o",
			Usage:     "List all open pull requests for the current repository",
			Action:    listOpenPullsCmd,
			Flags: []cli.Flag{
				cli.BoolFlag{"no-merge", "display only prs that cannot be merged"},
			},
		},
		{
			Name:      "closed",
			ShortName: "c",
			Usage:     "List all closed pull requests for the current repository",
			Action:    listClosedPullsCmd,
		},
		{
			Name:      "show",
			ShortName: "s",
			Usage:     "Show the pull request based on the number",
			Action:    showPullRequestCmd,
		},
		{
			Name:      "repository",
			ShortName: "repo",
			Usage:     "List information about the current repository",
			Action:    repositoryInfoCmd,
		},
		{
			Name:   "auth",
			Usage:  "Add a github token for authentication",
			Action: authCmd,
			Flags: []cli.Flag{
				cli.StringFlag{"add", "", "add new token for authentication"},
			},
		},
	}
}

func main() {
	app := cli.NewApp()

	app.Name = "pulls"
	app.Usage = "Manage github pull requets"
	app.Version = "0.0.1"

	client := gh.NewClient()

	config := loadConfig()
	if config.Token != "" {
		client.WithToken(config.Token)
	}

	org, name, err := getOriginUrl()
	if err != nil {
		panic(err)
	}
	t, err := pulls.NewMaintainer(client, org, name)
	if err != nil {
		panic(err)
	}
	m = t

	loadCommands(app)

	app.Run(os.Args)
}
<commit_msg>fix some typos<commit_after>package main

import (
	"fmt"
	"github.com/aybabtme/color/brush"
	"github.com/codegangsta/cli"
	gh "github.com/crosbymichael/octokat"
	"github.com/crosbymichael/pulls"
	"os"
	"path"
	"strings"
	"time"
)

const (
	defaultTimeFormat = time.RFC822
)

var (
	m          *pulls.Maintainer
	configPath = path.Join(os.Getenv("HOME"), ".maintainercfg")
)

func listOpenPullsCmd(c *cli.Context) {
	var (
		pulls []*gh.PullRequest
		err   error
	)

	if c.Bool("no-merge") {
		pulls, err = m.GetNoMergePullRequests()
	} else {
		pulls, err = m.GetPullRequests("open")
	}
	if err != nil {
		writeError("Error getting pull requests %s", err)
	}
	displayPullRequests(pulls)
}

func listClosedPullsCmd(c *cli.Context) {
	pulls, err := m.GetPullRequests("closed")
	if err != nil {
		writeError("Error getting pull requests %s", err)
	}
	displayPullRequests(pulls)
}

func displayPullRequests(pulls []*gh.PullRequest) {
	w := newTabwriter()
	for _, p := range pulls {
		fmt.Fprintf(w, "%d\t%s\t%s\n", p.Number, truncate(p.Title), p.CreatedAt.Format(defaultTimeFormat))
	}

	if err := w.Flush(); err != nil {
		writeError("%s", err)
	}
}

func showPullRequestCmd(c *cli.Context) {
	pr, err := m.GetPullRequest(c.Args()[0])
	if err != nil {
		writeError("%s", err)
	}
	displayPullRequest(pr)
}

func displayPullRequest(pr *gh.PullRequest) {
	fmt.Fprint(os.Stdout, brush.Green("Pull Request:"), "\n")
	fmt.Fprintf(os.Stdout, "No: %d\nTitle: %s\n\n", pr.Number, pr.Title)

	lines := strings.Split(pr.Body, "\n")
	for i, l := range lines {
		lines[i] = "\t" + l
	}
	fmt.Fprintf(os.Stdout, "Description:\n\n%s\n\n", strings.Join(lines, "\n"))

	comments, err := m.GetComments(pr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error getting comments: %s\n", err)
	} else {
		fmt.Fprintln(os.Stdout, "Comments:\n")
		for _, c := range comments {
			fmt.Fprintf(os.Stdout, "@%s %s\n%s\n", brush.Red(c.User.Login), c.CreatedAt.Format(defaultTimeFormat), c.Body)
			fmt.Fprint(os.Stdout, "\n\n")
		}
	}
}

func repositoryInfoCmd(c *cli.Context) {
	r, err := m.Repository()
	if err != nil {
		writeError("%s", err)
	}
	fmt.Fprintf(os.Stdout, "Name: %s\nForks: %d\nStars: %d\nIssues: %d\n", r.Name, r.Forks, r.Watchers, r.OpenIssues)
}

func authCmd(c *cli.Context) {
	if token := c.String("add"); token != "" {
		if err := saveConfig(Config{token}); err != nil {
			writeError("%s", err)
		}
		return
	}
	// Display token and user information
	if config := loadConfig(); config.Token != "" {
		fmt.Fprintf(os.Stdout, "Token: %s\n", config.Token)
	} else {
		fmt.Fprintf(os.Stderr, "No token registered\n")
		os.Exit(1)
	}
}

func loadCommands(app *cli.App) {
	app.Commands = []cli.Command{
		{
			Name:      "open",
			ShortName: "o",
			Usage:     "List all open pull requests for the current repository",
			Action:    listOpenPullsCmd,
			Flags: []cli.Flag{
				cli.BoolFlag{"no-merge", "display only prs that cannot be merged"},
			},
		},
		{
			Name:      "closed",
			ShortName: "c",
			Usage:     "List all closed pull requests for the current repository",
			Action:    listClosedPullsCmd,
		},
		{
			Name:      "show",
			ShortName: "s",
			Usage:     "Show the pull request based on the number",
			Action:    showPullRequestCmd,
		},
		{
			Name:      "repository",
			ShortName: "repo",
			Usage:     "List information about the current repository",
			Action:    repositoryInfoCmd,
		},
		{
			Name:   "auth",
			Usage:  "Add a github token for authentication",
			Action: authCmd,
			Flags: []cli.Flag{
				cli.StringFlag{"add", "", "add new token for authentication"},
			},
		},
	}
}

func main() {
	app := cli.NewApp()

	app.Name = "pulls"
	app.Usage = "Manage github pull requets"
	app.Version = "0.0.1"

	client := gh.NewClient()

	config := loadConfig()
	if config.Token != "" {
		client.WithToken(config.Token)
	}

	org, name, err := getOriginUrl()
	if err != nil {
		panic(err)
	}
	t, err := pulls.NewMaintainer(client, org, name)
	if err != nil {
		panic(err)
	}
	m = t

	loadCommands(app)

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>//    Copyright (C) 2016  mparaiso <mparaiso@online.fr>
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at

//      http://www.apache.org/licenses/LICENSE-2.0

//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package tiger

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"

	"path"

	"github.com/Mparaiso/go-tiger/matcher"
)

// Route is an app route
type Route struct {
	Handler     func(Container)
	Matchers    []matcher.Matcher
	Middlewares []Middleware
	Meta        *RouteMeta
}

// GetMeta gets route metadatas
func (r *Route) GetMeta() *RouteMeta {
	if r.Meta == nil {
		r.Meta = &RouteMeta{}
	}
	return r.Meta
}

// SetName sets the route metadata's name
func (r *Route) SetName(name string) *Route {
	r.GetMeta().Name = name
	return r
}

// GetName returns the route metadata name
func (r *Route) GetName() string {
	return r.GetMeta().Name
}

// Use adds middlewares to the route
func (r *Route) Use(middleware ...Middleware) *Route {
	r.Middlewares = append(r.Middlewares, middleware...)
	return r
}

// Match adds matchers to the route
func (r *Route) Match(matchers ...matcher.Matcher) *Route {
	r.Matchers = append(r.Matchers, matchers...)
	return r
}

// String returns a string representation of the route
func (r Route) String() string {
	return fmt.Sprintf("Route{Name:%s,Path:%s,Methods:%+v,Handler:%+v,Middlewares:%+v}", r.GetMeta().Name, r.GetMeta().Pattern,
		r.GetMeta().Methods,
		r.Handler, r.Middlewares)
}

// GetMatchers return the request matchers
func (r *Route) GetMatchers() matcher.Matchers {
	return r.Matchers
}

// Routes is a collection of routes
type Routes []*Route

// GetMetadatas returns an array of *RouteMeta
func (routes Routes) GetMetadatas() RouteMetas {
	metadatas := []*RouteMeta{}
	for _, route := range routes {
		metadatas = append(metadatas, route.GetMeta())
	}
	return metadatas
}

// ToMatcherProviders converts routes into matcher providers
func (routes Routes) ToMatcherProviders() matcher.MatcherProviders {
	matcherProviders := matcher.MatcherProviders{}
	for _, route := range routes {
		matcherProviders = append(matcherProviders, route)
	}
	return matcherProviders
}

// RouteMeta is a route metadata
type RouteMeta struct {
	Name         string
	Pattern      string
	Prefix       string
	URLVARPrefix string
	Methods      []string
	// ExtraData holds extra metadata
	// It can be used to add comments to routes
	// or annotations, in order to support Swagger for instance
	ExtraData map[string]interface{}
}

// Get gets ExtraData by key
func (routeMeta *RouteMeta) Get(key string) interface{} {
	return routeMeta.ExtraData[key]
}

// Set sets ExtraData
func (routeMeta *RouteMeta) Set(key string, value interface{}) *RouteMeta {
	routeMeta.ExtraData[key] = value
	return routeMeta
}

// Clone returns an new RouteMeta with the same values as the current one
func (routeMeta RouteMeta) Clone() *RouteMeta {
	return &RouteMeta{Name: routeMeta.Name,
		Pattern:      routeMeta.Pattern,
		Prefix:       routeMeta.Prefix,
		URLVARPrefix: routeMeta.URLVARPrefix,
		Methods:      routeMeta.Methods}
}

// GetPath returns the Pattern prefixed by Prefix
func (routeMeta RouteMeta) GetPath() string {
	return path.Join(routeMeta.Prefix, routeMeta.Pattern)
}

// Generate generates a path by completing RouteMeta.Path with variables in attributes
func (routeMeta RouteMeta) Generate(attributes map[string]interface{}) string {
	path := regexp.MustCompile(`(\:[^\s /]+)`).ReplaceAllStringFunc(routeMeta.GetPath(), func(part string) string {
		value, ok := attributes[strings.TrimLeft(part, ":")]
		if ok {
			delete(attributes, strings.TrimLeft(part, ":"))
		}
		return fmt.Sprint(value)
	})
	if len(attributes) == 0 {
		return path
	}
	values := url.Values{}
	for key, value := range attributes {
		values.Set(key, url.QueryEscape(fmt.Sprint(value)))
	}
	return path + "?" + values.Encode()

}

// RouteMetas is a collection of *RouteMeta
type RouteMetas []*RouteMeta

// FindByName selects a *RouteMeta by name
// this method silently fails and will always return
// a route meta even if it isn't found. You'll need to
// compare to ZeroRouteMeta if you want to know whether
// a route was found or not.
func (routeMetas RouteMetas) FindByName(name string) *RouteMeta {
	for _, routeMeta := range routeMetas {
		if routeMeta.Name == name {
			return routeMeta
		}
	}
	return ZeroRouteMeta
}

var (
	ZeroRouteMeta = new(RouteMeta)
)
<commit_msg>routemeta extra is now interface/interface<commit_after>//    Copyright (C) 2016  mparaiso <mparaiso@online.fr>
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at

//      http://www.apache.org/licenses/LICENSE-2.0

//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

package tiger

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"

	"path"

	"github.com/Mparaiso/go-tiger/matcher"
)

// Route is an app route
type Route struct {
	Handler     func(Container)
	Matchers    []matcher.Matcher
	Middlewares []Middleware
	Meta        *RouteMeta
}

// GetMeta gets route metadatas
func (r *Route) GetMeta() *RouteMeta {
	if r.Meta == nil {
		r.Meta = &RouteMeta{}
	}
	return r.Meta
}

// SetName sets the route metadata's name
func (r *Route) SetName(name string) *Route {
	r.GetMeta().Name = name
	return r
}

// GetName returns the route metadata name
func (r *Route) GetName() string {
	return r.GetMeta().Name
}

// Use adds middlewares to the route
func (r *Route) Use(middleware ...Middleware) *Route {
	r.Middlewares = append(r.Middlewares, middleware...)
	return r
}

// Match adds matchers to the route
func (r *Route) Match(matchers ...matcher.Matcher) *Route {
	r.Matchers = append(r.Matchers, matchers...)
	return r
}

// String returns a string representation of the route
func (r Route) String() string {
	return fmt.Sprintf("Route{Name:%s,Path:%s,Methods:%+v,Middlewares:%+v}",
		r.GetName(), r.GetMeta().GetPath(), r.GetMeta().Methods, r.Middlewares,
	)
}

// GetMatchers return the request matchers
func (r *Route) GetMatchers() matcher.Matchers {
	return r.Matchers
}

// Routes is a collection of routes
type Routes []*Route

// GetMetadatas returns an array of *RouteMeta
func (routes Routes) GetMetadatas() RouteMetas {
	metadatas := []*RouteMeta{}
	for _, route := range routes {
		metadatas = append(metadatas, route.GetMeta())
	}
	return metadatas
}

// ToMatcherProviders converts routes into matcher providers
func (routes Routes) ToMatcherProviders() matcher.MatcherProviders {
	matcherProviders := matcher.MatcherProviders{}
	for _, route := range routes {
		matcherProviders = append(matcherProviders, route)
	}
	return matcherProviders
}

// RouteMeta is a route metadata
type RouteMeta struct {
	Name         string
	Pattern      string
	Prefix       string
	URLVARPrefix string
	Methods      []string
	// ExtraData holds extra metadata
	// It can be used to add comments to routes
	// or annotations, in order to support Swagger for instance
	ExtraData map[interface{}]interface{}
}

// Get gets ExtraData by key
func (routeMeta *RouteMeta) Get(key interface{}) interface{} {
	return routeMeta.ExtraData[key]
}

// Set sets ExtraData
func (routeMeta *RouteMeta) Set(key interface{}, value interface{}) *RouteMeta {
	routeMeta.ExtraData[key] = value
	return routeMeta
}

// Clone returns an new RouteMeta with the same values as the current one
func (routeMeta RouteMeta) Clone() *RouteMeta {
	return &RouteMeta{Name: routeMeta.Name,
		Pattern:      routeMeta.Pattern,
		Prefix:       routeMeta.Prefix,
		URLVARPrefix: routeMeta.URLVARPrefix,
		Methods:      routeMeta.Methods}
}

// GetPath returns the Pattern prefixed by Prefix
func (routeMeta RouteMeta) GetPath() string {
	return path.Join(routeMeta.Prefix, routeMeta.Pattern)
}

// Generate generates a path by completing RouteMeta.Path with variables in attributes
func (routeMeta RouteMeta) Generate(attributes map[string]interface{}) string {
	path := regexp.MustCompile(`(\:[^\s /]+)`).ReplaceAllStringFunc(routeMeta.GetPath(), func(part string) string {
		value, ok := attributes[strings.TrimLeft(part, ":")]
		if ok {
			delete(attributes, strings.TrimLeft(part, ":"))
		}
		return fmt.Sprint(value)
	})
	if len(attributes) == 0 {
		return path
	}
	values := url.Values{}
	for key, value := range attributes {
		values.Set(key, url.QueryEscape(fmt.Sprint(value)))
	}
	return path + "?" + values.Encode()

}

// RouteMetas is a collection of *RouteMeta
type RouteMetas []*RouteMeta

// FindByName selects a *RouteMeta by name
// this method silently fails and will always return
// a route meta even if it isn't found. You'll need to
// compare to ZeroRouteMeta if you want to know whether
// a route was found or not.
func (routeMetas RouteMetas) FindByName(name string) *RouteMeta {
	for _, routeMeta := range routeMetas {
		if routeMeta.Name == name {
			return routeMeta
		}
	}
	return ZeroRouteMeta
}

var (
	ZeroRouteMeta = new(RouteMeta)
)
<|endoftext|>