<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package logstream

import (
	"io"
	"sync"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/version"

	"github.com/juju/juju/api/base"
	"github.com/juju/juju/api/common/stream"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/logfwd"
)

// jsonReadCloser provides the functionality to send JSON-serialized
// values over a streaming connection.
type jsonReadCloser interface {
	io.Closer

	// ReadJSON decodes the next JSON value from the connection and
	// sets the value at the provided pointer to that newly decoded one.
	ReadJSON(interface{}) error
}

// LogStream streams log entries of the /logstream API endpoint over
// a websocket connection.
type LogStream struct {
	mu             sync.Mutex
	stream         jsonReadCloser
	controllerUUID string
}

// Open opens a websocket to the API's /logstream endpoint and returns
// a stream of log records from that connection.
func Open(conn base.StreamConnector, cfg params.LogStreamConfig, controllerUUID string) (*LogStream, error) {
	wsStream, err := stream.Open(conn, "/logstream", &cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ls := &LogStream{
		stream:         wsStream,
		controllerUUID: controllerUUID,
	}
	return ls, nil
}

// Next returns the next log record from the server. The records are
// coverted from the wire format into logfwd.Record.
func (ls *LogStream) Next() (logfwd.Record, error) {
	var record logfwd.Record
	apiRecord, err := ls.next()
	if err != nil {
		return record, errors.Trace(err)
	}
	record, err = api2record(apiRecord, ls.controllerUUID)
	if err != nil {
		return record, errors.Trace(err)
	}
	return record, nil
}

func (ls *LogStream) next() (params.LogStreamRecord, error) {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	var apiRec params.LogStreamRecord
	if ls.stream == nil {
		return apiRec, errors.Errorf("cannot read from closed stream")
	}

	err := ls.stream.ReadJSON(&apiRec)
	if err != nil {
		return apiRec, errors.Trace(err)
	}
	return apiRec, nil
}

// Close closes the stream.
func (ls *LogStream) Close() error {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	if ls.stream == nil {
		return nil
	}
	if err := ls.stream.Close(); err != nil {
		return errors.Trace(err)
	}
	ls.stream = nil
	return nil
}

// See the counterpart in apiserver/logstream.go.
func api2record(apiRec params.LogStreamRecord, controllerUUID string) (logfwd.Record, error) {
	rec := logfwd.Record{
		Origin: logfwd.Origin{
			ControllerUUID: controllerUUID,
			ModelUUID:      apiRec.ModelUUID,
		},
		Timestamp: apiRec.Timestamp,
		Message:   apiRec.Message,
	}

	ver, err := version.Parse(apiRec.Version)
	if err != nil {
		return rec, errors.Annotatef(err, "invalid version %q", apiRec.Version)
	}
	rec.Origin.JujuVersion = ver

	loc, err := logfwd.ParseLocation(apiRec.Module, apiRec.Location)
	if err != nil {
		return rec, errors.Trace(err)
	}
	rec.Location = loc

	level, ok := loggo.ParseLevel(apiRec.Level)
	if !ok {
		return rec, errors.Errorf("unrecognized log level %q", apiRec.Level)
	}
	rec.Level = level

	if err := rec.Validate(); err != nil {
		return rec, errors.Trace(err)
	}

	return rec, nil
}
<commit_msg>Add more details about the error case in LogStream.Next().<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package logstream

import (
	"io"
	"sync"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/version"

	"github.com/juju/juju/api/base"
	"github.com/juju/juju/api/common/stream"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/logfwd"
)

// jsonReadCloser provides the functionality to send JSON-serialized
// values over a streaming connection.
type jsonReadCloser interface {
	io.Closer

	// ReadJSON decodes the next JSON value from the connection and
	// sets the value at the provided pointer to that newly decoded one.
	ReadJSON(interface{}) error
}

// LogStream streams log entries of the /logstream API endpoint over
// a websocket connection.
type LogStream struct {
	mu             sync.Mutex
	stream         jsonReadCloser
	controllerUUID string
}

// Open opens a websocket to the API's /logstream endpoint and returns
// a stream of log records from that connection.
func Open(conn base.StreamConnector, cfg params.LogStreamConfig, controllerUUID string) (*LogStream, error) {
	wsStream, err := stream.Open(conn, "/logstream", &cfg)
	if err != nil {
		return nil, errors.Trace(err)
	}
	ls := &LogStream{
		stream:         wsStream,
		controllerUUID: controllerUUID,
	}
	return ls, nil
}

// Next returns the next log record from the server. The records are
// coverted from the wire format into logfwd.Record. The first returned
// record will be the one after the last successfully sent record. If no
// records have been sent yet then it will be the oldest log record.
//
// An error indicates either the streaming connection is closed, the
// connection failed, or the data read from the connection is corrupted.
// In each of these cases the stream should be re-opened. It will start
// at the record after the one marked as successfully sent. So the
// the record at which Next() failed previously will be streamed again.
//
// That is only a problem when the same record is consistently streamed
// as invalid data. This will happen only if the record was invalid
// before being stored in the DB or if the DB on-disk storage for the
// record becomes corrupted. Both scenarios are highly unlikely and
// the respective systems are managed such that neither should happen.
func (ls *LogStream) Next() (logfwd.Record, error) {
	var record logfwd.Record
	apiRecord, err := ls.next()
	if err != nil {
		return record, errors.Trace(err)
	}
	record, err = api2record(apiRecord, ls.controllerUUID)
	if err != nil {
		// This should only happen if the data got corrupted over the
		// network. Any other cause should be addressed by fixing the
		// code that resulted in the bad data that caused the error
		// here. If that code is between the DB on-disk storage and
		// the server-side stream then care should be taken to not
		// block on a consistently invalid record or to throw away
		// a record. The log stream needs to maintain a high level
		// of reliable delivery.
		return record, errors.Trace(err)
	}
	return record, nil
}

func (ls *LogStream) next() (params.LogStreamRecord, error) {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	var apiRec params.LogStreamRecord
	if ls.stream == nil {
		return apiRec, errors.Errorf("cannot read from closed stream")
	}

	err := ls.stream.ReadJSON(&apiRec)
	if err != nil {
		return apiRec, errors.Trace(err)
	}
	return apiRec, nil
}

// Close closes the stream.
func (ls *LogStream) Close() error {
	ls.mu.Lock()
	defer ls.mu.Unlock()

	if ls.stream == nil {
		return nil
	}
	if err := ls.stream.Close(); err != nil {
		return errors.Trace(err)
	}
	ls.stream = nil
	return nil
}

// See the counterpart in apiserver/logstream.go.
func api2record(apiRec params.LogStreamRecord, controllerUUID string) (logfwd.Record, error) {
	rec := logfwd.Record{
		Origin: logfwd.Origin{
			ControllerUUID: controllerUUID,
			ModelUUID:      apiRec.ModelUUID,
		},
		Timestamp: apiRec.Timestamp,
		Message:   apiRec.Message,
	}

	ver, err := version.Parse(apiRec.Version)
	if err != nil {
		return rec, errors.Annotatef(err, "invalid version %q", apiRec.Version)
	}
	rec.Origin.JujuVersion = ver

	loc, err := logfwd.ParseLocation(apiRec.Module, apiRec.Location)
	if err != nil {
		return rec, errors.Trace(err)
	}
	rec.Location = loc

	level, ok := loggo.ParseLevel(apiRec.Level)
	if !ok {
		return rec, errors.Errorf("unrecognized log level %q", apiRec.Level)
	}
	rec.Level = level

	if err := rec.Validate(); err != nil {
		return rec, errors.Trace(err)
	}

	return rec, nil
}
<|endoftext|>


<commit_before>package filecache

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/bradfitz/iter"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/anacrolix/missinggo/v2"
)

func TestCache(t *testing.T) {
	td, err := ioutil.TempDir("", "gotest")
	require.NoError(t, err)
	defer os.RemoveAll(td)

	c, err := NewCache(filepath.Join(td, "cache"))
	require.NoError(t, err)
	assert.EqualValues(t, CacheInfo{
		Filled:   0,
		Capacity: -1,
		NumItems: 0,
	}, c.Info())

	c.WalkItems(func(i ItemInfo) {})

	_, err = c.OpenFile("/", os.O_CREATE)
	assert.NotNil(t, err)

	_, err = c.OpenFile("", os.O_CREATE)
	assert.NotNil(t, err)

	c.WalkItems(func(i ItemInfo) {})

	require.Equal(t, CacheInfo{
		Filled:   0,
		Capacity: -1,
		NumItems: 0,
	}, c.Info())

	_, err = c.OpenFile("notexist", 0)
	assert.True(t, os.IsNotExist(err), err)

	_, err = c.OpenFile("/notexist", 0)
	assert.True(t, os.IsNotExist(err), err)

	_, err = c.OpenFile("/dir/notexist", 0)
	assert.True(t, os.IsNotExist(err), err)

	f, err := c.OpenFile("dir/blah", os.O_CREATE)
	require.NoError(t, err)
	defer f.Close()
	require.Equal(t, CacheInfo{
		Filled:   0,
		Capacity: -1,
		NumItems: 1,
	}, c.Info())

	c.WalkItems(func(i ItemInfo) {})

	assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
	assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))
	assert.Equal(t, 1, c.Info().NumItems)

	c.Remove("dir/blah")
	assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
	assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))
	_, err = f.ReadAt(nil, 0)
	assert.NotEqual(t, io.EOF, err)

	a, err := c.OpenFile("/a", os.O_CREATE|os.O_WRONLY)
	defer a.Close()
	require.NoError(t, err)
	b, err := c.OpenFile("b", os.O_CREATE|os.O_WRONLY)
	defer b.Close()
	require.NoError(t, err)
	c.mu.Lock()
	assert.False(t, c.pathInfo("a").Accessed.After(c.pathInfo("b").Accessed))
	c.mu.Unlock()
	n, err := a.WriteAt([]byte("hello"), 0)
	assert.NoError(t, err)
	assert.EqualValues(t, 5, n)
	assert.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: -1,
		NumItems: 2,
	}, c.Info())
	assert.False(t, c.pathInfo("b").Accessed.After(c.pathInfo("a").Accessed))

	// Reopen a, to check that the info values remain correct.
	assert.NoError(t, a.Close())
	a, err = c.OpenFile("a", 0)
	require.NoError(t, err)
	require.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: -1,
		NumItems: 2,
	}, c.Info())

	c.SetCapacity(5)
	require.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: 5,
		NumItems: 2,
	}, c.Info())

	n, err = a.WriteAt([]byte(" world"), 5)
	assert.Error(t, err)
	n, err = b.WriteAt([]byte("boom!"), 0)
	// "a" and "b" have been evicted.
	require.NoError(t, err)
	require.EqualValues(t, 5, n)
	require.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: 5,
		NumItems: 1,
	}, c.Info())
}

func TestSanitizePath(t *testing.T) {
	assert.EqualValues(t, "", sanitizePath("////"))
	assert.EqualValues(t, "", sanitizePath("/../.."))
	assert.EqualValues(t, "a", sanitizePath("/a//b/.."))
	assert.EqualValues(t, "a", sanitizePath("../a"))
	assert.EqualValues(t, "a", sanitizePath("./a"))
}

func BenchmarkCacheOpenFile(t *testing.B) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	c, err := NewCache(td)
	for range iter.N(t.N) {
		func() {
			f, err := c.OpenFile("a", os.O_CREATE|os.O_RDWR)
			require.NoError(t, err)
			assert.NoError(t, f.Close())
		}()
	}
}

func TestFileReadWrite(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)

	c, err := NewCache(td)
	require.NoError(t, err)

	a, err := c.OpenFile("a", os.O_CREATE|os.O_EXCL|os.O_RDWR)
	require.NoError(t, err)
	defer a.Close()

	for off, c := range []byte("herp") {
		n, err := a.WriteAt([]byte{c}, int64(off))
		assert.NoError(t, err)
		require.EqualValues(t, 1, n)
	}
	for off, c := range []byte("herp") {
		var b [1]byte
		n, err := a.ReadAt(b[:], int64(off))
		require.EqualValues(t, 1, n)
		require.NoError(t, err)
		assert.EqualValues(t, []byte{c}, b[:])
	}

}
<commit_msg>Fix filecache tests on Windows<commit_after>package filecache

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/bradfitz/iter"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/anacrolix/missinggo/v2"
)

