<commit_before>// slice_flags is a tool to instantly implement a flag.Value called
// <Something>SliceFlag, which contains a
// slice of <Something>Flags, an accessor called <Something>Slice, and a test to
// ensure that the SliceFlag parses and Preprocesses correctly if necessary.
//
// It's intended for use in cmd/bytemark/app/flags - see slice_flags.go in that
// package for the go:generate directives.
//
// example usage: to add a SliceFlag for some NewFlag which uses Preprocess and
// for which "blah" is a value value:
// go run ./gen/slice_flags/slice_flags.go -preprocesser -o new_flag.go
// --example-input "blah" New
//
// For the generated test it is assumed that your original Flag, when Set with
// some string will return the same string when String() is called. If this is
// not the case, we need to modify this code and template_test.go.tmpl to
// accept an example output as well as the example input.
//
// If, for whatever reason, it is useful to reuse this code with different
// templates, the -t and -tt options specify the template for the SliceFlag and
// the
package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

type sliceFlag struct {
	TypeName     string
	Preprocess   bool
	ExampleInput string
}

func log(text string) {
	_, _ = fmt.Fprintln(os.Stderr, text)
}

// ok for this bad boy you gotta set the arguments as all the types you wanna
// generate slice flags for.
func main() {
	outputFile := flag.String("o", "-", "File to output to. Blank or - for stdin")
	testOutputFile := flag.String("ot", "", "Test file to output to. - for stdin. If unset, defaults to same base same path without extension as outputFile, with _test.go appended")
	templateFile := flag.String("t", "./gen/slice_flags/template.go.tmpl", "File to use as template")
	testTemplateFile := flag.String("tt", "./gen/slice_flags/template_test.go.tmpl", "File to use as template")
	exampleInput := flag.String("example-input", "", "Example input, used to ensure that the test works")
	preprocess := flag.Bool("preprocesser", false, "Whether Preprocessor is implemented on the single-version of these flags")

	flag.Parse()
	var err error

	if *testOutputFile == "" {
		name := strings.TrimSuffix(filepath.Base(*outputFile), ".go")
		*testOutputFile = filepath.Join(filepath.Dir(*outputFile), name+"_test.go")
	}

	args := flag.Args()
	if len(args) != 1 {
		log("usage: go run slice_flags -o <output file> -t <template file> [-preprocesser] [-example-input <example>] <type name>")
		log("")
		log("creates a new SomeTypeSliceFlag for a flag type names SomeTypeFlag.")
		flag.PrintDefaults()
		fmt.Println("")
		fmt.Println("To generate multiple slice flags, call this multiple times")
		os.Exit(1)
	}

	data := sliceFlag{
		TypeName:     args[0],
		Preprocess:   *preprocess,
		ExampleInput: *exampleInput,
	}
	err = writeTemplate(*outputFile, *templateFile, data)
	if err != nil {
		fmt.Printf("couldn't write %s: %s\n", *outputFile, err)
		os.Exit(1)
	}
	err = writeTemplate(*testOutputFile, *testTemplateFile, data)
	if err != nil {
		fmt.Printf("couldn't write %s: %s\n", *testOutputFile, err)
		os.Exit(1)
	}
}

func writeTemplate(outputFile, templateFile string, data sliceFlag) (err error) {
	fmt.Println("writeTemplate started")
	var outputWriter io.WriteCloser = os.Stdout
	if outputFile != "" && outputFile != "-" {
		outputWriter, err = os.Create(outputFile)
		if err != nil {
			return
		}
	}
	defer func() { _ = outputWriter.Close() }()

	tmpl, err := template.ParseFiles(templateFile)
	if err != nil {
		return
	}

	inputReader, inputWriter := io.Pipe()
	betweenReader, betweenWriter := io.Pipe()
	gofmtPath, err := exec.LookPath("gofmt")
	if err != nil {
		return
	}
	importsPath, err := exec.LookPath("goimports")
	if err != nil {
		return
	}
	gofmt := exec.Cmd{
		Path:   gofmtPath,
		Args:   []string{"gofmt"},
		Stdin:  inputReader,
		Stdout: betweenWriter,
		Stderr: os.Stderr,
	}
	imports := exec.Cmd{
		Path:   importsPath,
		Args:   []string{"goimports"},
		Stdin:  betweenReader,
		Stdout: outputWriter,
		Stderr: os.Stderr,
	}
	err = gofmt.Start()
	if err != nil {
		return fmt.Errorf("Couldn't start gofmt: %s", err)
	}
	err = imports.Start()
	if err != nil {
		return fmt.Errorf("Couldn't start goimports: %s", err)
	}

	// errors are echoed rather than returned from here on out in order to
	// ensure resources get cleaned up properly, otherwise we get a deadlock
	fmt.Println("executing template")
	err = tmpl.Execute(inputWriter, data)
	if err != nil {
		fmt.Printf("template errored: %s\n", err)
	}

	fmt.Println("closing inputWriter")
	_ = inputWriter.Close()

	fmt.Println("waiting for gofmt to finish")
	fmtErr := gofmt.Wait()
	if fmtErr != nil {
		fmt.Printf("gofmt errored: %s\n", fmtErr)
	}
	_ = betweenWriter.Close()

	fmt.Println("waiting for goimports to finish")
	importsErr := imports.Wait()
	if importsErr != nil {
		fmt.Printf("goimports errored: %s\n", importsErr)
	}
	if err != nil || fmtErr != nil || importsErr != nil {
		err = errors.New("template/goimports/gofmt errored")
	}
	fmt.Println("returning from writeTemplate")
	return
}
<commit_msg>Finish documentation comment for slice_flags<commit_after>// slice_flags is a tool to instantly implement a flag.Value called
// <Something>SliceFlag, which contains a
// slice of <Something>Flags, an accessor called <Something>Slice, and a test to
// ensure that the SliceFlag parses and Preprocesses correctly if necessary.
//
// It's intended for use in cmd/bytemark/app/flags - see slice_flags.go in that
// package for the go:generate directives.
//
// example usage: to add a SliceFlag for some NewFlag which uses Preprocess and
// for which "blah" is a value value:
// go run ./gen/slice_flags/slice_flags.go -preprocesser -o new_flag.go
// --example-input "blah" New
//
// For the generated test it is assumed that your original Flag, when Set with
// some string will return the same string when String() is called. If this is
// not the case, we need to modify this code and template_test.go.tmpl to
// accept an example output as well as the example input.
//
// If, for whatever reason, it is useful to reuse this code with different
// templates, the -t and -tt options specify the template for the SliceFlag and
// the test for the SliceFlag, respectively.
package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

type sliceFlag struct {
	TypeName     string
	Preprocess   bool
	ExampleInput string
}

func log(text string) {
	_, _ = fmt.Fprintln(os.Stderr, text)
}

// ok for this bad boy you gotta set the arguments as all the types you wanna
// generate slice flags for.
func main() {
	outputFile := flag.String("o", "-", "File to output to. Blank or - for stdin")
	testOutputFile := flag.String("ot", "", "Test file to output to. - for stdin. If unset, defaults to same base same path without extension as outputFile, with _test.go appended")
	templateFile := flag.String("t", "./gen/slice_flags/template.go.tmpl", "File to use as template")
	testTemplateFile := flag.String("tt", "./gen/slice_flags/template_test.go.tmpl", "File to use as template")
	exampleInput := flag.String("example-input", "", "Example input, used to ensure that the test works")
	preprocess := flag.Bool("preprocesser", false, "Whether Preprocessor is implemented on the single-version of these flags")

	flag.Parse()
	var err error

	if *testOutputFile == "" {
		name := strings.TrimSuffix(filepath.Base(*outputFile), ".go")
		*testOutputFile = filepath.Join(filepath.Dir(*outputFile), name+"_test.go")
	}

	args := flag.Args()
	if len(args) != 1 {
		log("usage: go run slice_flags -o <output file> -t <template file> [-preprocesser] [-example-input <example>] <type name>")
		log("")
		log("creates a new SomeTypeSliceFlag for a flag type names SomeTypeFlag.")
		flag.PrintDefaults()
		fmt.Println("")
		fmt.Println("To generate multiple slice flags, call this multiple times")
		os.Exit(1)
	}

	data := sliceFlag{
		TypeName:     args[0],
		Preprocess:   *preprocess,
		ExampleInput: *exampleInput,
	}
	err = writeTemplate(*outputFile, *templateFile, data)
	if err != nil {
		fmt.Printf("couldn't write %s: %s\n", *outputFile, err)
		os.Exit(1)
	}
	err = writeTemplate(*testOutputFile, *testTemplateFile, data)
	if err != nil {
		fmt.Printf("couldn't write %s: %s\n", *testOutputFile, err)
		os.Exit(1)
	}
}

func writeTemplate(outputFile, templateFile string, data sliceFlag) (err error) {
	fmt.Println("writeTemplate started")
	var outputWriter io.WriteCloser = os.Stdout
	if outputFile != "" && outputFile != "-" {
		outputWriter, err = os.Create(outputFile)
		if err != nil {
			return
		}
	}
	defer func() { _ = outputWriter.Close() }()

	tmpl, err := template.ParseFiles(templateFile)
	if err != nil {
		return
	}

	inputReader, inputWriter := io.Pipe()
	betweenReader, betweenWriter := io.Pipe()
	gofmtPath, err := exec.LookPath("gofmt")
	if err != nil {
		return
	}
	importsPath, err := exec.LookPath("goimports")
	if err != nil {
		return
	}
	gofmt := exec.Cmd{
		Path:   gofmtPath,
		Args:   []string{"gofmt"},
		Stdin:  inputReader,
		Stdout: betweenWriter,
		Stderr: os.Stderr,
	}
	imports := exec.Cmd{
		Path:   importsPath,
		Args:   []string{"goimports"},
		Stdin:  betweenReader,
		Stdout: outputWriter,
		Stderr: os.Stderr,
	}
	err = gofmt.Start()
	if err != nil {
		return fmt.Errorf("Couldn't start gofmt: %s", err)
	}
	err = imports.Start()
	if err != nil {
		return fmt.Errorf("Couldn't start goimports: %s", err)
	}

	// errors are echoed rather than returned from here on out in order to
	// ensure resources get cleaned up properly, otherwise we get a deadlock
	fmt.Println("executing template")
	err = tmpl.Execute(inputWriter, data)
	if err != nil {
		fmt.Printf("template errored: %s\n", err)
	}

	fmt.Println("closing inputWriter")
	_ = inputWriter.Close()

	fmt.Println("waiting for gofmt to finish")
	fmtErr := gofmt.Wait()
	if fmtErr != nil {
		fmt.Printf("gofmt errored: %s\n", fmtErr)
	}
	_ = betweenWriter.Close()

	fmt.Println("waiting for goimports to finish")
	importsErr := imports.Wait()
	if importsErr != nil {
		fmt.Printf("goimports errored: %s\n", importsErr)
	}
	if err != nil || fmtErr != nil || importsErr != nil {
		err = errors.New("template/goimports/gofmt errored")
	}
	fmt.Println("returning from writeTemplate")
	return
}
<|endoftext|>


<commit_before>package morningStar

import (
	"../jsonHttp"
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"sync"
	"time"
)

const PERFORMANCE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=1&id=`
const VOLATILITE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=2&id=`
const REFRESH_DELAY = 18

var LIST_REQUEST = regexp.MustCompile(`^/list$`)
var PERF_REQUEST = regexp.MustCompile(`^/(.+?)$`)

