<commit_before>package tricks

import (
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSliceValid(t *testing.T) {
	normal := Slice([]int{1, 2, 3, 4, 5}).Value().([]int)
	variadic := Slice(1, 2, 3, 4, 5).Value().([]int)

	assert.Equal(t, []int{1, 2, 3, 4, 5}, normal)
	assert.Equal(t, []int{1, 2, 3, 4, 5}, variadic)
	assert.Equal(t, 5, cap(normal))
	assert.Equal(t, 5, cap(variadic))
}

func TestSliceNil(t *testing.T) {
	// These type assertions should all work.
	t.Log(Slice([]interface{}{}).Value().([]interface{}))
	t.Log(Slice([]interface{}{nil}).Value().([]interface{}))
	t.Log(Slice([]interface{}{nil, nil, nil}).Value().([]interface{}))
	t.Log(Slice(nil).Value().([]interface{}))
	t.Log(Slice(nil, nil, nil).Value().([]interface{}))
}

func TestSlicePanics(t *testing.T) {
	assert.Panics(t, func() { Slice(1, 2, 3, 4, "5") })
	assert.Panics(t, func() { Slice(1, 2, 3, 4, nil) })
	assert.Panics(t, func() { Slice(Slice(1, 2, 3), 4, 5) })
}

func TestSliceSingle(t *testing.T) {
	assert.Equal(t, []int{1}, Slice(1).Value().([]int))
	assert.Equal(t, []string{"abc"}, Slice("abc").Value().([]string))
}

func TestSliceTrickSlice(t *testing.T) {
	first := Slice(1, 2, 3)
	second := Slice(first)
	assert.Equal(t, []int{1, 2, 3}, first.Value().([]int))
	assert.Equal(t, []int{1, 2, 3}, second.Value().([]int))
	assert.Equal(t, first, second)
}

func TestFirstReslicesOriginal(t *testing.T) {
	var numbers = make([]int, 10)[:5]
	copy(numbers, []int{1, 2, 3, 4, 5})

	first3 := Slice(numbers).First(3).Value().([]int)
	first3[0] = 7

	assert.Equal(t, []int{7, 2, 3}, first3)
	assert.Equal(t, 3, cap(first3))
	assert.Equal(t, []int{7, 2, 3, 4, 5}, numbers)
	assert.Equal(t, 10, cap(numbers))
}

func TestLastReslicesOriginal(t *testing.T) {
	var numbers = make([]int, 10)[:5]
	copy(numbers, []int{1, 2, 3, 4, 5})

	last3 := Slice(numbers).Last(3).Value().([]int)
	last3[0] = 7

	assert.Equal(t, []int{7, 4, 5}, last3)
	assert.Equal(t, 3, cap(last3))
	assert.Equal(t, []int{1, 2, 7, 4, 5}, numbers)
	assert.Equal(t, 10, cap(numbers))
}

func TestSliceCopyPreservesOriginal(t *testing.T) {
	var numbers = []int{4, 3, 2, 1}

	sorted := Slice(numbers).Copy().Sort().Value().([]int)

	assert.Equal(t, []int{4, 3, 2, 1}, numbers)
	assert.NotEqual(t, numbers, sorted)
	assert.Equal(t, []int{1, 2, 3, 4}, sorted)
}

func TestSortStringsInPlace(t *testing.T) {
	var animals = []string{"dog", "cat", "bear", "cow", "bull", "pig", "iguana"}

	sorted := Slice(animals).Sort().Value().([]string)

	assert.Equal(t, []string{"bear", "bull", "cat", "cow", "dog", "iguana", "pig"}, sorted)
	assert.Equal(t, sorted, animals)
}

func TestSortIntsInPlace(t *testing.T) {
	var numbers = []int{3, 5, 21, 1, 34, 55, 13, 2, 8, 89, 1}

	sorted := Slice(numbers).Sort().Value().([]int)

	assert.Equal(t, []int{1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}, sorted)
	assert.Equal(t, sorted, numbers)
}

func TestSortFloatsInPlace(t *testing.T) {
	var numbers = []float64{3.5, 21.1, 34.55, 13.2, 8.89, 1}

	sorted := Slice(numbers).Sort().Value().([]float64)

	assert.Equal(t, []float64{1, 3.5, 8.89, 13.2, 21.1, 34.55}, sorted)
	assert.Equal(t, sorted, numbers)
}

type testSortByLen []struct{ string }
type testUnsortable []struct{ string }

func (t testSortByLen) Len() int           { return len(t) }
func (t testSortByLen) Swap(i, j int)      { t[i].string, t[j].string = t[j].string, t[i].string }
func (t testSortByLen) Less(i, j int) bool { return len(t[i].string) < len(t[j].string) }

func TestSortInterfaceInPlace(t *testing.T) {
	var animals = testSortByLen{
		struct{ string }{"dog"},
		struct{ string }{"cat"},
		struct{ string }{"bear"},
		struct{ string }{"cow"},
		struct{ string }{"bull"},
		struct{ string }{"pig"},
		struct{ string }{"iguana"},
	}

	sorted := Slice(animals).Sort().Value().(testSortByLen)

	var expected = testSortByLen{
		struct{ string }{"dog"},
		struct{ string }{"cat"},
		struct{ string }{"cow"},
		struct{ string }{"pig"},
		struct{ string }{"bear"},
		struct{ string }{"bull"},
		struct{ string }{"iguana"},
	}
	assert.Equal(t, expected, sorted)
	assert.Equal(t, sorted, animals)
}

func TestGroupBy(t *testing.T) {
	var animals = []string{"dog", "cat", "bear", "cow", "bull", "pig", "iguana"}

	grouped := Slice(animals).
		GroupBy(func(s string) int { return len(s) }).
		Value().(map[int][]string)

	expected := map[int][]string{
		3: []string{"dog", "cat", "cow", "pig"},
		4: []string{"bear", "bull"},
		6: []string{"iguana"},
	}
	assert.Equal(t, expected, grouped)
}

func TestSliceMap(t *testing.T) {
	var animals = []string{"dog", "cat", "bear", "cow"}

	result := Slice(animals).Map(strings.ToUpper).Value().([]string)

	expected := []string{"DOG", "CAT", "BEAR", "COW"}
	assert.Equal(t, expected, result)
	assert.NotEqual(t, animals, result)
}

func TestMaxAndMin(t *testing.T) {
	var numbers = []int{3, 5, 21, 1, 34, 55, 13, 2, 8, 89, 1}
	var number = []int{42}

	max := Slice(numbers).Max().(int)
	assert.Equal(t, 89, max)

	max = Slice(number).Max().(int)
	assert.Equal(t, 42, max)

	_, ok := Slice([]int{}).Max().(int)
	assert.False(t, ok)

	min := Slice(numbers).Min().(int)
	assert.Equal(t, 1, min)

	min = Slice(number).Min().(int)
	assert.Equal(t, 42, min)

	_, ok = Slice([]int{}).Min().(int)
	assert.False(t, ok)
}
<commit_msg>Add test assertions<commit_after>package tricks

import (
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSliceValid(t *testing.T) {
	normal := Slice([]int{1, 2, 3, 4, 5}).Value().([]int)
	variadic := Slice(1, 2, 3, 4, 5).Value().([]int)

	assert.Equal(t, []int{1, 2, 3, 4, 5}, normal)
	assert.Equal(t, []int{1, 2, 3, 4, 5}, variadic)
	assert.Equal(t, 5, cap(normal))
	assert.Equal(t, 5, cap(variadic))
}

func TestSliceNil(t *testing.T) {
	// These type assertions should all work.
	assert.Equal(t, 0, len(Slice([]interface{}{}).Value().([]interface{})))
	assert.Equal(t, 1, len(Slice([]interface{}{nil}).Value().([]interface{})))
	assert.Equal(t, 3, len(Slice([]interface{}{nil, nil, nil}).Value().([]interface{})))
	assert.Equal(t, 0, len(Slice(nil).Value().([]interface{})))
	assert.Equal(t, 0, len(Slice(nil, nil, nil).Value().([]interface{})))
}

func TestSlicePanics(t *testing.T) {
	assert.Panics(t, func() { Slice(1, 2, 3, 4, "5") })
	assert.Panics(t, func() { Slice(1, 2, 3, 4, nil) })
	assert.Panics(t, func() { Slice(Slice(1, 2, 3), 4, 5) })
}

func TestSliceSingle(t *testing.T) {
	assert.Equal(t, []int{1}, Slice(1).Value().([]int))
	assert.Equal(t, []string{"abc"}, Slice("abc").Value().([]string))
}

func TestSliceTrickSlice(t *testing.T) {
	first := Slice(1, 2, 3)
	second := Slice(first)
	assert.Equal(t, []int{1, 2, 3}, first.Value().([]int))
	assert.Equal(t, []int{1, 2, 3}, second.Value().([]int))
	assert.Equal(t, first, second)
}

func TestFirstReslicesOriginal(t *testing.T) {
	var numbers = make([]int, 10)[:5]
	copy(numbers, []int{1, 2, 3, 4, 5})

	first3 := Slice(numbers).First(3).Value().([]int)
	first3[0] = 7

	assert.Equal(t, []int{7, 2, 3}, first3)
	assert.Equal(t, 3, cap(first3))
	assert.Equal(t, []int{7, 2, 3, 4, 5}, numbers)
	assert.Equal(t, 10, cap(numbers))
}

func TestLastReslicesOriginal(t *testing.T) {
	var numbers = make([]int, 10)[:5]
	copy(numbers, []int{1, 2, 3, 4, 5})

	last3 := Slice(numbers).Last(3).Value().([]int)
	last3[0] = 7

	assert.Equal(t, []int{7, 4, 5}, last3)
	assert.Equal(t, 3, cap(last3))
	assert.Equal(t, []int{1, 2, 7, 4, 5}, numbers)
	assert.Equal(t, 10, cap(numbers))
}

func TestSliceCopyPreservesOriginal(t *testing.T) {
	var numbers = []int{4, 3, 2, 1}

	sorted := Slice(numbers).Copy().Sort().Value().([]int)

	assert.Equal(t, []int{4, 3, 2, 1}, numbers)
	assert.NotEqual(t, numbers, sorted)
	assert.Equal(t, []int{1, 2, 3, 4}, sorted)
}

func TestSortStringsInPlace(t *testing.T) {
	var animals = []string{"dog", "cat", "bear", "cow", "bull", "pig", "iguana"}

	sorted := Slice(animals).Sort().Value().([]string)

	assert.Equal(t, []string{"bear", "bull", "cat", "cow", "dog", "iguana", "pig"}, sorted)
	assert.Equal(t, sorted, animals)
}

func TestSortIntsInPlace(t *testing.T) {
	var numbers = []int{3, 5, 21, 1, 34, 55, 13, 2, 8, 89, 1}

	sorted := Slice(numbers).Sort().Value().([]int)

	assert.Equal(t, []int{1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}, sorted)
	assert.Equal(t, sorted, numbers)
}

func TestSortFloatsInPlace(t *testing.T) {
	var numbers = []float64{3.5, 21.1, 34.55, 13.2, 8.89, 1}

	sorted := Slice(numbers).Sort().Value().([]float64)

	assert.Equal(t, []float64{1, 3.5, 8.89, 13.2, 21.1, 34.55}, sorted)
	assert.Equal(t, sorted, numbers)
}

type testSortByLen []struct{ string }
type testUnsortable []struct{ string }

func (t testSortByLen) Len() int           { return len(t) }
func (t testSortByLen) Swap(i, j int)      { t[i].string, t[j].string = t[j].string, t[i].string }
func (t testSortByLen) Less(i, j int) bool { return len(t[i].string) < len(t[j].string) }

func TestSortInterfaceInPlace(t *testing.T) {
	var animals = testSortByLen{
		struct{ string }{"dog"},
		struct{ string }{"cat"},
		struct{ string }{"bear"},
		struct{ string }{"cow"},
		struct{ string }{"bull"},
		struct{ string }{"pig"},
		struct{ string }{"iguana"},
	}

	sorted := Slice(animals).Sort().Value().(testSortByLen)

	var expected = testSortByLen{
		struct{ string }{"dog"},
		struct{ string }{"cat"},
		struct{ string }{"cow"},
		struct{ string }{"pig"},
		struct{ string }{"bear"},
		struct{ string }{"bull"},
		struct{ string }{"iguana"},
	}
	assert.Equal(t, expected, sorted)
	assert.Equal(t, sorted, animals)
}

func TestGroupBy(t *testing.T) {
	var animals = []string{"dog", "cat", "bear", "cow", "bull", "pig", "iguana"}

	grouped := Slice(animals).
		GroupBy(func(s string) int { return len(s) }).
		Value().(map[int][]string)

	expected := map[int][]string{
		3: []string{"dog", "cat", "cow", "pig"},
		4: []string{"bear", "bull"},
		6: []string{"iguana"},
	}
	assert.Equal(t, expected, grouped)
}

func TestSliceMap(t *testing.T) {
	var animals = []string{"dog", "cat", "bear", "cow"}

	result := Slice(animals).Map(strings.ToUpper).Value().([]string)

	expected := []string{"DOG", "CAT", "BEAR", "COW"}
	assert.Equal(t, expected, result)
	assert.NotEqual(t, animals, result)
}

func TestMaxAndMin(t *testing.T) {
	var numbers = []int{3, 5, 21, 1, 34, 55, 13, 2, 8, 89, 1}
	var number = []int{42}

	max := Slice(numbers).Max().(int)
	assert.Equal(t, 89, max)

	max = Slice(number).Max().(int)
	assert.Equal(t, 42, max)

	_, ok := Slice([]int{}).Max().(int)
	assert.False(t, ok)

	min := Slice(numbers).Min().(int)
	assert.Equal(t, 1, min)

	min = Slice(number).Min().(int)
	assert.Equal(t, 42, min)

	_, ok = Slice([]int{}).Min().(int)
	assert.False(t, ok)
}
<|endoftext|>


<commit_before>package announce

// Handled in this file is declaring a consistent API for consumers of new
// torrent additions.
//
// Essentially, whenever a new torrent is added, we want to announce that
// torrent to disparate services (think IRC, websockets, RSS feed).

// AnnouncerNotifier handles declaring the API for notifying different channels
// of nwe torrent additions.
type AnnouncerNotifier interface {
	// SpawnNotifier handles creating a new notifier. The notifier will live by
	// itself and not need any
	SpawnNotifier() *AnnounceNotifier
	// KillNotifier will handle cleanup and closing of the notifier. Necessary
	// for a clean exit for Notorious.
	KillNotifier() error
	// sendNotification Ought to be private, as `SpawnNotifier` spins up a
	// goroutine to handle sendNotifications.
	sendNotification()
}
<commit_msg>Issue #170<commit_after>package announce

// Handled in this file is declaring a consistent API for consumers of new
// torrent additions.
//
// Essentially, whenever a new torrent is added, we want to announce that
// torrent to disparate services (think IRC, websockets, RSS feed).

// AnnouncerNotifier handles declaring the API for notifying different channels
// of nwe torrent additions.
type AnnouncerNotifier interface {
	// SpawnNotifier handles creating a new notifier. The notifier will live by
	// itself and not need any
	SpawnNotifier() *interface{}
	// KillNotifier will handle cleanup and closing of the notifier. Necessary
	// for a clean exit for Notorious.
	KillNotifier() error
	// sendNotification Ought to be private, as `SpawnNotifier` spins up a
	// goroutine to handle sendNotifications.
	sendNotification()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/go-chi/render"
)

// Services is the global services context
type Services struct {
	log *log.Logger
}

func NewHandlerFunc(host string, logger *log.Logger) func(http.ResponseWriter, *http.Request) {

	services := Services{log: logger}
	var _ = services

	return func(w http.ResponseWriter, r *http.Request) {
		data := struct {
			Message string
			Date    time.Time
		}{
			fmt.Sprintf("Hello world from %s", host),
			time.Now(),
		}
		render.JSON(w, r, &data)
	}
}
<commit_msg>refactor: req services<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/go-chi/render"
)

// Services is the global services context
type Services struct {
	log *log.Logger
}

func NewHandlerFunc(host string, logger *log.Logger) func(http.ResponseWriter, *http.Request) {

	services := Services{log: logger}
	var _ = services

	return func(w http.ResponseWriter, r *http.Request) {

		requestServices := services
		var _ = requestServices

		data := struct {
			Message string
			Date    time.Time
		}{
			fmt.Sprintf("Hello world from %s", host),
			time.Now(),
		}
		render.JSON(w, r, &data)
	}
}
<|endoftext|>


<commit_before>package gobrake

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

	tdigest "github.com/caio/go-tdigest"
)