func TestCache(t *testing.T) {
	td, err := ioutil.TempDir("", "gotest")
	require.NoError(t, err)
	defer os.RemoveAll(td)

	c, err := NewCache(filepath.Join(td, "cache"))
	require.NoError(t, err)
	assert.EqualValues(t, CacheInfo{
		Filled:   0,
		Capacity: -1,
		NumItems: 0,
	}, c.Info())

	c.WalkItems(func(i ItemInfo) {})

	_, err = c.OpenFile("/", os.O_CREATE)
	assert.NotNil(t, err)

	_, err = c.OpenFile("", os.O_CREATE)
	assert.NotNil(t, err)

	c.WalkItems(func(i ItemInfo) {})

	require.Equal(t, CacheInfo{
		Filled:   0,
		Capacity: -1,
		NumItems: 0,
	}, c.Info())

	_, err = c.OpenFile("notexist", 0)
	assert.True(t, os.IsNotExist(err), err)

	_, err = c.OpenFile("/notexist", 0)
	assert.True(t, os.IsNotExist(err), err)

	_, err = c.OpenFile("/dir/notexist", 0)
	assert.True(t, os.IsNotExist(err), err)

	f, err := c.OpenFile("dir/blah", os.O_CREATE)
	require.NoError(t, err)
	defer f.Close()
	require.Equal(t, CacheInfo{
		Filled:   0,
		Capacity: -1,
		NumItems: 1,
	}, c.Info())

	c.WalkItems(func(i ItemInfo) {})

	assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
	assert.True(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))
	assert.Equal(t, 1, c.Info().NumItems)

	_, err = f.ReadAt(nil, 0)
	assert.NotEqual(t, io.EOF, err)
	f.Close()

	require.NoError(t, c.Remove("dir/blah"))
	assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/blah"))))
	assert.False(t, missinggo.FilePathExists(filepath.Join(td, filepath.FromSlash("cache/dir/"))))

	a, err := c.OpenFile("/a", os.O_CREATE|os.O_WRONLY)
	defer a.Close()
	require.NoError(t, err)
	b, err := c.OpenFile("b", os.O_CREATE|os.O_WRONLY)
	defer b.Close()
	require.NoError(t, err)
	c.mu.Lock()
	assert.False(t, c.pathInfo("a").Accessed.After(c.pathInfo("b").Accessed))
	c.mu.Unlock()
	n, err := a.WriteAt([]byte("hello"), 0)
	assert.NoError(t, err)
	assert.EqualValues(t, 5, n)
	assert.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: -1,
		NumItems: 2,
	}, c.Info())
	assert.False(t, c.pathInfo("b").Accessed.After(c.pathInfo("a").Accessed))

	// Reopen a, to check that the info values remain correct.
	assert.NoError(t, a.Close())
	a, err = c.OpenFile("a", 0)
	require.NoError(t, err)
	require.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: -1,
		NumItems: 2,
	}, c.Info())

	c.SetCapacity(5)
	require.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: 5,
		NumItems: 2,
	}, c.Info())

	n, err = a.WriteAt([]byte(" world"), 5)
	assert.Error(t, err)
	n, err = b.WriteAt([]byte("boom!"), 0)
	// "a" and "b" have been evicted.
	require.NoError(t, err)
	require.EqualValues(t, 5, n)
	require.EqualValues(t, CacheInfo{
		Filled:   5,
		Capacity: 5,
		NumItems: 1,
	}, c.Info())
}

func TestSanitizePath(t *testing.T) {
	assert.EqualValues(t, "", sanitizePath("////"))
	assert.EqualValues(t, "", sanitizePath("/../.."))
	assert.EqualValues(t, "a", sanitizePath("/a//b/.."))
	assert.EqualValues(t, "a", sanitizePath("../a"))
	assert.EqualValues(t, "a", sanitizePath("./a"))
}

func BenchmarkCacheOpenFile(t *testing.B) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)
	c, err := NewCache(td)
	for range iter.N(t.N) {
		func() {
			f, err := c.OpenFile("a", os.O_CREATE|os.O_RDWR)
			require.NoError(t, err)
			assert.NoError(t, f.Close())
		}()
	}
}

func TestFileReadWrite(t *testing.T) {
	td, err := ioutil.TempDir("", "")
	require.NoError(t, err)
	defer os.RemoveAll(td)

	c, err := NewCache(td)
	require.NoError(t, err)

	a, err := c.OpenFile("a", os.O_CREATE|os.O_EXCL|os.O_RDWR)
	require.NoError(t, err)
	defer a.Close()

	for off, c := range []byte("herp") {
		n, err := a.WriteAt([]byte{c}, int64(off))
		assert.NoError(t, err)
		require.EqualValues(t, 1, n)
	}
	for off, c := range []byte("herp") {
		var b [1]byte
		n, err := a.ReadAt(b[:], int64(off))
		require.EqualValues(t, 1, n)
		require.NoError(t, err)
		assert.EqualValues(t, []byte{c}, b[:])
	}

}
<|endoftext|>


<commit_before>package rmux_test

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/piotrkowalczuk/rmux"

	"io/ioutil"
)

func ExampleNewServeMux() {
	mux := rmux.NewServeMux(rmux.ServeMuxOpts{})
	mux.Handle("GET/user/deactivate", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusForbidden)
	}))
	mux.Handle("GET/user/:id", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		id := rmux.Params(r).Path.Get("id")

		rw.WriteHeader(http.StatusOK)
		io.WriteString(rw, `{"id": `+id+`}`)
	}))

	ts := httptest.NewServer(mux)

	var (
		res *http.Response
		err error
		pay []byte
	)

	if res, err = http.Get(ts.URL + "/user/9000"); err == nil {
		defer res.Body.Close()
		if pay, err = ioutil.ReadAll(res.Body); err == nil {
			fmt.Println(string(pay))
		}
	}
	if err != nil {
		fmt.Println(err.Error())
	}

	// Output:
	// {"id": 9000}
}

func TestServeMux_ServeHTTP(t *testing.T) {
	sm := rmux.NewServeMux(rmux.ServeMuxOpts{
		NotFound: http.NotFoundHandler(),
	})
	for pattern, given := range testPaths {
		sm.Handle(pattern, func(pat, exp string) http.Handler {
			return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
				if strings.Contains(pat, ":") {
					val := rmux.Params(r)
					if val.Path == nil {
						t.Error("context should not be empty")
					}
				}
				if r.URL.Path != exp {
					t.Errorf("executed handler do not match expected path, expected %s, got %s", exp, r.URL.Path)
				} else {
					t.Logf("proper handler executed for path: %s", exp)
				}
			})
		}(pattern, given))
	}

	ts := httptest.NewServer(sm)

	for pattern, path := range testPaths {
		t.Run(pattern, func(t *testing.T) {
			resp, err := http.Get(ts.URL + path)
			if err != nil {
				t.Fatalf("unexpected error: %s", err.Error())
			}
			if resp.StatusCode != http.StatusOK {
				t.Errorf("wrong status code: expected %s but got %s", http.StatusText(http.StatusOK), http.StatusText(resp.StatusCode))
			}
		})
	}
}

var testPaths = map[string]string{
	"GET/a/:a/b/:b/c/:c/d/:d/e/:e/f/:f/g/:g/h/:h": "/a/a/b/b/c/c/d/d/e/e/f/f/g/g/h/h",
	"GET/users":                                        "/users",
	"GET/comments":                                     "/comments/",
	"GET/users/cleanup":                                "/users/cleanup",
	"GET/users/:id":                                    "/users/123",
	"GET/authorizations":                               "/authorizations",
	"GET/authorizations/:id":                           "/authorizations/1",
	"POST/authorizations":                              "/authorizations",
	"DELETE/authorizations/:id":                        "/authorizations/1",
	"GET/applications/:client_id/tokens/:access_token": "/applications/1/tokens/123456789",
}
<commit_msg>GoString test<commit_after>package rmux_test

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/piotrkowalczuk/rmux"

	"io/ioutil"
)

func ExampleNewServeMux() {
	mux := rmux.NewServeMux(rmux.ServeMuxOpts{})
	mux.Handle("GET/user/deactivate", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusForbidden)
	}))
	mux.Handle("GET/user/:id", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		id := rmux.Params(r).Path.Get("id")

		rw.WriteHeader(http.StatusOK)
		io.WriteString(rw, `{"id": `+id+`}`)
	}))

	ts := httptest.NewServer(mux)

	var (
		res *http.Response
		err error
		pay []byte
	)

	if res, err = http.Get(ts.URL + "/user/9000"); err == nil {
		defer res.Body.Close()
		if pay, err = ioutil.ReadAll(res.Body); err == nil {
			fmt.Println(string(pay))
		}
	}
	if err != nil {
		fmt.Println(err.Error())
	}

	// Output:
	// {"id": 9000}
}

func TestServeMux_ServeHTTP(t *testing.T) {
	sm := rmux.NewServeMux(rmux.ServeMuxOpts{
		NotFound: http.NotFoundHandler(),
	})
	for pattern, given := range testPaths {
		sm.Handle(pattern, func(pat, exp string) http.Handler {
			return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
				if strings.Contains(pat, ":") {
					val := rmux.Params(r)
					if val.Path == nil {
						t.Error("context should not be empty")
					}
				}
				if r.URL.Path != exp {
					t.Errorf("executed handler do not match expected path, expected %s, got %s", exp, r.URL.Path)
				} else {
					t.Logf("proper handler executed for path: %s", exp)
				}
			})
		}(pattern, given))
	}

	ts := httptest.NewServer(sm)

	for pattern, path := range testPaths {
		t.Run(pattern, func(t *testing.T) {
			resp, err := http.Get(ts.URL + path)
			if err != nil {
				t.Fatalf("unexpected error: %s", err.Error())
			}
			if resp.StatusCode != http.StatusOK {
				t.Errorf("wrong status code: expected %s but got %s", http.StatusText(http.StatusOK), http.StatusText(resp.StatusCode))
			}
		})
	}
}

var testPaths = map[string]string{
	"GET/a/:a/b/:b/c/:c/d/:d/e/:e/f/:f/g/:g/h/:h": "/a/a/b/b/c/c/d/d/e/e/f/f/g/g/h/h",
	"GET/users":                                        "/users",
	"GET/comments":                                     "/comments/",
	"GET/users/cleanup":                                "/users/cleanup",
	"GET/users/:id":                                    "/users/123",
	"GET/authorizations":                               "/authorizations",
	"GET/authorizations/:id":                           "/authorizations/1",
	"POST/authorizations":                              "/authorizations",
	"DELETE/authorizations/:id":                        "/authorizations/1",
	"GET/applications/:client_id/tokens/:access_token": "/applications/1/tokens/123456789",
}

func TestServeMux_GoString(t *testing.T) {
	mux := rmux.NewServeMux(rmux.ServeMuxOpts{})
	mux.Handle("GET/user/deactivate", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(http.StatusForbidden)
	}))

	got := mux.GoString()
	expected := `{
	"GET": {
		"Resource": "GET",
		"End": false,
		"Kind": 0,
		"Handler": false,
		"NonStatic": null,
		"Static": {
			"user": {
				"Resource": "user",
				"End": false,
				"Kind": 1,
				"Handler": false,
				"NonStatic": null,
				"Static": {
					"deactivate": {
						"Resource": "deactivate",
						"End": true,
						"Kind": 1,
						"Handler": true,
						"NonStatic": null,
						"Static": {}
					}
				}
			}
		}
	}
}`

	if got != expected {
		t.Errorf("wrong output, expected:\n	%s but got:\n	%s", expected, got)
	}
}<|endoftext|>


<commit_before>package bon

import (
	"bytes"
	"net/http"
	"net/http/httptest"
	"testing"
)

func setup() *httptest.Server {
	r := NewRouter()
	r.Get("/foo", foo)
	r.Get("/bar", bar)
	r.Get("/baz", baz)
	return httptest.NewServer(r)
}

func teardown(ts *httptest.Server) {
	ts.Close()
}

func TestMux(t *testing.T) {
	ts := setup()
	defer teardown(ts)

	cases := []struct {
		Path   string
		Status int
		Body   string
	}{
		{"/foo", 200, "foo"},
		{"/bar", 200, "bar"},
		{"/baz", 200, "baz"},
		{"/hoge", 404, "404 page not found\n"},
	}

	for _, tc := range cases {
		res, err := http.Get(ts.URL + tc.Path)

		if err != nil {
			t.Fatalf("something went to wrong: %s", err)
		}

		if got, want := res.StatusCode, tc.Status; got != want {
			t.Fatalf("StatusCode=%d, want=%d", got, want)
		}

		buf := new(bytes.Buffer)
		buf.ReadFrom(res.Body)

		if got, want := buf.String(), tc.Body; got != want {
			t.Fatalf("Body=%q, want=%q", got, want)
		}
	}
}

func foo(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("foo"))
}

func bar(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("bar"))
}

func baz(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("baz"))
}
<commit_msg>test pattern<commit_after>package bon

import (
	"bytes"
	"net/http"
	"net/http/httptest"
	"testing"
)

type Pattern struct {
	Reqests []*Reqest
	Server  *httptest.Server
}

type Reqest struct {
	Path           string
	WantStatusCode int
	WantBody       string
}

func (p *Pattern) Do(t *testing.T) {
	for _, v := range p.Reqests {
		res, err := http.Get(p.Server.URL + v.Path)

		if err != nil {
			t.Fatalf(err.Error())
		}

		if res.StatusCode != v.WantStatusCode {
			t.Fatalf("Path=%q, StatusCode=%d, Want=%d", v.Path, res.StatusCode, v.WantStatusCode)
		}

		var buf bytes.Buffer

		if _, err := buf.ReadFrom(res.Body); err != nil {
			t.Fatalf(err.Error())
		}

		if buf.String() != v.WantBody {
			t.Fatalf("Path=%q, Body=%q, Want=%q", v.Path, buf.String(), v.WantBody)
		}
	}
}

func (p *Pattern) Close() {
	p.Server.Close()
}