var ISIN = regexp.MustCompile(`ISIN.:(\S+)`)
var LABEL = regexp.MustCompile(`<h1[^>]*?>((?:.|\n)*?)</h1>`)
var RATING = regexp.MustCompile(`<span\sclass=".*?stars([0-9]).*?">`)
var CATEGORY = regexp.MustCompile(`<span[^>]*?>Catégorie</span>.*?<span[^>]*?>(.*?)</span>`)
var PERF_ONE_MONTH = regexp.MustCompile(`<td[^>]*?>1 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_THREE_MONTH = regexp.MustCompile(`<td[^>]*?>3 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_SIX_MONTH = regexp.MustCompile(`<td[^>]*?>6 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_ONE_YEAR = regexp.MustCompile(`<td[^>]*?>1 an</td><td[^>]*?>(.*?)</td>`)
var VOL_3_YEAR = regexp.MustCompile(`<td[^>]*?>Ecart-type 3 ans.?</td><td[^>]*?>(.*?)</td>`)

type SyncedMap struct {
	sync.RWMutex
	performances map[string]Performance
}

func (m *SyncedMap) get(key string) (Performance, bool) {
	m.RLock()
	defer m.RUnlock()

	performance, ok := m.performances[key]
	return performance, ok
}

func (m *SyncedMap) push(key string, performance Performance) {
	m.Lock()
	defer m.Unlock()
	m.performances[key] = performance
}

var PERFORMANCE_CACHE = SyncedMap{m: make(map[string]Performance)}

type Performance struct {
	Id            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonth    float64   `json:"3m"`
	SixMonth      float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

type PerformanceAsync struct {
	performance *Performance
	err         error
}

type Search struct {
	Id    string `json:"i"`
	Label string `json:"n"`
}

type Results struct {
	Results interface{} `json:"results"`
}

func readBody(body io.ReadCloser) ([]byte, error) {
	defer body.Close()
	return ioutil.ReadAll(body)
}

func getBody(url string) ([]byte, error) {
	response, err := http.Get(url)
	if err != nil {
		return nil, errors.New(`Error while retrieving data from ` + url)
	}

	if response.StatusCode >= 400 {
		return nil, errors.New(`Got error ` + strconv.Itoa(response.StatusCode) + ` while getting ` + url)
	}

	body, err := readBody(response.Body)
	if err != nil {
		return nil, errors.New(`Error while reading body of ` + url)
	}

	return body, nil
}

func getLabel(extract *regexp.Regexp, body []byte) []byte {
	match := extract.FindSubmatch(body)
	if match == nil {
		return nil
	}

	return bytes.Replace(match[1], []byte(`&`), []byte(`&`), -1)
}

func getPerformance(extract *regexp.Regexp, body []byte) float64 {
	dotResult := bytes.Replace(getLabel(extract, body), []byte(`,`), []byte(`.`), -1)
	percentageResult := bytes.Replace(dotResult, []byte(`%`), []byte(``), -1)
	trimResult := bytes.TrimSpace(percentageResult)

	result, err := strconv.ParseFloat(string(trimResult), 64)
	if err != nil {
		return 0.0
	}
	return result
}

func SinglePerformance(morningStarId []byte) (*Performance, error) {
	cleanId := string(bytes.ToLower(morningStarId))

	performance, ok := PERFORMANCE_CACHE.get(cleanId)

	if ok && time.Now().Add(time.Hour*-REFRESH_DELAY).Before(performance.Update) {
		return &performance, nil
	}

	performanceBody, err := getBody(PERFORMANCE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	volatiliteBody, err := getBody(VOLATILITE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	isin := string(getLabel(ISIN, performanceBody))
	label := string(getLabel(LABEL, performanceBody))
	rating := string(getLabel(RATING, performanceBody))
	category := string(getLabel(CATEGORY, performanceBody))
	oneMonth := getPerformance(PERF_ONE_MONTH, performanceBody)
	threeMonths := getPerformance(PERF_THREE_MONTH, performanceBody)
	sixMonths := getPerformance(PERF_SIX_MONTH, performanceBody)
	oneYear := getPerformance(PERF_ONE_YEAR, performanceBody)
	volThreeYears := getPerformance(VOL_3_YEAR, volatiliteBody)

	score := (0.25 * oneMonth) + (0.3 * threeMonths) + (0.25 * sixMonths) + (0.2 * oneYear) - (0.1 * volThreeYears)
	scoreTruncated := float64(int(score*100)) / 100

	performance = Performance{cleanId, isin, label, category, rating, oneMonth, threeMonths, sixMonths, oneYear, volThreeYears, scoreTruncated, time.Now()}

	PERFORMANCE_CACHE.push(cleanId, performance)

	return &performance, nil
}

func singlePerformanceAsync(morningStarId []byte, ch chan<- PerformanceAsync) {
	performance, err := SinglePerformance(morningStarId)
	ch <- PerformanceAsync{performance, err}
}

func singlePerformanceHandler(w http.ResponseWriter, morningStarId []byte) {
	performance, err := SinglePerformance(morningStarId)

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		jsonHttp.ResponseJson(w, *performance)
	}
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	listBody, err := readBody(r.Body)
	if err != nil {
		http.Error(w, `Error while reading body for list`, 500)
		return
	}

	if len(bytes.TrimSpace(listBody)) == 0 {
		jsonHttp.ResponseJson(w, Results{[0]Performance{}})
		return
	}

	ids := bytes.Split(listBody, []byte(`,`))
	size := len(ids)

	ch := make(chan PerformanceAsync, size)
	for _, id := range ids {
		go singlePerformanceAsync(id, ch)
	}

	results := make([]Performance, 0, size)
	for range ids {
		if performanceAsync := <-ch; performanceAsync.err == nil {
			results = append(results, *performanceAsync.performance)
		}
	}

	jsonHttp.ResponseJson(w, Results{results})
}

type Handler struct {
}

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	urlPath := []byte(r.URL.Path)

	if LIST_REQUEST.Match(urlPath) {
		listHandler(w, r)
	} else if PERF_REQUEST.Match(urlPath) {
		singlePerformanceHandler(w, PERF_REQUEST.FindSubmatch(urlPath)[1])
	}
}
<commit_msg>Update morningStar.go<commit_after>package morningStar

import (
	"../jsonHttp"
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"sync"
	"time"
)

const PERFORMANCE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=1&id=`
const VOLATILITE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=2&id=`
const REFRESH_DELAY = 18

var LIST_REQUEST = regexp.MustCompile(`^/list$`)
var PERF_REQUEST = regexp.MustCompile(`^/(.+?)$`)

var ISIN = regexp.MustCompile(`ISIN.:(\S+)`)
var LABEL = regexp.MustCompile(`<h1[^>]*?>((?:.|\n)*?)</h1>`)
var RATING = regexp.MustCompile(`<span\sclass=".*?stars([0-9]).*?">`)
var CATEGORY = regexp.MustCompile(`<span[^>]*?>Catégorie</span>.*?<span[^>]*?>(.*?)</span>`)
var PERF_ONE_MONTH = regexp.MustCompile(`<td[^>]*?>1 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_THREE_MONTH = regexp.MustCompile(`<td[^>]*?>3 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_SIX_MONTH = regexp.MustCompile(`<td[^>]*?>6 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_ONE_YEAR = regexp.MustCompile(`<td[^>]*?>1 an</td><td[^>]*?>(.*?)</td>`)
var VOL_3_YEAR = regexp.MustCompile(`<td[^>]*?>Ecart-type 3 ans.?</td><td[^>]*?>(.*?)</td>`)

type SyncedMap struct {
	sync.RWMutex
	performances map[string]Performance
}

func (m *SyncedMap) get(key string) (Performance, bool) {
	m.RLock()
	defer m.RUnlock()

	performance, ok := m.performances[key]
	return performance, ok
}

func (m *SyncedMap) push(key string, performance Performance) {
	m.Lock()
	defer m.Unlock()
	m.performances[key] = performance
}

var PERFORMANCE_CACHE = SyncedMap{performances: make(map[string]Performance)}

type Performance struct {
	Id            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonth    float64   `json:"3m"`
	SixMonth      float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

type PerformanceAsync struct {
	performance *Performance
	err         error
}

type Search struct {
	Id    string `json:"i"`
	Label string `json:"n"`
}

type Results struct {
	Results interface{} `json:"results"`
}

func readBody(body io.ReadCloser) ([]byte, error) {
	defer body.Close()
	return ioutil.ReadAll(body)
}

func getBody(url string) ([]byte, error) {
	response, err := http.Get(url)
	if err != nil {
		return nil, errors.New(`Error while retrieving data from ` + url)
	}

	if response.StatusCode >= 400 {
		return nil, errors.New(`Got error ` + strconv.Itoa(response.StatusCode) + ` while getting ` + url)
	}

	body, err := readBody(response.Body)
	if err != nil {
		return nil, errors.New(`Error while reading body of ` + url)
	}

	return body, nil
}

func getLabel(extract *regexp.Regexp, body []byte) []byte {
	match := extract.FindSubmatch(body)
	if match == nil {
		return nil
	}

	return bytes.Replace(match[1], []byte(`&`), []byte(`&`), -1)
}

func getPerformance(extract *regexp.Regexp, body []byte) float64 {
	dotResult := bytes.Replace(getLabel(extract, body), []byte(`,`), []byte(`.`), -1)
	percentageResult := bytes.Replace(dotResult, []byte(`%`), []byte(``), -1)
	trimResult := bytes.TrimSpace(percentageResult)

	result, err := strconv.ParseFloat(string(trimResult), 64)
	if err != nil {
		return 0.0
	}
	return result
}

func SinglePerformance(morningStarId []byte) (*Performance, error) {
	cleanId := string(bytes.ToLower(morningStarId))

	performance, ok := PERFORMANCE_CACHE.get(cleanId)

	if ok && time.Now().Add(time.Hour*-REFRESH_DELAY).Before(performance.Update) {
		return &performance, nil
	}

	performanceBody, err := getBody(PERFORMANCE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	volatiliteBody, err := getBody(VOLATILITE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	isin := string(getLabel(ISIN, performanceBody))
	label := string(getLabel(LABEL, performanceBody))
	rating := string(getLabel(RATING, performanceBody))
	category := string(getLabel(CATEGORY, performanceBody))
	oneMonth := getPerformance(PERF_ONE_MONTH, performanceBody)
	threeMonths := getPerformance(PERF_THREE_MONTH, performanceBody)
	sixMonths := getPerformance(PERF_SIX_MONTH, performanceBody)
	oneYear := getPerformance(PERF_ONE_YEAR, performanceBody)
	volThreeYears := getPerformance(VOL_3_YEAR, volatiliteBody)

	score := (0.25 * oneMonth) + (0.3 * threeMonths) + (0.25 * sixMonths) + (0.2 * oneYear) - (0.1 * volThreeYears)
	scoreTruncated := float64(int(score*100)) / 100

	performance = Performance{cleanId, isin, label, category, rating, oneMonth, threeMonths, sixMonths, oneYear, volThreeYears, scoreTruncated, time.Now()}

	PERFORMANCE_CACHE.push(cleanId, performance)

	return &performance, nil
}

func singlePerformanceAsync(morningStarId []byte, ch chan<- PerformanceAsync) {
	performance, err := SinglePerformance(morningStarId)
	ch <- PerformanceAsync{performance, err}
}

func singlePerformanceHandler(w http.ResponseWriter, morningStarId []byte) {
	performance, err := SinglePerformance(morningStarId)

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		jsonHttp.ResponseJson(w, *performance)
	}
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	listBody, err := readBody(r.Body)
	if err != nil {
		http.Error(w, `Error while reading body for list`, 500)
		return
	}

	if len(bytes.TrimSpace(listBody)) == 0 {
		jsonHttp.ResponseJson(w, Results{[0]Performance{}})
		return
	}

	ids := bytes.Split(listBody, []byte(`,`))
	size := len(ids)

	ch := make(chan PerformanceAsync, size)
	for _, id := range ids {
		go singlePerformanceAsync(id, ch)
	}

	results := make([]Performance, 0, size)
	for range ids {
		if performanceAsync := <-ch; performanceAsync.err == nil {
			results = append(results, *performanceAsync.performance)
		}
	}

	jsonHttp.ResponseJson(w, Results{results})
}

type Handler struct {
}

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	urlPath := []byte(r.URL.Path)

	if LIST_REQUEST.Match(urlPath) {
		listHandler(w, r)
	} else if PERF_REQUEST.Match(urlPath) {
		singlePerformanceHandler(w, PERF_REQUEST.FindSubmatch(urlPath)[1])
	}
}
<|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package channels

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

	"github.com/ikravets/errs"
)

type Config interface {
	LoadFromReader(rd io.Reader) error
	LoadFromStr(nameOrAddr string) error
	Addrs() []string
}

type config struct {
	addrs []string
}

var _ Config = &config{}

func NewConfig() Config {
	return &config{}
}

func (c *config) LoadFromReader(rd io.Reader) (err error) {
	defer errs.PassE(&err)
	all, err := ioutil.ReadAll(rd)
	errs.CheckE(err)
	for _, str := range strings.Fields(string(all)) {
		errs.CheckE(c.LoadFromStr(str))
	}
	return
}

func ParseChannel(channel string) (ch *net.UDPAddr, subch int, err error) {
	defer errs.PassE(&err)
	subch = -1
	if fields := strings.Split(channel, ":"); len(fields) == 3 {
		ch, err = net.ResolveUDPAddr("udp", strings.Join(fields[:2], ":"))
		errs.CheckE(err)
		subch, err = strconv.Atoi(fields[2])
	} else {
		ch, err = net.ResolveUDPAddr("udp", channel)
		errs.CheckE(err)
	}
	return
}

func (c *config) LoadFromStr(nameOrAddr string) (err error) {
	switch nameOrAddr {
	case "nasdaq":
		for i := 0; i < 4; i++ {
			c.addAddr(fmt.Sprintf("233.54.12.%d:%d", 1+i, 18001+i))
		}
	case "bats":
		for i := 0; i < 32; i++ {
			c.addAddr(fmt.Sprintf("224.0.131.%d:%d", i/4, 30101+i))
		}
	case "bats-b":
		for i := 0; i < 32; i++ {
			c.addAddr(fmt.Sprintf("233.130.124.%d:%d", i/4, 30101+i))
		}
	case "miax":
		for i := 0; i < 24; i++ {
			c.addAddr(fmt.Sprintf("224.0.105.%d:%d", 1+i, 51001+i))
		}
	default:
		_, _, err = ParseChannel(nameOrAddr)
		c.addAddr(nameOrAddr)
	}
	return
}

func (c *config) Addrs() []string {
	return c.addrs
}

func (c *config) addAddr(addr string) {
	c.addrs = append(c.addrs, addr)
	return
}
<commit_msg>channels: add subchannel flow<commit_after>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package channels

import (
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"strconv"
	"strings"

	"github.com/google/gopacket"
	"github.com/ikravets/errs"
)

type Config interface {
	LoadFromReader(rd io.Reader) error
	LoadFromStr(nameOrAddr string) error
	Addrs() []string
}

type config struct {
	addrs []string
}

var _ Config = &config{}

func NewConfig() Config {
	return &config{}
}

func (c *config) LoadFromReader(rd io.Reader) (err error) {
	defer errs.PassE(&err)
	all, err := ioutil.ReadAll(rd)
	errs.CheckE(err)
	for _, str := range strings.Fields(string(all)) {
		errs.CheckE(c.LoadFromStr(str))
	}
	return
}

func ParseChannel(channel string) (ch *net.UDPAddr, subch int, err error) {
	defer errs.PassE(&err)
	subch = -1
	if fields := strings.Split(channel, ":"); len(fields) == 3 {
		ch, err = net.ResolveUDPAddr("udp", strings.Join(fields[:2], ":"))
		errs.CheckE(err)
		subch, err = strconv.Atoi(fields[2])
	} else {
		ch, err = net.ResolveUDPAddr("udp", channel)
		errs.CheckE(err)
	}
	return
}

func (c *config) LoadFromStr(nameOrAddr string) (err error) {
	switch nameOrAddr {
	case "nasdaq":
		for i := 0; i < 4; i++ {
			c.addAddr(fmt.Sprintf("233.54.12.%d:%d", 1+i, 18001+i))
		}
	case "bats":
		for i := 0; i < 32; i++ {
			c.addAddr(fmt.Sprintf("224.0.131.%d:%d", i/4, 30101+i))
		}
	case "bats-b":
		for i := 0; i < 32; i++ {
			c.addAddr(fmt.Sprintf("233.130.124.%d:%d", i/4, 30101+i))
		}
	case "miax":
		for i := 0; i < 24; i++ {
			c.addAddr(fmt.Sprintf("224.0.105.%d:%d", 1+i, 51001+i))
		}
	default:
		_, _, err = ParseChannel(nameOrAddr)
		c.addAddr(nameOrAddr)
	}
	return
}

func (c *config) Addrs() []string {
	return c.addrs
}

func (c *config) addAddr(addr string) {
	c.addrs = append(c.addrs, addr)
	return
}

var EndpointSubchannelMetadata = gopacket.EndpointTypeMetadata{"subchannel", func(b []byte) string {
	return strconv.Itoa(int(binary.LittleEndian.Uint32(b)))
}}
var EndpointSubchannel = gopacket.RegisterEndpointType(13000, EndpointSubchannelMetadata)

func SubchannelFlow(s int) gopacket.Flow {
	b := make([]byte, 4)
	binary.LittleEndian.PutUint32(b, uint32(s))
	return gopacket.NewFlow(EndpointSubchannel, nil, b)
}
<|endoftext|>


<commit_before>package jig

import (
	//"errors"
	"fmt"
	"github.com/bitly/go-simplejson"
	"github.com/nsf/termbox-go"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

const (
	PROMPT              = "[jig]>> "
	DefaultY     int    = 1
	FilterPrompt string = "[Filter]> "
)

var (
	f        *[]rune
	complete *[]rune
)

type Engine struct {
	json       *simplejson.Json
	orgJson    *simplejson.Json
	currenKeys []string
	query      bool
	pretty     bool
}

func NewEngine(s *os.File, q bool, p bool) *Engine {
	j := parse(s)
	e := &Engine{
		json:       j,
		orgJson:    j,
		currenKeys: []string{},
		query:      q,
		pretty:     p,
	}
	return e
}

func (e Engine) Run() int {

	if !e.render(e.json) {
		return 2
	}
	if e.query {
		fmt.Printf("%s", string(*f))
	} else if e.pretty {
		s, err := e.json.EncodePretty()
		if err != nil {
			return 1
		}
		fmt.Printf("%s", string(s))
	} else {
		s, err := e.json.Encode()
		if err != nil {
			return 1
		}
		fmt.Printf("%s", s)
	}
	return 0
}

func parse(content *os.File) *simplejson.Json {
	buf, err := ioutil.ReadAll(content)

	if err != nil {
		log.Fatal(err)
	}

	js, err := simplejson.NewJson(buf)

	if err != nil {
		log.Fatal(err)
	}

	return js
}

// fix:me
func (e *Engine) render(json *simplejson.Json) bool {

	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	f = &[]rune{}
	complete = &[]rune{}

	contents := e.prettyContents()
	keymode := false

	for {
		e.filterJson(string(*f))
		e.suggest()
		if keymode {
			contents = e.currenKeys
		} else {
			contents = e.prettyContents()
		}
		draw(contents)
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc, termbox.KeyCtrlC:
				return false
			case termbox.KeyCtrlK:
				keymode = !keymode
			case termbox.KeySpace:
				*f = append(*f, rune(' '))
			case termbox.KeyCtrlW:
				//delete whole word to period
				s := string(*f)
				kws := strings.Split(s, ".")
				lki := len(kws) - 1
				_, kws = kws[lki], kws[:lki]
				s = strings.Join(kws, ".")
				*f = []rune(s[0:len(s)])
			case termbox.KeyBackspace, termbox.KeyBackspace2:
				if i := len(*f) - 1; i >= 0 {
					slice := *f
					*f = slice[0:i]
				}
			case termbox.KeyTab:
				if len(*complete) > 0 {
					e.autoComplete()
				} else {
					e.suggest()
				}
			case termbox.KeyEnter:
				return true
			case 0:
				*f = append(*f, rune(ev.Ch))
			default:
			}
		case termbox.EventError:
			panic(ev.Err)
			break
		default:
		}
	}
}

func (e *Engine) autoComplete() {
	*f = append(*f, *complete...)
	*complete = []rune("")
}

func (e *Engine) suggest() bool {
	s := string(*f)
	if arr, _ := e.json.Array(); arr != nil {
		le := s[len(s)-1:]
		if le == "." {
			*complete = []rune("")
			return false
		}
		var rs string
		ds := regexp.MustCompile("\\[([0-9]*)?\\]?$").FindString(s)
		if len(arr) > 1 {
			if ds == "" {
				rs = "["
			} else if le != "]" {
				rs = "]"
			}
		} else {
			rs = "[0]"
		}
		cs := strings.Replace(rs, ds, "", -1)
		*complete = []rune(cs)
		return true
	}
	kws := strings.Split(s, ".")
	lki := len(kws) - 1
	if lki == 0 {
		return false
	}
	lkw, tkws := kws[lki], kws[:lki]

	re, err := regexp.Compile("(?i)^" + lkw)
	if err != nil {
		return false
	}
	m := e.getFilteredCurrentKeys(lkw)

	if len(m) == 1 {
		for k := range m {
			kw := re.ReplaceAllString(e.currenKeys[k], "")
			*complete = []rune(kw)
			s = strings.Join(tkws, ".") + "." + m[k]
		}
		*f = []rune(s)
		return true
	}
	//	else {
	//		km := map[string]int{}
	//		var sw string
	//		for k, v := range m {
	//			sw = v
	//			km[v] = k
	//		}
	//		if len(km) == 1 {
	//			kw := re.ReplaceAllString(sw, "")
	//			*complete = []rune(kw)
	//			s = strings.Join(tkws, ".") + "." + lkw
	//		}
	//	}
	*complete = []rune("")
	return false
}

func (e *Engine) getFilteredCurrentKeys(kw string) map[int]string {
	m := map[int]string{}

	re, err := regexp.Compile("(?i)^" + kw)
	if err != nil {
		return m
	}

	for i, k := range e.currenKeys {
		if str := re.FindString(k); str != "" {
			m[i] = str
		}
	}

	return m
}

func (e *Engine) prettyContents() []string {
	s, _ := e.json.EncodePretty()
	return strings.Split(string(s), "\n")
}

func (e *Engine) filterJson(q string) {
	e.json = e.orgJson
	e.setCurrentKeys()
	if len(q) < 1 {
		return
	}
	keywords := strings.Split(q, ".")

	// check start "."
	if keywords[0] != "" {
		return
	}

	keywords = keywords[1:]

	re := regexp.MustCompile("\\[[0-9]*\\]")
	delre := regexp.MustCompile("\\[([0-9]+)?")

	j := e.json
	lastIdx := len(keywords) - 1

	//eachFlg := false
	for ki, keyword := range keywords {
		if len(keyword) == 0 {
			if ki != lastIdx {
				e.json = simplejson.New()
			}
			break
		}
		// abc[0]
		if keyword[:1] == "[" {
			break
		}
		if keyword[len(keyword)-1:] == "]" {
			matchIndexes := re.FindAllStringIndex(keyword, -1)
			kw := re.ReplaceAllString(keyword, "")

			tj := j.Get(kw)
			if ki != lastIdx {
				j = tj
			} else if !isEmptyJson(tj) {
				j = tj
			}
			lmi := len(matchIndexes) - 1
			for idx, m := range matchIndexes {
				i, _ := strconv.Atoi(keyword[m[0]+1 : m[1]-1])
				if idx == lmi && m[1]-m[0] == 2 {
					//eachFlg = true
				} else if tj := j.GetIndex(i); !isEmptyJson(tj) {
					j = tj
				}
			}
		} else {
			kw := delre.ReplaceAllString(keyword, "")
			tj := j.Get(kw)
			if ki != lastIdx {
				j = tj
				// kokoni
			} else if len(e.getFilteredCurrentKeys(kw)) < 1 {
				j = tj
			}
		}
	}
	e.json = j
	e.setCurrentKeys()
}

func (e *Engine) setCurrentKeys() {
	m, err := e.json.Map()
	if err != nil {
		// is array
	}
	keys := []string{}
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	e.currenKeys = keys
}

func isEmptyJson(j *simplejson.Json) bool {
	switch j.Interface().(type) {
	case nil:
		return true
	default:
		return false
	}
}

func draw(rows []string) {

	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	fs := FilterPrompt + string(*f)
	cs := string(*complete)
	drawln(0, 0, fs+cs, []([]int){[]int{len(fs), len(fs) + len(cs)}})
	termbox.SetCursor(len(fs), 0)

	for idx, row := range rows {
		drawln(0, idx+DefaultY, row, nil)
	}

	termbox.Flush()
}

func drawln(x int, y int, str string, matches [][]int) {
	color := termbox.ColorDefault
	backgroundColor := termbox.ColorDefault

	var c termbox.Attribute
	for i, s := range str {
		c = color
		for _, match := range matches {
			if i >= match[0] && i < match[1] {
				c = termbox.ColorGreen
			}
		}
		termbox.SetCell(x+i, y, s, c, backgroundColor)
	}
}
<commit_msg> 0.2.3 improve suggest & filter interface<commit_after>package jig

import (
	//"errors"
	"fmt"
	"github.com/bitly/go-simplejson"
	"github.com/nsf/termbox-go"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

const (
	PROMPT              = "[jig]>> "
	DefaultY     int    = 1
	FilterPrompt string = "[Filter]> "
)

var (
	f        *[]rune
	complete *[]rune
)

type Engine struct {
	json        *simplejson.Json
	orgJson     *simplejson.Json
	currentKeys []string
	query       bool
	pretty      bool
}

func NewEngine(s *os.File, q bool, p bool) *Engine {
	j := parse(s)
	e := &Engine{
		json:        j,
		orgJson:     j,
		currentKeys: []string{},
		query:       q,
		pretty:      p,
	}
	return e
}

func (e Engine) Run() int {

	if !e.render(e.json) {
		return 2
	}
	if e.query {
		fmt.Printf("%s", string(*f))
	} else if e.pretty {
		s, err := e.json.EncodePretty()
		if err != nil {
			return 1
		}
		fmt.Printf("%s", string(s))
	} else {
		s, err := e.json.Encode()
		if err != nil {
			return 1
		}
		fmt.Printf("%s", s)
	}
	return 0
}

func parse(content *os.File) *simplejson.Json {
	buf, err := ioutil.ReadAll(content)

	if err != nil {
		log.Fatal(err)
	}

	js, err := simplejson.NewJson(buf)

	if err != nil {
		log.Fatal(err)
	}

	return js
}

// fix:me
func (e *Engine) render(json *simplejson.Json) bool {

	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	f = &[]rune{}
	complete = &[]rune{}

	contents := e.prettyContents()
	keymode := false

	for {
		e.filterJson(string(*f))
		e.suggest()
		if keymode {
			contents = e.currentKeys
		} else {
			contents = e.prettyContents()
		}
		draw(contents)
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyEsc, termbox.KeyCtrlC:
				return false
			case termbox.KeyCtrlK:
				keymode = !keymode
			case termbox.KeySpace:
				*f = append(*f, rune(' '))
			case termbox.KeyCtrlW:
				//delete whole word to period
				s := string(*f)
				kws := strings.Split(s, ".")
				lki := len(kws) - 1
				_, kws = kws[lki], kws[:lki]
				s = strings.Join(kws, ".")
				*f = []rune(s[0:len(s)])
			case termbox.KeyBackspace, termbox.KeyBackspace2:
				if i := len(*f) - 1; i >= 0 {
					slice := *f
					*f = slice[0:i]
				}
			case termbox.KeyTab:
				if len(*complete) > 0 {
					e.autoComplete()
				} else {
					e.suggest()
				}
			case termbox.KeyEnter:
				return true
			case 0:
				*f = append(*f, rune(ev.Ch))
			default:
			}
		case termbox.EventError:
			panic(ev.Err)
			break
		default:
		}
	}
}

func (e *Engine) autoComplete() {
	*f = append(*f, *complete...)
	*complete = []rune("")
}

func (e *Engine) suggest() bool {
	s := string(*f)
	if arr, _ := e.json.Array(); arr != nil {
		le := s[len(s)-1:]
		if le == "." {
			*complete = []rune("")
			return false
		}
		var rs string
		ds := regexp.MustCompile("\\[([0-9]*)?\\]?$").FindString(s)
		if len(arr) > 1 {
			if ds == "" {
				rs = "["
			} else if le != "]" {
				rs = "]"
			}
		} else {
			rs = "[0]"
		}
		cs := strings.Replace(rs, ds, "", -1)
		*complete = []rune(cs)
		return true
	}
	kws := strings.Split(s, ".")
	lki := len(kws) - 1
	if lki == 0 {
		return false
	}
	lkw, tkws := kws[lki], kws[:lki]

	re, err := regexp.Compile("(?i)^" + lkw)
	if err != nil {
		return false
	}
	m := e.getFilteredCurrentKeys(lkw)

	if len(m) == 1 {
		for k := range m {
			kw := re.ReplaceAllString(e.currentKeys[k], "")
			*complete = []rune(kw)
			s = strings.Join(tkws, ".") + "." + m[k]
		}
		*f = []rune(s)
		return true
	}
	//	else {
	//		km := map[string]int{}
	//		var sw string
	//		for k, v := range m {
	//			sw = v
	//			km[v] = k
	//		}
	//		if len(km) == 1 {
	//			kw := re.ReplaceAllString(sw, "")
	//			*complete = []rune(kw)
	//			s = strings.Join(tkws, ".") + "." + lkw
	//		}
	//	}
	*complete = []rune("")
	return false
}

func (e *Engine) getFilteredCurrentKeys(kw string) map[int]string {
	m := map[int]string{}

	re, err := regexp.Compile("(?i)^" + kw)
	if err != nil {
		return m
	}

	for i, k := range e.currentKeys {
		if str := re.FindString(k); str != "" {
			m[i] = str
		}
	}
	return m
}

func (e *Engine) prettyContents() []string {
	s, _ := e.json.EncodePretty()
	return strings.Split(string(s), "\n")
}

func (e *Engine) filterJson(q string) {
	json := e.orgJson
	e.setCurrentKeys()
	if len(q) < 1 {
		e.json = json
		return
	}
	keywords := strings.Split(q, ".")

	// check start "."
	if keywords[0] != "" {
		e.json = &simplejson.Json{}
		return
	}

	keywords = keywords[1:]

	re := regexp.MustCompile("\\[[0-9]*\\]")
	delre := regexp.MustCompile("\\[([0-9]+)?")

	j := json
	lastIdx := len(keywords) - 1

	//eachFlg := false
	for ki, keyword := range keywords {
		if len(keyword) == 0 {
			if ki != lastIdx {
				e.json = &simplejson.Json{}
			}
			break
		}
		// abc[0]
		if keyword[:1] == "[" {
			break
		}
		if keyword[len(keyword)-1:] == "]" {
			matchIndexes := re.FindAllStringIndex(keyword, -1)
			kw := re.ReplaceAllString(keyword, "")

			tj := j.Get(kw)
			if ki != lastIdx {
				j = tj
			} else if !isEmptyJson(tj) {
				j = tj
			}
			lmi := len(matchIndexes) - 1
			for idx, m := range matchIndexes {
				i, _ := strconv.Atoi(keyword[m[0]+1 : m[1]-1])
				if idx == lmi && m[1]-m[0] == 2 {
					//eachFlg = true
				} else if tj := j.GetIndex(i); !isEmptyJson(tj) {
					j = tj
				}
			}
		} else {
			kw := delre.ReplaceAllString(keyword, "")
			tj := j.Get(kw)
			if ki != lastIdx {
				j = tj
				// kokoni
			} else if len(e.getFilteredCurrentKeys(kw)) < 1 {
				j = tj
			} else if !isEmptyJson(tj) {
				j = tj
			}
		}
	}
	e.json = j
	e.setCurrentKeys()
}

func (e *Engine) setCurrentKeys() {
	m, err := e.json.Map()
	if err != nil {
		// is array
	}
	keys := []string{}
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	e.currentKeys = keys
}

func isEmptyJson(j *simplejson.Json) bool {
	switch j.Interface().(type) {
	case nil:
		return true
	default:
		return false
	}
}

func draw(rows []string) {

	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	fs := FilterPrompt + string(*f)
	cs := string(*complete)
	drawln(0, 0, fs+cs, []([]int){[]int{len(fs), len(fs) + len(cs)}})
	termbox.SetCursor(len(fs), 0)

	for idx, row := range rows {
		drawln(0, idx+DefaultY, row, nil)
	}

	termbox.Flush()
}

func drawln(x int, y int, str string, matches [][]int) {
	color := termbox.ColorDefault
	backgroundColor := termbox.ColorDefault

	var c termbox.Attribute
	for i, s := range str {
		c = color
		for _, match := range matches {
			if i >= match[0] && i < match[1] {
				c = termbox.ColorGreen
			}
		}
		termbox.SetCell(x+i, y, s, c, backgroundColor)
	}
}
<|endoftext|>


<commit_before>package citadel

import (
	"crypto/tls"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/samalba/dockerclient"
)

type Engine struct {
	ID     string   `json:"id,omitempty"`
	Addr   string   `json:"addr,omitempty"`
	Cpus   float64  `json:"cpus,omitempty"`
	Memory float64  `json:"memory,omitempty"`
	Labels []string `json:"labels,omitempty"`

	client       *dockerclient.DockerClient
	eventHandler EventHandler
}

func (e *Engine) Connect(config *tls.Config) error {
	c, err := dockerclient.NewDockerClient(e.Addr, config)
	if err != nil {
		return err
	}

	e.client = c

	return nil
}

func (e *Engine) SetClient(c *dockerclient.DockerClient) {
	e.client = c
}

// IsConnected returns true if the engine is connected to a remote docker API
func (e *Engine) IsConnected() bool {
	return e.client != nil
}

func (e *Engine) Pull(image string) error {
	if err := e.client.PullImage(image); err != nil {
		return err
	}
	return nil
}

func (e *Engine) Start(c *Container, pullImage bool) error {
	var (
		err    error
		env    = []string{}
		client = e.client
		i      = c.Image
	)
	c.Engine = e

	for k, v := range i.Environment {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}

	env = append(env,
		fmt.Sprintf("_citadel_type=%s", i.Type),
		fmt.Sprintf("_citadel_labels=%s", strings.Join(i.Labels, ",")),
	)

	vols := make(map[string]struct{})
	binds := []string{}
	for _, v := range i.Volumes {
		if strings.Index(v, ":") > -1 {
			cv := strings.Split(v, ":")
			binds = append(binds, v)
			v = cv[1]
		}
		vols[v] = struct{}{}
	}
	config := &dockerclient.ContainerConfig{
		Hostname:     i.Hostname,
		Domainname:   i.Domainname,
		Image:        i.Name,
		Cmd:          i.Args,
		Memory:       int64(i.Memory) * 1024 * 1024,
		Env:          env,
		CpuShares:    int64(i.Cpus * 100.0 / e.Cpus),
		ExposedPorts: make(map[string]struct{}),
		Volumes:      vols,
	}

	links := []string{}
	for k, v := range i.Links {
		links = append(links, fmt.Sprintf("%s:%s", k, v))
	}
	hostConfig := &dockerclient.HostConfig{
		PublishAllPorts: i.Publish,
		PortBindings:    make(map[string][]dockerclient.PortBinding),
		Links:           links,
		Binds:           binds,
		RestartPolicy: dockerclient.RestartPolicy{
			Name:              i.RestartPolicy.Name,
			MaximumRetryCount: i.RestartPolicy.MaximumRetryCount,
		},
		NetworkMode: i.NetworkMode,
	}

	for _, b := range i.BindPorts {
		key := fmt.Sprintf("%d/%s", b.ContainerPort, b.Proto)
		config.ExposedPorts[key] = struct{}{}

		hostConfig.PortBindings[key] = []dockerclient.PortBinding{
			{
				HostIp:   b.HostIp,
				HostPort: fmt.Sprint(b.Port),
			},
		}
	}

	if pullImage {
		if err := e.Pull(i.Name); err != nil {
			return err
		}
	}

	if c.ID, err = client.CreateContainer(config, c.Name); err != nil {
		return err
	}

	if err := client.StartContainer(c.ID, hostConfig); err != nil {
		return err
	}

	return e.updatePortInformation(c)
}

func (e *Engine) ListImages() ([]string, error) {
	images, err := e.client.ListImages()
	if err != nil {
		return nil, err
	}

	out := []string{}

	for _, i := range images {
		for _, t := range i.RepoTags {
			out = append(out, t)
		}
	}

	return out, nil
}

func (e *Engine) updatePortInformation(c *Container) error {
	info, err := e.client.InspectContainer(c.ID)
	if err != nil {
		return err
	}

	return parsePortInformation(info, c)
}

func (e *Engine) ListContainers(all bool) ([]*Container, error) {
	out := []*Container{}

	c, err := e.client.ListContainers(all, true, "")
	if err != nil {
		return nil, err
	}

	for _, ci := range c {
		cc, err := FromDockerContainer(ci.Id, ci.Image, e)
		if err != nil {
			return nil, err
		}

		out = append(out, cc)
	}

	return out, nil
}

func (e *Engine) Logs(container *Container, stdout bool, stderr bool) (io.ReadCloser, error) {
	logopts := &dockerclient.LogOptions{
		Stdout:     stdout,
		Stderr:     stderr,
		Timestamps: false,
		Follow:     false,
		Tail:       0,
	}
	return e.client.ContainerLogs(container.ID, logopts)
}

func (e *Engine) Kill(container *Container, sig int) error {
	return e.client.KillContainer(container.ID)
}

func (e *Engine) Stop(container *Container) error {
	return e.client.StopContainer(container.ID, 8)
}

func (e *Engine) Restart(container *Container, timeout int) error {
	return e.client.RestartContainer(container.ID, timeout)
}

func (e *Engine) Remove(container *Container) error {
	return e.client.RemoveContainer(container.ID, true)
}

func (e *Engine) Events(h EventHandler) error {
	if e.eventHandler != nil {
		return fmt.Errorf("event handler already set")
	}
	e.eventHandler = h

	e.client.StartMonitorEvents(e.handler)

	return nil
}

func (e *Engine) String() string {
	return fmt.Sprintf("engine %s addr %s", e.ID, e.Addr)
}

func (e *Engine) handler(ev *dockerclient.Event, args ...interface{}) {
	event := &Event{
		Engine: e,
		Type:   ev.Status,
		Time:   time.Unix(int64(ev.Time), 0),
	}

	container, err := FromDockerContainer(ev.Id, ev.From, e)
	if err != nil {
		// TODO: un fuck this shit, fuckin handler
		return
	}

	event.Container = container

	e.eventHandler.Handle(event)
}
<commit_msg>added version to engine<commit_after>package citadel

import (
	"crypto/tls"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/samalba/dockerclient"
)

type Engine struct {
	ID     string   `json:"id,omitempty"`
	Addr   string   `json:"addr,omitempty"`
	Cpus   float64  `json:"cpus,omitempty"`
	Memory float64  `json:"memory,omitempty"`
	Labels []string `json:"labels,omitempty"`

	client       *dockerclient.DockerClient
	eventHandler EventHandler
}

func (e *Engine) Connect(config *tls.Config) error {
	c, err := dockerclient.NewDockerClient(e.Addr, config)
	if err != nil {
		return err
	}

	e.client = c

	return nil
}

func (e *Engine) SetClient(c *dockerclient.DockerClient) {
	e.client = c
}

// IsConnected returns true if the engine is connected to a remote docker API
func (e *Engine) IsConnected() bool {
	return e.client != nil
}

func (e *Engine) Pull(image string) error {
	if err := e.client.PullImage(image); err != nil {
		return err
	}
	return nil
}

func (e *Engine) Start(c *Container, pullImage bool) error {
	var (
		err    error
		env    = []string{}
		client = e.client
		i      = c.Image
	)
	c.Engine = e

	for k, v := range i.Environment {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}

	env = append(env,
		fmt.Sprintf("_citadel_type=%s", i.Type),
		fmt.Sprintf("_citadel_labels=%s", strings.Join(i.Labels, ",")),
	)

	vols := make(map[string]struct{})
	binds := []string{}
	for _, v := range i.Volumes {
		if strings.Index(v, ":") > -1 {
			cv := strings.Split(v, ":")
			binds = append(binds, v)
			v = cv[1]
		}
		vols[v] = struct{}{}
	}
	config := &dockerclient.ContainerConfig{
		Hostname:     i.Hostname,
		Domainname:   i.Domainname,
		Image:        i.Name,
		Cmd:          i.Args,
		Memory:       int64(i.Memory) * 1024 * 1024,
		Env:          env,
		CpuShares:    int64(i.Cpus * 100.0 / e.Cpus),
		ExposedPorts: make(map[string]struct{}),
		Volumes:      vols,
	}

	links := []string{}
	for k, v := range i.Links {
		links = append(links, fmt.Sprintf("%s:%s", k, v))
	}
	hostConfig := &dockerclient.HostConfig{
		PublishAllPorts: i.Publish,
		PortBindings:    make(map[string][]dockerclient.PortBinding),
		Links:           links,
		Binds:           binds,
		RestartPolicy: dockerclient.RestartPolicy{
			Name:              i.RestartPolicy.Name,
			MaximumRetryCount: i.RestartPolicy.MaximumRetryCount,
		},
		NetworkMode: i.NetworkMode,
	}

	for _, b := range i.BindPorts {
		key := fmt.Sprintf("%d/%s", b.ContainerPort, b.Proto)
		config.ExposedPorts[key] = struct{}{}

		hostConfig.PortBindings[key] = []dockerclient.PortBinding{
			{
				HostIp:   b.HostIp,
				HostPort: fmt.Sprint(b.Port),
			},
		}
	}

	if pullImage {
		if err := e.Pull(i.Name); err != nil {
			return err
		}
	}

	if c.ID, err = client.CreateContainer(config, c.Name); err != nil {
		return err
	}

	if err := client.StartContainer(c.ID, hostConfig); err != nil {
		return err
	}

	return e.updatePortInformation(c)
}

func (e *Engine) ListImages() ([]string, error) {
	images, err := e.client.ListImages()
	if err != nil {
		return nil, err
	}

	out := []string{}

	for _, i := range images {
		for _, t := range i.RepoTags {
			out = append(out, t)
		}
	}

	return out, nil
}

func (e *Engine) updatePortInformation(c *Container) error {
	info, err := e.client.InspectContainer(c.ID)
	if err != nil {
		return err
	}

	return parsePortInformation(info, c)
}

func (e *Engine) ListContainers(all bool) ([]*Container, error) {
	out := []*Container{}

	c, err := e.client.ListContainers(all, true, "")
	if err != nil {
		return nil, err
	}

	for _, ci := range c {
		cc, err := FromDockerContainer(ci.Id, ci.Image, e)
		if err != nil {
			return nil, err
		}

		out = append(out, cc)
	}

	return out, nil
}

func (e *Engine) Logs(container *Container, stdout bool, stderr bool) (io.ReadCloser, error) {
	logopts := &dockerclient.LogOptions{
		Stdout:     stdout,
		Stderr:     stderr,
		Timestamps: false,
		Follow:     false,
		Tail:       0,
	}
	return e.client.ContainerLogs(container.ID, logopts)
}

func (e *Engine) Kill(container *Container, sig int) error {
	return e.client.KillContainer(container.ID)
}

func (e *Engine) Stop(container *Container) error {
	return e.client.StopContainer(container.ID, 8)
}

func (e *Engine) Restart(container *Container, timeout int) error {
	return e.client.RestartContainer(container.ID, timeout)
}

func (e *Engine) Remove(container *Container) error {
	return e.client.RemoveContainer(container.ID, true)
}

func (e *Engine) Version() (*dockerclient.Version, error) {
	return e.client.Version()
}

func (e *Engine) Events(h EventHandler) error {
	if e.eventHandler != nil {
		return fmt.Errorf("event handler already set")
	}
	e.eventHandler = h

	e.client.StartMonitorEvents(e.handler)

	return nil
}

func (e *Engine) String() string {
	return fmt.Sprintf("engine %s addr %s", e.ID, e.Addr)
}

func (e *Engine) handler(ev *dockerclient.Event, args ...interface{}) {
	event := &Event{
		Engine: e,
		Type:   ev.Status,
		Time:   time.Unix(int64(ev.Time), 0),
	}

	container, err := FromDockerContainer(ev.Id, ev.From, e)
	if err != nil {
		// TODO: un fuck this shit, fuckin handler
		return
	}

	event.Container = container

	e.eventHandler.Handle(event)
}
<|endoftext|>


<commit_before><commit_msg>First constructor passing tests<commit_after><|endoftext|>


<commit_before>package rpc

import (
	"fmt"
)

type RecoverableError interface {
	error
	CanRecover() bool
}

type BasicRPCError struct{}

func (BasicRPCError) CanRecover() bool { return false }

type RPCErrorWrapper struct {
	BasicRPCError
	error
}

func newRPCErrorWrapper(err error) RPCErrorWrapper {
	return RPCErrorWrapper{error: err}
}

type PacketizerError struct {
	BasicRPCError
	msg string
}

func (p PacketizerError) Error() string {
	return "packetizer error: " + p.msg
}

func NewPacketizerError(d string, a ...interface{}) PacketizerError {
	return PacketizerError{msg: fmt.Sprintf(d, a...)}
}

type DispatcherError struct {
	BasicRPCError
	msg string
}

func (p DispatcherError) Error() string {
	return "dispatcher error: " + p.msg
}

func NewDispatcherError(d string, a ...interface{}) DispatcherError {
	return DispatcherError{msg: fmt.Sprintf(d, a...)}
}

type ReceiverError struct {
	BasicRPCError
	msg string
}

func (p ReceiverError) Error() string {
	return "dispatcher error: " + p.msg
}

func NewReceiverError(d string, a ...interface{}) ReceiverError {
	return ReceiverError{msg: fmt.Sprintf(d, a...)}
}

type MethodNotFoundError struct {
	BasicRPCError
	p string
	m string
}

func newMethodNotFoundError(p, m string) MethodNotFoundError {
	return MethodNotFoundError{
		p: p,
		m: m,
	}
}

func (m MethodNotFoundError) Error() string {
	return fmt.Sprintf("method '%s' not found in protocol '%s'", m.m, m.p)
}

type ProtocolNotFoundError struct {
	BasicRPCError
	p string
}

func newProtocolNotFoundError(p string) ProtocolNotFoundError {
	return ProtocolNotFoundError{p: p}
}

func (p ProtocolNotFoundError) Error() string {
	return "protocol not found: " + p.p
}

type AlreadyRegisteredError struct {
	BasicRPCError
	p string
}

func newAlreadyRegisteredError(p string) AlreadyRegisteredError {
	return AlreadyRegisteredError{p: p}
}

func (a AlreadyRegisteredError) Error() string {
	return a.p + ": protocol already registered"
}

type TypeError struct {
	BasicRPCError
	p string
}

func (t TypeError) Error() string {
	return t.p
}

func NewTypeError(expected, actual interface{}) TypeError {
	return TypeError{p: fmt.Sprintf("Invalid type for arguments. Expected: %T, actual: %T", expected, actual)}
}

type CallNotFoundError struct {
	BasicRPCError
	seqno seqNumber
}

func newCallNotFoundError(s seqNumber) CallNotFoundError {
	return CallNotFoundError{seqno: s}
}

func (c CallNotFoundError) Error() string {
	return fmt.Sprintf("Call not found for sequence number %d", c.seqno)
}

func (CallNotFoundError) CanRecover() bool { return true }

type NilResultError struct {
	BasicRPCError
	seqno seqNumber
}

func (c NilResultError) Error() string {
	return fmt.Sprintf("Nil result supplied for sequence number %d", c.seqno)
}

type RPCDecodeError struct {
	typ MethodType
	len int
	err error
}

func (r RPCDecodeError) CanRecover() bool {
	if err, ok := r.err.(RecoverableError); ok {
		return err.CanRecover()
	}
	return false
}

func (r RPCDecodeError) Error() string {
	return fmt.Sprintf("RPC error: type %d, length %d, error: %v", r.typ, r.len, r.err)
}

func newRPCDecodeError(t MethodType, l int, e error) RPCDecodeError {
	return RPCDecodeError{
		typ: t,
		len: l,
		err: e,
	}
}

func newRPCMessageFieldDecodeError(i int, err error) error {
	return fmt.Errorf("error decoding message field at position %d, error: %v", i, err)
}
<commit_msg>Cleaned up the error handling a bit<commit_after>package rpc

import (
	"fmt"
)

type RecoverableError interface {
	error
	CanRecover() bool
}

type BasicRPCError struct{}

func (BasicRPCError) CanRecover() bool { return false }

type RPCErrorWrapper struct {
	BasicRPCError
	error
}

func newRPCErrorWrapper(e error) RecoverableError {
	if err, ok := e.(RecoverableError); ok {
		return err
	}
	return RPCErrorWrapper{error: e}
}

type PacketizerError struct {
	BasicRPCError
	msg string
}

func (p PacketizerError) Error() string {
	return "packetizer error: " + p.msg
}

func NewPacketizerError(d string, a ...interface{}) PacketizerError {
	return PacketizerError{msg: fmt.Sprintf(d, a...)}
}

type DispatcherError struct {
	BasicRPCError
	msg string
}

func (p DispatcherError) Error() string {
	return "dispatcher error: " + p.msg
}

func NewDispatcherError(d string, a ...interface{}) DispatcherError {
	return DispatcherError{msg: fmt.Sprintf(d, a...)}
}

type ReceiverError struct {
	BasicRPCError
	msg string
}

func (p ReceiverError) Error() string {
	return "dispatcher error: " + p.msg
}

func NewReceiverError(d string, a ...interface{}) ReceiverError {
	return ReceiverError{msg: fmt.Sprintf(d, a...)}
}

type MethodNotFoundError struct {
	BasicRPCError
	p string
	m string
}

func newMethodNotFoundError(p, m string) MethodNotFoundError {
	return MethodNotFoundError{
		p: p,
		m: m,
	}
}

func (m MethodNotFoundError) Error() string {
	return fmt.Sprintf("method '%s' not found in protocol '%s'", m.m, m.p)
}

type ProtocolNotFoundError struct {
	BasicRPCError
	p string
}

func newProtocolNotFoundError(p string) ProtocolNotFoundError {
	return ProtocolNotFoundError{p: p}
}

func (p ProtocolNotFoundError) Error() string {
	return "protocol not found: " + p.p
}

type AlreadyRegisteredError struct {
	BasicRPCError
	p string
}

func newAlreadyRegisteredError(p string) AlreadyRegisteredError {
	return AlreadyRegisteredError{p: p}
}

func (a AlreadyRegisteredError) Error() string {
	return a.p + ": protocol already registered"
}

type TypeError struct {
	BasicRPCError
	p string
}

func (t TypeError) Error() string {
	return t.p
}

func NewTypeError(expected, actual interface{}) TypeError {
	return TypeError{p: fmt.Sprintf("Invalid type for arguments. Expected: %T, actual: %T", expected, actual)}
}

type CallNotFoundError struct {
	BasicRPCError
	seqno seqNumber
}

func newCallNotFoundError(s seqNumber) CallNotFoundError {
	return CallNotFoundError{seqno: s}
}

func (c CallNotFoundError) Error() string {
	return fmt.Sprintf("Call not found for sequence number %d", c.seqno)
}

func (CallNotFoundError) CanRecover() bool { return true }

type NilResultError struct {
	BasicRPCError
	seqno seqNumber
}

func (c NilResultError) Error() string {
	return fmt.Sprintf("Nil result supplied for sequence number %d", c.seqno)
}

type RPCDecodeError struct {
	RecoverableError
	typ MethodType
	len int
}

func (r RPCDecodeError) Error() string {
	return fmt.Sprintf("RPC error: type %d, length %d, error: %v", r.typ, r.len, r.RecoverableError)
}

func newRPCDecodeError(t MethodType, l int, e error) RPCDecodeError {
	return RPCDecodeError{
		RecoverableError: newRPCErrorWrapper(e),
		typ:              t,
		len:              l,
	}
}

func newRPCMessageFieldDecodeError(i int, err error) error {
	return fmt.Errorf("error decoding message field at position %d, error: %v", i, err)
}
<|endoftext|>


<commit_before>package kite

import (
	"fmt"

	"github.com/koding/kite/dnode"
)

// Error is the type of the kite related errors returned from kite package.
type Error struct {
	Type    string `json:"type"`
	Message string `json:"message"`
	Code    string `json:"code"`
}

func (e Error) Error() string {
	if e.Type == "genericError" {
		return e.Message
	}

	return fmt.Sprintf("kite error %s - %s - %s", e.Type, e.Message, e.Code)
}

// createError creates a new kite.Error for the given r variable
func createError(r interface{}) *Error {
	if r == nil {
		return nil
	}

	var kiteErr *Error
	switch err := r.(type) {
	case *Error:
		kiteErr = err
	case *dnode.ArgumentError:
		kiteErr = &Error{
			Type:    "argumentError",
			Message: err.Error(),
		}
	default:
		kiteErr = &Error{
			Type:    "genericError",
			Message: fmt.Sprint(r),
		}
	}

	return kiteErr
}
<commit_msg>kite/error: implement a Code() method to imply third party interfaces<commit_after>package kite

import (
	"fmt"

	"github.com/koding/kite/dnode"
)

// Error is the type of the kite related errors returned from kite package.
type Error struct {
	Type    string `json:"type"`
	Message string `json:"message"`
	CodeVal string `json:"code"`
}

func (e Error) Code() string {
	return e.CodeVal
}

func (e Error) Error() string {
	if e.Type == "genericError" {
		return e.Message
	}

	return fmt.Sprintf("kite error %s - %s - %s", e.Type, e.Message, e.Code)
}

// createError creates a new kite.Error for the given r variable
func createError(r interface{}) *Error {
	if r == nil {
		return nil
	}

	var kiteErr *Error
	switch err := r.(type) {
	case *Error:
		kiteErr = err
	case *dnode.ArgumentError:
		kiteErr = &Error{
			Type:    "argumentError",
			Message: err.Error(),
		}
	default:
		kiteErr = &Error{
			Type:    "genericError",
			Message: fmt.Sprint(r),
		}
	}

	return kiteErr
}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2013 Frank Laub

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// Package ergo contains generalized error utilities.
package ergo

import (
	"bytes"
	"fmt"
	"log"
	"runtime"
	"text/template"
)

// ErrCode defines a type for error codes.
type ErrCode int

// ErrInfo is a collection of named values associated with an error.
type ErrInfo map[string]interface{}

// DomainMap is used to define message formats associated with error coddes.
type DomainMap map[ErrCode]string

// FormatFunc is a function that users can implement to define their own message formats.
type FormatFunc func(err *Error) string

// Error is a generic error designed to be serializable and provide
// additional information for developers while keeping
// friendly messages distinct for end users.
type Error struct {
	_struct bool `codec:",omitempty"` // set omitempty for every field

	// The domain of this error.
	Domain string `json:",omitempty"`

	// The error code of this error.
	Code ErrCode `json:",omitempty"`

	// A collection of named values associated with this error.
	Info ErrInfo `json:",omitempty"`

	// Additional context to help developers determine the source of an error.
	// In go, this is a stack trace. In C++, this could be file:line.
	Context string `json:",omitempty"`

	// Used for defining a chain of errors.
	// The innermost error represents the original error.
	Inner *Error `json:",omitempty"`
}

var (
	domains = make(map[string]FormatFunc)
)

func init() {
	DomainFunc("go", func(err *Error) string {
		return "Error: " + err.Info["_err"].(string)
	})
}

// New creates a new error.
// "skip" is used to skip stack frames,
// a value of 0 means the stack will start at the call site of Make().
// "args" is a set of pairs to be used to populate "Info":
// first is the key, second is the value.
func New(skip int, domain string, code ErrCode, args ...interface{}) *Error {
	err := &Error{
		Domain:  domain,
		Code:    code,
		Info:    make(ErrInfo),
		Context: stackTrace(skip + 2),
	}
	var name string
	for _, arg := range args {
		if name == "" {
			name = arg.(string)
		} else {
			err.Info[name] = arg
			name = ""
		}
	}
	return err
}

func _Wrap(skip int, err error, args ...interface{}) *Error {
	sys := []interface{}{"_err", err.Error()}
	return New(skip+1, "go", 0, append(sys, args...)...)
}

// Wrap takes a generic interface "x" and returns an Error.
// If "x" is nil, nil is returned.
// If "x" is an Error, this is returned.
// If "x" implements the standard error interface, a standard Error is generated.
// Otherwise, "x" is converted into a string and used to generate a standard Error.
func Wrap(x interface{}, args ...interface{}) *Error {
	if x == nil {
		return nil
	}
	if err, ok := x.(*Error); ok {
		return err
	}
	if err, ok := x.(error); ok {
		return _Wrap(1, err, args...)
	}
	return _Wrap(1, fmt.Errorf("%v", x), args...)
}

// Chain links an inner error to an outer one.
// The result is the outer error.
func Chain(inner *Error, err *Error) *Error {
	err.Inner = inner
	return err
}

// Cause returns the cause of the error,
// which is the innermost error in a chain.
func Cause(err *Error) *Error {
	if err.Inner == nil {
		return err
	}
	return Cause(err.Inner)
}

// DomainFunc allows users to define custom domains.
// This is a low-level API.
func DomainFunc(name string, fn FormatFunc) {
	_, ok := domains[name]
	if ok {
		log.Panicf("Domain conflict: %v", name)
	}
	domains[name] = fn
}

// Domain allows users to define custom domains.
// A domain represents a set of error codes and their associated
// message formats. The format string is processed by text/template.
func Domain(name string, domain DomainMap) {
	tmpls := make(map[ErrCode]*template.Template)
	for code, text := range domain {
		name := fmt.Sprintf("[%v:%d]", name, code)
		tmpl := template.Must(template.New(name).Parse(text))
		tmpls[code] = tmpl
	}
	DomainFunc(name, func(err *Error) string {
		tmpl, ok := tmpls[err.Code]
		if !ok {
			return "Unknown error"
		}
		var buf bytes.Buffer
		terr := tmpl.Execute(&buf, err.Info)
		if terr != nil {
			panic(terr)
		}
		return buf.String()
	})
}

func stackTrace(skip int) string {
	buf := bytes.Buffer{}
	stack := [50]uintptr{}
	n := runtime.Callers(skip+1, stack[:])
	for _, pc := range stack[:n] {
		fn := runtime.FuncForPC(pc)
		file, line := fn.FileLine(pc)
		fmt.Fprintf(&buf, "%v:%v\n", file, line)
		fmt.Fprintf(&buf, "\t%v\n", fn.Name())
	}
	return buf.String()
}

// Message returns the friendly error message without context.
// This is appropriate for displaying to end users.
func (err *Error) Message() string {
	domain, ok := domains[err.Domain]
	if ok {
		return domain(err)
	}
	return fmt.Sprintf("Domain missing: [%v:%d] %v",
		err.Domain, err.Code, err.Info)
}

// Error implements error.Error().
// The entire chain along with context is returned.
// Use Message() to display end user friendly messages.
func (err *Error) Error() string {
	str := fmt.Sprintf("[%v:%d] %v\n%v",
		err.Domain, err.Code, err.Message(), err.Context)
	if err.Inner == nil {
		return str
	}
	return err.Inner.Error() + "\n" + str
}
<commit_msg>Allow benign use of Chain(). If inner is nil, return nil.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2013 Frank Laub

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// Package ergo contains generalized error utilities.
package ergo

import (
	"bytes"
	"fmt"
	"log"
	"runtime"
	"text/template"
)

// ErrCode defines a type for error codes.
type ErrCode int

// ErrInfo is a collection of named values associated with an error.
type ErrInfo map[string]interface{}

// DomainMap is used to define message formats associated with error coddes.
type DomainMap map[ErrCode]string

// FormatFunc is a function that users can implement to define their own message formats.
type FormatFunc func(err *Error) string

// Error is a generic error designed to be serializable and provide
// additional information for developers while keeping
// friendly messages distinct for end users.
type Error struct {
	_struct bool `codec:",omitempty"` // set omitempty for every field

	// The domain of this error.
	Domain string `json:",omitempty"`

	// The error code of this error.
	Code ErrCode `json:",omitempty"`

	// A collection of named values associated with this error.
	Info ErrInfo `json:",omitempty"`

	// Additional context to help developers determine the source of an error.
	// In go, this is a stack trace. In C++, this could be file:line.
	Context string `json:",omitempty"`

	// Used for defining a chain of errors.
	// The innermost error represents the original error.
	Inner *Error `json:",omitempty"`
}

var (
	domains = make(map[string]FormatFunc)
)

func init() {
	DomainFunc("go", func(err *Error) string {
		return "Error: " + err.Info["_err"].(string)
	})
}

// New creates a new error.
// "skip" is used to skip stack frames,
// a value of 0 means the stack will start at the call site of Make().
// "args" is a set of pairs to be used to populate "Info":
// first is the key, second is the value.
func New(skip int, domain string, code ErrCode, args ...interface{}) *Error {
	err := &Error{
		Domain:  domain,
		Code:    code,
		Info:    make(ErrInfo),
		Context: stackTrace(skip + 2),
	}
	var name string
	for _, arg := range args {
		if name == "" {
			name = arg.(string)
		} else {
			err.Info[name] = arg
			name = ""
		}
	}
	return err
}

func _Wrap(skip int, err error, args ...interface{}) *Error {
	sys := []interface{}{"_err", err.Error()}
	return New(skip+1, "go", 0, append(sys, args...)...)
}

// Wrap takes a generic interface "x" and returns an Error.
// If "x" is nil, nil is returned.
// If "x" is an Error, this is returned.
// If "x" implements the standard error interface, a standard Error is generated.
// Otherwise, "x" is converted into a string and used to generate a standard Error.
func Wrap(x interface{}, args ...interface{}) *Error {
	if x == nil {
		return nil
	}
	if err, ok := x.(*Error); ok {
		return err
	}
	if err, ok := x.(error); ok {
		return _Wrap(1, err, args...)
	}
	return _Wrap(1, fmt.Errorf("%v", x), args...)
}

// Chain links an inner error to an outer one.
// The result is the outer error.
func Chain(inner *Error, err *Error) *Error {
	if inner == nil {
		return nil
	}
	err.Inner = inner
	return err
}

// Cause returns the cause of the error,
// which is the innermost error in a chain.
func Cause(err *Error) *Error {
	if err.Inner == nil {
		return err
	}
	return Cause(err.Inner)
}

// DomainFunc allows users to define custom domains.
// This is a low-level API.
func DomainFunc(name string, fn FormatFunc) {
	_, ok := domains[name]
	if ok {
		log.Panicf("Domain conflict: %v", name)
	}
	domains[name] = fn
}

// Domain allows users to define custom domains.
// A domain represents a set of error codes and their associated
// message formats. The format string is processed by text/template.
func Domain(name string, domain DomainMap) {
	tmpls := make(map[ErrCode]*template.Template)
	for code, text := range domain {
		name := fmt.Sprintf("[%v:%d]", name, code)
		tmpl := template.Must(template.New(name).Parse(text))
		tmpls[code] = tmpl
	}
	DomainFunc(name, func(err *Error) string {
		tmpl, ok := tmpls[err.Code]
		if !ok {
			return "Unknown error"
		}
		var buf bytes.Buffer
		terr := tmpl.Execute(&buf, err.Info)
		if terr != nil {
			panic(terr)
		}
		return buf.String()
	})
}

func stackTrace(skip int) string {
	buf := bytes.Buffer{}
	stack := [50]uintptr{}
	n := runtime.Callers(skip+1, stack[:])
	for _, pc := range stack[:n] {
		fn := runtime.FuncForPC(pc)
		file, line := fn.FileLine(pc)
		fmt.Fprintf(&buf, "%v:%v\n", file, line)
		fmt.Fprintf(&buf, "\t%v\n", fn.Name())
	}
	return buf.String()
}

// Message returns the friendly error message without context.
// This is appropriate for displaying to end users.
func (err *Error) Message() string {
	domain, ok := domains[err.Domain]
	if ok {
		return domain(err)
	}
	return fmt.Sprintf("Domain missing: [%v:%d] %v",
		err.Domain, err.Code, err.Info)
}

// Error implements error.Error().
// The entire chain along with context is returned.
// Use Message() to display end user friendly messages.
func (err *Error) Error() string {
	str := fmt.Sprintf("[%v:%d] %v\n%v",
		err.Domain, err.Code, err.Message(), err.Context)
	if err.Inner == nil {
		return str
	}
	return err.Inner.Error() + "\n" + str
}
<|endoftext|>


<commit_before>package dbr

import "errors"

// package errors
var (
	ErrNotFound           = errors.New("dbr: not found")
	ErrNotSupported       = errors.New("dbr: not supported")
	ErrTableNotSpecified  = errors.New("dbr: table not specified")
	ErrColumnNotSpecified = errors.New("dbr: column not specified")
	ErrInvalidPointer     = errors.New("dbr: attempt to load into an invalid pointer")
	ErrPlaceholderCount   = errors.New("dbr: wrong placeholder count")
	ErrInvalidSliceLength = errors.New("length of slice is 0. length must be >= 1")
)
<commit_msg>TWEAK: prefix all errors with<commit_after>package dbr

import "errors"

// package errors
var (
	ErrNotFound           = errors.New("dbr: not found")
	ErrNotSupported       = errors.New("dbr: not supported")
	ErrTableNotSpecified  = errors.New("dbr: table not specified")
	ErrColumnNotSpecified = errors.New("dbr: column not specified")
	ErrInvalidPointer     = errors.New("dbr: attempt to load into an invalid pointer")
	ErrPlaceholderCount   = errors.New("dbr: wrong placeholder count")
	ErrInvalidSliceLength = errors.New("dbr: length of slice is 0. length must be >= 1")
)
<|endoftext|>


<commit_before>package jsonptr

import "strings"

// Escape a property name with JSON Pointer escapes:
// "~" => "~0",
// "/" => "~1"
func Escape(name string) string {
	var shift int
	for i := range name {
		switch name[i] {
		case '~', '/':
			shift++
		}
	}
	if shift == 0 {
		return name
	}

	b := make([]byte, len(name)+shift)
	copy(b, name[:])

	for i := len(name) - 1; i >= 0; i-- {
		switch b[i] {
		case '~':
			b[i+shift] = '0'
		case '/':
			b[i+shift] = '1'
		default:
			b[i+shift] = b[i]
			continue
		}
		shift--
		b[i+shift] = '~'
		if shift == 0 {
			break
		}
	}

	return string(b)
}

var ErrUsage = stringError("invalid use of json.Unescape on string with '/'")

// Unescape unescapes a property name:
//  `~1` => '/'
//  `~0` => '~'
// Any '~' followed by something else (or nothing) is an error ErrSyntax
// Any '/' is an error ErrUsage
func Unescape(token string) (string, error) {
	p := strings.IndexByte(token, '~')
	if p == -1 {
		/*
			// Costly check just to detect unlikely bad usage
			if strings.IndexByte(token, '/') >= 0 {
				return "", ErrUsage
			}
		*/
		return token, nil
	}
	if token[len(token)-1] == '~' {
		return "", ErrSyntax
	}

	// Copy to a working buffer
	b := []byte(token)
	for q := p; q < len(token); q++ {
		switch b[q] {
		case '~':
			q++
			switch b[q] {
			case '0':
				b[p] = '~'
			case '1':
				b[p] = '/'
			default:
				return "", ErrSyntax
			}
		case '/':
			return "", ErrUsage
		default:
			// Move byte
			b[p] = b[q]
		}
		p++
	}
	return string(b[:p]), nil
}
<commit_msg>Escape: now faster than the strings.NewReplacer version<commit_after>package jsonptr

import "strings"

// Escape a property name with JSON Pointer escapes:
// "~" => "~0",
// "/" => "~1"
func Escape(name string) string {
	var shift int
	for i := len(name) - 1; i >= 0; i-- {
		switch name[i] {
		case '~', '/':
			shift++
		}
	}
	if shift == 0 {
		return name
	}

	b := make([]byte, len(name)+shift)
	copy(b, name[:])

	for i := len(name) - 1; i >= 0; i-- {
		switch b[i] {
		case '~':
			b[i+shift] = '0'
		case '/':
			b[i+shift] = '1'
		default:
			b[i+shift] = b[i]
			continue
		}
		shift--
		b[i+shift] = '~'
		if shift == 0 {
			break
		}
	}

	return string(b)
}

var ErrUsage = stringError("invalid use of json.Unescape on string with '/'")

// Unescape unescapes a property name:
//  `~1` => '/'
//  `~0` => '~'
// Any '~' followed by something else (or nothing) is an error ErrSyntax
// Any '/' is an error ErrUsage
func Unescape(token string) (string, error) {
	p := strings.IndexByte(token, '~')
	if p == -1 {
		/*
			// Costly check just to detect unlikely bad usage
			if strings.IndexByte(token, '/') >= 0 {
				return "", ErrUsage
			}
		*/
		return token, nil
	}
	if token[len(token)-1] == '~' {
		return "", ErrSyntax
	}

	// Copy to a working buffer
	b := []byte(token)
	for q := p; q < len(token); q++ {
		switch b[q] {
		case '~':
			q++
			switch b[q] {
			case '0':
				b[p] = '~'
			case '1':
				b[p] = '/'
			default:
				return "", ErrSyntax
			}
		case '/':
			return "", ErrUsage
		default:
			// Move byte
			b[p] = b[q]
		}
		p++
	}
	return string(b[:p]), nil
}
<|endoftext|>


<commit_before>package lfs

import "sync/atomic"

// Batcher provides a way to process a set of items in groups of n. Items can
// be added to the batcher from multiple goroutines and pulled off in groups
// when one of the following conditions occurs:
//   * The batch size is reached
//   * Exit() is called
// When an Exit() occurs, the group may be smaller than the batch size.
type Batcher struct {
	exited     uint32
	batchSize  int
	input      chan interface{}
	batchReady chan []interface{}
	truncate   chan interface{}
}

// NewBatcher creates a Batcher with the batchSize.
func NewBatcher(batchSize int) *Batcher {
	b := &Batcher{
		batchSize:  batchSize,
		input:      make(chan interface{}),
		batchReady: make(chan []interface{}),
		truncate:   make(chan interface{}),
	}

	go b.acceptInput()
	return b
}

// Add adds an item to the batcher. Add is safe to call from multiple
// goroutines.
func (b *Batcher) Add(t interface{}) {
	if atomic.CompareAndSwapUint32(&b.exited, 1, 0) {
		b.input = make(chan interface{})
		b.truncate = make(chan interface{})
		go b.acceptInput()
	}

	b.input <- t
}

// Next will wait for the one of the above batch triggers to occur and return
// the accumulated batch.
func (b *Batcher) Next() []interface{} {
	return <-b.batchReady
}

// Truncate causes the current batch to halt accumulation and return
// immediately, even if it is smaller than the given batch size.
func (b *Batcher) Truncate() {
	b.truncate <- struct{}{}
}

// Exit stops all batching and allows Next() to return. Calling Add() after
// calling Exit() will reset the batcher.
func (b *Batcher) Exit() {
	atomic.StoreUint32(&b.exited, 1)
	close(b.input)
	close(b.truncate)
}

// acceptInput runs in its own goroutine and accepts input from external
// clients. Without truncation, it fills and dispenses batches in a sequential
// order: for a batch size N, N items will be processed before a new batch is
// ready. If a batch is truncated while still filling itself, it will be
// returned immediately, opening up a new batch for all subsequent items.
func (b *Batcher) acceptInput() {
	exit := false

	for {
		batch := make([]interface{}, 0, b.batchSize)
	Acc:
		for len(batch) < b.batchSize {
			select {
			case t, ok := <-b.input:
				if !ok {
					exit = true // input channel was closed by Exit()
					break Acc
				}
				batch = append(batch, t)
			case <-b.truncate:
				break Acc
			}
		}

		b.batchReady <- batch

		if exit {
			return
		}
	}
}
<commit_msg>lfs/batcher: use zero-value initialization for exit bool<commit_after>package lfs

import "sync/atomic"

// Batcher provides a way to process a set of items in groups of n. Items can
// be added to the batcher from multiple goroutines and pulled off in groups
// when one of the following conditions occurs:
//   * The batch size is reached
//   * Exit() is called
// When an Exit() occurs, the group may be smaller than the batch size.
type Batcher struct {
	exited     uint32
	batchSize  int
	input      chan interface{}
	batchReady chan []interface{}
	truncate   chan interface{}
}

// NewBatcher creates a Batcher with the batchSize.
func NewBatcher(batchSize int) *Batcher {
	b := &Batcher{
		batchSize:  batchSize,
		input:      make(chan interface{}),
		batchReady: make(chan []interface{}),
		truncate:   make(chan interface{}),
	}

	go b.acceptInput()
	return b
}

// Add adds an item to the batcher. Add is safe to call from multiple
// goroutines.
func (b *Batcher) Add(t interface{}) {
	if atomic.CompareAndSwapUint32(&b.exited, 1, 0) {
		b.input = make(chan interface{})
		b.truncate = make(chan interface{})
		go b.acceptInput()
	}

	b.input <- t
}

// Next will wait for the one of the above batch triggers to occur and return
// the accumulated batch.
func (b *Batcher) Next() []interface{} {
	return <-b.batchReady
}

// Truncate causes the current batch to halt accumulation and return
// immediately, even if it is smaller than the given batch size.
func (b *Batcher) Truncate() {
	b.truncate <- struct{}{}
}

// Exit stops all batching and allows Next() to return. Calling Add() after
// calling Exit() will reset the batcher.
func (b *Batcher) Exit() {
	atomic.StoreUint32(&b.exited, 1)
	close(b.input)
	close(b.truncate)
}

// acceptInput runs in its own goroutine and accepts input from external
// clients. Without truncation, it fills and dispenses batches in a sequential
// order: for a batch size N, N items will be processed before a new batch is
// ready. If a batch is truncated while still filling itself, it will be
// returned immediately, opening up a new batch for all subsequent items.
func (b *Batcher) acceptInput() {
	var exit bool

	for {
		batch := make([]interface{}, 0, b.batchSize)
	Acc:
		for len(batch) < b.batchSize {
			select {
			case t, ok := <-b.input:
				if !ok {
					exit = true // input channel was closed by Exit()
					break Acc
				}
				batch = append(batch, t)
			case <-b.truncate:
				break Acc
			}
		}

		b.batchReady <- batch

		if exit {
			return
		}
	}
}
<|endoftext|>


<commit_before>package lfs

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	v1Aliases = []string{
		"http://git-media.io/v/2",            // alpha
		"https://hawser.github.com/spec/v1",  // pre-release
		"https://git-lfs.github.com/spec/v1", // public launch
	}
	latest      = "https://git-lfs.github.com/spec/v1"
	oidType     = "sha256"
	oidRE       = regexp.MustCompile(`\A[[:alnum:]]{64}`)
	matcherRE   = regexp.MustCompile("git-media|hawser|git-lfs")
	extRE       = regexp.MustCompile(`\Aext-\d{1}-\w+`)
	pointerKeys = []string{"version", "oid", "size"}
)