const flushPeriod = 15 * time.Second

type RequestInfo struct {
	Method     string
	Route      string
	StatusCode int
	Start      time.Time
	End        time.Time
}

type routeKey struct {
	Method     string    `json:"method"`
	Route      string    `json:"route"`
	StatusCode int       `json:"status_code"`
	Time       time.Time `json:"time"`
}

type routeStat struct {
	mu      sync.Mutex
	Count   int     `json:"count"`
	Sum     float64 `json:"sum"`
	Sumsq   float64 `json:"sumsq"`
	TDigest []byte  `json:"tdigest"`
	td      *tdigest.TDigest
}

func (s *routeStat) Add(ms float64) error {
	if s.td == nil {
		td, err := tdigest.New(tdigest.Compression(20))
		if err != nil {
			return err
		}
		s.td = td
	}

	s.Count++
	s.Sum += ms
	s.Sumsq += ms * ms
	return s.td.Add(ms)
}

type routeKeyStat struct {
	routeKey
	*routeStat
}

// routeStats aggregates information about requests and periodically sends
// collected data to Airbrake.
type routeStats struct {
	opt    *NotifierOptions
	apiURL string

	mu sync.Mutex
	m  map[routeKey]*routeStat

	flushTimer *time.Timer
}

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

func (s *routeStats) init() {
	if s.m == nil && s.flushTimer == nil {
		s.m = make(map[routeKey]*routeStat)
		s.flushTimer = time.AfterFunc(flushPeriod, s.flush)
	}
}

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

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

	s.mu.Unlock()

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

type routesStatsJSONRequest struct {
	Routes []routeKeyStat `json:"routes"`
}

func (s *routeStats) send(m map[routeKey]*routeStat) error {
	var routes []routeKeyStat
	for k, v := range m {
		err := v.td.Compress()
		if err != nil {
			return err
		}

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

		routes = append(routes, routeKeyStat{
			routeKey:  k,
			routeStat: v,
		})
	}

	jsonReq := routesStatsJSONRequest{
		Routes: routes,
	}

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

	buf.Reset()
	err := json.NewEncoder(buf).Encode(jsonReq)
	if err != nil {
		return err
	}

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

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

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

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

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

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

func (s *routeStats) NotifyRequest(req *RequestInfo) error {
	key := routeKey{
		Method:     req.Method,
		Route:      req.Route,
		StatusCode: req.StatusCode,
		Time:       req.Start.UTC().Truncate(time.Minute),
	}

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

	ms := float64(req.End.Sub(req.Start)) / float64(time.Millisecond)

	stat.mu.Lock()
	err := stat.Add(ms)
	stat.mu.Unlock()

	return err
}
<commit_msg>status_code -> statusCode<commit_after>package gobrake

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

	tdigest "github.com/caio/go-tdigest"
)

const flushPeriod = 15 * time.Second

type RequestInfo struct {
	Method     string
	Route      string
	StatusCode int
	Start      time.Time
	End        time.Time
}

type routeKey struct {
	Method     string    `json:"method"`
	Route      string    `json:"route"`
	StatusCode int       `json:"statusCode"`
	Time       time.Time `json:"time"`
}

type routeStat struct {
	mu      sync.Mutex
	Count   int     `json:"count"`
	Sum     float64 `json:"sum"`
	Sumsq   float64 `json:"sumsq"`
	TDigest []byte  `json:"tdigest"`
	td      *tdigest.TDigest
}

func (s *routeStat) Add(ms float64) error {
	if s.td == nil {
		td, err := tdigest.New(tdigest.Compression(20))
		if err != nil {
			return err
		}
		s.td = td
	}

	s.Count++
	s.Sum += ms
	s.Sumsq += ms * ms
	return s.td.Add(ms)
}

type routeKeyStat struct {
	routeKey
	*routeStat
}

// routeStats aggregates information about requests and periodically sends
// collected data to Airbrake.
type routeStats struct {
	opt    *NotifierOptions
	apiURL string

	mu sync.Mutex
	m  map[routeKey]*routeStat

	flushTimer *time.Timer
}

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

func (s *routeStats) init() {
	if s.m == nil && s.flushTimer == nil {
		s.m = make(map[routeKey]*routeStat)
		s.flushTimer = time.AfterFunc(flushPeriod, s.flush)
	}
}

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

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

	s.mu.Unlock()

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

type routesStatsJSONRequest struct {
	Routes []routeKeyStat `json:"routes"`
}