func TestMuxBasicParam(t *testing.T) {
	r := NewRouter()

	r.Get("/users/:name", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte([]byte(URLParam(r, "name"))))
	})

	p := &Pattern{
		Reqests: []*Reqest{
			{"/users/taro", 200, "taro"},
			{"/users/jiro", 200, "jiro"},
			{"/users", 404, "404 page not found\n"},
		},

		Server: httptest.NewServer(r),
	}

	defer p.Close()
	p.Do(t)
}
<|endoftext|>


<commit_before>package menu

import (
	gltext "github.com/4ydx/gltext"
	glfw "github.com/go-gl/glfw3"
	"github.com/go-gl/glow/gl-core/3.3/gl"
	"github.com/go-gl/mathgl/mgl32"
	"os"
)

type Point struct {
	X, Y float32
}

var vertexShaderSource string = `
#version 330

uniform mat4 matrix;

in vec4 position;

void main() {
  gl_Position = matrix * position;
}
` + "\x00"

var fragmentShaderSource string = `
#version 330

uniform vec4 background;
out vec4 fragment_color;

void main() {
  fragment_color = background;
}
` + "\x00"

type MouseClick int

const (
	MouseUnclicked MouseClick = iota
	MouseLeft
	MouseRight
	MouseCenter
)

type Menu struct {
	// options
	Visible      bool
	ShowOn       glfw.Key
	Height       float32
	Width        float32
	IsAutoCenter bool
	lowerLeft    Point

	backgroundUniform int32
	Background        mgl32.Vec4

	// interactive objects
	Font          *gltext.Font
	Labels        []*Label
	TextScaleRate float32 // increment during a scale operation

	// opengl oriented
	WindowWidth   float32
	WindowHeight  float32
	program       uint32 // shader program
	glMatrix      int32  // ortho matrix
	position      uint32 // index location
	vao           uint32
	vbo           uint32
	ebo           uint32
	ortho         mgl32.Mat4
	vboData       []float32
	vboIndexCount int
	eboData       []int32
	eboIndexCount int
}

func (menu *Menu) AddLabel(label *Label, str string) {
	label.Load(menu, menu.Font)
	label.Text.SetString(str)
	label.Text.SetScale(1)
	label.Text.SetPosition(0, 0)
	label.Text.SetColor(0, 0, 0)
	menu.Labels = append(menu.Labels, label)
}

func (menu *Menu) Show() {
	for i := range menu.Labels {
		menu.Labels[i].Reset()
	}
	menu.Visible = true
}

func (menu *Menu) Hide() {
	for i := range menu.Labels {
		menu.Labels[i].Reset()
	}
	menu.Visible = false
}

func (menu *Menu) Toggle() {
	for i := range menu.Labels {
		menu.Labels[i].Reset()
	}
	menu.Visible = !menu.Visible
}

func (menu *Menu) Load(width float32, height float32, scale int32) (err error) {
	glfloat_size := 4
	glint_size := 4

	menu.Visible = false
	menu.ShowOn = glfw.KeyM
	menu.Width = width
	menu.Height = height

	// load font
	fd, err := os.Open("font/luximr.ttf")
	if err != nil {
		return
	}
	defer fd.Close()

	menu.Font, err = gltext.LoadTruetype(fd, scale, 32, 127)
	if err != nil {
		return
	}

	// 2DO: make this time dependent rather than fps dependent
	menu.TextScaleRate = 0.01

	// create shader program and define attributes and uniforms
	menu.program, err = gltext.NewProgram(vertexShaderSource, fragmentShaderSource)
	if err != nil {
		return
	}
	menu.glMatrix = gl.GetUniformLocation(menu.program, gl.Str("matrix\x00"))
	menu.backgroundUniform = gl.GetUniformLocation(menu.program, gl.Str("background\x00"))
	menu.position = uint32(gl.GetAttribLocation(menu.program, gl.Str("position\x00")))

	gl.GenVertexArrays(1, &menu.vao)
	gl.GenBuffers(1, &menu.vbo)
	gl.GenBuffers(1, &menu.ebo)

	// vao
	gl.BindVertexArray(menu.vao)

	// vbo
	// specify the buffer for which the VertexAttribPointer calls apply
	gl.BindBuffer(gl.ARRAY_BUFFER, menu.vbo)

	gl.EnableVertexAttribArray(menu.position)
	gl.VertexAttribPointer(
		menu.position,
		2,
		gl.FLOAT,
		false,
		0, // no stride... yet
		gl.PtrOffset(0),
	)

	// ebo
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, menu.ebo)

	// i am guessing that order is important here
	gl.BindVertexArray(0)
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)

	// ebo, vbo data
	menu.vboIndexCount = 4 * 2 // four indices (2 points per index)
	menu.eboIndexCount = 6     // 6 triangle indices for a quad
	menu.vboData = make([]float32, menu.vboIndexCount, menu.vboIndexCount)
	menu.eboData = make([]int32, menu.eboIndexCount, menu.eboIndexCount)
	menu.lowerLeft = menu.findCenter()
	menu.makeBufferData()

	// setup context
	gl.BindVertexArray(menu.vao)
	gl.BindBuffer(gl.ARRAY_BUFFER, menu.vbo)
	gl.BufferData(
		gl.ARRAY_BUFFER, glfloat_size*menu.vboIndexCount, gl.Ptr(menu.vboData), gl.DYNAMIC_DRAW)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, menu.ebo)
	gl.BufferData(
		gl.ELEMENT_ARRAY_BUFFER, glint_size*menu.eboIndexCount, gl.Ptr(menu.eboData), gl.DYNAMIC_DRAW)
	gl.BindVertexArray(0)

	// not necesssary, but i just want to better understand using vertex arrays
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
	return nil
}

func (menu *Menu) ResizeWindow(width float32, height float32) {
	menu.WindowWidth = width
	menu.WindowHeight = height
	menu.Font.ResizeWindow(width, height)
	menu.ortho = mgl32.Ortho2D(-menu.WindowWidth/2, menu.WindowWidth/2, -menu.WindowHeight/2, menu.WindowHeight/2)
}

func (menu *Menu) makeBufferData() {
	// index (0,0)
	menu.vboData[0] = menu.lowerLeft.X // position
	menu.vboData[1] = menu.lowerLeft.Y

	// index (1,0)
	menu.vboData[2] = menu.lowerLeft.X + menu.Width
	menu.vboData[3] = menu.lowerLeft.Y

	// index (1,1)
	menu.vboData[4] = menu.lowerLeft.X + menu.Width
	menu.vboData[5] = menu.lowerLeft.Y + menu.Height

	// index (0,1)
	menu.vboData[6] = menu.lowerLeft.X
	menu.vboData[7] = menu.lowerLeft.Y + menu.Height

	menu.eboData[0] = 0
	menu.eboData[1] = 1
	menu.eboData[2] = 2
	menu.eboData[3] = 0
	menu.eboData[4] = 2
	menu.eboData[5] = 3
}

func (menu *Menu) Release() {
	gl.DeleteBuffers(1, &menu.vbo)
	gl.DeleteBuffers(1, &menu.ebo)
	gl.DeleteBuffers(1, &menu.vao)
	for i := range menu.Labels {
		menu.Labels[i].Text.Release()
		if menu.Labels[i].Shadow != nil && menu.Labels[i].Shadow.Text != nil {
			menu.Labels[i].Shadow.Text.Release()
		}
	}
}

func (menu *Menu) Draw() bool {
	if !menu.Visible {
		return menu.Visible
	}
	gl.UseProgram(menu.program)

	gl.UniformMatrix4fv(menu.glMatrix, 1, false, &menu.ortho[0])
	gl.Uniform4fv(menu.backgroundUniform, 1, &menu.Background[0])

	gl.BindVertexArray(menu.vao)
	gl.DrawElements(gl.TRIANGLES, int32(menu.eboIndexCount), gl.UNSIGNED_INT, nil)
	gl.BindVertexArray(0)
	for i := range menu.Labels {
		if !menu.Labels[i].IsHover {
			if menu.Labels[i].OnNotHover != nil {
				menu.Labels[i].OnNotHover(menu.Labels[i])
				if menu.Labels[i].Shadow != nil {
					menu.Labels[i].OnNotHover(&menu.Labels[i].Shadow.Label)
				}
			}
		}
		menu.Labels[i].Draw()
	}
	return menu.Visible
}

func (menu *Menu) OrthoToMouseCoord() (x, y float32) {
	x = menu.lowerLeft.X + menu.WindowWidth/2
	y = menu.lowerLeft.Y + menu.WindowHeight/2
	return
}

func (menu *Menu) MouseClick(xPos, yPos float64, button MouseClick) {
	if !menu.Visible {
		return
	}
	yPos = float64(menu.WindowHeight) - yPos
	for i := range menu.Labels {
		if menu.Labels[i].OnClick != nil {
			menu.Labels[i].IsClicked(xPos, yPos, button)
		}
	}
}

func (menu *Menu) MouseRelease(xPos, yPos float64, button MouseClick) {
	if !menu.Visible {
		return
	}
	yPos = float64(menu.WindowHeight) - yPos
	for i := range menu.Labels {
		if menu.Labels[i].OnRelease != nil {
			menu.Labels[i].IsReleased(xPos, yPos, button)
		}
	}
}

func (menu *Menu) MouseHover(xPos, yPos float64) {
	if !menu.Visible {
		return
	}
	yPos = float64(menu.WindowHeight) - yPos
	for i := range menu.Labels {
		if menu.Labels[i].OnHover != nil {
			menu.Labels[i].IsHovered(xPos, yPos)
		}
	}
}

func (menu *Menu) findCenter() (lowerLeft Point) {
	menuWidthHalf := menu.Width / 2
	menuHeightHalf := menu.Height / 2

	lowerLeft.X = -menuWidthHalf
	lowerLeft.Y = -menuHeightHalf
	return
}
<commit_msg>Set appropriate depth test.<commit_after>package menu

import (
	gltext "github.com/4ydx/gltext"
	glfw "github.com/go-gl/glfw3"
	"github.com/go-gl/glow/gl-core/3.3/gl"
	"github.com/go-gl/mathgl/mgl32"
	"os"
)

type Point struct {
	X, Y float32
}

var vertexShaderSource string = `
#version 330

uniform mat4 matrix;

in vec4 position;

void main() {
  gl_Position = matrix * position;
}
` + "\x00"

var fragmentShaderSource string = `
#version 330

uniform vec4 background;
out vec4 fragment_color;

void main() {
  fragment_color = background;
}
` + "\x00"

type MouseClick int

const (
	MouseUnclicked MouseClick = iota
	MouseLeft
	MouseRight
	MouseCenter
)

type Menu struct {
	// options
	Visible      bool
	ShowOn       glfw.Key
	Height       float32
	Width        float32
	IsAutoCenter bool
	lowerLeft    Point

	backgroundUniform int32
	Background        mgl32.Vec4

	// interactive objects
	Font          *gltext.Font
	Labels        []*Label
	TextScaleRate float32 // increment during a scale operation

	// opengl oriented
	WindowWidth   float32
	WindowHeight  float32
	program       uint32 // shader program
	glMatrix      int32  // ortho matrix
	position      uint32 // index location
	vao           uint32
	vbo           uint32
	ebo           uint32
	ortho         mgl32.Mat4
	vboData       []float32
	vboIndexCount int
	eboData       []int32
	eboIndexCount int
}

func (menu *Menu) AddLabel(label *Label, str string) {
	label.Load(menu, menu.Font)
	label.Text.SetString(str)
	label.Text.SetScale(1)
	label.Text.SetPosition(0, 0)
	label.Text.SetColor(0, 0, 0)
	menu.Labels = append(menu.Labels, label)
}

func (menu *Menu) Show() {
	for i := range menu.Labels {
		menu.Labels[i].Reset()
	}
	menu.Visible = true
}

func (menu *Menu) Hide() {
	for i := range menu.Labels {
		menu.Labels[i].Reset()
	}
	menu.Visible = false
}

func (menu *Menu) Toggle() {
	for i := range menu.Labels {
		menu.Labels[i].Reset()
	}
	menu.Visible = !menu.Visible
}

