<commit_before>package gcache

import (
	"fmt"
	"net/http"

	"google.golang.org/api/drive/v3"
	"google.golang.org/api/googleapi"
)

var (
	rootFolderID string

	folderParams = &drive.File{
		Name:     rootFolderName,
		MimeType: mimeGSuiteFolder,
	}

	folderPermission *drive.Permission
	rootFolderName   string
)

// SetRootFolder sets name and permission to a top folder on Google Drive.
func SetRootFolder(
	name string,
	permission *drive.Permission,
) {
	folderPermission = permission
	rootFolderName = name
}

func getRootFolderID(
	r *http.Request,
) (
	string,
	error,
) {
	if rootFolderID != "" {
		return rootFolderID, nil
	}
	fileList, err := getFolder(r, fmt.Sprintf("name='%s'", rootFolderName), "")
	if err != nil {
		return "", err
	}

	if len(fileList.Files) == 1 {
		return fileList.Files[0].Id, nil
	}

	return createFolder(r)
}

func getFolder(
	r *http.Request,
	q string,
	field googleapi.Field,
) (
	*drive.FileList,
	error,
) {
	if q != "" {
		q = fmt.Sprintf("mimeType='%s' and (%s)", mimeGSuiteFolder, q)
	}
	if field == "" {
		field = MinimumField
	}

	var refreshToken bool
	n := 1
refresh:
	service, err := GetGDriveService(r)
	if err != nil {
		return nil, err
	}

retry:
	fileList, err := service.Files.List().PageSize(1).Spaces("drive").Q(q).Fields(field).Do()

	if err != nil {
		refreshToken, n, err = triable(n, err)
		if err != nil {
			return nil, err
		}
		if refreshToken {
			goto refresh
		}
		goto retry
	}

	return fileList, nil
}

func createFolder(
	r *http.Request,
) (
	string,
	error,
) {

	var refreshToken bool
	n := 1
refresh:
	service, err := GetGDriveService(r)
	if err != nil {
		return "", err
	}

retryFiles:
	file, err := service.Files.Create(folderParams).Do()

	if err != nil {
		refreshToken, n, err = triable(n, err)
		if err != nil {
			return "", err
		}
		if refreshToken {
			goto refresh
		}
		goto retryFiles
	}

retryPermissions:
	_, err = service.Permissions.Create(file.Id, folderPermission).Do()

	if err != nil {
		refreshToken, n, err = triable(n, err)
		if err != nil {
			return "", err
		}
		if refreshToken {
			service, err = GetGDriveService(r)
			if err != nil {
				return "", err
			}
		}
		goto retryPermissions
	}

	return file.Id, nil
}
<commit_msg>add GetGDiveFolderIDs<commit_after>package gcache

import (
	"fmt"
	"net/http"

	"google.golang.org/api/drive/v3"
	"google.golang.org/api/googleapi"
)

var (
	rootFolderID string

	folderParams = &drive.File{
		Name:     rootFolderName,
		MimeType: mimeGSuiteFolder,
	}

	folderPermission *drive.Permission
	rootFolderName   string
)

// SetRootFolder sets name and permission to a top folder on Google Drive.
func SetRootFolder(
	name string,
	permission *drive.Permission,
) {
	folderPermission = permission
	rootFolderName = name
}

func getRootFolderID(
	r *http.Request,
) (
	string,
	error,
) {
	if rootFolderID != "" {
		return rootFolderID, nil
	}
	fileList, err := getFolder(r, fmt.Sprintf("name='%s'", rootFolderName), "")
	if err != nil {
		return "", err
	}

	if len(fileList.Files) == 1 {
		return fileList.Files[0].Id, nil
	}

	return createFolder(r)
}

func getFolder(
	r *http.Request,
	q string,
	field googleapi.Field,
) (
	*drive.FileList,
	error,
) {
	if q != "" {
		q = fmt.Sprintf("mimeType='%s' and (%s)", mimeGSuiteFolder, q)
	}
	if field == "" {
		field = MinimumField
	}

	var refreshToken bool
	n := 1
refresh:
	service, err := GetGDriveService(r)
	if err != nil {
		return nil, err
	}

retry:
	fileList, err := service.Files.List().PageSize(1).Spaces("drive").Q(q).Fields(field).Do()

	if err != nil {
		refreshToken, n, err = triable(n, err)
		if err != nil {
			return nil, err
		}
		if refreshToken {
			goto refresh
		}
		goto retry
	}

	return fileList, nil
}

// GetGDiveFolderIDs returns a map of folder IDs on Google Drive.
func GetGDiveFolderIDs(
	r *http.Request,
	q string,
) (
	*map[string]string,
	error,
) {

	const field googleapi.Field = "files(" +
		"name," +
		"id)"

	fileList, err := getFolder(r, q, field)
	if err != nil {
		return nil, err
	}

	result := make(map[string]string, len(fileList.Files))
	for _, v := range fileList.Files {
		result[v.Name] = v.Id
	}
	return &result, nil
}

func createFolder(
	r *http.Request,
) (
	string,
	error,
) {

	var refreshToken bool
	n := 1
refresh:
	service, err := GetGDriveService(r)
	if err != nil {
		return "", err
	}

retryFiles:
	file, err := service.Files.Create(folderParams).Do()

	if err != nil {
		refreshToken, n, err = triable(n, err)
		if err != nil {
			return "", err
		}
		if refreshToken {
			goto refresh
		}
		goto retryFiles
	}

retryPermissions:
	_, err = service.Permissions.Create(file.Id, folderPermission).Do()

	if err != nil {
		refreshToken, n, err = triable(n, err)
		if err != nil {
			return "", err
		}
		if refreshToken {
			service, err = GetGDriveService(r)
			if err != nil {
				return "", err
			}
		}
		goto retryPermissions
	}

	return file.Id, nil
}
<|endoftext|>


<commit_before>package constants

// THIS FILE IS GENERATED AUTOMATICALLY, NO TOUCHING!!!!!

const (
	AWSBOSHURL      = "https://s3.amazonaws.com/bbl-precompiled-bosh-releases/release-bosh-260-on-ubuntu-trusty-stemcell-3312.8.tgz"
	AWSBOSHSHA1     = "76a4198741dc717b0cd2deb76394ee588e7ff209"
	BOSHAWSCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-aws-cpi-release?v=62"
	BOSHAWSCPISHA1  = "f36967927ceae09e5663a41fdda199edfe649dc6"
	AWSStemcellURL  = "https://s3.amazonaws.com/bosh-aws-light-stemcells/light-bosh-stemcell-3312.8-aws-xen-hvm-ubuntu-trusty-go_agent.tgz"
	AWSStemcellSHA1 = "50fb9765d92b5f38407bc248a2477152cbe80ebf"
	GCPBOSHURL      = "https://bosh.io/d/github.com/cloudfoundry/bosh?v=260"
	GCPBOSHSHA1     = "f8f086974d9769263078fb6cb7927655744dacbc"
	BOSHGCPCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-google-cpi-release?v=25.6.1"
	BOSHGCPCPISHA1  = "fc8c7025c3ba3aef66e005a4bdf7fd3d5e997974"
	GCPStemcellURL  = "https://s3.amazonaws.com/bosh-core-stemcells/google/bosh-stemcell-3312.9-google-kvm-ubuntu-trusty-go_agent.tgz"
	GCPStemcellSHA1 = "b9e646fe583c9de6fa524d4d81e527e6183b837a"
)
<commit_msg>Update constants<commit_after>package constants

// THIS FILE IS GENERATED AUTOMATICALLY, NO TOUCHING!!!!!

const (
	AWSBOSHURL      = "https://s3.amazonaws.com/bbl-precompiled-bosh-releases/release-bosh-260-on-ubuntu-trusty-stemcell-3312.9.tgz"
	AWSBOSHSHA1     = "1f65c3ca18b477a94ceae5532d9653b779f49244"
	BOSHAWSCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-aws-cpi-release?v=62"
	BOSHAWSCPISHA1  = "f36967927ceae09e5663a41fdda199edfe649dc6"
	AWSStemcellURL  = "https://s3.amazonaws.com/bosh-aws-light-stemcells/light-bosh-stemcell-3312.9-aws-xen-hvm-ubuntu-trusty-go_agent.tgz"
	AWSStemcellSHA1 = "bad64a03bd1b6bc85d4427cfa9b04a0a12b543a5"
	GCPBOSHURL      = "https://bosh.io/d/github.com/cloudfoundry/bosh?v=260"
	GCPBOSHSHA1     = "f8f086974d9769263078fb6cb7927655744dacbc"
	BOSHGCPCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-google-cpi-release?v=25.6.1"
	BOSHGCPCPISHA1  = "fc8c7025c3ba3aef66e005a4bdf7fd3d5e997974"
	GCPStemcellURL  = "https://s3.amazonaws.com/bosh-core-stemcells/google/bosh-stemcell-3312.9-google-kvm-ubuntu-trusty-go_agent.tgz"
	GCPStemcellSHA1 = "b9e646fe583c9de6fa524d4d81e527e6183b837a"
)
<|endoftext|>


<commit_before>package constants

// THIS FILE IS GENERATED AUTOMATICALLY, NO TOUCHING!!!!!

const (
	AWSBOSHURL      = "https://s3.amazonaws.com/bbl-precompiled-bosh-releases/release-bosh-260.3-on-ubuntu-trusty-stemcell-3312.15.tgz"
	AWSBOSHSHA1     = "16a9d5c26caf43564952285c5db8bb9bff782b91"
	BOSHAWSCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-aws-cpi-release?v=62"
	BOSHAWSCPISHA1  = "f36967927ceae09e5663a41fdda199edfe649dc6"
	AWSStemcellURL  = "https://s3.amazonaws.com/bosh-aws-light-stemcells/light-bosh-stemcell-3312.15-aws-xen-hvm-ubuntu-trusty-go_agent.tgz"
	AWSStemcellSHA1 = "f5e78f61b8026377300db544e9d3ca0b89516af3"
	GCPBOSHURL      = "https://bosh.io/d/github.com/cloudfoundry/bosh?v=260.3"
	GCPBOSHSHA1     = "22c79db2a785efa9cbc32c62b8094500e952e170"
	BOSHGCPCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-google-cpi-release?v=25.6.2"
	BOSHGCPCPISHA1  = "b4865397d867655fdcc112bc5a7f9a5025cdf311"
	GCPStemcellURL  = "https://s3.amazonaws.com/bosh-core-stemcells/google/bosh-stemcell-3312.15-google-kvm-ubuntu-trusty-go_agent.tgz"
	GCPStemcellSHA1 = "3e00695743f1be7119f032ce5264e335e95732bf"
)
<commit_msg>Update constants<commit_after>package constants

// THIS FILE IS GENERATED AUTOMATICALLY, NO TOUCHING!!!!!

const (
	AWSBOSHURL      = "https://s3.amazonaws.com/bbl-precompiled-bosh-releases/aws-release-bosh-260.3-on-ubuntu-trusty-stemcell-3312.15.tgz"
	AWSBOSHSHA1     = "9e132cbffbb5f0056ce60edd63fb7124e8190e0c"
	BOSHAWSCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-aws-cpi-release?v=62"
	BOSHAWSCPISHA1  = "f36967927ceae09e5663a41fdda199edfe649dc6"
	AWSStemcellURL  = "https://s3.amazonaws.com/bosh-aws-light-stemcells/light-bosh-stemcell-3312.15-aws-xen-hvm-ubuntu-trusty-go_agent.tgz"
	AWSStemcellSHA1 = "f5e78f61b8026377300db544e9d3ca0b89516af3"
	GCPBOSHURL      = "https://bosh.io/d/github.com/cloudfoundry/bosh?v=260.3"
	GCPBOSHSHA1     = "22c79db2a785efa9cbc32c62b8094500e952e170"
	BOSHGCPCPIURL   = "https://bosh.io/d/github.com/cloudfoundry-incubator/bosh-google-cpi-release?v=25.6.2"
	BOSHGCPCPISHA1  = "b4865397d867655fdcc112bc5a7f9a5025cdf311"
	GCPStemcellURL  = "https://s3.amazonaws.com/bosh-gce-light-stemcells/light-bosh-stemcell-3312.15-google-kvm-ubuntu-trusty-go_agent.tgz"
	GCPStemcellSHA1 = "3ac3ee83750f75bd74e8d3e3ad97808db23c30ba"
)
<|endoftext|>


