<commit_before>package main

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
)

// Definition represents a single metadata or configuration parameter definition
// with optional nested definitions
type Definition struct {
	desc     string
	dfl      string
	unit     string
	parent   *Definition
	children DefinitionList
}

func (def *Definition) dumpString() string {
	str := ""
	str += "    desc: " + def.desc + "\n"
	str += "    dfl:  " + def.dfl + "\n"
	str += "    unit: " + def.unit + "\n"
	return str
}

// DefinitionList contains a map of definitions
type DefinitionList map[string]*Definition

// newDefinitionListFromString parses the input string and creates a (nested) DefinitionList object
func newDefinitionListFromString(text string) DefinitionList {
	emptyLineRE := regexp.MustCompile("^[[:space:]]*$")
	depthRE := regexp.MustCompile("^( *)(.*)")
	startDefRE := regexp.MustCompile("^( *)- ([^:]+):[[:space:]]*(.*)")

	var (
		currentList  *DefinitionList
		currentItem  *Definition
		currentDepth int
	)

	topList := DefinitionList{}
	currentList = &topList

	for lineNo, line := range strings.Split(text, "\n") {
		// nesting depth == nr. of indentation spaces
		if emptyLineRE.MatchString(line) {
			continue
		}
		newDepth := len(depthRE.ReplaceAllString(line, "$1"))

		if startDefRE.MatchString(line) {
			// Start new definition
			newName := startDefRE.ReplaceAllString(line, "$2")
			newItem := &Definition{
				desc: startDefRE.ReplaceAllString(line, "$3\n"),
			}

			if newDepth == currentDepth {
				// Keep current nesting level
				if currentItem != nil {
					newItem.parent = currentItem.parent
				}

			} else if newDepth > currentDepth {
				// Dive deeper
				newItem.parent = currentItem
				currentItem.children = DefinitionList{}
				currentList = &currentItem.children

			} else if newDepth < currentDepth {
				// One up
				currentList = &currentItem.parent.parent.children
			}

			if _, found := (*currentList)[newName]; found {
				panic(fmt.Sprintf("Comment line %d: Duplicate key \"%s\" near \"%s\"\n",
					lineNo, newName, line))
			}
			(*currentList)[newName] = newItem

			currentItem = newItem
			currentDepth = newDepth

		} else if currentItem == nil {
			// Parse error
			panic(fmt.Sprintf("Comment line %d: Parse error: currentItem == nil "+
				"near \"%s\" (plain paragraphs mixed with definition list?)\n", lineNo, line))

		} else if newDepth != currentDepth {
			// Parse error
			panic(fmt.Sprintf("Comment line %d: Indentation error: expected indentation "+
				"to remain at %d, found %d near \"%s\"\n", lineNo, currentDepth, newDepth, line))

		} else {
			// Append to current definition (without indentation)
			currentItem.desc += depthRE.ReplaceAllString(line, "$2") + "\n"
		}
	}

	return topList
}

func (list *DefinitionList) set(name string, def *Definition) {
	(*list)[name] = def
}

func (list *DefinitionList) dumpString() string {
	str := ""
	for name, def := range *list {
		str += "- " + name + "\n" + def.dumpString() + "\n"
	}
	return str
}

// getRST formats the DefinitionList as ReStructuredText
func (list DefinitionList) getRST(paramFields bool, depth int) string {
	// List items in alphabetical order
	var sorted []string
	for name := range list {
		sorted = append(sorted, name)
	}
	sort.Strings(sorted)

	result := ""
	for _, name := range sorted {
		// Heading
		result +=  indentLines("**" + name + "**", 2 * depth)

		// Optional default value and unit
		if paramFields && (list[name].unit != "" || list[name].dfl != "") {
			// TODO: cleaner formatting
			result += " ("
			if list[name].dfl != "" {
				result += fmt.Sprintf("default: %s", list[name].dfl)
			}
			if list[name].dfl != "" && list[name].unit != "" {
				result += ", "
			}
			if list[name].unit != "" {
				result += fmt.Sprintf("unit: %s", list[name].unit)
			}
			result += ")"
		}
		result += "\n\n"

		// Body
		result += indentLines(docBulletsToRstBullets(list[name].desc), 2 * (depth + 1))
		//result += list[name].desc
		result += "\n\n"

		// Children
		result += list[name].children.getRST(paramFields, depth+1)
	}

	//return indentLines(result, 2 * (depth + 1))
	return result
}

// Inserts two spaces at the beginning of each line, making the string a blockquote in RST
func indentLines(source string, level int) string {
	return regexp.MustCompile("(?m:(^))").ReplaceAllString(source, strings.Repeat(" ", level))
}
<commit_msg>Fix pointer handling in RST generator<commit_after>package main

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
)

// Definition represents a single metadata or configuration parameter definition
// with optional nested definitions
type Definition struct {
	desc     string
	dfl      string
	unit     string
	parent   *Definition
	children DefinitionList
}

func (def *Definition) dumpString() string {
	str := ""
	str += "    desc: " + def.desc + "\n"
	str += "    dfl:  " + def.dfl + "\n"
	str += "    unit: " + def.unit + "\n"
	return str
}

// DefinitionList contains a map of definitions
type DefinitionList map[string]*Definition

// newDefinitionListFromString parses the input string and creates a (nested) DefinitionList object
func newDefinitionListFromString(text string) DefinitionList {
	emptyLineRE := regexp.MustCompile("^[[:space:]]*$")
	depthRE := regexp.MustCompile("^( *)(.*)")
	startDefRE := regexp.MustCompile("^( *)- ([^:]+):[[:space:]]*(.*)")

	var (
		currentList  *DefinitionList
		currentItem  *Definition
		currentDepth int
	)

	topList := DefinitionList{}
	currentList = &topList

	for lineNo, line := range strings.Split(text, "\n") {
		// nesting depth == nr. of indentation spaces
		if emptyLineRE.MatchString(line) {
			continue
		}
		newDepth := len(depthRE.ReplaceAllString(line, "$1"))

		if startDefRE.MatchString(line) {
			// Start new definition
			newName := startDefRE.ReplaceAllString(line, "$2")
			newItem := &Definition{
				desc: startDefRE.ReplaceAllString(line, "$3\n"),
			}

			if newDepth == currentDepth {
				// Keep current nesting level
				if currentItem != nil {
					newItem.parent = currentItem.parent
				}

			} else if newDepth > currentDepth {
				// Dive deeper
				newItem.parent = currentItem
				currentItem.children = DefinitionList{}
				currentList = &currentItem.children

			} else if newDepth < currentDepth {
				// One up
				if currentItem.parent.parent != nil {
					currentList = &currentItem.parent.parent.children
				} else {
					currentList = &topList
				}
			}

			if _, found := (*currentList)[newName]; found {
				panic(fmt.Sprintf("Comment line %d: Duplicate key \"%s\" near \"%s\"\n",
					lineNo, newName, line))
			}
			(*currentList)[newName] = newItem

			currentItem = newItem
			currentDepth = newDepth

		} else if currentItem == nil {
			// Parse error
			panic(fmt.Sprintf("Comment line %d: Parse error: currentItem == nil "+
				"near \"%s\" (plain paragraphs mixed with definition list?)\n", lineNo, line))

		} else if newDepth != currentDepth {
			// Parse error
			panic(fmt.Sprintf("Comment line %d: Indentation error: expected indentation "+
				"to remain at %d, found %d near \"%s\"\n", lineNo, currentDepth, newDepth, line))

		} else {
			// Append to current definition (without indentation)
			currentItem.desc += depthRE.ReplaceAllString(line, "$2") + "\n"
		}
	}

	return topList
}

func (list *DefinitionList) set(name string, def *Definition) {
	(*list)[name] = def
}

func (list *DefinitionList) dumpString() string {
	str := ""
	for name, def := range *list {
		str += "- " + name + "\n" + def.dumpString() + "\n"
	}
	return str
}

// getRST formats the DefinitionList as ReStructuredText
func (list DefinitionList) getRST(paramFields bool, depth int) string {
	// List items in alphabetical order
	var sorted []string
	for name := range list {
		sorted = append(sorted, name)
	}
	sort.Strings(sorted)

	result := ""
	for _, name := range sorted {
		// Heading
		result += indentLines("**"+name+"**", 2*depth)

		// Optional default value and unit
		if paramFields && (list[name].unit != "" || list[name].dfl != "") {
			// TODO: cleaner formatting
			result += " ("
			if list[name].dfl != "" {
				result += fmt.Sprintf("default: %s", list[name].dfl)
			}
			if list[name].dfl != "" && list[name].unit != "" {
				result += ", "
			}
			if list[name].unit != "" {
				result += fmt.Sprintf("unit: %s", list[name].unit)
			}
			result += ")"
		}
		result += "\n\n"

		// Body
		result += indentLines(docBulletsToRstBullets(list[name].desc), 2*(depth+1))
		//result += list[name].desc
		result += "\n\n"

		// Children
		result += list[name].children.getRST(paramFields, depth+1)
	}

	//return indentLines(result, 2 * (depth + 1))
	return result
}

// Inserts two spaces at the beginning of each line, making the string a blockquote in RST
func indentLines(source string, level int) string {
	return regexp.MustCompile("(?m:(^))").ReplaceAllString(source, strings.Repeat(" ", level))
}
<|endoftext|>


<commit_before>// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// syz-hubtool uploads local reproducers to syz-hub.
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"path/filepath"
	"runtime"

	"github.com/google/syzkaller/pkg/db"
	"github.com/google/syzkaller/pkg/rpctype"
	"github.com/google/syzkaller/prog"
	_ "github.com/google/syzkaller/sys"
)

func main() {
	var (
		flagOS         = flag.String("os", runtime.GOOS, "target OS")
		flagArch       = flag.String("arch", runtime.GOARCH, "target Arch")
		flagHubAddress = flag.String("addr", "", "hub address")
		flagHubClient  = flag.String("client", "", "hub API client")
		flagHubKey     = flag.String("key", "", "hub API key")
		flagHubManager = flag.String("manager", "", "manager name to upload on behalf of")
		flagRepro      = flag.String("repro", "", "reproducer glob pattern to upload")
		flagCorpus     = flag.String("corpus", "", "coprpus file to upload")
		flagWorkdir    = flag.String("workdir", "", "workdir to upload coprpus and reproducers")
		flagDrain      = flag.Bool("drain", false, "drain hub corpus and reproducers for the given manager")
	)
	flag.Parse()
	target, err := prog.GetTarget(*flagOS, *flagArch)
	if err != nil {
		log.Fatal(err)
	}
	if *flagWorkdir != "" {
		*flagRepro = filepath.Join(*flagWorkdir, "crashes", "*", "repro.prog")
		*flagCorpus = filepath.Join(*flagWorkdir, "corpus.db")
	}
	var repros, corpus [][]byte
	if *flagRepro != "" {
		repros = loadRepros(target, *flagRepro)
	}
	if *flagCorpus != "" {
		corpus = loadCorpus(target, *flagCorpus)
	}
	log.Printf("loaded %v reproducers, %v corpus programs", len(repros), len(corpus))
	if len(repros)+len(corpus) == 0 {
		return
	}
	log.Printf("connecting to hub at %v...", *flagHubAddress)
	conn, err := rpctype.NewRPCClient(*flagHubAddress)
	if err != nil {
		log.Fatalf("failed to connect to hub: %v", err)
	}
	connectArgs := &rpctype.HubConnectArgs{
		Client:  *flagHubClient,
		Key:     *flagHubKey,
		Manager: *flagHubManager,
		Fresh:   false,
		Calls:   nil,
		Corpus:  corpus,
	}
	if err := conn.Call("Hub.Connect", connectArgs, nil); err != nil {
		log.Fatalf("Hub.Connect failed: %v", err)
	}
	log.Printf("uploaded %v corpus programs", len(corpus))
	if len(repros) != 0 {
		syncArgs := &rpctype.HubSyncArgs{
			Client:  *flagHubClient,
			Key:     *flagHubKey,
			Manager: *flagHubManager,
			Repros:  repros,
		}
		if err := conn.Call("Hub.Sync", syncArgs, new(rpctype.HubSyncRes)); err != nil {
			log.Fatalf("Hub.Sync failed: %v", err)
		}
		log.Printf("uploaded %v reproducers", len(repros))
	}
	for *flagDrain {
		syncArgs := &rpctype.HubSyncArgs{
			Client:     *flagHubClient,
			Key:        *flagHubKey,
			Manager:    *flagHubManager,
			NeedRepros: true,
		}
		resp := new(rpctype.HubSyncRes)
		if err := conn.Call("Hub.Sync", syncArgs, resp); err != nil {
			log.Fatalf("Hub.Sync failed: %v", err)
		}
		log.Printf("received %v progs, %v repros, %v more", len(resp.Progs), len(resp.Repros), resp.More)
		if len(resp.Progs)+len(resp.Repros) == 0 {
			break
		}
	}
}