func (s *routeStats) send(m map[routeKey]*routeStat) error {
	var routes []routeKeyStat
	for k, v := range m {
		err := v.td.Compress()
		if err != nil {
			return err
		}

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

		routes = append(routes, routeKeyStat{
			routeKey:  k,
			routeStat: v,
		})
	}

	jsonReq := routesStatsJSONRequest{
		Routes: routes,
	}

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

	buf.Reset()
	err := json.NewEncoder(buf).Encode(jsonReq)
	if err != nil {
		return err
	}

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

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

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

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

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

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

func (s *routeStats) NotifyRequest(req *RequestInfo) error {
	key := routeKey{
		Method:     req.Method,
		Route:      req.Route,
		StatusCode: req.StatusCode,
		Time:       req.Start.UTC().Truncate(time.Minute),
	}

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

	ms := float64(req.End.Sub(req.Start)) / float64(time.Millisecond)

	stat.mu.Lock()
	err := stat.Add(ms)
	stat.mu.Unlock()

	return err
}
<|endoftext|>


<commit_before>package websocket

import (
	"bytes"
	"fmt"
	"io"
	"net/url"
	"time"

	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
)

func beginHandlerOpen(ch chan error, removeHandlers func()) func(ev js.Object) {
	return func(ev js.Object) {
		removeHandlers()
		close(ch)
	}
}

// closeError allows a CloseEvent to be used as an error.
type closeError struct {
	*dom.CloseEvent
}

func (e *closeError) Error() string {
	var cleanStmt string
	if e.WasClean {
		cleanStmt = "clean"
	} else {
		cleanStmt = "unclean"
	}
	return fmt.Sprintf("CloseEvent: (%s) (%d) %s", cleanStmt, e.Code, e.Reason)
}

func beginHandlerClose(ch chan error, removeHandlers func()) func(ev js.Object) {
	return func(ev js.Object) {
		removeHandlers()
		go func() {
			ce := dom.WrapEvent(ev).(*dom.CloseEvent)
			ch <- &closeError{CloseEvent: ce}
			close(ch)
		}()
	}
}

type deadlineErr struct{}

func (e *deadlineErr) Error() string   { return "i/o timeout: deadline reached" }
func (e *deadlineErr) Timeout() bool   { return true }
func (e *deadlineErr) Temporary() bool { return true }

var errDeadlineReached = &deadlineErr{}

// Dial opens a new WebSocket connection. It will block until the connection is
// established or fails to connect.
func Dial(url string) (*Conn, error) {
	ws, err := New(url)
	if err != nil {
		return nil, err
	}
	conn := &Conn{
		WebSocket: ws,
		ch:        make(chan *dom.MessageEvent, 1),
	}
	conn.initialize()

	openCh := make(chan error, 1)

	var (
		openHandler  func(ev js.Object)
		closeHandler func(ev js.Object)
	)

	// Handlers need to be removed to prevent a panic when the WebSocket closes
	// immediately and fires both open and close before they can be removed.
	// This way, handlers are removed before the channel is closed.
	removeHandlers := func() {
		ws.RemoveEventListener("open", false, openHandler)
		ws.RemoveEventListener("close", false, closeHandler)
	}

	// We have to use variables for the functions so that we can remove the
	// event handlers afterwards.
	openHandler = beginHandlerOpen(openCh, removeHandlers)
	closeHandler = beginHandlerClose(openCh, removeHandlers)

	ws.AddEventListener("open", false, openHandler)
	ws.AddEventListener("close", false, closeHandler)

	err, ok := <-openCh
	if ok && err != nil {
		return nil, err
	}

	return conn, nil
}

// Conn is a high-level wrapper around WebSocket. It is intended to
// provide a net.TCPConn-like interface.
type Conn struct {
	*WebSocket

	ch      chan *dom.MessageEvent
	readBuf *bytes.Reader

	readDeadline time.Time
}

func (c *Conn) onMessage(event js.Object) {
	go func() {
		c.ch <- dom.WrapEvent(event).(*dom.MessageEvent)
	}()
}

func (c *Conn) onClose(event js.Object) {
	go func() {
		// We queue nil to the end so that any messages received prior to
		// closing get handled first.
		c.ch <- nil
	}()
}

// Initialize adds all of the event handlers necessary for a Conn to function.
// It should never be called more than once and is already called if you used
// Dial to create the Conn.
func (c *Conn) initialize() {
	// We need this so that received binary data is in ArrayBufferView format so
	// that it can easily be read.
	c.BinaryType = "arraybuffer"

	c.AddEventListener("message", false, c.onMessage)
	c.AddEventListener("close", false, c.onClose)
}

// handleFrame handles a single frame received from the channel. This is a
// convenience funciton to dedupe code for the multiple deadline cases.
func (c *Conn) handleFrame(item *dom.MessageEvent, ok bool) (*dom.MessageEvent, error) {
	if !ok { // The channel has been closed
		return nil, io.EOF
	} else if item == nil {
		// See onClose for the explanation about sending a nil item.
		close(c.ch)
		return nil, io.EOF
	}

	return item, nil
}

// receiveFrame receives one full frame from the WebSocket. It blocks until the
// frame is received.
func (c *Conn) receiveFrame(observeDeadline bool) (*dom.MessageEvent, error) {
	var deadlineChan <-chan time.Time // Receiving on a nil channel always blocks indefinitely

	if observeDeadline && !c.readDeadline.IsZero() {
		now := time.Now()
		if now.After(c.readDeadline) {
			select {
			case item, ok := <-c.ch:
				return c.handleFrame(item, ok)
			default:
				return nil, errDeadlineReached
			}
		}

		timer := time.NewTimer(c.readDeadline.Sub(now))
		defer timer.Stop()

		deadlineChan = timer.C
	}

	select {
	case item, ok := <-c.ch:
		return c.handleFrame(item, ok)
	case <-deadlineChan:
		return nil, errDeadlineReached
	}
}

func getFrameData(obj js.Object) []byte {
	// Check if it's an array buffer. If so, convert it to a Go byte slice.
	if constructor := obj.Get("constructor"); constructor == js.Global.Get("ArrayBuffer") {
		int8Array := js.Global.Get("Uint8Array").New(obj)
		return int8Array.Interface().([]byte)
	}

	return []byte(obj.Str())
}

func (c *Conn) Read(b []byte) (n int, err error) {
	// TODO(nightexcessive): Implement the deadline functions.

	if c.readBuf != nil {
		n, err = c.readBuf.Read(b)
		if err == io.EOF {
			c.readBuf = nil
			err = nil
		}
		// If we read nothing from the buffer, continue to trying to receive.
		// This saves us when the last Read call emptied the buffer and this
		// call triggers the EOF. There's probably a better way of doing this,
		// but I'm really tired.
		if n > 0 {
			return
		}
	}

	frame, err := c.receiveFrame(true)
	if err != nil {
		return 0, err
	}

	receivedBytes := getFrameData(frame.Data)

	n = copy(b, receivedBytes)
	// Fast path: The entire frame's contents have been copied into b.
	if n >= len(receivedBytes) {
		return
	}

	c.readBuf = bytes.NewReader(receivedBytes[n:])
	return
}

// Write writes the contents of b to the WebSocket using a binary opcode.
func (c *Conn) Write(b []byte) (n int, err error) {
	// []byte is converted to an (U)Int8Array by GopherJS, which fullfils the
	// ArrayBufferView definition.
	err = c.Send(b)
	if err != nil {
		return
	}
	n = len(b)
	return
}

// WriteString writes the contents of s to the WebSocket using a text frame
// opcode.
func (c *Conn) WriteString(s string) (n int, err error) {
	err = c.Send(s)
	if err != nil {
		return
	}
	n = len(s)
	return
}

// BUG(nightexcessive): Conn doesn't currently fulfill the net.Conn interface
// because we can't return net.Addr from Conn.LocalAddr and Conn.RemoteAddr
// because net.init() causes a panic due to attempts to make syscalls.
//
// See: https://github.com/gopherjs/gopherjs/issues/123

// LocalAddr would typically return the local network address, but due to
// limitations in the JavaScript API, it is unable to. Calling this method will
// cause a panic.
func (c *Conn) LocalAddr() *Addr {
	// BUG(nightexcessive): Conn.LocalAddr() panics because the underlying
	// JavaScript API has no way of figuring out the local address.

	// TODO(nightexcessive): Find a more graceful way to handle this
	panic("we are unable to implement websocket.Conn.LocalAddr() due to limitations in the underlying JavaScript API")
}

// RemoteAddr returns the remote network address, based on
// websocket.WebSocket.URL.
func (c *Conn) RemoteAddr() *Addr {
	wsURL, err := url.Parse(c.URL)
	if err != nil {
		// TODO(nightexcessive): Should we be panicking for this?
		panic(err)
	}
	return &Addr{wsURL}
}

// SetDeadline sets the read and write deadlines associated with the connection.
// It is equivalent to calling both SetReadDeadline and SetWriteDeadline.
//
// A zero value for t means that I/O operations will not time out.
func (c *Conn) SetDeadline(t time.Time) error {
	c.readDeadline = t
	return nil
}

// SetReadDeadline sets the deadline for future Read calls. A zero value for t
// means Read will not time out.
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.readDeadline = t
	return nil
}

// SetWriteDeadline sets the deadline for future Write calls. Because our writes
// do not block, this function is a no-op.
func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}
<commit_msg>Remove deadline TODO<commit_after>package websocket

import (
	"bytes"
	"fmt"
	"io"
	"net/url"
	"time"

	"github.com/gopherjs/gopherjs/js"
	"honnef.co/go/js/dom"
)

func beginHandlerOpen(ch chan error, removeHandlers func()) func(ev js.Object) {
	return func(ev js.Object) {
		removeHandlers()
		close(ch)
	}
}

// closeError allows a CloseEvent to be used as an error.
type closeError struct {
	*dom.CloseEvent
}

func (e *closeError) Error() string {
	var cleanStmt string
	if e.WasClean {
		cleanStmt = "clean"
	} else {
		cleanStmt = "unclean"
	}
	return fmt.Sprintf("CloseEvent: (%s) (%d) %s", cleanStmt, e.Code, e.Reason)
}

func beginHandlerClose(ch chan error, removeHandlers func()) func(ev js.Object) {
	return func(ev js.Object) {
		removeHandlers()
		go func() {
			ce := dom.WrapEvent(ev).(*dom.CloseEvent)
			ch <- &closeError{CloseEvent: ce}
			close(ch)
		}()
	}
}

type deadlineErr struct{}

func (e *deadlineErr) Error() string   { return "i/o timeout: deadline reached" }
func (e *deadlineErr) Timeout() bool   { return true }
func (e *deadlineErr) Temporary() bool { return true }

var errDeadlineReached = &deadlineErr{}

// Dial opens a new WebSocket connection. It will block until the connection is
// established or fails to connect.
func Dial(url string) (*Conn, error) {
	ws, err := New(url)
	if err != nil {
		return nil, err
	}
	conn := &Conn{
		WebSocket: ws,
		ch:        make(chan *dom.MessageEvent, 1),
	}
	conn.initialize()

	openCh := make(chan error, 1)

	var (
		openHandler  func(ev js.Object)
		closeHandler func(ev js.Object)
	)

	// Handlers need to be removed to prevent a panic when the WebSocket closes
	// immediately and fires both open and close before they can be removed.
	// This way, handlers are removed before the channel is closed.
	removeHandlers := func() {
		ws.RemoveEventListener("open", false, openHandler)
		ws.RemoveEventListener("close", false, closeHandler)
	}

	// We have to use variables for the functions so that we can remove the
	// event handlers afterwards.
	openHandler = beginHandlerOpen(openCh, removeHandlers)
	closeHandler = beginHandlerClose(openCh, removeHandlers)

	ws.AddEventListener("open", false, openHandler)
	ws.AddEventListener("close", false, closeHandler)

	err, ok := <-openCh
	if ok && err != nil {
		return nil, err
	}

	return conn, nil
}

// Conn is a high-level wrapper around WebSocket. It is intended to
// provide a net.TCPConn-like interface.
type Conn struct {
	*WebSocket

	ch      chan *dom.MessageEvent
	readBuf *bytes.Reader

	readDeadline time.Time
}

func (c *Conn) onMessage(event js.Object) {
	go func() {
		c.ch <- dom.WrapEvent(event).(*dom.MessageEvent)
	}()
}

func (c *Conn) onClose(event js.Object) {
	go func() {
		// We queue nil to the end so that any messages received prior to
		// closing get handled first.
		c.ch <- nil
	}()
}

// Initialize adds all of the event handlers necessary for a Conn to function.
// It should never be called more than once and is already called if you used
// Dial to create the Conn.
func (c *Conn) initialize() {
	// We need this so that received binary data is in ArrayBufferView format so
	// that it can easily be read.
	c.BinaryType = "arraybuffer"

	c.AddEventListener("message", false, c.onMessage)
	c.AddEventListener("close", false, c.onClose)
}

