<commit_before>// Copyright © 2016 Abcum Ltd
//
// 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 gcs

import (
	"context"
	"errors"
	"io"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/abcum/bump"

	"cloud.google.com/go/storage"
)

var (
	// ErrSeekPositionNotSupported occurs when a syncable type receives a
	// seek destination which it can not fulfil to. These syncable types
	// usually only support seeking to the beginning or to the end, as they
	// write to and read from multiple data files.
	ErrSeekPositionNotSupported = errors.New("Seek position is not supported.")
)

// Options represents syncing configuration options.
type Options struct {
	// MinSize is used to determine the number of megabytes to write to
	// a file before the file should be rotated, and a new file used. This
	// is only available on certain types which support rotation of files,
	// but do not support appendable writes. The file is overwritten on
	// each write using the buffered data. MinSize should be specified in
	// megabytes.
	MinSize int
}

// Storage represents a Amazon S3 reader and writer.
type Storage struct {
	sync.Mutex
	opts *Options
	lock sync.Mutex
	size struct {
		min int
		now int
	}
	serv struct {
		srv *storage.Client
		ctx context.Context
		bck *storage.BucketHandle
	}
	buff struct {
		bit []byte
		wtr *bump.Writer
		rdr io.ReadCloser
	}
	file struct {
		buck string
		last string
		full string
		path string
		base string
		name string
		extn string
		main string
		poss []string
	}
}

// New creates a new Syncable storage instance for reading and writing.
func New(name string, opts *Options) (s *Storage, e error) {

	p := strings.Index(name, "/")

	s = &Storage{}

	s.opts = opts
	s.file.buck = name[:p]
	s.file.full = name[p+1:]
	s.file.path = path.Dir(s.file.full)
	s.file.base = path.Base(s.file.full)
	s.file.extn = path.Ext(s.file.full)
	s.file.name = s.file.base[:len(s.file.base)-len(s.file.extn)]
	s.file.main = path.Join(s.file.path, s.file.base)

	s.serv.ctx = context.Background()

	s.buff.wtr = bump.NewWriterBytes(&s.buff.bit)

	s.size.min = opts.MinSize * 1024 * 1024

	s.serv.srv, e = storage.NewClient(s.serv.ctx)

	s.serv.bck = s.serv.srv.Bucket(s.file.buck)

	return

}

// Close closes the underlying Syncable storage data stream, and prevents
// any further reads of writes to the Storage instance.
func (s *Storage) Close() error {

	s.lock.Lock()
	defer s.lock.Unlock()

	defer func() {
		s.opts = nil
		s.buff.bit = nil
		s.buff.wtr = nil
		s.buff.rdr = nil
		s.file.last = ""
		s.file.full = ""
		s.file.path = ""
		s.file.base = ""
		s.file.name = ""
		s.file.extn = ""
		s.file.main = ""
		s.file.poss = nil
	}()

	s.serv.srv.Close()

	return s.stop()

}

// Read reads up to len(p) bytes into p, reading from the Storage data
// stream, and automatically rotating files when the end of a file is
// reached. It returns the number of bytes read (0 <= n <= len(p)) and
// any error encountered. If the Storage has reached the final log file,
// then an EOF error will be returned.
func (s *Storage) Read(p []byte) (int, error) {

	s.lock.Lock()
	defer s.lock.Unlock()

	// No data file is open yet so open one.
	if s.buff.rdr == nil {
		if err := s.head(); err != nil {
			return 0, err
		}
	}

	n, err := s.buff.rdr.Read(p)
	if err == io.EOF {
		err = s.next()
	}

	return n, err

}

// Write writes len(p) bytes from p to the underlying Storage data
// stream. It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early. Write
// will always append data to the end of the Storage data stream, ensuring
// data is append-only and never overwritten.
func (s *Storage) Write(p []byte) (int, error) {

	s.lock.Lock()
	defer s.lock.Unlock()

	if s.size.min < s.size.now+len(p) {
		if err := s.sync(); err != nil {
			return 0, err
		}
	}

	s.size.now += len(p)

	return len(p), s.buff.wtr.WriteBytes(p)

}

// Seek sets the offset for the next Read or Write to offset, interpreted
// according to whence: SeekStart means relative to the start of the file,
// SeekCurrent means relative to the current offset, and SeekE8nd means
// relative to the end. Seek returns the new offset relative to the start
// of the file and an error, if any. In some storage types, Seek only
// supports seeking to the beginning and end of the data stream.
func (s *Storage) Seek(offset int64, whence int) (int64, error) {

	s.lock.Lock()
	defer s.lock.Unlock()

	if offset != 0 {
		return 0, ErrSeekPositionNotSupported
	}

	if whence != 0 && whence != 2 {
		return 0, ErrSeekPositionNotSupported
	}

	if whence == 0 {
		s.head()
	}

	return 0, nil

}

// Sync ensures that any buffered data in the stream is committed to stable
// storage. In some Syncable types, Seek does nothing, as the data is written
// and persisted immediately when a Write is made. On Syncable types which
// support BufferWrites, then Sync will ensure the data stored is flushed.
func (s *Storage) Sync() error {

	s.lock.Lock()
	defer s.lock.Unlock()

	return s.sync()

}

// ---------------------------------------------------------------------------

func (s *Storage) load(file string) (err error) {

	s.buff.rdr, err = s.serv.bck.Object(file).NewReader(s.serv.ctx)

	s.file.last = file

	return nil

}

func (s *Storage) sync() (err error) {

	if s.size.now == 0 {
		return nil
	}

	wtr := s.serv.bck.Object(s.name()).NewWriter(s.serv.ctx)

	if _, err = wtr.Write(s.buff.bit); err != nil {
		return err
	}

	if err = s.buff.wtr.ResetBytes(&s.buff.bit); err != nil {
		return err
	}

	s.size.now = 0

	return wtr.Close()

}

func (s *Storage) stop() (err error) {

	if s.buff.rdr == nil {
		return nil
	}

	defer func() {
		s.buff.rdr = nil
	}()

	return s.buff.rdr.Close()

}

func (s *Storage) head() (err error) {

	err = s.stop()
	if err != nil {
		return err
	}

	for _, file := range s.look() {
		return s.load(file)
	}

	return io.EOF

}

func (s *Storage) next() (err error) {

	err = s.stop()
	if err != nil {
		return err
	}

	for _, file := range s.look() {
		if file > s.file.last {
			return s.load(file)
		}
	}

	return io.EOF

}

func (s *Storage) look() []string {

	if s.file.poss != nil {
		return s.file.poss
	}

	i := s.serv.bck.Objects(s.serv.ctx, &storage.Query{
		Prefix: s.file.path,
	})

	for {

		o, e := i.Next()
		if e != nil {
			break
		}

		// Ignore folders and invisibles
		if o.Name[:1] == "." {
			continue
		}

		s.file.poss = append(s.file.poss, o.Name)

	}

	return s.file.poss

}

func (s *Storage) name() string {
	return path.Join(s.file.path, s.file.name+"-"+s.time()+s.file.extn)
}

func (s *Storage) time() string {
	return time.Now().UTC().Format("2006-01-02T15-04-05.999999999")
}
<commit_msg>Fix comments<commit_after>// Copyright © 2016 Abcum Ltd
//
// 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 gcs

import (
	"context"
	"errors"
	"io"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/abcum/bump"

	"cloud.google.com/go/storage"
)

var (
	// ErrSeekPositionNotSupported occurs when a syncable type receives a
	// seek destination which it can not fulfil to. These syncable types
	// usually only support seeking to the beginning or to the end, as they
	// write to and read from multiple data files.
	ErrSeekPositionNotSupported = errors.New("Seek position is not supported.")
)

// Options represents syncing configuration options.
type Options struct {
	// MinSize is used to determine the number of megabytes to write to
	// a file before the file should be rotated, and a new file used. This
	// is only available on certain types which support rotation of files,
	// but do not support appendable writes. The file is overwritten on
	// each write using the buffered data. MinSize should be specified in
	// megabytes.
	MinSize int
}

// Storage represents a Google Cloud Storage reader and writer.
type Storage struct {
	sync.Mutex
	opts *Options
	lock sync.Mutex
	size struct {
		min int
		now int
	}
	serv struct {
		srv *storage.Client
		ctx context.Context
		bck *storage.BucketHandle
	}
	buff struct {
		bit []byte
		wtr *bump.Writer
		rdr io.ReadCloser
	}
	file struct {
		buck string
		last string
		full string
		path string
		base string
		name string
		extn string
		main string
		poss []string
	}
}

// New creates a new Syncable storage instance for reading and writing.
func New(name string, opts *Options) (s *Storage, e error) {

	p := strings.Index(name, "/")

	s = &Storage{}

	s.opts = opts
	s.file.buck = name[:p]
	s.file.full = name[p+1:]
	s.file.path = path.Dir(s.file.full)
	s.file.base = path.Base(s.file.full)
	s.file.extn = path.Ext(s.file.full)
	s.file.name = s.file.base[:len(s.file.base)-len(s.file.extn)]
	s.file.main = path.Join(s.file.path, s.file.base)

	s.serv.ctx = context.Background()

	s.buff.wtr = bump.NewWriterBytes(&s.buff.bit)

	s.size.min = opts.MinSize * 1024 * 1024

	s.serv.srv, e = storage.NewClient(s.serv.ctx)

	s.serv.bck = s.serv.srv.Bucket(s.file.buck)

	return

}

// Close closes the underlying Syncable storage data stream, and prevents
// any further reads of writes to the Storage instance.
func (s *Storage) Close() error {

	s.lock.Lock()
	defer s.lock.Unlock()

	defer func() {
		s.opts = nil
		s.buff.bit = nil
		s.buff.wtr = nil
		s.buff.rdr = nil
		s.file.last = ""
		s.file.full = ""
		s.file.path = ""
		s.file.base = ""
		s.file.name = ""
		s.file.extn = ""
		s.file.main = ""
		s.file.poss = nil
	}()

	s.serv.srv.Close()

	return s.stop()

}

// Read reads up to len(p) bytes into p, reading from the Storage data
// stream, and automatically rotating files when the end of a file is
// reached. It returns the number of bytes read (0 <= n <= len(p)) and
// any error encountered. If the Storage has reached the final log file,
// then an EOF error will be returned.
func (s *Storage) Read(p []byte) (int, error) {

	s.lock.Lock()
	defer s.lock.Unlock()

	// No data file is open yet so open one.
	if s.buff.rdr == nil {
		if err := s.head(); err != nil {
			return 0, err
		}
	}

	n, err := s.buff.rdr.Read(p)
	if err == io.EOF {
		err = s.next()
	}

	return n, err

}

// Write writes len(p) bytes from p to the underlying Storage data
// stream. It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early. Write
// will always append data to the end of the Storage data stream, ensuring
// data is append-only and never overwritten.
func (s *Storage) Write(p []byte) (int, error) {

	s.lock.Lock()
	defer s.lock.Unlock()

	if s.size.min < s.size.now+len(p) {
		if err := s.sync(); err != nil {
			return 0, err
		}
	}

	s.size.now += len(p)

	return len(p), s.buff.wtr.WriteBytes(p)

}

// Seek sets the offset for the next Read or Write to offset, interpreted
// according to whence: SeekStart means relative to the start of the file,
// SeekCurrent means relative to the current offset, and SeekE8nd means
// relative to the end. Seek returns the new offset relative to the start
// of the file and an error, if any. In some storage types, Seek only
// supports seeking to the beginning and end of the data stream.
func (s *Storage) Seek(offset int64, whence int) (int64, error) {

	s.lock.Lock()
	defer s.lock.Unlock()

	if offset != 0 {
		return 0, ErrSeekPositionNotSupported
	}

	if whence != 0 && whence != 2 {
		return 0, ErrSeekPositionNotSupported
	}

	if whence == 0 {
		s.head()
	}

	return 0, nil

}

// Sync ensures that any buffered data in the stream is committed to stable
// storage. In some Syncable types, Seek does nothing, as the data is written
// and persisted immediately when a Write is made. On Syncable types which
// support BufferWrites, then Sync will ensure the data stored is flushed.
func (s *Storage) Sync() error {

	s.lock.Lock()
	defer s.lock.Unlock()

	return s.sync()

}

// ---------------------------------------------------------------------------

func (s *Storage) load(file string) (err error) {

	s.buff.rdr, err = s.serv.bck.Object(file).NewReader(s.serv.ctx)

	s.file.last = file

	return nil

}

func (s *Storage) sync() (err error) {

	if s.size.now == 0 {
		return nil
	}

	wtr := s.serv.bck.Object(s.name()).NewWriter(s.serv.ctx)

	if _, err = wtr.Write(s.buff.bit); err != nil {
		return err
	}

	if err = s.buff.wtr.ResetBytes(&s.buff.bit); err != nil {
		return err
	}

	s.size.now = 0

	return wtr.Close()

}

func (s *Storage) stop() (err error) {

	if s.buff.rdr == nil {
		return nil
	}

	defer func() {
		s.buff.rdr = nil
	}()

	return s.buff.rdr.Close()

}

func (s *Storage) head() (err error) {

	err = s.stop()
	if err != nil {
		return err
	}

	for _, file := range s.look() {
		return s.load(file)
	}

	return io.EOF

}

