<commit_before>package vanity

import (
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

var (
	hostname = "kkn.fi"
	config   = map[Path]Package{
		"/gist":              *NewPackage("/gist", "git", "https://github.com/kare/gist"),
		"/vanity":            *NewPackage("/vanity", "git", "https://github.com/kare/vanity"),
		"/vanity/cmd":        *NewPackage("/vanity", "git", "https://github.com/kare/vanity"),
		"/vanity/cmd/vanity": *NewPackage("/vanity", "git", "https://github.com/kare/vanity"),
		"/foo/bar":           *NewPackage("/foo", "git", "https://github.com/kare/foo"),
		"/foo/bar/baz":       *NewPackage("/foo", "git", "https://github.com/kare/foo"),
		"/":                  *NewPackage("/", "git", "https://github.com/project"),
	}
)

func TestHTTPMethodsSupport(t *testing.T) {
	server := NewServer(hostname, config)
	tests := []struct {
		method string
		status int
	}{
		{"GET", 200},
		{"HEAD", 405},
		{"POST", 405},
		{"PUT", 405},
		{"DELETE", 405},
		{"TRACE", 405},
		{"OPTIONS", 405},
	}
	for _, test := range tests {
		req, err := http.NewRequest(test.method, "/gist?go-get=1", nil)
		if err != nil {
			t.Errorf("http request with method %v failed with error: %v", test.method, err)
		}
		res := httptest.NewRecorder()
		server.ServeHTTP(res, req)
		if res.Code != test.status {
			t.Fatalf("Expecting status code %v for method '%v', but got %v", test.status, test.method, res.Code)
		}
	}
}

func TestGoTool(t *testing.T) {
	server := httptest.NewServer(NewServer(hostname, config))
	defer server.Close()

	tests := []struct {
		path   string
		result string
	}{
		{"/gist?go-get=1", "kkn.fi/gist git https://github.com/kare/gist"},
		{"/vanity?go-get=1", "kkn.fi/vanity git https://github.com/kare/vanity"},
		{"/foo/bar?go-get=1", "kkn.fi/foo git https://github.com/kare/foo"},
		{"/foo/bar/baz?go-get=1", "kkn.fi/foo git https://github.com/kare/foo"},
	}
	for _, test := range tests {
		url := server.URL + test.path
		res, err := http.Get(url)
		if err != nil {
			t.Errorf("error requesting url %v\n%v", url, err)
		}
		defer func() {
			if err := res.Body.Close(); err != nil {
				t.Errorf("error closing response body: %v", err)
			}
		}()
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			t.Errorf("reading response body failed with error: %v", err)
		}

		expected := `<meta name="go-import" content="` + test.result + `">`
		if !strings.Contains(string(body), expected) {
			log.Fatalf("Expecting body to contain html meta tag: '%v', but got:\n'%v'", expected, string(body))
		}

		expected = "text/html; charset=utf-8"
		if res.Header.Get("content-type") != expected {
			t.Fatalf("Expecting content type '%v', but got '%v'", expected, res.Header.Get("content-type"))
		}

		if res.StatusCode != http.StatusOK {
			t.Fatalf("Expected response status 200, but got %v", res.StatusCode)
		}
	}
}

func TestGoToolPackageNotFound(t *testing.T) {
	server := httptest.NewServer(NewServer(hostname, config))
	defer server.Close()

	url := server.URL + "/package-not-found?go-get=1"
	res, err := http.Get(url)
	if err != nil {
		t.Errorf("error requesting url %v\n%v", url, err)
	}
	defer func() {
		if err := res.Body.Close(); err != nil {
			t.Errorf("error closing response body: %v", err)
		}
	}()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Errorf("reading response body failed with error: %v", err)
	}

	if res.StatusCode != http.StatusNotFound {
		t.Fatalf("Expected response status 404, but got %v", res.StatusCode)
	}
	expected := "404 page not found\n"
	if string(body) != expected {
		t.Fatalf("Expecting '%v', but got '%v'", expected, string(body))
	}
}

func TestBrowserGoDoc(t *testing.T) {
	server := httptest.NewServer(NewServer(hostname, config))
	defer server.Close()

	tests := []struct {
		path   string
		result string
	}{
		{"/gist", "https://godoc.org/kkn.fi/gist"},
		{"/vanity", "https://godoc.org/kkn.fi/vanity"},
		{"/vanity/cmd", "https://godoc.org/kkn.fi/vanity/cmd"},
		{"/vanity/cmd/vanity", "https://godoc.org/kkn.fi/vanity/cmd/vanity"},
		{"/foo/bar", "https://godoc.org/kkn.fi/foo/bar"},
		{"/foo/bar/baz", "https://godoc.org/kkn.fi/foo/bar/baz"},
	}
	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}
	for _, test := range tests {
		url := server.URL + test.path
		res, err := client.Get(url)
		if err != nil {
			t.Errorf("error requesting url %v\n%v", url, err)
		}
		defer func() {
			if err := res.Body.Close(); err != nil {
				t.Errorf("error closing response body: %v", err)
			}
		}()

		if res.StatusCode != http.StatusTemporaryRedirect {
			t.Fatalf("Expected response status %v, but got %v", http.StatusTemporaryRedirect, res.StatusCode)
		}

		location := res.Header.Get("location")
		if location != test.result {
			t.Fatalf("Expecting location header to match '%v', but got '%v'", test.result, location)
		}

		expected := "text/html; charset=utf-8"
		contentType := res.Header.Get("content-type")
		if contentType != expected {
			t.Fatalf("Expecting content type '%v', but got '%v'", expected, contentType)
		}

	}
}
<commit_msg>Review tests<commit_after>package vanity

import (
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

var (
	hostname = "kkn.fi"
	config   = map[Path]Package{
		"/gist":              *NewPackage("/gist", "git", "https://github.com/kare/gist"),
		"/vanity":            *NewPackage("/vanity", "git", "https://github.com/kare/vanity"),
		"/vanity/cmd":        *NewPackage("/vanity", "git", "https://github.com/kare/vanity"),
		"/vanity/cmd/vanity": *NewPackage("/vanity", "git", "https://github.com/kare/vanity"),
		"/foo/bar":           *NewPackage("/foo", "git", "https://github.com/kare/foo"),
		"/foo/bar/baz":       *NewPackage("/foo", "git", "https://github.com/kare/foo"),
		"/":                  *NewPackage("/", "git", "https://github.com/project"),
	}
)

func TestHTTPMethodsSupport(t *testing.T) {
	server := NewServer(hostname, config)
	tests := []struct {
		method string
		status int
	}{
		{"GET", 200},
		{"HEAD", 405},
		{"POST", 405},
		{"PUT", 405},
		{"DELETE", 405},
		{"TRACE", 405},
		{"OPTIONS", 405},
	}
	for _, test := range tests {
		req, err := http.NewRequest(test.method, "/gist?go-get=1", nil)
		if err != nil {
			t.Skipf("http request with method %v failed with error: %v", test.method, err)
		}
		res := httptest.NewRecorder()
		server.ServeHTTP(res, req)
		if res.Code != test.status {
			t.Fatalf("Expecting status code %v for method '%v', but got %v", test.status, test.method, res.Code)
		}
	}
}

func TestGoTool(t *testing.T) {
	server := httptest.NewServer(NewServer(hostname, config))
	defer server.Close()

	tests := []struct {
		path   string
		result string
	}{
		{"/gist?go-get=1", "kkn.fi/gist git https://github.com/kare/gist"},
		{"/vanity?go-get=1", "kkn.fi/vanity git https://github.com/kare/vanity"},
		{"/foo/bar?go-get=1", "kkn.fi/foo git https://github.com/kare/foo"},
		{"/foo/bar/baz?go-get=1", "kkn.fi/foo git https://github.com/kare/foo"},
	}
	for _, test := range tests {
		url := server.URL + test.path
		res, err := http.Get(url)
		if err != nil {
			t.Skipf("error requesting url %v\n%v", url, err)
		}
		defer func() {
			if err := res.Body.Close(); err != nil {
				t.Errorf("error closing response body: %v", err)
			}
		}()
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			t.Fatalf("reading response body failed with error: %v", err)
		}

		expected := `<meta name="go-import" content="` + test.result + `">`
		if !strings.Contains(string(body), expected) {
			log.Fatalf("Expecting body to contain html meta tag: '%v', but got:\n'%v'", expected, string(body))
		}

		expected = "text/html; charset=utf-8"
		if res.Header.Get("content-type") != expected {
			t.Fatalf("Expecting content type '%v', but got '%v'", expected, res.Header.Get("content-type"))
		}

		if res.StatusCode != http.StatusOK {
			t.Fatalf("Expected response status 200, but got %v", res.StatusCode)
		}
	}
}

func TestGoToolPackageNotFound(t *testing.T) {
	server := httptest.NewServer(NewServer(hostname, config))
	defer server.Close()

	url := server.URL + "/package-not-found?go-get=1"
	res, err := http.Get(url)
	if err != nil {
		t.Skipf("error requesting url %v\n%v", url, err)
	}
	defer func() {
		if err := res.Body.Close(); err != nil {
			t.Errorf("error closing response body: %v", err)
		}
	}()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatalf("reading response body failed with error: %v", err)
	}

	if res.StatusCode != http.StatusNotFound {
		t.Fatalf("Expected response status 404, but got %v", res.StatusCode)
	}
	expected := "404 page not found\n"
	if string(body) != expected {
		t.Fatalf("Expecting '%v', but got '%v'", expected, string(body))
	}
}

func TestBrowserGoDoc(t *testing.T) {
	server := httptest.NewServer(NewServer(hostname, config))
	defer server.Close()

	tests := []struct {
		path   string
		result string
	}{
		{"/gist", "https://godoc.org/kkn.fi/gist"},
		{"/vanity", "https://godoc.org/kkn.fi/vanity"},
		{"/vanity/cmd", "https://godoc.org/kkn.fi/vanity/cmd"},
		{"/vanity/cmd/vanity", "https://godoc.org/kkn.fi/vanity/cmd/vanity"},
		{"/foo/bar", "https://godoc.org/kkn.fi/foo/bar"},
		{"/foo/bar/baz", "https://godoc.org/kkn.fi/foo/bar/baz"},
	}
	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}
	for _, test := range tests {
		url := server.URL + test.path
		res, err := client.Get(url)
		if err != nil {
			t.Skipf("error requesting url %v\n%v", url, err)
		}
		defer func() {
			if err := res.Body.Close(); err != nil {
				t.Errorf("error closing response body: %v", err)
			}
		}()

		if res.StatusCode != http.StatusTemporaryRedirect {
			t.Fatalf("Expected response status %v, but got %v", http.StatusTemporaryRedirect, res.StatusCode)
		}

		location := res.Header.Get("location")
		if location != test.result {
			t.Fatalf("Expecting location header to match '%v', but got '%v'", test.result, location)
		}

		expected := "text/html; charset=utf-8"
		contentType := res.Header.Get("content-type")
		if contentType != expected {
			t.Fatalf("Expecting content type '%v', but got '%v'", expected, contentType)
		}

	}
}
<|endoftext|>


<commit_before>package vault

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"syscall"
	"text/template"
	"time"
)

const vaultServerConfigTemplate = `
backend "file" {
  path = "db"
}

listener "tcp" {
  address     = "{{.address}}"
  tls_disable = 1
}
`

