<commit_before>package test

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_model/go"
)

func fatalf(t *testing.T, format string, args ...interface{}) {
	fmt.Printf("\t"+format+"\n", args...)
	t.FailNow()
}

// Return short format caller info for printing errors, so errors don't all
// appear to come from test-tools.go.
func caller() string {
	_, file, line, _ := runtime.Caller(2)
	splits := strings.Split(file, "/")
	filename := splits[len(splits)-1]
	return fmt.Sprintf("%s:%d:", filename, line)
}

// Assert a boolean
func Assert(t *testing.T, result bool, message string) {
	t.Helper()
	if !result {
		t.Fatal(message)
	}
}

// AssertNotNil checks an object to be non-nil
func AssertNotNil(t *testing.T, obj interface{}, message string) {
	t.Helper()
	if obj == nil {
		t.Fatal(message)
	}
}

// AssertNotError checks that err is nil
func AssertNotError(t *testing.T, err error, message string) {
	t.Helper()
	if err != nil {
		t.Fatalf("%s: %s", message, err)
	}
}

// AssertError checks that err is non-nil
func AssertError(t *testing.T, err error, message string) {
	t.Helper()
	if err == nil {
		t.Fatalf("%s: expected error but received none", message)
	}
}

// AssertEquals uses the equality operator (==) to measure one and two
func AssertEquals(t *testing.T, one interface{}, two interface{}) {
	t.Helper()
	if one != two {
		t.Fatalf("%#v != %#v", one, two)
	}
}

// AssertDeepEquals uses the reflect.DeepEqual method to measure one and two
func AssertDeepEquals(t *testing.T, one interface{}, two interface{}) {
	t.Helper()
	if !reflect.DeepEqual(one, two) {
		t.Fatalf("[%+v] !(deep)= [%+v]", one, two)
	}
}

// AssertMarshaledEquals marshals one and two to JSON, and then uses
// the equality operator to measure them
func AssertMarshaledEquals(t *testing.T, one interface{}, two interface{}) {
	oneJSON, err := json.Marshal(one)
	AssertNotError(t, err, "Could not marshal 1st argument")
	twoJSON, err := json.Marshal(two)
	AssertNotError(t, err, "Could not marshal 2nd argument")

	if !bytes.Equal(oneJSON, twoJSON) {
		t.Fatalf("[%s] !(json)= [%s]", oneJSON, twoJSON)
	}
}

// AssertUnmarshaledEquals unmarshals two JSON strings (got and expected) to
// a map[string]interface{} and then uses reflect.DeepEqual to check they are
// the same
func AssertUnmarshaledEquals(t *testing.T, got, expected string) {
	t.Helper()
	var gotMap, expectedMap map[string]interface{}
	err := json.Unmarshal([]byte(got), &gotMap)
	AssertNotError(t, err, "Could not unmarshal 'got'")
	err = json.Unmarshal([]byte(expected), &expectedMap)
	AssertNotError(t, err, "Could not unmarshal 'expected'")
	if len(gotMap) != len(expectedMap) {
		t.Errorf("Expected had %d keys, got had %d", len(gotMap), len(expectedMap))
	}
	for k, v := range expectedMap {
		if !reflect.DeepEqual(v, gotMap[k]) {
			t.Errorf("Field %q: Expected \"%v\", got \"%v\"", k, v, gotMap[k])
		}
	}
}

// AssertNotEquals uses the equality operator to measure that one and two
// are different
func AssertNotEquals(t *testing.T, one interface{}, two interface{}) {
	t.Helper()
	if one == two {
		t.Fatalf("%#v == %#v", one, two)
	}
}

// AssertByteEquals uses bytes.Equal to measure one and two for equality.
func AssertByteEquals(t *testing.T, one []byte, two []byte) {
	t.Helper()
	if !bytes.Equal(one, two) {
		t.Fatalf("Byte [%s] != [%s]",
			base64.StdEncoding.EncodeToString(one),
			base64.StdEncoding.EncodeToString(two))
	}
}

// AssertContains determines whether needle can be found in haystack
func AssertContains(t *testing.T, haystack string, needle string) {
	t.Helper()
	if !strings.Contains(haystack, needle) {
		t.Fatalf("String [%s] does not contain [%s]", haystack, needle)
	}
}

// AssertNotContains determines if needle is not found in haystack
func AssertNotContains(t *testing.T, haystack string, needle string) {
	t.Helper()
	if strings.Contains(haystack, needle) {
		t.Fatalf("String [%s] contains [%s]", haystack, needle)
	}
}

// CountCounterVec returns the count by label and value of a prometheus metric
func CountCounterVec(labelName string, value string, counterVec *prometheus.CounterVec) int {
	return CountCounter(counterVec.With(prometheus.Labels{labelName: value}))
}

// CountCounter returns the count by label and value of a prometheus metric
func CountCounter(counter prometheus.Counter) int {
	ch := make(chan prometheus.Metric, 10)
	counter.Collect(ch)
	var m prometheus.Metric
	select {
	case <-time.After(time.Second):
		panic("timed out collecting metrics")
	case m = <-ch:
	}
	var iom io_prometheus_client.Metric
	_ = m.Write(&iom)
	return int(iom.Counter.GetValue())
}

func CountHistogramSamples(hist prometheus.Histogram) int {
	ch := make(chan prometheus.Metric, 10)
	hist.Collect(ch)
	var m prometheus.Metric
	select {
	case <-time.After(time.Second):
		panic("timed out collecting metrics")
	case m = <-ch:
	}
	var iom io_prometheus_client.Metric
	_ = m.Write(&iom)
	return int(iom.Histogram.GetSampleCount())
}

// GaugeValueWithLabels returns the current value with the provided labels from the
// the GaugeVec argument, or an error if there was a problem collecting the value.
func GaugeValueWithLabels(vecGauge *prometheus.GaugeVec, labels prometheus.Labels) (int, error) {
	gauge, err := vecGauge.GetMetricWith(labels)
	if err != nil {
		return 0, err
	}

	ch := make(chan prometheus.Metric, 10)
	gauge.Collect(ch)
	var m prometheus.Metric
	select {
	case <-time.After(time.Second):
		return 0, fmt.Errorf("timed out collecting gauge metrics")
	case m = <-ch:
	}
	var iom io_prometheus_client.Metric
	_ = m.Write(&iom)

	return int(iom.Gauge.GetValue()), nil
}
<commit_msg>Enforce type equality in test.AssertEquals (#4111)<commit_after>package test

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_model/go"
)

func fatalf(t *testing.T, format string, args ...interface{}) {
	fmt.Printf("\t"+format+"\n", args...)
	t.FailNow()
}

// Return short format caller info for printing errors, so errors don't all
// appear to come from test-tools.go.
func caller() string {
	_, file, line, _ := runtime.Caller(2)
	splits := strings.Split(file, "/")
	filename := splits[len(splits)-1]
	return fmt.Sprintf("%s:%d:", filename, line)
}

// Assert a boolean
func Assert(t *testing.T, result bool, message string) {
	t.Helper()
	if !result {
		t.Fatal(message)
	}
}

// AssertNotNil checks an object to be non-nil
func AssertNotNil(t *testing.T, obj interface{}, message string) {
	t.Helper()
	if obj == nil {
		t.Fatal(message)
	}
}

// AssertNotError checks that err is nil
func AssertNotError(t *testing.T, err error, message string) {
	t.Helper()
	if err != nil {
		t.Fatalf("%s: %s", message, err)
	}
}

// AssertError checks that err is non-nil
func AssertError(t *testing.T, err error, message string) {
	t.Helper()
	if err == nil {
		t.Fatalf("%s: expected error but received none", message)
	}
}

// AssertEquals uses the equality operator (==) to measure one and two
func AssertEquals(t *testing.T, one interface{}, two interface{}) {
	t.Helper()
	if reflect.TypeOf(one) != reflect.TypeOf(two) {
		t.Fatalf("cannot test equality of different types: %T != %T", one, two)
	}
	if one != two {
		t.Fatalf("%#v != %#v", one, two)
	}
}

// AssertDeepEquals uses the reflect.DeepEqual method to measure one and two
func AssertDeepEquals(t *testing.T, one interface{}, two interface{}) {
	t.Helper()
	if !reflect.DeepEqual(one, two) {
		t.Fatalf("[%+v] !(deep)= [%+v]", one, two)
	}
}

// AssertMarshaledEquals marshals one and two to JSON, and then uses
// the equality operator to measure them
func AssertMarshaledEquals(t *testing.T, one interface{}, two interface{}) {
	oneJSON, err := json.Marshal(one)
	AssertNotError(t, err, "Could not marshal 1st argument")
	twoJSON, err := json.Marshal(two)
	AssertNotError(t, err, "Could not marshal 2nd argument")

	if !bytes.Equal(oneJSON, twoJSON) {
		t.Fatalf("[%s] !(json)= [%s]", oneJSON, twoJSON)
	}
}

// AssertUnmarshaledEquals unmarshals two JSON strings (got and expected) to
// a map[string]interface{} and then uses reflect.DeepEqual to check they are
// the same
func AssertUnmarshaledEquals(t *testing.T, got, expected string) {
	t.Helper()
	var gotMap, expectedMap map[string]interface{}
	err := json.Unmarshal([]byte(got), &gotMap)
	AssertNotError(t, err, "Could not unmarshal 'got'")
	err = json.Unmarshal([]byte(expected), &expectedMap)
	AssertNotError(t, err, "Could not unmarshal 'expected'")
	if len(gotMap) != len(expectedMap) {
		t.Errorf("Expected had %d keys, got had %d", len(gotMap), len(expectedMap))
	}
	for k, v := range expectedMap {
		if !reflect.DeepEqual(v, gotMap[k]) {
			t.Errorf("Field %q: Expected \"%v\", got \"%v\"", k, v, gotMap[k])
		}
	}
}

// AssertNotEquals uses the equality operator to measure that one and two
// are different
func AssertNotEquals(t *testing.T, one interface{}, two interface{}) {
	t.Helper()
	if one == two {
		t.Fatalf("%#v == %#v", one, two)
	}
}

// AssertByteEquals uses bytes.Equal to measure one and two for equality.
func AssertByteEquals(t *testing.T, one []byte, two []byte) {
	t.Helper()
	if !bytes.Equal(one, two) {
		t.Fatalf("Byte [%s] != [%s]",
			base64.StdEncoding.EncodeToString(one),
			base64.StdEncoding.EncodeToString(two))
	}
}

// AssertContains determines whether needle can be found in haystack
func AssertContains(t *testing.T, haystack string, needle string) {
	t.Helper()
	if !strings.Contains(haystack, needle) {
		t.Fatalf("String [%s] does not contain [%s]", haystack, needle)
	}
}

// AssertNotContains determines if needle is not found in haystack
func AssertNotContains(t *testing.T, haystack string, needle string) {
	t.Helper()
	if strings.Contains(haystack, needle) {
		t.Fatalf("String [%s] contains [%s]", haystack, needle)
	}
}

// CountCounterVec returns the count by label and value of a prometheus metric
func CountCounterVec(labelName string, value string, counterVec *prometheus.CounterVec) int {
	return CountCounter(counterVec.With(prometheus.Labels{labelName: value}))
}

// CountCounter returns the count by label and value of a prometheus metric
func CountCounter(counter prometheus.Counter) int {
	ch := make(chan prometheus.Metric, 10)
	counter.Collect(ch)
	var m prometheus.Metric
	select {
	case <-time.After(time.Second):
		panic("timed out collecting metrics")
	case m = <-ch:
	}
	var iom io_prometheus_client.Metric
	_ = m.Write(&iom)
	return int(iom.Counter.GetValue())
}

func CountHistogramSamples(hist prometheus.Histogram) int {
	ch := make(chan prometheus.Metric, 10)
	hist.Collect(ch)
	var m prometheus.Metric
	select {
	case <-time.After(time.Second):
		panic("timed out collecting metrics")
	case m = <-ch:
	}
	var iom io_prometheus_client.Metric
	_ = m.Write(&iom)
	return int(iom.Histogram.GetSampleCount())
}

// GaugeValueWithLabels returns the current value with the provided labels from the
// the GaugeVec argument, or an error if there was a problem collecting the value.
func GaugeValueWithLabels(vecGauge *prometheus.GaugeVec, labels prometheus.Labels) (int, error) {
	gauge, err := vecGauge.GetMetricWith(labels)
	if err != nil {
		return 0, err
	}

	ch := make(chan prometheus.Metric, 10)
	gauge.Collect(ch)
	var m prometheus.Metric
	select {
	case <-time.After(time.Second):
		return 0, fmt.Errorf("timed out collecting gauge metrics")
	case m = <-ch:
	}
	var iom io_prometheus_client.Metric
	_ = m.Write(&iom)

	return int(iom.Gauge.GetValue()), nil
}
<|endoftext|>