type Pointer struct {
	Version    string
	Oid        string
	Size       int64
	OidType    string
	Extensions []*PointerExtension
}

// A PointerExtension is parsed from the Git LFS Pointer file.
type PointerExtension struct {
	Name     string
	Priority int
	Oid      string
	OidType  string
}

type ByPriority []*PointerExtension

func (p ByPriority) Len() int           { return len(p) }
func (p ByPriority) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p ByPriority) Less(i, j int) bool { return p[i].Priority < p[j].Priority }

func NewPointer(oid string, size int64, exts []*PointerExtension) *Pointer {
	return &Pointer{latest, oid, size, oidType, exts}
}

func NewPointerExtension(name string, priority int, oid string) *PointerExtension {
	return &PointerExtension{name, priority, oid, oidType}
}

func (p *Pointer) Smudge(writer io.Writer, workingfile string, download bool, cb CopyCallback) error {
	return PointerSmudge(writer, p, workingfile, download, cb)
}

func (p *Pointer) Encode(writer io.Writer) (int, error) {
	return EncodePointer(writer, p)
}

func (p *Pointer) Encoded() string {
	var buffer bytes.Buffer
	buffer.WriteString(fmt.Sprintf("version %s\n", latest))
	for _, ext := range p.Extensions {
		buffer.WriteString(fmt.Sprintf("ext-%d-%s %s:%s\n", ext.Priority, ext.Name, ext.OidType, ext.Oid))
	}
	buffer.WriteString(fmt.Sprintf("oid %s:%s\n", p.OidType, p.Oid))
	buffer.WriteString(fmt.Sprintf("size %d\n", p.Size))
	return buffer.String()
}