type folders struct {
	db string
}
type files struct {
	conf string
	pid  string
}

type layout struct {
	folders folders
	files   files
}

func localGetLayout(folder string) layout {
	return layout{
		folders: folders{
			db: path.Join(folder, "db"),
		},
		files: files{
			conf: path.Join(folder, "config.hcl"),
			pid:  path.Join(folder, ".pid"),
		},
	}
}

func getLocalVaultAddress() string {
	return "http://" + vaultAddr
}

func LocalSetEnv() {
	os.Setenv("VAULT_ADDR", getLocalVaultAddress())
	fmt.Println("setting environment variable VAULT_ADDR:",getLocalVaultAddress())
}

func LocalSetup(folder string) error {
	l := localGetLayout(folder)
	err := os.MkdirAll(l.folders.db, 0744)
	if err != nil {
		return err
	}
	templateData := make(map[string]string)
	templateData["address"] = vaultAddr
	t, err := template.New("temp").Parse(string(vaultServerConfigTemplate))
	if err != nil {
		return err
	}
	out := bytes.NewBuffer([]byte{})
	err = t.Execute(out, templateData)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(l.files.conf, out.Bytes(), 0644)
}

func LocalStart(folder string) (cmd *exec.Cmd, chanVaultErr chan error) {
	chanVaultErr = make(chan error)
	var runErr error
	go func() {
		fmt.Println("starting vault server with config.hcl in directory",folder)
		cmd = exec.Command("vault", "server", "-config", "config.hcl")
		cmd.Dir = folder
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Run()
		chanVaultErr <- runErr
	}()
	for {
		select {
		case <-time.After(time.Millisecond * 500):
			if runErr != nil {
				fmt.Println("waiting for vault to start")
				return nil, nil
			}
			if LocalIsRunning() {
				return cmd, chanVaultErr
			} else {
				fmt.Println("local is not running")
			}
		case <-time.After(time.Second * 3):
			return nil, nil
		}
	}
}

func LocalIsRunning() bool {
	addr := os.Getenv("VAULT_ADDR")
	response, err := http.Get(addr + "/v1/")
	if err != nil {
		fmt.Println("Could not get vault from address "+addr)
		fmt.Println(err)
		return false
	}
	contentTypes, ok := response.Header["Content-Type"]
	return response.StatusCode == http.StatusServiceUnavailable && ok && len(contentTypes) == 1 && contentTypes[0] == "application/json"
}

func _LocalIsRunning() bool {
	cmd := exec.Command("vault", "status")
	err := cmd.Run()
	//fmt.Println("state:", cmd.ProcessState.ExitStatus(), err, string(combined))

	if exiterr, ok := err.(*exec.ExitError); ok {
		// The program has exited with an exit code != 0

		// This works on both Unix and Windows. Although package
		// syscall is generally platform dependent, WaitStatus is
		// defined for both Unix and Windows and in both cases has
		// an ExitStatus() method with the same signature.
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			switch status.ExitStatus() {
			case 2:
				// sealed but up
				return true
			default:
				log.Println("vault is in status", status.ExitStatus())
			}
		}
	}

	return err == nil
}

func LocalIsSetUp(folder string) bool {
	l := localGetLayout(folder)
	checks := map[string]bool{
		l.files.conf: false,
		l.folders.db: true,
	}
	for file, isDir := range checks {
		info, err := os.Stat(file)
		if err != nil {
			return false
		}
		if isDir && !info.IsDir() || !isDir && info.IsDir() {
			return false
		}
	}
	return true
}
<commit_msg>extract command to return the command value (to terminate vault) and change verification function (get url)<commit_after>package vault

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"syscall"
	"text/template"
	"time"
)

const vaultServerConfigTemplate = `
backend "file" {
  path = "db"
}

listener "tcp" {
  address     = "{{.address}}"
  tls_disable = 1
}
`

type folders struct {
	db string
}
type files struct {
	conf string
	pid  string
}

type layout struct {
	folders folders
	files   files
}

func localGetLayout(folder string) layout {
	return layout{
		folders: folders{
			db: path.Join(folder, "db"),
		},
		files: files{
			conf: path.Join(folder, "config.hcl"),
			pid:  path.Join(folder, ".pid"),
		},
	}
}

func getLocalVaultAddress() string {
	return "http://" + vaultAddr
}

func LocalSetEnv() {
	os.Setenv("VAULT_ADDR", getLocalVaultAddress())
	fmt.Println("setting environment variable VAULT_ADDR:",getLocalVaultAddress())
}

func LocalSetup(folder string) error {
	l := localGetLayout(folder)
	err := os.MkdirAll(l.folders.db, 0744)
	if err != nil {
		return err
	}
	templateData := make(map[string]string)
	templateData["address"] = vaultAddr
	t, err := template.New("temp").Parse(string(vaultServerConfigTemplate))
	if err != nil {
		return err
	}
	out := bytes.NewBuffer([]byte{})
	err = t.Execute(out, templateData)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(l.files.conf, out.Bytes(), 0644)
}

func LocalStart(folder string) (cmd *exec.Cmd, chanVaultErr chan error) {
	chanVaultErr = make(chan error)
	cmd = exec.Command("vault", "server", "-config", "config.hcl")
	cmd.Dir = folder
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	var runErr error
	go func() {
		fmt.Println("starting vault server with config.hcl in directory",folder)

		cmd.Run()
		chanVaultErr <- runErr
	}()
	for {
		select {
		case <-time.After(time.Millisecond * 500):
			if runErr != nil {
				fmt.Println("waiting for vault to start")
				return nil, nil
			}
			if LocalIsRunning() {
				return cmd, chanVaultErr
			} else {
				fmt.Println("local is not running")
			}
		case <-time.After(time.Second * 3):
			return nil, nil
		}
	}
}

func LocalIsRunning() bool {
	addr := os.Getenv("VAULT_ADDR")
	response, err := http.Get(addr + "/v1/sys/init")
	if err != nil {
		fmt.Println("Could not get vault from address "+addr)
		fmt.Println(err)
		return false
	}
	contentTypes, ok := response.Header["Content-Type"]
	return (response.StatusCode == http.StatusOK) && ok && len(contentTypes) == 1 && contentTypes[0] == "application/json"
}

func _LocalIsRunning() bool {
	cmd := exec.Command("vault", "status")
	err := cmd.Run()
	//fmt.Println("state:", cmd.ProcessState.ExitStatus(), err, string(combined))

	if exiterr, ok := err.(*exec.ExitError); ok {
		// The program has exited with an exit code != 0

		// This works on both Unix and Windows. Although package
		// syscall is generally platform dependent, WaitStatus is
		// defined for both Unix and Windows and in both cases has
		// an ExitStatus() method with the same signature.
		if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
			switch status.ExitStatus() {
			case 2:
				// sealed but up
				return true
			default:
				log.Println("vault is in status", status.ExitStatus())
			}
		}
	}

	return err == nil
}

func LocalIsSetUp(folder string) bool {
	l := localGetLayout(folder)
	checks := map[string]bool{
		l.files.conf: false,
		l.folders.db: true,
	}
	for file, isDir := range checks {
		info, err := os.Stat(file)
		if err != nil {
			return false
		}
		if isDir && !info.IsDir() || !isDir && info.IsDir() {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package vdoext

import "testing"

func TestGet(t *testing.T) {

}
<commit_msg>Update test<commit_after>package vdoext

import "testing"

func TestGet(t *testing.T) {
	n := Get()
	if len(n) <= 0 {
		t.Fatalf("There must be atleast one extension provided")
	}
}
<|endoftext|>


<commit_before>package view

import (
	//"log"
	"errors"
	"fmt"
	"github.com/ha/doozer"
	"net"
)

func PublishAddr(addr string) error {
	doozerConn, err := doozer.Dial("localhost:8046")
	if err != nil {
		return err
	}
	defer doozerConn.Close()

	rev, err := doozerConn.Rev()
	if err != nil {
		return err
	}

	files, err := doozerConn.Getdir("/goft/servers/", rev, 0, -1)
	if err != nil && err.Error() != "NOENT" {
		return err
	}

	id := len(files)
	_, err = doozerConn.Set(fmt.Sprintf("/goft/servers/%d", id), 0, []byte(addr))
	for err != nil && err.Error() == "REV_MISMATCH" {
		id++
		_, err = doozerConn.Set(fmt.Sprintf("/goft/servers/%d", id), 0, []byte(addr))
	}
	if err != nil {
		return err
	}

	return nil
}

func GetRunningServer() (string, error) {
	doozerConn, err := doozer.Dial("localhost:8046")
	if err != nil {
		return "", err
	}
	defer doozerConn.Close()

	rev, err := doozerConn.Rev()
	if err != nil {
		return "", err
	}

	files, err := doozerConn.Getdir("/goft/servers", rev, 0, -1)
	if err != nil {
		return "", err
	}
	//log.Println(files)

	for _, file := range files {
		path := fmt.Sprintf("/goft/servers/%v", file)
		addr, rev, err := doozerConn.Get(path, nil)
		if err != nil {
			return "", err
		}

		//fmt.Println(string(addr))
		conn, err := net.Dial("tcp", string(addr))
		if err != nil {
			//fmt.Println("falhou dial", err)
			err := doozerConn.Del(path, rev)
			if err != nil {
				return "", err
			}
			continue
		}
		defer conn.Close()

		if err != nil {
			return "", err
		}
		//log.Println("Did it!")
		return conn.RemoteAddr().String(), nil
	}
	return "", errors.New("Failed to find a running server")
}
<commit_msg>Improve error message<commit_after>package view

import (
	//"log"
	"errors"
	"fmt"
	"github.com/ha/doozer"
	"net"
)

func PublishAddr(addr string) error {
	doozerConn, err := doozer.Dial("localhost:8046")
	if err != nil {
		return errors.New(fmt.Sprint("Failed to connect to Doozer: ", err))
	}
	defer doozerConn.Close()

	rev, err := doozerConn.Rev()
	if err != nil {
		return err
	}

	files, err := doozerConn.Getdir("/goft/servers/", rev, 0, -1)
	if err != nil && err.Error() != "NOENT" {
		return err
	}

	id := len(files)
	_, err = doozerConn.Set(fmt.Sprintf("/goft/servers/%d", id), 0, []byte(addr))
	for err != nil && err.Error() == "REV_MISMATCH" {
		id++
		_, err = doozerConn.Set(fmt.Sprintf("/goft/servers/%d", id), 0, []byte(addr))
	}
	if err != nil {
		return err
	}

	return nil
}

func GetRunningServer() (string, error) {
	doozerConn, err := doozer.Dial("localhost:8046")
	if err != nil {
		return "", err
	}
	defer doozerConn.Close()

	rev, err := doozerConn.Rev()
	if err != nil {
		return "", err
	}

	files, err := doozerConn.Getdir("/goft/servers", rev, 0, -1)
	if err != nil {
		return "", err
	}
	//log.Println(files)

	for _, file := range files {
		path := fmt.Sprintf("/goft/servers/%v", file)
		addr, rev, err := doozerConn.Get(path, nil)
		if err != nil {
			return "", err
		}

		//fmt.Println(string(addr))
		conn, err := net.Dial("tcp", string(addr))
		if err != nil {
			//fmt.Println("falhou dial", err)
			err := doozerConn.Del(path, rev)
			if err != nil {
				return "", err
			}
			continue
		}
		defer conn.Close()

		if err != nil {
			return "", err
		}
		//log.Println("Did it!")
		return conn.RemoteAddr().String(), nil
	}
	return "", errors.New("Failed to find a running server")
}
<|endoftext|>


<commit_before>package storage

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

	"github.com/bitmark-inc/bitmarkd/storage/mocks"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"github.com/syndtr/goleveldb/leveldb"
)

const (
	dbName     = "data-access"
	defaultKey = "key"
)

var (
	db           *leveldb.DB
	trx          *leveldb.Batch
	defaultValue = []byte{'a'}
)

func initialiseVars() {
	trx = new(leveldb.Batch)
	if nil == db {
		db, _ = leveldb.OpenFile(dbName, nil)
	}
}

func newMockCache(t *testing.T) (*mocks.MockCache, *gomock.Controller) {
	ctl := gomock.NewController(t)
	return mocks.NewMockCache(ctl), ctl
}

func setupDummyMockCache(t *testing.T) *mocks.MockCache {
	mockCache, ctl := newMockCache(t)
	defer ctl.Finish()

	mockCache.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mockCache.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
	mockCache.EXPECT().Clear().AnyTimes()

	return mockCache
}

func setupTestDataAccess(mockCache *mocks.MockCache) DataAccess {
	return newDA(db, trx, mockCache)
}

func removeDir(dirName string) {
	dirPath, _ := filepath.Abs(dirName)
	_ = os.RemoveAll(dirPath)
}

func teardownTestDataAccess() {
	_ = db.Close()
	removeDir(dbName)
}

func TestMain(m *testing.M) {
	initialiseVars()
	result := m.Run()
	teardownTestDataAccess()
	os.Exit(result)
}

func TestBeginShouldErrorWhenAlreadyInTransaction(t *testing.T) {
	mc := setupDummyMockCache(t)
	da := setupTestDataAccess(mc)

	err := da.Begin()
	assert.Equal(t, nil, err, "first time Begin should with not error")

	err = da.Begin()
	assert.NotEqual(t, nil, err, "second time Begin should return error")
}

func TestCommitUnlockInUse(t *testing.T) {
	mc := setupDummyMockCache(t)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	_ = da.Commit()

	err := da.Begin()
	assert.Equal(t, nil, err, "did not reset internal inUse ")
}

func TestCommitResetTransaction(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	_ = da.Commit()

	actual := da.DumpTx()
	assert.Equal(t, 0, len(actual), "Commit did not reset transaction")
}

func TestCommitWriteToDB(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(defaultValue, false).AnyTimes()
	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	_ = da.Commit()

	actual, _ := da.Get([]byte(defaultKey))
	assert.Equal(t, defaultValue, actual, "commit not write to db")
}

func TestPutActionCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
}

func TestDeleteActionCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mc.EXPECT().Set(dbPut, "a", []byte{'b'}).Times(1)
	mc.EXPECT().Set(dbDelete, "a", []byte{}).Times(1)
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	fixture := struct {
		key   []byte
		value []byte
	}{
		[]byte{'a'},
		[]byte{'b'},
	}

	_ = da.Begin()
	da.Put(fixture.key, fixture.value)
	da.Delete(fixture.key)
}