// handleFrame handles a single frame received from the channel. This is a
// convenience funciton to dedupe code for the multiple deadline cases.
func (c *Conn) handleFrame(item *dom.MessageEvent, ok bool) (*dom.MessageEvent, error) {
	if !ok { // The channel has been closed
		return nil, io.EOF
	} else if item == nil {
		// See onClose for the explanation about sending a nil item.
		close(c.ch)
		return nil, io.EOF
	}

	return item, nil
}

// receiveFrame receives one full frame from the WebSocket. It blocks until the
// frame is received.
func (c *Conn) receiveFrame(observeDeadline bool) (*dom.MessageEvent, error) {
	var deadlineChan <-chan time.Time // Receiving on a nil channel always blocks indefinitely

	if observeDeadline && !c.readDeadline.IsZero() {
		now := time.Now()
		if now.After(c.readDeadline) {
			select {
			case item, ok := <-c.ch:
				return c.handleFrame(item, ok)
			default:
				return nil, errDeadlineReached
			}
		}

		timer := time.NewTimer(c.readDeadline.Sub(now))
		defer timer.Stop()

		deadlineChan = timer.C
	}

	select {
	case item, ok := <-c.ch:
		return c.handleFrame(item, ok)
	case <-deadlineChan:
		return nil, errDeadlineReached
	}
}

func getFrameData(obj js.Object) []byte {
	// Check if it's an array buffer. If so, convert it to a Go byte slice.
	if constructor := obj.Get("constructor"); constructor == js.Global.Get("ArrayBuffer") {
		int8Array := js.Global.Get("Uint8Array").New(obj)
		return int8Array.Interface().([]byte)
	}

	return []byte(obj.Str())
}

func (c *Conn) Read(b []byte) (n int, err error) {
	if c.readBuf != nil {
		n, err = c.readBuf.Read(b)
		if err == io.EOF {
			c.readBuf = nil
			err = nil
		}
		// If we read nothing from the buffer, continue to trying to receive.
		// This saves us when the last Read call emptied the buffer and this
		// call triggers the EOF. There's probably a better way of doing this,
		// but I'm really tired.
		if n > 0 {
			return
		}
	}

	frame, err := c.receiveFrame(true)
	if err != nil {
		return 0, err
	}

	receivedBytes := getFrameData(frame.Data)

	n = copy(b, receivedBytes)
	// Fast path: The entire frame's contents have been copied into b.
	if n >= len(receivedBytes) {
		return
	}

	c.readBuf = bytes.NewReader(receivedBytes[n:])
	return
}

// Write writes the contents of b to the WebSocket using a binary opcode.
func (c *Conn) Write(b []byte) (n int, err error) {
	// []byte is converted to an (U)Int8Array by GopherJS, which fullfils the
	// ArrayBufferView definition.
	err = c.Send(b)
	if err != nil {
		return
	}
	n = len(b)
	return
}

// WriteString writes the contents of s to the WebSocket using a text frame
// opcode.
func (c *Conn) WriteString(s string) (n int, err error) {
	err = c.Send(s)
	if err != nil {
		return
	}
	n = len(s)
	return
}

// BUG(nightexcessive): Conn doesn't currently fulfill the net.Conn interface
// because we can't return net.Addr from Conn.LocalAddr and Conn.RemoteAddr
// because net.init() causes a panic due to attempts to make syscalls.
//
// See: https://github.com/gopherjs/gopherjs/issues/123

// LocalAddr would typically return the local network address, but due to
// limitations in the JavaScript API, it is unable to. Calling this method will
// cause a panic.
func (c *Conn) LocalAddr() *Addr {
	// BUG(nightexcessive): Conn.LocalAddr() panics because the underlying
	// JavaScript API has no way of figuring out the local address.

	// TODO(nightexcessive): Find a more graceful way to handle this
	panic("we are unable to implement websocket.Conn.LocalAddr() due to limitations in the underlying JavaScript API")
}

// RemoteAddr returns the remote network address, based on
// websocket.WebSocket.URL.
func (c *Conn) RemoteAddr() *Addr {
	wsURL, err := url.Parse(c.URL)
	if err != nil {
		// TODO(nightexcessive): Should we be panicking for this?
		panic(err)
	}
	return &Addr{wsURL}
}

// SetDeadline sets the read and write deadlines associated with the connection.
// It is equivalent to calling both SetReadDeadline and SetWriteDeadline.
//
// A zero value for t means that I/O operations will not time out.
func (c *Conn) SetDeadline(t time.Time) error {
	c.readDeadline = t
	return nil
}

// SetReadDeadline sets the deadline for future Read calls. A zero value for t
// means Read will not time out.
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.readDeadline = t
	return nil
}

// SetWriteDeadline sets the deadline for future Write calls. Because our writes
// do not block, this function is a no-op.
func (c *Conn) SetWriteDeadline(t time.Time) error {
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 trivago GmbH
//
// 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 tstrings

import (
	"fmt"
	"math"
	"strings"
)

var simpleEscapeChars = strings.NewReplacer("\\n", "\n", "\\r", "\r", "\\t", "\t")
var jsonEscapeChars = strings.NewReplacer("\\", "\\\\", "\"", "\\\"")

// Unescape replaces occurrences of \\n, \\r and \\t with real escape codes.
func Unescape(text string) string {
	return simpleEscapeChars.Replace(text)
}

// EscapeJSON replaces occurrences of \ and " with escaped versions.
func EscapeJSON(text string) string {
	return jsonEscapeChars.Replace(text)
}

// ItoLen returns the length of an unsingned integer when converted to a string
func ItoLen(number uint64) int {
	switch {
	case number < 10:
		return 1
	default:
		return int(math.Log10(float64(number)) + 1)
	}
}

// Itob writes an unsigned integer to the start of a given byte buffer.
func Itob(number uint64, buffer []byte) error {
	numberLen := ItoLen(number)
	bufferLen := len(buffer)

	if numberLen > bufferLen {
		return fmt.Errorf("Number too large for buffer")
	}

	for i := numberLen - 1; i >= 0; i-- {
		buffer[i] = '0' + byte(number%10)
		number /= 10
	}

	return nil
}

// Itobe writes an unsigned integer to the end of a given byte buffer.
func Itobe(number uint64, buffer []byte) error {
	for i := len(buffer) - 1; i >= 0; i-- {
		buffer[i] = '0' + byte(number%10)
		number /= 10

		// Check here because the number 0 has to be written, too
		if number == 0 {
			return nil
		}
	}

	return fmt.Errorf("Number too large for buffer")
}

// Btoi is a fast byte buffer to unsigned int parser that reads until the first
// non-number character is found. It returns the number value as well as the
// length of the number string encountered.
// If a number could not be parsed the returned length will be 0
func Btoi(buffer []byte) (uint64, int) {
	number := uint64(0)
	index := 0
	bufferLen := len(buffer)

	for index < bufferLen && buffer[index] >= '0' && buffer[index] <= '9' {
		parsed := uint64(buffer[index] - '0')
		number = number*10 + parsed
		index++
	}

	return number, index
}

// IndexN returns the nth occurrences of sep in s or -1 if there is no nth
// occurrence of sep.
func IndexN(s, sep string, n int) int {
	sepIdx := 0
	for i := 0; i < n; i++ {
		nextIdx := strings.Index(s[sepIdx:], sep)
		if nextIdx == -1 {
			return -1 // ### return, not found ###
		}
		sepIdx += nextIdx + 1
	}
	return sepIdx - 1
}

// LastIndexN returns the nth occurrence of sep in s or -1 if there is no nth
// occurrence of sep. Searching is going from the end of the string to the start.
func LastIndexN(s, sep string, n int) int {
	if n == 0 {
		return -1 // ### return, nonsense ###
	}
	sepIdx := len(s)
	for i := 0; i < n; i++ {
		sepIdx = strings.LastIndex(s[:sepIdx], sep)
		if sepIdx == -1 {
			return -1 // ### return, not found ###
		}
	}
	return sepIdx
}

// IsInt returns true if the given string can be converted to an integer.
// The string must contain no whitespaces.
func IsInt(s string) bool {
	for i, c := range s {
		if (c <= '0' || c >= '9') && (c != '-' || i != 0) {
			return false
		}
	}
	return true
}

// JoinStringers works like strings.Join but takes an array of fmt.Stringer.
func JoinStringers(a []fmt.Stringer, sep string) string {
	if len(a) == 0 {
		return ""
	}
	if len(a) == 1 {
		return a[0].String()
	}

	str := make([]string, len(a))
	for i, s := range a {
		str[i] = s.String()
	}

	return strings.Join(str, sep)
}
<commit_msg>added json and byte/string ref functions<commit_after>// Copyright 2015-2016 trivago GmbH
//
// 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 tstrings

import (
	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"strings"
	"unsafe"
)

var simpleEscapeChars = strings.NewReplacer("\\n", "\n", "\\r", "\r", "\\t", "\t", "\\f", "\f", "\\b", "\b")
var jsonEscapeChars = strings.NewReplacer("\\", "\\\\", "\"", "\\\"", "\\n", "\n", "\\r", "\r", "\\t", "\t", "\\f", "\f", "\\b", "\b")

// Unescape replaces occurrences of \\n, \\r and \\t with real escape codes.
func Unescape(text string) string {
	return simpleEscapeChars.Replace(text)
}

// EscapeJSON replaces occurrences of \ and " with escaped versions.
func EscapeJSON(text string) string {
	return jsonEscapeChars.Replace(text)
}

// ItoLen returns the length of an unsingned integer when converted to a string
func ItoLen(number uint64) int {
	switch {
	case number < 10:
		return 1
	default:
		return int(math.Log10(float64(number)) + 1)
	}
}

// Itob writes an unsigned integer to the start of a given byte buffer.
func Itob(number uint64, buffer []byte) error {
	numberLen := ItoLen(number)
	bufferLen := len(buffer)

	if numberLen > bufferLen {
		return fmt.Errorf("Number too large for buffer")
	}

	for i := numberLen - 1; i >= 0; i-- {
		buffer[i] = '0' + byte(number%10)
		number /= 10
	}

	return nil
}

// Itobe writes an unsigned integer to the end of a given byte buffer.
func Itobe(number uint64, buffer []byte) error {
	for i := len(buffer) - 1; i >= 0; i-- {
		buffer[i] = '0' + byte(number%10)
		number /= 10

		// Check here because the number 0 has to be written, too
		if number == 0 {
			return nil
		}
	}

	return fmt.Errorf("Number too large for buffer")
}

// Btoi is a fast byte buffer to unsigned int parser that reads until the first
// non-number character is found. It returns the number value as well as the
// length of the number string encountered.
// If a number could not be parsed the returned length will be 0
func Btoi(buffer []byte) (uint64, int) {
	number := uint64(0)
	index := 0
	bufferLen := len(buffer)

	for index < bufferLen && buffer[index] >= '0' && buffer[index] <= '9' {
		parsed := uint64(buffer[index] - '0')
		number = number*10 + parsed
		index++
	}

	return number, index
}

// IndexN returns the nth occurrences of sep in s or -1 if there is no nth
// occurrence of sep.
func IndexN(s, sep string, n int) int {
	sepIdx := 0
	for i := 0; i < n; i++ {
		nextIdx := strings.Index(s[sepIdx:], sep)
		if nextIdx == -1 {
			return -1 // ### return, not found ###
		}
		sepIdx += nextIdx + 1
	}
	return sepIdx - 1
}

// LastIndexN returns the nth occurrence of sep in s or -1 if there is no nth
// occurrence of sep. Searching is going from the end of the string to the start.
func LastIndexN(s, sep string, n int) int {
	if n == 0 {
		return -1 // ### return, nonsense ###
	}
	sepIdx := len(s)
	for i := 0; i < n; i++ {
		sepIdx = strings.LastIndex(s[:sepIdx], sep)
		if sepIdx == -1 {
			return -1 // ### return, not found ###
		}
	}
	return sepIdx
}

// IsInt returns true if the given string can be converted to an integer.
// The string must contain no whitespaces.
func IsInt(s string) bool {
	for i, c := range s {
		if (c <= '0' || c >= '9') && (c != '-' || i != 0) {
			return false
		}
	}
	return true
}

// IsJSON returns true if the given byte slice contains valid JSON data.
// You can access the results by utiliuing the RawMessage returned.
func IsJSON(data []byte) (bool, json.RawMessage) {
	delayedResult := json.RawMessage{}
	err := json.Unmarshal(data, delayedResult)
	return err == nil, delayedResult

}

// ByteRef is a standard byte slice that is referencing a string.
// This type is considered unsafe and should only be used for fast conversion
// in case of read-only string access via []byte.
type ByteRef []byte

// NewByteRef creates an unsafe byte slice reference to the given string.
// The referenced data is not guaranteed to stay valid if no reference to the
// original string is held anymore. The returned ByteRef does not count as
// reference.
// Writing to the returned ByteRef will result in a segfault.
func NewByteRef(a string) ByteRef {
	stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&a))
	sliceHeader := &reflect.SliceHeader{
		Data: stringHeader.Data,
		Len:  stringHeader.Len,
		Cap:  stringHeader.Len,
	}
	return *(*ByteRef)(unsafe.Pointer(sliceHeader))
}