func loadRepros(target *prog.Target, reproGlob string) [][]byte {
	files, err := filepath.Glob(reproGlob)
	if err != nil {
		log.Fatal(err)
	}
	var repros [][]byte
	dedup := make(map[string]bool)
	for _, file := range files {
		data, err := ioutil.ReadFile(file)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := target.Deserialize(data, prog.NonStrict); err != nil {
			log.Printf("failed to deserialize %v: %v", file, err)
			continue
		}
		if dedup[string(data)] {
			log.Printf("%v is a duplicate", file)
			continue
		}
		dedup[string(data)] = true
		repros = append(repros, data)
	}
	return repros
}

func loadCorpus(target *prog.Target, corpusDB string) [][]byte {
	progs, err := db.ReadCorpus(corpusDB, target)
	if err != nil {
		log.Fatal(err)
	}
	var corpus [][]byte
	for _, p := range progs {
		corpus = append(corpus, p.Serialize())
	}
	return corpus
}
<commit_msg>tools/syz-hubtool: fix up drain mode<commit_after>// Copyright 2020 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// syz-hubtool uploads local reproducers to syz-hub.
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"path/filepath"
	"runtime"

	"github.com/google/syzkaller/pkg/db"
	"github.com/google/syzkaller/pkg/rpctype"
	"github.com/google/syzkaller/prog"
	_ "github.com/google/syzkaller/sys"
)

func main() {
	var (
		flagOS         = flag.String("os", runtime.GOOS, "target OS")
		flagArch       = flag.String("arch", runtime.GOARCH, "target Arch")
		flagHubAddress = flag.String("addr", "", "hub address")
		flagHubClient  = flag.String("client", "", "hub API client")
		flagHubKey     = flag.String("key", "", "hub API key")
		flagHubManager = flag.String("manager", "", "manager name to upload on behalf of")
		flagRepro      = flag.String("repro", "", "reproducer glob pattern to upload")
		flagCorpus     = flag.String("corpus", "", "coprpus file to upload")
		flagWorkdir    = flag.String("workdir", "", "workdir to upload coprpus and reproducers")
		flagDrain      = flag.Bool("drain", false, "drain hub corpus and reproducers for the given manager")
	)
	flag.Parse()
	target, err := prog.GetTarget(*flagOS, *flagArch)
	if err != nil {
		log.Fatal(err)
	}
	if *flagWorkdir != "" {
		*flagRepro = filepath.Join(*flagWorkdir, "crashes", "*", "repro.prog")
		*flagCorpus = filepath.Join(*flagWorkdir, "corpus.db")
	}
	var repros, corpus [][]byte
	if *flagRepro != "" {
		repros = loadRepros(target, *flagRepro)
	}
	if *flagCorpus != "" {
		corpus = loadCorpus(target, *flagCorpus)
	}
	log.Printf("loaded %v reproducers, %v corpus programs", len(repros), len(corpus))
	if len(repros)+len(corpus) == 0 && !*flagDrain {
		return
	}
	log.Printf("connecting to hub at %v...", *flagHubAddress)
	conn, err := rpctype.NewRPCClient(*flagHubAddress)
	if err != nil {
		log.Fatalf("failed to connect to hub: %v", err)
	}
	connectArgs := &rpctype.HubConnectArgs{
		Client:  *flagHubClient,
		Key:     *flagHubKey,
		Manager: *flagHubManager,
		Fresh:   false,
		Calls:   nil,
		Corpus:  corpus,
	}
	if err := conn.Call("Hub.Connect", connectArgs, nil); err != nil {
		log.Fatalf("Hub.Connect failed: %v", err)
	}
	log.Printf("uploaded %v corpus programs", len(corpus))
	if len(repros) != 0 {
		syncArgs := &rpctype.HubSyncArgs{
			Client:  *flagHubClient,
			Key:     *flagHubKey,
			Manager: *flagHubManager,
			Repros:  repros,
		}
		if err := conn.Call("Hub.Sync", syncArgs, new(rpctype.HubSyncRes)); err != nil {
			log.Fatalf("Hub.Sync failed: %v", err)
		}
		log.Printf("uploaded %v reproducers", len(repros))
	}
	for *flagDrain {
		syncArgs := &rpctype.HubSyncArgs{
			Client:     *flagHubClient,
			Key:        *flagHubKey,
			Manager:    *flagHubManager,
			NeedRepros: true,
		}
		resp := new(rpctype.HubSyncRes)
		if err := conn.Call("Hub.Sync", syncArgs, resp); err != nil {
			log.Fatalf("Hub.Sync failed: %v", err)
		}
		log.Printf("received %v progs, %v repros, %v more", len(resp.Progs), len(resp.Repros), resp.More)
		if len(resp.Progs)+len(resp.Repros) == 0 {
			break
		}
	}
}

func loadRepros(target *prog.Target, reproGlob string) [][]byte {
	files, err := filepath.Glob(reproGlob)
	if err != nil {
		log.Fatal(err)
	}
	var repros [][]byte
	dedup := make(map[string]bool)
	for _, file := range files {
		data, err := ioutil.ReadFile(file)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := target.Deserialize(data, prog.NonStrict); err != nil {
			log.Printf("failed to deserialize %v: %v", file, err)
			continue
		}
		if dedup[string(data)] {
			log.Printf("%v is a duplicate", file)
			continue
		}
		dedup[string(data)] = true
		repros = append(repros, data)
	}
	return repros
}

func loadCorpus(target *prog.Target, corpusDB string) [][]byte {
	progs, err := db.ReadCorpus(corpusDB, target)
	if err != nil {
		log.Fatal(err)
	}
	var corpus [][]byte
	for _, p := range progs {
		corpus = append(corpus, p.Serialize())
	}
	return corpus
}
<|endoftext|>


<commit_before>package handler

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

	"github.com/influxdb/influxdb/client"
	"github.com/jeffbmartinez/log"

	"github.com/jeffbmartinez/timeline/storage/influxdb"
)

func Event(response http.ResponseWriter, request *http.Request) {
	handler := methodNotAllowed

	switch request.Method {
	case "GET":
		handler = getEvent
	case "POST":
		handler = postEvent
	}

	handler(response, request)
}

func getEvent(response http.ResponseWriter, request *http.Request) {
	notImplemented(response, request)
}

// func postEvent(response http.ResponseWriter, request *http.Request) {
// 	urlArgs, err := request.URL.Query()
// 	log.Infof("Event storage request with args: %v", urlArgs)

// 	REQUIRED_ARGS := []string{
// 		"series",
// 	}

// 	missingArgs := GetMissingArgs(urlArgs, REQUIRED_ARGS)

// 	if len(missingArgs) > 0 {
// 		errorMessage := fmt.Sprintf("Missing required arguments: %v", missingArgs)
// 		log.Infof(errorMessage)
// 		WriteSimpleResponse(response, errorMessage, http.StatusBadRequest)
// 		return
// 	}

// 	point := newSimplePoint(urlArgs)

// 	influxDbConfig, err := influxdb.GetConfiguration()
// 	if err != nil {
// 		log.Errorf("Problem reading influxdb configuration: %v", err)
// 		WriteSimpleResponse(response, "Unable to store event", http.StatusInternalServerError)
// 		return
// 	}

// 	const RETENTION_POLICY string = "default"

// 	err = influxdb.StorePoint(point, influxDbConfig.DbName, RETENTION_POLICY)
// 	if err != nil {
// 		log.Errorf("Problem writing to influxdb: %v", err)
// 		WriteSimpleResponse(response, "Unable to store event", http.StatusInternalServerError)
// 		return
// 	}

// 	WriteSimpleResponse(response, "event recorded", http.StatusOK)
// }

func postEvent(response http.ResponseWriter, request *http.Request) {
	urlArgs, err := parseUrlArgs(request.URL.Query())
	if err != nil {
		errorMessage := err.Error()
		log.Infof(errorMessage)
		WriteSimpleResponse(response, errorMessage, http.StatusBadRequest)
		return
	}

	log.Infof("Event storage request with args: %v", urlArgs)

	REQUIRED_ARGS := []string{
		"name",
	}

	missingArgs := GetMissingArgs(urlArgs, REQUIRED_ARGS)

	if len(missingArgs) > 0 {
		errorMessage := fmt.Sprintf("Missing required arguments: %v", missingArgs)
		log.Infof(errorMessage)
		WriteSimpleResponse(response, errorMessage, http.StatusBadRequest)
		return
	}

	tags := getTagsFromUrlArgs(urlArgs, REQUIRED_ARGS)
	event := newEvent(urlArgs["name"], tags)

	influxDbConfig, err := influxdb.GetConfiguration()
	if err != nil {
		log.Errorf("Problem reading influxdb configuration: %v", err)
		WriteSimpleResponse(response, "Unable to store event", http.StatusInternalServerError)
		return
	}

	const RETENTION_POLICY string = "default"

	err = influxdb.StorePoint(event, influxDbConfig.DbName, RETENTION_POLICY)
	if err != nil {
		log.Errorf("Problem writing to influxdb: %v", err)
		WriteSimpleResponse(response, "Unable to store event", http.StatusInternalServerError)
		return
	}

	WriteSimpleResponse(response, "event recorded", http.StatusOK)
}

// Influxdb doesn't support the storage of a measurement without fields
// attached to it. In this case, I'm just using a field of value=1
// in order to store an event. The 1 is arbitrary, except that perhaps
// it could be used to sum up the number of events that occurred within
// a timeframe.
func newEvent(name string, tags map[string]string) client.Point {
	return client.Point{
		Measurement: name,
		Tags:        tags,
		Time:        time.Now(),
		Fields: map[string]interface{}{
			"value": 1,
		},
		Precision: "s",
	}
}
<commit_msg>delete commented out code<commit_after>package handler

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

	"github.com/influxdb/influxdb/client"
	"github.com/jeffbmartinez/log"

	"github.com/jeffbmartinez/timeline/storage/influxdb"
)

func Event(response http.ResponseWriter, request *http.Request) {
	handler := methodNotAllowed

	switch request.Method {
	case "GET":
		handler = getEvent
	case "POST":
		handler = postEvent
	}

	handler(response, request)
}

func getEvent(response http.ResponseWriter, request *http.Request) {
	notImplemented(response, request)
}