func (menu *Menu) Load(width float32, height float32, scale int32) (err error) {
	glfloat_size := 4
	glint_size := 4

	menu.Visible = false
	menu.ShowOn = glfw.KeyM
	menu.Width = width
	menu.Height = height

	// load font
	fd, err := os.Open("font/luximr.ttf")
	if err != nil {
		return
	}
	defer fd.Close()

	menu.Font, err = gltext.LoadTruetype(fd, scale, 32, 127)
	if err != nil {
		return
	}

	// 2DO: make this time dependent rather than fps dependent
	menu.TextScaleRate = 0.01

	// create shader program and define attributes and uniforms
	menu.program, err = gltext.NewProgram(vertexShaderSource, fragmentShaderSource)
	if err != nil {
		return
	}
	menu.glMatrix = gl.GetUniformLocation(menu.program, gl.Str("matrix\x00"))
	menu.backgroundUniform = gl.GetUniformLocation(menu.program, gl.Str("background\x00"))
	menu.position = uint32(gl.GetAttribLocation(menu.program, gl.Str("position\x00")))

	gl.GenVertexArrays(1, &menu.vao)
	gl.GenBuffers(1, &menu.vbo)
	gl.GenBuffers(1, &menu.ebo)

	// vao
	gl.BindVertexArray(menu.vao)

	// 2DO: Change text depth to get it to render? For now this works.
	gl.Enable(gl.DEPTH_TEST)
	gl.DepthFunc(gl.LEQUAL)

	// vbo
	// specify the buffer for which the VertexAttribPointer calls apply
	gl.BindBuffer(gl.ARRAY_BUFFER, menu.vbo)

	gl.EnableVertexAttribArray(menu.position)
	gl.VertexAttribPointer(
		menu.position,
		2,
		gl.FLOAT,
		false,
		0, // no stride... yet
		gl.PtrOffset(0),
	)

	// ebo
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, menu.ebo)

	// i am guessing that order is important here
	gl.BindVertexArray(0)
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)

	// ebo, vbo data
	menu.vboIndexCount = 4 * 2 // four indices (2 points per index)
	menu.eboIndexCount = 6     // 6 triangle indices for a quad
	menu.vboData = make([]float32, menu.vboIndexCount, menu.vboIndexCount)
	menu.eboData = make([]int32, menu.eboIndexCount, menu.eboIndexCount)
	menu.lowerLeft = menu.findCenter()
	menu.makeBufferData()

	// setup context
	gl.BindVertexArray(menu.vao)
	gl.BindBuffer(gl.ARRAY_BUFFER, menu.vbo)
	gl.BufferData(
		gl.ARRAY_BUFFER, glfloat_size*menu.vboIndexCount, gl.Ptr(menu.vboData), gl.DYNAMIC_DRAW)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, menu.ebo)
	gl.BufferData(
		gl.ELEMENT_ARRAY_BUFFER, glint_size*menu.eboIndexCount, gl.Ptr(menu.eboData), gl.DYNAMIC_DRAW)
	gl.BindVertexArray(0)

	// not necesssary, but i just want to better understand using vertex arrays
	gl.BindBuffer(gl.ARRAY_BUFFER, 0)
	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
	return nil
}

func (menu *Menu) ResizeWindow(width float32, height float32) {
	menu.WindowWidth = width
	menu.WindowHeight = height
	menu.Font.ResizeWindow(width, height)
	menu.ortho = mgl32.Ortho2D(-menu.WindowWidth/2, menu.WindowWidth/2, -menu.WindowHeight/2, menu.WindowHeight/2)
}

func (menu *Menu) makeBufferData() {
	// index (0,0)
	menu.vboData[0] = menu.lowerLeft.X // position
	menu.vboData[1] = menu.lowerLeft.Y

	// index (1,0)
	menu.vboData[2] = menu.lowerLeft.X + menu.Width
	menu.vboData[3] = menu.lowerLeft.Y

	// index (1,1)
	menu.vboData[4] = menu.lowerLeft.X + menu.Width
	menu.vboData[5] = menu.lowerLeft.Y + menu.Height

	// index (0,1)
	menu.vboData[6] = menu.lowerLeft.X
	menu.vboData[7] = menu.lowerLeft.Y + menu.Height

	menu.eboData[0] = 0
	menu.eboData[1] = 1
	menu.eboData[2] = 2
	menu.eboData[3] = 0
	menu.eboData[4] = 2
	menu.eboData[5] = 3
}

func (menu *Menu) Release() {
	gl.DeleteBuffers(1, &menu.vbo)
	gl.DeleteBuffers(1, &menu.ebo)
	gl.DeleteBuffers(1, &menu.vao)
	for i := range menu.Labels {
		menu.Labels[i].Text.Release()
		if menu.Labels[i].Shadow != nil && menu.Labels[i].Shadow.Text != nil {
			menu.Labels[i].Shadow.Text.Release()
		}
	}
}

func (menu *Menu) Draw() bool {
	if !menu.Visible {
		return menu.Visible
	}
	gl.UseProgram(menu.program)

	gl.UniformMatrix4fv(menu.glMatrix, 1, false, &menu.ortho[0])
	gl.Uniform4fv(menu.backgroundUniform, 1, &menu.Background[0])

	gl.BindVertexArray(menu.vao)
	gl.DrawElements(gl.TRIANGLES, int32(menu.eboIndexCount), gl.UNSIGNED_INT, nil)
	gl.BindVertexArray(0)
	for i := range menu.Labels {
		if !menu.Labels[i].IsHover {
			if menu.Labels[i].OnNotHover != nil {
				menu.Labels[i].OnNotHover(menu.Labels[i])
				if menu.Labels[i].Shadow != nil {
					menu.Labels[i].OnNotHover(&menu.Labels[i].Shadow.Label)
				}
			}
		}
		menu.Labels[i].Draw()
	}
	return menu.Visible
}

func (menu *Menu) OrthoToMouseCoord() (x, y float32) {
	x = menu.lowerLeft.X + menu.WindowWidth/2
	y = menu.lowerLeft.Y + menu.WindowHeight/2
	return
}

func (menu *Menu) MouseClick(xPos, yPos float64, button MouseClick) {
	if !menu.Visible {
		return
	}
	yPos = float64(menu.WindowHeight) - yPos
	for i := range menu.Labels {
		if menu.Labels[i].OnClick != nil {
			menu.Labels[i].IsClicked(xPos, yPos, button)
		}
	}
}

func (menu *Menu) MouseRelease(xPos, yPos float64, button MouseClick) {
	if !menu.Visible {
		return
	}
	yPos = float64(menu.WindowHeight) - yPos
	for i := range menu.Labels {
		if menu.Labels[i].OnRelease != nil {
			menu.Labels[i].IsReleased(xPos, yPos, button)
		}
	}
}

func (menu *Menu) MouseHover(xPos, yPos float64) {
	if !menu.Visible {
		return
	}
	yPos = float64(menu.WindowHeight) - yPos
	for i := range menu.Labels {
		if menu.Labels[i].OnHover != nil {
			menu.Labels[i].IsHovered(xPos, yPos)
		}
	}
}

func (menu *Menu) findCenter() (lowerLeft Point) {
	menuWidthHalf := menu.Width / 2
	menuHeightHalf := menu.Height / 2

	lowerLeft.X = -menuWidthHalf
	lowerLeft.Y = -menuHeightHalf
	return
}
<|endoftext|>


<commit_before>package v0

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

	"github.com/mfine30/prodda/domain"
	"github.com/mfine30/prodda/timer"
)

type prodsCreateRequestBody struct {
	Time      time.Time `json:"time"`
	Token     string    `json:"token"`
	BuildID   uint      `json:"buildID"`
	Frequency string    `json:"frequency"`
}

func prodsCreateHandler() http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		decoder := json.NewDecoder(r.Body)
		var b prodsCreateRequestBody

		err := decoder.Decode(&b)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}

		err = validateProdRequestBody(b)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %s\n", err)
			return
		}

		// We parse the frequency separately as json decoding does not offer
		// the flexibility that ParseDuration does.
		frequency, err := time.ParseDuration(b.Frequency)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}

		task := domain.NewTravisTask(b.Token, b.BuildID)
		prod, err := domain.NewProd(b.Time, task, frequency)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}

		scheduler, err := timer.NewScheduler(prod)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}
		fmt.Printf("Scheduler created\n")

		go scheduler.Start()

		fmt.Fprintln(rw, "New prod created")
	})
}

func validateProdRequestBody(b prodsCreateRequestBody) error {
	if b.Time.IsZero() {
		return errors.New("Time must be provided")
	}

	if b.Token == "" {
		return errors.New("Token must be provided")
	}

	if b.BuildID == 0 {
		return errors.New("BuildID must be provided")
	}
	return nil
}
<commit_msg>Prod validator allows nil time in body<commit_after>package v0

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

	"github.com/mfine30/prodda/domain"
	"github.com/mfine30/prodda/timer"
)

type prodsCreateRequestBody struct {
	Time      time.Time `json:"time"`
	Token     string    `json:"token"`
	BuildID   uint      `json:"buildID"`
	Frequency string    `json:"frequency"`
}

func prodsCreateHandler() http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		decoder := json.NewDecoder(r.Body)
		var b prodsCreateRequestBody

		err := decoder.Decode(&b)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}

		err = validateProdRequestBody(b)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %s\n", err)
			return
		}

		// We parse the frequency separately as json decoding does not offer
		// the flexibility that ParseDuration does.
		frequency, err := time.ParseDuration(b.Frequency)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}

		task := domain.NewTravisTask(b.Token, b.BuildID)
		prod, err := domain.NewProd(b.Time, task, frequency)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}

		scheduler, err := timer.NewScheduler(prod)
		if err != nil {
			fmt.Fprintf(rw, "ERROR: %v\n", err)
			return
		}
		fmt.Printf("Scheduler created\n")

		go scheduler.Start()

		fmt.Fprintln(rw, "New prod created")
	})
}

func validateProdRequestBody(b prodsCreateRequestBody) error {
	if b.Token == "" {
		return errors.New("Token must be provided")
	}

	if b.BuildID == 0 {
		return errors.New("BuildID must be provided")
	}
	return nil
}
<|endoftext|>


<commit_before>// Package etm provides a set of Encrypt-Then-MAC AEAD implementations, which
// combine block ciphers like AES with HMACs.
//
// The AEAD (Authenticated Encryption with Associated Data) construction provides
// a unified API for sealing messages in a way which provides both
// confidentiality *and* integrity. Unlike unauthenticated modes like CBC,
// AEAD algorithms are resistant to chosen ciphertext attacks, such as padding
// oracle attacks, etc., and add only a small amount of overhead.
//
// See http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-02 for
// technical details.
package etm

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"crypto/subtle"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
)

// NewAES128SHA256 returns an AEAD_AES_128_CBC_HMAC_SHA_256 instance given a
// 32-byte key or an error if the key is the wrong size.
// AEAD_AES_128_CBC_HMAC_SHA_256 combines AES-128 in CBC mode with
// HMAC-SHA-256-128.
func NewAES128SHA256(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha256.New,
		encKeySize:   16,
		macKeySize:   16,
		tagSize:      16,
		key:          key,
	})
}

// NewAES192SHA384 returns an AEAD_AES_192_CBC_HMAC_SHA_384 instance given a
// 48-byte key or an error if the key is the wrong size.
// AEAD_AES_192_CBC_HMAC_SHA_384 combines AES-192 in CBC mode with
// HMAC-SHA-384-192.
func NewAES192SHA384(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha512.New384,
		encKeySize:   24,
		macKeySize:   24,
		tagSize:      24,
		key:          key,
	})
}

// NewAES256SHA384 returns an AEAD_AES_256_CBC_HMAC_SHA_384 instance given a
// 56-byte key or an error if the key is the wrong size.
// AEAD_AES_256_CBC_HMAC_SHA_384 combines AES-256 in CBC mode with
// HMAC-SHA-384-192.
func NewAES256SHA384(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha512.New384,
		encKeySize:   32,
		macKeySize:   24,
		tagSize:      24,
		key:          key,
	})
}

// NewAES256SHA512 returns an AEAD_AES_256_CBC_HMAC_SHA_512 instance given a
// 64-byte key or an error if the key is the wrong size.
// AEAD_AES_256_CBC_HMAC_SHA_512 combines AES-256 in CBC mode with
// HMAC-SHA-512-256.
func NewAES256SHA512(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha512.New,
		encKeySize:   32,
		macKeySize:   32,
		tagSize:      32,
		key:          key,
	})
}

// NewAES128SHA1 returns an AEAD_AES_128_CBC_HMAC_SHA1 instance given a 36-byte
// key or an error if the key is the wrong size.
// AEAD_AES_128_CBC_HMAC_SHA1 combines AES-128 in CBC mode with HMAC-SHA1-96.
func NewAES128SHA1(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha1.New,
		encKeySize:   16,
		macKeySize:   20,
		tagSize:      12,
		key:          key,
	})
}

type etmParams struct {
	cipherParams
	encKeySize, macKeySize, tagSize int

	key    []byte
	macAlg func() hash.Hash
}

func create(p etmParams) (cipher.AEAD, error) {
	l := p.encKeySize + p.macKeySize
	if len(p.key) != l {
		return nil, fmt.Errorf("etm: key must be %d bytes long", l)
	}
	encKey, macKey := split(p.key, p.encKeySize, p.macKeySize)
	return &etmAEAD{
		etmParams: p,
		encKey:    encKey,
		macKey:    macKey,
	}, nil
}

const (
	dataLenSize = 8
)

type etmAEAD struct {
	etmParams
	encKey, macKey []byte
}

func (aead *etmAEAD) Overhead() int {
	return aead.padSize + aead.tagSize + dataLenSize + aead.NonceSize()
}

func (aead *etmAEAD) NonceSize() int {
	return aead.nonceSize
}

func (aead *etmAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
	b, _ := aead.encAlg(aead.encKey) // guaranteed to work

	c := aead.encrypter(b, nonce)
	i := aead.pad(plaintext, aead.blockSize)
	s := make([]byte, len(i))
	c.CryptBlocks(s, i)
	s = append(nonce, s...)

	t := tag(hmac.New(aead.macAlg, aead.macKey), data, s, aead.tagSize)

	return append(dst, append(s, t...)...)
}