// StringRef is a standard string that is referencing the data of a byte slice.
// The string changes if the referenced byte slice changes. If the referenced
// byte slice changes size this data might get invalid and result in segfaults.
// As of that, this type is to be considered unsafe and should only be used
// in special, performance critical situations.
type StringRef string

// NewStringRef creates a unsafe string reference to to the given byte slice.
// The referenced data is not guaranteed to stay valid if no reference to the
// original byte slice is held anymore. The returned StringRef does not count as
// reference.
// Changing the size of the underlying byte slice will result in a segfault.
func NewStringRef(a []byte) StringRef {
	sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&a))
	stringHeader := &reflect.StringHeader{
		Data: sliceHeader.Data,
		Len:  sliceHeader.Len,
	}
	return *(*StringRef)(unsafe.Pointer(stringHeader))
}

// JoinStringers works like strings.Join but takes an array of fmt.Stringer.
func JoinStringers(a []fmt.Stringer, sep string) string {
	if len(a) == 0 {
		return ""
	}
	if len(a) == 1 {
		return a[0].String()
	}

	str := make([]string, len(a))
	for i, s := range a {
		str[i] = s.String()
	}

	return strings.Join(str, sep)
}
<|endoftext|>


<commit_before>package pq

import (
	"database/sql/driver"
	"encoding/binary"
	"errors"
	"fmt"
	"sync"
)

var (
	errCopyInClosed               = errors.New("pq: copyin statement has already been closed")
	errBinaryCopyNotSupported     = errors.New("pq: only text format supported for COPY")
	errCopyToNotSupported         = errors.New("pq: COPY TO is not supported")
	errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction")
	errCopyInProgress             = errors.New("pq: COPY in progress")
)

// CopyIn creates a COPY FROM statement which can be prepared with
// Tx.Prepare().  The target table should be visible in search_path.
func CopyIn(table string, columns ...string) string {
	stmt := "COPY " + QuoteIdentifier(table) + " ("
	for i, col := range columns {
		if i != 0 {
			stmt += ", "
		}
		stmt += QuoteIdentifier(col)
	}
	stmt += ") FROM STDIN"
	return stmt
}

// CopyInSchema creates a COPY FROM statement which can be prepared with
// Tx.Prepare().
func CopyInSchema(schema, table string, columns ...string) string {
	stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " ("
	for i, col := range columns {
		if i != 0 {
			stmt += ", "
		}
		stmt += QuoteIdentifier(col)
	}
	stmt += ") FROM STDIN"
	return stmt
}

type copyin struct {
	cn      *conn
	buffer  []byte
	rowData chan []byte
	done    chan bool
	driver.Result

	closed bool

	sync.Mutex // guards err
	err        error
}

const ciBufferSize = 64 * 1024

// flush buffer before the buffer is filled up and needs reallocation
const ciBufferFlushSize = 63 * 1024

func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) {
	if !cn.isInTransaction() {
		return nil, errCopyNotSupportedOutsideTxn
	}

	ci := &copyin{
		cn:      cn,
		buffer:  make([]byte, 0, ciBufferSize),
		rowData: make(chan []byte),
		done:    make(chan bool, 1),
	}
	// add CopyData identifier + 4 bytes for message length
	ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0)

	b := cn.writeBuf('Q')
	b.string(q)
	cn.send(b)

awaitCopyInResponse:
	for {
		t, r := cn.recv1()
		switch t {
		case 'G':
			if r.byte() != 0 {
				err = errBinaryCopyNotSupported
				break awaitCopyInResponse
			}
			go ci.resploop()
			return ci, nil
		case 'H':
			err = errCopyToNotSupported
			break awaitCopyInResponse
		case 'E':
			err = parseError(r)
		case 'Z':
			if err == nil {
				ci.setBad()
				errorf("unexpected ReadyForQuery in response to COPY")
			}
			cn.processReadyForQuery(r)
			return nil, err
		default:
			ci.setBad()
			errorf("unknown response for copy query: %q", t)
		}
	}

	// something went wrong, abort COPY before we return
	b = cn.writeBuf('f')
	b.string(err.Error())
	cn.send(b)

	for {
		t, r := cn.recv1()
		switch t {
		case 'c', 'C', 'E':
		case 'Z':
			// correctly aborted, we're done
			cn.processReadyForQuery(r)
			return nil, err
		default:
			ci.setBad()
			errorf("unknown response for CopyFail: %q", t)
		}
	}
}

func (ci *copyin) flush(buf []byte) {
	// set message length (without message identifier)
	binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1))

	_, err := ci.cn.c.Write(buf)
	if err != nil {
		panic(err)
	}
}

func (ci *copyin) resploop() {
	for {
		var r readBuf
		t, err := ci.cn.recvMessage(&r)
		if err != nil {
			ci.setBad()
			ci.setError(err)
			ci.done <- true
			return
		}
		switch t {
		case 'C':
			// complete
			res, _ := ci.cn.parseComplete(r.string())
			ci.setResult(res)
		case 'N':
			if n := ci.cn.noticeHandler; n != nil {
				n(parseError(&r))
			}
		case 'Z':
			ci.cn.processReadyForQuery(&r)
			ci.done <- true
			return
		case 'E':
			err := parseError(&r)
			ci.setError(err)
		default:
			ci.setBad()
			ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t))
			ci.done <- true
			return
		}
	}
}

func (ci *copyin) setBad() {
	ci.Lock()
	ci.cn.bad = true
	ci.Unlock()
}

func (ci *copyin) isBad() bool {
	ci.Lock()
	b := ci.cn.bad
	ci.Unlock()
	return b
}

func (ci *copyin) isErrorSet() bool {
	ci.Lock()
	isSet := (ci.err != nil)
	ci.Unlock()
	return isSet
}

// setError() sets ci.err if one has not been set already.  Caller must not be
// holding ci.Mutex.
func (ci *copyin) setError(err error) {
	ci.Lock()
	if ci.err == nil {
		ci.err = err
	}
	ci.Unlock()
}

func (ci *copyin) setResult(result driver.Result) {
	ci.Lock()
	ci.Result = result
	ci.Unlock()
}

func (ci *copyin) getResult() driver.Result {
	ci.Lock()
	result := ci.Result
	if result == nil {
		return driver.RowsAffected(0)
	}
	ci.Unlock()
	return result
}

func (ci *copyin) NumInput() int {
	return -1
}

func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) {
	return nil, ErrNotSupported
}

// Exec inserts values into the COPY stream. The insert is asynchronous
// and Exec can return errors from previous Exec calls to the same
// COPY stmt.
//
// You need to call Exec(nil) to sync the COPY stream and to get any
// errors from pending data, since Stmt.Close() doesn't return errors
// to the user.
func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
	if ci.closed {
		return nil, errCopyInClosed
	}

	if ci.isBad() {
		return nil, driver.ErrBadConn
	}
	defer ci.cn.errRecover(&err)

	if ci.isErrorSet() {
		return nil, ci.err
	}

	if len(v) == 0 {
		if err := ci.Close(); err != nil {
			return driver.RowsAffected(0), err
		}

		return ci.getResult(), nil
	}

	numValues := len(v)
	for i, value := range v {
		ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value)
		if i < numValues-1 {
			ci.buffer = append(ci.buffer, '\t')
		}
	}

	ci.buffer = append(ci.buffer, '\n')

	if len(ci.buffer) > ciBufferFlushSize {
		ci.flush(ci.buffer)
		// reset buffer, keep bytes for message identifier and length
		ci.buffer = ci.buffer[:5]
	}

	return driver.RowsAffected(0), nil
}

func (ci *copyin) Close() (err error) {
	if ci.closed { // Don't do anything, we're already closed
		return nil
	}
	ci.closed = true

	if ci.isBad() {
		return driver.ErrBadConn
	}
	defer ci.cn.errRecover(&err)

	if len(ci.buffer) > 0 {
		ci.flush(ci.buffer)
	}
	// Avoid touching the scratch buffer as resploop could be using it.
	err = ci.cn.sendSimpleMessage('c')
	if err != nil {
		return err
	}

	<-ci.done
	ci.cn.inCopy = false

	if ci.isErrorSet() {
		err = ci.err
		return err
	}
	return nil
}
<commit_msg>unlock mutex earlier<commit_after>package pq

import (
	"database/sql/driver"
	"encoding/binary"
	"errors"
	"fmt"
	"sync"
)

var (
	errCopyInClosed               = errors.New("pq: copyin statement has already been closed")
	errBinaryCopyNotSupported     = errors.New("pq: only text format supported for COPY")
	errCopyToNotSupported         = errors.New("pq: COPY TO is not supported")
	errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction")
	errCopyInProgress             = errors.New("pq: COPY in progress")
)

// CopyIn creates a COPY FROM statement which can be prepared with
// Tx.Prepare().  The target table should be visible in search_path.
func CopyIn(table string, columns ...string) string {
	stmt := "COPY " + QuoteIdentifier(table) + " ("
	for i, col := range columns {
		if i != 0 {
			stmt += ", "
		}
		stmt += QuoteIdentifier(col)
	}
	stmt += ") FROM STDIN"
	return stmt
}

// CopyInSchema creates a COPY FROM statement which can be prepared with
// Tx.Prepare().
func CopyInSchema(schema, table string, columns ...string) string {
	stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " ("
	for i, col := range columns {
		if i != 0 {
			stmt += ", "
		}
		stmt += QuoteIdentifier(col)
	}
	stmt += ") FROM STDIN"
	return stmt
}