func postEvent(response http.ResponseWriter, request *http.Request) {
	urlArgs, err := parseUrlArgs(request.URL.Query())
	if err != nil {
		errorMessage := err.Error()
		log.Infof(errorMessage)
		WriteSimpleResponse(response, errorMessage, http.StatusBadRequest)
		return
	}

	log.Infof("Event storage request with args: %v", urlArgs)

	REQUIRED_ARGS := []string{
		"name",
	}

	missingArgs := GetMissingArgs(urlArgs, REQUIRED_ARGS)

	if len(missingArgs) > 0 {
		errorMessage := fmt.Sprintf("Missing required arguments: %v", missingArgs)
		log.Infof(errorMessage)
		WriteSimpleResponse(response, errorMessage, http.StatusBadRequest)
		return
	}

	tags := getTagsFromUrlArgs(urlArgs, REQUIRED_ARGS)
	event := newEvent(urlArgs["name"], tags)

	influxDbConfig, err := influxdb.GetConfiguration()
	if err != nil {
		log.Errorf("Problem reading influxdb configuration: %v", err)
		WriteSimpleResponse(response, "Unable to store event", http.StatusInternalServerError)
		return
	}

	const RETENTION_POLICY string = "default"

	err = influxdb.StorePoint(event, influxDbConfig.DbName, RETENTION_POLICY)
	if err != nil {
		log.Errorf("Problem writing to influxdb: %v", err)
		WriteSimpleResponse(response, "Unable to store event", http.StatusInternalServerError)
		return
	}

	WriteSimpleResponse(response, "event recorded", http.StatusOK)
}

// Influxdb doesn't support the storage of a measurement without fields
// attached to it. In this case, I'm just using a field of value=1
// in order to store an event. The 1 is arbitrary, except that perhaps
// it could be used to sum up the number of events that occurred within
// a timeframe.
func newEvent(name string, tags map[string]string) client.Point {
	return client.Point{
		Measurement: name,
		Tags:        tags,
		Time:        time.Now(),
		Fields: map[string]interface{}{
			"value": 1,
		},
		Precision: "s",
	}
}
<|endoftext|>


<commit_before>package restserver

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
)

func TestJoin(t *testing.T) {
	var tests = []struct {
		base   string
		names  []string
		result string
	}{
		{"/", []string{"foo", "bar"}, "/foo/bar"},
		{"/srv/server", []string{"foo", "bar"}, "/srv/server/foo/bar"},
		{"/srv/server", []string{"foo", "..", "bar"}, "/srv/server/foo/bar"},
		{"/srv/server", []string{"..", "bar"}, "/srv/server/bar"},
		{"/srv/server", []string{".."}, "/srv/server"},
		{"/srv/server", []string{"..", ".."}, "/srv/server"},
		{"/srv/server", []string{"repo", "data"}, "/srv/server/repo/data"},
		{"/srv/server", []string{"repo", "data", "..", ".."}, "/srv/server/repo/data"},
		{"/srv/server", []string{"repo", "data", "..", "data", "..", "..", ".."}, "/srv/server/repo/data/data"},
	}

	for _, test := range tests {
		t.Run("", func(t *testing.T) {
			got, err := join(filepath.FromSlash(test.base), test.names...)
			if err != nil {
				t.Fatal(err)
			}

			want := filepath.FromSlash(test.result)
			if got != want {
				t.Fatalf("wrong result returned, want %v, got %v", want, got)
			}
		})
	}
}

// declare a few helper functions

// wantFunc tests the HTTP response in res and calls t.Error() if something is incorrect.
type wantFunc func(t testing.TB, res *httptest.ResponseRecorder)

// newRequest returns a new HTTP request with the given params. On error, t.Fatal is called.
func newRequest(t testing.TB, method, path string, body io.Reader) *http.Request {
	req, err := http.NewRequest(method, path, body)
	if err != nil {
		t.Fatal(err)
	}
	return req
}

// wantCode returns a function which checks that the response has the correct HTTP status code.
func wantCode(code int) wantFunc {
	return func(t testing.TB, res *httptest.ResponseRecorder) {
		if res.Code != code {
			t.Errorf("wrong response code, want %v, got %v", code, res.Code)
		}
	}
}

// wantBody returns a function which checks that the response has the data in the body.
func wantBody(body string) wantFunc {
	return func(t testing.TB, res *httptest.ResponseRecorder) {
		if res.Body == nil {
			t.Errorf("body is nil, want %q", body)
			return
		}

		if !bytes.Equal(res.Body.Bytes(), []byte(body)) {
			t.Errorf("wrong response body, want:\n  %q\ngot:\n  %q", body, res.Body.Bytes())
		}
	}
}

// checkRequest uses f to process the request and runs the checker functions on the result.
func checkRequest(t testing.TB, f http.HandlerFunc, req *http.Request, want []wantFunc) {
	rr := httptest.NewRecorder()
	f(rr, req)

	for _, fn := range want {
		fn(t, rr)
	}
}

// TestRequest is a sequence of HTTP requests with (optional) tests for the response.
type TestRequest struct {
	req  *http.Request
	want []wantFunc
}

// createOverwriteDeleteSeq returns a sequence which will create a new file at
// path, and then try to overwrite and delete it.
func createOverwriteDeleteSeq(t testing.TB, path string) []TestRequest {
	// add a file, try to overwrite and delete it
	req := []TestRequest{
		{
			req:  newRequest(t, "GET", path, nil),
			want: []wantFunc{wantCode(http.StatusNotFound)},
		},
		{
			req:  newRequest(t, "POST", path, strings.NewReader("foobar test config")),
			want: []wantFunc{wantCode(http.StatusOK)},
		},
		{
			req: newRequest(t, "GET", path, nil),
			want: []wantFunc{
				wantCode(http.StatusOK),
				wantBody("foobar test config"),
			},
		},
		{
			req:  newRequest(t, "POST", path, strings.NewReader("other config")),
			want: []wantFunc{wantCode(http.StatusForbidden)},
		},
		{
			req: newRequest(t, "GET", path, nil),
			want: []wantFunc{
				wantCode(http.StatusOK),
				wantBody("foobar test config"),
			},
		},
		{
			req:  newRequest(t, "DELETE", path, nil),
			want: []wantFunc{wantCode(http.StatusForbidden)},
		},
		{
			req: newRequest(t, "GET", path, nil),
			want: []wantFunc{
				wantCode(http.StatusOK),
				wantBody("foobar test config"),
			},
		},
	}
	return req
}

// TestResticHandler runs tests on the restic handler code, especially in append-only mode.
func TestResticHandler(t *testing.T) {
	buf := make([]byte, 32)
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		t.Fatal(err)
	}
	randomID := hex.EncodeToString(buf)

	var tests = []struct {
		seq []TestRequest
	}{
		{createOverwriteDeleteSeq(t, "/config")},
		{createOverwriteDeleteSeq(t, "/data/"+randomID)},
		{
			// ensure we can add and remove lock files
			[]TestRequest{
				{
					req:  newRequest(t, "GET", "/locks/"+randomID, nil),
					want: []wantFunc{wantCode(http.StatusNotFound)},
				},
				{
					req:  newRequest(t, "POST", "/locks/"+randomID, strings.NewReader("lock file")),
					want: []wantFunc{wantCode(http.StatusOK)},
				},
				{
					req: newRequest(t, "GET", "/locks/"+randomID, nil),
					want: []wantFunc{
						wantCode(http.StatusOK),
						wantBody("lock file"),
					},
				},
				{
					req:  newRequest(t, "POST", "/locks/"+randomID, strings.NewReader("other lock file")),
					want: []wantFunc{wantCode(http.StatusForbidden)},
				},
				{
					req:  newRequest(t, "DELETE", "/locks/"+randomID, nil),
					want: []wantFunc{wantCode(http.StatusOK)},
				},
				{
					req:  newRequest(t, "GET", "/locks/"+randomID, nil),
					want: []wantFunc{wantCode(http.StatusNotFound)},
				},
			},
		},
	}

	// setup rclone with a local backend in a temporary directory
	tempdir, err := ioutil.TempDir("", "rclone-restic-test-")
	if err != nil {
		t.Fatal(err)
	}

	// make sure the tempdir is properly removed
	defer func() {
		err := os.RemoveAll(tempdir)
		if err != nil {
			t.Fatal(err)
		}
	}()

	// set append-only mode and configure path
	mux, err := NewHandler(&Server{
		AppendOnly:   true,
		Path:         tempdir,
		NoAuth:       true,
		Debug:        true,
		PanicOnError: true,
	})
	if err != nil {
		t.Fatalf("error from NewHandler: %v", err)
	}

	// create the repo
	checkRequest(t, mux.ServeHTTP,
		newRequest(t, "POST", "/?create=true", nil),
		[]wantFunc{wantCode(http.StatusOK)})

	for _, test := range tests {
		t.Run("", func(t *testing.T) {
			for i, seq := range test.seq {
				t.Logf("request %v: %v %v", i, seq.req.Method, seq.req.URL.Path)
				checkRequest(t, mux.ServeHTTP, seq.req, seq.want)
			}
		})
	}
}

func TestSplitURLPath(t *testing.T) {
	var tests = []struct {
		// Params
		urlPath  string
		maxDepth int
		// Expected result
		folderPath []string
		remainder  string
	}{
		{"/", 0, nil, "/"},
		{"/", 2, nil, "/"},
		{"/foo/bar/locks/0123", 0, nil, "/foo/bar/locks/0123"},
		{"/foo/bar/locks/0123", 1, []string{"foo"}, "/bar/locks/0123"},
		{"/foo/bar/locks/0123", 2, []string{"foo", "bar"}, "/locks/0123"},
		{"/foo/bar/locks/0123", 3, []string{"foo", "bar"}, "/locks/0123"},
		{"/foo/bar/zzz/locks/0123", 2, []string{"foo", "bar"}, "/zzz/locks/0123"},
		{"/foo/bar/zzz/locks/0123", 3, []string{"foo", "bar", "zzz"}, "/locks/0123"},
		{"/foo/bar/locks/", 2, []string{"foo", "bar"}, "/locks/"},
		{"/foo/locks/", 2, []string{"foo"}, "/locks/"},
		{"/foo/data/", 2, []string{"foo"}, "/data/"},
		{"/foo/index/", 2, []string{"foo"}, "/index/"},
		{"/foo/keys/", 2, []string{"foo"}, "/keys/"},
		{"/foo/snapshots/", 2, []string{"foo"}, "/snapshots/"},
		{"/foo/config", 2, []string{"foo"}, "/config"},
		{"/foo/", 2, []string{"foo"}, "/"},
		{"/foo/bar/", 2, []string{"foo", "bar"}, "/"},
		{"/foo/bar", 2, []string{"foo"}, "/bar"},
		{"/locks/", 2, nil, "/locks/"},
		// This function only splits, it does not check the path components!
		{"/././locks/", 2, []string{"..", ".."}, "/locks/"},
		{"/../../locks/", 2, []string{"..", ".."}, "/locks/"},
		{"///locks/", 2, []string{"", ""}, "/locks/"},
		{"////locks/", 2, []string{"", ""}, "//locks/"},
		// Robustness against broken input
		{"/", -42, nil, "/"},
		{"foo", 2, nil, "foo"},
		{"foo/bar", 2, nil, "foo/bar"},
		{"", 2, nil, ""},
	}

	for i, test := range tests {
		t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
			folderPath, remainder := splitURLPath(test.urlPath, test.maxDepth)

			var fpEqual bool
			if len(test.folderPath) == 0 && len(folderPath) == 0 {
				fpEqual = true // this check allows for nil vs empty slice
			} else {
				fpEqual = reflect.DeepEqual(test.folderPath, folderPath)
			}
			if !fpEqual {
				t.Errorf("wrong folderPath: want %v, got %v", test.folderPath, folderPath)
			}

			if test.remainder != remainder {
				t.Errorf("wrong remainder: want %v, got %v", test.remainder, remainder)
			}
		})
	}
}
<commit_msg>Fix TestSplitURLPath<commit_after>package restserver

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
)