<commit_before>package main

import "fmt"

type gimmieStuff interface {
	GetPrinter() func([]string)
}

func main() {

	s := []string{"hello", "dolly", "world"}

	s2 := s[1:]
	s3 := s[0:1]

	fmt.Println(s)
	fmt.Println(s2)
	fmt.Println(s3)

	s[0] = "goodbye"
	s[1] = "cruel"

	// fmt.Println(s)
	// fmt.Println(s2)
	// fmt.Println(s3)

	// f := GetPrinter()
	// f(s)
	// f(s2)
	// f(s3)

	// g := giver{}

	// f2 := g.GetPrinter()
	// f2(s)

	// f3 := GetAPrinter(g)
	// f3(s)

	// m := make(map[int]string)

	// m[1] = "one"
	// m[2] = "two"
	// m[3] = "three"

	// for k, v := range m {
	// 	fmt.Println(k, ":", v)
	// }

	// gg := newGoGiver()
	// gg.Print(s)
	// gg.Done()
}

// func GetPrinter() func([]string) {
// 	return func(s []string) {
// 		fmt.Println(s)
// 	}
// }

// type giver struct{}

// func (g giver) GetPrinter() func([]string) {
// 	return func(s []string) {
// 		fmt.Println("giver:", s)
// 	}
// }

// type printer func([]string)

// func GetAPrinter(g gimmieStuff) printer {
// 	return g.GetPrinter()
// }

// type goGiver struct {
// 	s    chan []string
// 	done chan bool
// 	wg   *sync.WaitGroup
// }

// func newGoGiver() (g goGiver) {
// 	g = goGiver{s: make(chan []string),
// 		done: make(chan bool),
// 		wg:   &sync.WaitGroup{}}
// 	g.wg.Add(1)
// 	go g.Run()
// 	return g
// }

// func (g goGiver) Run() {
// 	defer g.wg.Done()
// 	for {
// 		select {
// 		case msg := <-g.s:
// 			fmt.Println("goGiver:", msg)
// 		case <-g.done:
// 			fmt.Println("closing")
// 			return

// 		}
// 	}
// }
// func (g goGiver) Done() {
// 	g.done <- true
// 	g.wg.Wait()
// 	fmt.Println("closed")
// }
// func (g goGiver) Print(s []string) {
// 	g.s <- s
// }
<commit_msg>Final view, added copy stuff<commit_after>package main

import (
	"fmt"
	"sync"
)

type gimmieStuff interface {
	GetPrinter() func([]string)
}

func main() {

	s := []string{"hello", "dolly", "world"}

	s2 := s[1:]
	s3 := s[0:1]
	s4 := make([]string, len(s[1:]))
	copy(s4, s[1:])

	fmt.Println(s)
	fmt.Println(s2)
	fmt.Println(s3)

	s[0] = "goodbye"
	s[1] = "cruel"

	fmt.Println(s)
	fmt.Println(s2)
	fmt.Println(s3)
	fmt.Println(s4)

	f := GetPrinter()
	f(s)
	f(s2)
	f(s3)

	g := giver{}

	f2 := g.GetPrinter()
	f2(s)

	f3 := GetAPrinter(g)
	f3(s)

	m := make(map[int]string)

	m[1] = "one"
	m[2] = "two"
	m[3] = "three"

	for k, v := range m {
		fmt.Println(k, ":", v)
	}

	gg := newGoGiver()
	gg.Print(s)
	gg.Done()
}

func GetPrinter() func([]string) {
	return func(s []string) {
		fmt.Println(s)
	}
}

type giver struct{}

func (g giver) GetPrinter() func([]string) {
	return func(s []string) {
		fmt.Println("giver:", s)
	}
}

type printer func([]string)

func GetAPrinter(g gimmieStuff) printer {
	return g.GetPrinter()
}

type goGiver struct {
	s    chan []string
	done chan bool
	wg   *sync.WaitGroup
}

func newGoGiver() (g goGiver) {
	g = goGiver{s: make(chan []string),
		done: make(chan bool),
		wg:   &sync.WaitGroup{}}
	g.wg.Add(1)
	go g.Run()
	return g
}

func (g goGiver) Run() {
	defer g.wg.Done()
	for {
		select {
		case msg := <-g.s:
			fmt.Println("goGiver:", msg)
		case <-g.done:
			fmt.Println("closing")
			return

		}
	}
}

func (g goGiver) Done() {
	g.done <- true
	g.wg.Wait()
	fmt.Println("closed")
}
func (g goGiver) Print(s []string) {
	g.s <- s
}
<|endoftext|>


<commit_before>package handlers

import (
	"github.com/gorilla/mux"
)

// SetupHandlers loads all routes into gorillaMux.
func SetupHandlers() *mux.Router {
	r := mux.NewRouter()
	// r.StrictSlash(true)
	m := r.PathPrefix("/api").Subrouter()

	// Auth
	m.HandleFunc("/session", Login).Methods("POST")

	// Users
	m.HandleFunc("/user", AuthRequired(GetAllUsers)).Methods("GET")

	// Classes
	m.HandleFunc("/class", AuthRequired(GetAllClasses)).Methods("GET")
	m.HandleFunc("/class", AuthRequired(CreateClass)).Methods("POST")
	m.HandleFunc("/class/{id}", AuthRequired(GetClass)).Methods("GET")
	m.HandleFunc("/class/{id}", AuthRequired(UpdateClass)).Methods("PUT")

	// ClassTerms
	m.HandleFunc("term", AuthRequired(GetAllClassTerms)).Methods("GET")
	m.HandleFunc("term", AuthRequired(CreateClassTerm)).Methods("POST")
	m.HandleFunc("term/{id}", AuthRequired(GetClassTerm)).Methods("GET")
	m.HandleFunc("term/{id}", AuthRequired(UpdateClassTerm)).Methods("PUT")

	// Assignments
	m.HandleFunc("assignment", AuthRequired(GetAllAssignments)).Methods("GET")
	m.HandleFunc("assignment", AuthRequired(CreateAssignment)).Methods("POST")
	m.HandleFunc("assignment/{id}", AuthRequired(GetAssignment)).Methods("GET")
	m.HandleFunc("assignment/{id}", AuthRequired(UpdateAssignment)).Methods("PUT")

	// People
	m.HandleFunc("/person", AuthRequired(GetAllPeople)).Methods("GET")
	m.HandleFunc("/person", AuthRequired(CreatePerson)).Methods("POST")
	m.HandleFunc("/person/{id}", AuthRequired(GetPerson)).Methods("GET")
	m.HandleFunc("/person/{id}", AuthRequired(UpdatePerson)).Methods("PUT")

	// Students
	m.HandleFunc("/student", AuthRequired(GetAllStudents)).Methods("GET")
	m.HandleFunc("/student", AuthRequired(CreateStudent)).Methods("POST")
	m.HandleFunc("/student/{id}", AuthRequired(GetStudent)).Methods("GET")
	m.HandleFunc("/student/{id}", AuthRequired(UpdateStudent)).Methods("PUT")

	// Teachers
	m.HandleFunc("/teacher", AuthRequired(GetAllTeachers)).Methods("GET")
	m.HandleFunc("/teacher", AuthRequired(CreateTeacher)).Methods("POST")
	m.HandleFunc("/teacher/{id}", AuthRequired(GetTeacher)).Methods("GET")
	m.HandleFunc("/teacher/{id}", AuthRequired(UpdateTeacher)).Methods("PUT")
	return r
}
<commit_msg>Added routes for Assignment Grades.<commit_after>package handlers

import (
	"github.com/gorilla/mux"
)

// SetupHandlers loads all routes into gorillaMux.
func SetupHandlers() *mux.Router {
	r := mux.NewRouter()
	// r.StrictSlash(true)
	m := r.PathPrefix("/api").Subrouter()

	// Auth
	m.HandleFunc("/session", Login).Methods("POST")

	// Users
	m.HandleFunc("/user", AuthRequired(GetAllUsers)).Methods("GET")

	// Classes
	m.HandleFunc("/class", AuthRequired(GetAllClasses)).Methods("GET")
	m.HandleFunc("/class", AuthRequired(CreateClass)).Methods("POST")
	m.HandleFunc("/class/{id}", AuthRequired(GetClass)).Methods("GET")
	m.HandleFunc("/class/{id}", AuthRequired(UpdateClass)).Methods("PUT")

	// ClassTerms
	m.HandleFunc("term", AuthRequired(GetAllClassTerms)).Methods("GET")
	m.HandleFunc("term", AuthRequired(CreateClassTerm)).Methods("POST")
	m.HandleFunc("term/{id}", AuthRequired(GetClassTerm)).Methods("GET")
	m.HandleFunc("term/{id}", AuthRequired(UpdateClassTerm)).Methods("PUT")

	// Assignments
	m.HandleFunc("assignment", AuthRequired(GetAllAssignments)).Methods("GET")
	m.HandleFunc("assignment", AuthRequired(CreateAssignment)).Methods("POST")
	m.HandleFunc("assignment/{id}", AuthRequired(GetAssignment)).Methods("GET")
	m.HandleFunc("assignment/{id}", AuthRequired(UpdateAssignment)).Methods("PUT")

	// AssignmentGrades
	m.HandleFunc("grade", AuthRequired(GetAllAssignmentGrades)).Methods("GET")
	m.HandleFunc("grade", AuthRequired(CreateAssignmentGrade)).Methods("POST")
	m.HandleFunc("grade/{id}", AuthRequired(GetAssignmentGrade)).Methods("GET")
	m.HandleFunc("grade/{id}", AuthRequired(UpdateAssignmentGrade)).Methods("PUT")

	// People
	m.HandleFunc("/person", AuthRequired(GetAllPeople)).Methods("GET")
	m.HandleFunc("/person", AuthRequired(CreatePerson)).Methods("POST")
	m.HandleFunc("/person/{id}", AuthRequired(GetPerson)).Methods("GET")
	m.HandleFunc("/person/{id}", AuthRequired(UpdatePerson)).Methods("PUT")

	// Students
	m.HandleFunc("/student", AuthRequired(GetAllStudents)).Methods("GET")
	m.HandleFunc("/student", AuthRequired(CreateStudent)).Methods("POST")
	m.HandleFunc("/student/{id}", AuthRequired(GetStudent)).Methods("GET")
	m.HandleFunc("/student/{id}", AuthRequired(UpdateStudent)).Methods("PUT")

	// Teachers
	m.HandleFunc("/teacher", AuthRequired(GetAllTeachers)).Methods("GET")
	m.HandleFunc("/teacher", AuthRequired(CreateTeacher)).Methods("POST")
	m.HandleFunc("/teacher/{id}", AuthRequired(GetTeacher)).Methods("GET")
	m.HandleFunc("/teacher/{id}", AuthRequired(UpdateTeacher)).Methods("PUT")
	return r
}
<|endoftext|>


<commit_before>package veneur

import (
	"compress/zlib"
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/stripe/veneur/v14/samplers"
	"github.com/stripe/veneur/v14/ssf"
	"github.com/stripe/veneur/v14/trace"
	"github.com/stripe/veneur/v14/trace/metrics"
)

type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request)

func (ch contextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()
	ch(ctx, w, r)
}

func handleProxy(p *Proxy) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		log.WithFields(logrus.Fields{
			"path": r.URL.Path,
			"host": r.URL.Host,
		}).Debug("Importing metrics on proxy")
		span, jsonMetrics, err := unmarshalMetricsFromHTTP(ctx, p.TraceClient, w, r)
		if err != nil {
			log.WithError(err).Error("Error unmarshalling metrics in proxy import")
			return
		}
		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go p.ProxyMetrics(span.Attach(ctx), jsonMetrics, strings.SplitN(r.RemoteAddr, ":", 2)[0])
	})
}

func handleSpans(p *Proxy) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		span, traces, err := handleTraceRequest(ctx, p.TraceClient, w, r)

		if err != nil {
			return
		}
		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go p.ProxyTraces(span.Attach(ctx), traces)
	})
}

// handleImport generates the handler that responds to POST requests submitting
// metrics to the global veneur instance.
func handleImport(s *Server) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		span, jsonMetrics, err := unmarshalMetricsFromHTTP(ctx, s.TraceClient, w, r)
		if err != nil {
			log.WithError(err).Error("Error unmarshalling metrics in global import")
			span.Add(ssf.Count("import.unmarshal.errors_total", 1, nil))
			return
		}
		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go s.ImportMetrics(span.Attach(ctx), jsonMetrics)
	})
}