<commit_before>package tcolor

import (
	"bytes"
	"fmt"
)

func Format(parts ...interface{}) (string, error) {
	return format(true, parts)
}

func FormatWithoutReset(parts ...interface{}) (string, error) {
	return format(false, parts)
}

func format(reset bool, parts ...interface{}) (string, error) {
	var b bytes.Buffer

	for i, part := range parts {
		// Just a simple piece of string to add to the result
		str, ok := part.(string)
		if ok {
			b.WriteString(str)
			continue
		}

		// Add a SGR definition
		sgr, ok := part.(Sgr)
		if ok {
			b.Write(CSI)
			b.Write(sgr.Sequence())
			b.WriteByte(SgrEnd)
			continue
		}

		return "", fmt.Errorf("Invalid part at position %d.", i)
	}

	if reset {
		b.Write(Reset.Sequence())
	}
	return b.String(), nil
}
<commit_msg>Fixed exported format wrappers.<commit_after>package tcolor

import (
	"bytes"
	"fmt"
)

func Format(parts ...interface{}) (string, error) {
	return format(true, parts...)
}

func FormatWithoutReset(parts ...interface{}) (string, error) {
	return format(false, parts...)
}

func format(reset bool, parts ...interface{}) (string, error) {
	var b bytes.Buffer

	for i, part := range parts {
		// Just a simple piece of string to add to the result
		str, strOk := part.(string)
		if strOk {
			b.WriteString(str)
			continue
		}

		// Add a SGR definition
		sgr, sgrOk := part.(Sgr)
		if sgrOk {
			b.Write(CSI)
			b.Write(sgr.Sequence())
			b.WriteByte(SgrEnd)
			continue
		}

		return "", fmt.Errorf("Invalid part at position %d. Expecting type `string` or `tcolor.Sgr`.", i)
	}

	if reset {
		b.Write(CSI)
		b.Write(Reset.Sequence())
		b.WriteByte(SgrEnd)
	}
	return b.String(), nil
}
<|endoftext|>


<commit_before>package generics_test

import (
	"io"
	"reflect"
	"testing"

	// Use the dot level import to really unleash the power of golangs library importation
	. "github.com/StabbyCutyou/generics"
)