func TestJoin(t *testing.T) {
	var tests = []struct {
		base   string
		names  []string
		result string
	}{
		{"/", []string{"foo", "bar"}, "/foo/bar"},
		{"/srv/server", []string{"foo", "bar"}, "/srv/server/foo/bar"},
		{"/srv/server", []string{"foo", "..", "bar"}, "/srv/server/foo/bar"},
		{"/srv/server", []string{"..", "bar"}, "/srv/server/bar"},
		{"/srv/server", []string{".."}, "/srv/server"},
		{"/srv/server", []string{"..", ".."}, "/srv/server"},
		{"/srv/server", []string{"repo", "data"}, "/srv/server/repo/data"},
		{"/srv/server", []string{"repo", "data", "..", ".."}, "/srv/server/repo/data"},
		{"/srv/server", []string{"repo", "data", "..", "data", "..", "..", ".."}, "/srv/server/repo/data/data"},
	}

	for _, test := range tests {
		t.Run("", func(t *testing.T) {
			got, err := join(filepath.FromSlash(test.base), test.names...)
			if err != nil {
				t.Fatal(err)
			}

			want := filepath.FromSlash(test.result)
			if got != want {
				t.Fatalf("wrong result returned, want %v, got %v", want, got)
			}
		})
	}
}

// declare a few helper functions

// wantFunc tests the HTTP response in res and calls t.Error() if something is incorrect.
type wantFunc func(t testing.TB, res *httptest.ResponseRecorder)

// newRequest returns a new HTTP request with the given params. On error, t.Fatal is called.
func newRequest(t testing.TB, method, path string, body io.Reader) *http.Request {
	req, err := http.NewRequest(method, path, body)
	if err != nil {
		t.Fatal(err)
	}
	return req
}

// wantCode returns a function which checks that the response has the correct HTTP status code.
func wantCode(code int) wantFunc {
	return func(t testing.TB, res *httptest.ResponseRecorder) {
		if res.Code != code {
			t.Errorf("wrong response code, want %v, got %v", code, res.Code)
		}
	}
}

// wantBody returns a function which checks that the response has the data in the body.
func wantBody(body string) wantFunc {
	return func(t testing.TB, res *httptest.ResponseRecorder) {
		if res.Body == nil {
			t.Errorf("body is nil, want %q", body)
			return
		}

		if !bytes.Equal(res.Body.Bytes(), []byte(body)) {
			t.Errorf("wrong response body, want:\n  %q\ngot:\n  %q", body, res.Body.Bytes())
		}
	}
}

// checkRequest uses f to process the request and runs the checker functions on the result.
func checkRequest(t testing.TB, f http.HandlerFunc, req *http.Request, want []wantFunc) {
	rr := httptest.NewRecorder()
	f(rr, req)

	for _, fn := range want {
		fn(t, rr)
	}
}

// TestRequest is a sequence of HTTP requests with (optional) tests for the response.
type TestRequest struct {
	req  *http.Request
	want []wantFunc
}

// createOverwriteDeleteSeq returns a sequence which will create a new file at
// path, and then try to overwrite and delete it.
func createOverwriteDeleteSeq(t testing.TB, path string) []TestRequest {
	// add a file, try to overwrite and delete it
	req := []TestRequest{
		{
			req:  newRequest(t, "GET", path, nil),
			want: []wantFunc{wantCode(http.StatusNotFound)},
		},
		{
			req:  newRequest(t, "POST", path, strings.NewReader("foobar test config")),
			want: []wantFunc{wantCode(http.StatusOK)},
		},
		{
			req: newRequest(t, "GET", path, nil),
			want: []wantFunc{
				wantCode(http.StatusOK),
				wantBody("foobar test config"),
			},
		},
		{
			req:  newRequest(t, "POST", path, strings.NewReader("other config")),
			want: []wantFunc{wantCode(http.StatusForbidden)},
		},
		{
			req: newRequest(t, "GET", path, nil),
			want: []wantFunc{
				wantCode(http.StatusOK),
				wantBody("foobar test config"),
			},
		},
		{
			req:  newRequest(t, "DELETE", path, nil),
			want: []wantFunc{wantCode(http.StatusForbidden)},
		},
		{
			req: newRequest(t, "GET", path, nil),
			want: []wantFunc{
				wantCode(http.StatusOK),
				wantBody("foobar test config"),
			},
		},
	}
	return req
}

// TestResticHandler runs tests on the restic handler code, especially in append-only mode.
func TestResticHandler(t *testing.T) {
	buf := make([]byte, 32)
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		t.Fatal(err)
	}
	randomID := hex.EncodeToString(buf)

	var tests = []struct {
		seq []TestRequest
	}{
		{createOverwriteDeleteSeq(t, "/config")},
		{createOverwriteDeleteSeq(t, "/data/"+randomID)},
		{
			// ensure we can add and remove lock files
			[]TestRequest{
				{
					req:  newRequest(t, "GET", "/locks/"+randomID, nil),
					want: []wantFunc{wantCode(http.StatusNotFound)},
				},
				{
					req:  newRequest(t, "POST", "/locks/"+randomID, strings.NewReader("lock file")),
					want: []wantFunc{wantCode(http.StatusOK)},
				},
				{
					req: newRequest(t, "GET", "/locks/"+randomID, nil),
					want: []wantFunc{
						wantCode(http.StatusOK),
						wantBody("lock file"),
					},
				},
				{
					req:  newRequest(t, "POST", "/locks/"+randomID, strings.NewReader("other lock file")),
					want: []wantFunc{wantCode(http.StatusForbidden)},
				},
				{
					req:  newRequest(t, "DELETE", "/locks/"+randomID, nil),
					want: []wantFunc{wantCode(http.StatusOK)},
				},
				{
					req:  newRequest(t, "GET", "/locks/"+randomID, nil),
					want: []wantFunc{wantCode(http.StatusNotFound)},
				},
			},
		},
	}

	// setup rclone with a local backend in a temporary directory
	tempdir, err := ioutil.TempDir("", "rclone-restic-test-")
	if err != nil {
		t.Fatal(err)
	}

	// make sure the tempdir is properly removed
	defer func() {
		err := os.RemoveAll(tempdir)
		if err != nil {
			t.Fatal(err)
		}
	}()

	// set append-only mode and configure path
	mux, err := NewHandler(&Server{
		AppendOnly:   true,
		Path:         tempdir,
		NoAuth:       true,
		Debug:        true,
		PanicOnError: true,
	})
	if err != nil {
		t.Fatalf("error from NewHandler: %v", err)
	}

	// create the repo
	checkRequest(t, mux.ServeHTTP,
		newRequest(t, "POST", "/?create=true", nil),
		[]wantFunc{wantCode(http.StatusOK)})

	for _, test := range tests {
		t.Run("", func(t *testing.T) {
			for i, seq := range test.seq {
				t.Logf("request %v: %v %v", i, seq.req.Method, seq.req.URL.Path)
				checkRequest(t, mux.ServeHTTP, seq.req, seq.want)
			}
		})
	}
}

func TestSplitURLPath(t *testing.T) {
	var tests = []struct {
		// Params
		urlPath  string
		maxDepth int
		// Expected result
		folderPath []string
		remainder  string
	}{
		{"/", 0, nil, "/"},
		{"/", 2, nil, "/"},
		{"/foo/bar/locks/0123", 0, nil, "/foo/bar/locks/0123"},
		{"/foo/bar/locks/0123", 1, []string{"foo"}, "/bar/locks/0123"},
		{"/foo/bar/locks/0123", 2, []string{"foo", "bar"}, "/locks/0123"},
		{"/foo/bar/locks/0123", 3, []string{"foo", "bar"}, "/locks/0123"},
		{"/foo/bar/zzz/locks/0123", 2, []string{"foo", "bar"}, "/zzz/locks/0123"},
		{"/foo/bar/zzz/locks/0123", 3, []string{"foo", "bar", "zzz"}, "/locks/0123"},
		{"/foo/bar/locks/", 2, []string{"foo", "bar"}, "/locks/"},
		{"/foo/locks/", 2, []string{"foo"}, "/locks/"},
		{"/foo/data/", 2, []string{"foo"}, "/data/"},
		{"/foo/index/", 2, []string{"foo"}, "/index/"},
		{"/foo/keys/", 2, []string{"foo"}, "/keys/"},
		{"/foo/snapshots/", 2, []string{"foo"}, "/snapshots/"},
		{"/foo/config", 2, []string{"foo"}, "/config"},
		{"/foo/", 2, []string{"foo"}, "/"},
		{"/foo/bar/", 2, []string{"foo", "bar"}, "/"},
		{"/foo/bar", 2, []string{"foo"}, "/bar"},
		{"/locks/", 2, nil, "/locks/"},
		// This function only splits, it does not check the path components!
		{"/././locks/", 2, []string{".", "."}, "/locks/"},
		{"/../../locks/", 2, []string{"..", ".."}, "/locks/"},
		{"///locks/", 2, []string{"", ""}, "/locks/"},
		{"////locks/", 2, []string{"", ""}, "//locks/"},
		// Robustness against broken input
		{"/", -42, nil, "/"},
		{"foo", 2, nil, "foo"},
		{"foo/bar", 2, nil, "foo/bar"},
		{"", 2, nil, ""},
	}

	for i, test := range tests {
		t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
			folderPath, remainder := splitURLPath(test.urlPath, test.maxDepth)

			var fpEqual bool
			if len(test.folderPath) == 0 && len(folderPath) == 0 {
				fpEqual = true // this check allows for nil vs empty slice
			} else {
				fpEqual = reflect.DeepEqual(test.folderPath, folderPath)
			}
			if !fpEqual {
				t.Errorf("wrong folderPath: want %v, got %v", test.folderPath, folderPath)
			}

			if test.remainder != remainder {
				t.Errorf("wrong remainder: want %v, got %v", test.remainder, remainder)
			}
		})
	}
}
<|endoftext|>


<commit_before>package dht

import (
	"bytes"
	"testing"

	proto "github.com/gogo/protobuf/proto"
	recpb "github.com/libp2p/go-libp2p-record/pb"
)

func TestCleanRecordSigned(t *testing.T) {
	actual := new(recpb.Record)
	actual.TimeReceived = "time"
	actual.Value = []byte("value")
	actual.Key = []byte("key")

	cleanRecord(actual)
	actualBytes, err := proto.Marshal(actual)
	if err != nil {
		t.Fatal(err)
	}

	expected := new(recpb.Record)
	expected.Value = []byte("value")
	expected.Key = []byte("key")
	expectedBytes, err := proto.Marshal(expected)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(actualBytes, expectedBytes) {
		t.Error("failed to clean record")
	}
}

func TestCleanRecord(t *testing.T) {
	actual := new(recpb.Record)
	actual.TimeReceived = "time"
	actual.Key = []byte("key")
	actual.Value = []byte("value")

	cleanRecord(actual)
	actualBytes, err := proto.Marshal(actual)
	if err != nil {
		t.Fatal(err)
	}

	expected := new(recpb.Record)
	expected.Key = []byte("key")
	expected.Value = []byte("value")
	expectedBytes, err := proto.Marshal(expected)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(actualBytes, expectedBytes) {
		t.Error("failed to clean record")
	}
}
<commit_msg>add benchmark for handleFindPeer<commit_after>package dht

import (
	"bytes"
	"context"
	"fmt"
	"math/rand"
	"testing"
	"time"

	proto "github.com/gogo/protobuf/proto"
	"github.com/libp2p/go-libp2p"
	crypto "github.com/libp2p/go-libp2p-core/crypto"
	peer "github.com/libp2p/go-libp2p-core/peer"
	pb "github.com/libp2p/go-libp2p-kad-dht/pb"
	recpb "github.com/libp2p/go-libp2p-record/pb"
	ma "github.com/multiformats/go-multiaddr"
)

func TestCleanRecordSigned(t *testing.T) {
	actual := new(recpb.Record)
	actual.TimeReceived = "time"
	actual.Value = []byte("value")
	actual.Key = []byte("key")

	cleanRecord(actual)
	actualBytes, err := proto.Marshal(actual)
	if err != nil {
		t.Fatal(err)
	}

	expected := new(recpb.Record)
	expected.Value = []byte("value")
	expected.Key = []byte("key")
	expectedBytes, err := proto.Marshal(expected)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(actualBytes, expectedBytes) {
		t.Error("failed to clean record")
	}
}