func handleTraceRequest(ctx context.Context, client *trace.Client, w http.ResponseWriter, r *http.Request) (*trace.Span, []DatadogTraceSpan, error) {
	var (
		traces []DatadogTraceSpan
		err    error
		span   *trace.Span
	)

	span, err = tracer.ExtractRequestChild("/spans", r, "veneur.opentracing.spans")
	if err != nil {
		log.WithError(err).Info("Could not extract span from request")
		span = tracer.StartSpan("/spans").(*trace.Span)
	} else {
		log.WithField("trace", span.Trace).Info("Extracted span from request")
	}
	defer span.ClientFinish(client)

	innerLogger := log.WithField("client", r.RemoteAddr)

	if err = json.NewDecoder(r.Body).Decode(&traces); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		span.Error(err)
		innerLogger.WithError(err).Error("Could not decode /spans request")
		metrics.ReportOne(client, ssf.Count("import.request_error_total", 1, map[string]string{"cause": "json"}))
		return nil, nil, err
	}

	if len(traces) == 0 {
		const msg = "Received empty /spans request"
		http.Error(w, msg, http.StatusBadRequest)
		span.Error(errors.New(msg))
		innerLogger.WithError(err).Error(msg)
		return nil, nil, err
	}

	w.WriteHeader(http.StatusAccepted)
	metrics.ReportOne(client, ssf.Timing("spans.response_duration_ns",
		time.Since(span.Start), time.Nanosecond,
		map[string]string{"part": "request"}))
	return span, traces, nil
}

// unmarshalMetricsFromHTTP takes care of the common need to unmarshal a slice of metrics from a request body,
// dealing with error handling, decoding, tracing, and the associated metrics.
func unmarshalMetricsFromHTTP(ctx context.Context, client *trace.Client, w http.ResponseWriter, r *http.Request) (*trace.Span, []samplers.JSONMetric, error) {
	var (
		jsonMetrics []samplers.JSONMetric
		body        io.ReadCloser
		err         error
		encoding    = r.Header.Get("Content-Encoding")
		span        *trace.Span
	)
	span, err = tracer.ExtractRequestChild("/import", r, "veneur.opentracing.import")
	if err != nil {
		log.WithError(err).Debug("Could not extract span from request")
		span = tracer.StartSpan("veneur.opentracing.import").(*trace.Span)
	} else {
		log.WithField("trace", span.Trace).Debug("Extracted span from request")
	}
	defer span.ClientFinish(client)

	innerLogger := log.WithField("client", r.RemoteAddr)

	switch encLogger := innerLogger.WithField("encoding", encoding); encoding {
	case "":
		body = r.Body
		encoding = "identity"
	case "deflate":
		body, err = zlib.NewReader(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			span.Error(err)
			encLogger.WithError(err).Error("Could not read compressed request body")
			span.Add(ssf.Count("import.request_error_total", 1, map[string]string{"cause": "deflate"}))
			return span, nil, err
		}
		defer body.Close()
	default:
		http.Error(w, encoding, http.StatusUnsupportedMediaType)
		span.Error(errors.New("Could not determine content-encoding of request"))
		encLogger.Error("Could not determine content-encoding of request")
		span.Add(ssf.Count("import.request_error_total", 1, map[string]string{"cause": "unknown_content_encoding"}))
		return span, nil, err
	}
	span.Add(ssf.Count("import.bytes", float32(r.ContentLength), nil))

	if err = json.NewDecoder(body).Decode(&jsonMetrics); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		span.Error(err)
		innerLogger.WithError(err).Error("Could not decode /import request")
		span.Add(ssf.Count("import.request_error_total", 1, map[string]string{"cause": "json"}))
		return span, nil, err
	}

	if len(jsonMetrics) == 0 {
		const msg = "Received empty /import request"
		http.Error(w, msg, http.StatusBadRequest)
		span.Error(errors.New(msg))
		innerLogger.WithError(err).Error(msg)
		return span, nil, err
	}

	// We want to make sure we have at least one entry
	// that is not empty (ie, all fields are the zero value)
	// because that is usually the sign that we are unmarshalling
	// into the wrong struct type

	if !nonEmpty(span.Attach(ctx), client, jsonMetrics) {
		const msg = "Received empty or improperly-formed metrics"
		http.Error(w, msg, http.StatusBadRequest)
		span.Error(errors.New(msg))
		innerLogger.Error(msg)
		return nil, nil, err
	}

	w.WriteHeader(http.StatusAccepted)
	span.Add(ssf.RandomlySample(0.1,
		ssf.Timing("import.response_duration_ns", time.Since(span.Start),
			time.Nanosecond, map[string]string{
				"part":     "request",
				"encoding": encoding,
			}))...)
	return span, jsonMetrics, nil
}

// nonEmpty returns true if there is at least one non-empty
// metric
func nonEmpty(ctx context.Context, client *trace.Client, jsonMetrics []samplers.JSONMetric) bool {

	span, _ := trace.StartSpanFromContext(ctx, "veneur.opentracing.import.nonEmpty")
	defer span.ClientFinish(client)

	sentinel := samplers.JSONMetric{}
	for _, metric := range jsonMetrics {
		if !reflect.DeepEqual(sentinel, metric) {
			// we have found at least one entry that is properly formed
			return true

		}
	}
	return false
}
<commit_msg>Remove dead code. (#884)<commit_after>package veneur

import (
	"compress/zlib"
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/stripe/veneur/v14/samplers"
	"github.com/stripe/veneur/v14/ssf"
	"github.com/stripe/veneur/v14/trace"
)

type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request)

func (ch contextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()
	ch(ctx, w, r)
}

func handleProxy(p *Proxy) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		log.WithFields(logrus.Fields{
			"path": r.URL.Path,
			"host": r.URL.Host,
		}).Debug("Importing metrics on proxy")
		span, jsonMetrics, err := unmarshalMetricsFromHTTP(ctx, p.TraceClient, w, r)
		if err != nil {
			log.WithError(err).Error("Error unmarshalling metrics in proxy import")
			return
		}
		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go p.ProxyMetrics(span.Attach(ctx), jsonMetrics, strings.SplitN(r.RemoteAddr, ":", 2)[0])
	})
}

// handleImport generates the handler that responds to POST requests submitting
// metrics to the global veneur instance.
func handleImport(s *Server) http.Handler {
	return contextHandler(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		span, jsonMetrics, err := unmarshalMetricsFromHTTP(ctx, s.TraceClient, w, r)
		if err != nil {
			log.WithError(err).Error("Error unmarshalling metrics in global import")
			span.Add(ssf.Count("import.unmarshal.errors_total", 1, nil))
			return
		}
		// the server usually waits for this to return before finalizing the
		// response, so this part must be done asynchronously
		go s.ImportMetrics(span.Attach(ctx), jsonMetrics)
	})
}

// unmarshalMetricsFromHTTP takes care of the common need to unmarshal a slice of metrics from a request body,
// dealing with error handling, decoding, tracing, and the associated metrics.
func unmarshalMetricsFromHTTP(ctx context.Context, client *trace.Client, w http.ResponseWriter, r *http.Request) (*trace.Span, []samplers.JSONMetric, error) {
	var (
		jsonMetrics []samplers.JSONMetric
		body        io.ReadCloser
		err         error
		encoding    = r.Header.Get("Content-Encoding")
		span        *trace.Span
	)
	span, err = tracer.ExtractRequestChild("/import", r, "veneur.opentracing.import")
	if err != nil {
		log.WithError(err).Debug("Could not extract span from request")
		span = tracer.StartSpan("veneur.opentracing.import").(*trace.Span)
	} else {
		log.WithField("trace", span.Trace).Debug("Extracted span from request")
	}
	defer span.ClientFinish(client)

	innerLogger := log.WithField("client", r.RemoteAddr)

	switch encLogger := innerLogger.WithField("encoding", encoding); encoding {
	case "":
		body = r.Body
		encoding = "identity"
	case "deflate":
		body, err = zlib.NewReader(r.Body)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			span.Error(err)
			encLogger.WithError(err).Error("Could not read compressed request body")
			span.Add(ssf.Count("import.request_error_total", 1, map[string]string{"cause": "deflate"}))
			return span, nil, err
		}
		defer body.Close()
	default:
		http.Error(w, encoding, http.StatusUnsupportedMediaType)
		span.Error(errors.New("Could not determine content-encoding of request"))
		encLogger.Error("Could not determine content-encoding of request")
		span.Add(ssf.Count("import.request_error_total", 1, map[string]string{"cause": "unknown_content_encoding"}))
		return span, nil, err
	}
	span.Add(ssf.Count("import.bytes", float32(r.ContentLength), nil))

	if err = json.NewDecoder(body).Decode(&jsonMetrics); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		span.Error(err)
		innerLogger.WithError(err).Error("Could not decode /import request")
		span.Add(ssf.Count("import.request_error_total", 1, map[string]string{"cause": "json"}))
		return span, nil, err
	}

	if len(jsonMetrics) == 0 {
		const msg = "Received empty /import request"
		http.Error(w, msg, http.StatusBadRequest)
		span.Error(errors.New(msg))
		innerLogger.WithError(err).Error(msg)
		return span, nil, err
	}

	// We want to make sure we have at least one entry
	// that is not empty (ie, all fields are the zero value)
	// because that is usually the sign that we are unmarshalling
	// into the wrong struct type

	if !nonEmpty(span.Attach(ctx), client, jsonMetrics) {
		const msg = "Received empty or improperly-formed metrics"
		http.Error(w, msg, http.StatusBadRequest)
		span.Error(errors.New(msg))
		innerLogger.Error(msg)
		return nil, nil, err
	}

	w.WriteHeader(http.StatusAccepted)
	span.Add(ssf.RandomlySample(0.1,
		ssf.Timing("import.response_duration_ns", time.Since(span.Start),
			time.Nanosecond, map[string]string{
				"part":     "request",
				"encoding": encoding,
			}))...)
	return span, jsonMetrics, nil
}

// nonEmpty returns true if there is at least one non-empty
// metric
func nonEmpty(ctx context.Context, client *trace.Client, jsonMetrics []samplers.JSONMetric) bool {

	span, _ := trace.StartSpanFromContext(ctx, "veneur.opentracing.import.nonEmpty")
	defer span.ClientFinish(client)

	sentinel := samplers.JSONMetric{}
	for _, metric := range jsonMetrics {
		if !reflect.DeepEqual(sentinel, metric) {
			// we have found at least one entry that is properly formed
			return true

		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"

	"github.com/pkg/errors"
)

type jsonObjectRaw map[string]json.RawMessage
type jsonObject map[string]interface{}

type tilePosition struct {
	collection string
	path       string
}

// Log to standard error
var stderr = log.New(os.Stderr, "", 0)

// List of known file position
var fileList = make(map[string][]tilePosition, 4096)

var nameSelect = regexp.MustCompile(`[^:/\\]+$`)
var tilepathCut = regexp.MustCompile(`:/{0,2}`)

func getJSONRawSlice(r jsonObjectRaw, k string) (*[]jsonObjectRaw, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var rawSlice []jsonObjectRaw
	err := json.Unmarshal(blob, &rawSlice)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &rawSlice, nil
}

func getJSONSlice(r jsonObjectRaw, k string) (*[]jsonObject, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var decodedSlice []jsonObject
	err := json.Unmarshal(blob, &decodedSlice)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &decodedSlice, nil
}

func getJSONRawObject(r jsonObjectRaw, k string) (*jsonObjectRaw, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var decodedObject jsonObjectRaw
	err := json.Unmarshal(blob, &decodedObject)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &decodedObject, nil
}

func getJSONObject(r jsonObjectRaw, k string) (*jsonObject, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var decodedObject jsonObject
	err := json.Unmarshal(blob, &decodedObject)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &decodedObject, nil
}

func readMapFile(path string) (*jsonObjectRaw, error) {
	// Read the file
	mapBlob, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.Wrap(err, "read failed")
	}
	// Decode it in hexMap
	var hexMap jsonObjectRaw
	err = json.Unmarshal(mapBlob, &hexMap)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &hexMap, nil
}

func findHomeDir() (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "unable to get user information: ")
	}
	return u.HomeDir, nil
}

func readSettingsBlob(userConfig string) ([]byte, error) {
	settings := filepath.Join(userConfig, "hex-kit", "Settings")
	stderr.Print("Reading user settings from: ", settings)
	settingsBlob, err := ioutil.ReadFile(settings)
	return settingsBlob, err
}

