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

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	version          = flag.String("version", "", "The version to be tested (including the leading 'v'). An empty string defaults to the local build, but it can be set to any release (e.g. v0.4.4, v0.6.0).")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	serverTarName   = "kubernetes-server-linux-amd64.tar.gz"
	saltTarName     = "kubernetes-salt.tar.gz"
	downloadDirName = "_output/downloads"
	tarDirName      = "server"
	tempDirName     = "upgrade-e2e-temp-dir"
	minNodeCount    = 2
)

var (
	// Root directory of the specified cluster version, rather than of where
	// this script is being run from.
	versionRoot = *root
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	if *version != "" {
		// If the desired version isn't available already, do whatever's needed
		// to make it available. Once done, update the root directory for client
		// tools to be the root of the release directory so that the given
		// release's tools will be used. We can't use this new root for
		// everything because it likely doesn't have the hack/ directory in it.
		if newVersionRoot, err := PrepareVersion(*version); err != nil {
			log.Fatalf("Error preparing a binary of version %s: %s. Aborting.", *version, err)
		} else {
			versionRoot = newVersionRoot
			os.Setenv("KUBE_VERSION_ROOT", newVersionRoot)
		}
	}

	os.Setenv("KUBECTL", versionRoot+`/cluster/kubectl.sh`+kubectlArgs())

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(versionRoot, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}

	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

// PrepareVersion makes sure that the specified release version is locally
// available and ready to be used by kube-up or kube-push. Returns the director
// path of the release.
func PrepareVersion(version string) (string, error) {
	if version == "" {
		// Assume that the build flag already handled building a local binary.
		return *root, nil
	}

	// If the version isn't a local build, try fetching the release from Google
	// Cloud Storage.
	downloadDir := filepath.Join(*root, downloadDirName)
	if err := os.MkdirAll(downloadDir, 0755); err != nil {
		return "", err
	}
	localReleaseDir := filepath.Join(downloadDir, version)
	if err := os.MkdirAll(localReleaseDir, 0755); err != nil {
		return "", err
	}

	remoteReleaseTar := fmt.Sprintf("https://storage.googleapis.com/kubernetes-release/release/%s/kubernetes.tar.gz", version)
	localReleaseTar := filepath.Join(downloadDir, fmt.Sprintf("kubernetes-%s.tar.gz", version))
	if _, err := os.Stat(localReleaseTar); os.IsNotExist(err) {
		out, err := os.Create(localReleaseTar)
		if err != nil {
			return "", err
		}
		resp, err := http.Get(remoteReleaseTar)
		if err != nil {
			out.Close()
			return "", err
		}
		defer resp.Body.Close()
		io.Copy(out, resp.Body)
		if err != nil {
			out.Close()
			return "", err
		}
		out.Close()
	}
	if !finishRunning("untarRelease", exec.Command("tar", "-C", localReleaseDir, "-zxf", localReleaseTar, "--strip-components=1")) {
		log.Fatal("Failed to untar release. Aborting.")
	}
	// Now that we have the binaries saved locally, use the path to the untarred
	// directory as the "root" path for future operations.
	return localReleaseDir, nil
}

// Fisher-Yates shuffle using the given RNG r
func shuffleStrings(strings []string, r *rand.Rand) {
	for i := len(strings) - 1; i > 0; i-- {
		j := r.Intn(i + 1)
		strings[i], strings[j] = strings[j], strings[i]
	}
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	ValidateClusterSize()

	return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
}

// All nonsense below is temporary until we have go versions of these things.

// call the returned anonymous function to stop.
func runBashUntil(stepName string, cmd *exec.Cmd) func() {
	log.Printf("Running in background: %v", stepName)
	output := bytes.NewBuffer(nil)
	cmd.Stdout, cmd.Stderr = output, output
	if err := cmd.Start(); err != nil {
		log.Printf("Unable to start '%v': '%v'", stepName, err)
		return func() {}
	}
	return func() {
		cmd.Process.Signal(os.Interrupt)
		headerprefix := stepName + " "
		lineprefix := "  "
		printBashOutputs(headerprefix, lineprefix, string(output.Bytes()), false)
	}
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

func printBashOutputs(headerprefix, lineprefix, output string, escape bool) {
	if output != "" {
		fmt.Printf("%voutput: |\n", headerprefix)
		printPrefixedLines(lineprefix, output)
	}
}

func printPrefixedLines(prefix, s string) {
	for _, line := range strings.Split(s, "\n") {
		fmt.Printf("%v%v\n", prefix, line)
	}
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	if *checkVersionSkew {
		return " --match-server-version"
	}
	return ""
}
<commit_msg>Remove unused functions from hack/e2e.go.<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	version          = flag.String("version", "", "The version to be tested (including the leading 'v'). An empty string defaults to the local build, but it can be set to any release (e.g. v0.4.4, v0.6.0).")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	pushup           = flag.Bool("pushup", false, "If true, push to e2e cluster if it's up, otherwise start the e2e cluster.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run Ginkgo tests.")
	testArgs         = flag.String("test_args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")

	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

const (
	serverTarName   = "kubernetes-server-linux-amd64.tar.gz"
	saltTarName     = "kubernetes-salt.tar.gz"
	downloadDirName = "_output/downloads"
	tarDirName      = "server"
	tempDirName     = "upgrade-e2e-temp-dir"
	minNodeCount    = 2
)

var (
	// Root directory of the specified cluster version, rather than of where
	// this script is being run from.
	versionRoot = *root
)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

type TestResult struct {
	Pass int
	Fail int
}

type ResultsByTest map[string]TestResult

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	flag.Parse()

	if *isup {
		status := 1
		if IsUp() {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		// The build-release script needs stdin to ask the user whether
		// it's OK to download the docker image.
		cmd := exec.Command(path.Join(*root, "hack/e2e-internal/build-release.sh"))
		cmd.Stdin = os.Stdin
		if !finishRunning("build-release", cmd) {
			log.Fatal("Error building. Aborting.")
		}
	}

	if *version != "" {
		// If the desired version isn't available already, do whatever's needed
		// to make it available. Once done, update the root directory for client
		// tools to be the root of the release directory so that the given
		// release's tools will be used. We can't use this new root for
		// everything because it likely doesn't have the hack/ directory in it.
		if newVersionRoot, err := PrepareVersion(*version); err != nil {
			log.Fatalf("Error preparing a binary of version %s: %s. Aborting.", *version, err)
		} else {
			versionRoot = newVersionRoot
			os.Setenv("KUBE_VERSION_ROOT", newVersionRoot)
		}
	}

	os.Setenv("KUBECTL", versionRoot+`/cluster/kubectl.sh`+kubectlArgs())

	if *pushup {
		if IsUp() {
			log.Printf("e2e cluster is up, pushing.")
			*up = false
			*push = true
		} else {
			log.Printf("e2e cluster is down, creating.")
			*up = true
			*push = false
		}
	}
	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !finishRunning("push", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-push.sh"))) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	success := true
	switch {
	case *ctlCmd != "":
		ctlArgs := strings.Fields(*ctlCmd)
		os.Setenv("KUBE_CONFIG_FILE", "config-test.sh")
		success = finishRunning("'kubectl "+*ctlCmd+"'", exec.Command(path.Join(versionRoot, "cluster/kubectl.sh"), ctlArgs...))
	case *test:
		success = Test()
	}

	if *down {
		TearDown()
	}

	if !success {
		os.Exit(1)
	}
}

func TearDown() bool {
	return finishRunning("teardown", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-down.sh")))
}

// Up brings an e2e cluster up, recreating it if one is already running.
func Up() bool {
	if IsUp() {
		log.Printf("e2e cluster already running; will teardown")
		if res := TearDown(); !res {
			return false
		}
	}

	return finishRunning("up", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-up.sh")))
}

// Ensure that the cluster is large engough to run the e2e tests.
func ValidateClusterSize() {
	// Check that there are at least minNodeCount nodes running
	cmd := exec.Command(path.Join(*root, "hack/e2e-internal/e2e-cluster-size.sh"))
	if *verbose {
		cmd.Stderr = os.Stderr
	}
	stdout, err := cmd.Output()
	if err != nil {
		log.Fatalf("Could not get nodes to validate cluster size (%s)", err)
	}

	numNodes, err := strconv.Atoi(strings.TrimSpace(string(stdout)))
	if err != nil {
		log.Fatalf("Could not count number of nodes to validate cluster size (%s)", err)
	}

	if numNodes < minNodeCount {
		log.Fatalf("Cluster size (%d) is too small to run e2e tests.  %d Nodes are required.", numNodes, minNodeCount)
	}
}

// Is the e2e cluster up?
func IsUp() bool {
	return finishRunning("get status", exec.Command(path.Join(*root, "hack/e2e-internal/e2e-status.sh")))
}

// PrepareVersion makes sure that the specified release version is locally
// available and ready to be used by kube-up or kube-push. Returns the director
// path of the release.
func PrepareVersion(version string) (string, error) {
	if version == "" {
		// Assume that the build flag already handled building a local binary.
		return *root, nil
	}

	// If the version isn't a local build, try fetching the release from Google
	// Cloud Storage.
	downloadDir := filepath.Join(*root, downloadDirName)
	if err := os.MkdirAll(downloadDir, 0755); err != nil {
		return "", err
	}
	localReleaseDir := filepath.Join(downloadDir, version)
	if err := os.MkdirAll(localReleaseDir, 0755); err != nil {
		return "", err
	}

	remoteReleaseTar := fmt.Sprintf("https://storage.googleapis.com/kubernetes-release/release/%s/kubernetes.tar.gz", version)
	localReleaseTar := filepath.Join(downloadDir, fmt.Sprintf("kubernetes-%s.tar.gz", version))
	if _, err := os.Stat(localReleaseTar); os.IsNotExist(err) {
		out, err := os.Create(localReleaseTar)
		if err != nil {
			return "", err
		}
		resp, err := http.Get(remoteReleaseTar)
		if err != nil {
			out.Close()
			return "", err
		}
		defer resp.Body.Close()
		io.Copy(out, resp.Body)
		if err != nil {
			out.Close()
			return "", err
		}
		out.Close()
	}
	if !finishRunning("untarRelease", exec.Command("tar", "-C", localReleaseDir, "-zxf", localReleaseTar, "--strip-components=1")) {
		log.Fatal("Failed to untar release. Aborting.")
	}
	// Now that we have the binaries saved locally, use the path to the untarred
	// directory as the "root" path for future operations.
	return localReleaseDir, nil
}

func Test() bool {
	if !IsUp() {
		log.Fatal("Testing requested, but e2e cluster not up!")
	}

	ValidateClusterSize()

	return finishRunning("Ginkgo tests", exec.Command(filepath.Join(*root, "hack/ginkgo-e2e.sh"), strings.Fields(*testArgs)...))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	log.Printf("Running: %v", stepName)
	defer func(start time.Time) {
		log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
	}(time.Now())

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubectl command (beginning with a space).
func kubectlArgs() string {
	if *checkVersionSkew {
		return " --match-server-version"
	}
	return ""
}
<|endoftext|>


<commit_before>package rest

import (
	"gopkg.in/zatiti/router.v1"
	"net/http"
)

// write creates a http handler for creating or updating a document depending on the mode provided
func (s *Service) persist(modelFactory ModelFactory, mode string) router.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		// Model is request scoped
		model := modelFactory.New(r)
		// Event is the name used to track metrics
		event := model.Name() + "_" + mode
		// Track how long this function take to return
		stop := s.NewTimer(event)
		defer stop()
		// HTTP response status code
		var status int
		// HTTP response body
		var body []byte
		// Send response back to client
		write := func(status int, body []byte) {
			w.WriteHeader(status)
			w.Write(body)
		}
		// Instanciate a value of the model being created from the request body
		err := model.Decode()
		if err != nil {
			status, body = BadRequestResponse()
			write(status, body)
			s.Logger.Error(err)
			return
		}
		// Validate user input
		verr := model.Validate()
		if verr != nil {
			status, body = verr.Code, []byte(verr.Message)
			write(status, body)
			s.Logger.Error(verr.Message)
			return
		}
		// Call the relevant model action
		switch {
		case mode == "insert":
			err = model.Create()
		case mode == "update":
			err = model.Update()
		case mode == "upsert":
			err = model.Upsert()
		}
		// Handle failed database operation
		if err != nil {
			status, body = InternalServerErrorResponse()
			write(status, body)
			s.Logger.Error(err)
			return
		}
		response := model.Response()
		// If event broker is defined use it
		if s.Broker != nil {
			err = s.Broker.Publish(event, response)
			if err != nil {
				status, body = InternalServerErrorResponse()
				write(status, body)
				s.Logger.Error(err)
				return
			}
		}
		// Get Response from model
		status = response.Status
		headers := response.Headers
		// Encode the response body to the appropriate format
		body, _ = model.Encode(response.Body)
		// Set response headers
		for key, value := range headers {
			w.Header().Set(key, value)
		}
		// Send response to client
		write(status, body)
		// if metrics client is defined, count this function call
		if s.Metrics != nil {
			s.Metrics.Incr(event, 1)
		}
	}
}

// Insert creates a http handler that will create a document in model's database.
func (s *Service) Insert(modelFactory ModelFactory) router.Handler {
	return s.persist(modelFactory, "insert")
}

// Update creates a http handler that will updates a document by the model's update selector in model's database
func (s *Service) Update(modelFactory ModelFactory) router.Handler {
	return s.persist(modelFactory, "update")
}

// Upsert creates a http handler that will upsert(create or update if it exists) a document selected by the model's upsert selector
func (s *Service) Upsert(modelFactory ModelFactory) router.Handler {
	return s.persist(modelFactory, "upsert")
}

// find creates a http handler that will list documents or return one document from a model's database
func (s *Service) find(modelFactory ModelFactory, mode string) router.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		// model is request scoped
		model := modelFactory.New(r)
		// event is the name used to track metrics
		event := model.Name() + "_find_" + mode
		// HTTP response status code
		var status int
		// HTTP response body
		var body []byte
		// Track how long this function take to return
		stop := s.NewTimer(event)
		defer stop()
		// Send response back to client
		write := func(status int, body []byte) {
			w.WriteHeader(status)
			w.Write(body)
		}
		// Validate user input
		verr := model.Validate()
		if verr != nil {
			status, body = verr.Code, []byte(verr.Message)
			write(status, body)
			s.Logger.Error(verr.Message)
			return
		}
		var err error
		switch {
		case mode == "one":
			err = model.FindOne()
		case mode == "many":
			err = model.FindMany()
		}
		if err != nil {
			// Something wicked happened while fetching document/s
			status, body = InternalServerErrorResponse()
			write(status, body)
			s.Logger.Error(err)
			return
		}
		response := model.Response()
		// Notify other services, if an event broker exists
		if s.Broker != nil {
			err = s.Broker.Publish(event, response)
			if err != nil {
				status, body = InternalServerErrorResponse()
				write(status, body)
				s.Logger.Error(err)
				return
			}
		}
		body, _ = model.Encode(response.Body)
		status = response.Status
		// Set response headers
		for key, value := range response.Headers {
			w.Header().Set(key, value)
		}
		write(status, body)
		// If a metrics client is defined count this successful request
		if s.Metrics != nil {
			s.Metrics.Incr(event, 1)
		}
	}
}

// FindOne - creates a http handler that will return one document from a model's database if the id exists
func (s *Service) FindOne(modelFactory ModelFactory) router.Handler {
	return s.find(modelFactory, "one")
}

// FindMany - creates a http handler that will list documents from a model's database
func (s *Service) FindMany(modelFactory ModelFactory) router.Handler {
	return s.find(modelFactory, "many")
}

// Remove creates a http handler that will delete a document by remove selector specified in the model model
func (s *Service) Remove(modelFactory ModelFactory) router.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		model := modelFactory.New(r)
		// event is the name used to track metrics
		event := model.Name() + "_delete"
		// Track how long this function take to return
		stop := s.NewTimer(event)
		defer stop()
		// HTTP response status code
		var status int
		// HTTP response body
		var body []byte
		// Send response back to client
		write := func(status int, body []byte) {
			w.WriteHeader(status)
			w.Write(body)
		}
		// Validate the user input
		verr := model.Validate()
		if verr != nil {
			status, body = verr.Code, []byte(verr.Message)
			s.Logger.Error(verr.Message)
			write(status, body)
			return
		}
		// Remove the item if it exists
		err := model.Remove()
		if err != nil {
			status, body = InternalServerErrorResponse()
			write(status, body)
			s.Logger.Error(err)
			return
		}
		response := model.Response()
		if s.Broker != nil {
			err = s.Broker.Publish(event, response)
			if err != nil {
				status, body = InternalServerErrorResponse()
				write(status, body)
				s.Logger.Error(err)
				return
			}
		}
		// Set response headers
		for key, value := range response.Headers {
			w.Header().Set(key, value)
		}
		status = response.Status
		body, _ = model.Encode(response.Body)
		// Notify other services, if an event broker exists
		write(status, body)
		// If a metrics client is defined count this successful request
		if s.Metrics != nil {
			s.Metrics.Incr(event, 1)
		}
	}
}
<commit_msg>use model status and body<commit_after>package rest