func TestCleanRecord(t *testing.T) {
	actual := new(recpb.Record)
	actual.TimeReceived = "time"
	actual.Key = []byte("key")
	actual.Value = []byte("value")

	cleanRecord(actual)
	actualBytes, err := proto.Marshal(actual)
	if err != nil {
		t.Fatal(err)
	}

	expected := new(recpb.Record)
	expected.Key = []byte("key")
	expected.Value = []byte("value")
	expectedBytes, err := proto.Marshal(expected)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(actualBytes, expectedBytes) {
		t.Error("failed to clean record")
	}
}

func BenchmarkHandleFindPeer(b *testing.B) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	h, err := libp2p.New(ctx)
	if err != nil {
		b.Fatal(err)
	}

	d, err := New(ctx, h)
	if err != nil {
		b.Fatal(err)
	}

	rng := rand.New(rand.NewSource(150))
	var peers []peer.ID
	for i := 0; i < 1000; i++ {
		_, pubk, _ := crypto.GenerateEd25519Key(rng)
		id, err := peer.IDFromPublicKey(pubk)
		if err != nil {
			panic(err)
		}

		d.routingTable.Update(id)

		peers = append(peers, id)
		a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 2000+i))
		if err != nil {
			panic(err)
		}

		d.host.Peerstore().AddAddr(id, a, time.Minute*50)
	}

	var reqs []*pb.Message
	for i := 0; i < b.N; i++ {
		reqs = append(reqs, &pb.Message{
			Key: []byte("asdasdasd"),
		})
	}
	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		d.handleFindPeer(ctx, peers[0], reqs[i])
	}

}
<|endoftext|>


<commit_before>package hcl

import (
	"github.com/zclconf/go-cty/cty"
)

// File is the top-level node that results from parsing a HCL file.
type File struct {
	Body  Body
	Bytes []byte

	// Nav is used to integrate with the "hcled" editor integration package,
	// and with diagnostic information formatters. It is not for direct use
	// by a calling application.
	Nav interface{}
}

// Block represents a nested block within a Body.
type Block struct {
	Type   string
	Labels []string
	Body   Body

	DefRange    Range   // Range that can be considered the "definition" for seeking in an editor
	TypeRange   Range   // Range for the block type declaration specifically.
	LabelRanges []Range // Ranges for the label values specifically.
}

// Blocks is a sequence of Block.
type Blocks []*Block

// Attributes is a set of attributes keyed by their names.
type Attributes map[string]*Attribute

// Body is a container for attributes and blocks. It serves as the primary
// unit of heirarchical structure within configuration.
//
// The content of a body cannot be meaningfully intepreted without a schema,
// so Body represents the raw body content and has methods that allow the
// content to be extracted in terms of a given schema.
type Body interface {
	// Content verifies that the entire body content conforms to the given
	// schema and then returns it, and/or returns diagnostics. The returned
	// body content is valid if non-nil, regardless of whether Diagnostics
	// are provided, but diagnostics should still be eventually shown to
	// the user.
	Content(schema *BodySchema) (*BodyContent, Diagnostics)

	// PartialContent is like Content except that it permits the configuration
	// to contain additional blocks or attributes not specified in the
	// schema. If any are present, the returned Body is non-nil and contains
	// the remaining items from the body that were not selected by the schema.
	PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics)

	// JustAttributes attempts to interpret all of the contents of the body
	// as attributes, allowing for the contents to be accessed without a priori
	// knowledge of the structure.
	//
	// The behavior of this method depends on the body's source language.
	// Some languages, like JSON, can't distinguish between attributes and
	// blocks without schema hints, but for languages that _can_ error
	// diagnostics will be generated if any blocks are present in the body.
	//
	// Diagnostics may be produced for other reasons too, such as duplicate
	// declarations of the same attribute.
	JustAttributes() (Attributes, Diagnostics)

	// MissingItemRange returns a range that represents where a missing item
	// might hypothetically be inserted. This is used when producing
	// diagnostics about missing required attributes or blocks. Not all bodies
	// will have an obvious single insertion point, so the result here may
	// be rather arbitrary.
	MissingItemRange() Range
}

// BodyContent is the result of applying a BodySchema to a Body.
type BodyContent struct {
	Attributes Attributes
	Blocks     Blocks

	MissingItemRange Range
}

// Attribute represents an attribute from within a body.
type Attribute struct {
	Name string
	Expr Expression

	Range     Range
	NameRange Range
}

// Expression is a literal value or an expression provided in the
// configuration, which can be evaluated within a scope to produce a value.
type Expression interface {
	// Value returns the value resulting from evaluating the expression
	// in the given evaluation context.
	//
	// The context may be nil, in which case the expression may contain
	// only constants and diagnostics will be produced for any non-constant
	// sub-expressions. (The exact definition of this depends on the source
	// language.)
	//
	// The context may instead be set but have either its Variables or
	// Functions maps set to nil, in which case only use of these features
	// will return diagnostics.
	//
	// Different diagnostics are provided depending on whether the given
	// context maps are nil or empty. In the former case, the message
	// tells the user that variables/functions are not permitted at all,
	// while in the latter case usage will produce a "not found" error for
	// the specific symbol in question.
	Value(ctx *EvalContext) (cty.Value, Diagnostics)

	// Variables returns a list of variables referenced in the receiving
	// expression. These are expressed as absolute Traversals, so may include
	// additional information about how the variable is used, such as
	// attribute lookups, which the calling application can potentially use
	// to only selectively populate the scope.
	Variables() []Traversal

	Range() Range
	StartRange() Range
}

// OfType filters the receiving block sequence by block type name,
// returning a new block sequence including only the blocks of the
// requested type.
func (els Blocks) OfType(typeName string) Blocks {
	ret := make(Blocks, 0)
	for _, el := range els {
		if el.Type == typeName {
			ret = append(ret, el)
		}
	}
	return ret
}

// ByType transforms the receiving block sequence into a map from type
// name to block sequences of only that type.
func (els Blocks) ByType() map[string]Blocks {
	ret := make(map[string]Blocks)
	for _, el := range els {
		ty := el.Type
		if ret[ty] == nil {
			ret[ty] = make(Blocks, 0, 1)
		}
		ret[ty] = append(ret[ty], el)
	}
	return ret
}
<commit_msg>hcl: fix minor typos in docs (#71)<commit_after>package hcl

import (
	"github.com/zclconf/go-cty/cty"
)

// File is the top-level node that results from parsing a HCL file.
type File struct {
	Body  Body
	Bytes []byte

	// Nav is used to integrate with the "hcled" editor integration package,
	// and with diagnostic information formatters. It is not for direct use
	// by a calling application.
	Nav interface{}
}

// Block represents a nested block within a Body.
type Block struct {
	Type   string
	Labels []string
	Body   Body

	DefRange    Range   // Range that can be considered the "definition" for seeking in an editor
	TypeRange   Range   // Range for the block type declaration specifically.
	LabelRanges []Range // Ranges for the label values specifically.
}

// Blocks is a sequence of Block.
type Blocks []*Block

// Attributes is a set of attributes keyed by their names.
type Attributes map[string]*Attribute

// Body is a container for attributes and blocks. It serves as the primary
// unit of hierarchical structure within configuration.
//
// The content of a body cannot be meaningfully interpreted without a schema,
// so Body represents the raw body content and has methods that allow the
// content to be extracted in terms of a given schema.
type Body interface {
	// Content verifies that the entire body content conforms to the given
	// schema and then returns it, and/or returns diagnostics. The returned
	// body content is valid if non-nil, regardless of whether Diagnostics
	// are provided, but diagnostics should still be eventually shown to
	// the user.
	Content(schema *BodySchema) (*BodyContent, Diagnostics)

	// PartialContent is like Content except that it permits the configuration
	// to contain additional blocks or attributes not specified in the
	// schema. If any are present, the returned Body is non-nil and contains
	// the remaining items from the body that were not selected by the schema.
	PartialContent(schema *BodySchema) (*BodyContent, Body, Diagnostics)

	// JustAttributes attempts to interpret all of the contents of the body
	// as attributes, allowing for the contents to be accessed without a priori
	// knowledge of the structure.
	//
	// The behavior of this method depends on the body's source language.
	// Some languages, like JSON, can't distinguish between attributes and
	// blocks without schema hints, but for languages that _can_ error
	// diagnostics will be generated if any blocks are present in the body.
	//
	// Diagnostics may be produced for other reasons too, such as duplicate
	// declarations of the same attribute.
	JustAttributes() (Attributes, Diagnostics)

	// MissingItemRange returns a range that represents where a missing item
	// might hypothetically be inserted. This is used when producing
	// diagnostics about missing required attributes or blocks. Not all bodies
	// will have an obvious single insertion point, so the result here may
	// be rather arbitrary.
	MissingItemRange() Range
}

// BodyContent is the result of applying a BodySchema to a Body.
type BodyContent struct {
	Attributes Attributes
	Blocks     Blocks

	MissingItemRange Range
}

// Attribute represents an attribute from within a body.
type Attribute struct {
	Name string
	Expr Expression

	Range     Range
	NameRange Range
}

// Expression is a literal value or an expression provided in the
// configuration, which can be evaluated within a scope to produce a value.
type Expression interface {
	// Value returns the value resulting from evaluating the expression
	// in the given evaluation context.
	//
	// The context may be nil, in which case the expression may contain
	// only constants and diagnostics will be produced for any non-constant
	// sub-expressions. (The exact definition of this depends on the source
	// language.)
	//
	// The context may instead be set but have either its Variables or
	// Functions maps set to nil, in which case only use of these features
	// will return diagnostics.
	//
	// Different diagnostics are provided depending on whether the given
	// context maps are nil or empty. In the former case, the message
	// tells the user that variables/functions are not permitted at all,
	// while in the latter case usage will produce a "not found" error for
	// the specific symbol in question.
	Value(ctx *EvalContext) (cty.Value, Diagnostics)

	// Variables returns a list of variables referenced in the receiving
	// expression. These are expressed as absolute Traversals, so may include
	// additional information about how the variable is used, such as
	// attribute lookups, which the calling application can potentially use
	// to only selectively populate the scope.
	Variables() []Traversal

	Range() Range
	StartRange() Range
}

// OfType filters the receiving block sequence by block type name,
// returning a new block sequence including only the blocks of the
// requested type.
func (els Blocks) OfType(typeName string) Blocks {
	ret := make(Blocks, 0)
	for _, el := range els {
		if el.Type == typeName {
			ret = append(ret, el)
		}
	}
	return ret
}

// ByType transforms the receiving block sequence into a map from type
// name to block sequences of only that type.
func (els Blocks) ByType() map[string]Blocks {
	ret := make(map[string]Blocks)
	for _, el := range els {
		ty := el.Type
		if ret[ty] == nil {
			ret[ty] = make(Blocks, 0, 1)
		}
		ret[ty] = append(ret[ty], el)
	}
	return ret
}
<|endoftext|>


<commit_before>package health

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

	"github.com/distribution/distribution/v3/context"
	"github.com/distribution/distribution/v3/registry/api/errcode"
)

// A Registry is a collection of checks. Most applications will use the global
// registry defined in DefaultRegistry. However, unit tests may need to create
// separate registries to isolate themselves from other tests.
type Registry struct {
	mu               sync.RWMutex
	registeredChecks map[string]Checker
}

// NewRegistry creates a new registry. This isn't necessary for normal use of
// the package, but may be useful for unit tests so individual tests have their
// own set of checks.
func NewRegistry() *Registry {
	return &Registry{
		registeredChecks: make(map[string]Checker),
	}
}