type copyin struct {
	cn      *conn
	buffer  []byte
	rowData chan []byte
	done    chan bool
	driver.Result

	closed bool

	sync.Mutex // guards err
	err        error
}

const ciBufferSize = 64 * 1024

// flush buffer before the buffer is filled up and needs reallocation
const ciBufferFlushSize = 63 * 1024

func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) {
	if !cn.isInTransaction() {
		return nil, errCopyNotSupportedOutsideTxn
	}

	ci := &copyin{
		cn:      cn,
		buffer:  make([]byte, 0, ciBufferSize),
		rowData: make(chan []byte),
		done:    make(chan bool, 1),
	}
	// add CopyData identifier + 4 bytes for message length
	ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0)

	b := cn.writeBuf('Q')
	b.string(q)
	cn.send(b)

awaitCopyInResponse:
	for {
		t, r := cn.recv1()
		switch t {
		case 'G':
			if r.byte() != 0 {
				err = errBinaryCopyNotSupported
				break awaitCopyInResponse
			}
			go ci.resploop()
			return ci, nil
		case 'H':
			err = errCopyToNotSupported
			break awaitCopyInResponse
		case 'E':
			err = parseError(r)
		case 'Z':
			if err == nil {
				ci.setBad()
				errorf("unexpected ReadyForQuery in response to COPY")
			}
			cn.processReadyForQuery(r)
			return nil, err
		default:
			ci.setBad()
			errorf("unknown response for copy query: %q", t)
		}
	}

	// something went wrong, abort COPY before we return
	b = cn.writeBuf('f')
	b.string(err.Error())
	cn.send(b)

	for {
		t, r := cn.recv1()
		switch t {
		case 'c', 'C', 'E':
		case 'Z':
			// correctly aborted, we're done
			cn.processReadyForQuery(r)
			return nil, err
		default:
			ci.setBad()
			errorf("unknown response for CopyFail: %q", t)
		}
	}
}

func (ci *copyin) flush(buf []byte) {
	// set message length (without message identifier)
	binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1))

	_, err := ci.cn.c.Write(buf)
	if err != nil {
		panic(err)
	}
}

func (ci *copyin) resploop() {
	for {
		var r readBuf
		t, err := ci.cn.recvMessage(&r)
		if err != nil {
			ci.setBad()
			ci.setError(err)
			ci.done <- true
			return
		}
		switch t {
		case 'C':
			// complete
			res, _ := ci.cn.parseComplete(r.string())
			ci.setResult(res)
		case 'N':
			if n := ci.cn.noticeHandler; n != nil {
				n(parseError(&r))
			}
		case 'Z':
			ci.cn.processReadyForQuery(&r)
			ci.done <- true
			return
		case 'E':
			err := parseError(&r)
			ci.setError(err)
		default:
			ci.setBad()
			ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t))
			ci.done <- true
			return
		}
	}
}

func (ci *copyin) setBad() {
	ci.Lock()
	ci.cn.bad = true
	ci.Unlock()
}

func (ci *copyin) isBad() bool {
	ci.Lock()
	b := ci.cn.bad
	ci.Unlock()
	return b
}

func (ci *copyin) isErrorSet() bool {
	ci.Lock()
	isSet := (ci.err != nil)
	ci.Unlock()
	return isSet
}

// setError() sets ci.err if one has not been set already.  Caller must not be
// holding ci.Mutex.
func (ci *copyin) setError(err error) {
	ci.Lock()
	if ci.err == nil {
		ci.err = err
	}
	ci.Unlock()
}

func (ci *copyin) setResult(result driver.Result) {
	ci.Lock()
	ci.Result = result
	ci.Unlock()
}

func (ci *copyin) getResult() driver.Result {
	ci.Lock()
	result := ci.Result
	ci.Unlock()
	if result == nil {
		return driver.RowsAffected(0)
	}
	return result
}

func (ci *copyin) NumInput() int {
	return -1
}

func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) {
	return nil, ErrNotSupported
}

// Exec inserts values into the COPY stream. The insert is asynchronous
// and Exec can return errors from previous Exec calls to the same
// COPY stmt.
//
// You need to call Exec(nil) to sync the COPY stream and to get any
// errors from pending data, since Stmt.Close() doesn't return errors
// to the user.
func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
	if ci.closed {
		return nil, errCopyInClosed
	}

	if ci.isBad() {
		return nil, driver.ErrBadConn
	}
	defer ci.cn.errRecover(&err)

	if ci.isErrorSet() {
		return nil, ci.err
	}

	if len(v) == 0 {
		if err := ci.Close(); err != nil {
			return driver.RowsAffected(0), err
		}

		return ci.getResult(), nil
	}

	numValues := len(v)
	for i, value := range v {
		ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value)
		if i < numValues-1 {
			ci.buffer = append(ci.buffer, '\t')
		}
	}

	ci.buffer = append(ci.buffer, '\n')

	if len(ci.buffer) > ciBufferFlushSize {
		ci.flush(ci.buffer)
		// reset buffer, keep bytes for message identifier and length
		ci.buffer = ci.buffer[:5]
	}

	return driver.RowsAffected(0), nil
}

func (ci *copyin) Close() (err error) {
	if ci.closed { // Don't do anything, we're already closed
		return nil
	}
	ci.closed = true

	if ci.isBad() {
		return driver.ErrBadConn
	}
	defer ci.cn.errRecover(&err)

	if len(ci.buffer) > 0 {
		ci.flush(ci.buffer)
	}
	// Avoid touching the scratch buffer as resploop could be using it.
	err = ci.cn.sendSimpleMessage('c')
	if err != nil {
		return err
	}

	<-ci.done
	ci.cn.inCopy = false

	if ci.isErrorSet() {
		err = ci.err
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package mailjet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
)

// DebugLevel defines the verbosity of the debug.
var DebugLevel int

// These are the different level of debug.
const (
	LevelNone      = iota // No debug.
	LevelDebug            // Debug without body.
	LevelDebugFull        // Debug with body.
)

// User-Agent is formated as "UserAgentBase/UserAgentVersion;runtime.Version()".
const (
	UserAgentBase    = "mailjet-api-v3-go"
	UserAgentVersion = "3.0.1"
)

const (
	apiBase  = "https://api.mailjet.com/v3"
	apiPath  = "REST"
	dataPath = "DATA"
)

// createRequest is the main core function.
func createRequest(method string, url string,
	payload interface{}, onlyFields []string,
	options ...RequestOptions) (req *http.Request, err error) {

	body, err := convertPayload(payload, onlyFields)
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	req, err = http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	for _, option := range options {
		option(req)
	}
	userAgent(req)
	req.Header.Add("Accept", "application/json")
	return req, err
}

// converPayload returns payload casted in []byte.
// If the payload is a structure, it's encoded to JSON.
func convertPayload(payload interface{}, onlyFields []string) (body []byte, err error) {
	if payload != nil {
		switch t := payload.(type) {
		case string:
			body = []byte(t)
		case []byte:
			body = t
		default:
			v := reflect.Indirect(reflect.ValueOf(payload))
			if v.Kind() == reflect.Ptr {
				return convertPayload(v.Interface(), onlyFields)
			} else if v.Kind() == reflect.Struct {
				body, err = json.Marshal(buildMap(v, onlyFields))
				if err != nil {
					return body, err
				}
			}
		}
		if DebugLevel == LevelDebugFull {
			log.Println("Body:", string(body))
		}
	}
	return body, err
}

// buildMap returns a map with fields specified in onlyFields (all fields if nil)
// and without the read_only fields.
func buildMap(v reflect.Value, onlyFields []string) map[string]interface{} {
	res := make(map[string]interface{})
	if onlyFields != nil {
		for _, onlyField := range onlyFields {
			fieldType, exist := v.Type().FieldByName(onlyField)
			if exist {
				addFieldToMap(true, fieldType, v.FieldByName(onlyField), res)
			}
		}
	} else {
		for i := 0; i < v.NumField(); i++ {
			addFieldToMap(false, v.Type().Field(i), v.Field(i), res)
		}
	}
	return res
}

func addFieldToMap(onlyField bool, fieldType reflect.StructField,
	fieldValue reflect.Value, res map[string]interface{}) {
	if fieldType.Tag.Get("mailjet") != "read_only" {
		name, second := parseTag(fieldType.Tag.Get("json"))
		if name == "" {
			name = fieldType.Name
		}
		if !onlyField && second == "omitempty" &&
			isEmptyValue(fieldValue) {
			return
		}
		res[name] = fieldValue.Interface()
	}
}

func parseTag(tag string) (string, string) {
	if idx := strings.Index(tag, ","); idx != -1 {
		return tag[:idx], tag[idx+1:]
	}
	return tag, ""
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

// userAgent add the User-Agent value to the request header.
func userAgent(req *http.Request) {
	ua := fmt.Sprintf("%s/%s;%s",
		UserAgentBase,
		UserAgentVersion,
		runtime.Version(),
	)
	req.Header.Add("User-Agent", ua)
}

func buildURL(baseURL string, info *Request) string {
	tokens := []string{baseURL, apiPath, info.Resource}
	if info.ID != 0 {
		id := strconv.FormatInt(info.ID, 10)
		tokens = append(tokens, id)
	} else if info.AltID != "" {
		tokens = append(tokens, string(info.AltID))
	}
	if info.Action != "" {
		tokens = append(tokens, info.Action)
	}
	if info.ActionID != 0 {
		actionID := strconv.FormatInt(info.ActionID, 10)
		tokens = append(tokens, actionID)
	}
	return strings.Join(tokens, "/")
}

func buildDataURL(baseURL string, info *DataRequest) string {
	tokens := []string{baseURL, dataPath, info.SourceType}
	if info.SourceTypeID != 0 {
		id := strconv.FormatInt(info.SourceTypeID, 10)
		tokens = append(tokens, id)
	}
	if info.DataType != "" {
		tokens = append(tokens, info.DataType)
		if info.MimeType != "" {
			tokens = append(tokens, info.MimeType)
		}
	}
	if info.DataTypeID != 0 {
		DataTypeID := strconv.FormatInt(info.DataTypeID, 10)
		tokens = append(tokens, DataTypeID)
	} else if info.LastID == true {
		tokens = append(tokens, "LAST")
	}
	return strings.Join(tokens, "/")
}

// readJsonResult decodes the API response, returns Count and Total values
// and stores the Data in the value pointed to by data.
func readJSONResult(r io.Reader, data interface{}) (int, int, error) {
	var res RequestResult
	res.Data = data

	jsonBlob, err := ioutil.ReadAll(r) // ReadAll and store in jsonBlob (mandatory if we want to unmarshal two times)
	if err != nil {
		return 0, 0, fmt.Errorf("Error reading API response: %s", err)
	}
	if DebugLevel == LevelDebugFull {
		log.Println("Body: ", string(jsonBlob)) // DEBUG
	}

	err = json.Unmarshal(jsonBlob, &res) // First try with the RequestResult struct
	if err != nil {
		return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
	} else if _, ok := data.(**SentResult); ok { // Send API case
		err = json.Unmarshal(jsonBlob, data) // Trying directly with struct specified in parameter
		if err != nil {
			return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
		}
		return 0, 0, nil // Count and Total are undetermined
	}
	return res.Count, res.Total, nil
}

// NbAttempt defines the number of attempt
// for a request as long as StatusCode == 500.
var NbAttempt = 5

// doRequest is called to execute the request. Authentification is set
// with the public key and the secret key specified in MailjetClient.
func (c *HTTPClient) doRequest(req *http.Request) (resp *http.Response, err error) {
	debugRequest(req) //DEBUG
	req.SetBasicAuth(c.apiKeyPublic, c.apiKeyPrivate)
	for attempt := 0; attempt < NbAttempt; attempt++ {
		if resp != nil {
			resp.Body.Close()
		}
		resp, err = c.client.Do(req)
		if err != nil || (resp != nil && resp.StatusCode != 500) {
			break
		}
	}
	defer debugResponse(resp) //DEBUG
	if err != nil {
		return resp, fmt.Errorf("Error getting %s: %s", req.URL, err)
	}
	err = checkResponseError(resp)
	return resp, err
}

// checkResponseError returns response error if the statuscode is < 200 or >= 400.
func checkResponseError(resp *http.Response) error {
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		var mailjetErr RequestError
		mailjetErr.StatusCode = resp.StatusCode

		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			mailjetErr.ErrorMessage = "unable to read response body"
			mailjetErr.ErrorInfo = err.Error()
			return mailjetErr
		}

		err = json.Unmarshal(b, &mailjetErr)
		if err != nil {
			mailjetErr.ErrorMessage = "unexpected server response: %s" + string(b)
			mailjetErr.ErrorInfo = "json unmarshal error: " + err.Error()
			return mailjetErr
		}
		return mailjetErr
	}

	return nil
}