// https://electron.atom.io/docs/api/app/#appgetpathname
func getSettings() (jsonObjectRaw, error) {
	var userConfig string
	var settingsBlob []byte
	var settingsRaw jsonObjectRaw
	var err error
	switch runtime.GOOS {
	case "darwin":
		homeDir, err := findHomeDir()
		if err != nil {
			return nil, err
		}
		userConfig = filepath.Join(homeDir, "Library", "Application Support")
		settingsBlob, err = readSettingsBlob(userConfig)
	case "linux":
		userConfig = os.Getenv("XDG_CONFIG_HOME")
		if userConfig != "" {
			settingsBlob, err = readSettingsBlob(userConfig)
		}
		if userConfig == "" || err != nil {
			homeDir, err := findHomeDir()
			if err != nil {
				return nil, err
			}
			userConfig = filepath.Join(homeDir, ".config")
			settingsBlob, err = readSettingsBlob(userConfig)
		}
	case "windows":
		userConfig = os.Getenv("APPDATA")
		if userConfig == "" {
			return nil, errors.New("Error: unable to find user config (no APPDATA environment variable)")
		}
		settingsBlob, err = readSettingsBlob(userConfig)
	}
	// Did the log read succeed
	if err != nil {
		return nil, errors.Wrap(err, "unable to read settings")
	}
	err = json.Unmarshal(settingsBlob, &settingsRaw)
	if err != nil {
		return nil, errors.Wrap(err, "unable to decode settings")
	}
	return settingsRaw, nil
}

// Search fon all png files under the current path
func pathMap(collectionName, basePath string) filepath.WalkFunc {
	return func(path string, info os.FileInfo, err error) error {
		if err != nil {
			stderr.Println("Warning: while searching for PNG files: ", err)
			return nil
		}
		tileName := info.Name()
		lenPath := len(path)
		if lenPath > 4 && path[lenPath-4:] == ".png" {
			relPathTile, _ := filepath.Rel(basePath, path)
			var tp tilePosition
			tp.collection = collectionName
			tp.path = filepath.ToSlash(filepath.Clean(relPathTile))
			fileList[tileName] = append(fileList[tileName], tp)
		}
		return nil
	}
}

func getCollectionDir(settings jsonObjectRaw) (*map[string]string, error) {
	// Build the list of collections
	collectionsDir := make(map[string]string)
	collections, err := getJSONRawObject(settings, "tiles")
	if err != nil {
		return nil, errors.Wrap(err, "unable to access the \"tiles\" list")
	}
	for name, collectionBlob := range *collections {
		var collection jsonObject
		err := json.Unmarshal(collectionBlob, &collection)
		if err != nil {
			return nil, errors.Wrapf(err, "unable to parse tiles[%s]", name)
		}
		// Ignore source if hidden
		hiddenIntf, ok := collection["hidden"]
		if ok {
			hidden, isBool := hiddenIntf.(bool)
			if isBool && hidden {
				continue
			}
		}
		pathIntf, ok := collection["path"]
		if !ok {
			return nil, errors.Wrapf(err, "no path for %s", name)
		}
		path, ok := pathIntf.(string)
		if !ok {
			return nil, errors.Wrapf(err, "the path for %s is not a string", name)
		}
		// Relative collection path
		if !filepath.IsAbs(path) {
			collectionsDir[name] = filepath.Join(os.Args[1], "resources", "app.asar.unpacked", path)
			continue
		}
		// Absolute collection path
		collectionsDir[name] = path
	}
	return &collectionsDir, nil

}

func main() {
	if len(os.Args) != 3 {
		stderr.Println("Usage:", os.Args[0], "HexkitPath MapPath")
		return
	}
	// Read the settngs and get the
	settings, err := getSettings()
	if err != nil {
		log.Fatal("Unable to read user settings: ", err)
	}
	collectionsDir, err := getCollectionDir(settings)
	if err != nil {
		log.Fatal("Unable to read the list of collections: ", err)
	}
	// Build the list of PNG files
	for name, path := range *collectionsDir {
		err := filepath.Walk(path, pathMap(name, path))
		if err != nil {
			stderr.Fatal(err)
		}
	}
	// Read the file
	hexMap, err := readMapFile(os.Args[2])
	if err != nil {
		stderr.Fatal("Error: unable to read map file: ", err)
	}
	// Get the layers list
	layers, err := getJSONRawSlice(*hexMap, "layers")
	if err != nil {
		stderr.Fatal("Map format error", err)
	}
	// Search for tiles
	layersModified := false
	for i, v := range *layers {
		tiles, err := getJSONSlice(v, "tiles")
		if err != nil {
			stderr.Fatal("Map format error in layer ", i+1, ": ", err)
		}
		// Search for the source of tiles
		tilesModified := false
		for j, t := range *tiles {
			// Ignore undefined tiles
			if t == nil {
				continue
			}
			sourceBlob, ok := t["source"]
			if !ok {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "no tile source found")
				continue
			}
			source, ok := sourceBlob.(string)
			if !ok {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "the tile source is incorrect (not a string)")
				continue
			}
			// Skip the default blank tiles
			if source[:6] == "Blank:" {
				continue
			}
			// Have we found the tile
			fileName := nameSelect.FindString(source)
			pathList, ok := fileList[fileName]
			if !ok {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "unable to find tile image file for", source)
				continue
			}
			// Search for the current path in the
			firstSplit := tilepathCut.Split(source, 2)
			if len(firstSplit) < 2 {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "incorrect source: ", source)
				continue
			}
			targetCollection := firstSplit[0]
			targetPath := firstSplit[1]
			var bestScore int
			var selected tilePosition
		pathSearch:
			for _, p := range pathList {
				if targetCollection == p.collection && targetPath == p.path {
					break pathSearch
				}
				currentScore := len(p.path)
				if targetCollection == p.collection && (currentScore+256) > bestScore {
					bestScore = currentScore + 256
					selected = p
				}
				if currentScore > bestScore {
					bestScore = currentScore
					selected = p
				}
			}
			// A new value was found: update the source
			if selected.collection != "" && selected.path != "" {
				tilesModified = true
				t["source"] = selected.collection + "://" + selected.path
			}
		}
		if tilesModified {
			tilesBlob, err := json.Marshal(tiles)
			if err != nil {
				stderr.Fatal(err)
			}
			v["tiles"] = tilesBlob
			layersModified = true
		}
	}
	if layersModified {
		layersBlob, err := json.Marshal(layers)
		if err != nil {
			stderr.Fatal(err)
		}
		(*hexMap)["layers"] = layersBlob
	}
	b, err := json.Marshal(*hexMap)
	if err != nil {
		stderr.Fatal(err)
	}
	_, err = os.Stdout.Write(b)
	if err != nil {
		stderr.Fatal(err)
	}
}
<commit_msg>Dead code removal<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"

	"github.com/pkg/errors"
)

type jsonObjectRaw map[string]json.RawMessage
type jsonObject map[string]interface{}

type tilePosition struct {
	collection string
	path       string
}

// Log to standard error
var stderr = log.New(os.Stderr, "", 0)

// List of known file position
var fileList = make(map[string][]tilePosition, 4096)

var nameSelect = regexp.MustCompile(`[^:/\\]+$`)
var tilepathCut = regexp.MustCompile(`:/{0,2}`)

func getJSONRawSlice(r jsonObjectRaw, k string) (*[]jsonObjectRaw, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var rawSlice []jsonObjectRaw
	err := json.Unmarshal(blob, &rawSlice)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &rawSlice, nil
}

func getJSONSlice(r jsonObjectRaw, k string) (*[]jsonObject, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var decodedSlice []jsonObject
	err := json.Unmarshal(blob, &decodedSlice)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &decodedSlice, nil
}

func getJSONRawObject(r jsonObjectRaw, k string) (*jsonObjectRaw, error) {
	blob, ok := r[k]
	if !ok {
		return nil, fmt.Errorf("no \"%s\" key in object", k)
	}
	var decodedObject jsonObjectRaw
	err := json.Unmarshal(blob, &decodedObject)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &decodedObject, nil
}

func readMapFile(path string) (*jsonObjectRaw, error) {
	// Read the file
	mapBlob, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.Wrap(err, "read failed")
	}
	// Decode it in hexMap
	var hexMap jsonObjectRaw
	err = json.Unmarshal(mapBlob, &hexMap)
	if err != nil {
		return nil, errors.Wrap(err, "JSON decode failed")
	}
	return &hexMap, nil
}

func findHomeDir() (string, error) {
	u, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "unable to get user information: ")
	}
	return u.HomeDir, nil
}

func readSettingsBlob(userConfig string) ([]byte, error) {
	settings := filepath.Join(userConfig, "hex-kit", "Settings")
	stderr.Print("Reading user settings from: ", settings)
	settingsBlob, err := ioutil.ReadFile(settings)
	return settingsBlob, err
}

// https://electron.atom.io/docs/api/app/#appgetpathname
func getSettings() (jsonObjectRaw, error) {
	var userConfig string
	var settingsBlob []byte
	var settingsRaw jsonObjectRaw
	var err error
	switch runtime.GOOS {
	case "darwin":
		homeDir, err := findHomeDir()
		if err != nil {
			return nil, err
		}
		userConfig = filepath.Join(homeDir, "Library", "Application Support")
		settingsBlob, err = readSettingsBlob(userConfig)
	case "linux":
		userConfig = os.Getenv("XDG_CONFIG_HOME")
		if userConfig != "" {
			settingsBlob, err = readSettingsBlob(userConfig)
		}
		if userConfig == "" || err != nil {
			homeDir, err := findHomeDir()
			if err != nil {
				return nil, err
			}
			userConfig = filepath.Join(homeDir, ".config")
			settingsBlob, err = readSettingsBlob(userConfig)
		}
	case "windows":
		userConfig = os.Getenv("APPDATA")
		if userConfig == "" {
			return nil, errors.New("Error: unable to find user config (no APPDATA environment variable)")
		}
		settingsBlob, err = readSettingsBlob(userConfig)
	}
	// Did the log read succeed
	if err != nil {
		return nil, errors.Wrap(err, "unable to read settings")
	}
	err = json.Unmarshal(settingsBlob, &settingsRaw)
	if err != nil {
		return nil, errors.Wrap(err, "unable to decode settings")
	}
	return settingsRaw, nil
}

// Search fon all png files under the current path
func pathMap(collectionName, basePath string) filepath.WalkFunc {
	return func(path string, info os.FileInfo, err error) error {
		if err != nil {
			stderr.Println("Warning: while searching for PNG files: ", err)
			return nil
		}
		tileName := info.Name()
		lenPath := len(path)
		if lenPath > 4 && path[lenPath-4:] == ".png" {
			relPathTile, _ := filepath.Rel(basePath, path)
			var tp tilePosition
			tp.collection = collectionName
			tp.path = filepath.ToSlash(filepath.Clean(relPathTile))
			fileList[tileName] = append(fileList[tileName], tp)
		}
		return nil
	}
}

func getCollectionDir(settings jsonObjectRaw) (*map[string]string, error) {
	// Build the list of collections
	collectionsDir := make(map[string]string)
	collections, err := getJSONRawObject(settings, "tiles")
	if err != nil {
		return nil, errors.Wrap(err, "unable to access the \"tiles\" list")
	}
	for name, collectionBlob := range *collections {
		var collection jsonObject
		err := json.Unmarshal(collectionBlob, &collection)
		if err != nil {
			return nil, errors.Wrapf(err, "unable to parse tiles[%s]", name)
		}
		// Ignore source if hidden
		hiddenIntf, ok := collection["hidden"]
		if ok {
			hidden, isBool := hiddenIntf.(bool)
			if isBool && hidden {
				continue
			}
		}
		pathIntf, ok := collection["path"]
		if !ok {
			return nil, errors.Wrapf(err, "no path for %s", name)
		}
		path, ok := pathIntf.(string)
		if !ok {
			return nil, errors.Wrapf(err, "the path for %s is not a string", name)
		}
		// Relative collection path
		if !filepath.IsAbs(path) {
			collectionsDir[name] = filepath.Join(os.Args[1], "resources", "app.asar.unpacked", path)
			continue
		}
		// Absolute collection path
		collectionsDir[name] = path
	}
	return &collectionsDir, nil

}