// DefaultRegistry is the default registry where checks are registered. It is
// the registry used by the HTTP handler.
var DefaultRegistry *Registry

// Checker is the interface for a Health Checker
type Checker interface {
	// Check returns nil if the service is okay.
	Check() error
}

// CheckFunc is a convenience type to create functions that implement
// the Checker interface
type CheckFunc func() error

// Check Implements the Checker interface to allow for any func() error method
// to be passed as a Checker
func (cf CheckFunc) Check() error {
	return cf()
}

// Updater implements a health check that is explicitly set.
type Updater interface {
	Checker

	// Update updates the current status of the health check.
	Update(status error)
}

// updater implements Checker and Updater, providing an asynchronous Update
// method.
// This allows us to have a Checker that returns the Check() call immediately
// not blocking on a potentially expensive check.
type updater struct {
	mu     sync.Mutex
	status error
}

// Check implements the Checker interface
func (u *updater) Check() error {
	u.mu.Lock()
	defer u.mu.Unlock()

	return u.status
}

// Update implements the Updater interface, allowing asynchronous access to
// the status of a Checker.
func (u *updater) Update(status error) {
	u.mu.Lock()
	defer u.mu.Unlock()

	u.status = status
}

// NewStatusUpdater returns a new updater
func NewStatusUpdater() Updater {
	return &updater{}
}

// thresholdUpdater implements Checker and Updater, providing an asynchronous Update
// method.
// This allows us to have a Checker that returns the Check() call immediately
// not blocking on a potentially expensive check.
type thresholdUpdater struct {
	mu        sync.Mutex
	status    error
	threshold int
	count     int
}

// Check implements the Checker interface
func (tu *thresholdUpdater) Check() error {
	tu.mu.Lock()
	defer tu.mu.Unlock()

	if tu.count >= tu.threshold {
		return tu.status
	}

	return nil
}

// thresholdUpdater implements the Updater interface, allowing asynchronous
// access to the status of a Checker.
func (tu *thresholdUpdater) Update(status error) {
	tu.mu.Lock()
	defer tu.mu.Unlock()

	if status == nil {
		tu.count = 0
	} else if tu.count < tu.threshold {
		tu.count++
	}

	tu.status = status
}

// NewThresholdStatusUpdater returns a new thresholdUpdater
func NewThresholdStatusUpdater(t int) Updater {
	return &thresholdUpdater{threshold: t}
}

// PeriodicChecker wraps an updater to provide a periodic checker
func PeriodicChecker(check Checker, period time.Duration) Checker {
	u := NewStatusUpdater()
	go func() {
		t := time.NewTicker(period)
		for {
			<-t.C
			u.Update(check.Check())
		}
	}()

	return u
}

// PeriodicThresholdChecker wraps an updater to provide a periodic checker that
// uses a threshold before it changes status
func PeriodicThresholdChecker(check Checker, period time.Duration, threshold int) Checker {
	tu := NewThresholdStatusUpdater(threshold)
	go func() {
		t := time.NewTicker(period)
		for {
			<-t.C
			tu.Update(check.Check())
		}
	}()

	return tu
}

// CheckStatus returns a map with all the current health check errors
func (registry *Registry) CheckStatus() map[string]string { // TODO(stevvooe) this needs a proper type
	registry.mu.RLock()
	defer registry.mu.RUnlock()
	statusKeys := make(map[string]string)
	for k, v := range registry.registeredChecks {
		err := v.Check()
		if err != nil {
			statusKeys[k] = err.Error()
		}
	}

	return statusKeys
}

// CheckStatus returns a map with all the current health check errors from the
// default registry.
func CheckStatus() map[string]string {
	return DefaultRegistry.CheckStatus()
}

// Register associates the checker with the provided name.
func (registry *Registry) Register(name string, check Checker) {
	if registry == nil {
		registry = DefaultRegistry
	}
	registry.mu.Lock()
	defer registry.mu.Unlock()
	_, ok := registry.registeredChecks[name]
	if ok {
		panic("Check already exists: " + name)
	}
	registry.registeredChecks[name] = check
}

// Register associates the checker with the provided name in the default
// registry.
func Register(name string, check Checker) {
	DefaultRegistry.Register(name, check)
}

// RegisterFunc allows the convenience of registering a checker directly from
// an arbitrary func() error.
func (registry *Registry) RegisterFunc(name string, check func() error) {
	registry.Register(name, CheckFunc(check))
}

// RegisterFunc allows the convenience of registering a checker in the default
// registry directly from an arbitrary func() error.
func RegisterFunc(name string, check func() error) {
	DefaultRegistry.RegisterFunc(name, check)
}

// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
// from an arbitrary func() error.
func (registry *Registry) RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
	registry.Register(name, PeriodicChecker(check, period))
}

// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
// in the default registry from an arbitrary func() error.
func RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
	DefaultRegistry.RegisterPeriodicFunc(name, period, check)
}

// RegisterPeriodicThresholdFunc allows the convenience of registering a
// PeriodicChecker from an arbitrary func() error.
func (registry *Registry) RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
	registry.Register(name, PeriodicThresholdChecker(check, period, threshold))
}

// RegisterPeriodicThresholdFunc allows the convenience of registering a
// PeriodicChecker in the default registry from an arbitrary func() error.
func RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
	DefaultRegistry.RegisterPeriodicThresholdFunc(name, period, threshold, check)
}

// StatusHandler returns a JSON blob with all the currently registered Health Checks
// and their corresponding status.
// Returns 503 if any Error status exists, 200 otherwise
func StatusHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		checks := CheckStatus()
		status := http.StatusOK

		// If there is an error, return 503
		if len(checks) != 0 {
			status = http.StatusServiceUnavailable
		}

		statusResponse(w, r, status, checks)
	} else {
		http.NotFound(w, r)
	}
}

// Handler returns a handler that will return 503 response code if the health
// checks have failed. If everything is okay with the health checks, the
// handler will pass through to the provided handler. Use this handler to
// disable a web application when the health checks fail.
func Handler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		checks := CheckStatus()
		if len(checks) != 0 {
			errcode.ServeJSON(w, errcode.ErrorCodeUnavailable.
				WithDetail("health check failed: please see /debug/health"))
			return
		}

		handler.ServeHTTP(w, r) // pass through
	})
}

// statusResponse completes the request with a response describing the health
// of the service.
func statusResponse(w http.ResponseWriter, r *http.Request, status int, checks map[string]string) {
	p, err := json.Marshal(checks)
	if err != nil {
		context.GetLogger(context.Background()).Errorf("error serializing health status: %v", err)
		p, err = json.Marshal(struct {
			ServerError string `json:"server_error"`
		}{
			ServerError: "Could not parse error message",
		})
		status = http.StatusInternalServerError

		if err != nil {
			context.GetLogger(context.Background()).Errorf("error serializing health status failure message: %v", err)
			return
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Content-Length", fmt.Sprint(len(p)))
	w.WriteHeader(status)
	if _, err := w.Write(p); err != nil {
		context.GetLogger(context.Background()).Errorf("error writing health status response body: %v", err)
	}
}

// Registers global /debug/health api endpoint, creates default registry
func init() {
	DefaultRegistry = NewRegistry()
	http.HandleFunc("/debug/health", StatusHandler)
}
<commit_msg>Release ticker resources<commit_after>package health

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

	"github.com/distribution/distribution/v3/context"
	"github.com/distribution/distribution/v3/registry/api/errcode"
)

// A Registry is a collection of checks. Most applications will use the global
// registry defined in DefaultRegistry. However, unit tests may need to create
// separate registries to isolate themselves from other tests.
type Registry struct {
	mu               sync.RWMutex
	registeredChecks map[string]Checker
}

// NewRegistry creates a new registry. This isn't necessary for normal use of
// the package, but may be useful for unit tests so individual tests have their
// own set of checks.
func NewRegistry() *Registry {
	return &Registry{
		registeredChecks: make(map[string]Checker),
	}
}

// DefaultRegistry is the default registry where checks are registered. It is
// the registry used by the HTTP handler.
var DefaultRegistry *Registry

// Checker is the interface for a Health Checker
type Checker interface {
	// Check returns nil if the service is okay.
	Check() error
}

// CheckFunc is a convenience type to create functions that implement
// the Checker interface
type CheckFunc func() error

// Check Implements the Checker interface to allow for any func() error method
// to be passed as a Checker
func (cf CheckFunc) Check() error {
	return cf()
}

// Updater implements a health check that is explicitly set.
type Updater interface {
	Checker

	// Update updates the current status of the health check.
	Update(status error)
}

// updater implements Checker and Updater, providing an asynchronous Update
// method.
// This allows us to have a Checker that returns the Check() call immediately
// not blocking on a potentially expensive check.
type updater struct {
	mu     sync.Mutex
	status error
}

// Check implements the Checker interface
func (u *updater) Check() error {
	u.mu.Lock()
	defer u.mu.Unlock()

	return u.status
}

// Update implements the Updater interface, allowing asynchronous access to
// the status of a Checker.
func (u *updater) Update(status error) {
	u.mu.Lock()
	defer u.mu.Unlock()

	u.status = status
}

// NewStatusUpdater returns a new updater
func NewStatusUpdater() Updater {
	return &updater{}
}

// thresholdUpdater implements Checker and Updater, providing an asynchronous Update
// method.
// This allows us to have a Checker that returns the Check() call immediately
// not blocking on a potentially expensive check.
type thresholdUpdater struct {
	mu        sync.Mutex
	status    error
	threshold int
	count     int
}

// Check implements the Checker interface
func (tu *thresholdUpdater) Check() error {
	tu.mu.Lock()
	defer tu.mu.Unlock()

	if tu.count >= tu.threshold {
		return tu.status
	}

	return nil
}

// thresholdUpdater implements the Updater interface, allowing asynchronous
// access to the status of a Checker.
func (tu *thresholdUpdater) Update(status error) {
	tu.mu.Lock()
	defer tu.mu.Unlock()

	if status == nil {
		tu.count = 0
	} else if tu.count < tu.threshold {
		tu.count++
	}

	tu.status = status
}

// NewThresholdStatusUpdater returns a new thresholdUpdater
func NewThresholdStatusUpdater(t int) Updater {
	return &thresholdUpdater{threshold: t}
}

// PeriodicChecker wraps an updater to provide a periodic checker
func PeriodicChecker(check Checker, period time.Duration) Checker {
	u := NewStatusUpdater()
	go func() {
		t := time.NewTicker(period)
		defer t.Stop()
		for {
			<-t.C
			u.Update(check.Check())
		}
	}()

	return u
}

// PeriodicThresholdChecker wraps an updater to provide a periodic checker that
// uses a threshold before it changes status
func PeriodicThresholdChecker(check Checker, period time.Duration, threshold int) Checker {
	tu := NewThresholdStatusUpdater(threshold)
	go func() {
		t := time.NewTicker(period)
		defer t.Stop()
		for {
			<-t.C
			tu.Update(check.Check())
		}
	}()

	return tu
}

// CheckStatus returns a map with all the current health check errors
func (registry *Registry) CheckStatus() map[string]string { // TODO(stevvooe) this needs a proper type
	registry.mu.RLock()
	defer registry.mu.RUnlock()
	statusKeys := make(map[string]string)
	for k, v := range registry.registeredChecks {
		err := v.Check()
		if err != nil {
			statusKeys[k] = err.Error()
		}
	}

	return statusKeys
}

// CheckStatus returns a map with all the current health check errors from the
// default registry.
func CheckStatus() map[string]string {
	return DefaultRegistry.CheckStatus()
}

// Register associates the checker with the provided name.
func (registry *Registry) Register(name string, check Checker) {
	if registry == nil {
		registry = DefaultRegistry
	}
	registry.mu.Lock()
	defer registry.mu.Unlock()
	_, ok := registry.registeredChecks[name]
	if ok {
		panic("Check already exists: " + name)
	}
	registry.registeredChecks[name] = check
}

// Register associates the checker with the provided name in the default
// registry.
func Register(name string, check Checker) {
	DefaultRegistry.Register(name, check)
}

// RegisterFunc allows the convenience of registering a checker directly from
// an arbitrary func() error.
func (registry *Registry) RegisterFunc(name string, check func() error) {
	registry.Register(name, CheckFunc(check))
}

// RegisterFunc allows the convenience of registering a checker in the default
// registry directly from an arbitrary func() error.
func RegisterFunc(name string, check func() error) {
	DefaultRegistry.RegisterFunc(name, check)
}

// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
// from an arbitrary func() error.
func (registry *Registry) RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
	registry.Register(name, PeriodicChecker(check, period))
}