func TestCommitClearsCache(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	_ = da.Commit()
}

func TestGetActionReadsFromCache(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(defaultValue, true).Times(1)
	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Clear().Times(0)
	da := setupTestDataAccess(mc)

	fixture := struct {
		key   []byte
		value []byte
	}{
		[]byte(defaultKey),
		defaultValue,
	}

	_ = da.Begin()
	da.Put(fixture.key, fixture.value)
	actual, _ := da.Get(fixture.key)

	assert.Equal(t, fixture.value, actual, "wrong cached value")
}

func TestGetActionReadDBIfNotInCache(t *testing.T) {
	key := "random"
	value := []byte{'a', 'b', 'c'}

	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(value, false).Times(1)
	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(key), value)
	da.Commit()
	actual, _ := da.Get([]byte(key))

	assert.Equal(t, value, actual, "db value not set")
}

func TestInUse(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	da := setupTestDataAccess(mc)

	inUse := da.InUse()
	assert.Equal(t, false, inUse, "inUse default not true")

	_ = da.Begin()
	inUse = da.InUse()
	assert.Equal(t, true, inUse, "inUse not set")
}

func TestAbortResetInUse(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Abort()

	inUse := da.InUse()
	assert.Equal(t, false, inUse, "inUse is not set")
}

func TestAbortResetBatch(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Abort()

	dump := da.DumpTx()
	assert.Equal(t, []byte{}, dump, "batch not reset")
}

func TestAbortResetCache(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Abort()
}

func TestHasCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Get(defaultKey).Return(defaultValue, true).Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	has, err := da.Has([]byte(defaultKey))
	assert.Equal(t, true, has, "cannot cached cached key")
	assert.Equal(t, nil, err, "has with error")
}
<commit_msg>leveldb-2pc: add test case for data access Has, get from db<commit_after>package storage

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

	"github.com/bitmark-inc/bitmarkd/storage/mocks"
	"github.com/golang/mock/gomock"
	"github.com/stretchr/testify/assert"
	"github.com/syndtr/goleveldb/leveldb"
)

const (
	dbName     = "data-access"
	defaultKey = "key"
)

var (
	db           *leveldb.DB
	trx          *leveldb.Batch
	defaultValue = []byte{'a'}
)

func initialiseVars() {
	trx = new(leveldb.Batch)
	if nil == db {
		db, _ = leveldb.OpenFile(dbName, nil)
	}
}

func newMockCache(t *testing.T) (*mocks.MockCache, *gomock.Controller) {
	ctl := gomock.NewController(t)
	return mocks.NewMockCache(ctl), ctl
}

func setupDummyMockCache(t *testing.T) *mocks.MockCache {
	mockCache, ctl := newMockCache(t)
	defer ctl.Finish()

	mockCache.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mockCache.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
	mockCache.EXPECT().Clear().AnyTimes()

	return mockCache
}

func setupTestDataAccess(mockCache *mocks.MockCache) DataAccess {
	return newDA(db, trx, mockCache)
}

func removeDir(dirName string) {
	dirPath, _ := filepath.Abs(dirName)
	_ = os.RemoveAll(dirPath)
}

func teardownTestDataAccess() {
	_ = db.Close()
	removeDir(dbName)
}

func TestMain(m *testing.M) {
	initialiseVars()
	result := m.Run()
	teardownTestDataAccess()
	os.Exit(result)
}

func TestBeginShouldErrorWhenAlreadyInTransaction(t *testing.T) {
	mc := setupDummyMockCache(t)
	da := setupTestDataAccess(mc)

	err := da.Begin()
	assert.Equal(t, nil, err, "first time Begin should with not error")

	err = da.Begin()
	assert.NotEqual(t, nil, err, "second time Begin should return error")
}

func TestCommitUnlockInUse(t *testing.T) {
	mc := setupDummyMockCache(t)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	_ = da.Commit()

	err := da.Begin()
	assert.Equal(t, nil, err, "did not reset internal inUse ")
}

func TestCommitResetTransaction(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	_ = da.Commit()

	actual := da.DumpTx()
	assert.Equal(t, 0, len(actual), "Commit did not reset transaction")
}

func TestCommitWriteToDB(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(defaultValue, false).AnyTimes()
	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	_ = da.Commit()

	actual, _ := da.Get([]byte(defaultKey))
	assert.Equal(t, defaultValue, actual, "commit not write to db")
}

func TestPutActionCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
}

func TestDeleteActionCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mc.EXPECT().Set(dbPut, "a", []byte{'b'}).Times(1)
	mc.EXPECT().Set(dbDelete, "a", []byte{}).Times(1)
	mc.EXPECT().Clear().AnyTimes()
	da := setupTestDataAccess(mc)

	fixture := struct {
		key   []byte
		value []byte
	}{
		[]byte{'a'},
		[]byte{'b'},
	}

	_ = da.Begin()
	da.Put(fixture.key, fixture.value)
	da.Delete(fixture.key)
}

func TestCommitClearsCache(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return([]byte{}, true).AnyTimes()
	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	_ = da.Commit()
}

func TestGetActionReadsFromCache(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(defaultValue, true).Times(1)
	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Clear().Times(0)
	da := setupTestDataAccess(mc)

	fixture := struct {
		key   []byte
		value []byte
	}{
		[]byte(defaultKey),
		defaultValue,
	}

	_ = da.Begin()
	da.Put(fixture.key, fixture.value)
	actual, _ := da.Get(fixture.key)

	assert.Equal(t, fixture.value, actual, "wrong cached value")
}

func TestGetActionReadDBIfNotInCache(t *testing.T) {
	key := "random"
	value := []byte{'a', 'b', 'c'}

	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(value, false).Times(1)
	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(key), value)
	da.Commit()
	actual, _ := da.Get([]byte(key))

	assert.Equal(t, value, actual, "db value not set")
}

func TestInUse(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	da := setupTestDataAccess(mc)

	inUse := da.InUse()
	assert.Equal(t, false, inUse, "inUse default not true")

	_ = da.Begin()
	inUse = da.InUse()
	assert.Equal(t, true, inUse, "inUse not set")
}

func TestAbortResetInUse(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Abort()

	inUse := da.InUse()
	assert.Equal(t, false, inUse, "inUse is not set")
}

func TestAbortResetBatch(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Abort()

	dump := da.DumpTx()
	assert.Equal(t, []byte{}, dump, "batch not reset")
}

func TestAbortResetCache(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Abort()
}

func TestHasCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Set(dbPut, defaultKey, defaultValue).Times(1)
	mc.EXPECT().Get(defaultKey).Return(defaultValue, true).Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	has, err := da.Has([]byte(defaultKey))
	assert.Equal(t, true, has, "cannot cached cached key")
	assert.Equal(t, nil, err, "has with error")
}