func main() {
	if len(os.Args) != 3 {
		stderr.Println("Usage:", os.Args[0], "HexkitPath MapPath")
		return
	}
	// Read the settngs and get the
	settings, err := getSettings()
	if err != nil {
		log.Fatal("Unable to read user settings: ", err)
	}
	collectionsDir, err := getCollectionDir(settings)
	if err != nil {
		log.Fatal("Unable to read the list of collections: ", err)
	}
	// Build the list of PNG files
	for name, path := range *collectionsDir {
		err := filepath.Walk(path, pathMap(name, path))
		if err != nil {
			stderr.Fatal(err)
		}
	}
	// Read the file
	hexMap, err := readMapFile(os.Args[2])
	if err != nil {
		stderr.Fatal("Error: unable to read map file: ", err)
	}
	// Get the layers list
	layers, err := getJSONRawSlice(*hexMap, "layers")
	if err != nil {
		stderr.Fatal("Map format error", err)
	}
	// Search for tiles
	layersModified := false
	for i, v := range *layers {
		tiles, err := getJSONSlice(v, "tiles")
		if err != nil {
			stderr.Fatal("Map format error in layer ", i+1, ": ", err)
		}
		// Search for the source of tiles
		tilesModified := false
		for j, t := range *tiles {
			// Ignore undefined tiles
			if t == nil {
				continue
			}
			sourceBlob, ok := t["source"]
			if !ok {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "no tile source found")
				continue
			}
			source, ok := sourceBlob.(string)
			if !ok {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "the tile source is incorrect (not a string)")
				continue
			}
			// Skip the default blank tiles
			if source[:6] == "Blank:" {
				continue
			}
			// Have we found the tile
			fileName := nameSelect.FindString(source)
			pathList, ok := fileList[fileName]
			if !ok {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "unable to find tile image file for", source)
				continue
			}
			// Search for the current path in the
			firstSplit := tilepathCut.Split(source, 2)
			if len(firstSplit) < 2 {
				stderr.Println("Warning: layer", i+1, "tile", j+1, "incorrect source: ", source)
				continue
			}
			targetCollection := firstSplit[0]
			targetPath := firstSplit[1]
			var bestScore int
			var selected tilePosition
		pathSearch:
			for _, p := range pathList {
				if targetCollection == p.collection && targetPath == p.path {
					break pathSearch
				}
				currentScore := len(p.path)
				if targetCollection == p.collection && (currentScore+256) > bestScore {
					bestScore = currentScore + 256
					selected = p
				}
				if currentScore > bestScore {
					bestScore = currentScore
					selected = p
				}
			}
			// A new value was found: update the source
			if selected.collection != "" && selected.path != "" {
				tilesModified = true
				t["source"] = selected.collection + "://" + selected.path
			}
		}
		if tilesModified {
			tilesBlob, err := json.Marshal(tiles)
			if err != nil {
				stderr.Fatal(err)
			}
			v["tiles"] = tilesBlob
			layersModified = true
		}
	}
	if layersModified {
		layersBlob, err := json.Marshal(layers)
		if err != nil {
			stderr.Fatal(err)
		}
		(*hexMap)["layers"] = layersBlob
	}
	b, err := json.Marshal(*hexMap)
	if err != nil {
		stderr.Fatal(err)
	}
	_, err = os.Stdout.Write(b)
	if err != nil {
		stderr.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"github.com/Cepave/alarm/g"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/toolkits/file"
	"log"

	"sort"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/open-falcon/alarm/g"
	"github.com/toolkits/file"
)

type MainController struct {
	beego.Controller
}

func (this *MainController) Version() {
	this.Ctx.WriteString(g.VERSION)
}

func (this *MainController) Health() {
	this.Ctx.WriteString("ok")
}

func (this *MainController) Workdir() {
	this.Ctx.WriteString(fmt.Sprintf("%s", file.SelfDir()))
}

func (this *MainController) ConfigReload() {
	remoteAddr := this.Ctx.Request.RemoteAddr
	if strings.HasPrefix(remoteAddr, "127.0.0.1") {
		g.ParseConfig(g.ConfigFile)
		this.Data["json"] = g.Config()
		this.ServeJSON()
	} else {
		this.Ctx.WriteString("no privilege")
	}
}

func SelectSessionBySig(sig string) *Session {
	if sig == "" {
		return nil
	}

	obj := Session{Sig: sig}
	err := orm.NewOrm().Read(&obj, "Sig")
	if err != nil {
		if err != orm.ErrNoRows {
			log.Println(err.Error())
		}
		return nil
	}
	return &obj
}

func DeleteSessionById(id int64) (int64, error) {
	r, err := orm.NewOrm().Raw("DELETE FROM `session` WHERE `id` = ?", id).Exec()
	if err != nil {
		return 0, err
	}
	return r.RowsAffected()
}

func SelectUserById(id int64) *User {
	if id <= 0 {
		return nil
	}

	obj := User{Id: id}
	err := orm.NewOrm().Read(&obj, "Id")
	if err != nil {
		if err != orm.ErrNoRows {
			log.Println(err.Error())
		}
		return nil
	}
	return &obj
}

/**
 * @function name:	func CheckLoginStatusByCookie(sig) bool
 * @description:	This function checks user's login status by value of "sig" cookie.
 * @related issues:	OWL-127
 * @param:			sig string
 * @return:			bool
 * @author:			Don Hsieh
 * @since:			10/15/2015
 * @last modified: 	10/30/2015
 * @called by:		func (this *MainController) Index()
 *					 in http/controller.go
 */
func CheckLoginStatusByCookie(sig string) bool {
	if sig == "" {
		return false
	}

	sessionObj := SelectSessionBySig(sig)
	if sessionObj == nil {
		log.Println("no such sig")
		return false
	}

	if int64(sessionObj.Expired) < time.Now().Unix() {
		log.Println("session expired")
		DeleteSessionById(sessionObj.Id)
		return false
	}

	user := SelectUserById(sessionObj.Uid)
	if user == nil {
		log.Println("no such user")
		return false
	}

	if len(user.Name) > 0 {
		return true
	} else {
		return false
	}
}

func (this *MainController) Index() {

	events := g.Events.Clone()

	defer func() {
		this.Data["Now"] = time.Now().Unix()
		this.TplNames = "index.html"
		this.Data["FalconPortal"] = g.Config().Shortcut.FalconPortal
		this.Data["FalconDashboard"] = g.Config().Shortcut.FalconDashboard
		this.Data["GrafanaDashboard"] = g.Config().Shortcut.GrafanaDashboard
		this.Data["FalconAlarm"] = g.Config().Shortcut.FalconAlarm
		this.Data["FalconUIC"] = g.Config().Shortcut.FalconUIC
	}()

	count := len(events)
	if count == 0 {
		this.Data["Events"] = []*g.EventDto{}
		return
	}

	// 按照持续时间排序
	beforeOrder := make([]*g.EventDto, count)
	i := 0
	for _, event := range events {
		beforeOrder[i] = event
		i++
	}

	sort.Sort(g.OrderedEvents(beforeOrder))
	this.Data["Events"] = beforeOrder

}

func (this *MainController) Solve() {
	ids := this.GetString("ids")
	if ids == "" {
		this.Ctx.WriteString("")
		return
	}

	idArr := strings.Split(ids, ",,")
	for i := 0; i < len(idArr); i++ {
		g.Events.Delete(idArr[i])
	}

	this.Ctx.WriteString("")
}
<commit_msg>Hotfix: add missing login check<commit_after>package http

import (
	"fmt"
	"github.com/Cepave/alarm/g"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/toolkits/file"
	"log"

	"sort"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/open-falcon/alarm/g"
	"github.com/toolkits/file"
)

type MainController struct {
	beego.Controller
}

func (this *MainController) Version() {
	this.Ctx.WriteString(g.VERSION)
}

func (this *MainController) Health() {
	this.Ctx.WriteString("ok")
}

func (this *MainController) Workdir() {
	this.Ctx.WriteString(fmt.Sprintf("%s", file.SelfDir()))
}

func (this *MainController) ConfigReload() {
	remoteAddr := this.Ctx.Request.RemoteAddr
	if strings.HasPrefix(remoteAddr, "127.0.0.1") {
		g.ParseConfig(g.ConfigFile)
		this.Data["json"] = g.Config()
		this.ServeJSON()
	} else {
		this.Ctx.WriteString("no privilege")
	}
}

func SelectSessionBySig(sig string) *Session {
	if sig == "" {
		return nil
	}

	obj := Session{Sig: sig}
	err := orm.NewOrm().Read(&obj, "Sig")
	if err != nil {
		if err != orm.ErrNoRows {
			log.Println(err.Error())
		}
		return nil
	}
	return &obj
}

func DeleteSessionById(id int64) (int64, error) {
	r, err := orm.NewOrm().Raw("DELETE FROM `session` WHERE `id` = ?", id).Exec()
	if err != nil {
		return 0, err
	}
	return r.RowsAffected()
}

func SelectUserById(id int64) *User {
	if id <= 0 {
		return nil
	}

	obj := User{Id: id}
	err := orm.NewOrm().Read(&obj, "Id")
	if err != nil {
		if err != orm.ErrNoRows {
			log.Println(err.Error())
		}
		return nil
	}
	return &obj
}

/**
 * @function name:	func CheckLoginStatusByCookie(sig) bool
 * @description:	This function checks user's login status by value of "sig" cookie.
 * @related issues:	OWL-127
 * @param:			sig string
 * @return:			bool
 * @author:			Don Hsieh
 * @since:			10/15/2015
 * @last modified: 	10/30/2015
 * @called by:		func (this *MainController) Index()
 *					 in http/controller.go
 */
func CheckLoginStatusByCookie(sig string) bool {
	if sig == "" {
		return false
	}

	sessionObj := SelectSessionBySig(sig)
	if sessionObj == nil {
		log.Println("no such sig")
		return false
	}

	if int64(sessionObj.Expired) < time.Now().Unix() {
		log.Println("session expired")
		DeleteSessionById(sessionObj.Id)
		return false
	}

	user := SelectUserById(sessionObj.Uid)
	if user == nil {
		log.Println("no such user")
		return false
	}

	if len(user.Name) > 0 {
		return true
	} else {
		return false
	}
}

func (this *MainController) Index() {

	sig := this.Ctx.GetCookie("sig")
	isLoggedIn := CheckLoginStatusByCookie(sig)
	if !isLoggedIn {
		RedirectUrl := g.Config().RedirectUrl
		this.Redirect(RedirectUrl, 302)
	}

	events := g.Events.Clone()

	defer func() {
		this.Data["Now"] = time.Now().Unix()
		this.TplNames = "index.html"
		this.Data["FalconPortal"] = g.Config().Shortcut.FalconPortal
		this.Data["FalconDashboard"] = g.Config().Shortcut.FalconDashboard
		this.Data["GrafanaDashboard"] = g.Config().Shortcut.GrafanaDashboard
		this.Data["FalconAlarm"] = g.Config().Shortcut.FalconAlarm
		this.Data["FalconUIC"] = g.Config().Shortcut.FalconUIC
	}()

	count := len(events)
	if count == 0 {
		this.Data["Events"] = []*g.EventDto{}
		return
	}

	// 按照持续时间排序
	beforeOrder := make([]*g.EventDto, count)
	i := 0
	for _, event := range events {
		beforeOrder[i] = event
		i++
	}

	sort.Sort(g.OrderedEvents(beforeOrder))
	this.Data["Events"] = beforeOrder

}

func (this *MainController) Solve() {
	ids := this.GetString("ids")
	if ids == "" {
		this.Ctx.WriteString("")
		return
	}

	idArr := strings.Split(ids, ",,")
	for i := 0; i < len(idArr); i++ {
		g.Events.Delete(idArr[i])
	}

	this.Ctx.WriteString("")
}
<|endoftext|>


<commit_before>package goldb

import (
	"compress/flate"
	"os"

	"github.com/denisskin/bin"
)

type DumpOptions struct {
	Filter           *Query
	CompressionLevel int // 0-9 (0 - no compression; 9 - by default)
}

func (s *Storage) Dump(filepath string, options *DumpOptions) (err error) {
	op := DumpOptions{
		CompressionLevel: flate.DefaultCompression,
	}
	if options != nil {
		op = *options
	}

	file, err := os.Create(filepath)
	if err != nil {
		return
	}
	defer file.Close()

	fl, err := flate.NewWriter(file, op.CompressionLevel)
	if err != nil {
		return
	}
	defer fl.Close()
	w := bin.NewWriter(fl)

	q := op.Filter
	if q == nil {
		q = NewQuery(0)
		q.filter = nil // all keys
	}

	const SyncBatchSize = 32 * 1024 * 1024 // 32 MiB
	var nextSyncVol int64
	err = s.Fetch(q, func(rec Record) error {
		w.WriteVar(rec.Key)
		w.WriteVar(rec.Value)
		if w.CntWritten > nextSyncVol {
			nextSyncVol += SyncBatchSize
			if err := file.Sync(); err != nil {
				return err
			}
		}
		return w.Error()
	})

	w.WriteVar([]byte{}) // EOF - null-key
	return
}