// RegisterPeriodicFunc allows the convenience of registering a PeriodicChecker
// in the default registry from an arbitrary func() error.
func RegisterPeriodicFunc(name string, period time.Duration, check CheckFunc) {
	DefaultRegistry.RegisterPeriodicFunc(name, period, check)
}

// RegisterPeriodicThresholdFunc allows the convenience of registering a
// PeriodicChecker from an arbitrary func() error.
func (registry *Registry) RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
	registry.Register(name, PeriodicThresholdChecker(check, period, threshold))
}

// RegisterPeriodicThresholdFunc allows the convenience of registering a
// PeriodicChecker in the default registry from an arbitrary func() error.
func RegisterPeriodicThresholdFunc(name string, period time.Duration, threshold int, check CheckFunc) {
	DefaultRegistry.RegisterPeriodicThresholdFunc(name, period, threshold, check)
}

// StatusHandler returns a JSON blob with all the currently registered Health Checks
// and their corresponding status.
// Returns 503 if any Error status exists, 200 otherwise
func StatusHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		checks := CheckStatus()
		status := http.StatusOK

		// If there is an error, return 503
		if len(checks) != 0 {
			status = http.StatusServiceUnavailable
		}

		statusResponse(w, r, status, checks)
	} else {
		http.NotFound(w, r)
	}
}

// Handler returns a handler that will return 503 response code if the health
// checks have failed. If everything is okay with the health checks, the
// handler will pass through to the provided handler. Use this handler to
// disable a web application when the health checks fail.
func Handler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		checks := CheckStatus()
		if len(checks) != 0 {
			errcode.ServeJSON(w, errcode.ErrorCodeUnavailable.
				WithDetail("health check failed: please see /debug/health"))
			return
		}

		handler.ServeHTTP(w, r) // pass through
	})
}