import (
	"gopkg.in/zatiti/router.v1"
	"net/http"
)

// write creates a http handler for creating or updating a document depending on the mode provided
func (s *Service) persist(modelFactory ModelFactory, mode string) router.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		// Model is request scoped
		model := modelFactory.New(r)
		// Event is the name used to track metrics
		event := model.Name() + "_" + mode
		// Track how long this function take to return
		stop := s.NewTimer(event)
		defer stop()
		// HTTP response status code
		var status int
		// HTTP response body
		var body []byte
		// Send response back to client
		write := func(status int, body []byte) {
			w.WriteHeader(status)
			w.Write(body)
		}
		// Instanciate a value of the model being created from the request body
		err := model.Decode()
		if err != nil {
			status, body = BadRequestResponse()
			write(status, body)
			s.Logger.Error(err)
			return
		}
		// Validate user input
		verr := model.Validate()
		if verr != nil {
			status, body = verr.Code, []byte(verr.Message)
			write(status, body)
			s.Logger.Error(verr.Message)
			return
		}
		// Call the relevant model action
		switch {
		case mode == "insert":
			err = model.Create()
		case mode == "update":
			err = model.Update()
		case mode == "upsert":
			err = model.Upsert()
		}
		// Handle failed database operation
		if err != nil {
			s.Logger.Error(err)
		}
		response := model.Response()
		// If event broker is defined use it
		if s.Broker != nil {
			err = s.Broker.Publish(event, response)
			if err != nil {
				status, body = InternalServerErrorResponse()
				write(status, body)
				s.Logger.Error(err)
				return
			}
		}
		// Get Response from model
		status = response.Status
		headers := response.Headers
		// Encode the response body to the appropriate format
		body, _ = model.Encode(response.Body)
		// Set response headers
		for key, value := range headers {
			w.Header().Set(key, value)
		}
		// Send response to client
		write(status, body)
		// if metrics client is defined, count this function call
		if s.Metrics != nil {
			s.Metrics.Incr(event, 1)
		}
	}
}