func TestHasNotCached(t *testing.T) {
	mc, ctl := newMockCache(t)
	defer ctl.Finish()

	mc.EXPECT().Get(gomock.Any()).Return(defaultValue, false).Times(1)
	mc.EXPECT().Set(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
	mc.EXPECT().Clear().Times(1)
	da := setupTestDataAccess(mc)

	_ = da.Begin()
	da.Put([]byte(defaultKey), defaultValue)
	da.Commit()
	has, _ := da.Has([]byte(defaultKey))
	assert.Equal(t, true, has, "didn't check db")
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * 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 (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/naoina/toml"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
)

const (
	flagClobber = 1 << iota
	flagForce
	flagVerbose
)

func parse(filename string) (*config, error) {
	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	conf := &config{}
	switch path.Ext(filename) {
	case ".json":
		if err := json.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case ".toml":
		if err := toml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case ".yaml":
		if err := yaml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unsupported configuration file format")
	}

	return conf, nil
}

func usage() {
	fmt.Printf("Usage: %s [options] conf [src]\n", os.Args[0])
	fmt.Print("http://foosoft.net/projects/homemaker/\n\n")
	fmt.Print("Parameters:\n")
	flag.PrintDefaults()
}

func makeAbsPath(path string) string {
	path, err := filepath.Abs(path)
	if err != nil {
		log.Fatal(err)
	}

	return path
}

func main() {
	currUsr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	taskName := flag.String("task", "default", "name of task to execute")
	dstDir := flag.String("dest", currUsr.HomeDir, "target directory for tasks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")

	flag.Usage = usage
	flag.Parse()

	flags := 0
	if *clobber {
		flags |= flagClobber
	}
	if *force {
		flags |= flagForce
	}
	if *verbose {
		flags |= flagVerbose
	}

	if flag.NArg() == 2 {
		conf, err := parse(flag.Arg(0))
		if err != nil {
			log.Fatal(err)
		}

		if err := conf.process(makeAbsPath(flag.Arg(1)), makeAbsPath(*dstDir), *taskName, flags); err != nil {
			log.Fatal(err)
		}
	} else {
		usage()
		os.Exit(2)
	}
}
<commit_msg>Log usage instructions to stderr instead of stdout<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * 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 (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/naoina/toml"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
)

const (
	flagClobber = 1 << iota
	flagForce
	flagVerbose
)

func parse(filename string) (*config, error) {
	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	conf := &config{}
	switch path.Ext(filename) {
	case ".json":
		if err := json.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case ".toml":
		if err := toml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	case ".yaml":
		if err := yaml.Unmarshal(bytes, &conf); err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unsupported configuration file format")
	}

	return conf, nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] conf [src]\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "http://foosoft.net/projects/homemaker/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func makeAbsPath(path string) string {
	path, err := filepath.Abs(path)
	if err != nil {
		log.Fatal(err)
	}

	return path
}

func main() {
	currUsr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	taskName := flag.String("task", "default", "name of task to execute")
	dstDir := flag.String("dest", currUsr.HomeDir, "target directory for tasks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")

	flag.Usage = usage
	flag.Parse()

	flags := 0
	if *clobber {
		flags |= flagClobber
	}
	if *force {
		flags |= flagForce
	}
	if *verbose {
		flags |= flagVerbose
	}

	if flag.NArg() == 2 {
		conf, err := parse(flag.Arg(0))
		if err != nil {
			log.Fatal(err)
		}

		if err := conf.process(makeAbsPath(flag.Arg(1)), makeAbsPath(*dstDir), *taskName, flags); err != nil {
			log.Fatal(err)
		}
	} else {
		usage()
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package html

var (
	singleQuoteEntityBytes = []byte("'")
	doubleQuoteEntityBytes = []byte(""")
)

// EscapeAttrVal returns the escaped attribute value bytes with quotes. Either single or double quotes are used, whichever is shorter. If there are no quotes present in the value and the value is in HTML (not XML), it will return the value without quotes.
func EscapeAttrVal(buf *[]byte, orig, b []byte, isXML bool) []byte {
	singles := 0
	doubles := 0
	unquoted := true
	entities := false
	for _, c := range b {
		if charTable[c] {
			unquoted = false
			if c == '"' {
				doubles++
			} else if c == '\'' {
				singles++
			}
		}
	}
	if unquoted && !isXML {
		return b
	} else if !entities && len(orig) == len(b)+2 && (singles == 0 && orig[0] == '\'' || doubles == 0 && orig[0] == '"') {
		return orig
	}

	n := len(b) + 2
	var quote byte
	var escapedQuote []byte
	if singles >= doubles || isXML {
		n += doubles * 4
		quote = '"'
		escapedQuote = doubleQuoteEntityBytes
	} else {
		n += singles * 4
		quote = '\''
		escapedQuote = singleQuoteEntityBytes
	}
	if n > cap(*buf) {
		*buf = make([]byte, 0, n) // maximum size, not actual size
	}
	t := (*buf)[:n] // maximum size, not actual size
	t[0] = quote
	j := 1
	start := 0
	for i, c := range b {
		if c == quote {
			j += copy(t[j:], b[start:i])
			j += copy(t[j:], escapedQuote)
			start = i + 1
		}
	}
	j += copy(t[j:], b[start:])
	t[j] = quote
	return t[:j+1]
}

var charTable = [256]bool{
	// ASCII
	false, false, false, false, false, false, false, false,
	false, true, true, false, true, true, false, false, // tab, line feed, form feed, carriage return
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	true, false, true, false, false, false, false, true, // space, "), '
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, true, true, true, false, // <, =, >

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	true, false, false, false, false, false, false, false, // `
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	// non-ASCII
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
}
<commit_msg>HTML: keep original single/double quote for attribute values as much as possible<commit_after>package html

var (
	singleQuoteEntityBytes = []byte("'")
	doubleQuoteEntityBytes = []byte(""")
)

// EscapeAttrVal returns the escaped attribute value bytes with quotes. Either single or double quotes are used, whichever is shorter. If there are no quotes present in the value and the value is in HTML (not XML), it will return the value without quotes.
func EscapeAttrVal(buf *[]byte, b []byte, origQuote byte, mustQuote, isXML bool) []byte {
	singles := 0
	doubles := 0
	unquoted := true
	for _, c := range b {
		if charTable[c] {
			unquoted = false
			if c == '"' {
				doubles++
			} else if c == '\'' {
				singles++
			}
		}
	}
	if unquoted && (!mustQuote || origQuote == 0) && !isXML {
		return b
	} else if singles == 0 && origQuote == '\'' || doubles == 0 && origQuote == '"' {
		t := (*buf)[:len(b)+2]
		t[0] = origQuote
		copy(t[1:], b)
		t[1+len(b)] = origQuote
		return t
	}

	n := len(b) + 2
	var quote byte
	var escapedQuote []byte
	if singles >= doubles || isXML {
		n += doubles * 4
		quote = '"'
		escapedQuote = doubleQuoteEntityBytes
		if singles == doubles && origQuote == '\'' {
			quote = '\''
			escapedQuote = singleQuoteEntityBytes
		}
	} else {
		n += singles * 4
		quote = '\''
		escapedQuote = singleQuoteEntityBytes
	}
	if n > cap(*buf) {
		*buf = make([]byte, 0, n) // maximum size, not actual size
	}
	t := (*buf)[:n] // maximum size, not actual size
	t[0] = quote
	j := 1
	start := 0
	for i, c := range b {
		if c == quote {
			j += copy(t[j:], b[start:i])
			j += copy(t[j:], escapedQuote)
			start = i + 1
		}
	}
	j += copy(t[j:], b[start:])
	t[j] = quote
	return t[:j+1]
}

var charTable = [256]bool{
	// ASCII
	false, false, false, false, false, false, false, false,
	false, true, true, false, true, true, false, false, // tab, line feed, form feed, carriage return
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	true, false, true, false, false, false, false, true, // space, "), '
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, true, true, true, false, // <, =, >

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	true, false, false, false, false, false, false, false, // `
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	// non-ASCII
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,

	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false,
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
)

// EvalReadDataDiff is an EvalNode implementation that executes a data
// resource's ReadDataDiff method to discover what attributes it exports.
type EvalReadDataDiff struct {
	Provider    *ResourceProvider
	Output      **InstanceDiff
	OutputState **InstanceState
	Config      **ResourceConfig
	Info        *InstanceInfo

	// Set Previous when re-evaluating diff during apply, to ensure that
	// the "Destroy" flag is preserved.
	Previous **InstanceDiff
}

func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
	// TODO: test

	err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreDiff(n.Info, nil)
	})
	if err != nil {
		return nil, err
	}

	var diff *InstanceDiff

	if n.Previous != nil && *n.Previous != nil && (*n.Previous).GetDestroy() {
		// If we're re-diffing for a diff that was already planning to
		// destroy, then we'll just continue with that plan.
		diff = &InstanceDiff{Destroy: true}
	} else {
		provider := *n.Provider
		config := *n.Config

		var err error
		diff, err = provider.ReadDataDiff(n.Info, config)
		if err != nil {
			return nil, err
		}
		if diff == nil {
			diff = new(InstanceDiff)
		}

		// id is always computed, because we're always "creating a new resource"
		diff.init()
		diff.SetAttribute("id", &ResourceAttrDiff{
			Old:         "",
			NewComputed: true,
			RequiresNew: true,
			Type:        DiffAttrOutput,
		})
	}

	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostDiff(n.Info, diff)
	})
	if err != nil {
		return nil, err
	}

	*n.Output = diff

	if n.OutputState != nil {
		state := &InstanceState{}
		*n.OutputState = state

		// Apply the diff to the returned state, so the state includes
		// any attribute values that are not computed.
		if !diff.Empty() && n.OutputState != nil {
			*n.OutputState = state.MergeDiff(diff)
		}
	}

	return nil, nil
}

// EvalReadDataApply is an EvalNode implementation that executes a data
// resource's ReadDataApply method to read data from the data source.
type EvalReadDataApply struct {
	Provider *ResourceProvider
	Output   **InstanceState
	Diff     **InstanceDiff
	Info     *InstanceInfo
}

func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
	// TODO: test
	provider := *n.Provider
	diff := *n.Diff

	// If the diff is for *destroying* this resource then we'll
	// just drop its state and move on, since data resources don't
	// support an actual "destroy" action.
	if diff != nil && diff.GetDestroy() {
		if n.Output != nil {
			*n.Output = nil
		}
		return nil, nil
	}

	// For the purpose of external hooks we present a data apply as a
	// "Refresh" rather than an "Apply" because creating a data source
	// is presented to users/callers as a "read" operation.
	err := ctx.Hook(func(h Hook) (HookAction, error) {
		// We don't have a state yet, so we'll just give the hook an
		// empty one to work with.
		return h.PreRefresh(n.Info, &InstanceState{})
	})
	if err != nil {
		return nil, err
	}

	state, err := provider.ReadDataApply(n.Info, diff)
	if err != nil {
		return nil, fmt.Errorf("%s: %s", n.Info.Id, err)
	}

	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostRefresh(n.Info, state)
	})
	if err != nil {
		return nil, err
	}

	if n.Output != nil {
		*n.Output = state
	}

	return nil, nil
}
<commit_msg>core: don't force data resource id diff to be empty<commit_after>package terraform

import (
	"fmt"
)

// EvalReadDataDiff is an EvalNode implementation that executes a data
// resource's ReadDataDiff method to discover what attributes it exports.
type EvalReadDataDiff struct {
	Provider    *ResourceProvider
	Output      **InstanceDiff
	OutputState **InstanceState
	Config      **ResourceConfig
	Info        *InstanceInfo

	// Set Previous when re-evaluating diff during apply, to ensure that
	// the "Destroy" flag is preserved.
	Previous **InstanceDiff
}

func (n *EvalReadDataDiff) Eval(ctx EvalContext) (interface{}, error) {
	// TODO: test

	err := ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PreDiff(n.Info, nil)
	})
	if err != nil {
		return nil, err
	}

	var diff *InstanceDiff

	if n.Previous != nil && *n.Previous != nil && (*n.Previous).GetDestroy() {
		// If we're re-diffing for a diff that was already planning to
		// destroy, then we'll just continue with that plan.
		diff = &InstanceDiff{Destroy: true}
	} else {
		provider := *n.Provider
		config := *n.Config

		var err error
		diff, err = provider.ReadDataDiff(n.Info, config)
		if err != nil {
			return nil, err
		}
		if diff == nil {
			diff = new(InstanceDiff)
		}

		// if id isn't explicitly set then it's always computed, because we're
		// always "creating a new resource".
		diff.init()
		if _, ok := diff.Attributes["id"]; !ok {
			diff.SetAttribute("id", &ResourceAttrDiff{
				Old:         "",
				NewComputed: true,
				RequiresNew: true,
				Type:        DiffAttrOutput,
			})
		}
	}

	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostDiff(n.Info, diff)
	})
	if err != nil {
		return nil, err
	}

	*n.Output = diff

	if n.OutputState != nil {
		state := &InstanceState{}
		*n.OutputState = state

		// Apply the diff to the returned state, so the state includes
		// any attribute values that are not computed.
		if !diff.Empty() && n.OutputState != nil {
			*n.OutputState = state.MergeDiff(diff)
		}
	}

	return nil, nil
}