func (s *Storage) next() (err error) {

	err = s.stop()
	if err != nil {
		return err
	}

	for _, file := range s.look() {
		if file > s.file.last {
			return s.load(file)
		}
	}

	return io.EOF

}

func (s *Storage) look() []string {

	if s.file.poss != nil {
		return s.file.poss
	}

	i := s.serv.bck.Objects(s.serv.ctx, &storage.Query{
		Prefix: s.file.path,
	})

	for {

		o, e := i.Next()
		if e != nil {
			break
		}

		// Ignore folders and invisibles
		if o.Name[:1] == "." {
			continue
		}

		s.file.poss = append(s.file.poss, o.Name)

	}

	return s.file.poss

}

func (s *Storage) name() string {
	return path.Join(s.file.path, s.file.name+"-"+s.time()+s.file.extn)
}

func (s *Storage) time() string {
	return time.Now().UTC().Format("2006-01-02T15-04-05.999999999")
}
<|endoftext|>


<commit_before>package app

import (
	"github.com/pavlo/heatit/utils"
	"strings"
	"testing"
)

func TestEngineProcessInsertsSimpleInsertion(t *testing.T) {
	data := readFixture(t, "../fixtures/engine/simple_insertion/a.yaml")

	content, err := processInserts(data, 0)
	if err != nil {
		t.Fatal("Failed to process inserts!")
	}

	expectedContent := readFixture(t, "../fixtures/engine/simple_insertion/result.yaml")
	if content != expectedContent {
		t.Log("Failed to compare results!")
		t.Logf("Expected: \n[%v]\n", expectedContent)
		t.Logf("Actual: \n[%v]\n", content)
		t.Fail()
	}

}

func TestEngineProcessInsertsRecursiveInsertion(t *testing.T) {
	data := readFixture(t, "../fixtures/engine/recursive_insertion/a.yaml")

	content, err := processInserts(data, 0)
	if err != nil {
		t.Fatal("Failed to process inserts!")
	}

	expectedContent := readFixture(t, "../fixtures/engine/recursive_insertion/result.yaml")
	if strings.TrimSpace(content) != strings.TrimSpace(expectedContent) {
		t.Log("Failed to compare results!")
		t.Logf("Expected: \n[%v]\n", expectedContent)
		t.Logf("Actual: \n[%v]\n", content)
		t.Fail()
	}

}

func TestEngineProcessInsertsComplete(t *testing.T) {
	data := readFixture(t, "../fixtures/engine/complete/heat.yaml")

	content, err := processInserts(data, 0)
	if err != nil {
		t.Fatal("Failed to process inserts!")
	}

	expectedContent := readFixture(t, "../fixtures/engine/complete/result.yaml")
	if strings.TrimSpace(content) != strings.TrimSpace(expectedContent) {
		t.Log("Failed to compare results!")
		t.Logf("Expected: \n[%v]\n", expectedContent)
		t.Logf("Actual: \n[%v]\n", content)
		t.Fail()
	}

}

func readFixture(t *testing.T, path string) string {
	result, err := utils.ReadTextFile(path)
	if err != nil {
		t.Fatalf("Failed to read fixture file: %s", path)
	}

	return result
}
<commit_msg>refactoring to engine_test.go<commit_after>package app

import (
	"github.com/pavlo/heatit/utils"
	"strings"
	"testing"
)

func TestEngineProcessInsertsSimpleInsertion(t *testing.T) {
	assertProcess(
		t,
		"../fixtures/engine/simple_insertion/a.yaml",
		"../fixtures/engine/simple_insertion/result.yaml",
	)
}

func TestEngineProcessInsertsRecursiveInsertion(t *testing.T) {
	assertProcess(
		t,
		"../fixtures/engine/recursive_insertion/a.yaml",
		"../fixtures/engine/recursive_insertion/result.yaml",
	)
}

func TestEngineProcessInsertsComplete(t *testing.T) {
	assertProcess(
		t,
		"../fixtures/engine/complete/heat.yaml",
		"../fixtures/engine/complete/result.yaml",
	)
}

func assertProcess(t *testing.T, sourceFile string, expectedResultFile string) {
	data := readFixture(t, sourceFile)

	content, err := processInserts(data, 0)
	if err != nil {
		t.Fatal("Failed to process inserts!")
	}

	expectedContent := readFixture(t, expectedResultFile)
	if strings.TrimSpace(content) != strings.TrimSpace(expectedContent) {
		t.Log("Failed to compare results!")
		t.Logf("Expected: \n[%v]\n", expectedContent)
		t.Logf("Actual: \n[%v]\n", content)
		t.Fail()
	}
}

func readFixture(t *testing.T, path string) string {
	result, err := utils.ReadTextFile(path)
	if err != nil {
		t.Fatalf("Failed to read fixture file: %s", path)
	}

	return result
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"

	"github.com/hashicorp/go-retryablehttp"
	"github.com/hashicorp/terraform/state/remote"
)

func TestHTTPClient_impl(t *testing.T) {
	var _ remote.Client = new(httpClient)
	var _ remote.ClientLocker = new(httpClient)
}

func TestHTTPClient(t *testing.T) {
	handler := new(testHTTPHandler)
	ts := httptest.NewServer(http.HandlerFunc(handler.Handle))
	defer ts.Close()

	url, err := url.Parse(ts.URL)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Test basic get/update
	client := &httpClient{URL: url, Client: retryablehttp.NewClient()}
	remote.TestClient(t, client)

	// test just a single PUT
	p := &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		Client:       retryablehttp.NewClient(),
	}
	remote.TestClient(t, p)

	// Test locking and alternative UpdateMethod
	a := &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		LockURL:      url,
		LockMethod:   "LOCK",
		UnlockURL:    url,
		UnlockMethod: "UNLOCK",
		Client:       retryablehttp.NewClient(),
	}
	b := &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		LockURL:      url,
		LockMethod:   "LOCK",
		UnlockURL:    url,
		UnlockMethod: "UNLOCK",
		Client:       retryablehttp.NewClient(),
	}
	remote.TestRemoteLocks(t, a, b)

	// test a WebDAV-ish backend
	davhandler := new(testHTTPHandler)
	ts = httptest.NewServer(http.HandlerFunc(davhandler.HandleWebDAV))
	defer ts.Close()

	url, err = url.Parse(ts.URL)
	client = &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		Client:       retryablehttp.NewClient(),
	}
	remote.TestClient(t, client) // first time through: 201
	remote.TestClient(t, client) // second time, with identical data: 204

	// test a broken backend
	brokenHandler := new(testBrokenHTTPHandler)
	brokenHandler.handler = new(testHTTPHandler)
	ts = httptest.NewServer(http.HandlerFunc(brokenHandler.Handle))
	defer ts.Close()

	url, err = url.Parse(ts.URL)
	client = &httpClient{URL: url, Client: retryablehttp.NewClient()}
	remote.TestClient(t, client)
}

type testHTTPHandler struct {
	Data   []byte
	Locked bool
}

func (h *testHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		w.Write(h.Data)
	case "PUT":
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, r.Body); err != nil {
			w.WriteHeader(500)
		}
		w.WriteHeader(201)
		h.Data = buf.Bytes()
	case "POST":
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, r.Body); err != nil {
			w.WriteHeader(500)
		}
		h.Data = buf.Bytes()
	case "LOCK":
		if h.Locked {
			w.WriteHeader(423)
		} else {
			h.Locked = true
		}
	case "UNLOCK":
		h.Locked = false
	case "DELETE":
		h.Data = nil
		w.WriteHeader(200)
	default:
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
	}
}

// mod_dav-ish behavior
func (h *testHTTPHandler) HandleWebDAV(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		w.Write(h.Data)
	case "PUT":
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, r.Body); err != nil {
			w.WriteHeader(500)
		}
		if reflect.DeepEqual(h.Data, buf.Bytes()) {
			h.Data = buf.Bytes()
			w.WriteHeader(204)
		} else {
			h.Data = buf.Bytes()
			w.WriteHeader(201)
		}
	case "DELETE":
		h.Data = nil
		w.WriteHeader(200)
	default:
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
	}
}

type testBrokenHTTPHandler struct {
	lastRequestWasBroken bool
	handler              *testHTTPHandler
}

func (h *testBrokenHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
	if h.lastRequestWasBroken {
		h.lastRequestWasBroken = false
		h.handler.Handle(w, r)
	} else {
		h.lastRequestWasBroken = true
		w.WriteHeader(500)
	}
}
<commit_msg>backend/remote-state/http: fix dropped test errors<commit_after>package http

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"

	"github.com/hashicorp/go-retryablehttp"
	"github.com/hashicorp/terraform/state/remote"
)

func TestHTTPClient_impl(t *testing.T) {
	var _ remote.Client = new(httpClient)
	var _ remote.ClientLocker = new(httpClient)
}

func TestHTTPClient(t *testing.T) {
	handler := new(testHTTPHandler)
	ts := httptest.NewServer(http.HandlerFunc(handler.Handle))
	defer ts.Close()

	url, err := url.Parse(ts.URL)
	if err != nil {
		t.Fatalf("Parse: %s", err)
	}

	// Test basic get/update
	client := &httpClient{URL: url, Client: retryablehttp.NewClient()}
	remote.TestClient(t, client)

	// test just a single PUT
	p := &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		Client:       retryablehttp.NewClient(),
	}
	remote.TestClient(t, p)

	// Test locking and alternative UpdateMethod
	a := &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		LockURL:      url,
		LockMethod:   "LOCK",
		UnlockURL:    url,
		UnlockMethod: "UNLOCK",
		Client:       retryablehttp.NewClient(),
	}
	b := &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		LockURL:      url,
		LockMethod:   "LOCK",
		UnlockURL:    url,
		UnlockMethod: "UNLOCK",
		Client:       retryablehttp.NewClient(),
	}
	remote.TestRemoteLocks(t, a, b)

	// test a WebDAV-ish backend
	davhandler := new(testHTTPHandler)
	ts = httptest.NewServer(http.HandlerFunc(davhandler.HandleWebDAV))
	defer ts.Close()

	url, err = url.Parse(ts.URL)
	client = &httpClient{
		URL:          url,
		UpdateMethod: "PUT",
		Client:       retryablehttp.NewClient(),
	}
	if err != nil {
		t.Fatalf("Parse: %s", err)
	}

	remote.TestClient(t, client) // first time through: 201
	remote.TestClient(t, client) // second time, with identical data: 204

	// test a broken backend
	brokenHandler := new(testBrokenHTTPHandler)
	brokenHandler.handler = new(testHTTPHandler)
	ts = httptest.NewServer(http.HandlerFunc(brokenHandler.Handle))
	defer ts.Close()

	url, err = url.Parse(ts.URL)
	if err != nil {
		t.Fatalf("Parse: %s", err)
	}
	client = &httpClient{URL: url, Client: retryablehttp.NewClient()}
	remote.TestClient(t, client)
}

type testHTTPHandler struct {
	Data   []byte
	Locked bool
}

func (h *testHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		w.Write(h.Data)
	case "PUT":
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, r.Body); err != nil {
			w.WriteHeader(500)
		}
		w.WriteHeader(201)
		h.Data = buf.Bytes()
	case "POST":
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, r.Body); err != nil {
			w.WriteHeader(500)
		}
		h.Data = buf.Bytes()
	case "LOCK":
		if h.Locked {
			w.WriteHeader(423)
		} else {
			h.Locked = true
		}
	case "UNLOCK":
		h.Locked = false
	case "DELETE":
		h.Data = nil
		w.WriteHeader(200)
	default:
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
	}
}

// mod_dav-ish behavior
func (h *testHTTPHandler) HandleWebDAV(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		w.Write(h.Data)
	case "PUT":
		buf := new(bytes.Buffer)
		if _, err := io.Copy(buf, r.Body); err != nil {
			w.WriteHeader(500)
		}
		if reflect.DeepEqual(h.Data, buf.Bytes()) {
			h.Data = buf.Bytes()
			w.WriteHeader(204)
		} else {
			h.Data = buf.Bytes()
			w.WriteHeader(201)
		}
	case "DELETE":
		h.Data = nil
		w.WriteHeader(200)
	default:
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
	}
}

type testBrokenHTTPHandler struct {
	lastRequestWasBroken bool
	handler              *testHTTPHandler
}

func (h *testBrokenHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) {
	if h.lastRequestWasBroken {
		h.lastRequestWasBroken = false
		h.handler.Handle(w, r)
	} else {
		h.lastRequestWasBroken = true
		w.WriteHeader(500)
	}
}
<|endoftext|>


<commit_before>package gen

import (
	"fmt"
	"path/filepath"
	"sync"
)

type IosGenerator struct {
	opt  *Options
	mock *Mock
}

var iwd WidgetsDef