func TestBool(t *testing.T) {
	var original bool
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt(t *testing.T) {
	var original int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt8(t *testing.T) {
	var original int8
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt16(t *testing.T) {
	var original int16
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt32(t *testing.T) {
	var original int32
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt64(t *testing.T) {
	var original int64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt(t *testing.T) {
	var original uint
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt8(t *testing.T) {
	var original uint8
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt16(t *testing.T) {
	var original uint16
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt32(t *testing.T) {
	var original uint32
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt64(t *testing.T) {
	var original uint64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestFloat32(t *testing.T) {
	var original float32
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestFloat64(t *testing.T) {
	var original float64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestComplex64(t *testing.T) {
	var original complex64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestComplex128(t *testing.T) {
	var original complex128
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestString(t *testing.T) {
	var original string
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestArray(t *testing.T) {
	var original [12]int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestSlice(t *testing.T) {
	var original []int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUintpr(t *testing.T) {
	var original uintptr
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestChan(t *testing.T) {
	var original chan struct{}
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestFunc(t *testing.T) {
	var original func(int, string, complex128)
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestReader(t *testing.T) {
	var original io.Reader
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestWriter(t *testing.T) {
	var original io.Writer
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInterface(t *testing.T) {
	var original interface{}
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestStruct(t *testing.T) {
	var original struct{}
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestG(t *testing.T) {
	var original G
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestMap(t *testing.T) {
	var original map[string]int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestPointer(t *testing.T) {
	var original *int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestValue(t *testing.T) {
	var original reflect.Value
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestError(t *testing.T) {
	var original error
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}
<commit_msg>Test alis types byte and rune<commit_after>package generics_test

import (
	"io"
	"reflect"
	"testing"

	// Use the dot level import to really unleash the power of golangs library importation
	. "github.com/StabbyCutyou/generics"
)

func TestBool(t *testing.T) {
	var original bool
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt(t *testing.T) {
	var original int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt8(t *testing.T) {
	var original int8
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt16(t *testing.T) {
	var original int16
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt32(t *testing.T) {
	var original int32
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInt64(t *testing.T) {
	var original int64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt(t *testing.T) {
	var original uint
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt8(t *testing.T) {
	var original uint8
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt16(t *testing.T) {
	var original uint16
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt32(t *testing.T) {
	var original uint32
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUInt64(t *testing.T) {
	var original uint64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestFloat32(t *testing.T) {
	var original float32
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestFloat64(t *testing.T) {
	var original float64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestComplex64(t *testing.T) {
	var original complex64
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestComplex128(t *testing.T) {
	var original complex128
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestByte(t *testing.T) {
	var original byte
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestRune(t *testing.T) {
	var original rune
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestString(t *testing.T) {
	var original string
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestArray(t *testing.T) {
	var original [12]int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestSlice(t *testing.T) {
	var original []int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestUintpr(t *testing.T) {
	var original uintptr
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestChan(t *testing.T) {
	var original chan struct{}
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestFunc(t *testing.T) {
	var original func(int, string, complex128)
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestReader(t *testing.T) {
	var original io.Reader
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestWriter(t *testing.T) {
	var original io.Writer
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestInterface(t *testing.T) {
	var original interface{}
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestStruct(t *testing.T) {
	var original struct{}
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestG(t *testing.T) {
	var original G
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestMap(t *testing.T) {
	var original map[string]int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestPointer(t *testing.T) {
	var original *int
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestValue(t *testing.T) {
	var original reflect.Value
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}

func TestError(t *testing.T) {
	var original error
	result := func(g G) G { return g }(original)
	if !reflect.DeepEqual(original, result) {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/motemen/ghq/logger"
	"golang.org/x/xerrors"
)

type getter struct {
	update, shallow, silent, ssh bool
	vcs                          string
}

func (g *getter) get(argURL string) error {
	// If argURL is a "./foo" or "../bar" form,
	// find repository name trailing after github.com/USER/.
	parts := strings.Split(argURL, string(filepath.Separator))
	if parts[0] == "." || parts[0] == ".." {
		if wd, err := os.Getwd(); err == nil {
			path := filepath.Clean(filepath.Join(wd, filepath.Join(parts...)))

			var repoPath string
			roots, err := localRepositoryRoots()
			if err != nil {
				return err
			}
			for _, r := range roots {
				p := strings.TrimPrefix(path, r+string(filepath.Separator))
				if p != path && (repoPath == "" || len(p) < len(repoPath)) {
					repoPath = p
				}
			}

			if repoPath != "" {
				// Guess it
				logger.Log("resolved", fmt.Sprintf("relative %q to %q", argURL, "https://"+repoPath))
				argURL = "https://" + repoPath
			}
		}
	}

	u, err := newURL(argURL)
	if err != nil {
		return xerrors.Errorf("Could not parse URL %q: %w", argURL, err)
	}

	if g.ssh {
		// Assume Git repository if `-p` is given.
		if u, err = convertGitURLHTTPToSSH(u); err != nil {
			return xerrors.Errorf("Could not convet URL %q: %w", u, err)
		}
	}

	remote, err := NewRemoteRepository(u)
	if err != nil {
		return err
	}

	if remote.IsValid() == false {
		return fmt.Errorf("Not a valid repository: %s", u)
	}

	return g.getRemoteRepository(remote)
}

// getRemoteRepository clones or updates a remote repository remote.
// If doUpdate is true, updates the locally cloned repository. Otherwise does nothing.
// If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn)
func (g *getter) getRemoteRepository(remote RemoteRepository) error {
	remoteURL := remote.URL()
	local, err := LocalRepositoryFromURL(remoteURL)
	if err != nil {
		return err
	}

	path := local.FullPath
	newPath := false

	_, err = os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			newPath = true
			err = nil
		}
		if err != nil {
			return err
		}
	}

	if newPath {
		logger.Log("clone", fmt.Sprintf("%s -> %s", remoteURL, path))

		vcs := vcsRegistry[g.vcs]
		repoURL := remoteURL
		if vcs == nil {
			vcs, repoURL = remote.VCS()
			if vcs == nil {
				return fmt.Errorf("Could not find version control system: %s", remoteURL)
			}
		}

		err := vcs.Clone(repoURL, path, g.shallow, g.silent)
		if err != nil {
			return err
		}
	} else {
		if g.update {
			logger.Log("update", path)
			vcs, repoPath := local.VCS()
			if vcs == nil {
				return fmt.Errorf("failed to detect VCS for %q", path)
			}
			vcs.Update(repoPath, g.silent)
		} else {
			logger.Log("exists", path)
		}
	}
	return nil
}
<commit_msg>build repoPath before vcs.Clone<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/motemen/ghq/logger"
	"golang.org/x/xerrors"
)

type getter struct {
	update, shallow, silent, ssh bool
	vcs                          string
}

func (g *getter) get(argURL string) error {
	// If argURL is a "./foo" or "../bar" form,
	// find repository name trailing after github.com/USER/.
	parts := strings.Split(argURL, string(filepath.Separator))
	if parts[0] == "." || parts[0] == ".." {
		if wd, err := os.Getwd(); err == nil {
			path := filepath.Clean(filepath.Join(wd, filepath.Join(parts...)))

			var repoPath string
			roots, err := localRepositoryRoots()
			if err != nil {
				return err
			}
			for _, r := range roots {
				p := strings.TrimPrefix(path, r+string(filepath.Separator))
				if p != path && (repoPath == "" || len(p) < len(repoPath)) {
					repoPath = p
				}
			}

			if repoPath != "" {
				// Guess it
				logger.Log("resolved", fmt.Sprintf("relative %q to %q", argURL, "https://"+repoPath))
				argURL = "https://" + repoPath
			}
		}
	}

	u, err := newURL(argURL)
	if err != nil {
		return xerrors.Errorf("Could not parse URL %q: %w", argURL, err)
	}

	if g.ssh {
		// Assume Git repository if `-p` is given.
		if u, err = convertGitURLHTTPToSSH(u); err != nil {
			return xerrors.Errorf("Could not convet URL %q: %w", u, err)
		}
	}

	remote, err := NewRemoteRepository(u)
	if err != nil {
		return err
	}

	if remote.IsValid() == false {
		return fmt.Errorf("Not a valid repository: %s", u)
	}

	return g.getRemoteRepository(remote)
}

// getRemoteRepository clones or updates a remote repository remote.
// If doUpdate is true, updates the locally cloned repository. Otherwise does nothing.
// If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn)
func (g *getter) getRemoteRepository(remote RemoteRepository) error {
	remoteURL := remote.URL()
	local, err := LocalRepositoryFromURL(remoteURL)
	if err != nil {
		return err
	}

	path := local.FullPath
	root := local.RootPath
	newPath := false

	_, err = os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			newPath = true
			err = nil
		}
		if err != nil {
			return err
		}
	}

	if newPath {
		logger.Log("clone", fmt.Sprintf("%s -> %s", remoteURL, path))
		var (
			vcs      = vcsRegistry[g.vcs]
			repoPath = path
			repoURL  = remoteURL
		)
		if vcs == nil {
			vcs, repoURL = remote.VCS()
			if vcs == nil {
				return fmt.Errorf("Could not find version control system: %s", remoteURL)
			}
			repoPath = strings.TrimSuffix(filepath.Join(root, repoURL.Host, repoURL.Path), ".git")
		}
		err := vcs.Clone(repoURL, repoPath, g.shallow, g.silent)
		if err != nil {
			return err
		}
	} else {
		if g.update {
			logger.Log("update", path)
			vcs, repoPath := local.VCS()
			if vcs == nil {
				return fmt.Errorf("failed to detect VCS for %q", path)
			}
			vcs.Update(repoPath, g.silent)
		} else {
			logger.Log("exists", path)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

var routeTests = map[string][]string{
	// User
	"/user/:nick": []string{
		"/+Akyoto",
	},

	"/user/:nick/threads": []string{
		"/+Akyoto/threads",
	},

	"/user/:nick/posts": []string{
		"/+Akyoto/posts",
	},

	"/user/:nick/soundtracks": []string{
		"/+Akyoto/soundtracks",
	},

	"/user/:nick/followers": []string{
		"/+Akyoto/followers",
	},

	"/user/:nick/stats": []string{
		"/+Akyoto/stats",
	},

	"/user/:nick/animelist": []string{
		"/+Akyoto/animelist",
	},

	"/user/:nick/animelist/anime/:id": []string{
		"/+Akyoto/animelist/7929",
	},

	"/user/:nick/animelist/watching": []string{
		"/+Akyoto/animelist/watching",
	},

	"/user/:nick/animelist/completed": []string{
		"/+Akyoto/animelist/completed",
	},

	"/user/:nick/animelist/planned": []string{
		"/+Akyoto/animelist/planned",
	},

	"/user/:nick/animelist/hold": []string{
		"/+Akyoto/animelist/hold",
	},

	"/user/:nick/animelist/dropped": []string{
		"/+Akyoto/animelist/dropped",
	},

	// Pages
	"/anime/:id": []string{
		"/anime/1",
	},

	"/thread/:id": []string{
		"/thread/HJgS7c2K",
	},

	"/post/:id": []string{
		"/post/B1RzshnK",
	},

	"/forum/:tag": []string{
		"/forum/general",
	},

	"/search/:term": []string{
		"/search/Dragon Ball",
	},

	"/soundtrack/:id": []string{
		"/soundtrack/h0ac8sKkg",
	},

	"/character/:id": []string{
		"/character/6556",
	},

	// API
	"/api/anime/:id": []string{
		"/api/anime/1",
	},

	"/api/thread/:id": []string{
		"/api/thread/HJgS7c2K",
	},

	"/api/post/:id": []string{
		"/api/post/B1RzshnK",
	},

	"/api/animelist/:id": []string{
		"/api/animelist/4J6qpK1ve",
	},

	"/api/animelist/:id/get/:item": []string{
		"/api/animelist/4J6qpK1ve/get/7929",
	},

	"/api/animelist/:id/get/:item/:property": []string{
		"/api/animelist/4J6qpK1ve/get/7929/Episodes",
	},

	"/api/settings/:id": []string{
		"/api/settings/4J6qpK1ve",
	},

	"/api/user/:id": []string{
		"/api/user/4J6qpK1ve",
	},

	"/api/emailtouser/:id": []string{
		"/api/emailtouser/e.urbach@gmail.com",
	},

	"/api/googletouser/:id": []string{
		"/api/googletouser/106530160120373282283",
	},

	"/api/facebooktouser/:id": []string{
		"/api/facebooktouser/10207576239700188",
	},

	"/api/nicktouser/:id": []string{
		"/api/nicktouser/Akyoto",
	},

	"/api/searchindex/:id": []string{
		"/api/searchindex/Anime",
	},

	"/api/analytics/:id": []string{
		"/api/analytics/4J6qpK1ve",
	},

	"/api/soundtrack/:id": []string{
		"/api/soundtrack/h0ac8sKkg",
	},

	"/api/soundcloudtosoundtrack/:id": []string{
		"/api/soundcloudtosoundtrack/145918628",
	},

	"/api/youtubetosoundtrack/:id": []string{
		"/api/youtubetosoundtrack/hU2wqJuOIp4",
	},

	"/api/userfollows/:id": []string{
		"/api/userfollows/4J6qpK1ve",
	},

	"/api/anilisttoanime/:id": []string{
		"/api/anilisttoanime/527",
	},

	"/api/animecharacters/:id": []string{
		"/api/animecharacters/323",
	},

	"/api/animeepisodes/:id": []string{
		"/api/animeepisodes/323",
	},

	"/api/character/:id": []string{
		"/api/character/6556",
	},

	"/api/pushsubscriptions/:id": []string{
		"/api/pushsubscriptions/4J6qpK1ve",
	},

	"/api/myanimelisttoanime/:id": []string{
		"/api/myanimelisttoanime/527",
	},

	// Images
	"/images/avatars/large/:file": []string{
		"/images/avatars/large/4J6qpK1ve.webp",
	},

	"/images/avatars/small/:file": []string{
		"/images/avatars/small/4J6qpK1ve.webp",
	},

	"/images/brand/:file": []string{
		"/images/brand/64.webp",
	},

	"/images/login/:file": []string{
		"/images/login/google",
	},

	"/images/cover/:file": []string{
		"/images/cover/default",
	},

	"/images/elements/:file": []string{
		"/images/elements/no-avatar.svg",
	},

	// Disable these tests because they require authorization
	"/auth/google":                                   nil,
	"/auth/google/callback":                          nil,
	"/auth/facebook":                                 nil,
	"/auth/facebook/callback":                        nil,
	"/import":                                        nil,
	"/import/anilist/animelist":                      nil,
	"/import/anilist/animelist/finish":               nil,
	"/import/myanimelist/animelist":                  nil,
	"/import/myanimelist/animelist/finish":           nil,
	"/import/kitsu/animelist":                        nil,
	"/import/kitsu/animelist/finish":                 nil,
	"/api/test/notification":                         nil,
	"/api/paypal/payment/create":                     nil,
	"/api/userfollows/:id/get/:item":                 nil,
	"/api/userfollows/:id/get/:item/:property":       nil,
	"/api/pushsubscriptions/:id/get/:item":           nil,
	"/api/pushsubscriptions/:id/get/:item/:property": nil,
	"/paypal/success":                                nil,
	"/paypal/cancel":                                 nil,
	"/anime/:id/edit":                                nil,
	"/new/thread":                                    nil,
	"/new/soundtrack":                                nil,
	"/editor":                                        nil,
	"/user":                                          nil,
	"/settings":                                      nil,
	"/extension/embed":                               nil,
}
<commit_msg>Test fix<commit_after>package main

var routeTests = map[string][]string{
	// User
	"/user/:nick": []string{
		"/+Akyoto",
	},

	"/user/:nick/threads": []string{
		"/+Akyoto/threads",
	},

	"/user/:nick/posts": []string{
		"/+Akyoto/posts",
	},

	"/user/:nick/soundtracks": []string{
		"/+Akyoto/soundtracks",
	},

	"/user/:nick/followers": []string{
		"/+Akyoto/followers",
	},

	"/user/:nick/stats": []string{
		"/+Akyoto/stats",
	},

	"/user/:nick/animelist": []string{
		"/+Akyoto/animelist",
	},

	"/user/:nick/animelist/anime/:id": []string{
		"/+Akyoto/animelist/anime/7929",
	},

	"/user/:nick/animelist/watching": []string{
		"/+Akyoto/animelist/watching",
	},

	"/user/:nick/animelist/completed": []string{
		"/+Akyoto/animelist/completed",
	},

	"/user/:nick/animelist/planned": []string{
		"/+Akyoto/animelist/planned",
	},

	"/user/:nick/animelist/hold": []string{
		"/+Akyoto/animelist/hold",
	},

	"/user/:nick/animelist/dropped": []string{
		"/+Akyoto/animelist/dropped",
	},

	// Pages
	"/anime/:id": []string{
		"/anime/1",
	},

	"/thread/:id": []string{
		"/thread/HJgS7c2K",
	},

	"/post/:id": []string{
		"/post/B1RzshnK",
	},

	"/forum/:tag": []string{
		"/forum/general",
	},

	"/search/:term": []string{
		"/search/Dragon Ball",
	},

	"/soundtrack/:id": []string{
		"/soundtrack/h0ac8sKkg",
	},

	"/character/:id": []string{
		"/character/6556",
	},

	// API
	"/api/anime/:id": []string{
		"/api/anime/1",
	},

	"/api/thread/:id": []string{
		"/api/thread/HJgS7c2K",
	},

	"/api/post/:id": []string{
		"/api/post/B1RzshnK",
	},

	"/api/animelist/:id": []string{
		"/api/animelist/4J6qpK1ve",
	},

	"/api/animelist/:id/get/:item": []string{
		"/api/animelist/4J6qpK1ve/get/7929",
	},

	"/api/animelist/:id/get/:item/:property": []string{
		"/api/animelist/4J6qpK1ve/get/7929/Episodes",
	},

	"/api/settings/:id": []string{
		"/api/settings/4J6qpK1ve",
	},

	"/api/user/:id": []string{
		"/api/user/4J6qpK1ve",
	},

	"/api/emailtouser/:id": []string{
		"/api/emailtouser/e.urbach@gmail.com",
	},

	"/api/googletouser/:id": []string{
		"/api/googletouser/106530160120373282283",
	},

	"/api/facebooktouser/:id": []string{
		"/api/facebooktouser/10207576239700188",
	},

	"/api/nicktouser/:id": []string{
		"/api/nicktouser/Akyoto",
	},

	"/api/searchindex/:id": []string{
		"/api/searchindex/Anime",
	},

	"/api/analytics/:id": []string{
		"/api/analytics/4J6qpK1ve",
	},

	"/api/soundtrack/:id": []string{
		"/api/soundtrack/h0ac8sKkg",
	},

	"/api/soundcloudtosoundtrack/:id": []string{
		"/api/soundcloudtosoundtrack/145918628",
	},

	"/api/youtubetosoundtrack/:id": []string{
		"/api/youtubetosoundtrack/hU2wqJuOIp4",
	},

	"/api/userfollows/:id": []string{
		"/api/userfollows/4J6qpK1ve",
	},

	"/api/anilisttoanime/:id": []string{
		"/api/anilisttoanime/527",
	},

	"/api/animecharacters/:id": []string{
		"/api/animecharacters/323",
	},

	"/api/animeepisodes/:id": []string{
		"/api/animeepisodes/323",
	},

	"/api/character/:id": []string{
		"/api/character/6556",
	},

	"/api/pushsubscriptions/:id": []string{
		"/api/pushsubscriptions/4J6qpK1ve",
	},

	"/api/myanimelisttoanime/:id": []string{
		"/api/myanimelisttoanime/527",
	},

	// Images
	"/images/avatars/large/:file": []string{
		"/images/avatars/large/4J6qpK1ve.webp",
	},

	"/images/avatars/small/:file": []string{
		"/images/avatars/small/4J6qpK1ve.webp",
	},

	"/images/brand/:file": []string{
		"/images/brand/64.webp",
	},

	"/images/login/:file": []string{
		"/images/login/google",
	},

	"/images/cover/:file": []string{
		"/images/cover/default",
	},

	"/images/elements/:file": []string{
		"/images/elements/no-avatar.svg",
	},

	// Disable these tests because they require authorization
	"/auth/google":                                   nil,
	"/auth/google/callback":                          nil,
	"/auth/facebook":                                 nil,
	"/auth/facebook/callback":                        nil,
	"/import":                                        nil,
	"/import/anilist/animelist":                      nil,
	"/import/anilist/animelist/finish":               nil,
	"/import/myanimelist/animelist":                  nil,
	"/import/myanimelist/animelist/finish":           nil,
	"/import/kitsu/animelist":                        nil,
	"/import/kitsu/animelist/finish":                 nil,
	"/api/test/notification":                         nil,
	"/api/paypal/payment/create":                     nil,
	"/api/userfollows/:id/get/:item":                 nil,
	"/api/userfollows/:id/get/:item/:property":       nil,
	"/api/pushsubscriptions/:id/get/:item":           nil,
	"/api/pushsubscriptions/:id/get/:item/:property": nil,
	"/paypal/success":                                nil,
	"/paypal/cancel":                                 nil,
	"/anime/:id/edit":                                nil,
	"/new/thread":                                    nil,
	"/new/soundtrack":                                nil,
	"/editor":                                        nil,
	"/user":                                          nil,
	"/settings":                                      nil,
	"/extension/embed":                               nil,
}
<|endoftext|>


<commit_before>package toast

import (
	"bytes"
	"errors"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/nu7hatch/gouuid"
	"syscall"
)

var toastTemplate *template.Template

var (
	ErrorInvalidAudio    error = errors.New("toast: invalid audio")
	ErrorInvalidDuration       = errors.New("toast: invalid duration")
)

type toastAudio string

const (
	Default        toastAudio = "ms-winsoundevent:Notification.Default"
	IM                        = "ms-winsoundevent:Notification.IM"
	Mail                      = "ms-winsoundevent:Notification.Mail"
	Reminder                  = "ms-winsoundevent:Notification.Reminder"
	SMS                       = "ms-winsoundevent:Notification.SMS"
	LoopingAlarm              = "ms-winsoundevent:Notification.Looping.Alarm"
	LoopingAlarm2             = "ms-winsoundevent:Notification.Looping.Alarm2"
	LoopingAlarm3             = "ms-winsoundevent:Notification.Looping.Alarm3"
	LoopingAlarm4             = "ms-winsoundevent:Notification.Looping.Alarm4"
	LoopingAlarm5             = "ms-winsoundevent:Notification.Looping.Alarm5"
	LoopingAlarm6             = "ms-winsoundevent:Notification.Looping.Alarm6"
	LoopingAlarm7             = "ms-winsoundevent:Notification.Looping.Alarm7"
	LoopingAlarm8             = "ms-winsoundevent:Notification.Looping.Alarm8"
	LoopingAlarm9             = "ms-winsoundevent:Notification.Looping.Alarm9"
	LoopingAlarm10            = "ms-winsoundevent:Notification.Looping.Alarm10"
	LoopingCall               = "ms-winsoundevent:Notification.Looping.Call"
	LoopingCall2              = "ms-winsoundevent:Notification.Looping.Call2"
	LoopingCall3              = "ms-winsoundevent:Notification.Looping.Call3"
	LoopingCall4              = "ms-winsoundevent:Notification.Looping.Call4"
	LoopingCall5              = "ms-winsoundevent:Notification.Looping.Call5"
	LoopingCall6              = "ms-winsoundevent:Notification.Looping.Call6"
	LoopingCall7              = "ms-winsoundevent:Notification.Looping.Call7"
	LoopingCall8              = "ms-winsoundevent:Notification.Looping.Call8"
	LoopingCall9              = "ms-winsoundevent:Notification.Looping.Call9"
	LoopingCall10             = "ms-winsoundevent:Notification.Looping.Call10"
	Silent                    = "silent"
)

type toastDuration string

const (
	Short toastDuration = "short"
	Long                = "long"
)

func init() {
	toastTemplate = template.New("toast")
	toastTemplate.Parse(`
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
[Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null

$APP_ID = '{{if .AppID}}{{.AppID}}{{else}}Windows App{{end}}'

$template = @"
<toast activationType="{{.ActivationType}}" launch="{{.ActivationArguments}}" duration="{{.Duration}}">
    <visual>
        <binding template="ToastGeneric">
            {{if .Icon}}
            <image placement="appLogoOverride" src="{{.Icon}}" />
            {{end}}
            {{if .Title}}
            <text><![CDATA[{{.Title}}]]></text>
            {{end}}
            {{if .Message}}
            <text><![CDATA[{{.Message}}]]></text>
            {{end}}
        </binding>
    </visual>
    {{if ne .Audio "silent"}}
	<audio src="{{.Audio}}" loop="{{.Loop}}" />
	{{else}}
	<audio silent="true" />
	{{end}}
    {{if .Actions}}
    <actions>
        {{range .Actions}}
        <action activationType="{{.Type}}" content="{{.Label}}" arguments="{{.Arguments}}" />
        {{end}}
    </actions>
    {{end}}
</toast>
"@

$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
$xml.LoadXml($template)
$toast = New-Object Windows.UI.Notifications.ToastNotification $xml
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($APP_ID).Show($toast)
    `)
}

// Notification
//
// The toast notification data. The following fields are strongly recommended;
//   - AppID
//   - Title
//
// If no toastAudio is provided, then the toast notification will be silent.
// You can set the toast to have a default audio by setting "Audio" to "toast.Default", or if your go app takes
// user-provided input for audio, call the "toast.Audio(name)" func.
//
// The AppID is shown beneath the toast message (in certain cases), and above the notification within the Action
// Center - and is used to group your notifications together. It is recommended that you provide a "pretty"
// name for your app, and not something like "com.example.MyApp".
//
// If no Title is provided, but a Message is, the message will display as the toast notification's title -
// which is a slightly different font style (heavier).
//
// The Icon should be an absolute path to the icon (as the toast is invoked from a temporary path on the user's
// system, not the working directory).
//
// If you would like the toast to call an external process/open a webpage, then you can set ActivationArguments
// to the uri you would like to trigger when the toast is clicked. For example: "https://google.com" would open
// the Google homepage when the user clicks the toast notification.
// By default, clicking the toast just hides/dismisses it.
//
// The following would show a notification to the user letting them know they received an email, and opens
// gmail.com when they click the notification. It also makes the Windows 10 "mail" sound effect.
//
//     toast := toast.Notification{
//         AppID:               "Google Mail",
//         Title:               email.Subject,
//         Message:             email.Preview,
//         Icon:                "C:/Program Files/Google Mail/icons/logo.png",
//         ActivationArguments: "https://gmail.com",
//         Audio:               toast.Mail,
//     }
//
//     err := toast.Push()
type Notification struct {
	// The name of your app. This value shows up in Windows 10's Action Centre, so make it
	// something readable for your users. It can contain spaces, however special characters
	// (eg. é) are not supported.
	AppID string

	// The main title/heading for the toast notification.
	Title string

	// The single/multi line message to display for the toast notification.
	Message string

	// An optional path to an image on the OS to display to the left of the title & message.
	Icon string

	// The type of notification level action (like toast.Action)
	ActivationType string

	// The activation/action arguments (invoked when the user clicks the notification)
	ActivationArguments string

	// Optional action buttons to display below the notification title & message.
	Actions []Action

	// The audio to play when displaying the toast
	Audio toastAudio

	// Whether to loop the audio (default false)
	Loop bool

	// How long the toast should show up for (short/long)
	Duration toastDuration
}

// Action
//
// Defines an actionable button.
// See https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-adaptive-interactive-toasts for more info.
//
// Only protocol type action buttons are actually useful, as there's no way of receiving feedback from the
// user's choice. Examples of protocol type action buttons include: "bingmaps:?q=sushi" to open up Windows 10's
// maps app with a pre-populated search field set to "sushi".
//
//     toast.Action{"protocol", "Open Maps", "bingmaps:?q=sushi"}
type Action struct {
	Type      string
	Label     string
	Arguments string
}

func (n *Notification) applyDefaults() {
	if n.ActivationType == "" {
		n.ActivationType = "protocol"
	}
	if n.Duration == "" {
		n.Duration = Short
	}
	if n.Audio == "" {
		n.Audio = Default
	}
}

func (n *Notification) buildXML() (string, error) {
	var out bytes.Buffer
	err := toastTemplate.Execute(&out, n)
	if err != nil {
		return "", err
	}
	return out.String(), nil
}

// Builds the Windows PowerShell script & invokes it, causing the toast to display.
//
// Note: Running the PowerShell script is by far the slowest process here, and can take a few
// seconds in some cases.
//
//     notification := toast.Notification{
//         AppID: "Example App",
//         Title: "My notification",
//         Message: "Some message about how important something is...",
//         Icon: "go.png",
//         Actions: []toast.Action{
//             {"protocol", "I'm a button", ""},
//             {"protocol", "Me too!", ""},
//         },
//     }
//     err := notification.Push()
//     if err != nil {
//         log.Fatalln(err)
//     }
func (n *Notification) Push() error {
	n.applyDefaults()
	xml, err := n.buildXML()
	if err != nil {
		return err
	}
	return invokeTemporaryScript(xml)
}

// Returns a toastAudio given a user-provided input (useful for cli apps).
//
// If the "name" doesn't match, then the default toastAudio is returned, along with ErrorInvalidAudio.
//
// The following names are valid;
//   - default
//   - im
//   - mail
//   - reminder
//   - sms
//   - loopingalarm
//   - loopimgalarm[2-10]
//   - loopingcall
//   - loopingcall[2-10]
//   - silent
//
// Handle the error appropriately according to how your app should work.
func Audio(name string) (toastAudio, error) {
	switch strings.ToLower(name) {
	case "default":
		return Default, nil
	case "im":
		return IM, nil
	case "mail":
		return Mail, nil
	case "reminder":
		return Reminder, nil
	case "sms":
		return SMS, nil
	case "loopingalarm":
		return LoopingAlarm, nil
	case "loopingalarm2":
		return LoopingAlarm2, nil
	case "loopingalarm3":
		return LoopingAlarm3, nil
	case "loopingalarm4":
		return LoopingAlarm4, nil
	case "loopingalarm5":
		return LoopingAlarm5, nil
	case "loopingalarm6":
		return LoopingAlarm6, nil
	case "loopingalarm7":
		return LoopingAlarm7, nil
	case "loopingalarm8":
		return LoopingAlarm8, nil
	case "loopingalarm9":
		return LoopingAlarm9, nil
	case "loopingalarm10":
		return LoopingAlarm10, nil
	case "loopingcall":
		return LoopingCall, nil
	case "loopingcall2":
		return LoopingCall2, nil
	case "loopingcall3":
		return LoopingCall3, nil
	case "loopingcall4":
		return LoopingCall4, nil
	case "loopingcall5":
		return LoopingCall5, nil
	case "loopingcall6":
		return LoopingCall6, nil
	case "loopingcall7":
		return LoopingCall7, nil
	case "loopingcall8":
		return LoopingCall8, nil
	case "loopingcall9":
		return LoopingCall9, nil
	case "loopingcall10":
		return LoopingCall10, nil
	case "silent":
		return Silent, nil
	default:
		return Default, ErrorInvalidAudio
	}
}

// Returns a toastDuration given a user-provided input (useful for cli apps).
//
// The default duration is short. If the "name" doesn't match, then the default toastDuration is returned,
// along with ErrorInvalidDuration. Most of the time "short" is the most appropriate for a toast notification,
// and Microsoft recommend not using "long", but it can be useful for important dialogs or looping sound toasts.
//
// The following names are valid;
//   - short
//   - long
//
// Handle the error appropriately according to how your app should work.
func Duration(name string) (toastDuration, error) {
	switch strings.ToLower(name) {
	case "short":
		return Short, nil
	case "long":
		return Long, nil
	default:
		return Short, ErrorInvalidDuration
	}
}

func invokeTemporaryScript(content string) error {
	id, _ := uuid.NewV4()
	file := filepath.Join(os.TempDir(), id.String()+".ps1")
	defer os.Remove(file)
	err := ioutil.WriteFile(file, []byte(content), 0600)
	if err != nil {
		return err
	}
	cmd := exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	if err = cmd.Run(); err != nil {
		return err
	}
	return nil
}
<commit_msg>Add BOM UTF8 to support arabic in notification<commit_after>package toast

import (
	"bytes"
	"errors"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/nu7hatch/gouuid"
	"syscall"
)

var toastTemplate *template.Template

var (
	ErrorInvalidAudio    error = errors.New("toast: invalid audio")
	ErrorInvalidDuration       = errors.New("toast: invalid duration")
)

type toastAudio string

const (
	Default        toastAudio = "ms-winsoundevent:Notification.Default"
	IM                        = "ms-winsoundevent:Notification.IM"
	Mail                      = "ms-winsoundevent:Notification.Mail"
	Reminder                  = "ms-winsoundevent:Notification.Reminder"
	SMS                       = "ms-winsoundevent:Notification.SMS"
	LoopingAlarm              = "ms-winsoundevent:Notification.Looping.Alarm"
	LoopingAlarm2             = "ms-winsoundevent:Notification.Looping.Alarm2"
	LoopingAlarm3             = "ms-winsoundevent:Notification.Looping.Alarm3"
	LoopingAlarm4             = "ms-winsoundevent:Notification.Looping.Alarm4"
	LoopingAlarm5             = "ms-winsoundevent:Notification.Looping.Alarm5"
	LoopingAlarm6             = "ms-winsoundevent:Notification.Looping.Alarm6"
	LoopingAlarm7             = "ms-winsoundevent:Notification.Looping.Alarm7"
	LoopingAlarm8             = "ms-winsoundevent:Notification.Looping.Alarm8"
	LoopingAlarm9             = "ms-winsoundevent:Notification.Looping.Alarm9"
	LoopingAlarm10            = "ms-winsoundevent:Notification.Looping.Alarm10"
	LoopingCall               = "ms-winsoundevent:Notification.Looping.Call"
	LoopingCall2              = "ms-winsoundevent:Notification.Looping.Call2"
	LoopingCall3              = "ms-winsoundevent:Notification.Looping.Call3"
	LoopingCall4              = "ms-winsoundevent:Notification.Looping.Call4"
	LoopingCall5              = "ms-winsoundevent:Notification.Looping.Call5"
	LoopingCall6              = "ms-winsoundevent:Notification.Looping.Call6"
	LoopingCall7              = "ms-winsoundevent:Notification.Looping.Call7"
	LoopingCall8              = "ms-winsoundevent:Notification.Looping.Call8"
	LoopingCall9              = "ms-winsoundevent:Notification.Looping.Call9"
	LoopingCall10             = "ms-winsoundevent:Notification.Looping.Call10"
	Silent                    = "silent"
)

type toastDuration string

const (
	Short toastDuration = "short"
	Long                = "long"
)

func init() {
	toastTemplate = template.New("toast")
	toastTemplate.Parse(`
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
[Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null

$APP_ID = '{{if .AppID}}{{.AppID}}{{else}}Windows App{{end}}'

$template = @"
<toast activationType="{{.ActivationType}}" launch="{{.ActivationArguments}}" duration="{{.Duration}}">
    <visual>
        <binding template="ToastGeneric">
            {{if .Icon}}
            <image placement="appLogoOverride" src="{{.Icon}}" />
            {{end}}
            {{if .Title}}
            <text><![CDATA[{{.Title}}]]></text>
            {{end}}
            {{if .Message}}
            <text><![CDATA[{{.Message}}]]></text>
            {{end}}
        </binding>
    </visual>
    {{if ne .Audio "silent"}}
	<audio src="{{.Audio}}" loop="{{.Loop}}" />
	{{else}}
	<audio silent="true" />
	{{end}}
    {{if .Actions}}
    <actions>
        {{range .Actions}}
        <action activationType="{{.Type}}" content="{{.Label}}" arguments="{{.Arguments}}" />
        {{end}}
    </actions>
    {{end}}
</toast>
"@

$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
$xml.LoadXml($template)
$toast = New-Object Windows.UI.Notifications.ToastNotification $xml
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($APP_ID).Show($toast)
    `)
}

// Notification
//
// The toast notification data. The following fields are strongly recommended;
//   - AppID
//   - Title
//
// If no toastAudio is provided, then the toast notification will be silent.
// You can set the toast to have a default audio by setting "Audio" to "toast.Default", or if your go app takes
// user-provided input for audio, call the "toast.Audio(name)" func.
//
// The AppID is shown beneath the toast message (in certain cases), and above the notification within the Action
// Center - and is used to group your notifications together. It is recommended that you provide a "pretty"
// name for your app, and not something like "com.example.MyApp".
//
// If no Title is provided, but a Message is, the message will display as the toast notification's title -
// which is a slightly different font style (heavier).
//
// The Icon should be an absolute path to the icon (as the toast is invoked from a temporary path on the user's
// system, not the working directory).
//
// If you would like the toast to call an external process/open a webpage, then you can set ActivationArguments
// to the uri you would like to trigger when the toast is clicked. For example: "https://google.com" would open
// the Google homepage when the user clicks the toast notification.
// By default, clicking the toast just hides/dismisses it.
//
// The following would show a notification to the user letting them know they received an email, and opens
// gmail.com when they click the notification. It also makes the Windows 10 "mail" sound effect.
//
//     toast := toast.Notification{
//         AppID:               "Google Mail",
//         Title:               email.Subject,
//         Message:             email.Preview,
//         Icon:                "C:/Program Files/Google Mail/icons/logo.png",
//         ActivationArguments: "https://gmail.com",
//         Audio:               toast.Mail,
//     }
//
//     err := toast.Push()
type Notification struct {
	// The name of your app. This value shows up in Windows 10's Action Centre, so make it
	// something readable for your users. It can contain spaces, however special characters
	// (eg. é) are not supported.
	AppID string

	// The main title/heading for the toast notification.
	Title string

	// The single/multi line message to display for the toast notification.
	Message string

	// An optional path to an image on the OS to display to the left of the title & message.
	Icon string

	// The type of notification level action (like toast.Action)
	ActivationType string

	// The activation/action arguments (invoked when the user clicks the notification)
	ActivationArguments string

	// Optional action buttons to display below the notification title & message.
	Actions []Action

	// The audio to play when displaying the toast
	Audio toastAudio

	// Whether to loop the audio (default false)
	Loop bool

	// How long the toast should show up for (short/long)
	Duration toastDuration
}

// Action
//
// Defines an actionable button.
// See https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-adaptive-interactive-toasts for more info.
//
// Only protocol type action buttons are actually useful, as there's no way of receiving feedback from the
// user's choice. Examples of protocol type action buttons include: "bingmaps:?q=sushi" to open up Windows 10's
// maps app with a pre-populated search field set to "sushi".
//
//     toast.Action{"protocol", "Open Maps", "bingmaps:?q=sushi"}
type Action struct {
	Type      string
	Label     string
	Arguments string
}

func (n *Notification) applyDefaults() {
	if n.ActivationType == "" {
		n.ActivationType = "protocol"
	}
	if n.Duration == "" {
		n.Duration = Short
	}
	if n.Audio == "" {
		n.Audio = Default
	}
}

func (n *Notification) buildXML() (string, error) {
	var out bytes.Buffer
	err := toastTemplate.Execute(&out, n)
	if err != nil {
		return "", err
	}
	return out.String(), nil
}

// Builds the Windows PowerShell script & invokes it, causing the toast to display.
//
// Note: Running the PowerShell script is by far the slowest process here, and can take a few
// seconds in some cases.
//
//     notification := toast.Notification{
//         AppID: "Example App",
//         Title: "My notification",
//         Message: "Some message about how important something is...",
//         Icon: "go.png",
//         Actions: []toast.Action{
//             {"protocol", "I'm a button", ""},
//             {"protocol", "Me too!", ""},
//         },
//     }
//     err := notification.Push()
//     if err != nil {
//         log.Fatalln(err)
//     }
func (n *Notification) Push() error {
	n.applyDefaults()
	xml, err := n.buildXML()
	if err != nil {
		return err
	}
	return invokeTemporaryScript(xml)
}

// Returns a toastAudio given a user-provided input (useful for cli apps).
//
// If the "name" doesn't match, then the default toastAudio is returned, along with ErrorInvalidAudio.
//
// The following names are valid;
//   - default
//   - im
//   - mail
//   - reminder
//   - sms
//   - loopingalarm
//   - loopimgalarm[2-10]
//   - loopingcall
//   - loopingcall[2-10]
//   - silent
//
// Handle the error appropriately according to how your app should work.
func Audio(name string) (toastAudio, error) {
	switch strings.ToLower(name) {
	case "default":
		return Default, nil
	case "im":
		return IM, nil
	case "mail":
		return Mail, nil
	case "reminder":
		return Reminder, nil
	case "sms":
		return SMS, nil
	case "loopingalarm":
		return LoopingAlarm, nil
	case "loopingalarm2":
		return LoopingAlarm2, nil
	case "loopingalarm3":
		return LoopingAlarm3, nil
	case "loopingalarm4":
		return LoopingAlarm4, nil
	case "loopingalarm5":
		return LoopingAlarm5, nil
	case "loopingalarm6":
		return LoopingAlarm6, nil
	case "loopingalarm7":
		return LoopingAlarm7, nil
	case "loopingalarm8":
		return LoopingAlarm8, nil
	case "loopingalarm9":
		return LoopingAlarm9, nil
	case "loopingalarm10":
		return LoopingAlarm10, nil
	case "loopingcall":
		return LoopingCall, nil
	case "loopingcall2":
		return LoopingCall2, nil
	case "loopingcall3":
		return LoopingCall3, nil
	case "loopingcall4":
		return LoopingCall4, nil
	case "loopingcall5":
		return LoopingCall5, nil
	case "loopingcall6":
		return LoopingCall6, nil
	case "loopingcall7":
		return LoopingCall7, nil
	case "loopingcall8":
		return LoopingCall8, nil
	case "loopingcall9":
		return LoopingCall9, nil
	case "loopingcall10":
		return LoopingCall10, nil
	case "silent":
		return Silent, nil
	default:
		return Default, ErrorInvalidAudio
	}
}

// Returns a toastDuration given a user-provided input (useful for cli apps).
//
// The default duration is short. If the "name" doesn't match, then the default toastDuration is returned,
// along with ErrorInvalidDuration. Most of the time "short" is the most appropriate for a toast notification,
// and Microsoft recommend not using "long", but it can be useful for important dialogs or looping sound toasts.
//
// The following names are valid;
//   - short
//   - long
//
// Handle the error appropriately according to how your app should work.
func Duration(name string) (toastDuration, error) {
	switch strings.ToLower(name) {
	case "short":
		return Short, nil
	case "long":
		return Long, nil
	default:
		return Short, ErrorInvalidDuration
	}
}

func invokeTemporaryScript(content string) error {
	id, _ := uuid.NewV4()
	file := filepath.Join(os.TempDir(), id.String()+".ps1")
	defer os.Remove(file)
	bomUtf8 := []byte{0xEF, 0xBB, 0xBF}
	out := append(bomUtf8, []byte(content)...)
	err := ioutil.WriteFile(file, out, 0600)
	if err != nil {
		return err
	}
	cmd := exec.Command("PowerShell", "-ExecutionPolicy", "Bypass", "-File", file)
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	if err = cmd.Run(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"net/http"

	"github.com/google/go-github/github"
)

var ErrNotMergeable = errors.New("PullRequests is not mergeable.")
var ErrOutdatedMergeRef = errors.New("Merge failed because head branch has been modified.")

type PullRequests interface {
	Get(owner, repo string, number int) (*github.PullRequest, *github.Response, error)
	ListCommits(owner, repo string, number int, opt *github.ListOptions) ([]*github.RepositoryCommit, *github.Response, error)
	Merge(owner, repo string, number int, commitMessage string, opt *github.PullRequestOptions) (*github.PullRequestMergeResult, *github.Response, error)
}

type Repositories interface {
	CreateStatus(owner, repo, ref string, status *github.RepoStatus) (*github.RepoStatus, *github.Response, error)
	GetCombinedStatus(owner, repo, ref string, opt *github.ListOptions) (*github.CombinedStatus, *github.Response, error)
}

type Issues interface {
	AddLabelsToIssue(owner, repo string, number int, labels []string) ([]*github.Label, *github.Response, error)
	RemoveLabelForIssue(owner, repo string, number int, label string) (*github.Response, error)
}

func setPRHeadStatus(issueable Issueable, status *github.RepoStatus, pullRequests PullRequests, repositories Repositories) *ErrorResponse {
	pr, errResp := getPR(issueable, pullRequests)
	if errResp != nil {
		return errResp
	}
	return setStatus(pr, status, repositories)
}

func setStatus(pr *github.PullRequest, status *github.RepoStatus, repositories Repositories) *ErrorResponse {
	// I'm assuming (because the documentation on this is unclear) that the
	// status has to be reported for the Head repository. It might seem
	// weird, because why should a bot configured for the Base repository
	// have access to the Head repository, but AFAIK all forks must be
	// public and reporting statuses on public repos is always allowed.
	headRepository := HeadRepository(pr)
	_, _, err := repositories.CreateStatus(headRepository.Owner, headRepository.Name, *pr.Head.SHA, status)
	if err != nil {
		message := fmt.Sprintf("Failed to create a %s status for commit %s", *status.State, *pr.Head.SHA)
		return &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return nil
}

func getStatuses(pr *github.PullRequest, repositories Repositories) (string, []github.RepoStatus, *ErrorResponse) {
	headRepository := HeadRepository(pr)
	pageNr := 1
	statuses := []github.RepoStatus{}
	var state string
	for {
		listOptions := &github.ListOptions{
			Page: pageNr,
			// The maximum for this endpoint is higher than the default 30:
			// https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
			PerPage: 100,
		}
		combinedStatus, resp, err := repositories.GetCombinedStatus(headRepository.Owner, headRepository.Name, *pr.Head.SHA, listOptions)
		if err != nil {
			message := fmt.Sprintf("Failed to get combined status for ref %s", *pr.Head.SHA)
			return "", nil, &ErrorResponse{err, http.StatusBadGateway, message}
		}
		// Although the combined state should be the same for all pages, use
		// the combined state from the latest request, because that's always
		// the most up to date one
		state = *combinedStatus.State
		statuses = append(statuses, combinedStatus.Statuses...)
		if resp.NextPage == 0 {
			break
		}
		pageNr = resp.NextPage
	}
	return state, statuses, nil
}

func getPR(issueable Issueable, pullRequests PullRequests) (*github.PullRequest, *ErrorResponse) {
	issue := issueable.Issue()
	pr, _, err := pullRequests.Get(issue.Repository.Owner, issue.Repository.Name, issue.Number)
	if err != nil {
		message := fmt.Sprintf("Getting PR %s failed", issue.FullName())
		return nil, &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return pr, nil
}

func getCommits(issueable Issueable, pullRequests PullRequests) ([]*github.RepositoryCommit, *ErrorResponse) {
	issue := issueable.Issue()
	pageNr := 1
	commits := []*github.RepositoryCommit{}
	for {
		listOptions := &github.ListOptions{
			Page:    pageNr,
			PerPage: 30,
		}
		pageCommits, resp, err := pullRequests.ListCommits(issue.Repository.Owner, issue.Repository.Name, issue.Number, listOptions)
		if err != nil {
			message := fmt.Sprintf("Getting commits for PR %s failed", issue.FullName())
			return nil, &ErrorResponse{err, http.StatusBadGateway, message}
		}
		commits = append(commits, pageCommits...)
		if resp.NextPage == 0 {
			break
		}
		pageNr = resp.NextPage
	}
	return commits, nil
}

func addLabel(repository Repository, issueNumber int, label string, issues Issues) *ErrorResponse {
	_, _, err := issues.AddLabelsToIssue(repository.Owner, repository.Name, issueNumber, []string{label})
	if err != nil {
		message := fmt.Sprintf("Failed to set the label %s for issue #%d", label, issueNumber)
		return &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return nil
}

func removeLabel(repository Repository, issueNumber int, label string, issues Issues) *ErrorResponse {
	_, err := issues.RemoveLabelForIssue(repository.Owner, repository.Name, issueNumber, label)
	if err != nil {
		message := fmt.Sprintf("Failed to remove the label %s for issue #%d", label, issueNumber)
		return &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return nil
}

func merge(repository Repository, issueNumber int, pullRequests PullRequests) error {
	additionalCommitMessage := ""
	opt := &github.PullRequestOptions{MergeMethod: "merge"}
	result, resp, err := pullRequests.Merge(repository.Owner, repository.Name, issueNumber, additionalCommitMessage, opt)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed {
			return ErrNotMergeable
		} else if resp != nil && resp.StatusCode == http.StatusConflict {
			return ErrOutdatedMergeRef
		}
		return err
	} else if result.Merged == nil || !*result.Merged {
		return errors.New("Request successful, but PR not merged.")
	}
	return nil
}
<commit_msg>Add logging for when updating PR status<commit_after>package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"

	"github.com/google/go-github/github"
)

var ErrNotMergeable = errors.New("PullRequests is not mergeable.")
var ErrOutdatedMergeRef = errors.New("Merge failed because head branch has been modified.")

type PullRequests interface {
	Get(owner, repo string, number int) (*github.PullRequest, *github.Response, error)
	ListCommits(owner, repo string, number int, opt *github.ListOptions) ([]*github.RepositoryCommit, *github.Response, error)
	Merge(owner, repo string, number int, commitMessage string, opt *github.PullRequestOptions) (*github.PullRequestMergeResult, *github.Response, error)
}

type Repositories interface {
	CreateStatus(owner, repo, ref string, status *github.RepoStatus) (*github.RepoStatus, *github.Response, error)
	GetCombinedStatus(owner, repo, ref string, opt *github.ListOptions) (*github.CombinedStatus, *github.Response, error)
}

type Issues interface {
	AddLabelsToIssue(owner, repo string, number int, labels []string) ([]*github.Label, *github.Response, error)
	RemoveLabelForIssue(owner, repo string, number int, label string) (*github.Response, error)
}

func setPRHeadStatus(issueable Issueable, status *github.RepoStatus, pullRequests PullRequests, repositories Repositories) *ErrorResponse {
	pr, errResp := getPR(issueable, pullRequests)
	if errResp != nil {
		return errResp
	}
	log.Printf(
		"Setting %s status to %s for PR %s (revision %s).\n",
		*status.Context,
		*status.State,
		issueable.Issue().FullName(),
		*pr.Head.SHA,
	)
	return setStatus(pr, status, repositories)
}

func setStatus(pr *github.PullRequest, status *github.RepoStatus, repositories Repositories) *ErrorResponse {
	// I'm assuming (because the documentation on this is unclear) that the
	// status has to be reported for the Head repository. It might seem
	// weird, because why should a bot configured for the Base repository
	// have access to the Head repository, but AFAIK all forks must be
	// public and reporting statuses on public repos is always allowed.
	headRepository := HeadRepository(pr)
	_, _, err := repositories.CreateStatus(headRepository.Owner, headRepository.Name, *pr.Head.SHA, status)
	if err != nil {
		message := fmt.Sprintf("Failed to create a %s status for commit %s", *status.State, *pr.Head.SHA)
		return &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return nil
}

func getStatuses(pr *github.PullRequest, repositories Repositories) (string, []github.RepoStatus, *ErrorResponse) {
	headRepository := HeadRepository(pr)
	pageNr := 1
	statuses := []github.RepoStatus{}
	var state string
	for {
		listOptions := &github.ListOptions{
			Page: pageNr,
			// The maximum for this endpoint is higher than the default 30:
			// https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
			PerPage: 100,
		}
		combinedStatus, resp, err := repositories.GetCombinedStatus(headRepository.Owner, headRepository.Name, *pr.Head.SHA, listOptions)
		if err != nil {
			message := fmt.Sprintf("Failed to get combined status for ref %s", *pr.Head.SHA)
			return "", nil, &ErrorResponse{err, http.StatusBadGateway, message}
		}
		// Although the combined state should be the same for all pages, use
		// the combined state from the latest request, because that's always
		// the most up to date one
		state = *combinedStatus.State
		statuses = append(statuses, combinedStatus.Statuses...)
		if resp.NextPage == 0 {
			break
		}
		pageNr = resp.NextPage
	}
	return state, statuses, nil
}

func getPR(issueable Issueable, pullRequests PullRequests) (*github.PullRequest, *ErrorResponse) {
	issue := issueable.Issue()
	pr, _, err := pullRequests.Get(issue.Repository.Owner, issue.Repository.Name, issue.Number)
	if err != nil {
		message := fmt.Sprintf("Getting PR %s failed", issue.FullName())
		return nil, &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return pr, nil
}

func getCommits(issueable Issueable, pullRequests PullRequests) ([]*github.RepositoryCommit, *ErrorResponse) {
	issue := issueable.Issue()
	pageNr := 1
	commits := []*github.RepositoryCommit{}
	for {
		listOptions := &github.ListOptions{
			Page:    pageNr,
			PerPage: 30,
		}
		pageCommits, resp, err := pullRequests.ListCommits(issue.Repository.Owner, issue.Repository.Name, issue.Number, listOptions)
		if err != nil {
			message := fmt.Sprintf("Getting commits for PR %s failed", issue.FullName())
			return nil, &ErrorResponse{err, http.StatusBadGateway, message}
		}
		commits = append(commits, pageCommits...)
		if resp.NextPage == 0 {
			break
		}
		pageNr = resp.NextPage
	}
	return commits, nil
}

func addLabel(repository Repository, issueNumber int, label string, issues Issues) *ErrorResponse {
	_, _, err := issues.AddLabelsToIssue(repository.Owner, repository.Name, issueNumber, []string{label})
	if err != nil {
		message := fmt.Sprintf("Failed to set the label %s for issue #%d", label, issueNumber)
		return &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return nil
}

func removeLabel(repository Repository, issueNumber int, label string, issues Issues) *ErrorResponse {
	_, err := issues.RemoveLabelForIssue(repository.Owner, repository.Name, issueNumber, label)
	if err != nil {
		message := fmt.Sprintf("Failed to remove the label %s for issue #%d", label, issueNumber)
		return &ErrorResponse{err, http.StatusBadGateway, message}
	}
	return nil
}

func merge(repository Repository, issueNumber int, pullRequests PullRequests) error {
	additionalCommitMessage := ""
	opt := &github.PullRequestOptions{MergeMethod: "merge"}
	result, resp, err := pullRequests.Merge(repository.Owner, repository.Name, issueNumber, additionalCommitMessage, opt)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed {
			return ErrNotMergeable
		} else if resp != nil && resp.StatusCode == http.StatusConflict {
			return ErrOutdatedMergeRef
		}
		return err
	} else if result.Merged == nil || !*result.Merged {
		return errors.New("Request successful, but PR not merged.")
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012 Apcera Inc. All rights reserved.

package main

import (
	"flag"
	"log"
	"net/http"
	_ "net/http/pprof"

	"github.com/apcera/gnatsd/server"
)

func main() {
	// logging setup
	server.LogSetup()

	opts := server.Options{}

	var debugAndTrace bool

	// Parse flags
	flag.IntVar(&opts.Port, "port", server.DEFAULT_PORT, "Port to listen on.")
	flag.IntVar(&opts.Port, "p", server.DEFAULT_PORT, "Port to listen on.")
	flag.StringVar(&opts.Host, "host", server.DEFAULT_HOST, "Network host to listen on.")
	flag.StringVar(&opts.Host, "h", server.DEFAULT_HOST, "Network host to listen on.")
	flag.BoolVar(&opts.Debug, "D", false, "Enable Debug logging.")
	flag.BoolVar(&opts.Debug, "debug", false, "Enable Debug logging.")
	flag.BoolVar(&opts.Trace, "V", false, "Enable Trace logging.")
	flag.BoolVar(&opts.Trace, "trace", false, "Enable Trace logging.")
	flag.BoolVar(&debugAndTrace, "DV", false, "Enable Debug and Trace logging.")
	flag.Parse()

	if debugAndTrace {
		opts.Trace, opts.Debug = true, true
	}

	// Profiler
	go func() {
		log.Println(http.ListenAndServe("localhost:6062", nil))
	}()

	// Parse config if given

	s := server.New(opts)
	s.AcceptLoop()
}

<commit_msg>Added flags for auth, user, and pass.<commit_after>// Copyright 2012 Apcera Inc. All rights reserved.

package main

import (
	"flag"
	"log"
	"net/http"
	_ "net/http/pprof"

	"github.com/apcera/gnatsd/server"
)

func main() {
	// logging setup
	server.LogSetup()

	opts := server.Options{}

	var debugAndTrace bool

	// Parse flags
	flag.IntVar(&opts.Port, "port", server.DEFAULT_PORT, "Port to listen on.")
	flag.IntVar(&opts.Port, "p", server.DEFAULT_PORT, "Port to listen on.")
	flag.StringVar(&opts.Host, "host", server.DEFAULT_HOST, "Network host to listen on.")
	flag.StringVar(&opts.Host, "h", server.DEFAULT_HOST, "Network host to listen on.")
	flag.BoolVar(&opts.Debug, "D", false, "Enable Debug logging.")
	flag.BoolVar(&opts.Debug, "debug", false, "Enable Debug logging.")
	flag.BoolVar(&opts.Trace, "V", false, "Enable Trace logging.")
	flag.BoolVar(&opts.Trace, "trace", false, "Enable Trace logging.")
	flag.BoolVar(&debugAndTrace, "DV", false, "Enable Debug and Trace logging.")
	flag.StringVar(&opts.Username, "user", "", "Username required for connection.")
	flag.StringVar(&opts.Password, "pass", "", "Password required for connection.")
	flag.StringVar(&opts.Password, "auth", "", "Authorization token required for connection.")

	flag.Parse()

	if debugAndTrace {
		opts.Trace, opts.Debug = true, true
	}

	// Profiler
	go func() {
		log.Println(http.ListenAndServe("localhost:6062", nil))
	}()

	// TBD: Parse config if given

	s := server.New(opts)
	s.AcceptLoop()
}

<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"time"

	"github.com/tsliwowicz/go-wrk/loader"
	"github.com/tsliwowicz/go-wrk/util"
)

const APP_VERSION = "0.1"

//default that can be overridden from the command line
var versionFlag bool = false
var helpFlag bool = false
var duration int = 10 //seconds
var goroutines int = 2
var testUrl string
var method string = "GET"
var host string
var headerStr string
var header map[string]string
var statsAggregator chan *loader.RequesterStats
var timeoutms int
var allowRedirectsFlag bool = false
var disableCompression bool
var disableKeepAlive bool
var playbackFile string
var reqBody string
var clientCert string
var clientKey string
var caCert string
var http2 bool

func init() {
	flag.BoolVar(&versionFlag, "v", false, "Print version details")
	flag.BoolVar(&allowRedirectsFlag, "redir", false, "Allow Redirects")
	flag.BoolVar(&helpFlag, "help", false, "Print help")
	flag.BoolVar(&disableCompression, "no-c", false, "Disable Compression - Prevents sending the \"Accept-Encoding: gzip\" header")
	flag.BoolVar(&disableKeepAlive, "no-ka", false, "Disable KeepAlive - prevents re-use of TCP connections between different HTTP requests")
	flag.IntVar(&goroutines, "c", 10, "Number of goroutines to use (concurrent connections)")
	flag.IntVar(&duration, "d", 10, "Duration of test in seconds")
	flag.IntVar(&timeoutms, "T", 1000, "Socket/request timeout in ms")
	flag.StringVar(&method, "M", "GET", "HTTP method")
	flag.StringVar(&host, "host", "", "Host Header")
	flag.StringVar(&headerStr, "H", "", "header line, joined with ';'")
	flag.StringVar(&playbackFile, "f", "<empty>", "Playback file name")
	flag.StringVar(&reqBody, "body", "", "request body string or @filename")
	flag.StringVar(&clientCert, "cert", "", "CA certificate file to verify peer against (SSL/TLS)")
	flag.StringVar(&clientKey, "key", "", "Private key file name (SSL/TLS")
	flag.StringVar(&caCert, "ca", "", "CA file to verify peer against (SSL/TLS)")
	flag.BoolVar(&http2, "http", true, "Use HTTP/2")
}

//printDefaults a nicer format for the defaults
func printDefaults() {
	fmt.Println("Usage: go-wrk <options> <url>")
	fmt.Println("Options:")
	flag.VisitAll(func(flag *flag.Flag) {
		fmt.Println("\t-"+flag.Name, "\t", flag.Usage, "(Default "+flag.DefValue+")")
	})
}

func main() {
	//raising the limits. Some performance gains were achieved with the + goroutines (not a lot).
	runtime.GOMAXPROCS(runtime.NumCPU() + goroutines)

	statsAggregator = make(chan *loader.RequesterStats, goroutines)
	sigChan := make(chan os.Signal, 1)

	signal.Notify(sigChan, os.Interrupt)

	flag.Parse() // Scan the arguments list
	header = make(map[string]string)
	if headerStr != "" {
		headerPairs := strings.Split(headerStr, ";")
		for _, hdr := range headerPairs {
			hp := strings.Split(hdr, ":")
			header[hp[0]] = hp[1]
		}
	}

	if playbackFile != "<empty>" {
		file, err := os.Open(playbackFile) // For read access.
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer file.Close()
		url, err := ioutil.ReadAll(file)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		testUrl = string(url)
	} else {
		testUrl = flag.Arg(0)
	}

	if versionFlag {
		fmt.Println("Version:", APP_VERSION)
		return
	} else if helpFlag || len(testUrl) == 0 {
		printDefaults()
		return
	}

	fmt.Printf("Running %vs test @ %v\n  %v goroutine(s) running concurrently\n", duration, testUrl, goroutines)

	if len(reqBody) > 0 && reqBody[0] == '@' {
		bodyFilename := reqBody[1:]
		data, err := ioutil.ReadFile(bodyFilename)
		if err != nil {
			fmt.Println(fmt.Errorf("could not read file %q: %v", bodyFilename, err))
			os.Exit(1)
		}
		reqBody = string(data)
	}

	loadGen := loader.NewLoadCfg(duration, goroutines, testUrl, reqBody, method, host, header, statsAggregator, timeoutms,
		allowRedirectsFlag, disableCompression, disableKeepAlive, clientCert, clientKey, caCert, http2)

	for i := 0; i < goroutines; i++ {
		go loadGen.RunSingleLoadSession()
	}

	responders := 0
	aggStats := loader.RequesterStats{MinRequestTime: time.Minute}

	for responders < goroutines {
		select {
		case <-sigChan:
			loadGen.Stop()
			fmt.Printf("stopping...\n")
		case stats := <-statsAggregator:
			aggStats.NumErrs += stats.NumErrs
			aggStats.NumRequests += stats.NumRequests
			aggStats.TotRespSize += stats.TotRespSize
			aggStats.TotDuration += stats.TotDuration
			aggStats.MaxRequestTime = util.MaxDuration(aggStats.MaxRequestTime, stats.MaxRequestTime)
			aggStats.MinRequestTime = util.MinDuration(aggStats.MinRequestTime, stats.MinRequestTime)
			responders++
		}
	}

	if aggStats.NumRequests == 0 {
		fmt.Println("Error: No statistics collected / no requests found\n")
		return
	}

	avgThreadDur := aggStats.TotDuration / time.Duration(responders) //need to average the aggregated duration

	reqRate := float64(aggStats.NumRequests) / avgThreadDur.Seconds()
	avgReqTime := aggStats.TotDuration / time.Duration(aggStats.NumRequests)
	bytesRate := float64(aggStats.TotRespSize) / avgThreadDur.Seconds()
	fmt.Printf("%v requests in %v, %v read\n", aggStats.NumRequests, avgThreadDur, util.ByteSize{float64(aggStats.TotRespSize)})
	fmt.Printf("Requests/sec:\t\t%.2f\nTransfer/sec:\t\t%v\nAvg Req Time:\t\t%v\n", reqRate, util.ByteSize{bytesRate}, avgReqTime)
	fmt.Printf("Fastest Request:\t%v\n", aggStats.MinRequestTime)
	fmt.Printf("Slowest Request:\t%v\n", aggStats.MaxRequestTime)
	fmt.Printf("Number of Errors:\t%v\n", aggStats.NumErrs)

}
<commit_msg>fixing issue #11<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"time"

	"github.com/tsliwowicz/go-wrk/loader"
	"github.com/tsliwowicz/go-wrk/util"
)

const APP_VERSION = "0.1"

//default that can be overridden from the command line
var versionFlag bool = false
var helpFlag bool = false
var duration int = 10 //seconds
var goroutines int = 2
var testUrl string
var method string = "GET"
var host string
var headerStr string
var header map[string]string
var statsAggregator chan *loader.RequesterStats
var timeoutms int
var allowRedirectsFlag bool = false
var disableCompression bool
var disableKeepAlive bool
var playbackFile string
var reqBody string
var clientCert string
var clientKey string
var caCert string
var http2 bool

func init() {
	flag.BoolVar(&versionFlag, "v", false, "Print version details")
	flag.BoolVar(&allowRedirectsFlag, "redir", false, "Allow Redirects")
	flag.BoolVar(&helpFlag, "help", false, "Print help")
	flag.BoolVar(&disableCompression, "no-c", false, "Disable Compression - Prevents sending the \"Accept-Encoding: gzip\" header")
	flag.BoolVar(&disableKeepAlive, "no-ka", false, "Disable KeepAlive - prevents re-use of TCP connections between different HTTP requests")
	flag.IntVar(&goroutines, "c", 10, "Number of goroutines to use (concurrent connections)")
	flag.IntVar(&duration, "d", 10, "Duration of test in seconds")
	flag.IntVar(&timeoutms, "T", 1000, "Socket/request timeout in ms")
	flag.StringVar(&method, "M", "GET", "HTTP method")
	flag.StringVar(&host, "host", "", "Host Header")
	flag.StringVar(&headerStr, "H", "", "header line, joined with ';'")
	flag.StringVar(&playbackFile, "f", "<empty>", "Playback file name")
	flag.StringVar(&reqBody, "body", "", "request body string or @filename")
	flag.StringVar(&clientCert, "cert", "", "CA certificate file to verify peer against (SSL/TLS)")
	flag.StringVar(&clientKey, "key", "", "Private key file name (SSL/TLS")
	flag.StringVar(&caCert, "ca", "", "CA file to verify peer against (SSL/TLS)")
	flag.BoolVar(&http2, "http", true, "Use HTTP/2")
}

//printDefaults a nicer format for the defaults
func printDefaults() {
	fmt.Println("Usage: go-wrk <options> <url>")
	fmt.Println("Options:")
	flag.VisitAll(func(flag *flag.Flag) {
		fmt.Println("\t-"+flag.Name, "\t", flag.Usage, "(Default "+flag.DefValue+")")
	})
}

func main() {
	//raising the limits. Some performance gains were achieved with the + goroutines (not a lot).
	runtime.GOMAXPROCS(runtime.NumCPU() + goroutines)

	statsAggregator = make(chan *loader.RequesterStats, goroutines)
	sigChan := make(chan os.Signal, 1)

	signal.Notify(sigChan, os.Interrupt)

	flag.Parse() // Scan the arguments list
	header = make(map[string]string)
	if headerStr != "" {
		headerPairs := strings.Split(headerStr, ";")
		for _, hdr := range headerPairs {
			hp := strings.SplitN(hdr, ":", 2)
			header[hp[0]] = hp[1]
		}
	}

	if playbackFile != "<empty>" {
		file, err := os.Open(playbackFile) // For read access.
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		defer file.Close()
		url, err := ioutil.ReadAll(file)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		testUrl = string(url)
	} else {
		testUrl = flag.Arg(0)
	}

	if versionFlag {
		fmt.Println("Version:", APP_VERSION)
		return
	} else if helpFlag || len(testUrl) == 0 {
		printDefaults()
		return
	}

	fmt.Printf("Running %vs test @ %v\n  %v goroutine(s) running concurrently\n", duration, testUrl, goroutines)

	if len(reqBody) > 0 && reqBody[0] == '@' {
		bodyFilename := reqBody[1:]
		data, err := ioutil.ReadFile(bodyFilename)
		if err != nil {
			fmt.Println(fmt.Errorf("could not read file %q: %v", bodyFilename, err))
			os.Exit(1)
		}
		reqBody = string(data)
	}

	loadGen := loader.NewLoadCfg(duration, goroutines, testUrl, reqBody, method, host, header, statsAggregator, timeoutms,
		allowRedirectsFlag, disableCompression, disableKeepAlive, clientCert, clientKey, caCert, http2)

	for i := 0; i < goroutines; i++ {
		go loadGen.RunSingleLoadSession()
	}

	responders := 0
	aggStats := loader.RequesterStats{MinRequestTime: time.Minute}

	for responders < goroutines {
		select {
		case <-sigChan:
			loadGen.Stop()
			fmt.Printf("stopping...\n")
		case stats := <-statsAggregator:
			aggStats.NumErrs += stats.NumErrs
			aggStats.NumRequests += stats.NumRequests
			aggStats.TotRespSize += stats.TotRespSize
			aggStats.TotDuration += stats.TotDuration
			aggStats.MaxRequestTime = util.MaxDuration(aggStats.MaxRequestTime, stats.MaxRequestTime)
			aggStats.MinRequestTime = util.MinDuration(aggStats.MinRequestTime, stats.MinRequestTime)
			responders++
		}
	}

	if aggStats.NumRequests == 0 {
		fmt.Println("Error: No statistics collected / no requests found\n")
		return
	}

	avgThreadDur := aggStats.TotDuration / time.Duration(responders) //need to average the aggregated duration

	reqRate := float64(aggStats.NumRequests) / avgThreadDur.Seconds()
	avgReqTime := aggStats.TotDuration / time.Duration(aggStats.NumRequests)
	bytesRate := float64(aggStats.TotRespSize) / avgThreadDur.Seconds()
	fmt.Printf("%v requests in %v, %v read\n", aggStats.NumRequests, avgThreadDur, util.ByteSize{float64(aggStats.TotRespSize)})
	fmt.Printf("Requests/sec:\t\t%.2f\nTransfer/sec:\t\t%v\nAvg Req Time:\t\t%v\n", reqRate, util.ByteSize{bytesRate}, avgReqTime)
	fmt.Printf("Fastest Request:\t%v\n", aggStats.MinRequestTime)
	fmt.Printf("Slowest Request:\t%v\n", aggStats.MaxRequestTime)
	fmt.Printf("Number of Errors:\t%v\n", aggStats.NumErrs)

}
<|endoftext|>


<commit_before>/*

=======================================================

gobatt - Lightweight battery tray icon for Linux.

Repository: https://github.com/solusipse/gobatt

=======================================================

The MIT License (MIT)

Copyright (c) 2013 solusipse

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

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

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

package main

import (
        "strings"
        "strconv"
        "io/ioutil"
        "github.com/mattn/go-gtk/glib"
        "github.com/mattn/go-gtk/gtk"
)

const (
        ACPIPATH = "/sys/class/power_supply/BAT1/"
        UPDATE_TIME = 1
)

var lastPercentage float64
var timeSlice [10] float64

func main() {
    icon := trayIconInit()

    glib.TimeoutAdd(UPDATE_TIME*1000, func() bool {
        batteryStatus, batteryPercentage := updateData()
        setTrayIcon(icon, batteryStatus, batteryPercentage)
        return true
    })

    glib.TimeoutAdd(10000, func() bool {
        batteryStatus, batteryPercentage := updateData()
        getRemainingTime(icon, batteryStatus, batteryPercentage)
        return true
    })

    gtk.Main()
}

func getFileContent(filename string) string {
    content, _ := ioutil.ReadFile(ACPIPATH + filename)
    return string(content)
}

func getBatteryState() string {
    return strings.TrimSuffix(getFileContent("status"), "\n")
}

func getBatteryPercentage() float64 {
    _fc := strings.TrimSuffix(getFileContent("energy_full"), "\n")
    _nc := strings.TrimSuffix(getFileContent("energy_now"), "\n")
    fullCap, _ := strconv.Atoi(_fc)
    nowCap,  _ := strconv.Atoi(_nc)
    return (float64(nowCap)/float64(fullCap))
}

func updateData() (string, float64) {
    return getBatteryState(), getBatteryPercentage()
}

func trayIconInit() *gtk.StatusIcon {
    gtk.Init(nil)
    glib.SetApplicationName("wm-batt-tray")

    // TODO: add context menu
    //popupInfo := gtk.NewMenuItemWithLabel("")

    icon := gtk.NewStatusIcon()
    icon.SetTitle("wm-batt-tray")

    return icon
}

func getGtkIcon(percent float64, status string) string {
    percent = percent * 100
    if status == "Discharging" {
        if percent <= 10 {
            return "battery-caution-symbolic"
        } else if percent <= 20 {
            return "battery-empty-symbolic"
        } else if percent <= 45 {
            return "battery-low-symbolic"
        } else if percent <= 75 {
            return "battery-good-symbolic"
        } else if percent <= 100 {
            return "battery-full-symbolic"
        }
    }
    if status == "Charging" {
        if percent <= 10 {
            return "battery-caution-charging-symbolic"
        } else if percent <= 20 {
            return "battery-empty-charging-symbolic"
        } else if percent <= 45 {
            return "battery-low-charging-symbolic"
        } else if percent <= 75 {
            return "battery-good-charging-symbolic"
        } else if percent <= 99 {
            return "battery-full-charging-symbolic"
        } else if percent <= 100 {
            return "battery-full-charged-symbolic"
        }
    }
    if status == "Full" {
        return "battery-full-charged-symbolic"
    }

    return "battery-missing-symbolic"
}

func addTimeRecord(record float64) {
    if timeSlice[9] != 0 {
        var bufferSlice [10]float64
        for i := 0; i < 9; i++ {
            bufferSlice[i+1] = timeSlice[i]
        }
        timeSlice = bufferSlice
        timeSlice[0] = record
    } else {
        for i, j := range timeSlice {
            if j == 0 {
                timeSlice[i] = record
                break
            }
        }
    }
}

func getAverageTime() int {
    if timeSlice[9] != 0 {
        var buffer float64 = 0
        for _, j := range timeSlice {
            buffer += j
        }
        return int(buffer/10)
    }
    return -1
}

func getRemainingTime(icon *gtk.StatusIcon, status string, percent float64) {
    if lastPercentage == 0 {
        lastPercentage = percent
    }

    if lastPercentage > percent {
        remaining := ((10 * percent)/(lastPercentage - percent))/60

        addTimeRecord(remaining)
        lastPercentage = percent
    }

    if lastPercentage < percent {
        remaining := ((10 * (1 - percent))/(percent - lastPercentage))/60

        addTimeRecord(remaining)
        lastPercentage = percent
    }

}

func getTooltipString(percent float64, status string, time int) string {
    if percent*100 >= 99 {
        return "Battery is fully charged."
    }

    tooltipString := status
    tooltipString += ": " + strconv.Itoa(int(percent*100)) + "%\n"

    if time == -1 {
        tooltipString += "Remaining time: estimating."
    } else {
        hours := time/60
        minutes := time - hours*60
        tooltipString += "Remaining time: " + strconv.Itoa(hours) + "h " +
            strconv.Itoa(minutes) + "m."
    }

    return tooltipString
}

func setToolTip(icon *gtk.StatusIcon, status string, percent float64, time int) {
    icon.SetTooltipMarkup(getTooltipString(percent, status, time))
}

func setTrayIcon(icon *gtk.StatusIcon, status string, percent float64) {
    iconName := getGtkIcon(percent, status)

    if icon.GetIconName() != iconName {
        icon.SetFromIconName(iconName)
    }
    setToolTip(icon, status, percent, getAverageTime())
}<commit_msg>minor changes<commit_after>/*

=======================================================

gobatt - Lightweight battery tray icon for Linux.

Repository: https://github.com/solusipse/gobatt

=======================================================

The MIT License (MIT)

Copyright (c) 2013 solusipse

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

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

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

package main

import (
        "strings"
        "strconv"
        "io/ioutil"
        "github.com/mattn/go-gtk/glib"
        "github.com/mattn/go-gtk/gtk"
)

const (
        ACPIPATH = "/sys/class/power_supply/BAT1/"
        UPDATE_TIME = 1
)

var lastPercentage float64
var timeSlice [10] float64

func main() {
    icon := trayIconInit()

    glib.TimeoutAdd(UPDATE_TIME*1000, func() bool {
        batteryStatus, batteryPercentage := updateData()
        setTrayIcon(icon, batteryStatus, batteryPercentage)
        return true
    })

    glib.TimeoutAdd(10000, func() bool {
        batteryStatus, batteryPercentage := updateData()
        getRemainingTime(icon, batteryStatus, batteryPercentage)
        return true
    })

    gtk.Main()
}

func getFileContent(filename string) string {
    content, _ := ioutil.ReadFile(ACPIPATH + filename)
    return string(content)
}

func getBatteryState() string {
    return strings.TrimSuffix(getFileContent("status"), "\n")
}

func getBatteryPercentage() float64 {
    _fc := strings.TrimSuffix(getFileContent("energy_full"), "\n")
    _nc := strings.TrimSuffix(getFileContent("energy_now"), "\n")
    fullCap, _ := strconv.Atoi(_fc)
    nowCap,  _ := strconv.Atoi(_nc)
    return (float64(nowCap)/float64(fullCap))
}

func updateData() (string, float64) {
    return getBatteryState(), getBatteryPercentage()
}

func trayIconInit() *gtk.StatusIcon {
    gtk.Init(nil)
    glib.SetApplicationName("gobatt")

    icon := gtk.NewStatusIcon()
    icon.SetTitle("gobatt")

    return icon
}

func getGtkIcon(percent float64, status string) string {
    percent = percent * 100
    if status == "Discharging" {
        if percent <= 10 {
            return "battery-caution-symbolic"
        } else if percent <= 20 {
            return "battery-empty-symbolic"
        } else if percent <= 45 {
            return "battery-low-symbolic"
        } else if percent <= 75 {
            return "battery-good-symbolic"
        } else if percent <= 100 {
            return "battery-full-symbolic"
        }
    }
    if status == "Charging" {
        if percent <= 10 {
            return "battery-caution-charging-symbolic"
        } else if percent <= 20 {
            return "battery-empty-charging-symbolic"
        } else if percent <= 45 {
            return "battery-low-charging-symbolic"
        } else if percent <= 75 {
            return "battery-good-charging-symbolic"
        } else if percent <= 99 {
            return "battery-full-charging-symbolic"
        } else if percent <= 100 {
            return "battery-full-charged-symbolic"
        }
    }
    if status == "Full" {
        return "battery-full-charged-symbolic"
    }

    return "battery-missing-symbolic"
}

func addTimeRecord(record float64) {
    if timeSlice[9] != 0 {
        var bufferSlice [10]float64
        for i := 0; i < 9; i++ {
            bufferSlice[i+1] = timeSlice[i]
        }
        timeSlice = bufferSlice
        timeSlice[0] = record
    } else {
        for i, j := range timeSlice {
            if j == 0 {
                timeSlice[i] = record
                break
            }
        }
    }
}

func getAverageTime() int {
    if timeSlice[9] != 0 {
        var buffer float64 = 0
        for _, j := range timeSlice {
            buffer += j
        }
        return int(buffer/10)
    }
    return -1
}

func getRemainingTime(icon *gtk.StatusIcon, status string, percent float64) {
    if lastPercentage == 0 {
        lastPercentage = percent
    }

    if lastPercentage > percent {
        remaining := ((10 * percent)/(lastPercentage - percent))/60

        addTimeRecord(remaining)
        lastPercentage = percent
    }

    if lastPercentage < percent {
        remaining := ((10 * (1 - percent))/(percent - lastPercentage))/60

        addTimeRecord(remaining)
        lastPercentage = percent
    }

}

func getTooltipString(percent float64, status string, time int) string {
    if percent*100 >= 99 {
        return "Battery is fully charged."
    }

    tooltipString := status
    tooltipString += ": " + strconv.Itoa(int(percent*100)) + "%\n"

    if time == -1 {
        tooltipString += "Remaining time: estimating."
    } else {
        hours := time/60
        minutes := time - hours*60
        tooltipString += "Remaining time: " + strconv.Itoa(hours) + "h " +
            strconv.Itoa(minutes) + "m."
    }

    return tooltipString
}

func setToolTip(icon *gtk.StatusIcon, status string, percent float64, time int) {
    icon.SetTooltipMarkup(getTooltipString(percent, status, time))
}

func setTrayIcon(icon *gtk.StatusIcon, status string, percent float64) {
    iconName := getGtkIcon(percent, status)

    if icon.GetIconName() != iconName {
        icon.SetFromIconName(iconName)
    }
    setToolTip(icon, status, percent, getAverageTime())
}<|endoftext|>