func (s *Storage) Restore(filepath string) (err error) {
	file, err := os.Open(filepath)
	if err != nil {
		return
	}
	defer file.Close()

	fl := flate.NewReader(file)
	defer fl.Close()
	r := bin.NewReader(fl)

	s.mx.Lock()
	defer s.mx.Unlock()

	tr, err := s.db.OpenTransaction()
	if err != nil {
		return
	}
	var key, val []byte
	for i := 0; ; {
		if key, err = r.ReadBytes(); err != nil {
			return
		} else if len(key) == 0 { // EOF
			break
		}
		if val, err = r.ReadBytes(); err != nil {
			return
		}
		tr.Put(key, val, nil)

		if i++; i%10000 == 0 {
			if err = tr.Commit(); err != nil {
				return
			} else if tr, err = s.db.OpenTransaction(); err != nil {
				return
			}
		}
	}
	err = tr.Commit()
	return
}
<commit_msg>fix dump<commit_after>package goldb

import (
	"compress/flate"
	"os"

	"github.com/denisskin/bin"
)

type DumpOptions struct {
	Filter           *Query
	CompressionLevel int // 0-9 (0 - no compression; 9 - by default)
}

func (s *Storage) Dump(filepath string, options *DumpOptions) (err error) {
	op := DumpOptions{
		CompressionLevel: flate.DefaultCompression,
	}
	if options != nil {
		op = *options
	}

	file, err := os.Create(filepath)
	if err != nil {
		return
	}
	defer file.Close()

	fl, err := flate.NewWriter(file, op.CompressionLevel)
	if err != nil {
		return
	}
	defer fl.Close()
	w := bin.NewWriter(fl)

	q := op.Filter
	if q == nil {
		q = NewQuery(0)
		q.filter = nil // all keys
	}

	const SyncBatchSize = 32 * 1024 * 1024 // 32 MiB
	var nextSyncVol = int64(SyncBatchSize)
	err = s.Fetch(q, func(rec Record) error {
		w.WriteVar(rec.Key)
		w.WriteVar(rec.Value)
		if w.CntWritten > nextSyncVol {
			nextSyncVol += SyncBatchSize
			if err := file.Sync(); err != nil {
				return err
			}
		}
		return w.Error()
	})

	w.WriteVar([]byte{}) // EOF - null-key
	return
}

func (s *Storage) Restore(filepath string) (err error) {
	file, err := os.Open(filepath)
	if err != nil {
		return
	}
	defer file.Close()

	fl := flate.NewReader(file)
	defer fl.Close()
	r := bin.NewReader(fl)

	s.mx.Lock()
	defer s.mx.Unlock()

	tr, err := s.db.OpenTransaction()
	if err != nil {
		return
	}
	var key, val []byte
	for i := 0; ; {
		if key, err = r.ReadBytes(); err != nil {
			return
		} else if len(key) == 0 { // EOF
			break
		}
		if val, err = r.ReadBytes(); err != nil {
			return
		}
		tr.Put(key, val, nil)

		if i++; i%10000 == 0 {
			if err = tr.Commit(); err != nil {
				return
			} else if tr, err = s.db.OpenTransaction(); err != nil {
				return
			}
		}
	}
	err = tr.Commit()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/xproto"
)

type itemID int

const (
	batteryItem itemID = iota
	timeItem
	audioItem
	netItem
)

const (
	batSysPath = "/sys/class/power_supply"
	netSysPath = "/sys/class/net"
)

type iconID int

const (
	noIcon iconID = iota
	volumeIcon
	muteIcon
	timeIcon
	wifiIcon
	wiredIcon
	batteryIcon
	chargeIcon
	dischargeIcon
	fullIcon
	unknownIcon
)

var icons = map[iconID]string{
	noIcon:        "",
	volumeIcon:    "v:",
	muteIcon:      "m:",
	timeIcon:      "",
	wiredIcon:     "e:",
	wifiIcon:      "w:",
	batteryIcon:   "b:",
	chargeIcon:    "+",
	dischargeIcon: "-",
	fullIcon:      "=",
	unknownIcon:   "?",
}

var (
	updatePeriod = 5 * time.Second
	items        = []itemID{netItem, batteryItem, audioItem, timeItem}
	statusFormat = statusFmt

	netInterfaces = []string{"wlan0", "eth0"}
	wifiFormat    = wifiFmt
	wiredFormat   = wiredFmt
	netFormat     = netFmt
	ssidRE        = regexp.MustCompile(`SSID:\s+(.*)`)
	bitrateRE     = regexp.MustCompile(`tx bitrate:\s+(\d+)`)
	signalRE      = regexp.MustCompile(`signal:\s+(-\d+)`)

	batteries    = []string{"BAT0"}
	batteryIcons = map[string]iconID{
		"Charging": chargeIcon, "Discharging": dischargeIcon, "Full": fullIcon,
	}
	batteryDevFormat = batteryDevFmt
	batteryFormat    = batteryFmt

	audioFormat = audioFmt
	amixerRE    = regexp.MustCompile(`\[(\d+)%]\s*\[(\w+)]`)

	timeFormat = timeFmt
)

func wifiStatus(dev string, isUp bool) (string, bool) {
	ssid, bitrate, signal := "", 0, 0

	out, err := exec.Command("iw", "dev", dev, "link").Output()
	if err != nil {
		return "", false
	}
	if match := ssidRE.FindSubmatch(out); len(match) >= 2 {
		ssid = string(match[1])
	}
	if match := bitrateRE.FindSubmatch(out); len(match) >= 2 {
		if br, err := strconv.Atoi(string(match[1])); err == nil {
			bitrate = br
		}
	}
	if match := signalRE.FindSubmatch(out); len(match) >= 2 {
		if sig, err := strconv.Atoi(string(match[1])); err == nil {
			signal = sig
		}
	}
	return wifiFormat(dev, ssid, bitrate, signal, isUp)
}

func wiredStatus(dev string, isUp bool) (string, bool) {
	speed := 0
	if spd, err := sysfsIntVal(filepath.Join(netSysPath, dev, "speed")); err == nil {
		speed = spd
	}
	return wiredFormat(dev, speed, isUp)
}

func netDevStatus(dev string) (string, bool) {
	status, err := sysfsStringVal(filepath.Join(netSysPath, dev, "operstate"))
	isUp := true
	if err != nil || status != "up" {
		isUp = false
	}

	_, err = os.Stat(filepath.Join(netSysPath, dev, "wireless"))
	isWifi := !os.IsNotExist(err)

	if isWifi {
		return wifiStatus(dev, isUp)
	}
	return wiredStatus(dev, isUp)
}

func netStatus() string {
	var netStats []string
	for _, dev := range netInterfaces {
		devStat, ok := netDevStatus(dev)
		if ok {
			netStats = append(netStats, devStat)
		}
	}
	return netFormat(netStats)
}

func wifiFmt(dev, ssid string, bitrate, signal int, isUp bool) (string, bool) {
	return fmt.Sprintf("%s%s/%d/%d", icons[wifiIcon], ssid, bitrate, signal), isUp
}
func wiredFmt(dev string, speed int, isUp bool) (string, bool) {
	return fmt.Sprintf("%s%d", icons[wiredIcon], speed), isUp
}

func netFmt(devs []string) string {
	return strings.Join(devs, " ")
}

func batteryDevStatus(bat string) string {
	pct, err := sysfsIntVal(filepath.Join(batSysPath, bat, "capacity"))
	if err != nil {
		return icons[unknownIcon]
	}
	status, err := sysfsStringVal(filepath.Join(batSysPath, bat, "status"))
	if err != nil {
		return icons[unknownIcon]
	}
	return batteryDevFormat(pct, status)
}

func batteryStatus() string {
	var batStats []string
	for _, bat := range batteries {
		batStats = append(batStats, batteryDevStatus(bat))
	}
	return batteryFormat(batStats)
}

func batteryDevFmt(pct int, status string) string {
	return fmt.Sprintf("%d%s", pct, icons[batteryIcons[status]])
}

func batteryFmt(bats []string) string {
	return icons[batteryIcon] + strings.Join(bats, "/")
}

func audioStatus() string {
	out, err := exec.Command("amixer", "get", "Master").Output()
	if err != nil {
		return icons[unknownIcon]
	}
	match := amixerRE.FindSubmatch(out)
	if len(match) < 3 {
		return icons[unknownIcon]
	}
	vol, err := strconv.Atoi(string(match[1]))
	if err != nil {
		return icons[unknownIcon]
	}
	isMuted := (string(match[2]) == "off")
	return audioFormat(vol, isMuted)
}

func audioFmt(vol int, isMuted bool) string {
	icon := volumeIcon
	if isMuted {
		icon = muteIcon
	}
	return fmt.Sprintf("%s%d", icons[icon], vol)
}

func timeStatus() string {
	return timeFormat(time.Now())
}

func timeFmt(t time.Time) string {
	return t.Format("2006-01-02 15:04")
}

func statusFmt(s []string) string {
	return " " + strings.Join(s, " | ") + " "
}

func updateStatus(x *xgb.Conn, root xproto.Window) {
	var stats []string
	for _, item := range items {
		switch item {
		case batteryItem:
			stats = append(stats, batteryStatus())
		case audioItem:
			stats = append(stats, audioStatus())
		case netItem:
			stats = append(stats, netStatus())
		case timeItem:
			stats = append(stats, timeStatus())
		}
	}
	status := statusFormat(stats)

	xproto.ChangeProperty(x, xproto.PropModeReplace, root, xproto.AtomWmName,
		xproto.AtomString, 8, uint32(len(status)), []byte(status))
}

func sysfsIntVal(path string) (int, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return 0, err
	}
	val, err := strconv.Atoi(string(bytes.TrimSpace(data)))
	if err != nil {
		return 0, err
	}
	return val, nil
}

func sysfsStringVal(path string) (string, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(bytes.TrimSpace(data)), nil
}

func main() {
	x, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	root := xproto.Setup(x).DefaultScreen(x).Root
	for t := time.Tick(updatePeriod); ; <-t {
		updateStatus(x, root)
	}
}
<commit_msg>Empty line between funcs.<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/xproto"
)

type itemID int

const (
	batteryItem itemID = iota
	timeItem
	audioItem
	netItem
)

const (
	batSysPath = "/sys/class/power_supply"
	netSysPath = "/sys/class/net"
)

type iconID int

const (
	noIcon iconID = iota
	volumeIcon
	muteIcon
	timeIcon
	wifiIcon
	wiredIcon
	batteryIcon
	chargeIcon
	dischargeIcon
	fullIcon
	unknownIcon
)

var icons = map[iconID]string{
	noIcon:        "",
	volumeIcon:    "v:",
	muteIcon:      "m:",
	timeIcon:      "",
	wiredIcon:     "e:",
	wifiIcon:      "w:",
	batteryIcon:   "b:",
	chargeIcon:    "+",
	dischargeIcon: "-",
	fullIcon:      "=",
	unknownIcon:   "?",
}

var (
	updatePeriod = 5 * time.Second
	items        = []itemID{netItem, batteryItem, audioItem, timeItem}
	statusFormat = statusFmt

	netInterfaces = []string{"wlan0", "eth0"}
	wifiFormat    = wifiFmt
	wiredFormat   = wiredFmt
	netFormat     = netFmt
	ssidRE        = regexp.MustCompile(`SSID:\s+(.*)`)
	bitrateRE     = regexp.MustCompile(`tx bitrate:\s+(\d+)`)
	signalRE      = regexp.MustCompile(`signal:\s+(-\d+)`)

	batteries    = []string{"BAT0"}
	batteryIcons = map[string]iconID{
		"Charging": chargeIcon, "Discharging": dischargeIcon, "Full": fullIcon,
	}
	batteryDevFormat = batteryDevFmt
	batteryFormat    = batteryFmt

	audioFormat = audioFmt
	amixerRE    = regexp.MustCompile(`\[(\d+)%]\s*\[(\w+)]`)

	timeFormat = timeFmt
)

func wifiStatus(dev string, isUp bool) (string, bool) {
	ssid, bitrate, signal := "", 0, 0

	out, err := exec.Command("iw", "dev", dev, "link").Output()
	if err != nil {
		return "", false
	}
	if match := ssidRE.FindSubmatch(out); len(match) >= 2 {
		ssid = string(match[1])
	}
	if match := bitrateRE.FindSubmatch(out); len(match) >= 2 {
		if br, err := strconv.Atoi(string(match[1])); err == nil {
			bitrate = br
		}
	}
	if match := signalRE.FindSubmatch(out); len(match) >= 2 {
		if sig, err := strconv.Atoi(string(match[1])); err == nil {
			signal = sig
		}
	}
	return wifiFormat(dev, ssid, bitrate, signal, isUp)
}