// debugRequest is a custom dump of the request.
// Method used, final URl called, and Header content are logged.
func debugRequest(req *http.Request) {
	if DebugLevel > LevelNone && req != nil {
		log.Printf("Method used is: %s\n", req.Method)
		log.Printf("Final URL is: %s\n", req.URL)
		log.Printf("Header is: %s\n", req.Header)
	}
}

// debugResponse is a custom dump of the response.
// Status and Header content are logged.
func debugResponse(resp *http.Response) {
	if DebugLevel > LevelNone && resp != nil {
		log.Printf("Status is: %s\n", resp.Status)
		log.Printf("Header is: %s\n", resp.Header)
	}
}
<commit_msg>Fix user agent version<commit_after>package mailjet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
)

// DebugLevel defines the verbosity of the debug.
var DebugLevel int

// These are the different level of debug.
const (
	LevelNone      = iota // No debug.
	LevelDebug            // Debug without body.
	LevelDebugFull        // Debug with body.
)

// User-Agent is formated as "UserAgentBase/UserAgentVersion;runtime.Version()".
const (
	UserAgentBase    = "mailjet-api-v3-go"
	UserAgentVersion = "3.1.1"
)

const (
	apiBase  = "https://api.mailjet.com/v3"
	apiPath  = "REST"
	dataPath = "DATA"
)

// createRequest is the main core function.
func createRequest(method string, url string,
	payload interface{}, onlyFields []string,
	options ...RequestOptions) (req *http.Request, err error) {

	body, err := convertPayload(payload, onlyFields)
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	req, err = http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	for _, option := range options {
		option(req)
	}
	userAgent(req)
	req.Header.Add("Accept", "application/json")
	return req, err
}

// converPayload returns payload casted in []byte.
// If the payload is a structure, it's encoded to JSON.
func convertPayload(payload interface{}, onlyFields []string) (body []byte, err error) {
	if payload != nil {
		switch t := payload.(type) {
		case string:
			body = []byte(t)
		case []byte:
			body = t
		default:
			v := reflect.Indirect(reflect.ValueOf(payload))
			if v.Kind() == reflect.Ptr {
				return convertPayload(v.Interface(), onlyFields)
			} else if v.Kind() == reflect.Struct {
				body, err = json.Marshal(buildMap(v, onlyFields))
				if err != nil {
					return body, err
				}
			}
		}
		if DebugLevel == LevelDebugFull {
			log.Println("Body:", string(body))
		}
	}
	return body, err
}

// buildMap returns a map with fields specified in onlyFields (all fields if nil)
// and without the read_only fields.
func buildMap(v reflect.Value, onlyFields []string) map[string]interface{} {
	res := make(map[string]interface{})
	if onlyFields != nil {
		for _, onlyField := range onlyFields {
			fieldType, exist := v.Type().FieldByName(onlyField)
			if exist {
				addFieldToMap(true, fieldType, v.FieldByName(onlyField), res)
			}
		}
	} else {
		for i := 0; i < v.NumField(); i++ {
			addFieldToMap(false, v.Type().Field(i), v.Field(i), res)
		}
	}
	return res
}

func addFieldToMap(onlyField bool, fieldType reflect.StructField,
	fieldValue reflect.Value, res map[string]interface{}) {
	if fieldType.Tag.Get("mailjet") != "read_only" {
		name, second := parseTag(fieldType.Tag.Get("json"))
		if name == "" {
			name = fieldType.Name
		}
		if !onlyField && second == "omitempty" &&
			isEmptyValue(fieldValue) {
			return
		}
		res[name] = fieldValue.Interface()
	}
}

func parseTag(tag string) (string, string) {
	if idx := strings.Index(tag, ","); idx != -1 {
		return tag[:idx], tag[idx+1:]
	}
	return tag, ""
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

// userAgent add the User-Agent value to the request header.
func userAgent(req *http.Request) {
	ua := fmt.Sprintf("%s/%s;%s",
		UserAgentBase,
		UserAgentVersion,
		runtime.Version(),
	)
	req.Header.Add("User-Agent", ua)
}

func buildURL(baseURL string, info *Request) string {
	tokens := []string{baseURL, apiPath, info.Resource}
	if info.ID != 0 {
		id := strconv.FormatInt(info.ID, 10)
		tokens = append(tokens, id)
	} else if info.AltID != "" {
		tokens = append(tokens, string(info.AltID))
	}
	if info.Action != "" {
		tokens = append(tokens, info.Action)
	}
	if info.ActionID != 0 {
		actionID := strconv.FormatInt(info.ActionID, 10)
		tokens = append(tokens, actionID)
	}
	return strings.Join(tokens, "/")
}

func buildDataURL(baseURL string, info *DataRequest) string {
	tokens := []string{baseURL, dataPath, info.SourceType}
	if info.SourceTypeID != 0 {
		id := strconv.FormatInt(info.SourceTypeID, 10)
		tokens = append(tokens, id)
	}
	if info.DataType != "" {
		tokens = append(tokens, info.DataType)
		if info.MimeType != "" {
			tokens = append(tokens, info.MimeType)
		}
	}
	if info.DataTypeID != 0 {
		DataTypeID := strconv.FormatInt(info.DataTypeID, 10)
		tokens = append(tokens, DataTypeID)
	} else if info.LastID == true {
		tokens = append(tokens, "LAST")
	}
	return strings.Join(tokens, "/")
}

// readJsonResult decodes the API response, returns Count and Total values
// and stores the Data in the value pointed to by data.
func readJSONResult(r io.Reader, data interface{}) (int, int, error) {
	var res RequestResult
	res.Data = data

	jsonBlob, err := ioutil.ReadAll(r) // ReadAll and store in jsonBlob (mandatory if we want to unmarshal two times)
	if err != nil {
		return 0, 0, fmt.Errorf("Error reading API response: %s", err)
	}
	if DebugLevel == LevelDebugFull {
		log.Println("Body: ", string(jsonBlob)) // DEBUG
	}

	err = json.Unmarshal(jsonBlob, &res) // First try with the RequestResult struct
	if err != nil {
		return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
	} else if _, ok := data.(**SentResult); ok { // Send API case
		err = json.Unmarshal(jsonBlob, data) // Trying directly with struct specified in parameter
		if err != nil {
			return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
		}
		return 0, 0, nil // Count and Total are undetermined
	}
	return res.Count, res.Total, nil
}

// NbAttempt defines the number of attempt
// for a request as long as StatusCode == 500.
var NbAttempt = 5

// doRequest is called to execute the request. Authentification is set
// with the public key and the secret key specified in MailjetClient.
func (c *HTTPClient) doRequest(req *http.Request) (resp *http.Response, err error) {
	debugRequest(req) //DEBUG
	req.SetBasicAuth(c.apiKeyPublic, c.apiKeyPrivate)
	for attempt := 0; attempt < NbAttempt; attempt++ {
		if resp != nil {
			resp.Body.Close()
		}
		resp, err = c.client.Do(req)
		if err != nil || (resp != nil && resp.StatusCode != 500) {
			break
		}
	}
	defer debugResponse(resp) //DEBUG
	if err != nil {
		return resp, fmt.Errorf("Error getting %s: %s", req.URL, err)
	}
	err = checkResponseError(resp)
	return resp, err
}

// checkResponseError returns response error if the statuscode is < 200 or >= 400.
func checkResponseError(resp *http.Response) error {
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		var mailjetErr RequestError
		mailjetErr.StatusCode = resp.StatusCode

		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			mailjetErr.ErrorMessage = "unable to read response body"
			mailjetErr.ErrorInfo = err.Error()
			return mailjetErr
		}

		err = json.Unmarshal(b, &mailjetErr)
		if err != nil {
			mailjetErr.ErrorMessage = "unexpected server response: %s" + string(b)
			mailjetErr.ErrorInfo = "json unmarshal error: " + err.Error()
			return mailjetErr
		}
		return mailjetErr
	}

	return nil
}

// debugRequest is a custom dump of the request.
// Method used, final URl called, and Header content are logged.
func debugRequest(req *http.Request) {
	if DebugLevel > LevelNone && req != nil {
		log.Printf("Method used is: %s\n", req.Method)
		log.Printf("Final URL is: %s\n", req.URL)
		log.Printf("Header is: %s\n", req.Header)
	}
}

// debugResponse is a custom dump of the response.
// Status and Header content are logged.
func debugResponse(resp *http.Response) {
	if DebugLevel > LevelNone && resp != nil {
		log.Printf("Status is: %s\n", resp.Status)
		log.Printf("Header is: %s\n", resp.Header)
	}
}
<|endoftext|>


<commit_before>package logic

import (
	"fmt"
	"github.com/quintilesims/layer0/common/errors"
	"github.com/quintilesims/layer0/common/models"
)

type EnvironmentLogic interface {
	ListEnvironments() ([]*models.EnvironmentSummary, error)
	GetEnvironment(id string) (*models.Environment, error)
	DeleteEnvironment(id string) error
	CanCreateEnvironment(req models.CreateEnvironmentRequest) (bool, error)
	CreateEnvironment(req models.CreateEnvironmentRequest) (*models.Environment, error)
	UpdateEnvironment(id string, minClusterCount int) (*models.Environment, error)
	CreateEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error
	DeleteEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error
}

type L0EnvironmentLogic struct {
	Logic
}

func NewL0EnvironmentLogic(logic Logic) *L0EnvironmentLogic {
	return &L0EnvironmentLogic{
		Logic: logic,
	}
}

func (e *L0EnvironmentLogic) ListEnvironments() ([]*models.EnvironmentSummary, error) {
	environments, err := e.Backend.ListEnvironments()
	if err != nil {
		return nil, err
	}

	summaries := make([]*models.EnvironmentSummary, len(environments))
	for i, environment := range environments {
		if err := e.populateModel(environment); err != nil {
			return nil, err
		}

		summaries[i] = &models.EnvironmentSummary{
			EnvironmentID:   environment.EnvironmentID,
			EnvironmentName: environment.EnvironmentName,
			OperatingSystem: environment.OperatingSystem,
		}
	}

	return summaries, nil
}