func (aead *etmAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
	s := ciphertext[:len(ciphertext)-aead.tagSize]
	t := ciphertext[len(ciphertext)-aead.tagSize:]
	t2 := tag(hmac.New(aead.macAlg, aead.macKey), data, s, aead.tagSize)
	if nonce == nil {
		nonce = s[:aead.NonceSize()]
	}

	if subtle.ConstantTimeCompare(t, t2) != 1 {
		return nil, errors.New("etm: message authentication failed")
	}

	b, _ := aead.encAlg(aead.encKey) // guaranteed to work

	c := aead.decrypter(b, nonce)
	o := make([]byte, len(s)-len(nonce))
	c.CryptBlocks(o, s[len(nonce):])

	return append(dst, aead.unpad(o, aead.blockSize)...), nil
}

type cipherParams struct {
	nonceSize, blockSize, padSize int

	encAlg    func(key []byte) (cipher.Block, error)
	encrypter func(cipher.Block, []byte) cipher.BlockMode
	decrypter func(cipher.Block, []byte) cipher.BlockMode
	pad       func([]byte, int) []byte
	unpad     func([]byte, int) []byte
}

// AES-CBC-PKCS7
var aesCBC = cipherParams{
	encAlg:    aes.NewCipher,
	blockSize: aes.BlockSize,
	nonceSize: aes.BlockSize,
	encrypter: cipher.NewCBCEncrypter,
	decrypter: cipher.NewCBCDecrypter,
	padSize:   aes.BlockSize,
	pad:       pkcs7pad,
	unpad:     pkcs7unpad,
}

func tag(h hash.Hash, data, s []byte, l int) []byte {
	al := make([]byte, dataLenSize)
	binary.BigEndian.PutUint64(al, uint64(len(data)*8)) // in bits
	h.Write(data)
	h.Write(s)
	h.Write(al)
	return h.Sum(nil)[:l]
}

func split(key []byte, encKeyLen, macKeyLen int) ([]byte, []byte) {
	return key[0:encKeyLen], key[len(key)-macKeyLen:]
}

func pkcs7pad(b []byte, blockSize int) []byte {
	ps := make([]byte, blockSize-(len(b)%blockSize))
	for i := range ps {
		ps[i] = byte(len(ps))
	}
	return append(b, ps...)
}

func pkcs7unpad(b []byte, blockSize int) []byte {
	return b[:len(b)-int(b[len(b)-1])]
}
<commit_msg>Beef up the docs.<commit_after>// Package etm provides a set of Encrypt-Then-MAC AEAD implementations, which
// combine block ciphers like AES with HMACs.
//
// AEADs
//
// An AEAD (Authenticated Encryption with Associated Data) construction provides
// a unified API for sealing messages in a way which provides both
// confidentiality *and* integrity.
//
// This not only prevents malicious tampering but also eliminates online attacks
// like padding oracle attacks which can allow an attacker to recover plaintexts
// without knowledge of the secret key (e.g., Lucky 13 attack, BEAST attack,
// etc.).
//
// By rejecting ciphertexts which have been modified, these types of attacks are
// eliminated.
//
// Constructions
//
// This package implements five proposed standards:
//
//		AEAD_AES_128_CBC_HMAC_SHA_256
//		AEAD_AES_192_CBC_HMAC_SHA_384
//		AEAD_AES_256_CBC_HMAC_SHA_384
//		AEAD_AES_256_CBC_HMAC_SHA_512
//		AEAD_AES_128_CBC_HMAC_SHA1
//
// All five constructions combine AES in CBC mode with an HMAC, but vary in the
// degree of security offered and the amount of overhead required. See
// http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-02 for full
// technical details.
//
// AES-128-CBC-HMAC-SHA-256
//
// AEAD_AES_128_CBC_HMAC_SHA_256 requires a 32-byte key, provides 128 bits of
// security for both confidentiality and integrity, and adds up to 56 bytes of
// overhead per message.
//
// AES-192-CBC-HMAC-SHA-384
//
// AEAD_AES_192_CBC_HMAC_SHA_384 requires a 48-byte key, provides 192 bits of
// security for both confidentiality and integrity, and adds up to 64 bytes of
// overhead per message.
//
// AES-256-CBC-HMAC-SHA-384
//
// AEAD_AES_256_CBC_HMAC_SHA_384 requires a 56-byte key, provides 256 bits of
// security for confidentiality, provides 192 bits of security for integrity, and
// adds up to 64 bytes of overhead per message.
//
// AES-256-CBC-HMAC-SHA-512
//
// AEAD_AES_256_CBC_HMAC_SHA_512 requires a 64-byte key, provides 256 bits of
// security for both confidentiality and integrity, and adds up to 72 bytes of
// overhead per message.
//
// AES-128-CBC-HMAC-SHA1
//
// AEAD_AES_128_CBC_HMAC_SHA1 requires a 36-byte key, provides 128 bits of
// security for confidentiality, provides 96 bits of security for integrity, and
// adds up to 52 bytes of overhead per message. (This construction uses SHA-1,
// and should only be used when the use of SHA2 is not possible.)
package etm

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"crypto/subtle"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
)

// NewAES128SHA256 returns an AEAD_AES_128_CBC_HMAC_SHA_256 instance given a
// 32-byte key or an error if the key is the wrong size.
// AEAD_AES_128_CBC_HMAC_SHA_256 combines AES-128 in CBC mode with
// HMAC-SHA-256-128.
func NewAES128SHA256(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha256.New,
		encKeySize:   16,
		macKeySize:   16,
		tagSize:      16,
		key:          key,
	})
}

// NewAES192SHA384 returns an AEAD_AES_192_CBC_HMAC_SHA_384 instance given a
// 48-byte key or an error if the key is the wrong size.
// AEAD_AES_192_CBC_HMAC_SHA_384 combines AES-192 in CBC mode with
// HMAC-SHA-384-192.
func NewAES192SHA384(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha512.New384,
		encKeySize:   24,
		macKeySize:   24,
		tagSize:      24,
		key:          key,
	})
}

// NewAES256SHA384 returns an AEAD_AES_256_CBC_HMAC_SHA_384 instance given a
// 56-byte key or an error if the key is the wrong size.
// AEAD_AES_256_CBC_HMAC_SHA_384 combines AES-256 in CBC mode with
// HMAC-SHA-384-192.
func NewAES256SHA384(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha512.New384,
		encKeySize:   32,
		macKeySize:   24,
		tagSize:      24,
		key:          key,
	})
}

// NewAES256SHA512 returns an AEAD_AES_256_CBC_HMAC_SHA_512 instance given a
// 64-byte key or an error if the key is the wrong size.
// AEAD_AES_256_CBC_HMAC_SHA_512 combines AES-256 in CBC mode with
// HMAC-SHA-512-256.
func NewAES256SHA512(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha512.New,
		encKeySize:   32,
		macKeySize:   32,
		tagSize:      32,
		key:          key,
	})
}

// NewAES128SHA1 returns an AEAD_AES_128_CBC_HMAC_SHA1 instance given a 36-byte
// key or an error if the key is the wrong size.
// AEAD_AES_128_CBC_HMAC_SHA1 combines AES-128 in CBC mode with HMAC-SHA1-96.
func NewAES128SHA1(key []byte) (cipher.AEAD, error) {
	return create(etmParams{
		cipherParams: aesCBC,
		macAlg:       sha1.New,
		encKeySize:   16,
		macKeySize:   20,
		tagSize:      12,
		key:          key,
	})
}

type etmParams struct {
	cipherParams
	encKeySize, macKeySize, tagSize int

	key    []byte
	macAlg func() hash.Hash
}

func create(p etmParams) (cipher.AEAD, error) {
	l := p.encKeySize + p.macKeySize
	if len(p.key) != l {
		return nil, fmt.Errorf("etm: key must be %d bytes long", l)
	}
	encKey, macKey := split(p.key, p.encKeySize, p.macKeySize)
	return &etmAEAD{
		etmParams: p,
		encKey:    encKey,
		macKey:    macKey,
	}, nil
}

const (
	dataLenSize = 8
)

type etmAEAD struct {
	etmParams
	encKey, macKey []byte
}

func (aead *etmAEAD) Overhead() int {
	return aead.padSize + aead.tagSize + dataLenSize + aead.NonceSize()
}

func (aead *etmAEAD) NonceSize() int {
	return aead.nonceSize
}

func (aead *etmAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
	b, _ := aead.encAlg(aead.encKey) // guaranteed to work

	c := aead.encrypter(b, nonce)
	i := aead.pad(plaintext, aead.blockSize)
	s := make([]byte, len(i))
	c.CryptBlocks(s, i)
	s = append(nonce, s...)

	t := tag(hmac.New(aead.macAlg, aead.macKey), data, s, aead.tagSize)

	return append(dst, append(s, t...)...)
}

func (aead *etmAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
	s := ciphertext[:len(ciphertext)-aead.tagSize]
	t := ciphertext[len(ciphertext)-aead.tagSize:]
	t2 := tag(hmac.New(aead.macAlg, aead.macKey), data, s, aead.tagSize)
	if nonce == nil {
		nonce = s[:aead.NonceSize()]
	}

	if subtle.ConstantTimeCompare(t, t2) != 1 {
		return nil, errors.New("etm: message authentication failed")
	}

	b, _ := aead.encAlg(aead.encKey) // guaranteed to work

	c := aead.decrypter(b, nonce)
	o := make([]byte, len(s)-len(nonce))
	c.CryptBlocks(o, s[len(nonce):])

	return append(dst, aead.unpad(o, aead.blockSize)...), nil
}

type cipherParams struct {
	nonceSize, blockSize, padSize int

	encAlg    func(key []byte) (cipher.Block, error)
	encrypter func(cipher.Block, []byte) cipher.BlockMode
	decrypter func(cipher.Block, []byte) cipher.BlockMode
	pad       func([]byte, int) []byte
	unpad     func([]byte, int) []byte
}

// AES-CBC-PKCS7
var aesCBC = cipherParams{
	encAlg:    aes.NewCipher,
	blockSize: aes.BlockSize,
	nonceSize: aes.BlockSize,
	encrypter: cipher.NewCBCEncrypter,
	decrypter: cipher.NewCBCDecrypter,
	padSize:   aes.BlockSize,
	pad:       pkcs7pad,
	unpad:     pkcs7unpad,
}

func tag(h hash.Hash, data, s []byte, l int) []byte {
	al := make([]byte, dataLenSize)
	binary.BigEndian.PutUint64(al, uint64(len(data)*8)) // in bits
	h.Write(data)
	h.Write(s)
	h.Write(al)
	return h.Sum(nil)[:l]
}

func split(key []byte, encKeyLen, macKeyLen int) ([]byte, []byte) {
	return key[0:encKeyLen], key[len(key)-macKeyLen:]
}

func pkcs7pad(b []byte, blockSize int) []byte {
	ps := make([]byte, blockSize-(len(b)%blockSize))
	for i := range ps {
		ps[i] = byte(len(ps))
	}
	return append(b, ps...)
}

func pkcs7unpad(b []byte, blockSize int) []byte {
	return b[:len(b)-int(b[len(b)-1])]
}
<|endoftext|>


<commit_before>package jsonapivalidator

import "fmt"

// func validateMeta(meta interface{}, result *Result) {
// 	fmt.Println("validateMeta")
// }

func validateMetaObject(m interface{}, result *Result) {
	fmt.Println("validateMetaObject")
}
<commit_msg>Remove unused func<commit_after>package jsonapivalidator

import "fmt"

func validateMetaObject(m interface{}, result *Result) {
	fmt.Println("validateMetaObject")
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

// HTTPRequester defines the minimal interface needed for an http.Client to be implemented.
//
// Use it in conjunction with the SetHTTPClient function to allow for other capabilities
// like a tracing http.Client
type HTTPRequester interface {
	Do(*http.Request) (*http.Response, error)
}

var customHTTPClient HTTPRequester

// HTTPClient sets a custom http.Client
// deprecated: in favor of SetHTTPClient()
var HTTPClient = &http.Client{}

type WebResponse struct {
	Ok    bool      `json:"ok"`
	Error *WebError `json:"error"`
}

type WebError string

func (s WebError) Error() string {
	return string(s)
}

type RateLimitedError struct {
	RetryAfter uint64
}

func (e *RateLimitedError) Error() string {
	return fmt.Sprintf("Slack rate limit exceeded, retry after %d seconds", e.RetryAfter)
}

func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) {
	body := &bytes.Buffer{}
	wr := multipart.NewWriter(body)

	ioWriter, err := wr.CreateFormFile(fieldname, filename)
	if err != nil {
		wr.Close()
		return nil, err
	}
	_, err = io.Copy(ioWriter, r)
	if err != nil {
		wr.Close()
		return nil, err
	}
	// Close the multipart writer or the footer won't be written
	wr.Close()
	req, err := http.NewRequest("POST", path, body)
	req = req.WithContext(ctx)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", wr.FormDataContentType())
	req.URL.RawQuery = (values).Encode()
	return req, nil
}

func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error {
	response, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}

	// FIXME: will be api.Debugf
	if debug {
		logger.Printf("parseResponseBody: %s\n", string(response))
	}

	return json.Unmarshal(response, &intf)
}

func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
	fullpath, err := filepath.Abs(fpath)
	if err != nil {
		return err
	}
	file, err := os.Open(fullpath)
	if err != nil {
		return err
	}
	defer file.Close()
	return postWithMultipartResponse(ctx, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
}