func EncodePointer(writer io.Writer, pointer *Pointer) (int, error) {
	return writer.Write([]byte(pointer.Encoded()))
}

func DecodePointerFromFile(file string) (*Pointer, error) {
	// Check size before reading
	stat, err := os.Stat(file)
	if err != nil {
		return nil, err
	}
	if stat.Size() > blobSizeCutoff {
		return nil, newNotAPointerError(nil)
	}
	f, err := os.OpenFile(file, os.O_RDONLY, 0644)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return DecodePointer(f)
}
func DecodePointer(reader io.Reader) (*Pointer, error) {
	_, p, err := DecodeFrom(reader)
	return p, err
}

func DecodeFrom(reader io.Reader) ([]byte, *Pointer, error) {
	buf := make([]byte, blobSizeCutoff)
	written, err := reader.Read(buf)
	output := buf[0:written]

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

	p, err := decodeKV(bytes.TrimSpace(output))
	return output, p, err
}

func verifyVersion(version string) error {
	if len(version) == 0 {
		return newNotAPointerError(errors.New("Missing version"))
	}

	for _, v := range v1Aliases {
		if v == version {
			return nil
		}
	}

	return errors.New("Invalid version: " + version)
}

func decodeKV(data []byte) (*Pointer, error) {
	kvps, exts, err := decodeKVData(data)
	if err != nil {
		if IsBadPointerKeyError(err) {
			badErr := err.(badPointerKeyError)
			if badErr.Expected == "version" {
				return nil, newNotAPointerError(err)
			}
		}
		return nil, err
	}

	if err := verifyVersion(kvps["version"]); err != nil {
		return nil, err
	}

	value, ok := kvps["oid"]
	if !ok {
		return nil, errors.New("Invalid Oid")
	}

	oid, err := parseOid(value)
	if err != nil {
		return nil, err
	}

	value, ok = kvps["size"]
	size, err := strconv.ParseInt(value, 10, 0)
	if err != nil || size < 0 {
		return nil, fmt.Errorf("Invalid size: %q", value)
	}

	var extensions []*PointerExtension
	if exts != nil {
		for key, value := range exts {
			ext, err := parsePointerExtension(key, value)
			if err != nil {
				return nil, err
			}
			extensions = append(extensions, ext)
		}
		if err = validatePointerExtensions(extensions); err != nil {
			return nil, err
		}
		sort.Sort(ByPriority(extensions))
	}

	return NewPointer(oid, size, extensions), nil
}