func defineIosWidgets() {
	iwd = WidgetsDef{}
	iwd.Add("button", Widget{
		Name:     "UIButton",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("label", Widget{
		Name:     "UILabel",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("linear", Widget{
		Textable:    false,
		Orientation: OrientationVertical,
		SizeW:       SizeFill,
		SizeH:       SizeFill,
	})
	iwd.Add("relative", Widget{
		Textable: false,
		SizeW:    SizeFill,
		SizeH:    SizeFill,
	})
}

func (g *IosGenerator) Generate() {
	defineIosWidgets()

	outDir := g.opt.OutDir
	projectDir := filepath.Join(outDir, g.mock.Meta.Ios.Project)

	// TODO Generate base file set

	var wg sync.WaitGroup

	// Generate main.m
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosMain(mock, dir)
	}(g.mock, outDir)

	// Generate ViewControllers
	for _, screen := range g.mock.Screens {
		wg.Add(1)
		go func(mock *Mock, dir string, screen Screen) {
			defer wg.Done()
			genIosViewController(mock, dir, screen)
			genIosViewControllerLayout(mock, dir, screen)
		}(g.mock, projectDir, screen)
	}

	// Generate resources
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosLocalizedStrings(mock, dir)
	}(g.mock, projectDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosColors(mock, dir)
	}(g.mock, projectDir)

	wg.Wait()
}

func genIosMain(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosMain(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "main.m"))
}

func genCodeIosMain(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import <UIKit/UIKit.h>

#import "%sAppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([%sAppDelegate class]));
    }
}`,
		mock.Meta.Ios.ClassPrefix,
		mock.Meta.Ios.ClassPrefix)
}

func genIosViewController(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: ViewController generator: Not implemented...")
}

func genIosViewControllerLayout(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: Layout generator: Not implemented...")
}

func genIosLocalizedStrings(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: LocalizedString generator: Not implemented...")
}

func genIosColors(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: Colors generator: Not implemented...")
}
<commit_msg>Added iOS InfoPlist.strings generator.<commit_after>package gen

import (
	"fmt"
	"path/filepath"
	"sync"
)

type IosGenerator struct {
	opt  *Options
	mock *Mock
}

var iwd WidgetsDef

func defineIosWidgets() {
	iwd = WidgetsDef{}
	iwd.Add("button", Widget{
		Name:     "UIButton",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("label", Widget{
		Name:     "UILabel",
		Textable: true,
		Gravity:  GravityCenter,
		SizeW:    SizeFill,
		SizeH:    SizeWrap,
	})
	iwd.Add("linear", Widget{
		Textable:    false,
		Orientation: OrientationVertical,
		SizeW:       SizeFill,
		SizeH:       SizeFill,
	})
	iwd.Add("relative", Widget{
		Textable: false,
		SizeW:    SizeFill,
		SizeH:    SizeFill,
	})
}

func (g *IosGenerator) Generate() {
	defineIosWidgets()

	outDir := g.opt.OutDir
	projectDir := filepath.Join(outDir, g.mock.Meta.Ios.Project)

	// TODO Generate base file set

	var wg sync.WaitGroup

	// Generate main.m
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosMain(mock, dir)
	}(g.mock, outDir)

	// Generate InfoPlist.strings
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosInfoPlistStrings(mock, dir)
	}(g.mock, outDir)

	// Generate ViewControllers
	for _, screen := range g.mock.Screens {
		wg.Add(1)
		go func(mock *Mock, dir string, screen Screen) {
			defer wg.Done()
			genIosViewController(mock, dir, screen)
			genIosViewControllerLayout(mock, dir, screen)
		}(g.mock, projectDir, screen)
	}

	// Generate resources
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosLocalizedStrings(mock, dir)
	}(g.mock, projectDir)
	wg.Add(1)
	go func(mock *Mock, dir string) {
		defer wg.Done()
		genIosColors(mock, dir)
	}(g.mock, projectDir)

	wg.Wait()
}

func genIosMain(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosMain(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "main.m"))
}

func genCodeIosMain(mock *Mock, buf *CodeBuffer) {
	buf.add(`#import <UIKit/UIKit.h>