func (e *L0EnvironmentLogic) GetEnvironment(environmentID string) (*models.Environment, error) {
	environment, err := e.Backend.GetEnvironment(environmentID)
	if err != nil {
		return nil, err
	}

	if err := e.populateModel(environment); err != nil {
		return nil, err
	}

	return environment, nil
}

func (e *L0EnvironmentLogic) DeleteEnvironment(environmentID string) error {
	tags, err := e.TagStore.SelectByQuery("environment", environmentID)
	if err != nil {
		return err
	}

	for _, tag := range tags.WithKey("link") {
		if err := e.DeleteEnvironmentLink(environmentID, tag.Value); err != nil {
			return err
		}
	}

	if err := e.Backend.DeleteEnvironment(environmentID); err != nil {
		return err
	}

	if err := e.deleteEntityTags("environment", environmentID); err != nil {
		return err
	}

	return nil
}

func (e *L0EnvironmentLogic) CanCreateEnvironment(req models.CreateEnvironmentRequest) (bool, error) {
	tags, err := e.TagStore.SelectByQuery("environment", "")
	if err != nil {
		return false, err
	}

	tags = tags.WithKey("name").WithValue(req.EnvironmentName)
	return len(tags) == 0, nil
}

func (e *L0EnvironmentLogic) CreateEnvironment(req models.CreateEnvironmentRequest) (*models.Environment, error) {
	if req.EnvironmentName == "" {
		return nil, errors.Newf(errors.MissingParameter, "EnvironmentName is required")
	}

	if req.OperatingSystem == "" {
		return nil, errors.Newf(errors.MissingParameter, "OperatingSystem is required")
	}

	environment, err := e.Backend.CreateEnvironment(
		req.EnvironmentName,
		req.InstanceSize,
		req.OperatingSystem,
		req.AMIID,
		req.MinClusterCount,
		req.UserDataTemplate)
	if err != nil {
		return nil, err
	}

	if err := e.upsertTag(models.Tag{EntityID: environment.EnvironmentID, EntityType: "environment", Key: "name", Value: req.EnvironmentName}); err != nil {
		return nil, err
	}

	if err := e.upsertTag(models.Tag{EntityID: environment.EnvironmentID, EntityType: "environment", Key: "os", Value: req.OperatingSystem}); err != nil {
		return nil, err
	}

	if err := e.populateModel(environment); err != nil {
		return environment, err
	}

	return environment, nil
}

func (e *L0EnvironmentLogic) UpdateEnvironment(environmentID string, minClusterCount int) (*models.Environment, error) {
	environment, err := e.Backend.UpdateEnvironment(environmentID, minClusterCount)
	if err != nil {
		return nil, err
	}

	if err := e.populateModel(environment); err != nil {
		return nil, err
	}

	return environment, nil
}

func (e *L0EnvironmentLogic) CreateEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error {
	if err := e.Backend.CreateEnvironmentLink(sourceEnvironmentID, destEnvironmentID); err != nil {
		return nil
	}

	if err := e.upsertTag(models.Tag{EntityID: sourceEnvironmentID, EntityType: "environment", Key: "link", Value: destEnvironmentID}); err != nil {
		return nil
	}

	if err := e.upsertTag(models.Tag{EntityID: destEnvironmentID, EntityType: "environment", Key: "link", Value: sourceEnvironmentID}); err != nil {
		return nil
	}

	return nil
}

func (e *L0EnvironmentLogic) DeleteEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error {
	if err := e.Backend.DeleteEnvironmentLink(sourceEnvironmentID, destEnvironmentID); err != nil {
		return nil
	}

	sourceTags, err := e.TagStore.SelectByQuery("environment", sourceEnvironmentID)
	if err != nil {
		return err
	}

	for _, tag := range sourceTags.WithKey("link").WithValue(destEnvironmentID) {
		if err := e.TagStore.Delete(tag.TagID); err != nil {
			return err
		}
	}

	destTags, err := e.TagStore.SelectByQuery("environment", destEnvironmentID)
	if err != nil {
		return err
	}

	for _, tag := range destTags.WithKey("link").WithValue(sourceEnvironmentID) {
		if err := e.TagStore.Delete(tag.TagID); err != nil {
			return err
		}
	}

	return nil
}

func (e *L0EnvironmentLogic) populateModel(model *models.Environment) error {
	tags, err := e.TagStore.SelectByQuery("environment", model.EnvironmentID)
	if err != nil {
		return err
	}

	if tag := tags.WithKey("name").First(); tag != nil {
		model.EnvironmentName = tag.Value
	}

	if tag := tags.WithKey("os").First(); tag != nil {
		model.OperatingSystem = tag.Value
	}

	model.Links = []string{}
	for _, tag := range tags {
	fmt.Printf("%#v\n", tag)
	}

	for _, tag := range tags.WithKey("link") {
		model.Links = append(model.Links, tag.Value)
	}

	return nil
}
<commit_msg>rm debug statement<commit_after>package logic

import (
	"fmt"
	"github.com/quintilesims/layer0/common/errors"
	"github.com/quintilesims/layer0/common/models"
)

type EnvironmentLogic interface {
	ListEnvironments() ([]*models.EnvironmentSummary, error)
	GetEnvironment(id string) (*models.Environment, error)
	DeleteEnvironment(id string) error
	CanCreateEnvironment(req models.CreateEnvironmentRequest) (bool, error)
	CreateEnvironment(req models.CreateEnvironmentRequest) (*models.Environment, error)
	UpdateEnvironment(id string, minClusterCount int) (*models.Environment, error)
	CreateEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error
	DeleteEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error
}

type L0EnvironmentLogic struct {
	Logic
}

func NewL0EnvironmentLogic(logic Logic) *L0EnvironmentLogic {
	return &L0EnvironmentLogic{
		Logic: logic,
	}
}

func (e *L0EnvironmentLogic) ListEnvironments() ([]*models.EnvironmentSummary, error) {
	environments, err := e.Backend.ListEnvironments()
	if err != nil {
		return nil, err
	}

	summaries := make([]*models.EnvironmentSummary, len(environments))
	for i, environment := range environments {
		if err := e.populateModel(environment); err != nil {
			return nil, err
		}

		summaries[i] = &models.EnvironmentSummary{
			EnvironmentID:   environment.EnvironmentID,
			EnvironmentName: environment.EnvironmentName,
			OperatingSystem: environment.OperatingSystem,
		}
	}

	return summaries, nil
}

func (e *L0EnvironmentLogic) GetEnvironment(environmentID string) (*models.Environment, error) {
	environment, err := e.Backend.GetEnvironment(environmentID)
	if err != nil {
		return nil, err
	}

	if err := e.populateModel(environment); err != nil {
		return nil, err
	}

	return environment, nil
}

func (e *L0EnvironmentLogic) DeleteEnvironment(environmentID string) error {
	tags, err := e.TagStore.SelectByQuery("environment", environmentID)
	if err != nil {
		return err
	}

	for _, tag := range tags.WithKey("link") {
		if err := e.DeleteEnvironmentLink(environmentID, tag.Value); err != nil {
			return err
		}
	}

	if err := e.Backend.DeleteEnvironment(environmentID); err != nil {
		return err
	}

	if err := e.deleteEntityTags("environment", environmentID); err != nil {
		return err
	}

	return nil
}

func (e *L0EnvironmentLogic) CanCreateEnvironment(req models.CreateEnvironmentRequest) (bool, error) {
	tags, err := e.TagStore.SelectByQuery("environment", "")
	if err != nil {
		return false, err
	}

	tags = tags.WithKey("name").WithValue(req.EnvironmentName)
	return len(tags) == 0, nil
}

func (e *L0EnvironmentLogic) CreateEnvironment(req models.CreateEnvironmentRequest) (*models.Environment, error) {
	if req.EnvironmentName == "" {
		return nil, errors.Newf(errors.MissingParameter, "EnvironmentName is required")
	}

	if req.OperatingSystem == "" {
		return nil, errors.Newf(errors.MissingParameter, "OperatingSystem is required")
	}

	environment, err := e.Backend.CreateEnvironment(
		req.EnvironmentName,
		req.InstanceSize,
		req.OperatingSystem,
		req.AMIID,
		req.MinClusterCount,
		req.UserDataTemplate)
	if err != nil {
		return nil, err
	}

	if err := e.upsertTag(models.Tag{EntityID: environment.EnvironmentID, EntityType: "environment", Key: "name", Value: req.EnvironmentName}); err != nil {
		return nil, err
	}

	if err := e.upsertTag(models.Tag{EntityID: environment.EnvironmentID, EntityType: "environment", Key: "os", Value: req.OperatingSystem}); err != nil {
		return nil, err
	}

	if err := e.populateModel(environment); err != nil {
		return environment, err
	}

	return environment, nil
}

func (e *L0EnvironmentLogic) UpdateEnvironment(environmentID string, minClusterCount int) (*models.Environment, error) {
	environment, err := e.Backend.UpdateEnvironment(environmentID, minClusterCount)
	if err != nil {
		return nil, err
	}

	if err := e.populateModel(environment); err != nil {
		return nil, err
	}

	return environment, nil
}

func (e *L0EnvironmentLogic) CreateEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error {
	if err := e.Backend.CreateEnvironmentLink(sourceEnvironmentID, destEnvironmentID); err != nil {
		return nil
	}

	if err := e.upsertTag(models.Tag{EntityID: sourceEnvironmentID, EntityType: "environment", Key: "link", Value: destEnvironmentID}); err != nil {
		return nil
	}

	if err := e.upsertTag(models.Tag{EntityID: destEnvironmentID, EntityType: "environment", Key: "link", Value: sourceEnvironmentID}); err != nil {
		return nil
	}

	return nil
}

func (e *L0EnvironmentLogic) DeleteEnvironmentLink(sourceEnvironmentID, destEnvironmentID string) error {
	if err := e.Backend.DeleteEnvironmentLink(sourceEnvironmentID, destEnvironmentID); err != nil {
		return nil
	}

	sourceTags, err := e.TagStore.SelectByQuery("environment", sourceEnvironmentID)
	if err != nil {
		return err
	}

	for _, tag := range sourceTags.WithKey("link").WithValue(destEnvironmentID) {
		if err := e.TagStore.Delete(tag.TagID); err != nil {
			return err
		}
	}

	destTags, err := e.TagStore.SelectByQuery("environment", destEnvironmentID)
	if err != nil {
		return err
	}

	for _, tag := range destTags.WithKey("link").WithValue(sourceEnvironmentID) {
		if err := e.TagStore.Delete(tag.TagID); err != nil {
			return err
		}
	}

	return nil
}

func (e *L0EnvironmentLogic) populateModel(model *models.Environment) error {
	tags, err := e.TagStore.SelectByQuery("environment", model.EnvironmentID)
	if err != nil {
		return err
	}

	if tag := tags.WithKey("name").First(); tag != nil {
		model.EnvironmentName = tag.Value
	}

	if tag := tags.WithKey("os").First(); tag != nil {
		model.OperatingSystem = tag.Value
	}

	model.Links = []string{}
	for _, tag := range tags.WithKey("link") {
		model.Links = append(model.Links, tag.Value)
	}

	return nil
}
<|endoftext|>