func parseOid(value string) (string, error) {
	parts := strings.SplitN(value, ":", 2)
	if len(parts) != 2 {
		return "", errors.New("Invalid Oid value: " + value)
	}
	if parts[0] != oidType {
		return "", errors.New("Invalid Oid type: " + parts[0])
	}
	oid := parts[1]
	if !oidRE.Match([]byte(oid)) {
		return "", errors.New("Invalid Oid: " + oid)
	}
	return oid, nil
}

func parsePointerExtension(key string, value string) (*PointerExtension, error) {
	keyParts := strings.SplitN(key, "-", 3)
	if len(keyParts) != 3 || keyParts[0] != "ext" {
		return nil, errors.New("Invalid extension value: " + value)
	}

	p, err := strconv.Atoi(keyParts[1])
	if err != nil || p < 0 {
		return nil, errors.New("Invalid priority: " + keyParts[1])
	}

	name := keyParts[2]

	oid, err := parseOid(value)
	if err != nil {
		return nil, err
	}

	return NewPointerExtension(name, p, oid), nil
}

func validatePointerExtensions(exts []*PointerExtension) error {
	m := make(map[int]struct{})
	for _, ext := range exts {
		if _, exist := m[ext.Priority]; exist {
			return fmt.Errorf("Duplicate priority found: %d", ext.Priority)
		}
		m[ext.Priority] = struct{}{}
	}
	return nil
}