func postWithMultipartResponse(ctx context.Context, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error {
	req, err := fileUploadReq(ctx, SLACK_API+path, fieldname, name, values, r)
	if err != nil {
		return err
	}
	req = req.WithContext(ctx)
	resp, err := getHTTPClient().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusTooManyRequests {
		retry, err := strconv.ParseUint(resp.Header.Get("Retry-After"), 10, 64)
		if err != nil {
			retry = 1
		}
		return &RateLimitedError{retry}
	}

	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
	if resp.StatusCode != 200 {
		logResponse(resp, debug)
		return fmt.Errorf("Slack server error: %s.", resp.Status)
	}

	return parseResponseBody(resp.Body, &intf, debug)
}

func postForm(ctx context.Context, endpoint string, values url.Values, intf interface{}, debug bool) error {
	reqBody := strings.NewReader(values.Encode())
	req, err := http.NewRequest("POST", endpoint, reqBody)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	req = req.WithContext(ctx)
	resp, err := getHTTPClient().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusTooManyRequests {
		retry, err := strconv.ParseUint(resp.Header.Get("Retry-After"), 10, 64)
		if err != nil {
			retry = 1
		}
		return &RateLimitedError{retry}
	}

	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
	if resp.StatusCode != 200 {
		logResponse(resp, debug)
		return fmt.Errorf("Slack server error: %s.", resp.Status)
	}

	return parseResponseBody(resp.Body, &intf, debug)
}

func post(ctx context.Context, path string, values url.Values, intf interface{}, debug bool) error {
	return postForm(ctx, SLACK_API+path, values, intf, debug)
}

func parseAdminResponse(ctx context.Context, method string, teamName string, values url.Values, intf interface{}, debug bool) error {
	endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix())
	return postForm(ctx, endpoint, values, intf, debug)
}

func logResponse(resp *http.Response, debug bool) error {
	if debug {
		text, err := httputil.DumpResponse(resp, true)
		if err != nil {
			return err
		}

		logger.Print(string(text))
	}

	return nil
}

func getHTTPClient() HTTPRequester {
	if customHTTPClient != nil {
		return customHTTPClient
	}

	return HTTPClient
}

// SetHTTPClient allows you to specify a custom http.Client
// Use this instead of the package level HTTPClient variable if you want to use a custom client like the
// Stackdriver Trace HTTPClient https://godoc.org/cloud.google.com/go/trace#HTTPClient
func SetHTTPClient(client HTTPRequester) {
	customHTTPClient = client
}
<commit_msg>Switch RateLimitError to Duration and add default.<commit_after>package slack

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

// HTTPRequester defines the minimal interface needed for an http.Client to be implemented.
//
// Use it in conjunction with the SetHTTPClient function to allow for other capabilities
// like a tracing http.Client
type HTTPRequester interface {
	Do(*http.Request) (*http.Response, error)
}

var customHTTPClient HTTPRequester

// Default duration for rate limiting if Retry-After header does not exist/parse
const defaultRetrySeconds int64 = 60

// HTTPClient sets a custom http.Client
// deprecated: in favor of SetHTTPClient()
var HTTPClient = &http.Client{}

type WebResponse struct {
	Ok    bool      `json:"ok"`
	Error *WebError `json:"error"`
}

type WebError string

func (s WebError) Error() string {
	return string(s)
}

type RateLimitedError struct {
	RetryAfter time.Duration
}

func (e *RateLimitedError) Error() string {
	return fmt.Sprintf("Slack rate limit exceeded, retry after %s", e.RetryAfter)
}

func fileUploadReq(ctx context.Context, path, fieldname, filename string, values url.Values, r io.Reader) (*http.Request, error) {
	body := &bytes.Buffer{}
	wr := multipart.NewWriter(body)

	ioWriter, err := wr.CreateFormFile(fieldname, filename)
	if err != nil {
		wr.Close()
		return nil, err
	}
	_, err = io.Copy(ioWriter, r)
	if err != nil {
		wr.Close()
		return nil, err
	}
	// Close the multipart writer or the footer won't be written
	wr.Close()
	req, err := http.NewRequest("POST", path, body)
	req = req.WithContext(ctx)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", wr.FormDataContentType())
	req.URL.RawQuery = (values).Encode()
	return req, nil
}

func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error {
	response, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}

	// FIXME: will be api.Debugf
	if debug {
		logger.Printf("parseResponseBody: %s\n", string(response))
	}

	return json.Unmarshal(response, &intf)
}

func postLocalWithMultipartResponse(ctx context.Context, path, fpath, fieldname string, values url.Values, intf interface{}, debug bool) error {
	fullpath, err := filepath.Abs(fpath)
	if err != nil {
		return err
	}
	file, err := os.Open(fullpath)
	if err != nil {
		return err
	}
	defer file.Close()
	return postWithMultipartResponse(ctx, path, filepath.Base(fpath), fieldname, values, file, intf, debug)
}

func postWithMultipartResponse(ctx context.Context, path, name, fieldname string, values url.Values, r io.Reader, intf interface{}, debug bool) error {
	req, err := fileUploadReq(ctx, SLACK_API+path, fieldname, name, values, r)
	if err != nil {
		return err
	}
	req = req.WithContext(ctx)
	resp, err := getHTTPClient().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusTooManyRequests {
		retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
		if err != nil {
			retry = defaultRetrySeconds
		}
		return &RateLimitedError{time.Duration(retry) * time.Second}
	}

	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
	if resp.StatusCode != 200 {
		logResponse(resp, debug)
		return fmt.Errorf("Slack server error: %s.", resp.Status)
	}

	return parseResponseBody(resp.Body, &intf, debug)
}

func postForm(ctx context.Context, endpoint string, values url.Values, intf interface{}, debug bool) error {
	reqBody := strings.NewReader(values.Encode())
	req, err := http.NewRequest("POST", endpoint, reqBody)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	req = req.WithContext(ctx)
	resp, err := getHTTPClient().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusTooManyRequests {
		retry, err := strconv.ParseInt(resp.Header.Get("Retry-After"), 10, 64)
		if err != nil {
			retry = defaultRetrySeconds
		}
		return &RateLimitedError{time.Duration(retry) * time.Second}
	}

	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
	if resp.StatusCode != 200 {
		logResponse(resp, debug)
		return fmt.Errorf("Slack server error: %s.", resp.Status)
	}

	return parseResponseBody(resp.Body, &intf, debug)
}

func post(ctx context.Context, path string, values url.Values, intf interface{}, debug bool) error {
	return postForm(ctx, SLACK_API+path, values, intf, debug)
}

func parseAdminResponse(ctx context.Context, method string, teamName string, values url.Values, intf interface{}, debug bool) error {
	endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix())
	return postForm(ctx, endpoint, values, intf, debug)
}

func logResponse(resp *http.Response, debug bool) error {
	if debug {
		text, err := httputil.DumpResponse(resp, true)
		if err != nil {
			return err
		}

		logger.Print(string(text))
	}

	return nil
}

func getHTTPClient() HTTPRequester {
	if customHTTPClient != nil {
		return customHTTPClient
	}

	return HTTPClient
}

// SetHTTPClient allows you to specify a custom http.Client
// Use this instead of the package level HTTPClient variable if you want to use a custom client like the
// Stackdriver Trace HTTPClient https://godoc.org/cloud.google.com/go/trace#HTTPClient
func SetHTTPClient(client HTTPRequester) {
	customHTTPClient = client
}
<|endoftext|>