func wiredStatus(dev string, isUp bool) (string, bool) {
	speed := 0
	if spd, err := sysfsIntVal(filepath.Join(netSysPath, dev, "speed")); err == nil {
		speed = spd
	}
	return wiredFormat(dev, speed, isUp)
}

func netDevStatus(dev string) (string, bool) {
	status, err := sysfsStringVal(filepath.Join(netSysPath, dev, "operstate"))
	isUp := true
	if err != nil || status != "up" {
		isUp = false
	}

	_, err = os.Stat(filepath.Join(netSysPath, dev, "wireless"))
	isWifi := !os.IsNotExist(err)

	if isWifi {
		return wifiStatus(dev, isUp)
	}
	return wiredStatus(dev, isUp)
}

func netStatus() string {
	var netStats []string
	for _, dev := range netInterfaces {
		devStat, ok := netDevStatus(dev)
		if ok {
			netStats = append(netStats, devStat)
		}
	}
	return netFormat(netStats)
}

func wifiFmt(dev, ssid string, bitrate, signal int, isUp bool) (string, bool) {
	return fmt.Sprintf("%s%s/%d/%d", icons[wifiIcon], ssid, bitrate, signal), isUp
}

func wiredFmt(dev string, speed int, isUp bool) (string, bool) {
	return fmt.Sprintf("%s%d", icons[wiredIcon], speed), isUp
}

func netFmt(devs []string) string {
	return strings.Join(devs, " ")
}

func batteryDevStatus(bat string) string {
	pct, err := sysfsIntVal(filepath.Join(batSysPath, bat, "capacity"))
	if err != nil {
		return icons[unknownIcon]
	}
	status, err := sysfsStringVal(filepath.Join(batSysPath, bat, "status"))
	if err != nil {
		return icons[unknownIcon]
	}
	return batteryDevFormat(pct, status)
}

func batteryStatus() string {
	var batStats []string
	for _, bat := range batteries {
		batStats = append(batStats, batteryDevStatus(bat))
	}
	return batteryFormat(batStats)
}

func batteryDevFmt(pct int, status string) string {
	return fmt.Sprintf("%d%s", pct, icons[batteryIcons[status]])
}

func batteryFmt(bats []string) string {
	return icons[batteryIcon] + strings.Join(bats, "/")
}

func audioStatus() string {
	out, err := exec.Command("amixer", "get", "Master").Output()
	if err != nil {
		return icons[unknownIcon]
	}
	match := amixerRE.FindSubmatch(out)
	if len(match) < 3 {
		return icons[unknownIcon]
	}
	vol, err := strconv.Atoi(string(match[1]))
	if err != nil {
		return icons[unknownIcon]
	}
	isMuted := (string(match[2]) == "off")
	return audioFormat(vol, isMuted)
}

func audioFmt(vol int, isMuted bool) string {
	icon := volumeIcon
	if isMuted {
		icon = muteIcon
	}
	return fmt.Sprintf("%s%d", icons[icon], vol)
}

func timeStatus() string {
	return timeFormat(time.Now())
}

func timeFmt(t time.Time) string {
	return t.Format("2006-01-02 15:04")
}

func statusFmt(s []string) string {
	return " " + strings.Join(s, " | ") + " "
}

func updateStatus(x *xgb.Conn, root xproto.Window) {
	var stats []string
	for _, item := range items {
		switch item {
		case batteryItem:
			stats = append(stats, batteryStatus())
		case audioItem:
			stats = append(stats, audioStatus())
		case netItem:
			stats = append(stats, netStatus())
		case timeItem:
			stats = append(stats, timeStatus())
		}
	}
	status := statusFormat(stats)

	xproto.ChangeProperty(x, xproto.PropModeReplace, root, xproto.AtomWmName,
		xproto.AtomString, 8, uint32(len(status)), []byte(status))
}

func sysfsIntVal(path string) (int, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return 0, err
	}
	val, err := strconv.Atoi(string(bytes.TrimSpace(data)))
	if err != nil {
		return 0, err
	}
	return val, nil
}

func sysfsStringVal(path string) (string, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}
	return string(bytes.TrimSpace(data)), nil
}

func main() {
	x, err := xgb.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	root := xproto.Setup(x).DefaultScreen(x).Root
	for t := time.Tick(updatePeriod); ; <-t {
		updateStatus(x, root)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Company 0, LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package ttk

import (
	"strings"

	"github.com/nsf/termbox-go"
)

// WidgetEdit uniquely identifies the edit widget.
const (
	WidgetEdit = "edit"
)

var (
	_ Widgeter = (*Edit)(nil) // ensure interface is satisfied
)

// init registers the Edit Widget.
func init() {
	registeredWidgets[WidgetEdit] = NewEdit
}

// Edit is a text entry widget.  It prints the contents of target onto the
// window.  Note: all spaces are trimmed before and after the target string.
type Edit struct {
	Widget
	trueX      int     // actual x coordinate
	trueY      int     // actual y coordinate
	trueW      int     // actual width
	target     *string // result value of action
	display    []rune  // target as runes
	at         int     // start of displayed text
	width      int     // prefered widget width
	cx         int     // current cursor x position
	cy         int     // current cursor y position
	prevX      int     // previous window max x
	prevY      int     // previous window max y
	visibility Visibility
	attr       Attributes
}

func (e *Edit) Visibility(op Visibility) Visibility {
	switch op {
	case VisibilityGet:
		return e.visibility
	case VisibilityShow:
		e.visibility = op
		e.Render()
	case VisibilityHide:
		e.visibility = op
		e.clear()
	}

	return e.visibility
}

func (e *Edit) clear() {
	e.w.printf(e.trueX, e.trueY, defaultAttributes(), strings.Repeat(" ", e.trueW))
}

// Render implements the Render interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) Render() {
	if e.visibility == VisibilityHide {
		e.clear()
		return
	}

	filler := ""
	l := e.display[e.at:]
	if len(l) > e.trueW {
		l = e.display[e.at : e.at+e.trueW]
	} else {
		// just erase right hand side
		filler = strings.Repeat(" ", e.trueW-len(l))
	}
	e.w.printf(e.trueX, e.trueY, e.attr, "%v%v", string(l), filler)
}

func insert(slice []rune, index int, value rune) []rune {
	// Grow the slice by one element.
	slice = append(slice, value)
	// Use copy to move the upper part of the slice out of the way and open a hole.
	copy(slice[index+1:], slice[index:])
	// Store the new value.
	slice[index] = value
	// Return the result.
	return slice
}

// KeyHandler implements the interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) KeyHandler(ev termbox.Event) bool {
	var inString int

	switch ev.Key {
	case termbox.KeyCtrlA, termbox.KeyHome:
		e.cx = e.trueX
		e.at = 0
		setCursor(e.cx, e.cy)
		e.Render()
		return true
	case termbox.KeyCtrlE, termbox.KeyEnd:
		if len(e.display) < e.trueW-1 {
			// no need to call display
			e.cx = e.trueX + len(e.display)
			setCursor(e.cx, e.cy)
			return true
		}
		e.cx = e.trueX + e.trueW - 1
		e.at = len(e.display) - e.trueW + 1
		setCursor(e.cx, e.cy)
		e.Render()
		return true
	case termbox.KeyArrowRight:
		// check to see if we have content on the right hand side
		if e.cx-e.trueX == len(e.display[e.at:]) {
			return true
		}
		e.cx++
		if e.cx > e.trueW+e.trueX-1 {
			e.cx = e.trueW + e.trueX - 1

			// check for end of string before moving at
			if len(e.display[e.at:]) == 0 {
				return true
			}
			e.at++
			e.Render()
			return true
		}
		setCursor(e.cx, e.cy)
		return true
	case termbox.KeyArrowLeft:
		e.cx--
		if e.cx < e.trueX {
			e.cx = e.trueX
			e.at--
			if e.at < 0 {
				e.at = 0
			}
			e.Render()
		}
		setCursor(e.cx, e.cy)
		return true
	case termbox.KeyDelete:
		inString = e.cx - e.trueX + e.at
		if len(e.display) == inString {
			return true
		}
		// remove from slice
		e.display = append(e.display[:inString],
			e.display[inString+1:]...)
		e.Render()
		return true
	case termbox.KeyBackspace, termbox.KeyBackspace2:
		inString = e.cx - e.trueX + e.at
		if inString <= 0 {
			return true
		}
		e.display = append(e.display[:inString-1],
			e.display[inString:]...)

		// cursor left magic
		e.cx--
		if e.cx < e.trueX {
			e.cx = e.trueX
			e.at--
			if e.at < 0 {
				e.at = 0
			}
		}
		setCursor(e.cx, e.cy)
		e.Render()
		return true
	case termbox.KeySpace:
		// use space
		ev.Ch = ' '
	case termbox.KeyEnter:
		*e.target = string(e.display)
		// return false and let the application decide if it wants
		// to consume the action
		return false
	}

	// normal runes are displayed and stored
	if ev.Ch != 0 && ev.Mod != 0 && ev.Key == 0 {
		// forward special
		return false
	} else if ev.Ch == 0 {
		return false
	}

	inString = e.cx - e.trueX + e.at
	e.display = insert(e.display, inString, ev.Ch)
	if e.cx < e.trueW+e.trueX-1 {
		e.cx++
		setCursor(e.cx, e.cy)
	} else {
		e.at++
	}

	e.Render()
	return true
}

// CanFocus implements the interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) CanFocus() bool {
	return true // can focus
}

// Focus implements the interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) Focus() {
	if e.cx == -1 || e.cy == -1 {
		// || is deliberate to handle "just in case"
		e.cx = e.trueX
		e.cy = e.trueY
		e.at = 0
	}
	setCursor(e.cx, e.cy)
}

// NewEdit is the Edit initializer.  This call implements the NewWidget
// convention by taking a *Window and and an anchor point to render the widget.
func NewEdit(w *Window, x, y int) (Widgeter, error) {
	return &Edit{
		Widget: MakeWidget(w, x, y),
	}, nil
}

// SetAttributes sets the Attributes.  This will not be displayed immediately.
// SetAttributes shall be called from queue context.
func (e *Edit) SetAttributes(a Attributes) {
	e.attr = a
}

// GetText returns the edit text.
// GetText shall be called from queue context.
func (e *Edit) GetText() string {
	return string(e.display)
}

// SetText sets the edit text.  if end is set to true the cursor and text will
// be set to the end of the string.  This will not be displayed immediately.
// SetText shall be called from queue context.
func (e *Edit) SetText(s *string, end bool) {
	e.target = s
	e.display = []rune(*s)
	e.at = 0

	// send synthesized key to position cursor and text
	ev := termbox.Event{}
	if end {
		ev.Key = termbox.KeyCtrlE
	} else {
		ev.Key = termbox.KeyCtrlA
	}
	e.KeyHandler(ev)
}

func (e *Edit) Resize() {
	inString := e.cx - e.trueX + e.at
	e.trueX = e.x
	e.trueY = e.y
	e.trueW = e.width

	// y<0 is relative to bottom line
	if e.y < 0 {
		e.trueY = e.w.y + e.y + 1
	}

	// e.width <1 means -width from right hand side
	if e.width < 1 {
		e.trueW = e.w.x - e.x + e.width
	}

	// reset cursor and at
	if e.w.y != e.prevY {
		e.cy = e.trueY
		e.prevY = e.w.y
	}
	if e.w.x != e.prevX {
		switch {
		case len(e.display) == inString:
			// end of text
			if len(e.display) < e.trueW-1 {
				e.cx = e.trueX + len(e.display)
			} else {
				e.cx = e.trueX + e.trueW - 1
				e.at = len(e.display) - e.trueW + 1
			}
		case inString <= 0:
			// begin of text
			e.at = 0
			e.cx = e.trueX
		default:
			// middle of text
			if e.prevX <= e.w.x {
				// do nothing since x grew
			} else {
				// shift location of at based on shrinkage
				if e.cx >= e.w.x {
					e.cx -= e.prevX - e.w.x
					e.at += e.prevX - e.w.x
				}
			}
		}
		e.prevX = e.w.x
	}
}