func decodeKVData(data []byte) (kvps map[string]string, exts map[string]string, err error) {
	kvps = make(map[string]string)

	if !matcherRE.Match(data) {
		err = newNotAPointerError(err)
		return
	}

	scanner := bufio.NewScanner(bytes.NewBuffer(data))
	line := 0
	numKeys := len(pointerKeys)
	for scanner.Scan() {
		text := scanner.Text()
		if len(text) == 0 {
			continue
		}

		parts := strings.SplitN(text, " ", 2)
		if len(parts) < 2 {
			err = fmt.Errorf("Error reading line %d: %s", line, text)
			return
		}

		key := parts[0]
		value := parts[1]

		if numKeys <= line {
			err = fmt.Errorf("Extra line: %s", text)
			return
		}

		if expected := pointerKeys[line]; key != expected {
			if !extRE.Match([]byte(key)) {
				err = newBadPointerKeyError(expected, key)
				return
			}
			if exts == nil {
				exts = make(map[string]string)
			}
			exts[key] = value
			continue
		}

		line += 1
		kvps[key] = value
	}

	err = scanner.Err()
	return
}
<commit_msg>Don't create pointer for empty files<commit_after>package lfs

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var (
	v1Aliases = []string{
		"http://git-media.io/v/2",            // alpha
		"https://hawser.github.com/spec/v1",  // pre-release
		"https://git-lfs.github.com/spec/v1", // public launch
	}
	latest      = "https://git-lfs.github.com/spec/v1"
	oidType     = "sha256"
	oidRE       = regexp.MustCompile(`\A[[:alnum:]]{64}`)
	matcherRE   = regexp.MustCompile("git-media|hawser|git-lfs")
	extRE       = regexp.MustCompile(`\Aext-\d{1}-\w+`)
	pointerKeys = []string{"version", "oid", "size"}
)