// Insert creates a http handler that will create a document in model's database.
func (s *Service) Insert(modelFactory ModelFactory) router.Handler {
	return s.persist(modelFactory, "insert")
}

// Update creates a http handler that will updates a document by the model's update selector in model's database
func (s *Service) Update(modelFactory ModelFactory) router.Handler {
	return s.persist(modelFactory, "update")
}

// Upsert creates a http handler that will upsert(create or update if it exists) a document selected by the model's upsert selector
func (s *Service) Upsert(modelFactory ModelFactory) router.Handler {
	return s.persist(modelFactory, "upsert")
}

// find creates a http handler that will list documents or return one document from a model's database
func (s *Service) find(modelFactory ModelFactory, mode string) router.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		// model is request scoped
		model := modelFactory.New(r)
		// event is the name used to track metrics
		event := model.Name() + "_find_" + mode
		// HTTP response status code
		var status int
		// HTTP response body
		var body []byte
		// Track how long this function take to return
		stop := s.NewTimer(event)
		defer stop()
		// Send response back to client
		write := func(status int, body []byte) {
			w.WriteHeader(status)
			w.Write(body)
		}
		// Validate user input
		verr := model.Validate()
		if verr != nil {
			status, body = verr.Code, []byte(verr.Message)
			write(status, body)
			s.Logger.Error(verr.Message)
			return
		}
		var err error
		switch {
		case mode == "one":
			err = model.FindOne()
		case mode == "many":
			err = model.FindMany()
		}
		if err != nil {
			// Something wicked happened while fetching document/s
			s.Logger.Error(err)
		}
		response := model.Response()
		// Notify other services, if an event broker exists
		if s.Broker != nil {
			err = s.Broker.Publish(event, response)
			if err != nil {
				status, body = InternalServerErrorResponse()
				write(status, body)
				s.Logger.Error(err)
				return
			}
		}
		body, _ = model.Encode(response.Body)
		status = response.Status
		// Set response headers
		for key, value := range response.Headers {
			w.Header().Set(key, value)
		}
		write(status, body)
		// If a metrics client is defined count this successful request
		if s.Metrics != nil {
			s.Metrics.Incr(event, 1)
		}
	}
}

// FindOne - creates a http handler that will return one document from a model's database if the id exists
func (s *Service) FindOne(modelFactory ModelFactory) router.Handler {
	return s.find(modelFactory, "one")
}

// FindMany - creates a http handler that will list documents from a model's database
func (s *Service) FindMany(modelFactory ModelFactory) router.Handler {
	return s.find(modelFactory, "many")
}

// Remove creates a http handler that will delete a document by remove selector specified in the model model
func (s *Service) Remove(modelFactory ModelFactory) router.Handler {
	return func(w http.ResponseWriter, r *http.Request) {
		model := modelFactory.New(r)
		// event is the name used to track metrics
		event := model.Name() + "_delete"
		// Track how long this function take to return
		stop := s.NewTimer(event)
		defer stop()
		// HTTP response status code
		var status int
		// HTTP response body
		var body []byte
		// Send response back to client
		write := func(status int, body []byte) {
			w.WriteHeader(status)
			w.Write(body)
		}
		// Validate the user input
		verr := model.Validate()
		if verr != nil {
			status, body = verr.Code, []byte(verr.Message)
			s.Logger.Error(verr.Message)
			write(status, body)
			return
		}
		// Remove the item if it exists
		err := model.Remove()
		if err != nil {
			s.Logger.Error(err)
		}
		response := model.Response()
		if s.Broker != nil {
			err = s.Broker.Publish(event, response)
			if err != nil {
				status, body = InternalServerErrorResponse()
				write(status, body)
				s.Logger.Error(err)
				return
			}
		}
		// Set response headers
		for key, value := range response.Headers {
			w.Header().Set(key, value)
		}
		status = response.Status
		body, _ = model.Encode(response.Body)
		// Notify other services, if an event broker exists
		write(status, body)
		// If a metrics client is defined count this successful request
		if s.Metrics != nil {
			s.Metrics.Incr(event, 1)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"goji.io/middleware"
	"goji.io/pat"
)

func isHashed(dir string) bool {
	return dir == "data"
}

func getRepo(r *http.Request) string {
	if strings.HasPrefix(fmt.Sprintf("%s", middleware.Pattern(r.Context())), "/:repo/") {
		return filepath.Join(config.path, pat.Param(r, "repo"))
	}

	return config.path
}

func createDirectories(path string) {
	log.Println("Creating repository directories")

	if err := os.MkdirAll(path, 0700); err != nil {
		log.Fatal(err)
	}

	dirs := []string{
		"data",
		"index",
		"keys",
		"locks",
		"snapshots",
		"tmp",
	}

	for _, d := range dirs {
		if err := os.MkdirAll(filepath.Join(path, d), 0700); err != nil {
			log.Fatal(err)
		}
	}

	for i := 0; i < 256; i++ {
		if err := os.MkdirAll(filepath.Join(path, "data", fmt.Sprintf("%02x", i)), 0700); err != nil {
			log.Fatal(err)
		}
	}
}

// AuthHandler wraps h with a http.HandlerFunc that performs basic authentication against the user/passwords pairs
// stored in f and returns the http.HandlerFunc.
func AuthHandler(f *HtpasswdFile, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if username, password, ok := r.BasicAuth(); !ok || !f.Validate(username, password) {
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
			return
		}

		h.ServeHTTP(w, r)
	}
}

// CheckConfig checks whether a configuration exists.
func CheckConfig(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("CheckConfig()")
	}
	config := filepath.Join(getRepo(r), "config")
	st, err := os.Stat(config)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
}

// GetConfig allows for a config to be retrieved.
func GetConfig(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("GetConfig()")
	}
	config := filepath.Join(getRepo(r), "config")
	bytes, err := ioutil.ReadFile(config)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Write(bytes)
}

// SaveConfig allows for a config to be saved.
func SaveConfig(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("SaveConfig()")
	}
	config := filepath.Join(getRepo(r), "config")
	bytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if err := ioutil.WriteFile(config, bytes, 0600); err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write([]byte("200 ok"))
}

// ListBlobs lists all blobs of a given type in an arbitrary order.
func ListBlobs(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("ListBlobs()")
	}
	dir := pat.Param(r, "type")
	path := filepath.Join(getRepo(r), dir)

	items, err := ioutil.ReadDir(path)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	var names []string
	for _, i := range items {
		if isHashed(dir) {
			subpath := filepath.Join(path, i.Name())
			subitems, err := ioutil.ReadDir(subpath)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
				return
			}
			for _, f := range subitems {
				names = append(names, f.Name())
			}
		} else {
			names = append(names, i.Name())
		}
	}

	data, err := json.Marshal(names)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write(data)
}

// CheckBlob tests whether a blob exists.
func CheckBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("CheckBlob()")
	}
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(getRepo(r), dir, name)

	st, err := os.Stat(path)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
}

// GetBlob retrieves a blob from the repository.
func GetBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("GetBlob()")
	}
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(getRepo(r), dir, name)

	file, err := os.Open(path)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	http.ServeContent(w, r, "", time.Unix(0, 0), file)
	file.Close()
}

// SaveBlob saves a blob to the repository.
func SaveBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("SaveBlob()")
	}
	repo := getRepo(r)
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if dir == "keys" {
		if _, err := os.Stat("keys"); err != nil && os.IsNotExist(err) {
			createDirectories(repo)
		}
	}

	tmp := filepath.Join(repo, "tmp", name)

	tf, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	if _, err := io.Copy(tf, r.Body); err != nil {
		tf.Close()
		os.Remove(tmp)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if err := tf.Sync(); err != nil {
		tf.Close()
		os.Remove(tmp)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	if err := tf.Close(); err != nil {
		os.Remove(tmp)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(repo, dir, name)

	if err := os.Rename(tmp, path); err != nil {
		os.Remove(tmp)
		os.Remove(path)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write([]byte("200 ok"))
}

// DeleteBlob deletes a blob from the repository.
func DeleteBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("DeleteBlob()")
	}
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(getRepo(r), dir, name)

	if err := os.Remove(path); err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write([]byte("200 ok"))
}
<commit_msg>Don't shadow config struct<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"goji.io/middleware"
	"goji.io/pat"
)

func isHashed(dir string) bool {
	return dir == "data"
}

func getRepo(r *http.Request) string {
	if strings.HasPrefix(fmt.Sprintf("%s", middleware.Pattern(r.Context())), "/:repo/") {
		return filepath.Join(config.path, pat.Param(r, "repo"))
	}

	return config.path
}

func createDirectories(path string) {
	log.Println("Creating repository directories")

	if err := os.MkdirAll(path, 0700); err != nil {
		log.Fatal(err)
	}

	dirs := []string{
		"data",
		"index",
		"keys",
		"locks",
		"snapshots",
		"tmp",
	}

	for _, d := range dirs {
		if err := os.MkdirAll(filepath.Join(path, d), 0700); err != nil {
			log.Fatal(err)
		}
	}

	for i := 0; i < 256; i++ {
		if err := os.MkdirAll(filepath.Join(path, "data", fmt.Sprintf("%02x", i)), 0700); err != nil {
			log.Fatal(err)
		}
	}
}

// AuthHandler wraps h with a http.HandlerFunc that performs basic authentication against the user/passwords pairs
// stored in f and returns the http.HandlerFunc.
func AuthHandler(f *HtpasswdFile, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if username, password, ok := r.BasicAuth(); !ok || !f.Validate(username, password) {
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
			return
		}

		h.ServeHTTP(w, r)
	}
}

// CheckConfig checks whether a configuration exists.
func CheckConfig(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("CheckConfig()")
	}
	cfg := filepath.Join(getRepo(r), "config")
	st, err := os.Stat(cfg)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
}