// EvalReadDataApply is an EvalNode implementation that executes a data
// resource's ReadDataApply method to read data from the data source.
type EvalReadDataApply struct {
	Provider *ResourceProvider
	Output   **InstanceState
	Diff     **InstanceDiff
	Info     *InstanceInfo
}

func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
	// TODO: test
	provider := *n.Provider
	diff := *n.Diff

	// If the diff is for *destroying* this resource then we'll
	// just drop its state and move on, since data resources don't
	// support an actual "destroy" action.
	if diff != nil && diff.GetDestroy() {
		if n.Output != nil {
			*n.Output = nil
		}
		return nil, nil
	}

	// For the purpose of external hooks we present a data apply as a
	// "Refresh" rather than an "Apply" because creating a data source
	// is presented to users/callers as a "read" operation.
	err := ctx.Hook(func(h Hook) (HookAction, error) {
		// We don't have a state yet, so we'll just give the hook an
		// empty one to work with.
		return h.PreRefresh(n.Info, &InstanceState{})
	})
	if err != nil {
		return nil, err
	}

	state, err := provider.ReadDataApply(n.Info, diff)
	if err != nil {
		return nil, fmt.Errorf("%s: %s", n.Info.Id, err)
	}

	err = ctx.Hook(func(h Hook) (HookAction, error) {
		return h.PostRefresh(n.Info, state)
	})
	if err != nil {
		return nil, err
	}

	if n.Output != nil {
		*n.Output = state
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package swgohgg

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

// Collection parses a player home page and returns the entire collection list.
func (c *Client) Collection() (collection Collection, err error) {
	url := fmt.Sprintf("https://swgoh.gg/u/%s/collection/", c.profile)
	doc, err := c.Get(url)
	if err != nil {
		return nil, err
	}
	doc.Find(".collection-char-list .collection-char").Each(func(i int, s *goquery.Selection) {
		char := parseChar(s)
		if !collection.Contains(char.Name) {
			collection = append(collection, char)
		}
	})
	sort.Sort(ByStars(collection, false))
	return collection, nil
}

// Collection is a list of characters. Usually loaded up by the call
// to client.Collection().
type Collection []*Char

// Contains lookup a character by name and checks if it is present in the collection.
func (r Collection) Contains(char string) bool {
	for i := range r {
		if strings.ToLower(r[i].Name) == strings.ToLower(char) {
			return true
		}
	}
	return false
}

// ContainsAll checks if the collection has all the provided items.
func (r Collection) ContainsAll(chars ...string) bool {
	for _, char := range chars {
		if !r.Contains(char) {
			return false
		}
	}
	return true
}

// MinRarity returns a filtered collection containing the required min rarity.
func (r Collection) MinRarity(stars int) (filtered Collection) {
	for i := range r {
		if r[i].Stars >= stars {
			filtered = append(filtered, r[i])
		}
	}
	return filtered
}

// Char is a single character unit holding the basic stats.
type Char struct {
	Name  string
	Stars int
	Level int
	Gear  int
}

func (c *Char) String() string {
	if c == nil {
		return "nil"
	}
	return fmt.Sprintf("%s %d* G%d Lvl%d", c.Name, c.Stars, c.Gear, c.Level)
}

func parseChar(s *goquery.Selection) *Char {
	var char Char
	char.Name = s.Find(".collection-char-name-link").Text()
	char.Level, _ = strconv.Atoi(s.Find(".char-portrait-full-level").Text())
	char.Gear = gearLevel(s)
	char.Stars = stars(s)
	return &char
}

func stars(s *goquery.Selection) int {
	level := 0
	s.Find(".star").Each(func(i int, star *goquery.Selection) {
		if star.HasClass("star-inactive") {
			return
		}
		level++
	})
	return level
}

func gearLevel(s *goquery.Selection) int {
	switch s.Find(".char-portrait-full-gear-level").Text() {
	case "XII":
		return 12
	case "XI":
		return 11
	case "X":
		return 10
	case "IX":
		return 9
	case "VIII":
		return 8
	case "VII":
		return 7
	case "VI":
		return 6
	case "V":
		return 5
	case "IV":
		return 4
	case "III":
		return 3
	case "II":
		return 2
	case "I":
		return 1
	default:
		return 0
	}
}

// CharacterStats contains all detailed character stats, as displayed in the game.
type CharacterStats struct {
	Name      string
	Level     int
	GearLevel int
	Stars     int

	// Current character gallactic power
	GalacticPower int

	// List of skils of this character
	Skills []CharacterSkill

	// Basic Stats
	STR                int
	AGI                int
	INT                int
	StrenghGrowth      float64
	AgilityGrowth      float64
	IntelligenceGrowth float64

	// General
	Health         int
	Protection     int
	Speed          int
	CriticalDamage float64
	Potency        float64
	Tenacity       float64
	HealthSteal    float64

	PhysicalDamage     int
	PhysicalCritChance float64
	SpecialDamage      int
	SpecialCritChance  float64
}

// CharacterSkill holds basic info about a character skill.
type CharacterSkill struct {
	Name  string
	Level int
}

// CharacterStats fetches the characer detail page and extracts all stats.
func (c *Client) CharacterStats(char string) (*CharacterStats, error) {
	charSlug := CharSlug(CharName(char))
	doc, err := c.Get(fmt.Sprintf("https://swgoh.gg/u/%s/collection/%s/", c.profile, charSlug))
	if err != nil {
		return nil, fmt.Errorf("swgohgg: profile %s may not have %s activated. (err=%v)", c.profile, CharName(char), err.Error())
	}

	charStats := &CharacterStats{}
	charStats.Name = strings.TrimSpace(doc.Find(".pc-char-overview-name").Text())
	charStats.Level = atoi(doc.Find(".char-portrait-full-level").Text())
	charStats.Stars = int(stars(doc.Find(".player-char-portrait")))
	gearInfo := strings.Split(doc.Find(".pc-gear").First().Find(".pc-heading").First().AttrOr("title", "Gear -1 "), " ")
	if len(gearInfo) > 1 {
		charStats.GearLevel = atoi(gearInfo[1])
	}
	charStats.GalacticPower = atoi(doc.Find(".unit-gp-stat-amount-current").First().Text())
	// Skills
	doc.Find(".pc-skills-list").First().Find(".pc-skill").Each(func(i int, s *goquery.Selection) {
		skill := CharacterSkill{}
		skill.Name = s.Find(".pc-skill-name").First().Text()
		skill.Level = skillLevel(s)
		charStats.Skills = append(charStats.Skills, skill)
	})
	//Stats
	doc.Find(".unit-stat-group-stat").Each(func(i int, s *goquery.Selection) {
		name, value := s.Find(".unit-stat-group-stat-label").Text(), s.Find(".unit-stat-group-stat-value").Text()
		value = strings.TrimSpace(value)
		if strings.Contains(value, "(") {
			// Strip the later part for now.
			// We can use the "added from mods" properties later
			value = strings.Split(value, "(")[0]
		}

		switch strings.TrimSpace(name) {
		case "Strength (STR)":
			charStats.STR = atoi(value)
		case "Agility (AGI)":
			charStats.AGI = atoi(value)
		case "Intelligence (INT)":
			charStats.INT = atoi(value)
		case "Strength Growth":
			charStats.StrenghGrowth = atof(value)
		case "Agility Growth":
			charStats.AgilityGrowth = atof(value)
		case "Intelligence Growth":
			charStats.IntelligenceGrowth = atof(value)
		case "Health":
			charStats.Health = atoi(value)
		case "Protection":
			charStats.Protection = atoi(value)
		case "Speed":
			charStats.Speed = atoi(value)
		case "Critical Damage":
			charStats.CriticalDamage = atof(value)
		case "Potency":
			charStats.Potency = atof(value)
		case "Tenacity":
			charStats.Tenacity = atof(value)
		case "Health Steal":
			charStats.HealthSteal = atof(value)
		case "Physical Damage":
			charStats.PhysicalDamage = atoi(value)
		case "Special Damage":
			charStats.SpecialDamage = atoi(value)
		case "Physical Critical Chance":
			charStats.PhysicalCritChance = atof(value)
		case "Special Critical Chance":
			charStats.SpecialCritChance = atof(value)
		}
	})
	return charStats, nil
}

func skillLevel(s *goquery.Selection) int {
	title := s.Find(".pc-skill-levels").First().AttrOr("data-title", "Level -1")
	// Title is in the form 'Level X of Y'
	fields := strings.Fields(title)
	if len(fields) >= 2 {
		return atoi(fields[1])
	}
	return -1
}

func atof(src string) float64 {
	src = strings.Replace(src, "%", "", -1)
	v, _ := strconv.ParseFloat(src, 64)
	return v
}

// atoi best-effort convertion to int, return 0 if unparseable
func atoi(src string) int {
	src = strings.Replace(src, ",", "", -1)
	src = strings.Replace(src, ".", "", -1)
	src = strings.Replace(src, "%", "", -1)
	v, _ := strconv.ParseInt(src, 10, 32)
	return int(v)
}
<commit_msg>Fixed -collection character names and related API method.<commit_after>package swgohgg

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

// Collection parses a player home page and returns the entire collection list.
func (c *Client) Collection() (collection Collection, err error) {
	url := fmt.Sprintf("https://swgoh.gg/u/%s/collection/", c.profile)
	doc, err := c.Get(url)
	if err != nil {
		return nil, err
	}
	doc.Find(".collection-char-list .collection-char").Each(func(i int, s *goquery.Selection) {
		char := parseChar(s)
		if !collection.Contains(char.Name) {
			collection = append(collection, char)
		}
	})
	sort.Sort(ByStars(collection, false))
	return collection, nil
}

// Collection is a list of characters. Usually loaded up by the call
// to client.Collection().
type Collection []*Char

// Contains lookup a character by name and checks if it is present in the collection.
func (r Collection) Contains(char string) bool {
	for i := range r {
		if strings.ToLower(r[i].Name) == strings.ToLower(char) {
			return true
		}
	}
	return false
}

// ContainsAll checks if the collection has all the provided items.
func (r Collection) ContainsAll(chars ...string) bool {
	for _, char := range chars {
		if !r.Contains(char) {
			return false
		}
	}
	return true
}

// MinRarity returns a filtered collection containing the required min rarity.
func (r Collection) MinRarity(stars int) (filtered Collection) {
	for i := range r {
		if r[i].Stars >= stars {
			filtered = append(filtered, r[i])
		}
	}
	return filtered
}

// Char is a single character unit holding the basic stats.
type Char struct {
	Name  string
	Stars int
	Level int
	Gear  int
}

func (c *Char) String() string {
	if c == nil {
		return "nil"
	}
	return fmt.Sprintf("%s %d* G%d Lvl%d", c.Name, c.Stars, c.Gear, c.Level)
}

func parseChar(s *goquery.Selection) *Char {
	var char Char
	char.Name = strings.TrimSpace(s.Find(".collection-char-name-link").Text())
	char.Level, _ = strconv.Atoi(s.Find(".char-portrait-full-level").Text())
	char.Gear = gearLevel(s)
	char.Stars = stars(s)
	return &char
}

func stars(s *goquery.Selection) int {
	level := 0
	s.Find(".star").Each(func(i int, star *goquery.Selection) {
		if star.HasClass("star-inactive") {
			return
		}
		level++
	})
	return level
}

func gearLevel(s *goquery.Selection) int {
	switch s.Find(".char-portrait-full-gear-level").Text() {
	case "XII":
		return 12
	case "XI":
		return 11
	case "X":
		return 10
	case "IX":
		return 9
	case "VIII":
		return 8
	case "VII":
		return 7
	case "VI":
		return 6
	case "V":
		return 5
	case "IV":
		return 4
	case "III":
		return 3
	case "II":
		return 2
	case "I":
		return 1
	default:
		return 0
	}
}

// CharacterStats contains all detailed character stats, as displayed in the game.
type CharacterStats struct {
	Name      string
	Level     int
	GearLevel int
	Stars     int

	// Current character gallactic power
	GalacticPower int

	// List of skils of this character
	Skills []CharacterSkill

	// Basic Stats
	STR                int
	AGI                int
	INT                int
	StrenghGrowth      float64
	AgilityGrowth      float64
	IntelligenceGrowth float64

	// General
	Health         int
	Protection     int
	Speed          int
	CriticalDamage float64
	Potency        float64
	Tenacity       float64
	HealthSteal    float64

	PhysicalDamage     int
	PhysicalCritChance float64
	SpecialDamage      int
	SpecialCritChance  float64
}

// CharacterSkill holds basic info about a character skill.
type CharacterSkill struct {
	Name  string
	Level int
}

// CharacterStats fetches the characer detail page and extracts all stats.
func (c *Client) CharacterStats(char string) (*CharacterStats, error) {
	charSlug := CharSlug(CharName(char))
	doc, err := c.Get(fmt.Sprintf("https://swgoh.gg/u/%s/collection/%s/", c.profile, charSlug))
	if err != nil {
		return nil, fmt.Errorf("swgohgg: profile %s may not have %s activated. (err=%v)", c.profile, CharName(char), err.Error())
	}

	charStats := &CharacterStats{}
	charStats.Name = strings.TrimSpace(doc.Find(".pc-char-overview-name").Text())
	charStats.Level = atoi(doc.Find(".char-portrait-full-level").Text())
	charStats.Stars = int(stars(doc.Find(".player-char-portrait")))
	gearInfo := strings.Split(doc.Find(".pc-gear").First().Find(".pc-heading").First().AttrOr("title", "Gear -1 "), " ")
	if len(gearInfo) > 1 {
		charStats.GearLevel = atoi(gearInfo[1])
	}
	charStats.GalacticPower = atoi(doc.Find(".unit-gp-stat-amount-current").First().Text())
	// Skills
	doc.Find(".pc-skills-list").First().Find(".pc-skill").Each(func(i int, s *goquery.Selection) {
		skill := CharacterSkill{}
		skill.Name = s.Find(".pc-skill-name").First().Text()
		skill.Level = skillLevel(s)
		charStats.Skills = append(charStats.Skills, skill)
	})
	//Stats
	doc.Find(".unit-stat-group-stat").Each(func(i int, s *goquery.Selection) {
		name, value := s.Find(".unit-stat-group-stat-label").Text(), s.Find(".unit-stat-group-stat-value").Text()
		value = strings.TrimSpace(value)
		if strings.Contains(value, "(") {
			// Strip the later part for now.
			// We can use the "added from mods" properties later
			value = strings.Split(value, "(")[0]
		}

		switch strings.TrimSpace(name) {
		case "Strength (STR)":
			charStats.STR = atoi(value)
		case "Agility (AGI)":
			charStats.AGI = atoi(value)
		case "Intelligence (INT)":
			charStats.INT = atoi(value)
		case "Strength Growth":
			charStats.StrenghGrowth = atof(value)
		case "Agility Growth":
			charStats.AgilityGrowth = atof(value)
		case "Intelligence Growth":
			charStats.IntelligenceGrowth = atof(value)
		case "Health":
			charStats.Health = atoi(value)
		case "Protection":
			charStats.Protection = atoi(value)
		case "Speed":
			charStats.Speed = atoi(value)
		case "Critical Damage":
			charStats.CriticalDamage = atof(value)
		case "Potency":
			charStats.Potency = atof(value)
		case "Tenacity":
			charStats.Tenacity = atof(value)
		case "Health Steal":
			charStats.HealthSteal = atof(value)
		case "Physical Damage":
			charStats.PhysicalDamage = atoi(value)
		case "Special Damage":
			charStats.SpecialDamage = atoi(value)
		case "Physical Critical Chance":
			charStats.PhysicalCritChance = atof(value)
		case "Special Critical Chance":
			charStats.SpecialCritChance = atof(value)
		}
	})
	return charStats, nil
}

func skillLevel(s *goquery.Selection) int {
	title := s.Find(".pc-skill-levels").First().AttrOr("data-title", "Level -1")
	// Title is in the form 'Level X of Y'
	fields := strings.Fields(title)
	if len(fields) >= 2 {
		return atoi(fields[1])
	}
	return -1
}

func atof(src string) float64 {
	src = strings.Replace(src, "%", "", -1)
	v, _ := strconv.ParseFloat(src, 64)
	return v
}

// atoi best-effort convertion to int, return 0 if unparseable
func atoi(src string) int {
	src = strings.Replace(src, ",", "", -1)
	src = strings.Replace(src, ".", "", -1)
	src = strings.Replace(src, "%", "", -1)
	v, _ := strconv.ParseInt(src, 10, 32)
	return int(v)
}
<|endoftext|>


<commit_before>package config // import "github.com/docker/docker/daemon/config"

import (
	"fmt"
	"net"

	"github.com/docker/docker/api/types"
	containertypes "github.com/docker/docker/api/types/container"
	"github.com/docker/docker/opts"
	units "github.com/docker/go-units"
)

const (
	// DefaultIpcMode is default for container's IpcMode, if not set otherwise
	DefaultIpcMode = containertypes.IPCModePrivate

	// DefaultCgroupNamespaceMode is the default mode for containers cgroup namespace when using cgroups v2.
	DefaultCgroupNamespaceMode = containertypes.CgroupnsModePrivate

	// DefaultCgroupV1NamespaceMode is the default mode for containers cgroup namespace when using cgroups v1.
	DefaultCgroupV1NamespaceMode = containertypes.CgroupnsModeHost

	// StockRuntimeName is the reserved name/alias used to represent the
	// OCI runtime being shipped with the docker daemon package.
	StockRuntimeName = "runc"
)

// BridgeConfig stores all the bridge driver specific
// configuration.
type BridgeConfig struct {
	commonBridgeConfig

	// Fields below here are platform specific.
	DefaultIP                   net.IP `json:"ip,omitempty"`
	IP                          string `json:"bip,omitempty"`
	DefaultGatewayIPv4          net.IP `json:"default-gateway,omitempty"`
	DefaultGatewayIPv6          net.IP `json:"default-gateway-v6,omitempty"`
	InterContainerCommunication bool   `json:"icc,omitempty"`

	EnableIPv6          bool   `json:"ipv6,omitempty"`
	EnableIPTables      bool   `json:"iptables,omitempty"`
	EnableIP6Tables     bool   `json:"ip6tables,omitempty"`
	EnableIPForward     bool   `json:"ip-forward,omitempty"`
	EnableIPMasq        bool   `json:"ip-masq,omitempty"`
	EnableUserlandProxy bool   `json:"userland-proxy,omitempty"`
	UserlandProxyPath   string `json:"userland-proxy-path,omitempty"`
	FixedCIDRv6         string `json:"fixed-cidr-v6,omitempty"`
}

// Config defines the configuration of a docker daemon.
// It includes json tags to deserialize configuration from a file
// using the same names that the flags in the command line uses.
type Config struct {
	CommonConfig

	// Fields below here are platform specific.
	Runtimes             map[string]types.Runtime `json:"runtimes,omitempty"`
	DefaultInitBinary    string                   `json:"default-init,omitempty"`
	CgroupParent         string                   `json:"cgroup-parent,omitempty"`
	EnableSelinuxSupport bool                     `json:"selinux-enabled,omitempty"`
	RemappedRoot         string                   `json:"userns-remap,omitempty"`
	Ulimits              map[string]*units.Ulimit `json:"default-ulimits,omitempty"`
	CPURealtimePeriod    int64                    `json:"cpu-rt-period,omitempty"`
	CPURealtimeRuntime   int64                    `json:"cpu-rt-runtime,omitempty"`
	OOMScoreAdjust       int                      `json:"oom-score-adjust,omitempty"`
	Init                 bool                     `json:"init,omitempty"`
	InitPath             string                   `json:"init-path,omitempty"`
	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
	ShmSize              opts.MemBytes            `json:"default-shm-size,omitempty"`
	NoNewPrivileges      bool                     `json:"no-new-privileges,omitempty"`
	IpcMode              string                   `json:"default-ipc-mode,omitempty"`
	CgroupNamespaceMode  string                   `json:"default-cgroupns-mode,omitempty"`
	// ResolvConf is the path to the configuration of the host resolver
	ResolvConf string `json:"resolv-conf,omitempty"`
	Rootless   bool   `json:"rootless,omitempty"`
}

// GetRuntime returns the runtime path and arguments for a given
// runtime name
func (conf *Config) GetRuntime(name string) *types.Runtime {
	conf.Lock()
	defer conf.Unlock()
	if rt, ok := conf.Runtimes[name]; ok {
		return &rt
	}
	return nil
}

// GetAllRuntimes returns a copy of the runtimes map
func (conf *Config) GetAllRuntimes() map[string]types.Runtime {
	conf.Lock()
	rts := conf.Runtimes
	conf.Unlock()
	return rts
}

// GetExecRoot returns the user configured Exec-root
func (conf *Config) GetExecRoot() string {
	return conf.ExecRoot
}

// GetInitPath returns the configured docker-init path
func (conf *Config) GetInitPath() string {
	conf.Lock()
	defer conf.Unlock()
	if conf.InitPath != "" {
		return conf.InitPath
	}
	if conf.DefaultInitBinary != "" {
		return conf.DefaultInitBinary
	}
	return DefaultInitBinary
}

// GetResolvConf returns the appropriate resolv.conf
// Check setupResolvConf on how this is selected
func (conf *Config) GetResolvConf() string {
	return conf.ResolvConf
}

// IsSwarmCompatible defines if swarm mode can be enabled in this config
func (conf *Config) IsSwarmCompatible() error {
	if conf.LiveRestoreEnabled {
		return fmt.Errorf("--live-restore daemon configuration is incompatible with swarm mode")
	}
	return nil
}

func verifyDefaultIpcMode(mode string) error {
	const hint = `use "shareable" or "private"`

	dm := containertypes.IpcMode(mode)
	if !dm.Valid() {
		return fmt.Errorf("default IPC mode setting (%v) is invalid; "+hint, dm)
	}
	if dm != "" && !dm.IsPrivate() && !dm.IsShareable() {
		return fmt.Errorf(`IPC mode "%v" is not supported as default value; `+hint, dm)
	}
	return nil
}

func verifyDefaultCgroupNsMode(mode string) error {
	cm := containertypes.CgroupnsMode(mode)
	if !cm.Valid() {
		return fmt.Errorf(`default cgroup namespace mode (%v) is invalid; use "host" or "private"`, cm)
	}

	return nil
}

// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid.
func (conf *Config) ValidatePlatformConfig() error {
	if err := verifyDefaultIpcMode(conf.IpcMode); err != nil {
		return err
	}

	return verifyDefaultCgroupNsMode(conf.CgroupNamespaceMode)
}

// IsRootless returns conf.Rootless on Linux but false on Windows
func (conf *Config) IsRootless() bool {
	return conf.Rootless
}
<commit_msg>daemon/config: remove unneeded alias<commit_after>package config // import "github.com/docker/docker/daemon/config"

import (
	"fmt"
	"net"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/opts"
	units "github.com/docker/go-units"
)

const (
	// DefaultIpcMode is default for container's IpcMode, if not set otherwise
	DefaultIpcMode = container.IPCModePrivate

	// DefaultCgroupNamespaceMode is the default mode for containers cgroup namespace when using cgroups v2.
	DefaultCgroupNamespaceMode = container.CgroupnsModePrivate

	// DefaultCgroupV1NamespaceMode is the default mode for containers cgroup namespace when using cgroups v1.
	DefaultCgroupV1NamespaceMode = container.CgroupnsModeHost

	// StockRuntimeName is the reserved name/alias used to represent the
	// OCI runtime being shipped with the docker daemon package.
	StockRuntimeName = "runc"
)

// BridgeConfig stores all the bridge driver specific
// configuration.
type BridgeConfig struct {
	commonBridgeConfig

	// Fields below here are platform specific.
	DefaultIP                   net.IP `json:"ip,omitempty"`
	IP                          string `json:"bip,omitempty"`
	DefaultGatewayIPv4          net.IP `json:"default-gateway,omitempty"`
	DefaultGatewayIPv6          net.IP `json:"default-gateway-v6,omitempty"`
	InterContainerCommunication bool   `json:"icc,omitempty"`

	EnableIPv6          bool   `json:"ipv6,omitempty"`
	EnableIPTables      bool   `json:"iptables,omitempty"`
	EnableIP6Tables     bool   `json:"ip6tables,omitempty"`
	EnableIPForward     bool   `json:"ip-forward,omitempty"`
	EnableIPMasq        bool   `json:"ip-masq,omitempty"`
	EnableUserlandProxy bool   `json:"userland-proxy,omitempty"`
	UserlandProxyPath   string `json:"userland-proxy-path,omitempty"`
	FixedCIDRv6         string `json:"fixed-cidr-v6,omitempty"`
}

// Config defines the configuration of a docker daemon.
// It includes json tags to deserialize configuration from a file
// using the same names that the flags in the command line uses.
type Config struct {
	CommonConfig

	// Fields below here are platform specific.
	Runtimes             map[string]types.Runtime `json:"runtimes,omitempty"`
	DefaultInitBinary    string                   `json:"default-init,omitempty"`
	CgroupParent         string                   `json:"cgroup-parent,omitempty"`
	EnableSelinuxSupport bool                     `json:"selinux-enabled,omitempty"`
	RemappedRoot         string                   `json:"userns-remap,omitempty"`
	Ulimits              map[string]*units.Ulimit `json:"default-ulimits,omitempty"`
	CPURealtimePeriod    int64                    `json:"cpu-rt-period,omitempty"`
	CPURealtimeRuntime   int64                    `json:"cpu-rt-runtime,omitempty"`
	OOMScoreAdjust       int                      `json:"oom-score-adjust,omitempty"`
	Init                 bool                     `json:"init,omitempty"`
	InitPath             string                   `json:"init-path,omitempty"`
	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
	ShmSize              opts.MemBytes            `json:"default-shm-size,omitempty"`
	NoNewPrivileges      bool                     `json:"no-new-privileges,omitempty"`
	IpcMode              string                   `json:"default-ipc-mode,omitempty"`
	CgroupNamespaceMode  string                   `json:"default-cgroupns-mode,omitempty"`
	// ResolvConf is the path to the configuration of the host resolver
	ResolvConf string `json:"resolv-conf,omitempty"`
	Rootless   bool   `json:"rootless,omitempty"`
}

// GetRuntime returns the runtime path and arguments for a given
// runtime name
func (conf *Config) GetRuntime(name string) *types.Runtime {
	conf.Lock()
	defer conf.Unlock()
	if rt, ok := conf.Runtimes[name]; ok {
		return &rt
	}
	return nil
}

// GetAllRuntimes returns a copy of the runtimes map
func (conf *Config) GetAllRuntimes() map[string]types.Runtime {
	conf.Lock()
	rts := conf.Runtimes
	conf.Unlock()
	return rts
}

// GetExecRoot returns the user configured Exec-root
func (conf *Config) GetExecRoot() string {
	return conf.ExecRoot
}

// GetInitPath returns the configured docker-init path
func (conf *Config) GetInitPath() string {
	conf.Lock()
	defer conf.Unlock()
	if conf.InitPath != "" {
		return conf.InitPath
	}
	if conf.DefaultInitBinary != "" {
		return conf.DefaultInitBinary
	}
	return DefaultInitBinary
}

// GetResolvConf returns the appropriate resolv.conf
// Check setupResolvConf on how this is selected
func (conf *Config) GetResolvConf() string {
	return conf.ResolvConf
}

// IsSwarmCompatible defines if swarm mode can be enabled in this config
func (conf *Config) IsSwarmCompatible() error {
	if conf.LiveRestoreEnabled {
		return fmt.Errorf("--live-restore daemon configuration is incompatible with swarm mode")
	}
	return nil
}

func verifyDefaultIpcMode(mode string) error {
	const hint = `use "shareable" or "private"`

	dm := container.IpcMode(mode)
	if !dm.Valid() {
		return fmt.Errorf("default IPC mode setting (%v) is invalid; "+hint, dm)
	}
	if dm != "" && !dm.IsPrivate() && !dm.IsShareable() {
		return fmt.Errorf(`IPC mode "%v" is not supported as default value; `+hint, dm)
	}
	return nil
}

func verifyDefaultCgroupNsMode(mode string) error {
	cm := container.CgroupnsMode(mode)
	if !cm.Valid() {
		return fmt.Errorf(`default cgroup namespace mode (%v) is invalid; use "host" or "private"`, cm)
	}

	return nil
}

// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid.
func (conf *Config) ValidatePlatformConfig() error {
	if err := verifyDefaultIpcMode(conf.IpcMode); err != nil {
		return err
	}

	return verifyDefaultCgroupNsMode(conf.CgroupNamespaceMode)
}

// IsRootless returns conf.Rootless on Linux but false on Windows
func (conf *Config) IsRootless() bool {
	return conf.Rootless
}
<|endoftext|>


<commit_before>package restful

import ()

type WebService struct {
	rootPath string
	routes   []Route
	produces []string
	consumes []string
}

// Specify the root URL template path of the WebService.
// All Routes will be relative to this path.
func (self *WebService) Path(root string) *WebService {
	self.rootPath = root
	return self
}

// Create a new Route using the RouteBuilder and add to the ordered list of Routes.
func (self *WebService) Route(builder *RouteBuilder) *WebService {
	builder.copyDefaults(self.produces, self.consumes)
	self.routes = append(self.routes, builder.Build())
	return self
}

// Create a new RouteBuilder and initialize its http method
func (self *WebService) Method(httpMethod string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method(httpMethod)
}

// Specify that this WebService can produce one or more MIME types.
func (self *WebService) Produces(contentTypes ...string) *WebService {
	self.produces = contentTypes
	return self
}

// Specify that this WebService can consume one or more MIME types.
func (self *WebService) Consumes(accepts ...string) *WebService {
	self.consumes = accepts
	return self
}

func (self WebService) Routes() []Route {
	return self.routes
}
func (self WebService) RootPath() string {
	return self.rootPath
}

/*
	Convenience methods
*/

// Shortcut for .Method("GET").Path(subPath)
func (self *WebService) GET(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("GET").Path(subPath)
}

// Shortcut for .Method("POST").Path(subPath)
func (self *WebService) POST(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("POST").Path(subPath)
}

// Shortcut for .Method("PUT").Path(subPath)
func (self *WebService) PUT(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("PUT").Path(subPath)
}

// Shortcut for .Method("DELETE").Path(subPath)
func (self *WebService) DELETE(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("DELETE").Path(subPath)
}
<commit_msg>add path parameter doc on webservice (root) level<commit_after>package restful

import ()

type WebService struct {
	rootPath string
	routes   []Route
	produces []string
	consumes []string
}

// Specify the root URL template path of the WebService.
// All Routes will be relative to this path.
func (self *WebService) Path(root string) *WebService {
	self.rootPath = root
	return self
}

// Document the Path Parameter used in my Root
func (self *WebService) PathParam(name, documentation string) *WebService {
	// TODO
	return self
}

// Create a new Route using the RouteBuilder and add to the ordered list of Routes.
func (self *WebService) Route(builder *RouteBuilder) *WebService {
	builder.copyDefaults(self.produces, self.consumes)
	self.routes = append(self.routes, builder.Build())
	return self
}

// Create a new RouteBuilder and initialize its http method
func (self *WebService) Method(httpMethod string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method(httpMethod)
}

// Specify that this WebService can produce one or more MIME types.
func (self *WebService) Produces(contentTypes ...string) *WebService {
	self.produces = contentTypes
	return self
}

// Specify that this WebService can consume one or more MIME types.
func (self *WebService) Consumes(accepts ...string) *WebService {
	self.consumes = accepts
	return self
}

func (self WebService) Routes() []Route {
	return self.routes
}
func (self WebService) RootPath() string {
	return self.rootPath
}

/*
	Convenience methods
*/

// Shortcut for .Method("GET").Path(subPath)
func (self *WebService) GET(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("GET").Path(subPath)
}

// Shortcut for .Method("POST").Path(subPath)
func (self *WebService) POST(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("POST").Path(subPath)
}

// Shortcut for .Method("PUT").Path(subPath)
func (self *WebService) PUT(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("PUT").Path(subPath)
}

// Shortcut for .Method("DELETE").Path(subPath)
func (self *WebService) DELETE(subPath string) *RouteBuilder {
	return new(RouteBuilder).servicePath(self.rootPath).Method("DELETE").Path(subPath)
}
<|endoftext|>


<commit_before>package broker

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	log "github.com/golang/glog"
	"github.com/micro/go-micro/errors"
	"github.com/micro/go-micro/registry"
	"github.com/pborman/uuid"
)