type Pointer struct {
	Version    string
	Oid        string
	Size       int64
	OidType    string
	Extensions []*PointerExtension
}

// A PointerExtension is parsed from the Git LFS Pointer file.
type PointerExtension struct {
	Name     string
	Priority int
	Oid      string
	OidType  string
}

type ByPriority []*PointerExtension

func (p ByPriority) Len() int           { return len(p) }
func (p ByPriority) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p ByPriority) Less(i, j int) bool { return p[i].Priority < p[j].Priority }

func NewPointer(oid string, size int64, exts []*PointerExtension) *Pointer {
	return &Pointer{latest, oid, size, oidType, exts}
}

func NewPointerExtension(name string, priority int, oid string) *PointerExtension {
	return &PointerExtension{name, priority, oid, oidType}
}

func (p *Pointer) Smudge(writer io.Writer, workingfile string, download bool, cb CopyCallback) error {
	return PointerSmudge(writer, p, workingfile, download, cb)
}

func (p *Pointer) Encode(writer io.Writer) (int, error) {
	return EncodePointer(writer, p)
}

func (p *Pointer) Encoded() string {
	var buffer bytes.Buffer
	if p.Size != 0 {
		buffer.WriteString(fmt.Sprintf("version %s\n", latest))
		for _, ext := range p.Extensions {
			buffer.WriteString(fmt.Sprintf("ext-%d-%s %s:%s\n", ext.Priority, ext.Name, ext.OidType, ext.Oid))
		}
		buffer.WriteString(fmt.Sprintf("oid %s:%s\n", p.OidType, p.Oid))
		buffer.WriteString(fmt.Sprintf("size %d\n", p.Size))
	}
	return buffer.String()
}