// GetConfig allows for a config to be retrieved.
func GetConfig(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("GetConfig()")
	}
	cfg := filepath.Join(getRepo(r), "config")
	bytes, err := ioutil.ReadFile(cfg)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Write(bytes)
}

// SaveConfig allows for a config to be saved.
func SaveConfig(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("SaveConfig()")
	}
	cfg := filepath.Join(getRepo(r), "config")
	bytes, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if err := ioutil.WriteFile(cfg, bytes, 0600); err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write([]byte("200 ok"))
}

// ListBlobs lists all blobs of a given type in an arbitrary order.
func ListBlobs(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("ListBlobs()")
	}
	dir := pat.Param(r, "type")
	path := filepath.Join(getRepo(r), dir)

	items, err := ioutil.ReadDir(path)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	var names []string
	for _, i := range items {
		if isHashed(dir) {
			subpath := filepath.Join(path, i.Name())
			subitems, err := ioutil.ReadDir(subpath)
			if err != nil {
				http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
				return
			}
			for _, f := range subitems {
				names = append(names, f.Name())
			}
		} else {
			names = append(names, i.Name())
		}
	}

	data, err := json.Marshal(names)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write(data)
}

// CheckBlob tests whether a blob exists.
func CheckBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("CheckBlob()")
	}
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(getRepo(r), dir, name)

	st, err := os.Stat(path)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
}

// GetBlob retrieves a blob from the repository.
func GetBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("GetBlob()")
	}
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(getRepo(r), dir, name)

	file, err := os.Open(path)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
		return
	}

	http.ServeContent(w, r, "", time.Unix(0, 0), file)
	file.Close()
}

// SaveBlob saves a blob to the repository.
func SaveBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("SaveBlob()")
	}
	repo := getRepo(r)
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if dir == "keys" {
		if _, err := os.Stat("keys"); err != nil && os.IsNotExist(err) {
			createDirectories(repo)
		}
	}

	tmp := filepath.Join(repo, "tmp", name)

	tf, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	if _, err := io.Copy(tf, r.Body); err != nil {
		tf.Close()
		os.Remove(tmp)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	if err := tf.Sync(); err != nil {
		tf.Close()
		os.Remove(tmp)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	if err := tf.Close(); err != nil {
		os.Remove(tmp)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(repo, dir, name)

	if err := os.Rename(tmp, path); err != nil {
		os.Remove(tmp)
		os.Remove(path)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write([]byte("200 ok"))
}

// DeleteBlob deletes a blob from the repository.
func DeleteBlob(w http.ResponseWriter, r *http.Request) {
	if config.debug {
		log.Println("DeleteBlob()")
	}
	dir := pat.Param(r, "type")
	name := pat.Param(r, "name")

	if isHashed(dir) {
		name = filepath.Join(name[:2], name)
	}
	path := filepath.Join(getRepo(r), dir, name)

	if err := os.Remove(path); err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	w.Write([]byte("200 ok"))
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/nickvanw/ircx"
	"github.com/sorcix/irc"
)

func onWelcome(s ircx.Sender, m *irc.Message) {
	log.Printf("Connected.")
	s.Send(&irc.Message{
		Command: irc.JOIN,
		Params:  []string{strings.Join(config.Chans, ",")},
	})
}

func onPing(s ircx.Sender, m *irc.Message) {
	s.Send(&irc.Message{
		Command:  irc.PONG,
		Params:   m.Params,
		Trailing: m.Trailing,
	})
}

func onPrivmsg(s ircx.Sender, m *irc.Message) {
	channel := m.Params[0]
	line := m.Trailing
	for _, m := range allRe.FindAllString(line, -1) {
		for _, r := range repos {
			if ss := r.IssueRe.FindStringSubmatch(m); ss != nil && ss[0] == m {
				body, err := r.IssueInfo(ss[2])
				if err != nil {
					log.Printf("#%s: %v", ss[2], err)
					continue
				}
				message := fmt.Sprintf("[%s] %s", r.Name, body)
				go s.Send(&irc.Message{
					Command:  irc.NOTICE,
					Params:   []string{channel},
					Trailing: message,
				})
			}
			if ss := r.PullRe.FindStringSubmatch(m); ss != nil && ss[0] == m {
				body, err := r.PullInfo(ss[2])
				if err != nil {
					log.Printf("!%s: %v", ss[2], err)
					continue
				}
				message := fmt.Sprintf("[%s] %s", r.Name, body)
				go s.Send(&irc.Message{
					Command:  irc.NOTICE,
					Params:   []string{channel},
					Trailing: message,
				})
			}
			if ss := r.CommitRe.FindString(m); ss == m {
				body, err := r.CommitInfo(ss)
				if err != nil {
					log.Printf("%s: %v", ss, err)
					continue
				}
				message := fmt.Sprintf("[%s] %s", r.Name, body)
				go s.Send(&irc.Message{
					Command:  irc.NOTICE,
					Params:   []string{channel},
					Trailing: message,
				})
			}
		}
	}
}
<commit_msg>Deduplicate notice code<commit_after>// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/nickvanw/ircx"
	"github.com/sorcix/irc"
)

func onWelcome(s ircx.Sender, m *irc.Message) {
	log.Printf("Connected.")
	s.Send(&irc.Message{
		Command: irc.JOIN,
		Params:  []string{strings.Join(config.Chans, ",")},
	})
}

func onPing(s ircx.Sender, m *irc.Message) {
	s.Send(&irc.Message{
		Command:  irc.PONG,
		Params:   m.Params,
		Trailing: m.Trailing,
	})
}

func onPrivmsg(s ircx.Sender, m *irc.Message) {
	channel := m.Params[0]
	line := m.Trailing
	for _, m := range allRe.FindAllString(line, -1) {
		for _, r := range repos {
			if ss := r.IssueRe.FindStringSubmatch(m); ss != nil && ss[0] == m {
				body, err := r.IssueInfo(ss[2])
				if err != nil {
					log.Printf("#%s: %v", ss[2], err)
					continue
				}
				go sendNotice(s, channel, r.Name, body)
			}
			if ss := r.PullRe.FindStringSubmatch(m); ss != nil && ss[0] == m {
				body, err := r.PullInfo(ss[2])
				if err != nil {
					log.Printf("!%s: %v", ss[2], err)
					continue
				}
				go sendNotice(s, channel, r.Name, body)
			}
			if ss := r.CommitRe.FindString(m); ss == m {
				body, err := r.CommitInfo(ss)
				if err != nil {
					log.Printf("%s: %v", ss, err)
					continue
				}
				go sendNotice(s, channel, r.Name, body)
			}
		}
	}
}

func sendNotice(s ircx.Sender, channel, categ, body string) error {
	message := fmt.Sprintf("[%s] %s", categ, body)
	return s.Send(&irc.Message{
		Command:  irc.NOTICE,
		Params:   []string{channel},
		Trailing: message,
	})
}
<|endoftext|>


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

/*
Package handlers is a collection of handlers for use with Go's net/http package.
*/
package handlers

import (
	"bufio"
	"io"
	"net"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

// MethodHandler is an http.Handler that dispatches to a handler whose key in the MethodHandler's
// map matches the name of the HTTP request's method, eg: GET
//
// If the request's method is OPTIONS and OPTIONS is not a key in the map then the handler
// responds with a status of 200 and sets the Allow header to a comma-separated list of
// available methods.
//
// If the request's method doesn't match any of its keys the handler responds with
// a status of 406, Method not allowed and sets the Allow header to a comma-separated list
// of available methods.
type MethodHandler map[string]http.Handler

func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if handler, ok := h[req.Method]; ok {
		handler.ServeHTTP(w, req)
	} else {
		allow := []string{}
		for k := range h {
			allow = append(allow, k)
		}
		sort.Strings(allow)
		w.Header().Set("Allow", strings.Join(allow, ", "))
		if req.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
		} else {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	}
}

// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type loggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type combinedLoggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	h.handler.ServeHTTP(logger, req)
	writeLog(h.writer, req, t, logger.Status(), logger.Size())
}

func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	h.handler.ServeHTTP(logger, req)
	writeCombinedLog(h.writer, req, t, logger.Status(), logger.Size())
}

type loggingResponseWriter interface {
	http.ResponseWriter
	Status() int
	Size() int
}

// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
	w      http.ResponseWriter
	status int
	size   int
}

func (l *responseLogger) Header() http.Header {
	return l.w.Header()
}

func (l *responseLogger) Write(b []byte) (int, error) {
	if l.status == 0 {
		// The status will be StatusOK if WriteHeader has not been called yet
		l.status = http.StatusOK
	}
	size, err := l.w.Write(b)
	l.size += size
	return size, err
}

func (l *responseLogger) WriteHeader(s int) {
	l.w.WriteHeader(s)
	l.status = s
}

func (l *responseLogger) Status() int {
	return l.status
}

func (l *responseLogger) Size() int {
	return l.size
}

type hijackLogger struct {
	responseLogger
}

func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h := l.responseLogger.w.(http.Hijacker)
	conn, rw, err := h.Hijack()
	if err == nil && l.responseLogger.status == 0 {
		// The status will be StatusSwitchingProtocols if there was no error and WriteHeader has not been called yet
		l.responseLogger.status = http.StatusSwitchingProtocols
	}
	return conn, rw, err
}

// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, ts time.Time, status int, size int) string {
	username := "-"
	if req.URL.User != nil {
		if name := req.URL.User.Username(); name != "" {
			username = name
		}
	}

	host, _, err := net.SplitHostPort(req.RemoteAddr)

	if err != nil {
		host = req.RemoteAddr
	}

	return host + " - " + username + " [" + ts.Format("02/Jan/2006:15:04:05 -0700") + `] "` + req.Method + " " + req.URL.RequestURI() + " " + req.Proto + `" ` + strconv.Itoa(status) + " " + strconv.Itoa(size)
}

// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(w io.Writer, req *http.Request, ts time.Time, status, size int) {
	l := buildCommonLogLine(req, ts, status, size)
	io.WriteString(w, l+"\n")
}

// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(w io.Writer, req *http.Request, ts time.Time, status, size int) {
	l := buildCommonLogLine(req, ts, status, size)
	io.WriteString(w, l+` "`+req.Referer()+`" "`+req.UserAgent()+`"`+"\n")
}

// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return combinedLoggingHandler{out, h}
}

// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return loggingHandler{out, h}
}
<commit_msg>Escape log lines; resolves #5<commit_after>// Copyright 2013 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package handlers is a collection of handlers for use with Go's net/http package.
*/
package handlers

import (
	"bufio"
	"io"
	"net"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"
)

// MethodHandler is an http.Handler that dispatches to a handler whose key in the MethodHandler's
// map matches the name of the HTTP request's method, eg: GET
//
// If the request's method is OPTIONS and OPTIONS is not a key in the map then the handler
// responds with a status of 200 and sets the Allow header to a comma-separated list of
// available methods.
//
// If the request's method doesn't match any of its keys the handler responds with
// a status of 406, Method not allowed and sets the Allow header to a comma-separated list
// of available methods.
type MethodHandler map[string]http.Handler

func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if handler, ok := h[req.Method]; ok {
		handler.ServeHTTP(w, req)
	} else {
		allow := []string{}
		for k := range h {
			allow = append(allow, k)
		}
		sort.Strings(allow)
		w.Header().Set("Allow", strings.Join(allow, ", "))
		if req.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
		} else {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	}
}

// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type loggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

// combinedLoggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends
type combinedLoggingHandler struct {
	writer  io.Writer
	handler http.Handler
}

func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	h.handler.ServeHTTP(logger, req)
	writeLog(h.writer, req, t, logger.Status(), logger.Size())
}

func (h combinedLoggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	t := time.Now()
	var logger loggingResponseWriter
	if _, ok := w.(http.Hijacker); ok {
		logger = &hijackLogger{responseLogger: responseLogger{w: w}}
	} else {
		logger = &responseLogger{w: w}
	}
	h.handler.ServeHTTP(logger, req)
	writeCombinedLog(h.writer, req, t, logger.Status(), logger.Size())
}

type loggingResponseWriter interface {
	http.ResponseWriter
	Status() int
	Size() int
}

// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status
// code and body size
type responseLogger struct {
	w      http.ResponseWriter
	status int
	size   int
}

func (l *responseLogger) Header() http.Header {
	return l.w.Header()
}

func (l *responseLogger) Write(b []byte) (int, error) {
	if l.status == 0 {
		// The status will be StatusOK if WriteHeader has not been called yet
		l.status = http.StatusOK
	}
	size, err := l.w.Write(b)
	l.size += size
	return size, err
}

func (l *responseLogger) WriteHeader(s int) {
	l.w.WriteHeader(s)
	l.status = s
}

func (l *responseLogger) Status() int {
	return l.status
}

func (l *responseLogger) Size() int {
	return l.size
}

type hijackLogger struct {
	responseLogger
}

func (l *hijackLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h := l.responseLogger.w.(http.Hijacker)
	conn, rw, err := h.Hijack()
	if err == nil && l.responseLogger.status == 0 {
		// The status will be StatusSwitchingProtocols if there was no error and WriteHeader has not been called yet
		l.responseLogger.status = http.StatusSwitchingProtocols
	}
	return conn, rw, err
}

const lowerhex = "0123456789abcdef"

func appendQuoted(buf []byte, s string) []byte {
	var runeTmp [utf8.UTFMax]byte
	for width := 0; len(s) > 0; s = s[width:] {
		r := rune(s[0])
		width = 1
		if r >= utf8.RuneSelf {
			r, width = utf8.DecodeRuneInString(s)
		}
		if width == 1 && r == utf8.RuneError {
			buf = append(buf, `\x`...)
			buf = append(buf, lowerhex[s[0]>>4])
			buf = append(buf, lowerhex[s[0]&0xF])
			continue
		}
		if r == rune('"') || r == '\\' { // always backslashed
			buf = append(buf, '\\')
			buf = append(buf, byte(r))
			continue
		}
		if strconv.IsPrint(r) {
			n := utf8.EncodeRune(runeTmp[:], r)
			buf = append(buf, runeTmp[:n]...)
			continue
		}
		switch r {
		case '\a':
			buf = append(buf, `\a`...)
		case '\b':
			buf = append(buf, `\b`...)
		case '\f':
			buf = append(buf, `\f`...)
		case '\n':
			buf = append(buf, `\n`...)
		case '\r':
			buf = append(buf, `\r`...)
		case '\t':
			buf = append(buf, `\t`...)
		case '\v':
			buf = append(buf, `\v`...)
		default:
			switch {
			case r < ' ':
				buf = append(buf, `\x`...)
				buf = append(buf, lowerhex[s[0]>>4])
				buf = append(buf, lowerhex[s[0]&0xF])
			case r > utf8.MaxRune:
				r = 0xFFFD
				fallthrough
			case r < 0x10000:
				buf = append(buf, `\u`...)
				for s := 12; s >= 0; s -= 4 {
					buf = append(buf, lowerhex[r>>uint(s)&0xF])
				}
			default:
				buf = append(buf, `\U`...)
				for s := 28; s >= 0; s -= 4 {
					buf = append(buf, lowerhex[r>>uint(s)&0xF])
				}
			}
		}
	}
	return buf

}

// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, ts time.Time, status int, size int) []byte {
	username := "-"
	if req.URL.User != nil {
		if name := req.URL.User.Username(); name != "" {
			username = name
		}
	}

	host, _, err := net.SplitHostPort(req.RemoteAddr)

	if err != nil {
		host = req.RemoteAddr
	}

	uri := req.URL.RequestURI()

	buf := make([]byte, 0, 3*len(uri)/2)
	buf = append(buf, host...)
	buf = append(buf, " - "...)
	buf = append(buf, username...)
	buf = append(buf, " ["...)
	buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
	buf = append(buf, `] "`...)
	buf = append(buf, req.Method...)
	buf = append(buf, " "...)
	buf = appendQuoted(buf, uri)
	buf = append(buf, " "...)
	buf = append(buf, req.Proto...)
	buf = append(buf, `" `...)
	buf = append(buf, strconv.Itoa(status)...)
	buf = append(buf, " "...)
	buf = append(buf, strconv.Itoa(size)...)
	return buf
}

// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(w io.Writer, req *http.Request, ts time.Time, status, size int) {
	buf := buildCommonLogLine(req, ts, status, size)
	buf = append(buf, '\n')
	w.Write(buf)
}

// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(w io.Writer, req *http.Request, ts time.Time, status, size int) {
	buf := buildCommonLogLine(req, ts, status, size)
	buf = append(buf, ` "`...)
	buf = appendQuoted(buf, req.Referer())
	buf = append(buf, `" "`...)
	buf = appendQuoted(buf, req.UserAgent())
	buf = append(buf, '"', '\n')
	w.Write(buf)
}

// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return combinedLoggingHandler{out, h}
}

// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
	return loggingHandler{out, h}
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httptest"
	"os/exec"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

func getOAuthClient(ctx context.Context, cfg *oauth2.Config) (*oauth2.Token, error) {
	tok := new(oauth2.Token)
	// Have to get a new token.
	print("Launching browser for OAuth exchange. To skip, rerun with environment variable 'OAUTH' set to 'NOBROWSER'.\n")
	code, err := tokenFromWeb(ctx, cfg)
	if err != nil {
		cfg.RedirectURL = "urn:ietf:wg:oauth:2.0:oob"
		authURL := cfg.AuthCodeURL("")
		fmt.Printf("Authorize this app at %s and paste the authorization code.\n> ", authURL)
		_, err = fmt.Scanf("%s", &code)
	}
	tok, err = cfg.Exchange(ctx, code)
	return tok, nil
}

func tokenFromWeb(ctx context.Context, config *oauth2.Config) (string, error) {
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		http.Error(rw, "", 500)
	}))
	defer ts.Close()
	config.RedirectURL = ts.URL
	authURL := config.AuthCodeURL(randState)
	errs := make(chan error)
	go func() {
		err := openURL(authURL)
		errs <- err
	}()
	err := <-errs
	if err == nil {
		code := <-ch
		return code, nil
	} else {
		return "", err
	}
}

func openURL(url string) error {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return nil
		}
	}
	return fmt.Errorf("Error opening URL in browser.")
}
<commit_msg>Actually support OAUTH=NOBROWSER.<commit_after>package lib

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"os/exec"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

func getOAuthClient(ctx context.Context, cfg *oauth2.Config) (*oauth2.Token, error) {
	tok := new(oauth2.Token)
	// Have to get a new token.
	browser := os.Getenv("OAUTH") != "NOBROWSER"
	code := ""
	var err error
	if browser {
		print("Launching browser for OAuth exchange. To skip, rerun with environment variable 'OAUTH' set to 'NOBROWSER'.\n")
		code, err = tokenFromWeb(ctx, cfg)
	}
	if err != nil || !browser {
		// Fall back to non-browser auth by rewriting the redirect URL and reading the auth code from stdin.
		cfg.RedirectURL = "urn:ietf:wg:oauth:2.0:oob"
		authURL := cfg.AuthCodeURL("")
		fmt.Printf("Authorize this app at %s and paste the authorization code.\n> ", authURL)
		_, err = fmt.Scanf("%s", &code)
	}
	tok, err = cfg.Exchange(ctx, code)
	return tok, nil
}