// HTTP Broker is a placeholder for actual message brokers.
// This should not really be used in production but useful
// in developer where you want zero dependencies.

type httpBroker struct {
	id          string
	address     string
	unsubscribe chan *httpSubscriber

	sync.RWMutex
	subscribers map[string][]*httpSubscriber
	running     bool
	exit        chan chan error
}

type httpSubscriber struct {
	opts  SubscribeOptions
	id    string
	topic string
	ch    chan *httpSubscriber
	fn    Handler
	svc   *registry.Service
}

type httpPublication struct {
	m *Message
	t string
}

var (
	DefaultSubPath   = "/_sub"
	broadcastVersion = "ff.http.broadcast"
)

func init() {
	rand.Seed(time.Now().Unix())
}

func newHttpBroker(addrs []string, opt ...Option) Broker {
	addr := ":0"
	if len(addrs) > 0 && len(addrs[0]) > 0 {
		addr = addrs[0]
	}

	return &httpBroker{
		id:          "broker-" + uuid.NewUUID().String(),
		address:     addr,
		subscribers: make(map[string][]*httpSubscriber),
		unsubscribe: make(chan *httpSubscriber),
		exit:        make(chan chan error),
	}
}

func (h *httpPublication) Ack() error {
	return nil
}

func (h *httpPublication) Message() *Message {
	return h.m
}