// statusResponse completes the request with a response describing the health
// of the service.
func statusResponse(w http.ResponseWriter, r *http.Request, status int, checks map[string]string) {
	p, err := json.Marshal(checks)
	if err != nil {
		context.GetLogger(context.Background()).Errorf("error serializing health status: %v", err)
		p, err = json.Marshal(struct {
			ServerError string `json:"server_error"`
		}{
			ServerError: "Could not parse error message",
		})
		status = http.StatusInternalServerError

		if err != nil {
			context.GetLogger(context.Background()).Errorf("error serializing health status failure message: %v", err)
			return
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Content-Length", fmt.Sprint(len(p)))
	w.WriteHeader(status)
	if _, err := w.Write(p); err != nil {
		context.GetLogger(context.Background()).Errorf("error writing health status response body: %v", err)
	}
}

// Registers global /debug/health api endpoint, creates default registry
func init() {
	DefaultRegistry = NewRegistry()
	http.HandleFunc("/debug/health", StatusHandler)
}
<|endoftext|>


<commit_before>package estimoteslack

import (
	"encoding/json"
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/urlfetch"
	"html/template"
	"net/http"
	"net/url"
	"time"
)

const (
	incoming_webhook_url string = "YOUR_SLACKCHANNEL_INCOMING_WEBHOOK_URL"
)

type Asset struct {
	BeaconId   string
	BeaconName string
	AssetId    string
	AssetName  string
	AppType    string
	AppData    string
	Status     string
}

type Issue struct {
	AssetId       string
	AssetName     string
	IssueType     string
	IssueDateTime time.Time
	IssueDetails  string
	IssueRaisedBy string
	Status        string
}

type APIResponse struct {
	ErrorCode string
	ErrorMsg  string
}

type Message struct {
	Channel    string `json:"channel"`
	Text       string `json:"text"`
	Username   string `json:"username"`
	Icon_emoji string `json:"icon_emoji"`
}

func init() {
	http.HandleFunc("/", homepagehandler)
	http.HandleFunc("/assets", assetshandler)
	http.HandleFunc("/issues", issueshandler)
	http.HandleFunc("/listissues", issuespagehandler)
	http.HandleFunc("/addAsset", addAsset)
	http.HandleFunc("/raiseIssue", raiseIssue)
}

//This handler is for the home page. It shows the list of assets
func homepagehandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	q := datastore.NewQuery("Asset").Filter("Status=", "ACTIVE")
	var results []Asset
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = assetsListTemplate.Execute(w, results); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

//Show list of Issues
func issuespagehandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	q := datastore.NewQuery("Issue").Filter("Status=", "ACTIVE")
	var results []Issue
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = issuesListTemplate.Execute(w, results); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

//API - List of Assets
func assetshandler(w http.ResponseWriter, r *http.Request) {

	c := appengine.NewContext(r)
	q := datastore.NewQuery("Asset")
	beaconname := r.FormValue("BeaconName")

	if beaconname != "" {
		q = datastore.NewQuery("Asset").Filter("Status=", "ACTIVE").Filter("BeaconName=", beaconname)
	} else {
		q = datastore.NewQuery("Asset").Filter("Status=", "ACTIVE")
	}

	var results []Asset
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, e := json.Marshal(results)
	if e != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(w, string(b))
}

//API - List of Issues
func issueshandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	q := datastore.NewQuery("Issue").Filter("Status=", "ACTIVE")
	var results []Issue
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, e := json.Marshal(results)
	if e != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(w, string(b))
}

//Creates a new Asset Key
func assetKey(c context.Context, assetid string) *datastore.Key {
	return datastore.NewKey(c, "Asset", assetid, 0, nil)
}

//JSON API to insert Asset
func addAsset(w http.ResponseWriter, r *http.Request) {

	assetid := r.FormValue("AssetId")
	assetname := r.FormValue("AssetName")
	beaconid := r.FormValue("BeaconId")
	beaconname := r.FormValue("BeaconName")
	apptype := r.FormValue("AppType")
	appdata := r.FormValue("AppData")
	status := r.FormValue("Status")

	//Need to either insert or update the Asset

	c := appengine.NewContext(r)
	a := Asset{
		AssetId:    assetid,
		AssetName:  assetname,
		BeaconId:   beaconid,
		BeaconName: beaconname,
		AppType:    apptype,
		AppData:    appdata,
		Status:     status,
	}

	key := assetKey(c, assetid)
	_, err := datastore.Put(c, key, &a)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	resp := APIResponse{
		ErrorCode: "200",
		ErrorMsg:  "",
	}
	b, e := json.Marshal(&resp)
	if e != nil {
	}
	fmt.Fprint(w, string(b))
}

//Creates a new Issue Key
func issueKey(c context.Context, issueid string) *datastore.Key {
	return datastore.NewKey(c, "Issue", issueid, 0, nil)
}

//JSON API to insert Issue
func raiseIssue(w http.ResponseWriter, r *http.Request) {

	assetid := r.FormValue("AssetId")
	assetname := r.FormValue("AssetName")
	issuetype := r.FormValue("IssueType")
	issuedetails := r.FormValue("IssueDetails")
	issueraisedby := r.FormValue("IssueRaisedBy")
	t := time.Now()
	issuedatetime := t
	status := r.FormValue("Status")

	//Need to either insert Issue

	c := appengine.NewContext(r)
	i := Issue{
		AssetId:       assetid,
		AssetName:     assetname,
		IssueType:     issuetype,
		IssueDetails:  issuedetails,
		IssueDateTime: issuedatetime,
		IssueRaisedBy: issueraisedby,
		Status:        status,
	}

	key := issueKey(c, string(t.Unix()))
	_, err := datastore.Put(c, key, &i)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	//Send Slack Notification

	m := Message{"#general", "***************************************" + "\n New Problem Report" + "\n Asset Id: " + i.AssetId + "\n Asset Name :" + i.AssetName + "\n Issue Type: " + i.IssueType + "\n Reported By: " + i.IssueRaisedBy + "\n Details: " + i.IssueDetails + "\n Reported on: " + i.IssueDateTime.Format("Mon Jan _2 15:04:05 2006") + "\n ************************", "HelpDesk Bot", ":computer:"}
	b, err := json.Marshal(m)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	client := urlfetch.Client(c)

	v := url.Values{}
	v.Set("payload", string(b))
	_, err = client.PostForm(incoming_webhook_url, v)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	resp := APIResponse{
		ErrorCode: "200",
		ErrorMsg:  "",
	}
	b, e := json.Marshal(&resp)
	if e != nil {
	}
	fmt.Fprint(w, string(b))
}

//Asset List Template
var assetsListTemplate = template.Must(template.New("assetsListTemplate").Parse(`

    <title>Assets List</title>

Assets List

AssetId	AssetName	BeaconId	BeaconName	AppType	AppData	Status

    {{range .}}

	  {{.AssetId}}
	  {{.AssetName}}
    {{.BeaconId}}
	  {{.BeaconName}}
    {{.AppType}}
    {{.AppData}}
    {{.Status}}

    {{end}}

`))

//Issues List Template
var issuesListTemplate = template.Must(template.New("issuesListTemplate").Parse(`

    <title>Issues List</title>

Issues List

AssetId	AssetName	IssueType	IssueDetails	IssueDateTime	IssueRaisedBy	Status

    {{range .}}

	  {{.AssetId}}
	  {{.AssetName}}
    {{.IssueType}}
    {{.IssueDetails}}
	  {{.IssueDateTime}}
    {{.IssueRaisedBy}}
    {{.Status}}

    {{end}}

`))
<commit_msg>Update handlers for clearing data and configure Slack Incoming Webhook URL<commit_after>package estimoteslack

import (
	"encoding/json"
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/urlfetch"
	"html/template"
	"net/http"
	"net/url"
	"time"
)

type WebhookURL struct {
  PropName string
  PropValue string
}

type Asset struct {
	BeaconId   string
	BeaconName string
	AssetId    string
	AssetName  string
	AppType    string
	AppData    string
	Status     string
}

type Issue struct {
	AssetId       string
	AssetName     string
	IssueType     string
	IssueDateTime time.Time
	IssueDetails  string
	IssueRaisedBy string
	Status        string
}

type APIResponse struct {
	ErrorCode string
	ErrorMsg  string
}

type Message struct {
	Channel    string `json:"channel"`
	Text       string `json:"text"`
	Username   string `json:"username"`
	Icon_emoji string `json:"icon_emoji"`
}

func init() {
	http.HandleFunc("/", homepagehandler)
	http.HandleFunc("/assets", assetshandler)
	http.HandleFunc("/issues", issueshandler)
	http.HandleFunc("/listissues", issuespagehandler)
	http.HandleFunc("/addAsset", addAsset)
	http.HandleFunc("/raiseIssue", raiseIssue)
  http.HandleFunc("/addSlackTeamWebhook",addSlackTeamWebhook)
  http.HandleFunc("/SlackTeamWebhook",getSlackTeamWebhook)
  http.HandleFunc("/clearData",clearData)
}

//This handler is for the home page. It shows the list of assets
func homepagehandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	q := datastore.NewQuery("Asset").Filter("Status=", "ACTIVE")
	var results []Asset
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = assetsListTemplate.Execute(w, results); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

//Clear datastore
func clearData(w http.ResponseWriter, r *http.Request) {
   c := appengine.NewContext(r)
   q1 := datastore.NewQuery("Asset")
  var results1 []Asset
  keys1,err := q1.GetAll(c,&results1)
  if err != nil {
               http.Error(w, err.Error(), http.StatusInternalServerError)
               return
  }
  datastore.DeleteMulti(c,keys1)

  q2 := datastore.NewQuery("Issue")
  var results2 []Issue
  keys2,err := q2.GetAll(c,&results2)
  if err != nil {
               http.Error(w, err.Error(), http.StatusInternalServerError)
               return
  }
  datastore.DeleteMulti(c,keys2)

}

//Show list of Issues
func issuespagehandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	q := datastore.NewQuery("Issue").Filter("Status=", "ACTIVE")
	var results []Issue
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if err = issuesListTemplate.Execute(w, results); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

//API - List of Assets
func assetshandler(w http.ResponseWriter, r *http.Request) {

	c := appengine.NewContext(r)
	q := datastore.NewQuery("Asset")
	beaconname := r.FormValue("BeaconName")

	if beaconname != "" {
		q = datastore.NewQuery("Asset").Filter("Status=", "ACTIVE").Filter("BeaconName=", beaconname)
	} else {
		q = datastore.NewQuery("Asset").Filter("Status=", "ACTIVE")
	}

	var results []Asset
	_, err := q.GetAll(c, &results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	b, e := json.Marshal(results)
	if e != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(w, string(b))
}

//API - List of Issues
func issueshandler(w http.ResponseWriter, r *http.Request) {
  c := appengine.NewContext(r)
      q := datastore.NewQuery("Issue").Filter("Status=","ACTIVE")
    var results []Issue
    _,err := q.GetAll(c,&results)
    if err != nil {
                  http.Error(w, err.Error(), http.StatusInternalServerError)
                  return
          }

    b,e := json.Marshal(results)
    if e != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
                  return
    }
    fmt.Fprint(w,string(b))
}

func webhookURLKey(c context.Context, url string) *datastore.Key {
    return datastore.NewKey(c, "WebhookURL", "webhookurlkey", 0, nil)
}

//Add Slack Team Webhook url
func addSlackTeamWebhook(w http.ResponseWriter, r *http.Request) {
  webhookurl := r.FormValue("webhookurl")
  c := appengine.NewContext(r)
  wurl := WebhookURL {
     PropName  : "webhookurl",
     PropValue : webhookurl,
  }

key := webhookURLKey(c,webhookurl)
  _, err := datastore.Put(c, key, &wurl)
  if err != nil {
          http.Error(w, err.Error(), http.StatusInternalServerError)
          return
  }

resp := APIResponse{
  ErrorCode : "200",
  ErrorMsg  : "",
}
b,e := json.Marshal(&resp);
if e != nil {}
fmt.Fprint(w,string(b))

}

func getWebhook(c context.Context) string {
  var wurl WebhookURL
  q := datastore.NewQuery("WebhookURL")

    var results []WebhookURL
    keys,err := q.GetAll(c,&results)

    err = datastore.Get(c, keys[0], &wurl)
    if err != nil {

                  return ""
          }

    return wurl.PropValue

}

//API - Slack Team URL
func getSlackTeamWebhook(w http.ResponseWriter, r *http.Request) {

  var wurl WebhookURL
  c := appengine.NewContext(r)
  q := datastore.NewQuery("WebhookURL")

    var results []WebhookURL
    keys,err := q.GetAll(c,&results)

    err = datastore.Get(c, keys[0], &wurl)
    if err != nil {
                  http.Error(w, err.Error(), http.StatusInternalServerError)
                  return
          }

    fmt.Fprint(w,wurl.PropValue)
}

//Creates a new Asset Key
func assetKey(c context.Context, assetid string) *datastore.Key {
	return datastore.NewKey(c, "Asset", assetid, 0, nil)
}

//JSON API to insert Asset
func addAsset(w http.ResponseWriter, r *http.Request) {

	assetid := r.FormValue("AssetId")
	assetname := r.FormValue("AssetName")
	beaconid := r.FormValue("BeaconId")
	beaconname := r.FormValue("BeaconName")
	apptype := r.FormValue("AppType")
	appdata := r.FormValue("AppData")
	status := r.FormValue("Status")

	//Need to either insert or update the Asset

	c := appengine.NewContext(r)
	a := Asset{
		AssetId:    assetid,
		AssetName:  assetname,
		BeaconId:   beaconid,
		BeaconName: beaconname,
		AppType:    apptype,
		AppData:    appdata,
		Status:     status,
	}

	key := assetKey(c, assetid)
	_, err := datastore.Put(c, key, &a)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	resp := APIResponse{
		ErrorCode: "200",
		ErrorMsg:  "",
	}
	b, e := json.Marshal(&resp)
	if e != nil {
	}
	fmt.Fprint(w, string(b))
}

//Creates a new Issue Key
func issueKey(c context.Context, issueid string) *datastore.Key {
	return datastore.NewKey(c, "Issue", issueid, 0, nil)
}

//JSON API to insert Issue
func raiseIssue(w http.ResponseWriter, r *http.Request) {

	assetid := r.FormValue("AssetId")
	assetname := r.FormValue("AssetName")
	issuetype := r.FormValue("IssueType")
	issuedetails := r.FormValue("IssueDetails")
	issueraisedby := r.FormValue("IssueRaisedBy")
	t := time.Now()
	issuedatetime := t
	status := r.FormValue("Status")

	//Need to either insert Issue

	c := appengine.NewContext(r)
	i := Issue{
		AssetId:       assetid,
		AssetName:     assetname,
		IssueType:     issuetype,
		IssueDetails:  issuedetails,
		IssueDateTime: issuedatetime,
		IssueRaisedBy: issueraisedby,
		Status:        status,
	}

	key := issueKey(c, string(t.Unix()))
	_, err := datastore.Put(c, key, &i)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	//Send Slack Notification

	m := Message{"#general", "***************************************" + "\n New Problem Report" + "\n Asset Id: " + i.AssetId + "\n Asset Name :" + i.AssetName + "\n Issue Type: " + i.IssueType + "\n Reported By: " + i.IssueRaisedBy + "\n Details: " + i.IssueDetails + "\n Reported on: " + i.IssueDateTime.Format("Mon Jan _2 15:04:05 2006") + "\n ************************", "HelpDesk Bot", ":computer:"}
	b, err := json.Marshal(m)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	client := urlfetch.Client(c)

	v := url.Values{}
	v.Set("payload", string(b))
	_, err = client.PostForm(incoming_webhook_url, v)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	resp := APIResponse{
		ErrorCode: "200",
		ErrorMsg:  "",
	}
	b, e := json.Marshal(&resp)
	if e != nil {
	}
	fmt.Fprint(w, string(b))
}

//Asset List Template
var assetsListTemplate = template.Must(template.New("assetsListTemplate").Parse(`

    <title>Assets List</title>

Assets List

AssetId	AssetName	BeaconId	BeaconName	AppType	AppData	Status

    {{range .}}

	  {{.AssetId}}
	  {{.AssetName}}
    {{.BeaconId}}
	  {{.BeaconName}}
    {{.AppType}}
    {{.AppData}}
    {{.Status}}

    {{end}}

`))

//Issues List Template
var issuesListTemplate = template.Must(template.New("issuesListTemplate").Parse(`

    <title>Issues List</title>

Issues List

AssetId	AssetName	IssueType	IssueDetails	IssueDateTime	IssueRaisedBy	Status

    {{range .}}

	  {{.AssetId}}
	  {{.AssetName}}
    {{.IssueType}}
    {{.IssueDetails}}
	  {{.IssueDateTime}}
    {{.IssueRaisedBy}}
    {{.Status}}

    {{end}}

`))
<|endoftext|>


<commit_before>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   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 echo

import (
	"errors"
	"fmt"
	"strings"
)

// T 标记为多语言文本
func T(format string, args ...interface{}) string {
	if len(args) > 0 {
		return fmt.Sprintf(format, args...)
	}
	return format
}

func E(format string, args ...interface{}) error {
	if len(args) > 0 {
		return fmt.Errorf(format, args...)
	}
	return errors.New(format)
}

type Translator interface {
	T(format string, args ...interface{}) string
	E(format string, args ...interface{}) error
	Lang() LangCode
}

func NewLangCode(language string, separator ...string) LangCode {
	l := LangCode{}
	sep := `-`
	if len(separator) > 0 && len(separator[0]) > 0 {
		sep = separator[0]
	}
	lg := strings.SplitN(language, sep, 2)
	switch len(lg) {
	case 2:
		l.CountryLower = strings.ToLower(lg[1])
		l.CountryUpper = strings.ToUpper(lg[1])
		fallthrough
	case 1:
		l.Language = lg[0]
	}
	return l
}

type LangCode struct {
	Language     string
	CountryLower string
	CountryUpper string
}

func (l LangCode) String() string {
	if len(l.CountryLower) > 0 {
		return l.Language + `-` + l.CountryLower
	}
	return l.Language
}

func (l LangCode) Format(upperCountry bool, separator ...string) string {
	var country string
	if upperCountry {
		country = l.CountryUpper
	} else {
		country = l.CountryLower
	}
	if len(country) > 0 {
		if len(separator) > 0 {
			return l.Language + separator[0] + country
		}
		return l.Language + `-` + country
	}
	return l.Language
}

var DefaultNopTranslate Translator = &NopTranslate{
	code: LangCode{
		Language: `en`,
	},
}

type NopTranslate struct {
	code LangCode
}

func (n *NopTranslate) T(format string, args ...interface{}) string {
	return T(format, args...)
}

func (n *NopTranslate) E(format string, args ...interface{}) error {
	return E(format, args...)
}

func (n *NopTranslate) Lang() LangCode {
	return n.code
}
<commit_msg>update<commit_after>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   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 echo

import (
	"errors"
	"fmt"
	"strings"
)

// T 标记为多语言文本
func T(format string, args ...interface{}) string {
	if len(args) > 0 {
		return fmt.Sprintf(format, args...)
	}
	return format
}

func E(format string, args ...interface{}) error {
	if len(args) > 0 {
		return fmt.Errorf(format, args...)
	}
	return errors.New(format)
}

type Translator interface {
	T(format string, args ...interface{}) string
	E(format string, args ...interface{}) error
	Lang() LangCode
}

func NewLangCode(language string, separator ...string) LangCode {
	l := LangCode{}
	sep := `-`
	if len(separator) > 0 && len(separator[0]) > 0 {
		sep = separator[0]
	}
	lg := strings.SplitN(language, sep, 2)
	switch len(lg) {
	case 2:
		l.CountryLower = strings.ToLower(lg[1])
		l.CountryUpper = strings.ToUpper(lg[1])
		fallthrough
	case 1:
		l.Language = strings.ToLower(lg[0])
	}
	return l
}

type LangCode struct {
	Language     string
	CountryLower string
	CountryUpper string
}

func (l LangCode) String() string {
	if len(l.CountryLower) > 0 {
		return l.Language + `-` + l.CountryLower
	}
	return l.Language
}

func (l LangCode) Format(upperCountry bool, separator ...string) string {
	var country string
	if upperCountry {
		country = l.CountryUpper
	} else {
		country = l.CountryLower
	}
	if len(country) > 0 {
		if len(separator) > 0 {
			return l.Language + separator[0] + country
		}
		return l.Language + `-` + country
	}
	return l.Language
}

var DefaultNopTranslate Translator = &NopTranslate{
	code: LangCode{
		Language: `en`,
	},
}

type NopTranslate struct {
	code LangCode
}

func (n *NopTranslate) T(format string, args ...interface{}) string {
	return T(format, args...)
}

func (n *NopTranslate) E(format string, args ...interface{}) error {
	return E(format, args...)
}

func (n *NopTranslate) Lang() LangCode {
	return n.code
}
<|endoftext|>