// AddEdit is a convenience function to add a new edit to a window.  Capacity
// and width determine the maxima of the returned value.  It wraps the
// AddWidget call.  AddEdit must be called from queue.
func (w *Window) AddEdit(x, y, width int, target *string) *Edit {
	// we can ignore error for builtins
	e, _ := w.AddWidget(WidgetEdit, x, y)
	edit := e.(*Edit)
	edit.width = width

	// save current sizes to detect actual window resizes
	edit.prevX = w.x
	edit.prevY = w.y

	edit.Resize()

	// cursor
	edit.cx = -1
	edit.cy = -1

	// set target string
	edit.SetText(target, true)

	// flip attributes
	a := defaultAttributes()
	a2 := Attributes{
		Fg: a.Bg,
		Bg: a.Fg,
	}
	edit.SetAttributes(a2)

	return edit
}
<commit_msg>fix crash when handling ^e on long lines (#12)<commit_after>// Copyright (c) 2016 Company 0, LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package ttk

import (
	"strings"

	"github.com/nsf/termbox-go"
)

// WidgetEdit uniquely identifies the edit widget.
const (
	WidgetEdit = "edit"
)

var (
	_ Widgeter = (*Edit)(nil) // ensure interface is satisfied
)

// init registers the Edit Widget.
func init() {
	registeredWidgets[WidgetEdit] = NewEdit
}

// Edit is a text entry widget.  It prints the contents of target onto the
// window.  Note: all spaces are trimmed before and after the target string.
type Edit struct {
	Widget
	trueX      int     // actual x coordinate
	trueY      int     // actual y coordinate
	trueW      int     // actual width
	target     *string // result value of action
	display    []rune  // target as runes
	at         int     // start of displayed text
	width      int     // prefered widget width
	cx         int     // current cursor x position
	cy         int     // current cursor y position
	prevX      int     // previous window max x
	prevY      int     // previous window max y
	visibility Visibility
	attr       Attributes
}

func (e *Edit) Visibility(op Visibility) Visibility {
	switch op {
	case VisibilityGet:
		return e.visibility
	case VisibilityShow:
		e.visibility = op
		e.Render()
	case VisibilityHide:
		e.visibility = op
		e.clear()
	}

	return e.visibility
}

func (e *Edit) clear() {
	e.w.printf(e.trueX, e.trueY, defaultAttributes(), strings.Repeat(" ", e.trueW))
}

// Render implements the Render interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) Render() {
	if e.visibility == VisibilityHide {
		e.clear()
		return
	}

	filler := ""
	l := e.display[e.at:]
	if len(l) > e.trueW {
		l = e.display[e.at : e.at+e.trueW]
	} else {
		// just erase right hand side
		filler = strings.Repeat(" ", e.trueW-len(l))
	}
	e.w.printf(e.trueX, e.trueY, e.attr, "%v%v", string(l), filler)
}

func insert(slice []rune, index int, value rune) []rune {
	// Grow the slice by one element.
	slice = append(slice, value)
	// Use copy to move the upper part of the slice out of the way and open a hole.
	copy(slice[index+1:], slice[index:])
	// Store the new value.
	slice[index] = value
	// Return the result.
	return slice
}

// KeyHandler implements the interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) KeyHandler(ev termbox.Event) bool {
	var inString int

	switch ev.Key {
	case termbox.KeyCtrlA, termbox.KeyHome:
		e.cx = e.trueX
		e.at = 0
		setCursor(e.cx, e.cy)
		e.Render()
		return true
	case termbox.KeyCtrlE, termbox.KeyEnd:
		if len(e.display) < e.trueW-1 {
			// no need to call display
			e.cx = e.trueX + len(e.display) - e.at
			setCursor(e.cx, e.cy)
			return true
		}
		e.cx = e.trueX + e.trueW - 1
		e.at = len(e.display) - e.trueW + 1
		setCursor(e.cx, e.cy)
		e.Render()
		return true
	case termbox.KeyArrowRight:
		// check to see if we have content on the right hand side
		if e.cx-e.trueX == len(e.display[e.at:]) {
			return true
		}
		e.cx++
		if e.cx > e.trueW+e.trueX-1 {
			e.cx = e.trueW + e.trueX - 1

			// check for end of string before moving at
			if len(e.display[e.at:]) == 0 {
				return true
			}
			e.at++
			e.Render()
			return true
		}
		setCursor(e.cx, e.cy)
		return true
	case termbox.KeyArrowLeft:
		e.cx--
		if e.cx < e.trueX {
			e.cx = e.trueX
			e.at--
			if e.at < 0 {
				e.at = 0
			}
			e.Render()
		}
		setCursor(e.cx, e.cy)
		return true
	case termbox.KeyDelete:
		inString = e.cx - e.trueX + e.at
		if len(e.display) == inString {
			return true
		}
		// remove from slice
		e.display = append(e.display[:inString],
			e.display[inString+1:]...)
		e.Render()
		return true
	case termbox.KeyBackspace, termbox.KeyBackspace2:
		inString = e.cx - e.trueX + e.at
		if inString <= 0 {
			return true
		}
		e.display = append(e.display[:inString-1],
			e.display[inString:]...)

		// cursor left magic
		e.cx--
		if e.cx < e.trueX {
			e.cx = e.trueX
			e.at--
			if e.at < 0 {
				e.at = 0
			}
		}
		setCursor(e.cx, e.cy)
		e.Render()
		return true
	case termbox.KeySpace:
		// use space
		ev.Ch = ' '
	case termbox.KeyEnter:
		*e.target = string(e.display)
		// return false and let the application decide if it wants
		// to consume the action
		return false
	}

	// normal runes are displayed and stored
	if ev.Ch != 0 && ev.Mod != 0 && ev.Key == 0 {
		// forward special
		return false
	} else if ev.Ch == 0 {
		return false
	}

	inString = e.cx - e.trueX + e.at
	e.display = insert(e.display, inString, ev.Ch)
	if e.cx < e.trueW+e.trueX-1 {
		e.cx++
		setCursor(e.cx, e.cy)
	} else {
		e.at++
	}

	e.Render()
	return true
}

// CanFocus implements the interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) CanFocus() bool {
	return true // can focus
}

// Focus implements the interface.  This is called from queue context
// so be careful to not use blocking calls.
func (e *Edit) Focus() {
	if e.cx == -1 || e.cy == -1 {
		// || is deliberate to handle "just in case"
		e.cx = e.trueX
		e.cy = e.trueY
		e.at = 0
	}
	setCursor(e.cx, e.cy)
}

// NewEdit is the Edit initializer.  This call implements the NewWidget
// convention by taking a *Window and and an anchor point to render the widget.
func NewEdit(w *Window, x, y int) (Widgeter, error) {
	return &Edit{
		Widget: MakeWidget(w, x, y),
	}, nil
}

// SetAttributes sets the Attributes.  This will not be displayed immediately.
// SetAttributes shall be called from queue context.
func (e *Edit) SetAttributes(a Attributes) {
	e.attr = a
}

// GetText returns the edit text.
// GetText shall be called from queue context.
func (e *Edit) GetText() string {
	return string(e.display)
}

// SetText sets the edit text.  if end is set to true the cursor and text will
// be set to the end of the string.  This will not be displayed immediately.
// SetText shall be called from queue context.
func (e *Edit) SetText(s *string, end bool) {
	e.target = s
	e.display = []rune(*s)
	e.at = 0

	// send synthesized key to position cursor and text
	ev := termbox.Event{}
	if end {
		ev.Key = termbox.KeyCtrlE
	} else {
		ev.Key = termbox.KeyCtrlA
	}
	e.KeyHandler(ev)
}

func (e *Edit) Resize() {
	inString := e.cx - e.trueX + e.at
	e.trueX = e.x
	e.trueY = e.y
	e.trueW = e.width

	// y<0 is relative to bottom line
	if e.y < 0 {
		e.trueY = e.w.y + e.y + 1
	}

	// e.width <1 means -width from right hand side
	if e.width < 1 {
		e.trueW = e.w.x - e.x + e.width
	}

	// reset cursor and at
	if e.w.y != e.prevY {
		e.cy = e.trueY
		e.prevY = e.w.y
	}
	if e.w.x != e.prevX {
		switch {
		case len(e.display) == inString:
			// end of text
			if len(e.display) < e.trueW-1 {
				e.cx = e.trueX + len(e.display)
			} else {
				e.cx = e.trueX + e.trueW - 1
				e.at = len(e.display) - e.trueW + 1
			}
		case inString <= 0:
			// begin of text
			e.at = 0
			e.cx = e.trueX
		default:
			// middle of text
			if e.prevX <= e.w.x {
				// do nothing since x grew
			} else {
				// shift location of at based on shrinkage
				if e.cx >= e.w.x {
					e.cx -= e.prevX - e.w.x
					e.at += e.prevX - e.w.x
				}
			}
		}
		e.prevX = e.w.x
	}
}

// AddEdit is a convenience function to add a new edit to a window.  Capacity
// and width determine the maxima of the returned value.  It wraps the
// AddWidget call.  AddEdit must be called from queue.
func (w *Window) AddEdit(x, y, width int, target *string) *Edit {
	// we can ignore error for builtins
	e, _ := w.AddWidget(WidgetEdit, x, y)
	edit := e.(*Edit)
	edit.width = width

	// save current sizes to detect actual window resizes
	edit.prevX = w.x
	edit.prevY = w.y

	edit.Resize()

	// cursor
	edit.cx = -1
	edit.cy = -1

	// set target string
	edit.SetText(target, true)

	// flip attributes
	a := defaultAttributes()
	a2 := Attributes{
		Fg: a.Bg,
		Bg: a.Fg,
	}
	edit.SetAttributes(a2)

	return edit
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/coreos/fleet/third_party/github.com/codegangsta/cli"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/sign"
)

func newSubmitUnitCommand() cli.Command {
	return cli.Command{
		Name:  "submit",
		Usage: "Upload one or more units to the cluster without starting them",
		Description: `Upload one or more units to the cluster without starting them. Useful
validating units before they are started.

Submit a single unit:
fleetctl submit foo.service

Submit a directory of units with glob matching:
fleetctl submit myservice/*`,
		Flags: []cli.Flag{
			cli.BoolFlag{"sign", "Sign unit file signatures using local SSH identities"},
		},
		Action: submitUnitsAction,
	}
}

func submitUnitsAction(c *cli.Context) {
	toSign := c.Bool("sign")
	var sc *sign.SignatureCreator
	if toSign {
		var err error
		sc, err = sign.NewSignatureCreatorFromSSHAgent()
		if err != nil {
			fmt.Println("Fail to create SignatureVerifier:", err)
			return
		}
	}

	// First, validate each of the provided payloads
	payloads := make([]job.JobPayload, len(c.Args()))
	for i, v := range c.Args() {
		payload, err := getJobPayloadFromFile(v)
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		payloads[i] = *payload
	}

	// Only after all the provided payloads have been validated
	// do we push any changes to the Registry
	for _, payload := range payloads {
		err := registryCtl.CreatePayload(&payload)
		if err != nil {
			fmt.Printf("Creation of payload %s failed: %v\n", payload.Name, err)
			return
		}
		if toSign {
			s, err := sc.SignPayload(&payload)
			if err != nil {
				fmt.Printf("Creation of sign for payload %s failed: %v\n", payload.Name, err)
				return
			}
			registryCtl.CreateSignatureSet(s)
		}
	}
}
<commit_msg>fix(fleetctl): exit 1 in submit on failure<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/coreos/fleet/third_party/github.com/codegangsta/cli"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/sign"
)

func newSubmitUnitCommand() cli.Command {
	return cli.Command{
		Name:  "submit",
		Usage: "Upload one or more units to the cluster without starting them",
		Description: `Upload one or more units to the cluster without starting them. Useful
validating units before they are started.

Submit a single unit:
fleetctl submit foo.service

Submit a directory of units with glob matching:
fleetctl submit myservice/*`,
		Flags: []cli.Flag{
			cli.BoolFlag{"sign", "Sign unit file signatures using local SSH identities"},
		},
		Action: submitUnitsAction,
	}
}

func submitUnitsAction(c *cli.Context) {
	toSign := c.Bool("sign")
	var sc *sign.SignatureCreator
	if toSign {
		var err error
		sc, err = sign.NewSignatureCreatorFromSSHAgent()
		if err != nil {
			fmt.Println("Fail to create SignatureVerifier:", err)
			os.Exit(1)
		}
	}

	// First, validate each of the provided payloads
	payloads := make([]job.JobPayload, len(c.Args()))
	for i, v := range c.Args() {
		payload, err := getJobPayloadFromFile(v)
		if err != nil {
			fmt.Println(err.Error())
			os.Exit(1)
		}
		payloads[i] = *payload
	}

	// Only after all the provided payloads have been validated
	// do we push any changes to the Registry
	for _, payload := range payloads {
		err := registryCtl.CreatePayload(&payload)
		if err != nil {
			fmt.Printf("Creation of payload %s failed: %v\n", payload.Name, err)
			os.Exit(1)
		}
		if toSign {
			s, err := sc.SignPayload(&payload)
			if err != nil {
				fmt.Printf("Creation of sign for payload %s failed: %v\n", payload.Name, err)
				os.Exit(1)
			}
			registryCtl.CreateSignatureSet(s)
		}
	}
}
<|endoftext|>