func (h *httpPublication) Topic() string {
	return h.t
}

func (h *httpSubscriber) Config() SubscribeOptions {
	return h.opts
}

func (h *httpSubscriber) Topic() string {
	return h.topic
}

func (h *httpSubscriber) Unsubscribe() error {
	h.ch <- h
	return nil
}

func (h *httpBroker) start() error {
	h.Lock()
	defer h.Unlock()

	if h.running {
		return nil
	}

	l, err := net.Listen("tcp", h.address)
	if err != nil {
		return err
	}

	log.Infof("Broker Listening on %s", l.Addr().String())
	h.address = l.Addr().String()

	go http.Serve(l, h)

	go func() {
		for {
			select {
			case ch := <-h.exit:
				ch <- l.Close()
				h.Lock()
				h.running = false
				h.Unlock()
				return
			case subscriber := <-h.unsubscribe:
				h.Lock()
				var subscribers []*httpSubscriber
				for _, sub := range h.subscribers[subscriber.topic] {
					if sub.id == subscriber.id {
						registry.Deregister(sub.svc)
					}
					subscribers = append(subscribers, sub)
				}
				h.subscribers[subscriber.topic] = subscribers
				h.Unlock()
			}
		}
	}()

	h.running = true
	return nil
}

func (h *httpBroker) stop() error {
	ch := make(chan error)
	h.exit <- ch
	return <-ch
}