func tokenFromWeb(ctx context.Context, config *oauth2.Config) (string, error) {
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now().UnixNano())
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		http.Error(rw, "", 500)
	}))
	defer ts.Close()
	config.RedirectURL = ts.URL
	authURL := config.AuthCodeURL(randState)
	errs := make(chan error)
	go func() {
		err := openURL(authURL)
		errs <- err
	}()
	err := <-errs
	if err == nil {
		code := <-ch
		return code, nil
	} else {
		return "", err
	}
}

func openURL(url string) error {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return nil
		}
	}
	return fmt.Errorf("Error opening URL in browser.")
}
<|endoftext|>


<commit_before>package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
)

// Parser ...
type Parser interface {
	Name() string
	Extract(hostname string, bb *bytes.Buffer) (map[string]string, error)
}

// Packet holds the incoming traffic info
type Packet struct {
	Address  string
	Message  []byte
	LogEvent *LogEvent
}

// PreTag pretag json
type PreTag struct {
	Name, Type string
}

var hostToTypeCache = make(map[string]string)

func init() {
	if c, err := ioutil.ReadFile("pretag.json"); err == nil {
		var t []PreTag
		err = json.Unmarshal(c, &t)
		if err != nil {
			log.Printf("Error unmarshalling pretag.json: %s", err)
		}

		for _, v := range t {
			hostToTypeCache[v.Name] = v.Type
			log.Printf("pretag %s:%s", v.Name, v.Type)
		}
	} else {
		log.Printf("Error reading pretag.json: %s", err)
	}
}

func (p *Packet) String() string {
	return fmt.Sprintf("Address: %s '%s'", p.Address,
		Trunc(string(p.Message)))
}

func (p *Packet) determineParser() (fields map[string]string, err error) {
	var parser Parser

	for {
		parser = JournalJSONMako{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err == nil {
			hostToTypeCache[p.Address] = parser.Name()
			break
		}

		parser = MakoJSON{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err == nil {
			hostToTypeCache[p.Address] = parser.Name()
			break
		}

		parser = Base{}
		fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message))
		break
	}

	if err == nil {
		log.Printf("determined %s - %s", p.Address, parser.Name())
	}

	return
}

// Mill determines message type and parses into a LogEvent
func (p *Packet) Mill() (res *LogEvent, err error) {
	if p.Address == "" {
		return nil, fmt.Errorf("Address is empty")
	}
	if len(p.Message) == 0 {
		return nil, fmt.Errorf("Address is empty")
	}

	log.Printf("Mill packet: %s", p)

	var fields map[string]string

	switch hostToTypeCache[p.Address] {
	case "journaljsonmako":
		parser := JournalJSONMako{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
			log.Printf("Error parsing %s [%s]", parser.Name(), err)

			parser := Base{}
			if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
				return nil, err
			}
		}

	case "makojson":
		parser := MakoJSON{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
			log.Printf("Error parsing %s [%s]", parser.Name(), err)

			parser := Base{}
			if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
				return nil, err
			}
		}

	case "base":
		parser := Base{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
			return nil, err
		}

	default:
		if fields, err = p.determineParser(); err != nil {
			return nil, err
		}
	}

	stackTrace := []string{}
	fullStackTrace := fields["stack_trace"]
	if len(fullStackTrace) > 0 {
		stackTrace = strings.Split(fullStackTrace, "\n")
	}

	return &LogEvent{
		DataCenter:       fields["service_environment"],
		Cluster:          fields["service_pipeline"],
		Host:             fields["hostname"],
		Service:          fields["service_name"],
		Instance:         fields["service_version"],
		Version:          fields["version"],
		Level:            fields["level"],
		ThreadName:       fields["thread_name"],
		LoggerName:       fields["logger_name"],
		Message:          fields["message"],
		Timestamp:        fields["timestamp"],
		ThrownStackTrace: stackTrace,
	}, nil
}
<commit_msg>Added error checking for pretag.json decoding<commit_after>package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"strings"
)

// Parser ...
type Parser interface {
	Name() string
	Extract(hostname string, bb *bytes.Buffer) (map[string]string, error)
}

// Packet holds the incoming traffic info
type Packet struct {
	Address  string
	Message  []byte
	LogEvent *LogEvent
}

// PreTag pretag json
type PreTag struct {
	Name, Type string
}

// IsValid ...
func (pt PreTag) IsValid() bool {
	if pt.Name == "" {
		return false
	}
	if pt.Type == "" {
		return false
	}

	return true
}

var hostToTypeCache = make(map[string]string)

func init() {
	if c, err := ioutil.ReadFile("pretag.json"); err == nil {
		var pt []PreTag
		if err = json.Unmarshal(c, &pt); err == nil {
			for _, v := range pt {
				if v.IsValid() {
					hostToTypeCache[v.Name] = v.Type
					log.Printf("pretag: [%s:%s]", v.Name, v.Type)
				} else {
					log.Printf("Invalid pretag: [%s:%s]", v.Name, v.Type)
				}
			}
		} else {
			log.Fatalf("Error unmarshalling pretag.json: %s", err)
		}
	} else {
		log.Print(err)
	}
}

func (p *Packet) String() string {
	return fmt.Sprintf("Address: %s '%s'", p.Address,
		Trunc(string(p.Message)))
}

func (p *Packet) determineParser() (fields map[string]string, err error) {
	var parser Parser

	for {
		parser = JournalJSONMako{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err == nil {
			hostToTypeCache[p.Address] = parser.Name()
			break
		}

		parser = MakoJSON{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err == nil {
			hostToTypeCache[p.Address] = parser.Name()
			break
		}

		parser = Base{}
		fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message))
		break
	}

	if err == nil {
		log.Printf("determined %s - %s", p.Address, parser.Name())
	}

	return
}

// Mill determines message type and parses into a LogEvent
func (p *Packet) Mill() (res *LogEvent, err error) {
	if p.Address == "" {
		return nil, fmt.Errorf("Address is empty")
	}
	if len(p.Message) == 0 {
		return nil, fmt.Errorf("Address is empty")
	}

	log.Printf("Mill packet: %s", p)

	var fields map[string]string

	switch hostToTypeCache[p.Address] {
	case "journaljsonmako":
		parser := JournalJSONMako{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
			log.Printf("Error parsing %s [%s]", parser.Name(), err)

			parser := Base{}
			if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
				return nil, err
			}
		}

	case "makojson":
		parser := MakoJSON{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
			log.Printf("Error parsing %s [%s]", parser.Name(), err)

			parser := Base{}
			if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
				return nil, err
			}
		}

	case "base":
		parser := Base{}
		if fields, err = parser.Extract(p.Address, bytes.NewBuffer(p.Message)); err != nil {
			return nil, err
		}

	default:
		if fields, err = p.determineParser(); err != nil {
			return nil, err
		}
	}

	stackTrace := []string{}
	fullStackTrace := fields["stack_trace"]
	if len(fullStackTrace) > 0 {
		stackTrace = strings.Split(fullStackTrace, "\n")
	}

	return &LogEvent{
		DataCenter:       fields["service_environment"],
		Cluster:          fields["service_pipeline"],
		Host:             fields["hostname"],
		Service:          fields["service_name"],
		Instance:         fields["service_version"],
		Version:          fields["version"],
		Level:            fields["level"],
		ThreadName:       fields["thread_name"],
		LoggerName:       fields["logger_name"],
		Message:          fields["message"],
		Timestamp:        fields["timestamp"],
		ThrownStackTrace: stackTrace,
	}, nil
}
<|endoftext|>


<commit_before>// Package plastic is a game engine...
package plastic