<commit_before>/*
 *   Copyright 2016 Rémy MATHIEU
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package content

import (
	"fmt"

	"github.com/PuerkitoBio/goquery"
)

type RedditContent struct {
	Url string
}

func (reddit RedditContent) callAPI() ([]Content, error) {
	doc, err := goquery.NewDocument(reddit.Url)
	if err != nil {
		fmt.Println("Error while calling API")
		return nil, err
	}

	rv := make([]Content, 0)

	doc.Find(".link").Each(func(i int, selec *goquery.Selection) {
		// ignore sticky posts
		if selec.HasClass("stickied") {
			return
		}

		if len(rv) > 20 {
			return
		}

		l := selec.Find("p.title a.title")
		title := l.First().Text()
		externalLink, _ := l.Attr("href")

		if strings.HasPrefix(externalLink, "/r/") {
			externalLink = "https://reddit.com" + externalLink
		}

		rv = append(rv, Content{
			Text: title,
			Url:  externalLink,
		})
	})

	return rv, nil
}
<commit_msg>Default to ignore reddit self posts.<commit_after>/*
 *   Copyright 2016 Rémy MATHIEU
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package content

import (
	"fmt"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

type RedditContent struct {
	Url string
}

const (
	// set it to false at compilation to not ignore self posts
	REDDIT_IGNORE_SELF_POST = true
)

func (reddit RedditContent) callAPI() ([]Content, error) {
	doc, err := goquery.NewDocument(reddit.Url)
	if err != nil {
		fmt.Println("Error while calling API")
		return nil, err
	}

	rv := make([]Content, 0)

	doc.Find(".link").Each(func(i int, selec *goquery.Selection) {
		// ignore sticky posts
		if selec.HasClass("stickied") {
			return
		}

		if len(rv) > 20 {
			return
		}

		l := selec.Find("p.title a.title")
		title := l.First().Text()
		externalLink, _ := l.Attr("href")

		if strings.HasPrefix(externalLink, "/r/") {
			// ignore self post
			if REDDIT_IGNORE_SELF_POST {
				return
			}
			// link to the self post
			externalLink = "https://reddit.com" + externalLink
		}

		rv = append(rv, Content{
			Text: title,
			Url:  externalLink,
		})
	})

	return rv, nil
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/antonmedv/expr/file"
	"github.com/emicklei/melrose"
	"github.com/emicklei/melrose/dsl"
)

// LanguageServer can execute DSL statements received over HTTP
type LanguageServer struct {
	store     dsl.VariableStorage
	address   string
	evaluator *dsl.Evaluator
}

// NewLanguageServer returns a new LanguageService. It is not started.
func NewLanguageServer(store dsl.VariableStorage, loopControl melrose.LoopController, addr string) *LanguageServer {
	return &LanguageServer{store: store, address: addr, evaluator: dsl.NewEvaluator(store, loopControl)}
}

// Start will start a HTTP listener for serving DSL statements
// curl -v -d 'n = note("C")' http://localhost:8118/v1/statements
func (l *LanguageServer) Start() error {
	http.HandleFunc("/v1/statements", l.statementHandler)
	return http.ListenAndServe(l.address, nil)
}

func (l *LanguageServer) statementHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	r.ParseForm()
	trace := r.FormValue("trace") == "true"
	if trace {
		log.Printf("[melrose.trace] %s\n", r.URL.String())
	}
	// get line
	line := 1
	lineString := r.FormValue("line")
	if len(lineString) > 0 {
		if i, err := strconv.Atoi(lineString); err == nil {
			line = i
		}
	}
	// get expression
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	defer r.Body.Close()
	returnValue, err := l.evaluator.EvaluateProgram(string(data))
	var response interface{}
	if err != nil {
		// evaluation failed.
		w.WriteHeader(http.StatusInternalServerError)
		response = resultFrom(line, err)
	} else {
		// evaluation was ok.

		// check if play was requested and is playable
		if r.FormValue("action") == "play" {
			if s, ok := returnValue.(melrose.Sequenceable); ok {
				melrose.Context().AudioDevice.Play(
					s,
					melrose.Context().LoopControl.BPM(),
					time.Now())
			}
			if lp, ok := returnValue.(*melrose.Loop); ok {
				melrose.Context().LoopControl.Begin(lp)
			}
		}
		// loop operation
		if r.FormValue("action") == "begin" {
			if lp, ok := returnValue.(*melrose.Loop); ok {
				if !lp.IsRunning() {
					melrose.Context().LoopControl.Begin(lp)
				}
			}
			// ignore if not Loop
		}
		// loop operation
		if r.FormValue("action") == "end" {
			if lp, ok := returnValue.(*melrose.Loop); ok {
				if lp.IsRunning() {
					lp.Stop()
				}
			}
			// ignore if not Loop
		}
		if r.FormValue("action") == "kill" {
			// kill the play and any loop
			melrose.Context().LoopControl.Reset()
			melrose.Context().AudioDevice.Reset()
		}
		response = resultFrom(line, returnValue)
	}
	w.Header().Set("content-type", "application/json")
	enc := json.NewEncoder(w)
	enc.SetIndent("", "\t")
	err = enc.Encode(response)
	if err != nil {
		log.Printf("[melrose.error] %#v\n", err)
	}
	if trace {
		// doit again
		buf := bytes.Buffer{}
		enc := json.NewEncoder(&buf)
		enc.SetIndent("", "\t")
		err = enc.Encode(response)
		log.Printf("[melrose.trace] %#v, error:%v\n", buf.String(), err)
	}
}

type evaluationResult struct {
	Type     string      `json:"type"`
	IsError  bool        `json:"is-error"`
	Message  string      `json:"message"`
	Filename string      `json:"file"`
	Line     int         `json:"line"`
	Column   int         `json:"column"`
	Object   interface{} `json:"object"`
}

func resultFrom(line int, val interface{}) evaluationResult {
	t := fmt.Sprintf("%T", val)
	if err, ok := val.(error); ok {
		// patch Location of error
		if fe, ok := err.(*file.Error); ok {
			fe.Location.Line = fe.Location.Line - 1 + line
		}
		return evaluationResult{
			Type:     t,
			IsError:  true,
			Filename: "yours.mel",
			Message:  err.Error(),
			Line:     line,
			Object:   val,
		}
	}
	var msg string
	if stor, ok := val.(melrose.Storable); ok {
		msg = stor.Storex()
	}
	// no Object if ok
	return evaluationResult{Type: t, IsError: false, Message: msg}
}
<commit_msg>use query io from<commit_after>package server

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/antonmedv/expr/file"
	"github.com/emicklei/melrose"
	"github.com/emicklei/melrose/dsl"
)

// LanguageServer can execute DSL statements received over HTTP
type LanguageServer struct {
	store     dsl.VariableStorage
	address   string
	evaluator *dsl.Evaluator
}

// NewLanguageServer returns a new LanguageService. It is not started.
func NewLanguageServer(store dsl.VariableStorage, loopControl melrose.LoopController, addr string) *LanguageServer {
	return &LanguageServer{store: store, address: addr, evaluator: dsl.NewEvaluator(store, loopControl)}
}

// Start will start a HTTP listener for serving DSL statements
// curl -v -d 'n = note("C")' http://localhost:8118/v1/statements
func (l *LanguageServer) Start() error {
	http.HandleFunc("/v1/statements", l.statementHandler)
	return http.ListenAndServe(l.address, nil)
}

func (l *LanguageServer) statementHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	query := r.URL.Query()
	trace := query.Get("trace") == "true"
	if trace {
		log.Printf("[melrose.trace] %s\n", r.URL.String())
	}
	// get line
	line := 1
	lineString := query.Get("line")
	if len(lineString) > 0 {
		if i, err := strconv.Atoi(lineString); err == nil {
			line = i
		}
	}
	// get expression
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	defer r.Body.Close()
	returnValue, err := l.evaluator.EvaluateProgram(string(data))
	var response interface{}
	if err != nil {
		// evaluation failed.
		w.WriteHeader(http.StatusInternalServerError)
		response = resultFrom(line, err)
	} else {
		// evaluation was ok.

		// check if play was requested and is playable
		if query.Get("action") == "play" {
			if s, ok := returnValue.(melrose.Sequenceable); ok {
				melrose.Context().AudioDevice.Play(
					s,
					melrose.Context().LoopControl.BPM(),
					time.Now())
			}
			if lp, ok := returnValue.(*melrose.Loop); ok {
				melrose.Context().LoopControl.Begin(lp)
			}
		}
		// loop operation
		if query.Get("action") == "begin" {
			if lp, ok := returnValue.(*melrose.Loop); ok {
				if !lp.IsRunning() {
					melrose.Context().LoopControl.Begin(lp)
				}
			}
			// ignore if not Loop
		}
		// loop operation
		if query.Get("action") == "end" {
			if lp, ok := returnValue.(*melrose.Loop); ok {
				if lp.IsRunning() {
					lp.Stop()
				}
			}
			// ignore if not Loop
		}
		if query.Get("action") == "kill" {
			// kill the play and any loop
			melrose.Context().LoopControl.Reset()
			melrose.Context().AudioDevice.Reset()
		}
		response = resultFrom(line, returnValue)
	}
	w.Header().Set("content-type", "application/json")
	enc := json.NewEncoder(w)
	enc.SetIndent("", "\t")
	err = enc.Encode(response)
	if err != nil {
		log.Printf("[melrose.error] %#v\n", err)
	}
	if trace {
		// doit again
		buf := bytes.Buffer{}
		enc := json.NewEncoder(&buf)
		enc.SetIndent("", "\t")
		err = enc.Encode(response)
		log.Printf("[melrose.trace] %#v, error:%v\n", buf.String(), err)
	}
}

type evaluationResult struct {
	Type     string      `json:"type"`
	IsError  bool        `json:"is-error"`
	Message  string      `json:"message"`
	Filename string      `json:"file"`
	Line     int         `json:"line"`
	Column   int         `json:"column"`
	Object   interface{} `json:"object"`
}

func resultFrom(line int, val interface{}) evaluationResult {
	t := fmt.Sprintf("%T", val)
	if err, ok := val.(error); ok {
		// patch Location of error
		if fe, ok := err.(*file.Error); ok {
			fe.Location.Line = fe.Location.Line - 1 + line
		}
		return evaluationResult{
			Type:     t,
			IsError:  true,
			Filename: "yours.mel",
			Message:  err.Error(),
			Line:     line,
			Object:   val,
		}
	}
	// no error
	var msg string
	if stor, ok := val.(melrose.Storable); ok {
		msg = stor.Storex()
	}
	// no Object if ok
	return evaluationResult{Type: t, IsError: false, Message: msg}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
)

// iLevil FLAG, CompanyID, PackageID and Version
const (
	FLAG     = 0x7e  // iLevil FLAG symbol
	CID      = "LE"  // iLevil CompanyID
	PIDAHRS  = 0x01  // AHRS PackageID
	PIDAHRS1 = 0x01  // AHRS PackageID Version 1
	INTERR   = 32767 // Error value for int16 types
	UINTERR  = 65535 // Error value for uint16 types
)

var ahrsErrorStr = "AHRS Message Error: %s"

// AHRSMsg contains the data from an iLevil AHRS version 1 message
type AHRSMsg struct {
	companyID        string
	packageID        byte
	packageIDVersion byte
	roll             int16
	pitch            int16
	yaw              int16
	inclination      int16
	turnCoord        int16
	gLoad            int16
	kias             int16
	pAlt             uint16
	vertSpeed        int16
}

func (dat *AHRSMsg) decodeAHRSMsg(msg []byte) (err error) {
	if len(msg) != 28 {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Message length was %d, should be 28", len(msg)))
		return
	}
	if msg[0] != FLAG {
		err = fmt.Errorf(ahrsErrorStr, "Missing FLAG 0x7E at message beginning")
		return
	}
	if msg[27] != FLAG {
		err = fmt.Errorf(ahrsErrorStr, "Missing FLAG 0x7E at message end")
		return
	}
	if s := string(msg[1:3]); s != CID {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Incorrect CompanyID: %s", string(msg[1:3])))
		return
	} else {
		dat.companyID = s
	}
	if s := msg[3]; s != PIDAHRS {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Expecting ProductID 0x01, received %b", msg[3]))
		return
	} else {
		dat.packageID = s
	}
	if s := msg[4]; s != PIDAHRS1 {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Expecting ProductID Version 0x01, received %b", msg[4]))
		return
	} else {
		dat.packageIDVersion = s
	}
	if !crcCheck(msg) {
		err = fmt.Errorf(ahrsErrorStr, "CRC incorrect")
		return
	}

	dat.roll = bytes2int(msg[5:7])
	dat.pitch = bytes2int(msg[7:9])
	dat.yaw = bytes2int(msg[9:11])
	dat.inclination = bytes2int(msg[11:13])
	dat.turnCoord = bytes2int(msg[13:15])
	dat.gLoad = bytes2int(msg[15:17])
	dat.kias = bytes2int(msg[17:19])
	dat.pAlt = bytes2uint(msg[19:21])
	dat.vertSpeed = bytes2int(msg[21:23])

	return
}

func (dat *AHRSMsg) Roll() (roll float64, err error) {
	if dat.roll == INTERR {
		err = errors.New("Bad Roll value")
	} else {
		roll = float64(dat.roll) / 10
	}
	return
}

func (dat *AHRSMsg) Pitch() (pitch float64, err error) {
	if dat.pitch == INTERR {
		err = errors.New("Bad Pitch value")
	} else {
		pitch = float64(dat.pitch) / 10
	}
	return
}

func (dat *AHRSMsg) Yaw() (yaw float64, err error) {
	if dat.yaw == INTERR {
		err = errors.New("Bad Yaw value")
	} else {
		yaw = float64(dat.yaw) / 10
	}
	return
}

func (dat *AHRSMsg) Inclination() (inclination float64, err error) {
	if dat.inclination == INTERR {
		err = errors.New("Bad Inclination value")
	} else {
		inclination = float64(dat.inclination) / 10
	}
	return
}

func (dat *AHRSMsg) TurnCoord() (turnCoord float64, err error) {
	if dat.turnCoord == INTERR {
		err = errors.New("Bad TurnCoord value")
	} else {
		turnCoord = float64(dat.turnCoord) / 10
	}
	return
}

func (dat *AHRSMsg) GLoad() (gLoad float64, err error) {
	if dat.gLoad == INTERR {
		err = errors.New("Bad GLoad value")
	} else {
		gLoad = float64(dat.gLoad) / 10
	}
	return
}

func (dat *AHRSMsg) KIAS() (kias float64, err error) {
	if dat.kias == INTERR {
		err = errors.New("Bad KIAS value")
	} else {
		kias = float64(dat.kias) / 10
	}
	return
}

func (dat *AHRSMsg) PAlt() (pAlt float64, err error) {
	if dat.pAlt == UINTERR {
		err = errors.New("Bad PAlt value")
	} else {
		pAlt = float64(dat.pAlt) - 5000
	}
	return
}

func (dat *AHRSMsg) VertSpeed() (vertSpeed float64, err error) {
	if dat.vertSpeed == INTERR {
		err = errors.New("Bad VertSpeed value")
	} else {
		vertSpeed = float64(dat.vertSpeed)
	}
	return
}

func bytes2int(b []byte) int16 {
	return (int16(b[1]) << 0) | (int16(b[0]) << 8)
}

func bytes2uint(b []byte) uint16 {
	return (uint16(b[1]) << 0) | (uint16(b[0]) << 8)
}

func crcCheck(msg []byte) bool {
	return true //TODO westphae: implement this
}

func main() {
	var (
		ipAddress   string
		n           int
		err         error
		roll        float64
		pitch       float64
		yaw         float64
		inclination float64
		turnCoord   float64
		gLoad       float64
		kias        float64
		pAlt        float64
		vertSpeed   float64
	)

	if len(flag.Args()) == 0 {
		ipAddress = ""
	} else {
		ipAddress = flag.Args()[0]
	}

	conn, err := net.ListenPacket("udp", ipAddress+":4000")
	if err != nil {
		log.Fatalf("Couldn't dial UDP: %v\n", err)
	}
	defer conn.Close()

	ahrsMsg := new(AHRSMsg)
	buffer := make([]byte, 1024)
	for {
		n, _, err = conn.ReadFrom(buffer)
		if err != nil {
			log.Printf("Error: %v\n", err)
		} else {
			// log.Printf("Received %s (len %d) from %s\n", string(buffer[0:n]), n, addr)
			err = ahrsMsg.decodeAHRSMsg(buffer[0:n])
			if err != nil {
				continue
				// log.Println(err)
			} else {
				roll, err = ahrsMsg.Roll()
				if err == nil {
					log.Printf("%12s %+3.1f", "Roll", roll)
				}
				pitch, err = ahrsMsg.Pitch()
				if err == nil {
					log.Printf("%12s  %+2.1f", "Pitch", pitch)
				}
				yaw, err = ahrsMsg.Yaw()
				if err == nil {
					log.Printf("%12s %+3.1f", "Yaw", yaw)
				}
				inclination, err = ahrsMsg.Inclination()
				if err == nil {
					log.Printf("%12s  %+2.1f", "Inclination", inclination)
				}
				turnCoord, err = ahrsMsg.TurnCoord()
				if err == nil {
					log.Printf("%12s %+3.1f", "TurnCoord", turnCoord)
				}
				gLoad, err = ahrsMsg.GLoad()
				if err == nil {
					log.Printf("%12s   %+1.1f", "GLoad", gLoad)
				}
				kias, err = ahrsMsg.KIAS()
				if err == nil {
					log.Printf("%12s %+4.1f", "KIAS", kias)
				}
				pAlt, err = ahrsMsg.PAlt()
				if err == nil {
					log.Printf("%12s %+5.0f", "PAlt", pAlt)
				}
				vertSpeed, err = ahrsMsg.VertSpeed()
				if err == nil {
					log.Printf("%12s %+5.0f", "VertSpeed", vertSpeed)
				}
				log.Println()
			}
		}
	}
}
<commit_msg>Better messages and documentation for gdl90listener<commit_after>/* Parse and monitor the gdl90 stream.
   stratux only sends the gdl90 stream to wifi-connected devices
   (i.e. those with a dhcp lease on the 192.168.10.x network).

   To use it, connect to stratux over wifi, determine the wifi stratux
   ip address, e.g. 192.168.10.24, and then run
   `gdl90listener 192.168.10.24`.

   Stratux stops sending gdl90 messages after the message buffer overflows.
   If you don't receive any messages, `systemctl restart stratux.service`
   after gdl90listener is running.
*/
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"net"
)

// iLevil FLAG, CompanyID, PackageID and Version
const (
	FLAG     = 0x7e  // iLevil FLAG symbol
	CID      = "LE"  // iLevil CompanyID
	PIDAHRS  = 0x01  // AHRS PackageID
	PIDAHRS1 = 0x01  // AHRS PackageID Version 1
	INTERR   = 32767 // Error value for int16 types
	UINTERR  = 65535 // Error value for uint16 types
)

var ahrsErrorStr = "AHRS Message Error: %s"