func EncodePointer(writer io.Writer, pointer *Pointer) (int, error) {
	return writer.Write([]byte(pointer.Encoded()))
}

func DecodePointerFromFile(file string) (*Pointer, error) {
	// Check size before reading
	stat, err := os.Stat(file)
	if err != nil {
		return nil, err
	}
	if stat.Size() > blobSizeCutoff {
		return nil, newNotAPointerError(nil)
	}
	f, err := os.OpenFile(file, os.O_RDONLY, 0644)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return DecodePointer(f)
}
func DecodePointer(reader io.Reader) (*Pointer, error) {
	_, p, err := DecodeFrom(reader)
	return p, err
}

func DecodeFrom(reader io.Reader) ([]byte, *Pointer, error) {
	buf := make([]byte, blobSizeCutoff)
	written, err := reader.Read(buf)
	output := buf[0:written]

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

	p, err := decodeKV(bytes.TrimSpace(output))
	return output, p, err
}

func verifyVersion(version string) error {
	if len(version) == 0 {
		return newNotAPointerError(errors.New("Missing version"))
	}

	for _, v := range v1Aliases {
		if v == version {
			return nil
		}
	}

	return errors.New("Invalid version: " + version)
}

func decodeKV(data []byte) (*Pointer, error) {
	kvps, exts, err := decodeKVData(data)
	if err != nil {
		if IsBadPointerKeyError(err) {
			badErr := err.(badPointerKeyError)
			if badErr.Expected == "version" {
				return nil, newNotAPointerError(err)
			}
		}
		return nil, err
	}

	if err := verifyVersion(kvps["version"]); err != nil {
		return nil, err
	}

	value, ok := kvps["oid"]
	if !ok {
		return nil, errors.New("Invalid Oid")
	}

	oid, err := parseOid(value)
	if err != nil {
		return nil, err
	}

	value, ok = kvps["size"]
	size, err := strconv.ParseInt(value, 10, 0)
	if err != nil || size < 0 {
		return nil, fmt.Errorf("Invalid size: %q", value)
	}

	var extensions []*PointerExtension
	if exts != nil {
		for key, value := range exts {
			ext, err := parsePointerExtension(key, value)
			if err != nil {
				return nil, err
			}
			extensions = append(extensions, ext)
		}
		if err = validatePointerExtensions(extensions); err != nil {
			return nil, err
		}
		sort.Sort(ByPriority(extensions))
	}

	return NewPointer(oid, size, extensions), nil
}

func parseOid(value string) (string, error) {
	parts := strings.SplitN(value, ":", 2)
	if len(parts) != 2 {
		return "", errors.New("Invalid Oid value: " + value)
	}
	if parts[0] != oidType {
		return "", errors.New("Invalid Oid type: " + parts[0])
	}
	oid := parts[1]
	if !oidRE.Match([]byte(oid)) {
		return "", errors.New("Invalid Oid: " + oid)
	}
	return oid, nil
}

func parsePointerExtension(key string, value string) (*PointerExtension, error) {
	keyParts := strings.SplitN(key, "-", 3)
	if len(keyParts) != 3 || keyParts[0] != "ext" {
		return nil, errors.New("Invalid extension value: " + value)
	}

	p, err := strconv.Atoi(keyParts[1])
	if err != nil || p < 0 {
		return nil, errors.New("Invalid priority: " + keyParts[1])
	}

	name := keyParts[2]

	oid, err := parseOid(value)
	if err != nil {
		return nil, err
	}

	return NewPointerExtension(name, p, oid), nil
}

func validatePointerExtensions(exts []*PointerExtension) error {
	m := make(map[int]struct{})
	for _, ext := range exts {
		if _, exist := m[ext.Priority]; exist {
			return fmt.Errorf("Duplicate priority found: %d", ext.Priority)
		}
		m[ext.Priority] = struct{}{}
	}
	return nil
}

func decodeKVData(data []byte) (kvps map[string]string, exts map[string]string, err error) {
	kvps = make(map[string]string)

	if !matcherRE.Match(data) {
		err = newNotAPointerError(err)
		return
	}

	scanner := bufio.NewScanner(bytes.NewBuffer(data))
	line := 0
	numKeys := len(pointerKeys)
	for scanner.Scan() {
		text := scanner.Text()
		if len(text) == 0 {
			continue
		}

		parts := strings.SplitN(text, " ", 2)
		if len(parts) < 2 {
			err = fmt.Errorf("Error reading line %d: %s", line, text)
			return
		}

		key := parts[0]
		value := parts[1]

		if numKeys <= line {
			err = fmt.Errorf("Extra line: %s", text)
			return
		}

		if expected := pointerKeys[line]; key != expected {
			if !extRE.Match([]byte(key)) {
				err = newBadPointerKeyError(expected, key)
				return
			}
			if exts == nil {
				exts = make(map[string]string)
			}
			exts[key] = value
			continue
		}

		line += 1
		kvps[key] = value
	}

	err = scanner.Err()
	return
}
<|endoftext|>