import (
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/audio"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/collision"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/dlog"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/event"
	pmouse "bitbucket.org/oakmoundstudio/plasticpiston/plastic/mouse"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/render"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math/rand"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

var (
	initCh          = make(chan bool)
	sceneCh         = make(chan bool)
	quitCh          = make(chan bool)
	drawChannel     = make(chan bool)
	viewportChannel = make(chan [2]int)
	drawInit        = false
	runEventLoop    = false
	ScreenWidth     int
	ScreenHeight    int
	press           = key.DirPress
	release         = key.DirRelease
	black           = color.RGBA{0x00, 0x00, 0x00, 0xff}
	b               screen.Buffer
	winBuffer       screen.Buffer
	eb              *event.EventBus
	esc             = false
	l_debug         = false
	wd, _           = os.Getwd()
	imageDir        string
	audioDir        string
)

// Init initializes the plastic engine.
// It spawns off an event loop of several goroutines
// and loops through scenes after initalization.
func Init(firstScene string) {
	dlog.CreateLogFile()

	err := loadDefaultConf()

	// Set variables from conf file
	dlog.SetStringDebugLevel(conf.Debug.Level)
	dlog.SetDebugFilter(conf.Debug.Filter)

	if err != nil {
		dlog.Verb(err)
	}

	ScreenWidth = conf.Screen.Width
	ScreenHeight = conf.Screen.Height

	imageDir = filepath.Join(filepath.Dir(wd),
		conf.Assets.AssetPath,
		conf.Assets.ImagePath)
	audioDir = filepath.Join(filepath.Dir(wd),
		conf.Assets.AssetPath,
		conf.Assets.AudioPath)

	render.SetFontDefaults(wd, conf.Assets.AssetPath, conf.Assets.FontPath,
		conf.Font.Hinting, conf.Font.Color, conf.Font.File, conf.Font.Size,
		conf.Font.DPI)
	// END of loading variables from configuration

	// Init various engine pieces
	collision.Init()
	pmouse.Init()
	render.InitDrawHeap()
	audio.InitWinAudio()

	// Seed the rng
	curSeed := time.Now().UTC().UnixNano()
	rand.Seed(curSeed)
	dlog.Info("The seed is:", curSeed)
	fmt.Println("\n~~~~~~~~~~~~~~~\nTHE SEED IS:", curSeed, "\n~~~~~~~~~~~~~~~\n")

	// Load in assets
	err = render.BatchLoad(imageDir)
	if err != nil {
		dlog.Error(err)
		return
	}
	err = audio.BatchLoad(audioDir)
	if err != nil {
		dlog.Error(err)
		return
	}

	// Spawn off event loop goroutines
	go driver.Main(eventLoop)

	prevScene := ""
	sceneMap[firstScene].active = true

	<-initCh
	close(initCh)

	// Loop through scenes
	runEventLoop = true
	scene := firstScene
	var data interface{} = nil
	dlog.Info("First Scene Start")
	for {
		ViewX = 0
		ViewY = 0
		useViewBounds = false
		dlog.Info("~~~~~~~~~~~Scene Start~~~~~~~~~")
		sceneMap[scene].start(prevScene, data)
		// Send a signal to resume (or begin) drawing
		drawChannel <- true

		cont := true
		for cont {
			select {
			// The quit channel represents a signal
			// for the engine to stop.
			case <-quitCh:
				return
			case <-sceneCh:
				cont = sceneMap[scene].loop()
			}
		}
		dlog.Info("~~~~~~~~Scene End~~~~~~~~~~")
		prevScene = scene

		// Send a signal to stop drawing
		drawChannel <- true

		// Reset transient portions of the engine
		event.ResetEntities()
		event.ResetEventBus()
		render.ResetDrawHeap()
		collision.Clear()
		pmouse.Clear()
		render.PreDraw(0, nil)

		scene, data = sceneMap[scene].end()

		eb = event.GetEventBus()
	}
}

func eventLoop(s screen.Screen) {

	// The event loop requires information about
	// the size of the world and screen that is
	// being dealt with, and so initializes it here.
	//
	// Todo: add world size to config
	b, _ = s.NewBuffer(image.Point{4000, 4000})
	winBuffer, _ = s.NewBuffer(image.Point{ScreenWidth, ScreenHeight})
	w, err := s.NewWindow(&screen.NewWindowOptions{ScreenWidth, ScreenHeight})
	if err != nil {
		dlog.Error(err)
	}
	defer w.Release()

	// This initialization happens here on account of font's initialization
	// requiring a buffer to draw to. Can probably change in the future.
	render.InitFont(b, winBuffer)

	eb = event.GetEventBus()

	// Todo: add frame rate to config
	frameRate := 60
	frameCh := make(chan bool)

	// This goroutine maintains a logical framerate
	go func(frameCh chan bool, frameRate int64) {
		c := time.Tick(time.Second / time.Duration(frameRate))
		for range c {
			frameCh <- true
		}
	}(frameCh, int64(frameRate))

	// Native go event handler
	go func() {
		for {
			e := w.NextEvent()
			// format := "got %#v\n"
			// if _, ok := e.(fmt.Stringer); ok {
			// 	format = "got %v\n"
			// }
			// if l_debug {
			// 	fmt.Printf(format, e)
			// }
			switch e := e.(type) {

			// We only currently respond to death lifecycle events.
			case lifecycle.Event:
				if e.To == lifecycle.StageDead {
					quitCh <- true
					return
				}

			// Send key events
			//
			// Key events have two varieties:
			// The "KeyDown" and "KeyUp" events, which trigger for all keys
			// and specific "KeyDown$key", etc events which trigger only for $key.
			// The specific key that is pressed is passed as the data interface for
			// the former events, but not for the latter.
			case key.Event:
				k := GetKeyBind(e.Code.String()[4:])
				if e.Direction == press {
					fmt.Println("--------------------", e.Code.String()[4:], k)
					setDown(k)
					eb.Trigger("KeyDown", k)
					eb.Trigger("KeyDown"+k, nil)
				} else if e.Direction == release {
					setUp(k)
					eb.Trigger("KeyUp", k)
					eb.Trigger("KeyUp"+k, nil)
				}

			// Send mouse events
			//
			// Mouse events are parsed based on their button
			// and direction into an event name and then triggered:
			// 'MousePress', 'MouseRelease', 'MouseScrollDown', 'MouseScrollUp', and 'MouseDrag'
			//
			// The basic event name is meant for entities which
			// want to respond to the mouse event happening -anywhere-.
			//
			// For events which have mouse collision enabled, they'll recieve
			// $eventName+"On" when the event occurs within their collision area.
			//
			// Mouse events all recieve an x, y, and button string.
			case mouse.Event:
				button := pmouse.GetMouseButton(int32(e.Button))
				dlog.Verb("Mouse direction ", e.Direction.String(), " Button ", button)
				mevent := pmouse.MouseEvent{e.X, e.Y, button}
				var eventName string
				if e.Direction == mouse.DirPress {
					setDown(button)
					eventName = "MousePress"
				} else if e.Direction == mouse.DirRelease {
					setUp(button)
					eventName = "MouseRelease"
				} else if e.Button == -2 {
					eventName = "MouseScrollDown"
				} else if e.Button == -1 {
					eventName = "MouseScrollUp"
				} else {
					eventName = "MouseDrag"
				}
				eb.Trigger(eventName, mevent)
				pmouse.Propagate(eventName+"On", mevent)

			// I don't really know what a paint event is to be honest.
			case paint.Event:

			// We hypothetically don't allow the user to manually resize
			// their window, so we don't do anything special for such events.
			case size.Event:
				fmt.Println("Window resized")

			case error:
				dlog.Error(e)
			}

			// This is a hardcoded quit function bound to the escape key.
			if IsDown("Escape") {
				if esc {
					dlog.Warn("Quiting plastic from holding ESCAPE")
					w.Send(lifecycle.Event{0, 0, nil})
				}
				esc = true
			} else {
				esc = false
			}
		}
	}()

	// This sends a signal to initiate the first scene
	initCh <- true

	// The draw loop
	// Unless told to stop, the draw channel will repeatedly
	// 1. draw black to a temporary buffer
	// 2. run any functions bound to precede drawing.
	// 3. draw all elements onto the temporary buffer.
	// 4. run any functions bound to follow drawing.
	// 5. draw the buffer's data at the viewport's position to the screen.
	// 6. publish the screen to display in window.
	go func() {
		<-drawChannel
		lastTime := time.Now()
		text := render.NewText("", float64(10+ViewX), float64(20+ViewY))
		render.Draw(text, 60000)
		for {
			dlog.Verb("Draw Loop")
		drawSelect:
			select {

			case <-drawChannel:
				dlog.Verb("Got something from draw channel")
				for {
					select {
					case <-drawChannel:
						render.Draw(text, 60000)
						break drawSelect
					case viewPoint := <-viewportChannel:
						dlog.Verb("Got something from viewport channel (waiting on draw)")
						updateScreen(viewPoint[0], viewPoint[1])
					}

				}
			case viewPoint := <-viewportChannel:
				dlog.Verb("Got something from viewport channel")
				updateScreen(viewPoint[0], viewPoint[1])
			default:
				// dlog.Verb("Default")
				eb = event.GetEventBus()
				draw.Draw(b.RGBA(), b.Bounds(), image.Black, image.Point{0, 0}, screen.Src)

				eb.Trigger("PreDraw", nil)
				render.DrawHeap(b)
				draw.Draw(winBuffer.RGBA(), winBuffer.Bounds(), b.RGBA(), image.Point{ViewX, ViewY}, screen.Src)
				render.DrawStaticHeap(winBuffer)
				eb.Trigger("PostDraw", b)

				w.Upload(image.Point{0, 0}, winBuffer, winBuffer.Bounds())
				w.Publish()

				timeSince := 1000000000.0 / float64(time.Since(lastTime).Nanoseconds())
				text.SetText(strconv.Itoa(int(timeSince)))
				text.SetPos(float64(10+ViewX), float64(20+ViewY))
				lastTime = time.Now()
			}
		}
	}()

	// The logical loop.
	// In order, it waits on receiving a signal to begin a logical frame.
	// It then runs any functions bound to when a frame begins.
	// It then allows a scene to perform it's loop operation.
	// It then runs any functions bound to when a frame ends.
	for {
		for runEventLoop {
			<-frameCh
			eb.Trigger("EnterFrame", nil)
			eb.Trigger("ExitFrame", nil)
			sceneCh <- true
		}
	}
}

func GetScreen() draw.Image {
	return b.RGBA()
}
<commit_msg>Changed a room to no longer have stacking exits<commit_after>// Package plastic is a game engine...
package plastic

import (
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/audio"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/collision"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/dlog"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/event"
	pmouse "bitbucket.org/oakmoundstudio/plasticpiston/plastic/mouse"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/render"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math/rand"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

var (
	initCh          = make(chan bool)
	sceneCh         = make(chan bool)
	quitCh          = make(chan bool)
	drawChannel     = make(chan bool)
	viewportChannel = make(chan [2]int)
	drawInit        = false
	runEventLoop    = false
	ScreenWidth     int
	ScreenHeight    int
	press           = key.DirPress
	release         = key.DirRelease
	black           = color.RGBA{0x00, 0x00, 0x00, 0xff}
	b               screen.Buffer
	winBuffer       screen.Buffer
	eb              *event.EventBus
	esc             = false
	l_debug         = false
	wd, _           = os.Getwd()
	imageDir        string
	audioDir        string
)

// Init initializes the plastic engine.
// It spawns off an event loop of several goroutines
// and loops through scenes after initalization.
func Init(firstScene string) {
	dlog.CreateLogFile()

	err := loadDefaultConf()

	// Set variables from conf file
	dlog.SetStringDebugLevel(conf.Debug.Level)
	dlog.SetDebugFilter(conf.Debug.Filter)

	if err != nil {
		dlog.Verb(err)
	}

	ScreenWidth = conf.Screen.Width
	ScreenHeight = conf.Screen.Height

	imageDir = filepath.Join(filepath.Dir(wd),
		conf.Assets.AssetPath,
		conf.Assets.ImagePath)
	audioDir = filepath.Join(filepath.Dir(wd),
		conf.Assets.AssetPath,
		conf.Assets.AudioPath)

	render.SetFontDefaults(wd, conf.Assets.AssetPath, conf.Assets.FontPath,
		conf.Font.Hinting, conf.Font.Color, conf.Font.File, conf.Font.Size,
		conf.Font.DPI)
	// END of loading variables from configuration

	// Init various engine pieces
	collision.Init()
	pmouse.Init()
	render.InitDrawHeap()
	audio.InitWinAudio()

	// Seed the rng
	curSeed := time.Now().UTC().UnixNano()
	//curSeed = 1471104995917281000
	rand.Seed(curSeed)
	dlog.Info("The seed is:", curSeed)
	fmt.Println("\n~~~~~~~~~~~~~~~\nTHE SEED IS:", curSeed, "\n~~~~~~~~~~~~~~~\n")

	// Load in assets
	err = render.BatchLoad(imageDir)
	if err != nil {
		dlog.Error(err)
		return
	}
	err = audio.BatchLoad(audioDir)
	if err != nil {
		dlog.Error(err)
		return
	}

	// Spawn off event loop goroutines
	go driver.Main(eventLoop)

	prevScene := ""
	sceneMap[firstScene].active = true

	<-initCh
	close(initCh)

	// Loop through scenes
	runEventLoop = true
	scene := firstScene
	var data interface{} = nil
	dlog.Info("First Scene Start")
	for {
		ViewX = 0
		ViewY = 0
		useViewBounds = false
		dlog.Info("~~~~~~~~~~~Scene Start~~~~~~~~~")
		sceneMap[scene].start(prevScene, data)
		// Send a signal to resume (or begin) drawing
		drawChannel <- true

		cont := true
		for cont {
			select {
			// The quit channel represents a signal
			// for the engine to stop.
			case <-quitCh:
				return
			case <-sceneCh:
				cont = sceneMap[scene].loop()
			}
		}
		dlog.Info("~~~~~~~~Scene End~~~~~~~~~~")
		prevScene = scene

		// Send a signal to stop drawing
		drawChannel <- true

		// Reset transient portions of the engine
		event.ResetEntities()
		event.ResetEventBus()
		render.ResetDrawHeap()
		collision.Clear()
		pmouse.Clear()
		render.PreDraw(0, nil)

		scene, data = sceneMap[scene].end()

		eb = event.GetEventBus()
	}
}

func eventLoop(s screen.Screen) {

	// The event loop requires information about
	// the size of the world and screen that is
	// being dealt with, and so initializes it here.
	//
	// Todo: add world size to config
	b, _ = s.NewBuffer(image.Point{4000, 4000})
	winBuffer, _ = s.NewBuffer(image.Point{ScreenWidth, ScreenHeight})
	w, err := s.NewWindow(&screen.NewWindowOptions{ScreenWidth, ScreenHeight})
	if err != nil {
		dlog.Error(err)
	}
	defer w.Release()

	// This initialization happens here on account of font's initialization
	// requiring a buffer to draw to. Can probably change in the future.
	render.InitFont(b, winBuffer)

	eb = event.GetEventBus()

	// Todo: add frame rate to config
	frameRate := 60
	frameCh := make(chan bool)

	// This goroutine maintains a logical framerate
	go func(frameCh chan bool, frameRate int64) {
		c := time.Tick(time.Second / time.Duration(frameRate))
		for range c {
			frameCh <- true
		}
	}(frameCh, int64(frameRate))

	// Native go event handler
	go func() {
		for {
			e := w.NextEvent()
			// format := "got %#v\n"
			// if _, ok := e.(fmt.Stringer); ok {
			// 	format = "got %v\n"
			// }
			// if l_debug {
			// 	fmt.Printf(format, e)
			// }
			switch e := e.(type) {

			// We only currently respond to death lifecycle events.
			case lifecycle.Event:
				if e.To == lifecycle.StageDead {
					quitCh <- true
					return
				}

			// Send key events
			//
			// Key events have two varieties:
			// The "KeyDown" and "KeyUp" events, which trigger for all keys
			// and specific "KeyDown$key", etc events which trigger only for $key.
			// The specific key that is pressed is passed as the data interface for
			// the former events, but not for the latter.
			case key.Event:
				k := GetKeyBind(e.Code.String()[4:])
				if e.Direction == press {
					fmt.Println("--------------------", e.Code.String()[4:], k)
					setDown(k)
					eb.Trigger("KeyDown", k)
					eb.Trigger("KeyDown"+k, nil)
				} else if e.Direction == release {
					setUp(k)
					eb.Trigger("KeyUp", k)
					eb.Trigger("KeyUp"+k, nil)
				}

			// Send mouse events
			//
			// Mouse events are parsed based on their button
			// and direction into an event name and then triggered:
			// 'MousePress', 'MouseRelease', 'MouseScrollDown', 'MouseScrollUp', and 'MouseDrag'
			//
			// The basic event name is meant for entities which
			// want to respond to the mouse event happening -anywhere-.
			//
			// For events which have mouse collision enabled, they'll recieve
			// $eventName+"On" when the event occurs within their collision area.
			//
			// Mouse events all recieve an x, y, and button string.
			case mouse.Event:
				button := pmouse.GetMouseButton(int32(e.Button))
				dlog.Verb("Mouse direction ", e.Direction.String(), " Button ", button)
				mevent := pmouse.MouseEvent{e.X, e.Y, button}
				var eventName string
				if e.Direction == mouse.DirPress {
					setDown(button)
					eventName = "MousePress"
				} else if e.Direction == mouse.DirRelease {
					setUp(button)
					eventName = "MouseRelease"
				} else if e.Button == -2 {
					eventName = "MouseScrollDown"
				} else if e.Button == -1 {
					eventName = "MouseScrollUp"
				} else {
					eventName = "MouseDrag"
				}
				eb.Trigger(eventName, mevent)
				pmouse.Propagate(eventName+"On", mevent)

			// I don't really know what a paint event is to be honest.
			case paint.Event:

			// We hypothetically don't allow the user to manually resize
			// their window, so we don't do anything special for such events.
			case size.Event:
				fmt.Println("Window resized")

			case error:
				dlog.Error(e)
			}

			// This is a hardcoded quit function bound to the escape key.
			if IsDown("Escape") {
				if esc {
					dlog.Warn("Quiting plastic from holding ESCAPE")
					w.Send(lifecycle.Event{0, 0, nil})
				}
				esc = true
			} else {
				esc = false
			}
		}
	}()

	// This sends a signal to initiate the first scene
	initCh <- true

	// The draw loop
	// Unless told to stop, the draw channel will repeatedly
	// 1. draw black to a temporary buffer
	// 2. run any functions bound to precede drawing.
	// 3. draw all elements onto the temporary buffer.
	// 4. run any functions bound to follow drawing.
	// 5. draw the buffer's data at the viewport's position to the screen.
	// 6. publish the screen to display in window.
	go func() {
		<-drawChannel
		lastTime := time.Now()
		text := render.NewText("", float64(10+ViewX), float64(20+ViewY))
		render.Draw(text, 60000)
		for {
			dlog.Verb("Draw Loop")
		drawSelect:
			select {

			case <-drawChannel:
				dlog.Verb("Got something from draw channel")
				for {
					select {
					case <-drawChannel:
						render.Draw(text, 60000)
						break drawSelect
					case viewPoint := <-viewportChannel:
						dlog.Verb("Got something from viewport channel (waiting on draw)")
						updateScreen(viewPoint[0], viewPoint[1])
					}

				}
			case viewPoint := <-viewportChannel:
				dlog.Verb("Got something from viewport channel")
				updateScreen(viewPoint[0], viewPoint[1])
			default:
				// dlog.Verb("Default")
				eb = event.GetEventBus()
				draw.Draw(b.RGBA(), b.Bounds(), image.Black, image.Point{0, 0}, screen.Src)

				eb.Trigger("PreDraw", nil)
				render.DrawHeap(b)
				draw.Draw(winBuffer.RGBA(), winBuffer.Bounds(), b.RGBA(), image.Point{ViewX, ViewY}, screen.Src)
				render.DrawStaticHeap(winBuffer)
				eb.Trigger("PostDraw", b)

				w.Upload(image.Point{0, 0}, winBuffer, winBuffer.Bounds())
				w.Publish()

				timeSince := 1000000000.0 / float64(time.Since(lastTime).Nanoseconds())
				text.SetText(strconv.Itoa(int(timeSince)))
				text.SetPos(float64(10+ViewX), float64(20+ViewY))
				lastTime = time.Now()
			}
		}
	}()

	// The logical loop.
	// In order, it waits on receiving a signal to begin a logical frame.
	// It then runs any functions bound to when a frame begins.
	// It then allows a scene to perform it's loop operation.
	// It then runs any functions bound to when a frame ends.
	for {
		for runEventLoop {
			<-frameCh
			eb.Trigger("EnterFrame", nil)
			eb.Trigger("ExitFrame", nil)
			sceneCh <- true
		}
	}
}

func GetScreen() draw.Image {
	return b.RGBA()
}
<|endoftext|>