// AHRSMsg contains the data from an iLevil AHRS version 1 message
type AHRSMsg struct {
	companyID        string
	packageID        byte
	packageIDVersion byte
	roll             int16
	pitch            int16
	yaw              int16
	inclination      int16
	turnCoord        int16
	gLoad            int16
	kias             int16
	pAlt             uint16
	vertSpeed        int16
}

func (dat *AHRSMsg) decodeAHRSMsg(msg []byte) (err error) {
	if len(msg) != 28 {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Message length was %d, should be 28", len(msg)))
		return
	}
	if msg[0] != FLAG {
		err = fmt.Errorf(ahrsErrorStr, "Missing FLAG 0x7E at message beginning")
		return
	}
	if msg[27] != FLAG {
		err = fmt.Errorf(ahrsErrorStr, "Missing FLAG 0x7E at message end")
		return
	}
	if s := string(msg[1:3]); s != CID {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Incorrect CompanyID: %s", string(msg[1:3])))
		return
	} else {
		dat.companyID = s
	}
	if s := msg[3]; s != PIDAHRS {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Expecting ProductID 0x01, received %b", msg[3]))
		return
	} else {
		dat.packageID = s
	}
	if s := msg[4]; s != PIDAHRS1 {
		err = fmt.Errorf(ahrsErrorStr, fmt.Sprintf("Expecting ProductID Version 0x01, received %b", msg[4]))
		return
	} else {
		dat.packageIDVersion = s
	}
	if !crcCheck(msg) {
		err = fmt.Errorf(ahrsErrorStr, "CRC incorrect")
		return
	}

	dat.roll = bytes2int(msg[5:7])
	dat.pitch = bytes2int(msg[7:9])
	dat.yaw = bytes2int(msg[9:11])
	dat.inclination = bytes2int(msg[11:13])
	dat.turnCoord = bytes2int(msg[13:15])
	dat.gLoad = bytes2int(msg[15:17])
	dat.kias = bytes2int(msg[17:19])
	dat.pAlt = bytes2uint(msg[19:21])
	dat.vertSpeed = bytes2int(msg[21:23])

	return
}

func (dat *AHRSMsg) Roll() (roll float64, err error) {
	if dat.roll == INTERR {
		err = errors.New("Bad Roll value")
	} else {
		roll = float64(dat.roll) / 10
	}
	return
}

func (dat *AHRSMsg) Pitch() (pitch float64, err error) {
	if dat.pitch == INTERR {
		err = errors.New("Bad Pitch value")
	} else {
		pitch = float64(dat.pitch) / 10
	}
	return
}

func (dat *AHRSMsg) Yaw() (yaw float64, err error) {
	if dat.yaw == INTERR {
		err = errors.New("Bad Yaw value")
	} else {
		yaw = float64(dat.yaw) / 10
	}
	return
}

func (dat *AHRSMsg) Inclination() (inclination float64, err error) {
	if dat.inclination == INTERR {
		err = errors.New("Bad Inclination value")
	} else {
		inclination = float64(dat.inclination) / 10
	}
	return
}

func (dat *AHRSMsg) TurnCoord() (turnCoord float64, err error) {
	if dat.turnCoord == INTERR {
		err = errors.New("Bad TurnCoord value")
	} else {
		turnCoord = float64(dat.turnCoord) / 10
	}
	return
}

func (dat *AHRSMsg) GLoad() (gLoad float64, err error) {
	if dat.gLoad == INTERR {
		err = errors.New("Bad GLoad value")
	} else {
		gLoad = float64(dat.gLoad) / 10
	}
	return
}

func (dat *AHRSMsg) KIAS() (kias float64, err error) {
	if dat.kias == INTERR {
		err = errors.New("Bad KIAS value")
	} else {
		kias = float64(dat.kias) / 10
	}
	return
}

func (dat *AHRSMsg) PAlt() (pAlt float64, err error) {
	if dat.pAlt == UINTERR {
		err = errors.New("Bad PAlt value")
	} else {
		pAlt = float64(dat.pAlt) - 5000
	}
	return
}

func (dat *AHRSMsg) VertSpeed() (vertSpeed float64, err error) {
	if dat.vertSpeed == INTERR {
		err = errors.New("Bad VertSpeed value")
	} else {
		vertSpeed = float64(dat.vertSpeed)
	}
	return
}

func bytes2int(b []byte) int16 {
	return (int16(b[1]) << 0) | (int16(b[0]) << 8)
}

func bytes2uint(b []byte) uint16 {
	return (uint16(b[1]) << 0) | (uint16(b[0]) << 8)
}

func crcCheck(msg []byte) bool {
	return true //TODO westphae: implement this
}

func main() {
	var (
		ipAddress   string
		n           int
		err         error
		roll        float64
		pitch       float64
		yaw         float64
		inclination float64
		turnCoord   float64
		gLoad       float64
		kias        float64
		pAlt        float64
		vertSpeed   float64
	)

	flag.Parse()
	if len(flag.Args()) == 0 {
		ipAddress = ":4000"
	} else {
		ipAddress = flag.Args()[0] + ":4000"
	}

	conn, err := net.ListenPacket("udp", ipAddress)
	if err != nil {
		log.Fatalf("Couldn't dial UDP at %v: %v\n", ipAddress, err)
	}
	defer conn.Close()
	log.Printf("Dialed UDP: %v\n", ipAddress)

	ahrsMsg := new(AHRSMsg)
	buffer := make([]byte, 1024)
	for {
		n, _, err = conn.ReadFrom(buffer)
		if err != nil {
			log.Printf("Error: %v\n", err)
		} else {
			// log.Printf("Received %s (len %d) from %s\n", string(buffer[0:n]), n, addr)
			err = ahrsMsg.decodeAHRSMsg(buffer[0:n])
			if err != nil {
				continue
				// log.Println(err)
			} else {
				roll, err = ahrsMsg.Roll()
				if err == nil {
					log.Printf("%12s %+3.1f", "Roll", roll)
				}
				pitch, err = ahrsMsg.Pitch()
				if err == nil {
					log.Printf("%12s  %+2.1f", "Pitch", pitch)
				}
				yaw, err = ahrsMsg.Yaw()
				if err == nil {
					log.Printf("%12s %+3.1f", "Yaw", yaw)
				}
				inclination, err = ahrsMsg.Inclination()
				if err == nil {
					log.Printf("%12s  %+2.1f", "Inclination", inclination)
				}
				turnCoord, err = ahrsMsg.TurnCoord()
				if err == nil {
					log.Printf("%12s %+3.1f", "TurnCoord", turnCoord)
				}
				gLoad, err = ahrsMsg.GLoad()
				if err == nil {
					log.Printf("%12s   %+1.1f", "GLoad", gLoad)
				}
				kias, err = ahrsMsg.KIAS()
				if err == nil {
					log.Printf("%12s %+4.1f", "KIAS", kias)
				}
				pAlt, err = ahrsMsg.PAlt()
				if err == nil {
					log.Printf("%12s %+5.0f", "PAlt", pAlt)
				}
				vertSpeed, err = ahrsMsg.VertSpeed()
				if err == nil {
					log.Printf("%12s %+5.0f", "VertSpeed", vertSpeed)
				}
				log.Println()
			}
		}
	}
}
<|endoftext|>


<commit_before>package misc

import "fmt"
import "strings"
import "bufio"
import "os"
import "log"
import "code.google.com/p/go.crypto/ssh/terminal"

// ColesceStrings returns the first non-empty string value
// or it returns an empty string if none qualify
func CoalesceStrings(strs ...string) string {
    for _, s := range strs {
        if len(s) > 0 {
           return s
        }
    }
    return ""
}

// Prompt prompts user for input and returns their response
func Prompt(prompt string) string {
    fmt.Print(prompt)
    inReader := bufio.NewReader(os.Stdin)
    text, _ := inReader.ReadString('\n')
    return text
}

// PromptYesNo returns true if user answered something that starts with "y".
// PromptYesNo returns false if user answers something that starts with "n".
// PromptYesNo returns yesIsDefault if no answer given.
func PromptYesNo(prompt string, yesIsDefault bool) bool {
    for {
        if yesIsDefault {
            fmt.Print(prompt + " [Yn]? ")
        } else {
            fmt.Print(prompt + " [yN]? ")
        }

        // Read input
        inReader := bufio.NewReader(os.Stdin)
        text, err := inReader.ReadString('\n')
		check(err, "reading stdin")

        // Trim leading and trailing spaces, and convert to lower case
        text = strings.TrimSpace(strings.ToLower(text))
        if len(text) == 0 {
            return yesIsDefault
        } else if strings.HasPrefix(text, "y") {
            return true
        } else if strings.HasPrefix(text, "n") {
            return false
        }
        fmt.Println("Invalid value.  Must be 'y' or 'n'.  Try again.")
    }
}

// ChooseOne prompts the user to enter one of n values
// Example: answer := misc.PromptOne("Continue, Quit, or Redo? [cqr]: ", ["c", "q", "r"])
func ChooseOne(prompt string, values ...string) string {
    for {
        fmt.Print(prompt)

        // Read input
        inReader := bufio.NewReader(os.Stdin)
        text, _ := inReader.ReadString('\n')

        // Trim leading and trailing spaces, and convert to lower case
        text = strings.TrimSpace(text)
        if len(text) == 0 {
            return values[0]
        }

        for _, v := range values {
            if text == v {
                return v
            }
        }

        fmt.Println("Invalid entry, try again.")
    }
}

// PromptPassword prompts user for a password that is never echoed back to the screen.
func PromptPassword(prompt string) string {
    stdin := 1
    fmt.Fprintf(os.Stdin, prompt)

    // Get current state of terminal
//    s, err := terminal.MakeRaw(stdin)
//    check(err, "making raw terminal, Saving old terminal state")
//    defer terminal.Restore(stdin, s)

    // Read password from stdin
    b, err := terminal.ReadPassword(stdin)
    check(err, "reading from terminal")

    return string(b)
}

func check(err error, action string) {
    if err != nil {
        log.Fatalf("Error %s: %v\n", action, err)
    }
}
<commit_msg>added ContainsString and CompareString functions<commit_after>package misc

import "fmt"
import "strings"
import "bufio"
import "os"
import "log"
import "code.google.com/p/go.crypto/ssh/terminal"

// ColesceStrings returns the first non-empty string value
// or it returns an empty string if none qualify
func CoalesceStrings(strs ...string) string {
    for _, s := range strs {
        if len(s) > 0 {
           return s
        }
    }
    return ""
}

// Prompt prompts user for input and returns their response
func Prompt(prompt string) string {
    fmt.Print(prompt)
    inReader := bufio.NewReader(os.Stdin)
    text, _ := inReader.ReadString('\n')
    return text
}

// PromptYesNo returns true if user answered something that starts with "y".
// PromptYesNo returns false if user answers something that starts with "n".
// PromptYesNo returns yesIsDefault if no answer given.
func PromptYesNo(prompt string, yesIsDefault bool) bool {
    for {
        if yesIsDefault {
            fmt.Print(prompt + " [Yn]? ")
        } else {
            fmt.Print(prompt + " [yN]? ")
        }

        // Read input
        inReader := bufio.NewReader(os.Stdin)
        text, err := inReader.ReadString('\n')
		check(err, "reading stdin")

        // Trim leading and trailing spaces, and convert to lower case
        text = strings.TrimSpace(strings.ToLower(text))
        if len(text) == 0 {
            return yesIsDefault
        } else if strings.HasPrefix(text, "y") {
            return true
        } else if strings.HasPrefix(text, "n") {
            return false
        }
        fmt.Println("Invalid value.  Must be 'y' or 'n'.  Try again.")
    }
}

// ChooseOne prompts the user to enter one of n values
// Example: answer := misc.PromptOne("Continue, Quit, or Redo? [cqr]: ", ["c", "q", "r"])
func ChooseOne(prompt string, values ...string) string {
    for {
        fmt.Print(prompt)

        // Read input
        inReader := bufio.NewReader(os.Stdin)
        text, _ := inReader.ReadString('\n')

        // Trim leading and trailing spaces, and convert to lower case
        text = strings.TrimSpace(text)
        if len(text) == 0 {
            return values[0]
        }

        for _, v := range values {
            if text == v {
                return v
            }
        }

        fmt.Println("Invalid entry, try again.")
    }
}

// PromptPassword prompts user for a password that is never echoed back to the screen.
func PromptPassword(prompt string) string {
    stdin := 1
    fmt.Fprintf(os.Stdin, prompt)

    // Get current state of terminal
//    s, err := terminal.MakeRaw(stdin)
//    check(err, "making raw terminal, Saving old terminal state")
//    defer terminal.Restore(stdin, s)

    // Read password from stdin
    b, err := terminal.ReadPassword(stdin)
    check(err, "reading from terminal")

    return string(b)
}

// ContainsString returns true if the given slice contains the given string
func ContainsString(slice []string, value string) bool {
    for _, e := range slice {
        if value == e { return true }
    }
    return false
}

// CompareString returns 0 if the strings match, -1 if the first string is less than the second string, and +1 if greater
func CompareString(s1, s2 string) int {
    if s1 == s2 { return 0
    } else if s1 < s2 { return -1 }
    return 1
}

// =================
// Private functions
// =================
func check(err error, action string) {
    if err != nil {
        log.Fatalf("Error %s: %v\n", action, err)
    }
}
<|endoftext|>