#import "%sAppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([%sAppDelegate class]));
    }
}`,
		mock.Meta.Ios.ClassPrefix,
		mock.Meta.Ios.ClassPrefix)
}

func genIosInfoPlistStrings(mock *Mock, dir string) {
	var buf CodeBuffer
	genCodeIosInfoPlistStrings(mock, &buf)
	genFile(&buf, filepath.Join(dir, mock.Meta.Ios.Project, mock.Meta.Ios.Project, "en.lproj", "InfoPlist.strings"))
}

func genCodeIosInfoPlistStrings(mock *Mock, buf *CodeBuffer) {
	buf.add(`/* Localized versions of Info.plist keys */`)
}

func genIosViewController(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: ViewController generator: Not implemented...")
}

func genIosViewControllerLayout(mock *Mock, dir string, screen Screen) {
	// TODO
	fmt.Println("iOS: Layout generator: Not implemented...")
}

func genIosLocalizedStrings(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: LocalizedString generator: Not implemented...")
}

func genIosColors(mock *Mock, dir string) {
	// TODO
	fmt.Println("iOS: Colors generator: Not implemented...")
}
<|endoftext|>


<commit_before>package edit

import "fmt"

// Command history subsystem.

// Interface.

type hist struct {
	current int
	prefix  string
	line    string
}

func (hist) Mode() ModeType {
	return modeHistory
}

func (h *hist) ModeLine(width int) *buffer {
	return makeModeLine(fmt.Sprintf(" HISTORY #%d ", h.current), width)
}

func startHistory(ed *Editor) {
	ed.hist.prefix = ed.line[:ed.dot]
	ed.hist.current = -1
	if ed.prevHistory() {
		ed.mode = &ed.hist
	} else {
		ed.addTip("no matching history item")
	}
}

func historyUp(ed *Editor) {
	ed.prevHistory()
}

func historyDown(ed *Editor) {
	ed.nextHistory()
}

func historyDownOrQuit(ed *Editor) {
	if !ed.nextHistory() {
		ed.mode = &ed.insert
	}
}

func historySwitchToHistlist(ed *Editor) {
	startHistlist(ed)
	if ed.mode == ed.histlist {
		ed.line = ""
		ed.dot = 0
		ed.histlist.changeFilter(ed.hist.prefix)
	}
}

func historyDefault(ed *Editor) {
	ed.acceptHistory()
	ed.mode = &ed.insert
	ed.nextAction = action{typ: reprocessKey}
}

// Implementation.

func (h *hist) jump(i int, line string) {
	h.current = i
	h.line = line
}

func (ed *Editor) appendHistory(line string) {
	if ed.store != nil {
		ed.historyMutex.Lock()
		go func() {
			ed.store.Waits.Add(1)
			// TODO(xiaq): Report possible error
			err := ed.store.AddCmd(line)
			ed.store.Waits.Done()
			ed.historyMutex.Unlock()
			if err != nil {
				Logger.Println("failed to add cmd to store:", err)
			} else {
				Logger.Println("added cmd to store:", line)
			}
		}()
	}
}

func (ed *Editor) prevHistory() bool {
	if ed.store != nil {
		i, line, err := ed.store.LastCmd(ed.hist.current, ed.hist.prefix)
		if err == nil {
			ed.hist.jump(i, line)
			return true
		}
		// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	}
	return false
}

func (ed *Editor) nextHistory() bool {
	if ed.store != nil {
		// Persistent history
		i, line, err := ed.store.FirstCmd(ed.hist.current+1, ed.hist.prefix)
		if err == nil {
			ed.hist.jump(i, line)
			return true
		}
		// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
	}

	return false
}

// acceptHistory accepts the currently selected history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.hist.line
	ed.dot = len(ed.line)
}
<commit_msg>Implement deduplication in history mode.<commit_after>package edit

import (
	"fmt"

	"github.com/elves/elvish/store"
)

// Command history subsystem.

// Interface.

type hist struct {
	current int
	prefix  string
	line    string
	// Maps content to the index of the last appearance. Used for deduplication.
	last map[string]int
}

func (hist) Mode() ModeType {
	return modeHistory
}

func (h *hist) ModeLine(width int) *buffer {
	return makeModeLine(fmt.Sprintf(" HISTORY #%d ", h.current), width)
}

func startHistory(ed *Editor) {
	ed.hist.prefix = ed.line[:ed.dot]
	ed.hist.current = -1
	ed.hist.last = make(map[string]int)
	if ed.prevHistory() {
		ed.mode = &ed.hist
	} else {
		ed.addTip("no matching history item")
	}
}

func historyUp(ed *Editor) {
	ed.prevHistory()
}

func historyDown(ed *Editor) {
	ed.nextHistory()
}

func historyDownOrQuit(ed *Editor) {
	if !ed.nextHistory() {
		ed.mode = &ed.insert
	}
}

func historySwitchToHistlist(ed *Editor) {
	startHistlist(ed)
	if ed.mode == ed.histlist {
		ed.line = ""
		ed.dot = 0
		ed.histlist.changeFilter(ed.hist.prefix)
	}
}

func historyDefault(ed *Editor) {
	ed.acceptHistory()
	ed.mode = &ed.insert
	ed.nextAction = action{typ: reprocessKey}
}

// Implementation.

func (h *hist) jump(i int, line string) {
	h.current = i
	h.line = line
}

func (ed *Editor) appendHistory(line string) {
	if ed.store != nil {
		ed.historyMutex.Lock()
		go func() {
			ed.store.Waits.Add(1)
			// TODO(xiaq): Report possible error
			err := ed.store.AddCmd(line)
			ed.store.Waits.Done()
			ed.historyMutex.Unlock()
			if err != nil {
				Logger.Println("failed to add cmd to store:", err)
			} else {
				Logger.Println("added cmd to store:", line)
			}
		}()
	}
}

func (ed *Editor) prevHistory() bool {
	if ed.store == nil {
		return false
	}
	i := ed.hist.current
	var line string
	for {
		var err error
		i, line, err = ed.store.LastCmd(i, ed.hist.prefix)
		if err != nil {
			if err != store.ErrNoMatchingCmd {
				Logger.Println("LastCmd error:", err)
			}
			return false
		}
		if j, ok := ed.hist.last[line]; !ok || j == i {
			// Found the last among duplications
			ed.hist.last[line] = i
			break
		}
	}
	ed.hist.jump(i, line)
	return true
}

func (ed *Editor) nextHistory() bool {
	if ed.store == nil {
		return false
	}
	i := ed.hist.current + 1
	var line string
	for {
		var err error
		i, line, err = ed.store.FirstCmd(i+1, ed.hist.prefix)
		if err != nil {
			if err != store.ErrNoMatchingCmd {
				Logger.Println("LastCmd error:", err)
			}
			return false
		}
		if ed.hist.last[line] == i {
			break
		}
	}

	ed.hist.jump(i, line)
	return true
}

// acceptHistory accepts the currently selected history.
func (ed *Editor) acceptHistory() {
	ed.line = ed.hist.line
	ed.dot = len(ed.line)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gg

import (
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/aclements/go-gg/table"
)

// TODO: Split transforms, scalers, and layers into their own packages
// to clean up the name spaces and un-prefix their names?

// Warning is a logger for reporting conditions that don't prevent the
// production of a plot, but may lead to unexpected results.
var Warning = log.New(os.Stderr, "[gg] ", log.Lshortfile)

type Plot struct {
	//facet  facet

	env    *plotEnv
	scales map[string]scalerTree

	scaledData map[scaledDataKey]*scaledData
	scaleSet   map[scaleKey]bool
	marks      []plotMark

	axisLabels map[string][]string
}

func NewPlot(data table.Grouping) *Plot {
	p := &Plot{
		env: &plotEnv{
			data: data,
		},
		scales:     make(map[string]scalerTree),
		scaledData: make(map[scaledDataKey]*scaledData),
		scaleSet:   make(map[scaleKey]bool),
		axisLabels: make(map[string][]string),
	}
	return p
}

type plotEnv struct {
	parent *plotEnv
	data   table.Grouping
}

type scaleKey struct {
	gid   table.GroupID
	aes   string
	scale Scaler
}

// SetData sets p's current data table. The caller must not modify
// data in this table after this point.
func (p *Plot) SetData(data table.Grouping) *Plot {
	p.env.data = data
	return p
}

// Data returns p's current data table.
func (p *Plot) Data() table.Grouping {
	return p.env.data
}

type scalerTree struct {
	scales map[table.GroupID]Scaler
}

func newScalerTree() scalerTree {
	return scalerTree{map[table.GroupID]Scaler{
		table.RootGroupID: &defaultScale{},
	}}
}

func (t scalerTree) bind(gid table.GroupID, s Scaler) {
	// Unbind scales under gid.
	for ogid := range t.scales {
		if gid == table.RootGroupID {
			// Optimize binding the root GID.
			delete(t.scales, ogid)
			continue
		}

		for p := ogid; ; p = p.Parent() {
			if p == gid {
				delete(t.scales, ogid)
				break
			}
			if p == table.RootGroupID {
				break
			}
		}
	}
	t.scales[gid] = s
}

func (t scalerTree) find(gid table.GroupID) Scaler {
	for {
		if s, ok := t.scales[gid]; ok {
			return s
		}
		if gid == table.RootGroupID {
			// This should never happen.
			panic("no scale for group " + gid.String())
		}
		gid = gid.Parent()
	}
}

func (p *Plot) getScales(aes string) scalerTree {
	st, ok := p.scales[aes]
	if !ok {
		st = newScalerTree()
		p.scales[aes] = st
	}
	return st
}

// SetScale binds a scale to the given visual aesthetic. SetScale is
// shorthand for SetScaleAt(aes, s, table.RootGroupID).
//
// SetScale returns p for ease of chaining.
func (p *Plot) SetScale(aes string, s Scaler) *Plot {
	return p.SetScaleAt(aes, s, table.RootGroupID)
}

// SetScaleAt binds a scale to the given visual aesthetic for all data
// in group gid or descendants of gid.
func (p *Plot) SetScaleAt(aes string, s Scaler, gid table.GroupID) *Plot {
	// TODO: Should aes be an enum so you can't mix up aesthetics
	// and column names?
	p.getScales(aes).bind(gid, s)
	return p
}

// GetScale returns the scale for the given visual aesthetic used for
// data in group gid.
func (p *Plot) GetScale(aes string, gid table.GroupID) Scaler {
	return p.getScales(aes).find(gid)
}

type scaledDataKey struct {
	aes  string
	data table.Grouping
	col  string
}

// use binds a column of data to an aesthetic. It expands the domain
// of the aesthetic's scale to include the data in col, and returns
// the scaled data.
//
// col may be "", in which case it simply returns nil.
//
// TODO: Should aes be an enum?
func (p *Plot) use(aes string, col string) *scaledData {
	if col == "" {
		return nil
	}

	if col[0] == '@' {
		// TODO: Document this.
		n, err := strconv.Atoi(col[1:])
		if err == nil {
			cols := p.Data().Columns()
			if n >= len(cols) {
				panic(fmt.Sprintf("column index %d out of range; table has %d columns", n, len(cols)))
			}
			col = cols[n]
		}
	}

	// TODO: This is wrong. If the scale tree for aes changes,
	// this may return a stale scaledData bound to the wrong
	// scalers. If I got rid of scale trees, I could just put the
	// scaler in the key. Or I could clean up the cache when the
	// scale tree changes.

	sd := p.scaledData[scaledDataKey{aes, p.Data(), col}]
	if sd == nil {
		// Construct the scaledData.
		sd = &scaledData{
			seqs: make(map[table.GroupID]scaledSeq),
		}

		// Get the scale tree.
		st := p.getScales(aes)

		for _, gid := range p.Data().Tables() {
			table := p.Data().Table(gid)

			// Get the data.
			seq := table.MustColumn(col)

			// Find the scale.
			scaler := st.find(gid)

			// Add the scale to the scale set.
			p.scaleSet[scaleKey{gid, aes, scaler}] = true

			// Train the scale.
			scaler.ExpandDomain(seq)

			// Add it to the scaledData.
			sd.seqs[gid] = scaledSeq{seq, scaler}
		}

		p.scaledData[scaledDataKey{aes, p.Data(), col}] = sd
	}

	// Update axis labels.
	if aes == "x" || aes == "y" {
		p.axisLabels[aes] = append(p.axisLabels[aes], col)
	}

	return sd
}

// Save saves the current data table of p to a stack.
func (p *Plot) Save() *Plot {
	p.env = &plotEnv{
		parent: p.env,
		data:   p.env.data,
	}
	return p
}

// Restore restores the data table of p from the save stack.
func (p *Plot) Restore() *Plot {
	if p.env.parent == nil {
		panic("unbalanced Save/Restore")
	}
	p.env = p.env.parent
	return p
}

type Plotter interface {
	Apply(*Plot)
}

func (p *Plot) Add(plotters ...Plotter) *Plot {
	for _, plotter := range plotters {
		plotter.Apply(p)
	}
	return p
}
<commit_msg>gg: utility to define constant columns<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gg

import (
	"fmt"
	"log"
	"os"
	"strconv"

	"github.com/aclements/go-gg/table"
)

// TODO: Split transforms, scalers, and layers into their own packages
// to clean up the name spaces and un-prefix their names?

// Warning is a logger for reporting conditions that don't prevent the
// production of a plot, but may lead to unexpected results.
var Warning = log.New(os.Stderr, "[gg] ", log.Lshortfile)

type Plot struct {
	//facet  facet

	env    *plotEnv
	scales map[string]scalerTree

	scaledData map[scaledDataKey]*scaledData
	scaleSet   map[scaleKey]bool
	marks      []plotMark

	axisLabels map[string][]string

	constNonce int
}

func NewPlot(data table.Grouping) *Plot {
	p := &Plot{
		env: &plotEnv{
			data: data,
		},
		scales:     make(map[string]scalerTree),
		scaledData: make(map[scaledDataKey]*scaledData),
		scaleSet:   make(map[scaleKey]bool),
		axisLabels: make(map[string][]string),
	}
	return p
}

type plotEnv struct {
	parent *plotEnv
	data   table.Grouping
}

type scaleKey struct {
	gid   table.GroupID
	aes   string
	scale Scaler
}

// SetData sets p's current data table. The caller must not modify
// data in this table after this point.
func (p *Plot) SetData(data table.Grouping) *Plot {
	p.env.data = data
	return p
}

// Data returns p's current data table.
func (p *Plot) Data() table.Grouping {
	return p.env.data
}

// Const creates a new constant column bound to val in all groups and
// returns the generated column name. This is a convenient way to pass
// constant values to layers as columns.
//
// TODO: Typically this should be used with PreScaled or physical types.
func (p *Plot) Const(val interface{}) string {
	tab := p.Data()

retry:
	col := fmt.Sprintf("[gg-const-%d]", p.constNonce)
	p.constNonce++
	for _, col2 := range tab.Columns() {
		if col == col2 {
			goto retry
		}
	}

	p.SetData(table.MapTables(func(_ table.GroupID, t *table.Table) *table.Table {
		return t.AddConst(col, val)
	}, tab))

	return col
}

type scalerTree struct {
	scales map[table.GroupID]Scaler
}

func newScalerTree() scalerTree {
	return scalerTree{map[table.GroupID]Scaler{
		table.RootGroupID: &defaultScale{},
	}}
}

func (t scalerTree) bind(gid table.GroupID, s Scaler) {
	// Unbind scales under gid.
	for ogid := range t.scales {
		if gid == table.RootGroupID {
			// Optimize binding the root GID.
			delete(t.scales, ogid)
			continue
		}

		for p := ogid; ; p = p.Parent() {
			if p == gid {
				delete(t.scales, ogid)
				break
			}
			if p == table.RootGroupID {
				break
			}
		}
	}
	t.scales[gid] = s
}

func (t scalerTree) find(gid table.GroupID) Scaler {
	for {
		if s, ok := t.scales[gid]; ok {
			return s
		}
		if gid == table.RootGroupID {
			// This should never happen.
			panic("no scale for group " + gid.String())
		}
		gid = gid.Parent()
	}
}

func (p *Plot) getScales(aes string) scalerTree {
	st, ok := p.scales[aes]
	if !ok {
		st = newScalerTree()
		p.scales[aes] = st
	}
	return st
}

// SetScale binds a scale to the given visual aesthetic. SetScale is
// shorthand for SetScaleAt(aes, s, table.RootGroupID).
//
// SetScale returns p for ease of chaining.
func (p *Plot) SetScale(aes string, s Scaler) *Plot {
	return p.SetScaleAt(aes, s, table.RootGroupID)
}

// SetScaleAt binds a scale to the given visual aesthetic for all data
// in group gid or descendants of gid.
func (p *Plot) SetScaleAt(aes string, s Scaler, gid table.GroupID) *Plot {
	// TODO: Should aes be an enum so you can't mix up aesthetics
	// and column names?
	p.getScales(aes).bind(gid, s)
	return p
}

// GetScale returns the scale for the given visual aesthetic used for
// data in group gid.
func (p *Plot) GetScale(aes string, gid table.GroupID) Scaler {
	return p.getScales(aes).find(gid)
}

type scaledDataKey struct {
	aes  string
	data table.Grouping
	col  string
}

// use binds a column of data to an aesthetic. It expands the domain
// of the aesthetic's scale to include the data in col, and returns
// the scaled data.
//
// col may be "", in which case it simply returns nil.
//
// TODO: Should aes be an enum?
func (p *Plot) use(aes string, col string) *scaledData {
	if col == "" {
		return nil
	}

	if col[0] == '@' {
		// TODO: Document this.
		n, err := strconv.Atoi(col[1:])
		if err == nil {
			cols := p.Data().Columns()
			if n >= len(cols) {
				panic(fmt.Sprintf("column index %d out of range; table has %d columns", n, len(cols)))
			}
			col = cols[n]
		}
	}

	// TODO: This is wrong. If the scale tree for aes changes,
	// this may return a stale scaledData bound to the wrong
	// scalers. If I got rid of scale trees, I could just put the
	// scaler in the key. Or I could clean up the cache when the
	// scale tree changes.

	sd := p.scaledData[scaledDataKey{aes, p.Data(), col}]
	if sd == nil {
		// Construct the scaledData.
		sd = &scaledData{
			seqs: make(map[table.GroupID]scaledSeq),
		}

		// Get the scale tree.
		st := p.getScales(aes)

		for _, gid := range p.Data().Tables() {
			table := p.Data().Table(gid)

			// Get the data.
			seq := table.MustColumn(col)

			// Find the scale.
			scaler := st.find(gid)

			// Add the scale to the scale set.
			p.scaleSet[scaleKey{gid, aes, scaler}] = true

			// Train the scale.
			scaler.ExpandDomain(seq)

			// Add it to the scaledData.
			sd.seqs[gid] = scaledSeq{seq, scaler}
		}

		p.scaledData[scaledDataKey{aes, p.Data(), col}] = sd
	}

	// Update axis labels.
	if aes == "x" || aes == "y" {
		p.axisLabels[aes] = append(p.axisLabels[aes], col)
	}

	return sd
}

// Save saves the current data table of p to a stack.
func (p *Plot) Save() *Plot {
	p.env = &plotEnv{
		parent: p.env,
		data:   p.env.data,
	}
	return p
}

// Restore restores the data table of p from the save stack.
func (p *Plot) Restore() *Plot {
	if p.env.parent == nil {
		panic("unbalanced Save/Restore")
	}
	p.env = p.env.parent
	return p
}

type Plotter interface {
	Apply(*Plot)
}

func (p *Plot) Add(plotters ...Plotter) *Plot {
	for _, plotter := range plotters {
		plotter.Apply(p)
	}
	return p
}
<|endoftext|>


<commit_before>package nail

import (
	"fmt"
	"log"
	"strings"

	"github.com/antihax/evedata/internal/datapackages"
	"github.com/antihax/evedata/internal/gobcoder"
	nsq "github.com/nsqio/go-nsq"
)

func init() {
	AddHandler("characterAssets", spawnCharacterAssetsConsumer)
}

func spawnCharacterAssetsConsumer(s *Nail, consumer *nsq.Consumer) {
	consumer.AddHandler(s.wait(nsq.HandlerFunc(s.characterAssetsConsumer)))
}

func (s *Nail) characterAssetsConsumer(message *nsq.Message) error {
	assets := datapackages.CharacterAssets{}
	err := gobcoder.GobDecoder(message.Body, &assets)
	if err != nil {
		log.Println(err)
		return err
	}
	if len(assets.Assets) == 0 {
		return nil
	}
	var values []string

	err = s.doSQL("DELETE FROM evedata.assets WHERE characterID = ?;", assets.TokenCharacterID)
	if err != nil {
		log.Println(err)
		return err
	}

	// Dump all assets into the DB.
	count := 0
	for _, asset := range assets.Assets {
		count++
		values = append(values, fmt.Sprintf("(%d,%d,%d,%d,%q,%d,%q,%v)",
			asset.LocationId, asset.TypeId, asset.Quantity, assets.TokenCharacterID,
			asset.LocationFlag, asset.ItemId, asset.LocationType, asset.IsSingleton))

		if count > 100 {
			err := s.doSQL(doAssets(values))
			if err != nil {
				return err
			}
			values = values[:0]
			count = 0
		}
	}

	return s.doSQL(doAssets(values))
}

func doAssets(values []string) string {
	return fmt.Sprintf(`INSERT INTO evedata.assets
		(locationID, typeID, quantity, characterID,
		locationFlag, itemID, locationType, isSingleton)
		VALUES %s ON DUPLICATE KEY UPDATE locationID = locationID;`, strings.Join(values, ",\n"))
}
<commit_msg>Fix missing key field<commit_after>package nail

import (
	"fmt"
	"log"
	"strings"

	"github.com/antihax/evedata/internal/datapackages"
	"github.com/antihax/evedata/internal/gobcoder"
	nsq "github.com/nsqio/go-nsq"
)

func init() {
	AddHandler("characterAssets", spawnCharacterAssetsConsumer)
}

func spawnCharacterAssetsConsumer(s *Nail, consumer *nsq.Consumer) {
	consumer.AddHandler(s.wait(nsq.HandlerFunc(s.characterAssetsConsumer)))
}

func (s *Nail) characterAssetsConsumer(message *nsq.Message) error {
	assets := datapackages.CharacterAssets{}
	err := gobcoder.GobDecoder(message.Body, &assets)
	if err != nil {
		log.Println(err)
		return err
	}
	if len(assets.Assets) == 0 {
		return nil
	}
	var values []string

	err = s.doSQL("DELETE FROM evedata.assets WHERE characterID = ?;", assets.TokenCharacterID)
	if err != nil {
		log.Println(err)
		return err
	}

	// Dump all assets into the DB.
	count := 0
	for _, asset := range assets.Assets {
		count++
		values = append(values, fmt.Sprintf("(%d,%d,%d,%d,%q,%d,%q,%v)",
			asset.LocationId, asset.TypeId, asset.Quantity, assets.TokenCharacterID,
			asset.LocationFlag, asset.ItemId, asset.LocationType, asset.IsSingleton))

		if count > 100 {
			err := s.doSQL(doAssets(values))
			if err != nil {
				return err
			}
			values = values[:0]
			count = 0
		}
	}

	return s.doSQL(doAssets(values))
}

func doAssets(values []string) string {
	return fmt.Sprintf(`INSERT INTO evedata.assets
		(locationID, typeID, quantity, characterID,
		locationFlag, itemID, locationType, isSingleton)
		VALUES %s ON DUPLICATE KEY UPDATE itemID = itemID;`, strings.Join(values, ",\n"))
}
<|endoftext|>


<commit_before>package hsup

import (
	"errors"
	"io"
	"log"
	"os"
	"runtime"

	"bitbucket.org/kardianos/osext"
)

var ErrNoReleases = errors.New("No releases found")

type Notifier interface {
	Notify() <-chan *Processes
}

type Processes struct {
	Rel   *Release
	Forms []Formation

	Dd        DynoDriver
	OneShot   bool
	Executors []*Executor
}

type Formation interface {
	Args() []string
	Quantity() int
	Type() string
}

func linuxAmd64Path() string {
	exe, err := osext.Executable()
	if err != nil {
		log.Fatalf("could not locate own executable:", err)
	}

	if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" {
		return exe
	}

	return exe + "-linux-amd64"
}

func copyFile(src, dst string, mode os.FileMode) error {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer r.Close()

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

	if _, err := io.Copy(w, r); err != nil {
		return err
	}
	if err := os.Chmod(dst, mode); err != nil {
		return err
	}
	return nil
}
<commit_msg>do not swallow errors from file.Close()<commit_after>package hsup

import (
	"errors"
	"io"
	"log"
	"os"
	"runtime"
	"strings"

	"bitbucket.org/kardianos/osext"
)

var ErrNoReleases = errors.New("No releases found")

type Notifier interface {
	Notify() <-chan *Processes
}

type Processes struct {
	Rel   *Release
	Forms []Formation

	Dd        DynoDriver
	OneShot   bool
	Executors []*Executor
}

type Formation interface {
	Args() []string
	Quantity() int
	Type() string
}

func linuxAmd64Path() string {
	exe, err := osext.Executable()
	if err != nil {
		log.Fatalf("could not locate own executable:", err)
	}

	if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" {
		return exe
	}

	return exe + "-linux-amd64"
}

func copyFile(src, dst string, mode os.FileMode) (err error) {
	r, err := os.Open(src)
	if err != nil {
		return err
	}
	defer func() {
		err = combine(err, r.Close())
	}()

	w, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer func() {
		err = combine(err, w.Close())
	}()

	if _, err := io.Copy(w, r); err != nil {
		return err
	}
	if err := os.Chmod(dst, mode); err != nil {
		return err
	}
	return nil
}

type combinedError []error

func combine(errors ...error) combinedError {
	errors = make(combinedError, 0, len(errors))
	for _, err := range errors {
		if err == nil {
			continue
		}
		errors = append(errors, err)
	}
	return errors
}

func (e combinedError) Error() string {
	msgs := make([]string, len(e))
	for i, entry := range e {
		msgs[i] = entry.Error()
	}
	return strings.Join(msgs, " | ")
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 net

import (
	"fmt"
	"strconv"
	"strings"
)

// PortRange represents a range of TCP/UDP ports.  To represent a single port,
// set Size to 1.
type PortRange struct {
	Base int
	Size int
}

// Contains tests whether a given port falls within the PortRange.
func (pr *PortRange) Contains(p int) bool {
	return (p >= pr.Base) && ((p - pr.Base) < pr.Size)
}

// String converts the PortRange to a string representation, which can be
// parsed by PortRange.Set or ParsePortRange.
func (pr PortRange) String() string {
	if pr.Size == 0 {
		return ""
	}
	return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1)
}

// Set parses a string of the form "min-max", inclusive at both ends, and
// sets the PortRange from it.  This is part of the flag.Value and pflag.Value
// interfaces.
func (pr *PortRange) Set(value string) error {
	value = strings.TrimSpace(value)

	// TODO: Accept "80" syntax
	// TODO: Accept "80+8" syntax

	if value == "" {
		pr.Base = 0
		pr.Size = 0
		return nil
	}

	hyphenIndex := strings.Index(value, "-")
	if hyphenIndex == -1 {
		return fmt.Errorf("expected hyphen in port range")
	}

	var err error
	var low int
	var high int
	low, err = strconv.Atoi(value[:hyphenIndex])
	if err == nil {
		high, err = strconv.Atoi(value[hyphenIndex+1:])
	}
	if err != nil {
		return fmt.Errorf("unable to parse port range: %s", value)
	}

	if high < low {
		return fmt.Errorf("end port cannot be less than start port: %s", value)
	}
	pr.Base = low
	pr.Size = 1 + high - low
	return nil
}

// Type returns a descriptive string about this type.  This is part of the
// pflag.Value interface.
func (*PortRange) Type() string {
	return "portRange"
}

// ParsePortRange parses a string of the form "min-max", inclusive at both
// ends, and initializs a new PortRange from it.
func ParsePortRange(value string) (*PortRange, error) {
	pr := &PortRange{}
	err := pr.Set(value)
	if err != nil {
		return nil, err
	}
	return pr, nil
}
<commit_msg>update proxy server<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 net

import (
	"fmt"
	"strconv"
	"strings"
)

// PortRange represents a range of TCP/UDP ports.  To represent a single port,
// set Size to 1.
type PortRange struct {
	Base int
	Size int
}

// Contains tests whether a given port falls within the PortRange.
func (pr *PortRange) Contains(p int) bool {
	return (p >= pr.Base) && ((p - pr.Base) < pr.Size)
}

// String converts the PortRange to a string representation, which can be
// parsed by PortRange.Set or ParsePortRange.
func (pr PortRange) String() string {
	if pr.Size == 0 {
		return ""
	}
	return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1)
}

// Set parses a string of the form "min-max", inclusive at both ends, and
// sets the PortRange from it.  This is part of the flag.Value and pflag.Value
// interfaces.
func (pr *PortRange) Set(value string) error {
	value = strings.TrimSpace(value)

	// TODO: Accept "80" syntax
	// TODO: Accept "80+8" syntax

	if value == "" {
		pr.Base = 0
		pr.Size = 0
		return nil
	}

	hyphenIndex := strings.Index(value, "-")
	if hyphenIndex == -1 {
		return fmt.Errorf("expected hyphen in port range")
	}

	var err error
	var low int
	var high int
	low, err = strconv.Atoi(value[:hyphenIndex])
	if err == nil {
		high, err = strconv.Atoi(value[hyphenIndex+1:])
	}
	if err != nil {
		return fmt.Errorf("unable to parse port range: %s", value)
	}

	if high < low {
		return fmt.Errorf("end port cannot be less than start port: %s", value)
	}
	pr.Base = low
	pr.Size = 1 + high - low
	return nil
}

// Type returns a descriptive string about this type.  This is part of the
// pflag.Value interface.
func (*PortRange) Type() string {
	return "portRange"
}

// ParsePortRange parses a string of the form "min-max", inclusive at both
// ends, and initializs a new PortRange from it.
func ParsePortRange(value string) (*PortRange, error) {
	pr := &PortRange{}
	err := pr.Set(value)
	if err != nil {
		return nil, err
	}
	return pr, nil
}

func ParsePortRangeOrDie(value string) *PortRange {
	pr, err := ParsePortRange(value)
	if err != nil {
		panic(fmt.Sprintf("couldn't parse port range: %v"))
	}
	return pr
}
<|endoftext|>


<commit_before>/*
Package proj provides an interface to the Cartographic Projections Library PROJ.4 [cartography].

See: http://trac.osgeo.org/proj/

Example usage:

    merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33")
    defer merc.Close() // if omitted, this will be called on garbage collection
    if err != nil {
        log.Fatal(err)
    }

    ll, err := proj.NewProj("+proj=latlong")
    defer ll.Close()
    if err != nil {
        log.Fatal(err)
    }

    x, y, err := proj.Transform2(ll, merc, proj.DegToRad(-16), proj.DegToRad(20.25))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%.2f %.2f", x, y)  // should print: -1495284.21 1920596.79
*/
package proj

/*
#cgo LDFLAGS: -lproj
#include "proj.h"
*/
import "C"

import (
	"errors"
	"math"
	"runtime"
	"sync"
	"unsafe"
)

var (
	mu sync.Mutex
)

type Proj struct {
	pj     _Ctype_projPJ
	opened bool
}

func NewProj(definition string) (*Proj, error) {
	cs := C.CString(definition)
	defer C.free(unsafe.Pointer(cs))
	proj := &Proj{opened: false}

	mu.Lock()
	proj.pj = C.pj_init_plus(cs)
	errstring := C.GoString(C.get_err())
	mu.Unlock()

	var err error = nil
	if proj.pj != nil {
		proj.opened = true
	} else {
		err = errors.New(errstring)
	}

	runtime.SetFinalizer(proj, (*Proj).Close)

	return proj, err
}

func (p *Proj) Close() {
	if p.opened {
		C.pj_free(p.pj)
		p.opened = false
	}
}

func transform(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) {
	if !(srcpj.opened && dstpj.opened) {
		return []float64{}, []float64{}, []float64{}, errors.New("projection is closed")
	}

	var x1, y1, z1 []C.double

	ln := len(x)
	if len(y) < ln {
		ln = len(y)
	}
	if z != nil && len(z) < ln {
		ln = len(z)
	}

	if ln == 0 {
		return []float64{}, []float64{}, []float64{}, nil
	}

	x1 = make([]C.double, ln)
	y1 = make([]C.double, ln)
	if z != nil {
		z1 = make([]C.double, ln)
	}
	for i := 0; i < ln; i++ {
		x1[i] = C.double(x[i])
		y1[i] = C.double(y[i])
		if z != nil {
			z1[i] = C.double(z[i])
		}
	}

	var e *C.char
	if z != nil {
		e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], &z1[0])
	} else {
		e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], nil)
	}

	if e != nil {
		return []float64{}, []float64{}, []float64{}, errors.New(C.GoString(e))
	}

	var x2, y2, z2 []float64
	x2 = make([]float64, ln)
	y2 = make([]float64, ln)
	if z != nil {
		z2 = make([]float64, ln)
	}
	for i := 0; i < ln; i++ {
		x2[i] = float64(x1[i])
		y2[i] = float64(y1[i])
		if z != nil {
			z2[i] = float64(z1[i])
		}
	}
	return x2, y2, z2, nil
}

func Transform2(srcpj, dstpj *Proj, x, y float64) (float64, float64, error) {
	xx, yy, _, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, nil)
	if err != nil {
		return math.NaN(), math.NaN(), err
	}
	return xx[0], yy[0], err
}

func Transform3(srcpj, dstpj *Proj, x, y, z float64) (float64, float64, float64, error) {
	xx, yy, zz, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, []float64{z})
	if err != nil {
		return math.NaN(), math.NaN(), math.NaN(), err
	}
	return xx[0], yy[0], zz[0], err
}

func Transform2lst(srcpj, dstpj *Proj, x, y []float64) ([]float64, []float64, error) {
	xx, yy, _, err := transform(srcpj, dstpj, x, y, nil)
	return xx, yy, err
}

func Transform3lst(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) {
	return transform(srcpj, dstpj, x, y, z)
}

// Longitude and latitude in degrees
func Fwd(proj *Proj, long, lat float64) (x, y float64, err error) {
	if !proj.opened {
		return math.NaN(), math.NaN(), errors.New("projection is closed")
	}
	x1 := C.double(long)
	y1 := C.double(lat)
	e := C.fwd(proj.pj, &x1, &y1)
	if e != nil {
		return math.NaN(), math.NaN(), errors.New(C.GoString(e))
	}
	return float64(x1), float64(y1), nil
}

// Longitude and latitude in degrees
func Inv(proj *Proj, x, y float64) (long, lat float64, err error) {
	if !proj.opened {
		return math.NaN(), math.NaN(), errors.New("projection is closed")
	}
	x2 := C.double(x)
	y2 := C.double(y)
	e := C.inv(proj.pj, &x2, &y2)
	if e != nil {
		return math.NaN(), math.NaN(), errors.New(C.GoString(e))
	}
	return float64(x2), float64(y2), nil
}

func DegToRad(deg float64) float64 {
	return deg / 180.0 * math.Pi
}

func RadToDeg(rad float64) float64 {
	return rad / math.Pi * 180.0
}
<commit_msg>refactoring of NewProj()<commit_after>/*
Package proj provides an interface to the Cartographic Projections Library PROJ.4 [cartography].

See: http://trac.osgeo.org/proj/

Example usage:

    merc, err := proj.NewProj("+proj=merc +ellps=clrk66 +lat_ts=33")
    defer merc.Close() // if omitted, this will be called on garbage collection
    if err != nil {
        log.Fatal(err)
    }

    ll, err := proj.NewProj("+proj=latlong")
    defer ll.Close()
    if err != nil {
        log.Fatal(err)
    }

    x, y, err := proj.Transform2(ll, merc, proj.DegToRad(-16), proj.DegToRad(20.25))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%.2f %.2f", x, y)  // should print: -1495284.21 1920596.79
*/
package proj

/*
#cgo LDFLAGS: -lproj
#include "proj.h"
*/
import "C"

import (
	"errors"
	"math"
	"runtime"
	"sync"
	"unsafe"
)

var (
	mu sync.Mutex
)

type Proj struct {
	pj     _Ctype_projPJ
	opened bool
}

func NewProj(definition string) (*Proj, error) {
	cs := C.CString(definition)
	defer C.free(unsafe.Pointer(cs))
	proj := &Proj{opened: false}

	mu.Lock()
	defer mu.Unlock()

	proj.pj = C.pj_init_plus(cs)

	if proj.pj == nil {
		return proj, errors.New(C.GoString(C.get_err()))
	}

	proj.opened = true
	runtime.SetFinalizer(proj, (*Proj).Close)

	return proj, nil
}

func (p *Proj) Close() {
	if p.opened {
		C.pj_free(p.pj)
		p.opened = false
	}
}

func transform(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) {
	if !(srcpj.opened && dstpj.opened) {
		return []float64{}, []float64{}, []float64{}, errors.New("projection is closed")
	}

	var x1, y1, z1 []C.double

	ln := len(x)
	if len(y) < ln {
		ln = len(y)
	}
	if z != nil && len(z) < ln {
		ln = len(z)
	}

	if ln == 0 {
		return []float64{}, []float64{}, []float64{}, nil
	}

	x1 = make([]C.double, ln)
	y1 = make([]C.double, ln)
	if z != nil {
		z1 = make([]C.double, ln)
	}
	for i := 0; i < ln; i++ {
		x1[i] = C.double(x[i])
		y1[i] = C.double(y[i])
		if z != nil {
			z1[i] = C.double(z[i])
		}
	}

	var e *C.char
	if z != nil {
		e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], &z1[0])
	} else {
		e = C.transform(srcpj.pj, dstpj.pj, C.long(ln), &x1[0], &y1[0], nil)
	}

	if e != nil {
		return []float64{}, []float64{}, []float64{}, errors.New(C.GoString(e))
	}

	var x2, y2, z2 []float64
	x2 = make([]float64, ln)
	y2 = make([]float64, ln)
	if z != nil {
		z2 = make([]float64, ln)
	}
	for i := 0; i < ln; i++ {
		x2[i] = float64(x1[i])
		y2[i] = float64(y1[i])
		if z != nil {
			z2[i] = float64(z1[i])
		}
	}
	return x2, y2, z2, nil
}

func Transform2(srcpj, dstpj *Proj, x, y float64) (float64, float64, error) {
	xx, yy, _, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, nil)
	if err != nil {
		return math.NaN(), math.NaN(), err
	}
	return xx[0], yy[0], err
}

func Transform3(srcpj, dstpj *Proj, x, y, z float64) (float64, float64, float64, error) {
	xx, yy, zz, err := transform(srcpj, dstpj, []float64{x}, []float64{y}, []float64{z})
	if err != nil {
		return math.NaN(), math.NaN(), math.NaN(), err
	}
	return xx[0], yy[0], zz[0], err
}

func Transform2lst(srcpj, dstpj *Proj, x, y []float64) ([]float64, []float64, error) {
	xx, yy, _, err := transform(srcpj, dstpj, x, y, nil)
	return xx, yy, err
}

func Transform3lst(srcpj, dstpj *Proj, x, y, z []float64) ([]float64, []float64, []float64, error) {
	return transform(srcpj, dstpj, x, y, z)
}

// Longitude and latitude in degrees
func Fwd(proj *Proj, long, lat float64) (x, y float64, err error) {
	if !proj.opened {
		return math.NaN(), math.NaN(), errors.New("projection is closed")
	}
	x1 := C.double(long)
	y1 := C.double(lat)
	e := C.fwd(proj.pj, &x1, &y1)
	if e != nil {
		return math.NaN(), math.NaN(), errors.New(C.GoString(e))
	}
	return float64(x1), float64(y1), nil
}

// Longitude and latitude in degrees
func Inv(proj *Proj, x, y float64) (long, lat float64, err error) {
	if !proj.opened {
		return math.NaN(), math.NaN(), errors.New("projection is closed")
	}
	x2 := C.double(x)
	y2 := C.double(y)
	e := C.inv(proj.pj, &x2, &y2)
	if e != nil {
		return math.NaN(), math.NaN(), errors.New(C.GoString(e))
	}
	return float64(x2), float64(y2), nil
}

func DegToRad(deg float64) float64 {
	return deg / 180.0 * math.Pi
}

func RadToDeg(rad float64) float64 {
	return rad / math.Pi * 180.0
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	"github.com/prometheus/procfs"
)

const (
	binary  = "./node_exporter"
	address = "localhost:19100"
)

func TestFileDescriptorLeak(t *testing.T) {
	if _, err := os.Stat(binary); err != nil {
		t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err)
	}
	if _, err := procfs.NewStat(); err != nil {
		t.Skipf("proc filesystem is not available, but currently required to read number of open file descriptors: %s", err)
	}

	exporter := exec.Command(binary, "-web.listen-address", address)
	test := func(pid int) error {
		if err := queryExporter(address); err != nil {
			return err
		}
		proc, err := procfs.NewProc(pid)
		if err != nil {
			return err
		}
		fdsBefore, err := proc.FileDescriptors()
		if err != nil {
			return err
		}
		for i := 0; i < 5; i++ {
			if err := queryExporter(address); err != nil {
				return err
			}
		}
		fdsAfter, err := proc.FileDescriptors()
		if err != nil {
			return err
		}
		if want, have := len(fdsBefore), len(fdsAfter); want != have {
			return fmt.Errorf("want %d open file descriptors after metrics scrape, have %d", want, have)
		}
		return nil
	}

	if err := runCommandAndTests(exporter, test); err != nil {
		t.Error(err)
	}
}

func TestHandlingOfDuplicatedMetrics(t *testing.T) {
	if _, err := os.Stat(binary); err != nil {
		t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err)
	}

	dir, err := ioutil.TempDir("", "node-exporter")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	content := []byte("dummy_metric 1\n")
	if err := ioutil.WriteFile(filepath.Join(dir, "a.prom"), content, 0600); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(filepath.Join(dir, "b.prom"), content, 0600); err != nil {
		t.Fatal(err)
	}

	exporter := exec.Command(binary, "-web.listen-address", address, "-collector.textfile.directory", dir)
	test := func(_ int) error {
		return queryExporter(address)
	}

	if err := runCommandAndTests(exporter, test); err != nil {
		t.Error(err)
	}
}

func queryExporter(address string) error {
	resp, err := http.Get(fmt.Sprintf("http://%s/metrics", address))
	if err != nil {
		return err
	}
	if err := resp.Body.Close(); err != nil {
		return err
	}
	if want, have := http.StatusOK, resp.StatusCode; want != have {
		return fmt.Errorf("want /metrics status code %d, have %d", want, have)
	}
	return nil
}

func runCommandAndTests(cmd *exec.Cmd, fn func(pid int) error) error {
	errc := make(chan error)
	go func() {
		if err := cmd.Run(); err != nil {
			errc <- fmt.Errorf("execution of command failed: %s", err)
		} else {
			errc <- nil
		}
	}()

	// Allow the process to start before running any tests.
	select {
	case err := <-errc:
		return err
	case <-time.After(100 * time.Millisecond):
	}

	go func(pid int) {
		errc <- fn(pid)
	}(cmd.Process.Pid)

	select {
	case err := <-errc:
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Log response body if test fails.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	"github.com/prometheus/procfs"
)

const (
	binary  = "./node_exporter"
	address = "localhost:19100"
)

func TestFileDescriptorLeak(t *testing.T) {
	if _, err := os.Stat(binary); err != nil {
		t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err)
	}
	if _, err := procfs.NewStat(); err != nil {
		t.Skipf("proc filesystem is not available, but currently required to read number of open file descriptors: %s", err)
	}

	exporter := exec.Command(binary, "-web.listen-address", address)
	test := func(pid int) error {
		if err := queryExporter(address); err != nil {
			return err
		}
		proc, err := procfs.NewProc(pid)
		if err != nil {
			return err
		}
		fdsBefore, err := proc.FileDescriptors()
		if err != nil {
			return err
		}
		for i := 0; i < 5; i++ {
			if err := queryExporter(address); err != nil {
				return err
			}
		}
		fdsAfter, err := proc.FileDescriptors()
		if err != nil {
			return err
		}
		if want, have := len(fdsBefore), len(fdsAfter); want != have {
			return fmt.Errorf("want %d open file descriptors after metrics scrape, have %d", want, have)
		}
		return nil
	}

	if err := runCommandAndTests(exporter, test); err != nil {
		t.Error(err)
	}
}

func TestHandlingOfDuplicatedMetrics(t *testing.T) {
	if _, err := os.Stat(binary); err != nil {
		t.Skipf("node_exporter binary not available, try to run `make build` first: %s", err)
	}

	dir, err := ioutil.TempDir("", "node-exporter")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	content := []byte("dummy_metric 1\n")
	if err := ioutil.WriteFile(filepath.Join(dir, "a.prom"), content, 0600); err != nil {
		t.Fatal(err)
	}
	if err := ioutil.WriteFile(filepath.Join(dir, "b.prom"), content, 0600); err != nil {
		t.Fatal(err)
	}

	exporter := exec.Command(binary, "-web.listen-address", address, "-collector.textfile.directory", dir)
	test := func(_ int) error {
		return queryExporter(address)
	}

	if err := runCommandAndTests(exporter, test); err != nil {
		t.Error(err)
	}
}

func queryExporter(address string) error {
	resp, err := http.Get(fmt.Sprintf("http://%s/metrics", address))
	if err != nil {
		return err
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if err := resp.Body.Close(); err != nil {
		return err
	}
	if want, have := http.StatusOK, resp.StatusCode; want != have {
		return fmt.Errorf("want /metrics status code %d, have %d. Body:\n%s", want, have, b)
	}
	return nil
}

func runCommandAndTests(cmd *exec.Cmd, fn func(pid int) error) error {
	errc := make(chan error)
	go func() {
		if err := cmd.Run(); err != nil {
			errc <- fmt.Errorf("execution of command failed: %s", err)
		} else {
			errc <- nil
		}
	}()

	// Allow the process to start before running any tests.
	select {
	case err := <-errc:
		return err
	case <-time.After(100 * time.Millisecond):
	}

	go func(pid int) {
		errc <- fn(pid)
	}(cmd.Process.Pid)

	select {
	case err := <-errc:
		if cmd.Process != nil {
			cmd.Process.Kill()
		}
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package battery

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"os"
	"strconv"

	"github.com/fatih/color"
)

// LENGTH: 8
var chars = []rune{
	'▏',
	'▎',
	'▍',
	'▌',
	'▋',
	'▊',
	'▉',
	'█',
}

type Bar struct {
	buffer      bytes.Buffer
	Gauge       []rune
	GaugeWidth  int
	width       int
	nowVal      int
	totalVal    int
	charLen     int
	format      string
	prefix      rune
	postfix     rune
	charge      string
	ShowPercent bool
	ShowCounter bool
	Showthunder bool
	EnableColor bool
	EnableTmux  bool
}

func digit(num int) string {
	return strconv.Itoa(int(math.Log10(float64(num))) + 1)
}

func New(total int) *Bar {
	if total <= 0 {
		panic(errors.New("Please specify total size that is greater than zero"))
	}
	bar := &Bar{
		buffer:      bytes.Buffer{},
		totalVal:    total,
		nowVal:      -1,
		charLen:     len(chars),
		format:      "%s",
		prefix:      '|',
		postfix:     '|',
		charge:      "⚡",
		ShowPercent: true,
		ShowCounter: true,
		Showthunder: false,
		EnableColor: false,
		EnableTmux:  true,
	}
	return bar.SetWidth(3)
}

func (bar *Bar) SetPrefix(char rune) *Bar {
	bar.prefix = char
	return bar
}

func (bar *Bar) SetPostfix(char rune) *Bar {
	bar.postfix = char
	return bar
}

func (bar *Bar) SetWidth(width int) *Bar {
	bar.width = width
	// +1 for postfix
	bar.GaugeWidth = width + 1
	// +1 for prefix
	bar.Gauge = make([]rune, bar.GaugeWidth+1, bar.GaugeWidth+1)
	return bar
}

func (bar *Bar) Set(set int) *Bar {
	bar.nowVal = set
	return bar
}

func (bar *Bar) Run() {
	bar.writer()
}

func (bar *Bar) writer() {
	if bar.ShowPercent {
		bar.format = "%3d%%" + bar.format
	}

	if bar.ShowCounter {
		digit := digit(bar.totalVal)
		bar.format += " %" + digit + "d/%" + digit + "d"
	}

	// for thunder
	bar.format = "%s" + bar.format

	if bar.nowVal <= bar.totalVal {
		bar.print()
	}

}

func (bar *Bar) print() {
	frac := float64(bar.nowVal) / float64(bar.totalVal)
	barLen, fracBarLen := bar.divmod(frac)

	// append prefix
	bar.Gauge[0] = bar.prefix

	// append full block
	for i := 1; i < barLen; i++ {
		bar.Gauge[i] = chars[bar.charLen-1]
	}

	// append lower block
	bar.Gauge[barLen] = chars[fracBarLen]

	// padding with whitespace
	for i := barLen + 1; i < bar.GaugeWidth; i++ {
		bar.Gauge[i] = ' '
	}

	// append postfix
	bar.Gauge[bar.GaugeWidth] = bar.postfix

	bar.write(frac)
}

func (bar *Bar) write(frac float64) {
	var args []interface{}
	percent := int(frac * 100)

	if bar.Showthunder {
		args = append(args, bar.charge)
	} else {
		args = append(args, "  ")
	}

	if bar.ShowPercent {
		args = append(args, percent)
	}

	args = append(args, string(bar.Gauge))

	if bar.ShowCounter {
		args = append(args, bar.nowVal)
		args = append(args, bar.totalVal)
	}

	if bar.EnableColor {
		if bar.EnableTmux {
			bar.colorTmuxPrint(percent, args...)
		} else {
			bar.colorPrint(percent, args...)
		}
	} else {
		fmt.Fprintf(&bar.buffer, bar.format, args...)
	}
	os.Stdout.Write(bar.buffer.Bytes())
}

func (bar *Bar) colorTmuxPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		bar.format = "#[fg=green]" + bar.format
	} else if 20 <= percent && percent < 60 {
		bar.format = "#[fg=yellow]" + bar.format
	} else {
		bar.format = "#[fg=red]" + bar.format
	}
	fmt.Fprintf(&bar.buffer, bar.format+"#[default]", args...)
}

func (bar *Bar) colorPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		bar.buffer.WriteString(color.GreenString(bar.format, args...))
	} else if 20 <= percent && percent < 60 {
		bar.buffer.WriteString(color.YellowString(bar.format, args...))
	} else {
		bar.buffer.WriteString(color.RedString(bar.format, args...))
	}
}

func (bar *Bar) divmod(frac float64) (int, int) {
	// Over 100%
	if frac >= 1.0 {
		return bar.width, bar.charLen - 1
	}
	pre := int(frac * float64(bar.width) * float64(bar.charLen))
	return pre/bar.charLen + 1, pre % bar.charLen
}

func unshift() {

}
<commit_msg>Windows doesn't display colors<commit_after>package battery

import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"strconv"

	"github.com/fatih/color"
)

// LENGTH: 8
var chars = []rune{
	'▏',
	'▎',
	'▍',
	'▌',
	'▋',
	'▊',
	'▉',
	'█',
}

type Bar struct {
	buffer      bytes.Buffer
	Gauge       []rune
	GaugeWidth  int
	width       int
	nowVal      int
	totalVal    int
	charLen     int
	format      string
	prefix      rune
	postfix     rune
	charge      string
	ShowPercent bool
	ShowCounter bool
	Showthunder bool
	EnableColor bool
	EnableTmux  bool
}

func digit(num int) string {
	return strconv.Itoa(int(math.Log10(float64(num))) + 1)
}

func New(total int) *Bar {
	if total <= 0 {
		panic(errors.New("Please specify total size that is greater than zero"))
	}
	bar := &Bar{
		buffer:      bytes.Buffer{},
		totalVal:    total,
		nowVal:      -1,
		charLen:     len(chars),
		format:      "%s",
		prefix:      '|',
		postfix:     '|',
		charge:      "⚡",
		ShowPercent: true,
		ShowCounter: true,
		Showthunder: false,
		EnableColor: false,
		EnableTmux:  true,
	}
	return bar.SetWidth(3)
}

func (bar *Bar) SetPrefix(char rune) *Bar {
	bar.prefix = char
	return bar
}

func (bar *Bar) SetPostfix(char rune) *Bar {
	bar.postfix = char
	return bar
}

func (bar *Bar) SetWidth(width int) *Bar {
	bar.width = width
	// +1 for postfix
	bar.GaugeWidth = width + 1
	// +1 for prefix
	bar.Gauge = make([]rune, bar.GaugeWidth+1, bar.GaugeWidth+1)
	return bar
}

func (bar *Bar) Set(set int) *Bar {
	bar.nowVal = set
	return bar
}

func (bar *Bar) Run() {
	bar.writer()
}

func (bar *Bar) writer() {
	if bar.ShowPercent {
		bar.format = "%3d%%" + bar.format
	}

	if bar.ShowCounter {
		digit := digit(bar.totalVal)
		bar.format += " %" + digit + "d/%" + digit + "d"
	}

	// for thunder
	bar.format = "%s" + bar.format

	if bar.nowVal <= bar.totalVal {
		bar.print()
	}

}

func (bar *Bar) print() {
	frac := float64(bar.nowVal) / float64(bar.totalVal)
	barLen, fracBarLen := bar.divmod(frac)

	// append prefix
	bar.Gauge[0] = bar.prefix

	// append full block
	for i := 1; i < barLen; i++ {
		bar.Gauge[i] = chars[bar.charLen-1]
	}

	// append lower block
	bar.Gauge[barLen] = chars[fracBarLen]

	// padding with whitespace
	for i := barLen + 1; i < bar.GaugeWidth; i++ {
		bar.Gauge[i] = ' '
	}

	// append postfix
	bar.Gauge[bar.GaugeWidth] = bar.postfix

	bar.write(frac)
}

func (bar *Bar) write(frac float64) {
	var args []interface{}
	percent := int(frac * 100)

	if bar.Showthunder {
		args = append(args, bar.charge)
	} else {
		args = append(args, "  ")
	}

	if bar.ShowPercent {
		args = append(args, percent)
	}

	args = append(args, string(bar.Gauge))

	if bar.ShowCounter {
		args = append(args, bar.nowVal)
		args = append(args, bar.totalVal)
	}

	if bar.EnableColor {
		if bar.EnableTmux {
			bar.colorTmuxPrint(percent, args...)
		} else {
			bar.colorPrint(percent, args...)
		}
	} else {
		fmt.Fprintf(&bar.buffer, bar.format, args...)
	}
	color.Output.Write(bar.buffer.Bytes())
}

func (bar *Bar) colorTmuxPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		bar.format = "#[fg=green]" + bar.format
	} else if 20 <= percent && percent < 60 {
		bar.format = "#[fg=yellow]" + bar.format
	} else {
		bar.format = "#[fg=red]" + bar.format
	}
	fmt.Fprintf(&bar.buffer, bar.format+"#[default]", args...)
}

func (bar *Bar) colorPrint(percent int, args ...interface{}) {
	if percent >= 60 {
		bar.buffer.WriteString(color.GreenString(bar.format, args...))
	} else if 20 <= percent && percent < 60 {
		bar.buffer.WriteString(color.YellowString(bar.format, args...))
	} else {
		bar.buffer.WriteString(color.RedString(bar.format, args...))
	}
}

func (bar *Bar) divmod(frac float64) (int, int) {
	// Over 100%
	if frac >= 1.0 {
		return bar.width, bar.charLen - 1
	}
	pre := int(frac * float64(bar.width) * float64(bar.charLen))
	return pre/bar.charLen + 1, pre % bar.charLen
}

func unshift() {

}
<|endoftext|>


<commit_before>package version

// Version contains the LXD version number
var Version = "3.23"
<commit_msg>Release LXD 4.0.0<commit_after>package version

// Version contains the LXD version number
var Version = "4.0.0"
<|endoftext|>


<commit_before>package git

import (
	tlog "github.com/heysquirrel/tribe/log"
	"github.com/heysquirrel/tribe/shell"
	"log"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

type Contributor struct {
	Name         string
	Count        int
	RelativeDate string
	UnixTime     int
}

type RelatedFile struct {
	Name         string
	Count        int
	RelativeDate string
	UnixTime     int
}

type byRelevance []*RelatedFile

func (a byRelevance) Len() int      { return len(a) }
func (a byRelevance) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byRelevance) Less(i, j int) bool {
	if a[i].UnixTime == a[j].UnixTime {
		return a[i].Count < a[j].Count
	}

	return a[i].UnixTime < a[j].UnixTime
}

type Repo struct {
	shell  *shell.Shell
	logger *tlog.Log
}

func (repo *Repo) git(args ...string) (string, error) {
	return repo.shell.Exec("git", args...)
}

func New(dir string, logger *tlog.Log) (*Repo, error) {
	temp := shell.New(dir, logger)
	out, err := temp.Exec("git", "rev-parse", "--show-toplevel")
	if err != nil {
		return nil, err
	}

	repo := new(Repo)
	repo.shell = shell.New(strings.TrimSpace(out), logger)
	repo.logger = logger

	return repo, err
}

func (repo *Repo) Changes() []string {
	var results = make([]string, 1)

	cmdOut, err := repo.git("status", "--porcelain")
	if err != nil {
		repo.logger.Add(err.Error())
		return results
	}

	output := strings.Split(cmdOut, "\n")
	for _, change := range output {
		if len(change) > 0 {
			results = append(results, change[3:len(change)])
		}
	}

	return results
}

func (repo *Repo) RelatedFiles(filename string) []*RelatedFile {
	files := make([]*RelatedFile, 0)
	namedFiles := make(map[string]*RelatedFile)

	if len(filename) == 0 {
		return files
	}

	out, err := repo.git("log", "--pretty=format:%H", "--follow", filename)
	if err != nil {
		repo.logger.Add(err.Error())
	}

	output := strings.Split(out, "\n")
	for _, sha := range output {
		out, err = repo.git("show", "--pretty=format:%ar%m%at", "--name-only", sha)
		if err != nil {
			repo.logger.Add(err.Error())
		}
		lines := strings.Split(out, "\n")
		dateData := strings.Split(lines[0], ">")

		for _, file := range lines[1:] {
			if len(strings.TrimSpace(file)) == 0 || file == filename {
				continue
			}

			relatedFile, ok := namedFiles[file]
			if ok {
				relatedFile.Count += 1
			} else {
				relatedFile := new(RelatedFile)
				relatedFile.Name = file
				relatedFile.Count = 1
				relatedFile.RelativeDate = dateData[0]
				relatedFile.UnixTime, err = strconv.Atoi(dateData[1])
				if err != nil {
					repo.logger.Add(err.Error())
				}

				namedFiles[file] = relatedFile
				files = append(files, relatedFile)
			}
		}
	}

	sort.Sort(sort.Reverse(byRelevance(files)))
	return files
}

func (repo *Repo) RelevantWorkItems(filename string) []string {
	workItems := make([]string, 0)

	if len(filename) == 0 {
		return workItems
	}

	out, err := repo.git("log", "--pretty=format:%s", "--follow", filename)
	if err != nil {
		repo.logger.Add(err.Error())
	}

	subjects := strings.Split(out, "\n")
	re := regexp.MustCompile("(S|DE)[0-9]+")

	for _, subjects := range subjects {
		found := re.FindString(subjects)
		if len(found) > 0 {
			workItems = append(workItems, found)
		}
	}

	return workItems
}

func (repo *Repo) RecentContributors(filename string) []*Contributor {
	contributors := make([]*Contributor, 0)
	namedContributors := make(map[string]*Contributor)

	if len(filename) == 0 {
		return contributors
	}

	out, err := repo.git("log", "--pretty=format:%aN%m%ar%m%at", "--follow", filename)
	if err != nil {
		repo.logger.Add(err.Error())
	}

	output := strings.Split(out, "\n")
	for _, line := range output {
		if len(line) > 0 {
			contributorData := strings.Split(line, ">")
			name := strings.TrimSpace(contributorData[0])

			contributor, ok := namedContributors[name]
			if ok {
				contributor.Count += 1
			} else {
				contributor := new(Contributor)
				contributor.Name = name
				contributor.Count = 1
				contributor.RelativeDate = contributorData[1]
				contributor.UnixTime, err = strconv.Atoi(contributorData[2])
				if err != nil {
					log.Panicln(err)
				}

				namedContributors[name] = contributor
				contributors = append(contributors, contributor)
			}
		}
	}

	return contributors
}
<commit_msg>Cleanup of FormattedID regular expression<commit_after>package git

import (
	tlog "github.com/heysquirrel/tribe/log"
	"github.com/heysquirrel/tribe/shell"
	"log"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

type Contributor struct {
	Name         string
	Count        int
	RelativeDate string
	UnixTime     int
}

type RelatedFile struct {
	Name         string
	Count        int
	RelativeDate string
	UnixTime     int
}

type byRelevance []*RelatedFile

func (a byRelevance) Len() int      { return len(a) }
func (a byRelevance) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byRelevance) Less(i, j int) bool {
	if a[i].UnixTime == a[j].UnixTime {
		return a[i].Count < a[j].Count
	}

	return a[i].UnixTime < a[j].UnixTime
}

type Repo struct {
	shell  *shell.Shell
	logger *tlog.Log
}

func (repo *Repo) git(args ...string) (string, error) {
	return repo.shell.Exec("git", args...)
}

func New(dir string, logger *tlog.Log) (*Repo, error) {
	temp := shell.New(dir, logger)
	out, err := temp.Exec("git", "rev-parse", "--show-toplevel")
	if err != nil {
		return nil, err
	}

	repo := new(Repo)
	repo.shell = shell.New(strings.TrimSpace(out), logger)
	repo.logger = logger

	return repo, err
}

func (repo *Repo) Changes() []string {
	var results = make([]string, 1)

	cmdOut, err := repo.git("status", "--porcelain")
	if err != nil {
		repo.logger.Add(err.Error())
		return results
	}

	output := strings.Split(cmdOut, "\n")
	for _, change := range output {
		if len(change) > 0 {
			results = append(results, change[3:len(change)])
		}
	}

	return results
}

func (repo *Repo) RelatedFiles(filename string) []*RelatedFile {
	files := make([]*RelatedFile, 0)
	namedFiles := make(map[string]*RelatedFile)

	if len(filename) == 0 {
		return files
	}

	out, err := repo.git("log", "--pretty=format:%H", "--follow", filename)
	if err != nil {
		repo.logger.Add(err.Error())
	}

	output := strings.Split(out, "\n")
	for _, sha := range output {
		out, err = repo.git("show", "--pretty=format:%ar%m%at", "--name-only", sha)
		if err != nil {
			repo.logger.Add(err.Error())
		}
		lines := strings.Split(out, "\n")
		dateData := strings.Split(lines[0], ">")

		for _, file := range lines[1:] {
			if len(strings.TrimSpace(file)) == 0 || file == filename {
				continue
			}

			relatedFile, ok := namedFiles[file]
			if ok {
				relatedFile.Count += 1
			} else {
				relatedFile := new(RelatedFile)
				relatedFile.Name = file
				relatedFile.Count = 1
				relatedFile.RelativeDate = dateData[0]
				relatedFile.UnixTime, err = strconv.Atoi(dateData[1])
				if err != nil {
					repo.logger.Add(err.Error())
				}

				namedFiles[file] = relatedFile
				files = append(files, relatedFile)
			}
		}
	}

	sort.Sort(sort.Reverse(byRelevance(files)))
	return files
}

func (repo *Repo) RelevantWorkItems(filename string) []string {
	workItems := make([]string, 0)

	if len(filename) == 0 {
		return workItems
	}

	out, err := repo.git("log", "--pretty=format:%s", "--follow", filename)
	if err != nil {
		repo.logger.Add(err.Error())
	}

	subjects := strings.Split(out, "\n")
	re := regexp.MustCompile("(S|DE)[0-9][0-9]+")

	for _, subjects := range subjects {
		found := re.FindString(subjects)
		if len(found) > 0 {
			workItems = append(workItems, found)
		}
	}

	return workItems
}

func (repo *Repo) RecentContributors(filename string) []*Contributor {
	contributors := make([]*Contributor, 0)
	namedContributors := make(map[string]*Contributor)

	if len(filename) == 0 {
		return contributors
	}

	out, err := repo.git("log", "--pretty=format:%aN%m%ar%m%at", "--follow", filename)
	if err != nil {
		repo.logger.Add(err.Error())
	}

	output := strings.Split(out, "\n")
	for _, line := range output {
		if len(line) > 0 {
			contributorData := strings.Split(line, ">")
			name := strings.TrimSpace(contributorData[0])

			contributor, ok := namedContributors[name]
			if ok {
				contributor.Count += 1
			} else {
				contributor := new(Contributor)
				contributor.Name = name
				contributor.Count = 1
				contributor.RelativeDate = contributorData[1]
				contributor.UnixTime, err = strconv.Atoi(contributorData[2])
				if err != nil {
					log.Panicln(err)
				}

				namedContributors[name] = contributor
				contributors = append(contributors, contributor)
			}
		}
	}

	return contributors
}
<|endoftext|>


<commit_before>package collector

import (
	"errors"
	"strings"

	"github.com/fsouza/go-dockerclient"
)

const appLabel = "collectd_docker_app"
const appLocationLabel = "collectd_docker_app_label"
const taskLabel = "collectd_docker_task"
const taskLocationLabel = "collectd_docker_task_label"

const appEnvPrefix = "COLLECTD_DOCKER_APP="
const appEnvLocationPrefix = "COLLECTD_DOCKER_APP_ENV="
const appEnvLocationTrimPrefix = "COLLECTD_DOCKER_APP_ENV_TRIM_PREFIX="
const taskEnvPrefix = "COLLECTD_DOCKER_TASK="
const taskEnvLocationPrefix = "COLLECTD_DOCKER_TASK_ENV="
const taskEnvLocationTrimPrefix = "COLLECTD_DOCKER_TASK_ENV_TRIM_PREFIX="

const defaultTask = "default"

// ErrNoNeedToMonitor is used to skip containers
// that shouldn't be monitored by collectd
var ErrNoNeedToMonitor = errors.New("container is not supposed to be monitored")

// MonitorDockerClient represents restricted interface for docker client
// that is used in monitor, docker.Client is a subset of this interface
type MonitorDockerClient interface {
	InspectContainer(id string) (*docker.Container, error)
	Stats(opts docker.StatsOptions) error
}

// Monitor is responsible for monitoring of a single container (task)
type Monitor struct {
	client   MonitorDockerClient
	id       string
	app      string
	task     string
	interval int
}

// NewMonitor creates new monitor with specified docker client,
// container id and stat updating interval
func NewMonitor(c MonitorDockerClient, id string, interval int) (*Monitor, error) {
	container, err := c.InspectContainer(id)
	if err != nil {
		return nil, err
	}

	app := sanitizeForGraphite(extractApp(container))
	if app == "" {
		return nil, ErrNoNeedToMonitor
	}

	task := sanitizeForGraphite(extractTask(container))

	return &Monitor{
		client:   c,
		id:       container.ID,
		app:      app,
		task:     task,
		interval: interval,
	}, nil
}

func (m *Monitor) handle(ch chan<- Stats) error {
	in := make(chan *docker.Stats)

	go func() {
		i := 0
		for s := range in {
			if i%m.interval != 0 {
				i++
				continue
			}

			ch <- Stats{
				App:   m.app,
				Task:  m.task,
				Stats: *s,
			}

			i++
		}
	}()

	return m.client.Stats(docker.StatsOptions{
		ID:     m.id,
		Stats:  in,
		Stream: true,
	})
}

func extractApp(c *docker.Container) string {
	app := ""

	location := extractMetadata(c, appLocationLabel, appEnvLocationPrefix, "")
	if location != "" {
		app = extractMetadata(c, location, location+"=", "")
	} else {
		app = extractMetadata(c, appLabel, appEnvPrefix, "")
	}

	prefix := extractEnv(c, appEnvLocationTrimPrefix)
	if prefix != "" {
		return strings.TrimPrefix(app, prefix)
	}

	return app
}

func extractTask(c *docker.Container) string {
	task := defaultTask

	location := extractMetadata(c, taskLocationLabel, taskEnvLocationPrefix, "")
	if location != "" {
		task = extractMetadata(c, location, location+"=", defaultTask)
	} else {
		task = extractMetadata(c, taskLabel, taskEnvPrefix, defaultTask)
	}

	prefix := extractEnv(c, taskEnvLocationTrimPrefix)
	if prefix != "" {
		return strings.TrimPrefix(task, prefix)
	}

	return task
}

func extractMetadata(c *docker.Container, label, envPrefix, missing string) string {
	if app, ok := c.Config.Labels[label]; ok {
		return app
	}

	env := extractEnv(c, envPrefix)
	if env != "" {
		return env
	}

	return missing
}

func extractEnv(c *docker.Container, envPrefix string) string {
	for _, e := range c.Config.Env {
		if strings.HasPrefix(e, envPrefix) {
			return strings.TrimPrefix(e, envPrefix)
		}
	}

	return ""
}

func sanitizeForGraphite(s string) string {
	return strings.Replace(s, ".", "_", -1)
}
<commit_msg>Add support for setting labels/env keys via environment variables<commit_after>package collector

import (
	"errors"
	"strings"
	"os"

	"github.com/fsouza/go-dockerclient"
)

func Getenv(env string, defaultValue string) string {
  var value = os.Getenv(env)
  if len(value) > 0 {
    return value
  } else {
    return defaultValue
  }
}

var appLabel = Getenv("KEY_APP_LABEL", "collectd_docker_app")
var appLocationLabel = "collectd_docker_app_label"
var taskLabel = Getenv("KEY_TASK_LABEL", "collectd_docker_task")
var taskLocationLabel = "collectd_docker_task_label"

var appEnvPrefix = Getenv("KEY_APP_ENV", "COLLECTD_DOCKER_APP") + "="
var appEnvLocationPrefix = "COLLECTD_DOCKER_APP_ENV="
var appEnvLocationTrimPrefix = "COLLECTD_DOCKER_APP_ENV_TRIM_PREFIX="
var taskEnvPrefix = Getenv("KEY_TASK_ENV", "COLLECTD_DOCKER_TASK") + "="
var taskEnvLocationPrefix = "COLLECTD_DOCKER_TASK_ENV="
var taskEnvLocationTrimPrefix = "COLLECTD_DOCKER_TASK_ENV_TRIM_PREFIX="

const defaultTask = "default"

// ErrNoNeedToMonitor is used to skip containers
// that shouldn't be monitored by collectd
var ErrNoNeedToMonitor = errors.New("container is not supposed to be monitored")

// MonitorDockerClient represents restricted interface for docker client
// that is used in monitor, docker.Client is a subset of this interface
type MonitorDockerClient interface {
	InspectContainer(id string) (*docker.Container, error)
	Stats(opts docker.StatsOptions) error
}

// Monitor is responsible for monitoring of a single container (task)
type Monitor struct {
	client   MonitorDockerClient
	id       string
	app      string
	task     string
	interval int
}

// NewMonitor creates new monitor with specified docker client,
// container id and stat updating interval
func NewMonitor(c MonitorDockerClient, id string, interval int) (*Monitor, error) {
	container, err := c.InspectContainer(id)
	if err != nil {
		return nil, err
	}

	app := sanitizeForGraphite(extractApp(container))
	if app == "" {
		return nil, ErrNoNeedToMonitor
	}

	task := sanitizeForGraphite(extractTask(container))

	return &Monitor{
		client:   c,
		id:       container.ID,
		app:      app,
		task:     task,
		interval: interval,
	}, nil
}

func (m *Monitor) handle(ch chan<- Stats) error {
	in := make(chan *docker.Stats)

	go func() {
		i := 0
		for s := range in {
			if i%m.interval != 0 {
				i++
				continue
			}

			ch <- Stats{
				App:   m.app,
				Task:  m.task,
				Stats: *s,
			}

			i++
		}
	}()

	return m.client.Stats(docker.StatsOptions{
		ID:     m.id,
		Stats:  in,
		Stream: true,
	})
}

func extractApp(c *docker.Container) string {
	app := ""

	location := extractMetadata(c, appLocationLabel, appEnvLocationPrefix, "")
	if location != "" {
		app = extractMetadata(c, location, location+"=", "")
	} else {
		app = extractMetadata(c, appLabel, appEnvPrefix, "")
	}

	prefix := extractEnv(c, appEnvLocationTrimPrefix)
	if prefix != "" {
		return strings.TrimPrefix(app, prefix)
	}

	return app
}

func extractTask(c *docker.Container) string {
	task := defaultTask

	location := extractMetadata(c, taskLocationLabel, taskEnvLocationPrefix, "")
	if location != "" {
		task = extractMetadata(c, location, location+"=", defaultTask)
	} else {
		task = extractMetadata(c, taskLabel, taskEnvPrefix, defaultTask)
	}

	prefix := extractEnv(c, taskEnvLocationTrimPrefix)
	if prefix != "" {
		return strings.TrimPrefix(task, prefix)
	}

	return task
}

func extractMetadata(c *docker.Container, label, envPrefix, missing string) string {
	if app, ok := c.Config.Labels[label]; ok {
		return app
	}

	env := extractEnv(c, envPrefix)
	if env != "" {
		return env
	}

	return missing
}

func extractEnv(c *docker.Container, envPrefix string) string {
	for _, e := range c.Config.Env {
		if strings.HasPrefix(e, envPrefix) {
			return strings.TrimPrefix(e, envPrefix)
		}
	}

	return ""
}

func sanitizeForGraphite(s string) string {
	return strings.Replace(s, ".", "_", -1)
}
<|endoftext|>


<commit_before>package git

import (
	"log"
	"os"
	"os/exec"
)

func Init(path string) {
	if err := os.MkdirAll(path, os.ModeDir); err != nil {
		log.Fatal(err)
	}
	err := exec.Command("git", "init", path).Run()
	if err != nil {
		log.Fatal(err)
	}
}

func Add() {
}

func Commit() {
}
<commit_msg>Implement add and commit.<commit_after>package git

import (
	"log"
	"os"
	"os/exec"
)

func Init(path string) {
	if err := os.MkdirAll(path, os.ModeDir); err != nil {
		log.Fatal(err)
	}
	err := exec.Command("git", "init", path).Run()
	if err != nil {
		log.Fatal(err)
	}
}

func Add(path, filename string) {
	os.Chdir(path)
	err := exec.Command("git", "add", filename).Run()
	if err != nil {
		log.Fatal(err)
	}
}

func Commit(path, message, date string) {
	os.Chdir(path)
	err := exec.Command("git", "commit", "-m", message, "--date", date).Run()
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>