func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		err := errors.BadRequest("go.micro.broker", "Method not allowed")
		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
		return
	}
	defer req.Body.Close()

	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error reading request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	var m *Message
	if err = json.Unmarshal(b, &m); err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	topic := m.Header[":topic"]
	delete(m.Header, ":topic")

	if len(topic) == 0 {
		errr := errors.InternalServerError("go.micro.broker", "Topic not found")
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	p := &httpPublication{m: m, t: topic}
	h.RLock()
	for _, subscriber := range h.subscribers[topic] {
		subscriber.fn(p)
	}
	h.RUnlock()
}

func (h *httpBroker) Address() string {
	return h.address
}

func (h *httpBroker) Connect() error {
	return h.start()
}

func (h *httpBroker) Disconnect() error {
	return h.stop()
}

func (h *httpBroker) Init(opts ...Option) error {
	if len(h.id) == 0 {
		h.id = "broker-" + uuid.NewUUID().String()
	}

	http.Handle(DefaultSubPath, h)
	return nil
}

func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
	s, err := registry.GetService("topic:" + topic)
	if err != nil {
		return err
	}

	msg.Header[":topic"] = topic
	b, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	fn := func(node *registry.Node, b io.Reader) {
		r, err := http.Post(fmt.Sprintf("http://%s:%d%s", node.Address, node.Port, DefaultSubPath), "application/json", b)
		if err == nil {
			r.Body.Close()
		}
	}

	buf := bytes.NewBuffer(nil)

	for _, service := range s {
		// broadcast version means broadcast to all nodes
		if service.Version == broadcastVersion {
			for _, node := range service.Nodes {
				buf.Reset()
				buf.Write(b)
				fn(node, buf)
			}
			return nil
		}

		node := service.Nodes[rand.Int()%len(service.Nodes)]
		buf.Reset()
		buf.Write(b)
		fn(node, buf)
		return nil
	}

	buf.Reset()
	buf = nil

	return nil
}

func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
	opt := newSubscribeOptions(opts...)
	fmt.Println("subscribe to", topic)

	// parse address for host, port
	parts := strings.Split(h.Address(), ":")
	host := strings.Join(parts[:len(parts)-1], ":")
	port, _ := strconv.Atoi(parts[len(parts)-1])

	id := uuid.NewUUID().String()

	// register service
	node := &registry.Node{
		Id:      topic + "." + h.id + "." + id,
		Address: host,
		Port:    port,
	}

	version := opt.Queue
	if len(version) == 0 {
		version = broadcastVersion
	}

	service := &registry.Service{
		Name:    "topic:" + topic,
		Version: version,
		Nodes:   []*registry.Node{node},
	}

	subscriber := &httpSubscriber{
		opts:  opt,
		id:    id,
		topic: topic,
		ch:    h.unsubscribe,
		fn:    handler,
		svc:   service,
	}

	if err := registry.Register(service); err != nil {
		return nil, err
	}

	h.Lock()
	h.subscribers[topic] = append(h.subscribers[topic], subscriber)
	fmt.Println(h.subscribers)
	h.Unlock()
	return subscriber, nil
}

func (h *httpBroker) String() string {
	return "http"
}
<commit_msg>remove the print statements<commit_after>package broker

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	log "github.com/golang/glog"
	"github.com/micro/go-micro/errors"
	"github.com/micro/go-micro/registry"
	"github.com/pborman/uuid"
)

// HTTP Broker is a placeholder for actual message brokers.
// This should not really be used in production but useful
// in developer where you want zero dependencies.

type httpBroker struct {
	id          string
	address     string
	unsubscribe chan *httpSubscriber

	sync.RWMutex
	subscribers map[string][]*httpSubscriber
	running     bool
	exit        chan chan error
}

type httpSubscriber struct {
	opts  SubscribeOptions
	id    string
	topic string
	ch    chan *httpSubscriber
	fn    Handler
	svc   *registry.Service
}

type httpPublication struct {
	m *Message
	t string
}

var (
	DefaultSubPath   = "/_sub"
	broadcastVersion = "ff.http.broadcast"
)

func init() {
	rand.Seed(time.Now().Unix())
}

func newHttpBroker(addrs []string, opt ...Option) Broker {
	addr := ":0"
	if len(addrs) > 0 && len(addrs[0]) > 0 {
		addr = addrs[0]
	}

	return &httpBroker{
		id:          "broker-" + uuid.NewUUID().String(),
		address:     addr,
		subscribers: make(map[string][]*httpSubscriber),
		unsubscribe: make(chan *httpSubscriber),
		exit:        make(chan chan error),
	}
}

func (h *httpPublication) Ack() error {
	return nil
}

func (h *httpPublication) Message() *Message {
	return h.m
}

func (h *httpPublication) Topic() string {
	return h.t
}

func (h *httpSubscriber) Config() SubscribeOptions {
	return h.opts
}

func (h *httpSubscriber) Topic() string {
	return h.topic
}

func (h *httpSubscriber) Unsubscribe() error {
	h.ch <- h
	return nil
}

func (h *httpBroker) start() error {
	h.Lock()
	defer h.Unlock()

	if h.running {
		return nil
	}

	l, err := net.Listen("tcp", h.address)
	if err != nil {
		return err
	}

	log.Infof("Broker Listening on %s", l.Addr().String())
	h.address = l.Addr().String()

	go http.Serve(l, h)

	go func() {
		for {
			select {
			case ch := <-h.exit:
				ch <- l.Close()
				h.Lock()
				h.running = false
				h.Unlock()
				return
			case subscriber := <-h.unsubscribe:
				h.Lock()
				var subscribers []*httpSubscriber
				for _, sub := range h.subscribers[subscriber.topic] {
					if sub.id == subscriber.id {
						registry.Deregister(sub.svc)
					}
					subscribers = append(subscribers, sub)
				}
				h.subscribers[subscriber.topic] = subscribers
				h.Unlock()
			}
		}
	}()

	h.running = true
	return nil
}

func (h *httpBroker) stop() error {
	ch := make(chan error)
	h.exit <- ch
	return <-ch
}

func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != "POST" {
		err := errors.BadRequest("go.micro.broker", "Method not allowed")
		http.Error(w, err.Error(), http.StatusMethodNotAllowed)
		return
	}
	defer req.Body.Close()

	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error reading request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	var m *Message
	if err = json.Unmarshal(b, &m); err != nil {
		errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	topic := m.Header[":topic"]
	delete(m.Header, ":topic")

	if len(topic) == 0 {
		errr := errors.InternalServerError("go.micro.broker", "Topic not found")
		w.WriteHeader(500)
		w.Write([]byte(errr.Error()))
		return
	}

	p := &httpPublication{m: m, t: topic}
	h.RLock()
	for _, subscriber := range h.subscribers[topic] {
		subscriber.fn(p)
	}
	h.RUnlock()
}

func (h *httpBroker) Address() string {
	return h.address
}

func (h *httpBroker) Connect() error {
	return h.start()
}

func (h *httpBroker) Disconnect() error {
	return h.stop()
}

func (h *httpBroker) Init(opts ...Option) error {
	if len(h.id) == 0 {
		h.id = "broker-" + uuid.NewUUID().String()
	}

	http.Handle(DefaultSubPath, h)
	return nil
}

func (h *httpBroker) Publish(topic string, msg *Message, opts ...PublishOption) error {
	s, err := registry.GetService("topic:" + topic)
	if err != nil {
		return err
	}

	msg.Header[":topic"] = topic
	b, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	fn := func(node *registry.Node, b io.Reader) {
		r, err := http.Post(fmt.Sprintf("http://%s:%d%s", node.Address, node.Port, DefaultSubPath), "application/json", b)
		if err == nil {
			r.Body.Close()
		}
	}

	buf := bytes.NewBuffer(nil)

	for _, service := range s {
		// broadcast version means broadcast to all nodes
		if service.Version == broadcastVersion {
			for _, node := range service.Nodes {
				buf.Reset()
				buf.Write(b)
				fn(node, buf)
			}
			return nil
		}

		node := service.Nodes[rand.Int()%len(service.Nodes)]
		buf.Reset()
		buf.Write(b)
		fn(node, buf)
		return nil
	}

	buf.Reset()
	buf = nil

	return nil
}

func (h *httpBroker) Subscribe(topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
	opt := newSubscribeOptions(opts...)

	// parse address for host, port
	parts := strings.Split(h.Address(), ":")
	host := strings.Join(parts[:len(parts)-1], ":")
	port, _ := strconv.Atoi(parts[len(parts)-1])

	id := uuid.NewUUID().String()

	// register service
	node := &registry.Node{
		Id:      topic + "." + h.id + "." + id,
		Address: host,
		Port:    port,
	}

	version := opt.Queue
	if len(version) == 0 {
		version = broadcastVersion
	}

	service := &registry.Service{
		Name:    "topic:" + topic,
		Version: version,
		Nodes:   []*registry.Node{node},
	}

	subscriber := &httpSubscriber{
		opts:  opt,
		id:    id,
		topic: topic,
		ch:    h.unsubscribe,
		fn:    handler,
		svc:   service,
	}

	if err := registry.Register(service); err != nil {
		return nil, err
	}

	h.Lock()
	h.subscribers[topic] = append(h.subscribers[topic], subscriber)
	h.Unlock()
	return subscriber, nil
}

func (h *httpBroker) String() string {
	return "http"
}
<|endoftext|>