<commit_before>// Copyright 2015 Peter Goetz
//
// 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.

// Based on the work done in
// https://github.com/golang/mock/blob/d581abfc04272f381d7a05e4b80163ea4e2b9447/mockgen/mockgen.go

// MockGen generates mock implementations of Go interfaces.
package mockgen

// TODO: This does not support recursive embedded interfaces.
// TODO: This does not support embedding package-local interfaces in a separate file.

import (
	"bytes"
	"fmt"
	"go/format"
	"go/token"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"unicode"

	"github.com/golang/mock/mockgen/model"
)

const (
	importPath = "github.com/petergtz/pegomock"
)

func GenerateMock(packagePath string, interfaceName string) (bool, string) {
	tmppath := "mock_" + strings.ToLower(interfaceName) + "_test.go.tmp"
	Run("",
		tmppath,
		filepath.Base(packagePath),
		"",
		false,
		packagePath, interfaceName)

	existingFileContent, err := ioutil.ReadFile("mock_" + strings.ToLower(interfaceName) + "_test.go")
	if err != nil {
		if os.IsNotExist(err) {
			err = os.Rename(tmppath, "mock_"+strings.ToLower(interfaceName)+"_test.go")
			panicOnError(err)
			return true, "mock_" + strings.ToLower(interfaceName) + "_test.go"
		} else {
			panic(err)
		}
	}
	newFileContent, err := ioutil.ReadFile(tmppath)
	if err != nil {
		panic(err)
	}
	if string(existingFileContent) == string(newFileContent) {
		os.Remove(tmppath)
		return false, "mock_" + strings.ToLower(interfaceName) + "_test.go"
	} else {
		os.Rename(tmppath, "mock_"+strings.ToLower(interfaceName)+"_test.go")
		return true, "mock_" + strings.ToLower(interfaceName) + "_test.go"
	}
}

func Run(source string, destination string, packageOut string, selfPackage string, debugParser bool, args ...string) {

	var pkg *model.Package
	var err error
	if source != "" {
		pkg, err = ParseFile(source)
	} else {
		if len(args) != 2 {
			log.Fatal("Expected exactly two arguments")
		}
		pkg, err = Reflect(args[0], strings.Split(args[1], ","))
	}
	if err != nil {
		panic(fmt.Errorf("Loading input failed: %v", err))
	}

	if debugParser {
		pkg.Print(os.Stdout)
		return
	}

	dst := os.Stdout
	if len(destination) > 0 {
		f, err := os.Create(destination)
		if err != nil {
			panic(fmt.Errorf("Failed opening destination file: %v", err))
		}
		defer f.Close()
		dst = f
	}

	packageName := packageOut
	if packageName == "" {
		// pkg.Name in reflect mode is the base name of the import path,
		// which might have characters that are illegal to have in package names.
		packageName = "mock_" + sanitize(pkg.Name)
	}

	var src string
	if source != "" {
		src = source
	} else {
		src = fmt.Sprintf("%v (interfaces: %v)", args[0], args[1])
	}
	g := new(generator)
	if err := g.Generate(src, pkg, packageName, selfPackage); err != nil {
		panic(fmt.Errorf("Failed generating mock: %v", err))
	}
	if _, err := dst.Write(g.Output()); err != nil {
		panic(fmt.Errorf("Failed writing to destination: %v", err))
	}
}

type generator struct {
	buf    bytes.Buffer
	indent string

	packageMap map[string]string // map from import path to package name
}

func (g *generator) p(format string, args ...interface{}) *generator {
	fmt.Fprintf(&g.buf, g.indent+format+"\n", args...)
	return g
}

func (g *generator) in() *generator {
	g.indent += "\t"
	return g
}

func (g *generator) out() *generator {
	if len(g.indent) > 0 {
		g.indent = g.indent[0 : len(g.indent)-1]
	}
	return g
}

func removeDot(s string) string {
	if len(s) > 0 && s[len(s)-1] == '.' {
		return s[0 : len(s)-1]
	}
	return s
}

// sanitize cleans up a string to make a suitable package name.
func sanitize(s string) string {
	t := ""
	for _, r := range s {
		if t == "" {
			if unicode.IsLetter(r) || r == '_' {
				t += string(r)
				continue
			}
		} else {
			if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
				t += string(r)
				continue
			}
		}
		t += "_"
	}
	if t == "_" {
		t = "x"
	}
	return t
}

func (g *generator) Generate(source string, pkg *model.Package, pkgName string, selfPackage string) error {
	g.p("// Automatically generated by MockGen. DO NOT EDIT!")
	g.p("// Source: %v", source)
	g.p("")

	// Get all required imports, and generate unique names for them all.
	im := pkg.Imports()
	im[importPath] = true
	g.packageMap = make(map[string]string, len(im))
	localNames := make(map[string]bool, len(im))
	for pth := range im {
		base := sanitize(path.Base(pth))

		// Local names for an imported package can usually be the basename of the import path.
		// A couple of situations don't permit that, such as duplicate local names
		// (e.g. importing "html/template" and "text/template"), or where the basename is
		// a keyword (e.g. "foo/case").
		// try base0, base1, ...
		pkgName := base
		i := 0
		for localNames[pkgName] || token.Lookup(pkgName).IsKeyword() {
			pkgName = base + strconv.Itoa(i)
			i++
		}

		g.packageMap[pth] = pkgName
		localNames[pkgName] = true
	}

	g.p("package %v", pkgName)
	g.p("")
	g.p("import (")
	g.in()
	for path, pkg := range g.packageMap {
		if path == selfPackage {
			continue
		}
		g.p("%v %q", pkg, path)
	}
	for _, path := range pkg.DotImports {
		g.p(". %q", path)
	}
	g.out()
	g.p(")")

	for _, iface := range pkg.Interfaces {
		g.GenerateMockInterface(iface, selfPackage)
	}

	return nil
}

// The name of the mock type to use for the given interface identifier.
func mockName(typeName string) string {
	return "Mock" + typeName
}

func (g *generator) GenerateMockInterface(iface *model.Interface, selfPackage string) {
	mockType := mockName(iface.Name)

	g.p("")
	g.p("// Mock of %v interface", iface.Name)
	g.p("type %v struct {", mockType)
	g.in().p("fail func(message string, callerSkip ...int)").out()
	g.p("}")
	g.p("")

	g.p("func New%v() *%v {", mockType, mockType)
	g.in().p("return &%v{fail: pegomock.GlobalFailHandler}", mockType).out()
	g.p("}")
	g.p("")

	for _, method := range iface.Methods {
		g.GenerateMockMethod(mockType, method, selfPackage).p("")
	}
	g.p("type Verifier%v struct {", iface.Name)
	g.in().
		p("mock *Mock%v", iface.Name).
		p("invocationCountMatcher pegomock.Matcher").
		p("inOrderContext *pegomock.InOrderContext").
		out()
	g.p("}")
	g.p("")
	g.p("func (mock *Mock%v) VerifyWasCalledOnce() *Verifier%v {", iface.Name, iface.Name)
	g.in().p("return &Verifier%v{mock, pegomock.Times(1), nil}", iface.Name).out()
	g.p("}")
	g.p("")
	g.p("func (mock *Mock%v) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *Verifier%v {", iface.Name, iface.Name)
	g.in().p("return &Verifier%v{mock, invocationCountMatcher, nil}", iface.Name).out()
	g.p("}")
	g.p("")
	g.p("func (mock *Mock%v) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *Verifier%v {", iface.Name, iface.Name)
	g.in().p("return &Verifier%v{mock, invocationCountMatcher, inOrderContext}", iface.Name).out()
	g.p("}")
	g.p("")
	for _, method := range iface.Methods {
		g.GenerateVerifierMethod(iface.Name, method, selfPackage).p("")
	}
}

// GenerateMockMethod generates a mock method implementation.
// If non-empty, pkgOverride is the package in which unqualified types reside.
func (g *generator) GenerateMockMethod(mockType string, method *model.Method, pkgOverride string) *generator {
	_, _, argString, rets, retString, callArgs := getStuff(method, g, pkgOverride)
	g.p("func (mock *%v) %v(%v)%v {", mockType, method.Name, argString, retString)
	g.in()
	r := ""
	if len(method.Out) > 0 {
		r = "result :="
	}
	g.p("%v pegomock.GetGenericMockFrom(mock).Invoke(\"%v\", %v)", r, method.Name, callArgs)
	if len(method.Out) > 0 {
		// TODO: translate LastInvocation into a Matcher so it can be used as key for Stubbings
		g.p("if len(result) == 0 {")
		g.in()
		retValues := make([]string, len(rets))
		for i, ret := range rets {
			g.p("var ret%v %v", i, ret)
			retValues[i] = fmt.Sprintf("ret%v", i)
		}
		g.p("return %v", strings.Join(retValues, ", "))
		g.out()
		g.p("}")
		g.p("return %v", resultCast(rets))
	}
	g.out()
	g.p("}")
	return g
}

func resultCast(returnTypes []string) string {
	castedResults := make([]string, len(returnTypes))
	for i, returnType := range returnTypes {
		castedResults[i] = fmt.Sprintf("result[%v].(%v)", i, returnType)
	}
	return strings.Join(castedResults, ", ")
}

func (g *generator) GenerateVerifierMethod(interfaceName string, method *model.Method, pkgOverride string) *generator {
	_, _, argString, rets, retString, callArgs := getStuff(method, g, pkgOverride)

	g.p("func (verifier *Verifier%v) %v(%v)%v {", interfaceName, method.Name, argString, retString)
	g.p("pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, \"%v\", %v)", method.Name, callArgs)

	if len(method.Out) > 0 {
		retValues := make([]string, len(rets))
		for i, ret := range rets {
			g.p("var ret%v %v", i, ret)
			retValues[i] = fmt.Sprintf("ret%v", i)
		}
		g.p("return %v", strings.Join(retValues, ", "))
	}
	g.p("}")

	return g
}

func getStuff(method *model.Method, g *generator, pkgOverride string) (args []string, argNames []string, argString string, rets []string, retString string, callArgs string) {
	args = make([]string, len(method.In))
	argNames = make([]string, len(method.In))
	for i, p := range method.In {
		name := p.Name
		if name == "" {
			name = fmt.Sprintf("_param%d", i)
		}
		ts := p.Type.String(g.packageMap, pkgOverride)
		args[i] = name + " " + ts
		argNames[i] = name
	}
	if method.Variadic != nil {
		name := method.Variadic.Name
		if name == "" {
			name = fmt.Sprintf("_param%d", len(method.In))
		}
		ts := method.Variadic.Type.String(g.packageMap, pkgOverride)
		args = append(args, name+" ..."+ts)
		argNames = append(argNames, name)
	}
	argString = strings.Join(args, ", ")

	rets = make([]string, len(method.Out))
	for i, p := range method.Out {
		rets[i] = p.Type.String(g.packageMap, pkgOverride)
	}
	retString = strings.Join(rets, ", ")
	if len(rets) > 1 {
		retString = "(" + retString + ")"
	}
	if retString != "" {
		retString = " " + retString
	}

	callArgs = strings.Join(argNames, ", ")
	// TODO: variadic arguments
	// if method.Variadic != nil {
	// 	// Non-trivial. The generated code must build a []interface{},
	// 	// but the variadic argument may be any type.
	// 	g.p("_s := []interface{}{%s}", strings.Join(argNames[:len(argNames)-1], ", "))
	// 	g.p("for _, _x := range %s {", argNames[len(argNames)-1])
	// 	g.in()
	// 	g.p("_s = append(_s, _x)")
	// 	g.out()
	// 	g.p("}")
	// 	callArgs = ", _s..."
	// }
	return
}

// Output returns the generator's output, formatted in the standard Go style.
func (g *generator) Output() []byte {
	src, err := format.Source(g.buf.Bytes())
	if err != nil {
		panic(fmt.Errorf("Failed to format generated source code: %s\n%s", err, g.buf.String()))
	}
	return src
}

func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Some refactoring<commit_after>// Copyright 2015 Peter Goetz
//
// 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.

// Based on the work done in
// https://github.com/golang/mock/blob/d581abfc04272f381d7a05e4b80163ea4e2b9447/mockgen/mockgen.go

// MockGen generates mock implementations of Go interfaces.
package mockgen

// TODO: This does not support recursive embedded interfaces.
// TODO: This does not support embedding package-local interfaces in a separate file.

import (
	"bytes"
	"fmt"
	"go/format"
	"go/token"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"unicode"

	"github.com/golang/mock/mockgen/model"
)

const importPath = "github.com/petergtz/pegomock"

func GenerateMock(packagePath string, interfaceName string) (bool, string) {
	ast, err := Reflect(packagePath, []string{interfaceName})
	if err != nil {
		panic(fmt.Errorf("Loading input failed: %v", err))
	}

	output, err := GenerateOutput(
		ast,
		fmt.Sprintf("%v (interfaces: %v)", packagePath, interfaceName),
		filepath.Base(packagePath),
		"")
	if err != nil {
		panic(fmt.Errorf("Failed generating mock: %v", err))
	}

	outputFilepath := "mock_" + strings.ToLower(interfaceName) + "_test.go"

	existingFileContent, err := ioutil.ReadFile(outputFilepath)
	if err != nil {
		if os.IsNotExist(err) {
			err = ioutil.WriteFile(outputFilepath, output, 0666)
			panicOnError(err)
			return true, outputFilepath
		} else {
			panic(err)
		}
	}
	if string(existingFileContent) == string(output) {
		return false, outputFilepath
	} else {
		err = ioutil.WriteFile(outputFilepath, output, 0666)
		panicOnError(err)
		return true, outputFilepath
	}
}

func Run(source string, destination string, packageOut string, selfPackage string, debugParser bool, args ...string) {

	var pkg *model.Package
	var err error
	if source != "" {
		pkg, err = ParseFile(source)
	} else {
		if len(args) != 2 {
			log.Fatal("Expected exactly two arguments")
		}
		pkg, err = Reflect(args[0], strings.Split(args[1], ","))
	}
	if err != nil {
		panic(fmt.Errorf("Loading input failed: %v", err))
	}

	if debugParser {
		pkg.Print(os.Stdout)
		return
	}

	dst := os.Stdout
	if len(destination) > 0 {
		f, err := os.Create(destination)
		if err != nil {
			panic(fmt.Errorf("Failed opening destination file: %v", err))
		}
		defer f.Close()
		dst = f
	}

	var src string
	if source != "" {
		src = source
	} else {
		src = fmt.Sprintf("%v (interfaces: %v)", args[0], args[1])
	}
	g := new(generator)
	if err := g.Generate(src, pkg, packageOut, selfPackage); err != nil {
		panic(fmt.Errorf("Failed generating mock: %v", err))
	}
	if _, err := dst.Write(g.Output()); err != nil {
		panic(fmt.Errorf("Failed writing to destination: %v", err))
	}
}

type generator struct {
	buf    bytes.Buffer
	indent string

	packageMap map[string]string // map from import path to package name
}

func (g *generator) p(format string, args ...interface{}) *generator {
	fmt.Fprintf(&g.buf, g.indent+format+"\n", args...)
	return g
}

func (g *generator) in() *generator {
	g.indent += "\t"
	return g
}

func (g *generator) out() *generator {
	if len(g.indent) > 0 {
		g.indent = g.indent[0 : len(g.indent)-1]
	}
	return g
}

func removeDot(s string) string {
	if len(s) > 0 && s[len(s)-1] == '.' {
		return s[0 : len(s)-1]
	}
	return s
}

// sanitize cleans up a string to make a suitable package name.
func sanitize(s string) string {
	t := ""
	for _, r := range s {
		if t == "" {
			if unicode.IsLetter(r) || r == '_' {
				t += string(r)
				continue
			}
		} else {
			if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
				t += string(r)
				continue
			}
		}
		t += "_"
	}
	if t == "_" {
		t = "x"
	}
	return t
}

func GenerateOutput(ast *model.Package, source string, packageOut string, selfPackage string) ([]byte, error) {
	g := new(generator)
	if err := g.Generate(source, ast, packageOut, selfPackage); err != nil {
		return nil, fmt.Errorf("Failed generating mock: %v", err)
	}
	return g.Output(), nil
}

func (g *generator) Generate(source string, pkg *model.Package, pkgName string, selfPackage string) error {
	if pkgName == "" {
		// pkg.Name in reflect mode is the base name of the import path,
		// which might have characters that are illegal to have in package names.
		pkgName = "mock_" + sanitize(pkg.Name)
	}

	g.p("// Automatically generated by MockGen. DO NOT EDIT!")
	g.p("// Source: %v", source)
	g.p("")

	// Get all required imports, and generate unique names for them all.
	im := pkg.Imports()
	im[importPath] = true
	g.packageMap = make(map[string]string, len(im))
	localNames := make(map[string]bool, len(im))
	for pth := range im {
		base := sanitize(path.Base(pth))

		// Local names for an imported package can usually be the basename of the import path.
		// A couple of situations don't permit that, such as duplicate local names
		// (e.g. importing "html/template" and "text/template"), or where the basename is
		// a keyword (e.g. "foo/case").
		// try base0, base1, ...
		pkgName := base
		i := 0
		for localNames[pkgName] || token.Lookup(pkgName).IsKeyword() {
			pkgName = base + strconv.Itoa(i)
			i++
		}

		g.packageMap[pth] = pkgName
		localNames[pkgName] = true
	}

	g.p("package %v", pkgName)
	g.p("")
	g.p("import (")
	g.in()
	for path, pkg := range g.packageMap {
		if path == selfPackage {
			continue
		}
		g.p("%v %q", pkg, path)
	}
	for _, path := range pkg.DotImports {
		g.p(". %q", path)
	}
	g.out()
	g.p(")")

	for _, iface := range pkg.Interfaces {
		g.GenerateMockInterface(iface, selfPackage)
	}

	return nil
}

// The name of the mock type to use for the given interface identifier.
func mockName(typeName string) string {
	return "Mock" + typeName
}

func (g *generator) GenerateMockInterface(iface *model.Interface, selfPackage string) {
	mockType := mockName(iface.Name)

	g.p("")
	g.p("// Mock of %v interface", iface.Name)
	g.p("type %v struct {", mockType)
	g.in().p("fail func(message string, callerSkip ...int)").out()
	g.p("}")
	g.p("")

	g.p("func New%v() *%v {", mockType, mockType)
	g.in().p("return &%v{fail: pegomock.GlobalFailHandler}", mockType).out()
	g.p("}")
	g.p("")

	for _, method := range iface.Methods {
		g.GenerateMockMethod(mockType, method, selfPackage).p("")
	}
	g.p("type Verifier%v struct {", iface.Name)
	g.in().
		p("mock *Mock%v", iface.Name).
		p("invocationCountMatcher pegomock.Matcher").
		p("inOrderContext *pegomock.InOrderContext").
		out()
	g.p("}")
	g.p("")
	g.p("func (mock *Mock%v) VerifyWasCalledOnce() *Verifier%v {", iface.Name, iface.Name)
	g.in().p("return &Verifier%v{mock, pegomock.Times(1), nil}", iface.Name).out()
	g.p("}")
	g.p("")
	g.p("func (mock *Mock%v) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *Verifier%v {", iface.Name, iface.Name)
	g.in().p("return &Verifier%v{mock, invocationCountMatcher, nil}", iface.Name).out()
	g.p("}")
	g.p("")
	g.p("func (mock *Mock%v) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *Verifier%v {", iface.Name, iface.Name)
	g.in().p("return &Verifier%v{mock, invocationCountMatcher, inOrderContext}", iface.Name).out()
	g.p("}")
	g.p("")
	for _, method := range iface.Methods {
		g.GenerateVerifierMethod(iface.Name, method, selfPackage).p("")
	}
}

// GenerateMockMethod generates a mock method implementation.
// If non-empty, pkgOverride is the package in which unqualified types reside.
func (g *generator) GenerateMockMethod(mockType string, method *model.Method, pkgOverride string) *generator {
	_, _, argString, rets, retString, callArgs := getStuff(method, g, pkgOverride)
	g.p("func (mock *%v) %v(%v)%v {", mockType, method.Name, argString, retString)
	g.in()
	r := ""
	if len(method.Out) > 0 {
		r = "result :="
	}
	g.p("%v pegomock.GetGenericMockFrom(mock).Invoke(\"%v\", %v)", r, method.Name, callArgs)
	if len(method.Out) > 0 {
		// TODO: translate LastInvocation into a Matcher so it can be used as key for Stubbings
		g.p("if len(result) == 0 {")
		g.in()
		retValues := make([]string, len(rets))
		for i, ret := range rets {
			g.p("var ret%v %v", i, ret)
			retValues[i] = fmt.Sprintf("ret%v", i)
		}
		g.p("return %v", strings.Join(retValues, ", "))
		g.out()
		g.p("}")
		g.p("return %v", resultCast(rets))
	}
	g.out()
	g.p("}")
	return g
}

func resultCast(returnTypes []string) string {
	castedResults := make([]string, len(returnTypes))
	for i, returnType := range returnTypes {
		castedResults[i] = fmt.Sprintf("result[%v].(%v)", i, returnType)
	}
	return strings.Join(castedResults, ", ")
}

func (g *generator) GenerateVerifierMethod(interfaceName string, method *model.Method, pkgOverride string) *generator {
	_, _, argString, rets, retString, callArgs := getStuff(method, g, pkgOverride)

	g.p("func (verifier *Verifier%v) %v(%v)%v {", interfaceName, method.Name, argString, retString)
	g.p("pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, \"%v\", %v)", method.Name, callArgs)

	if len(method.Out) > 0 {
		retValues := make([]string, len(rets))
		for i, ret := range rets {
			g.p("var ret%v %v", i, ret)
			retValues[i] = fmt.Sprintf("ret%v", i)
		}
		g.p("return %v", strings.Join(retValues, ", "))
	}
	g.p("}")

	return g
}

func getStuff(method *model.Method, g *generator, pkgOverride string) (
	args []string,
	argNames []string,
	argString string,
	rets []string,
	retString string,
	callArgs string,
) {
	args = make([]string, len(method.In))
	argNames = make([]string, len(method.In))
	for i, p := range method.In {
		name := p.Name
		if name == "" {
			name = fmt.Sprintf("_param%d", i)
		}
		ts := p.Type.String(g.packageMap, pkgOverride)
		args[i] = name + " " + ts
		argNames[i] = name
	}
	if method.Variadic != nil {
		name := method.Variadic.Name
		if name == "" {
			name = fmt.Sprintf("_param%d", len(method.In))
		}
		ts := method.Variadic.Type.String(g.packageMap, pkgOverride)
		args = append(args, name+" ..."+ts)
		argNames = append(argNames, name)
	}
	argString = strings.Join(args, ", ")

	rets = make([]string, len(method.Out))
	for i, p := range method.Out {
		rets[i] = p.Type.String(g.packageMap, pkgOverride)
	}
	retString = strings.Join(rets, ", ")
	if len(rets) > 1 {
		retString = "(" + retString + ")"
	}
	if retString != "" {
		retString = " " + retString
	}

	callArgs = strings.Join(argNames, ", ")
	// TODO: variadic arguments
	// if method.Variadic != nil {
	// 	// Non-trivial. The generated code must build a []interface{},
	// 	// but the variadic argument may be any type.
	// 	g.p("_s := []interface{}{%s}", strings.Join(argNames[:len(argNames)-1], ", "))
	// 	g.p("for _, _x := range %s {", argNames[len(argNames)-1])
	// 	g.in()
	// 	g.p("_s = append(_s, _x)")
	// 	g.out()
	// 	g.p("}")
	// 	callArgs = ", _s..."
	// }
	return
}

// Output returns the generator's output, formatted in the standard Go style.
func (g *generator) Output() []byte {
	src, err := format.Source(g.buf.Bytes())
	if err != nil {
		panic(fmt.Errorf("Failed to format generated source code: %s\n%s", err, g.buf.String()))
	}
	return src
}

func panicOnError(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package photonstats

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

const (
	photonUrl     = "https://counter.photonengine.com/Counter/api/data/app/"
	photonRegion  = "jp"
	endSecondsAgo = 300
	secondsAgo    = 90
)

type PhotonStatsPlugin struct {
	Url           string
	AppId         string
	Region        string
	Token         string
	EndSecondsAgo int
	SecondsAgo    int
	Timeout       int
	Log           bool
}

var graphdef = map[string]mp.Graphs{
	"photon.rooms": {
		Label: "Photon Rooms",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "rooms", Label: "room", Diff: false},
		},
	},
	"photon.channel": {
		Label: "Photon Channel",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "channels", Label: "channel", Diff: false},
		},
	},
	"photon.stats": {
		Label: "Photon Stats",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "ccu", Label: "ccu", Diff: false},
			{Name: "rejects", Label: "reject", Diff: false},
		},
	},
	"photon.message": {
		Label: "Photon Message",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "messages", Label: "message", Diff: false},
		},
	},
	"photon.bandwidth": {
		Label: "Photon Bandwidth",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "bandwidth", Label: "bandwidth", Diff: false},
			{Name: "bandwidthchat", Label: "bandwidth chat", Diff: false},
		},
	},
}

func (u PhotonStatsPlugin) getPhotonStats(name string) (string, error) {
	endPointUrl := u.Url + u.AppId + "/" + u.Region + "/" + name
	photonUrl, err := url.Parse(endPointUrl)
	if err != nil {
		return "", err
	}
	q := photonUrl.Query()
	now := time.Now()
	end := now.Add(-time.Duration(u.EndSecondsAgo) * time.Second)
	start := end.Add(-time.Duration(u.SecondsAgo) * time.Second)
	q.Set("start", start.UTC().Format("2006-01-02T15:04:05"))
	q.Set("end", end.UTC().Format("2006-01-02T15:04:05"))
	photonUrl.RawQuery = q.Encode()
	if u.Log {
		log.Printf("request_url:%s", photonUrl.String())
		log.Printf("appid:%s", u.AppId)
		log.Printf("token:%s", u.Token)
	}
	req, err := http.NewRequest("GET", photonUrl.String(), nil)
	if err != nil {
		return "", err
	}
	req.Header.Set("Authorization", u.Token)

	client := &http.Client{Timeout: time.Duration(u.Timeout) * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("URL:%s, Range:%s(%s) - %s(%s), HTTP status error: %d",
			photonUrl.String(), start.Format("2006-01-02T15:04:05"), start.UTC().Format("2006-01-02T15:04:05"),
			end.Format("2006-01-02T15:04:05"), end.UTC().Format("2006-01-02T15:04:05"), resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	if u.Log {
		log.Printf("URL:%s, Range:%s(%s) - %s(%s), HTTP status error: %d",
			photonUrl.String(), start.Format("2006-01-02T15:04:05"), start.UTC().Format("2006-01-02T15:04:05"),
			end.Format("2006-01-02T15:04:05"), end.UTC().Format("2006-01-02T15:04:05"), resp.StatusCode)
		log.Printf("status:%d", resp.StatusCode)
		log.Printf("body:%s", string(body[:]))
	}
	return string(body[:]), nil
}

// FetchMetrics interface for mackerelplugin
func (u PhotonStatsPlugin) FetchMetrics() (stats map[string]interface{}, err error) {
	ccu, err := u.getPhotonStats("ccu")
	if err != nil {
		log.Printf("ccu_error:%s", err)
	}
	rooms, err := u.getPhotonStats("rooms")
	if err != nil {
		log.Printf("rooms_error:%s", err)
	}
	// NOTE Chatアプリケーションでのみ表示されます
	// 非Chatアプリはエラーステータスが返ります
	channels, err := u.getPhotonStats("channels")
	if err != nil {
		log.Printf("channels_error:%s", err)
	}
	rejects, err := u.getPhotonStats("rejects")
	if err != nil {
		log.Printf("reject_error:%s", err)
	}
	messages, err := u.getPhotonStats("messages")
	if err != nil {
		log.Printf("messages_error:%s", err)
	}
	bandwidth, err := u.getPhotonStats("bandwidth")
	if err != nil {
		log.Printf("bandwidth_error:%s", err)
	}
	bandwidthchat, err := u.getPhotonStats("bandwidthchat")
	if err != nil {
		log.Printf("bandwidthchat_error:%s", err)
	}
	return map[string]interface{}{
		"ccu":           ccu,
		"rooms":         rooms,
		"channels":      channels,
		"rejects":       rejects,
		"messages":      messages,
		"bandwidth":     bandwidth,
		"bandwidthchat": bandwidthchat,
	}, nil
}

// GraphDefinition interface for mackerelplugin
func (u PhotonStatsPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

func Do() {
	optEndSecondsAgo := flag.Int("end_seconds", endSecondsAgo, "seconds")
	optSecondsAgo := flag.Int("seconds", secondsAgo, "seconds")
	optAppid := flag.String("appid", "", "App Id")
	optUrl := flag.String("url", photonUrl, "Photon analytivs api url")
	optRegion := flag.String("region", photonRegion, "region")
	optToken := flag.String("token", "", "Authorization Token")
	optLog := flag.Bool("log", false, "Use logging")
	optTimeout := flag.Int("timeout", 10, "timeout")
	flag.Parse()

	var photon PhotonStatsPlugin
	photon.EndSecondsAgo = *optEndSecondsAgo
	photon.SecondsAgo = *optSecondsAgo
	photon.AppId = *optAppid
	photon.Url = *optUrl
	photon.Region = *optRegion
	photon.Token = *optToken
	photon.Log = *optLog
	photon.Timeout = *optTimeout

	helper := mp.NewMackerelPlugin(photon)
	helper.Run()
}
<commit_msg>300sec -> 180sec<commit_after>package photonstats

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
)

const (
	photonUrl     = "https://counter.photonengine.com/Counter/api/data/app/"
	photonRegion  = "jp"
	endSecondsAgo = 180
	secondsAgo    = 90
)

type PhotonStatsPlugin struct {
	Url           string
	AppId         string
	Region        string
	Token         string
	EndSecondsAgo int
	SecondsAgo    int
	Timeout       int
	Log           bool
}

var graphdef = map[string]mp.Graphs{
	"photon.rooms": {
		Label: "Photon Rooms",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "rooms", Label: "room", Diff: false},
		},
	},
	"photon.channel": {
		Label: "Photon Channel",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "channels", Label: "channel", Diff: false},
		},
	},
	"photon.stats": {
		Label: "Photon Stats",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "ccu", Label: "ccu", Diff: false},
			{Name: "rejects", Label: "reject", Diff: false},
		},
	},
	"photon.message": {
		Label: "Photon Message",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "messages", Label: "message", Diff: false},
		},
	},
	"photon.bandwidth": {
		Label: "Photon Bandwidth",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "bandwidth", Label: "bandwidth", Diff: false},
			{Name: "bandwidthchat", Label: "bandwidth chat", Diff: false},
		},
	},
}

func (u PhotonStatsPlugin) getPhotonStats(name string) (string, error) {
	endPointUrl := u.Url + u.AppId + "/" + u.Region + "/" + name
	photonUrl, err := url.Parse(endPointUrl)
	if err != nil {
		return "", err
	}
	q := photonUrl.Query()
	now := time.Now()
	end := now.Add(-time.Duration(u.EndSecondsAgo) * time.Second)
	start := end.Add(-time.Duration(u.SecondsAgo) * time.Second)
	q.Set("start", start.UTC().Format("2006-01-02T15:04:05"))
	q.Set("end", end.UTC().Format("2006-01-02T15:04:05"))
	photonUrl.RawQuery = q.Encode()
	if u.Log {
		log.Printf("request_url:%s", photonUrl.String())
		log.Printf("appid:%s", u.AppId)
		log.Printf("token:%s", u.Token)
	}
	req, err := http.NewRequest("GET", photonUrl.String(), nil)
	if err != nil {
		return "", err
	}
	req.Header.Set("Authorization", u.Token)

	client := &http.Client{Timeout: time.Duration(u.Timeout) * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("URL:%s, Range:%s(%s) - %s(%s), HTTP status error: %d",
			photonUrl.String(), start.Format("2006-01-02T15:04:05"), start.UTC().Format("2006-01-02T15:04:05"),
			end.Format("2006-01-02T15:04:05"), end.UTC().Format("2006-01-02T15:04:05"), resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	if u.Log {
		log.Printf("URL:%s, Range:%s(%s) - %s(%s), HTTP status error: %d",
			photonUrl.String(), start.Format("2006-01-02T15:04:05"), start.UTC().Format("2006-01-02T15:04:05"),
			end.Format("2006-01-02T15:04:05"), end.UTC().Format("2006-01-02T15:04:05"), resp.StatusCode)
		log.Printf("status:%d", resp.StatusCode)
		log.Printf("body:%s", string(body[:]))
	}
	return string(body[:]), nil
}

// FetchMetrics interface for mackerelplugin
func (u PhotonStatsPlugin) FetchMetrics() (stats map[string]interface{}, err error) {
	ccu, err := u.getPhotonStats("ccu")
	if err != nil {
		log.Printf("ccu_error:%s", err)
	}
	rooms, err := u.getPhotonStats("rooms")
	if err != nil {
		log.Printf("rooms_error:%s", err)
	}
	// NOTE Chatアプリケーションでのみ表示されます
	// 非Chatアプリはエラーステータスが返ります
	channels, err := u.getPhotonStats("channels")
	if err != nil {
		log.Printf("channels_error:%s", err)
	}
	rejects, err := u.getPhotonStats("rejects")
	if err != nil {
		log.Printf("reject_error:%s", err)
	}
	messages, err := u.getPhotonStats("messages")
	if err != nil {
		log.Printf("messages_error:%s", err)
	}
	bandwidth, err := u.getPhotonStats("bandwidth")
	if err != nil {
		log.Printf("bandwidth_error:%s", err)
	}
	bandwidthchat, err := u.getPhotonStats("bandwidthchat")
	if err != nil {
		log.Printf("bandwidthchat_error:%s", err)
	}
	return map[string]interface{}{
		"ccu":           ccu,
		"rooms":         rooms,
		"channels":      channels,
		"rejects":       rejects,
		"messages":      messages,
		"bandwidth":     bandwidth,
		"bandwidthchat": bandwidthchat,
	}, nil
}

// GraphDefinition interface for mackerelplugin
func (u PhotonStatsPlugin) GraphDefinition() map[string](mp.Graphs) {
	return graphdef
}

func Do() {
	optEndSecondsAgo := flag.Int("end_seconds", endSecondsAgo, "seconds")
	optSecondsAgo := flag.Int("seconds", secondsAgo, "seconds")
	optAppid := flag.String("appid", "", "App Id")
	optUrl := flag.String("url", photonUrl, "Photon analytivs api url")
	optRegion := flag.String("region", photonRegion, "region")
	optToken := flag.String("token", "", "Authorization Token")
	optLog := flag.Bool("log", false, "Use logging")
	optTimeout := flag.Int("timeout", 10, "timeout")
	flag.Parse()

	var photon PhotonStatsPlugin
	photon.EndSecondsAgo = *optEndSecondsAgo
	photon.SecondsAgo = *optSecondsAgo
	photon.AppId = *optAppid
	photon.Url = *optUrl
	photon.Region = *optRegion
	photon.Token = *optToken
	photon.Log = *optLog
	photon.Timeout = *optTimeout

	helper := mp.NewMackerelPlugin(photon)
	helper.Run()
}
<|endoftext|>


<commit_before>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

/*
 Monsti is a simple and resource efficient CMS.

 This package implements the document node type.
*/
package main

import (
	"flag"
	"fmt"
	"github.com/monsti/form"
	"github.com/monsti/service"
	"github.com/monsti/util"
	"github.com/monsti/util/l10n"
	"github.com/monsti/util/template"
	htmlT "html/template"
	"log"
	"os"
)

var settings struct {
	Monsti util.MonstiSettings
}

var renderer template.Renderer

type editFormData struct {
	Title, Body string
}

func edit(req service.Request, res *service.Response, infoServ *service.InfoClient) {
	G := l10n.UseCatalog(req.Session.Locale)
	data := editFormData{}
	dataServ, err := infoServ.FindDataService()
	if err != nil {
		panic("document: Could not connect to data service.")
	}
	form := form.NewForm(&data, form.Fields{
		"Title": form.Field{G("Title"), "", form.Required(G("Required.")), nil},
		"Body": form.Field{G("Body"), "", form.Required(G("Required.")),
			new(form.AlohaEditor)}})
	switch req.Method {
	case "GET":
		data.Title = req.Node.Title
		body, err := dataServ.GetNodeData(req.Site, req.Node.Path,
			"body.html")
		if err != nil {
			panic("document: Could not get node data")
		}
		data.Body = string(body)
	case "POST":
		if form.Fill(req.FormData) {
			node := req.Node
			node.Title = data.Title
			if err := dataServ.UpdateNode(req.Site, node); err != nil {
				panic("document: Could not update node: " + err.Error())
			}
			if err := dataServ.WriteNodeData(req.Site, req.Node.Path,
				"body.html", data.Body); err != nil {
				panic("document: Could not update node: " + err.Error())
			}
			res.Redirect = req.Node.Path
			return
		}
	default:
		panic("Request method not supported: " + req.Method)
	}
	fmt.Fprint(res, renderer.Render("document/edit",
		template.Context{"Form": form.RenderData()},
		req.Session.Locale, settings.Monsti.GetSiteTemplatesPath(req.Site)))
}

func view(req service.Request, res *service.Response,
	infoServ *service.InfoClient) {
	body := "yay!" //c.GetNodeData(req.Node.Path, "body.html")
	content := renderer.Render("document/view",
		template.Context{"Body": htmlT.HTML(body)},
		req.Session.Locale, settings.Monsti.GetSiteTemplatesPath(req.Site))
	fmt.Fprint(res, content)
}

func main() {
	logger := log.New(os.Stderr, "document ", log.LstdFlags)
	// Load configuration
	flag.Parse()
	if flag.NArg() != 1 {
		logger.Fatal("Expecting configuration path.")
	}
	cfgPath := util.GetConfigPath(flag.Arg(0))
	if err := util.LoadModuleSettings("document", cfgPath, &settings); err != nil {
		logger.Fatal("Could not load settings: ", err)
	}

	// Connect to Info service
	info, err := service.NewInfoConnection(settings.Monsti.GetServicePath(
		service.Info.String()))
	if err != nil {
		logger.Fatalf("Could not connect to INFO service: %v", err)
	}

	l10n.Setup("monsti", settings.Monsti.GetLocalePath())
	renderer.Root = settings.Monsti.GetTemplatesPath()

	provider := service.NewNodeProvider(logger, info)
	document := service.NodeTypeHandler{
		Name:       "Document",
		ViewAction: view,
		EditAction: edit,
	}
	provider.AddNodeType(&document)
	provider.Serve(settings.Monsti.GetServicePath(service.Node.String() +
		"_document"))
}
<commit_msg>Use Data service to fetch node data.<commit_after>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

/*
 Monsti is a simple and resource efficient CMS.

 This package implements the document node type.
*/
package main

import (
	"flag"
	"fmt"
	"github.com/monsti/form"
	"github.com/monsti/service"
	"github.com/monsti/util"
	"github.com/monsti/util/l10n"
	"github.com/monsti/util/template"
	htmlT "html/template"
	"log"
	"os"
)

var settings struct {
	Monsti util.MonstiSettings
}

var logger *log.Logger
var renderer template.Renderer

type editFormData struct {
	Title, Body string
}

func edit(req service.Request, res *service.Response, infoServ *service.InfoClient) {
	G := l10n.UseCatalog(req.Session.Locale)
	data := editFormData{}
	dataServ, err := infoServ.FindDataService()
	if err != nil {
		panic("document: Could not connect to data service.")
	}
	form := form.NewForm(&data, form.Fields{
		"Title": form.Field{G("Title"), "", form.Required(G("Required.")), nil},
		"Body": form.Field{G("Body"), "", form.Required(G("Required.")),
			new(form.AlohaEditor)}})
	switch req.Method {
	case "GET":
		data.Title = req.Node.Title
		body, err := dataServ.GetNodeData(req.Site, req.Node.Path,
			"body.html")
		if err != nil {
			panic("document: Could not get node data")
		}
		data.Body = string(body)
	case "POST":
		if form.Fill(req.FormData) {
			node := req.Node
			node.Title = data.Title
			if err := dataServ.UpdateNode(req.Site, node); err != nil {
				panic("document: Could not update node: " + err.Error())
			}
			if err := dataServ.WriteNodeData(req.Site, req.Node.Path,
				"body.html", data.Body); err != nil {
				panic("document: Could not update node: " + err.Error())
			}
			res.Redirect = req.Node.Path
			return
		}
	default:
		panic("Request method not supported: " + req.Method)
	}
	fmt.Fprint(res, renderer.Render("document/edit",
		template.Context{"Form": form.RenderData()},
		req.Session.Locale, settings.Monsti.GetSiteTemplatesPath(req.Site)))
}

func view(req service.Request, res *service.Response,
	infoServ *service.InfoClient) {
	dataServ, err := infoServ.FindDataService()
	if err != nil {
		logger.Fatalf("Could not connect to data service: %v", err)
	}
	body, err := dataServ.GetNodeData(req.Site, req.Node.Path, "body.html")
	if err != nil {
		logger.Fatalf("Could not fetch node data: %v", err)
	}
	content := renderer.Render("document/view",
		template.Context{"Body": htmlT.HTML(body)},
		req.Session.Locale, settings.Monsti.GetSiteTemplatesPath(req.Site))
	fmt.Fprint(res, content)
}

func main() {
	logger = log.New(os.Stderr, "document ", log.LstdFlags)
	// Load configuration
	flag.Parse()
	if flag.NArg() != 1 {
		logger.Fatal("Expecting configuration path.")
	}
	cfgPath := util.GetConfigPath(flag.Arg(0))
	if err := util.LoadModuleSettings("document", cfgPath, &settings); err != nil {
		logger.Fatal("Could not load settings: ", err)
	}

	// Connect to Info service
	info, err := service.NewInfoConnection(settings.Monsti.GetServicePath(
		service.Info.String()))
	if err != nil {
		logger.Fatalf("Could not connect to INFO service: %v", err)
	}

	l10n.Setup("monsti", settings.Monsti.GetLocalePath())
	renderer.Root = settings.Monsti.GetTemplatesPath()

	provider := service.NewNodeProvider(logger, info)
	document := service.NodeTypeHandler{
		Name:       "Document",
		ViewAction: view,
		EditAction: edit,
	}
	provider.AddNodeType(&document)
	provider.Serve(settings.Monsti.GetServicePath(service.Node.String() +
		"_document"))
}
<|endoftext|>


<commit_before>package set1

import (
	"bytes"
	"fmt"
	"regexp"
)

// decToHex is used to lookup a single hex value in decimal.
var decToHex = [16]byte{
	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
	'a', 'b', 'c', 'd', 'e', 'f',
}

// Encodes a decimal byte slice to a hexidecimal byte slice
func HexEncode(text []byte) []byte {
	// output is always twice the size of input
	out := make([]byte, len(text)*2)

	// based on this article:
	// https://learn.sparkfun.com/tutorials/hexadecimal#converting-tofrom-decimal
	for i, char := range text {
		secondDigit := decToHex[char%16]
		firstDigit := decToHex[char/16]
		out[i*2] = firstDigit
		out[i*2+1] = secondDigit
	}

	return out
}

// hexToDec is used to lookup a single decimal value in hex.
func hexToDec(char byte) (byte, error) {
	var val byte
	// subtract ASCII decimal codes so that:
	//   "0" becomes 0
	//   "a" and "A" become 10
	switch {
	case char >= '0' && char <= '9':
		val = char - '0'
	case char >= 'A' && char <= 'F':
		val = char - 'A' + 10
	case char >= 'a' && char <= 'f':
		val = char - 'a' + 10
	default:
		return 0, fmt.Errorf("invalid hex character: %s", []byte{char})
	}

	return val, nil
}

// HexDecode decodes a hexidecimal byte slice to a decimal byte slice
func HexDecode(text []byte) ([]byte, error) {
	if len(text)%2 != 0 {
		return []byte{}, fmt.Errorf("input must be an even size")
	}

	// output is always half the size of input
	out := make([]byte, len(text)/2)

	// based on this article:
	// https://learn.sparkfun.com/tutorials/hexadecimal#converting-tofrom-decimal
	for i := 0; i < len(out); i++ {
		firstDigit, err := hexToDec(text[i*2])
		if err != nil {
			return []byte{}, err
		}

		secondDigit, err := hexToDec(text[i*2+1])
		if err != nil {
			return []byte{}, err
		}

		out[i] = firstDigit*16 + secondDigit
	}

	return out, nil
}

// decToBase64 is used to lookup base64 characters using zero-indexed 6bit
// values
const decToBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

// sixBitsMax is a 6bit value of all binary 1s:
//	- bin: 111111
//	- dec: 63
//	- hex: 0x3F
const sixBitsMax = 0x3F

// Base64Encode encodes a byte slice into a base64 byte slice
func Base64Encode(text []byte) []byte {
	// 4 bytes out for every 3 bytes in
	size := len(text) / 3 * 4
	// allow for padding if input is not divisible by 3
	if len(text)%3 != 0 {
		size += 4
	}
	out := make([]byte, size)

	for i := 0; i < len(text); i += 3 {
		// convert three 8bit characters into one 24bit value by:
		// - converting from byte (int8) to int32
		// - bit shifting to the left, to pad least significant bits, so that they don't overlap
		// - bit ORing to combine the values into one
		// - ignore the last two characters if we've reached the end of the input
		combined := int32(text[i]) << 16
		if i+1 < len(text) {
			combined |= int32(text[i+1]) << 8
		}
		if i+2 < len(text) {
			combined |= int32(text[i+2]) << 0
		}

		// split the 24bit value into four 6bit values by:
		// - bit shifting to the right, so the least significant 6bits are what we want
		// - bit ANDing against 6bits of binary 1s to extract the least significant 6bits
		// - looking up the appropriate base64 character for the value
		// - pad the last two characters if we've reached the end of the input
		outIndex := i / 3 * 4
		out[outIndex] = decToBase64[combined>>18&sixBitsMax]
		out[outIndex+1] = decToBase64[combined>>12&sixBitsMax]

		if i+1 < len(text) {
			out[outIndex+2] = decToBase64[combined>>6&sixBitsMax]
		} else {
			out[outIndex+2] = '='
		}

		if i+2 < len(text) {
			out[outIndex+3] = decToBase64[combined>>0&sixBitsMax]
		} else {
			out[outIndex+3] = '='
		}
	}

	return out
}

// base64ToDec is used to lookup a zero-indexed value for a base64 character
func base64ToDec(char byte) (int32, error) {
	switch {
	case char >= 'A' && char <= 'Z':
		return int32(char) - 'A', nil
	case char >= 'a' && char <= 'z':
		return int32(char) - 'a' + 26, nil
	case char >= '0' && char <= '9':
		return int32(char) - '0' + 52, nil
	case char == '+':
		return 62, nil
	case char == '/':
		return 63, nil
	case char == '=':
		return 0, nil
	}

	return 0, fmt.Errorf("invalid base64 character: %s", []byte{char})
}

// StripBytes removes all occurrences of a byte from a byte slice.
func StripBytes(s []byte, c byte) []byte {
	for {
		i := bytes.IndexByte(s, c)
		if i == -1 {
			break
		}

		s = append(s[:i], s[i+1:]...)
	}

	return s
}

// eightBitsMax is a 8bit value of all binary 1s:
//	- bin: 11111111
//	- dec: 255
//	- hex: 0xFF
const eightBitsMax = 0xFF

// Base64Decode decodes a base64 byte slice into an byte slice
func Base64Decode(text []byte) ([]byte, error) {
	text = StripBytes(text, '\n')

	// 4 bytes out for every 3 bytes in
	out := make([]byte, len(text)/4*3)

	for i := 0; i < len(text); i += 4 {
		// convert four 6bit characters into one 24bit value by:
		// - looking up the appropriate index for the base64 character
		// - bit shifting to the left, to pad least significant bits, so that they don't overlap
		// - bit ORing to combine the values into one
		var combined int32
		for offset, shift := range []uint{18, 12, 6, 0} {
			v, err := base64ToDec(text[i+offset])
			if err != nil {
				return []byte{}, err
			}

			combined |= v << shift
		}

		// split the 24bit value into three 8bit values by:
		// - bit shifting to the right, so the least significant 8bits are what we want
		// - bit ANDing against 8bits of binary 1s to extract the least significant 8bits
		// - converting to byte so that we get the appropriate ASCII code
		outIndex := i / 4 * 3
		out[outIndex] = byte(combined >> 16 & eightBitsMax)
		out[outIndex+1] = byte(combined >> 8 & eightBitsMax)
		out[outIndex+2] = byte(combined >> 0 & eightBitsMax)
	}

	// remove trailing padding
	out = bytes.TrimRight(out, "\x00")

	return out, nil
}

// HexToBase64 converts hexidecimal encoded text to base64.
func HexToBase64(text []byte) ([]byte, error) {
	raw, err := HexDecode(text)
	if err != nil {
		return []byte{}, err
	}

	return Base64Encode(raw), nil
}

// FixedKeyXOR encrypts some text against a key of the same size.
func FixedKeyXOR(text, key []byte) ([]byte, error) {
	if len(text) != len(key) {
		return []byte{}, fmt.Errorf("text and key must be same size: %d != %d", len(text), len(key))
	}

	return RepeatingKeyXOR(text, key)
}

// RepeatingKeyXOR encrypts some text against a repeating key of a smaller
// size.
func RepeatingKeyXOR(text, key []byte) ([]byte, error) {
	var keyIndex int
	xor := make([]byte, len(text))
	for i := 0; i < len(xor); i++ {
		// repeat the beginning of the key if we've reached the end
		if keyIndex >= len(key) {
			keyIndex = 0
		}

		xor[i] = text[i] ^ key[keyIndex]
		keyIndex++
	}

	return xor, nil
}

// ScoreEnglish returns a score indicating the likelihood that a string
// is comprised of English words by counting the most commonly occurring
// letters in the English language.
func ScoreEnglish(text []byte) int {
	re := regexp.MustCompile("(?i)[ETAOIN SHRDLU]")
	matches := re.FindAll(text, -1)

	return len(matches)
}

// KeyScore can be used to keep track of the most likely key.
type KeyScore struct {
	Score     int
	Key, Text []byte
}

// BruteForceSingleByteXOR finds the single byte key that some text has been
// XORed against.
func BruteForceSingleByteXOR(text []byte) (KeyScore, error) {
	var highestScore KeyScore

	// try all printable ASCII characters
	for key := byte(32); key <= byte(127); key++ {
		out, err := RepeatingKeyXOR(text, []byte{key})
		if err != nil {
			return highestScore, err
		}

		if score := ScoreEnglish(out); score > highestScore.Score {
			highestScore = KeyScore{
				Score: score,
				Key:   []byte{key},
				Text:  out,
			}
		}
	}

	return highestScore, nil
}

// HammingDistance returns the number of differences between two byte
// slices: https://en.wikipedia.org/wiki/Hamming_distance
func HammingDistance(one, two []byte) (int, error) {
	if len(one) != len(two) {
		return 0, fmt.Errorf("inputs must be same length: %d != %d", len(one), len(two))
	}

	var dist int
	for i := 0; i < len(one); i++ {
		if one[i] != two[i] {
			dist++
		}
	}

	return dist, nil
}

// BruteForceMultiByteXOR finds the multi byte key that some text has been
// XORed against.
func BruteForceMultiByteXOR(text []byte) (KeyScore, error) {
	return KeyScore{}, nil
}
<commit_msg>s1c6: HammingDistance implementation<commit_after>package set1

import (
	"bytes"
	"fmt"
	"regexp"
)

// decToHex is used to lookup a single hex value in decimal.
var decToHex = [16]byte{
	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
	'a', 'b', 'c', 'd', 'e', 'f',
}

// Encodes a decimal byte slice to a hexidecimal byte slice
func HexEncode(text []byte) []byte {
	// output is always twice the size of input
	out := make([]byte, len(text)*2)

	// based on this article:
	// https://learn.sparkfun.com/tutorials/hexadecimal#converting-tofrom-decimal
	for i, char := range text {
		secondDigit := decToHex[char%16]
		firstDigit := decToHex[char/16]
		out[i*2] = firstDigit
		out[i*2+1] = secondDigit
	}

	return out
}

// hexToDec is used to lookup a single decimal value in hex.
func hexToDec(char byte) (byte, error) {
	var val byte
	// subtract ASCII decimal codes so that:
	//   "0" becomes 0
	//   "a" and "A" become 10
	switch {
	case char >= '0' && char <= '9':
		val = char - '0'
	case char >= 'A' && char <= 'F':
		val = char - 'A' + 10
	case char >= 'a' && char <= 'f':
		val = char - 'a' + 10
	default:
		return 0, fmt.Errorf("invalid hex character: %s", []byte{char})
	}

	return val, nil
}

// HexDecode decodes a hexidecimal byte slice to a decimal byte slice
func HexDecode(text []byte) ([]byte, error) {
	if len(text)%2 != 0 {
		return []byte{}, fmt.Errorf("input must be an even size")
	}

	// output is always half the size of input
	out := make([]byte, len(text)/2)

	// based on this article:
	// https://learn.sparkfun.com/tutorials/hexadecimal#converting-tofrom-decimal
	for i := 0; i < len(out); i++ {
		firstDigit, err := hexToDec(text[i*2])
		if err != nil {
			return []byte{}, err
		}

		secondDigit, err := hexToDec(text[i*2+1])
		if err != nil {
			return []byte{}, err
		}

		out[i] = firstDigit*16 + secondDigit
	}

	return out, nil
}

// decToBase64 is used to lookup base64 characters using zero-indexed 6bit
// values
const decToBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

// sixBitsMax is a 6bit value of all binary 1s:
//	- bin: 111111
//	- dec: 63
//	- hex: 0x3F
const sixBitsMax = 0x3F

// Base64Encode encodes a byte slice into a base64 byte slice
func Base64Encode(text []byte) []byte {
	// 4 bytes out for every 3 bytes in
	size := len(text) / 3 * 4
	// allow for padding if input is not divisible by 3
	if len(text)%3 != 0 {
		size += 4
	}
	out := make([]byte, size)

	for i := 0; i < len(text); i += 3 {
		// convert three 8bit characters into one 24bit value by:
		// - converting from byte (int8) to int32
		// - bit shifting to the left, to pad least significant bits, so that they don't overlap
		// - bit ORing to combine the values into one
		// - ignore the last two characters if we've reached the end of the input
		combined := int32(text[i]) << 16
		if i+1 < len(text) {
			combined |= int32(text[i+1]) << 8
		}
		if i+2 < len(text) {
			combined |= int32(text[i+2]) << 0
		}

		// split the 24bit value into four 6bit values by:
		// - bit shifting to the right, so the least significant 6bits are what we want
		// - bit ANDing against 6bits of binary 1s to extract the least significant 6bits
		// - looking up the appropriate base64 character for the value
		// - pad the last two characters if we've reached the end of the input
		outIndex := i / 3 * 4
		out[outIndex] = decToBase64[combined>>18&sixBitsMax]
		out[outIndex+1] = decToBase64[combined>>12&sixBitsMax]

		if i+1 < len(text) {
			out[outIndex+2] = decToBase64[combined>>6&sixBitsMax]
		} else {
			out[outIndex+2] = '='
		}

		if i+2 < len(text) {
			out[outIndex+3] = decToBase64[combined>>0&sixBitsMax]
		} else {
			out[outIndex+3] = '='
		}
	}

	return out
}

// base64ToDec is used to lookup a zero-indexed value for a base64 character
func base64ToDec(char byte) (int32, error) {
	switch {
	case char >= 'A' && char <= 'Z':
		return int32(char) - 'A', nil
	case char >= 'a' && char <= 'z':
		return int32(char) - 'a' + 26, nil
	case char >= '0' && char <= '9':
		return int32(char) - '0' + 52, nil
	case char == '+':
		return 62, nil
	case char == '/':
		return 63, nil
	case char == '=':
		return 0, nil
	}

	return 0, fmt.Errorf("invalid base64 character: %s", []byte{char})
}

// StripBytes removes all occurrences of a byte from a byte slice.
func StripBytes(s []byte, c byte) []byte {
	for {
		i := bytes.IndexByte(s, c)
		if i == -1 {
			break
		}

		s = append(s[:i], s[i+1:]...)
	}

	return s
}

// eightBitsMax is a 8bit value of all binary 1s:
//	- bin: 11111111
//	- dec: 255
//	- hex: 0xFF
const eightBitsMax = 0xFF

// Base64Decode decodes a base64 byte slice into an byte slice
func Base64Decode(text []byte) ([]byte, error) {
	text = StripBytes(text, '\n')

	// 4 bytes out for every 3 bytes in
	out := make([]byte, len(text)/4*3)

	for i := 0; i < len(text); i += 4 {
		// convert four 6bit characters into one 24bit value by:
		// - looking up the appropriate index for the base64 character
		// - bit shifting to the left, to pad least significant bits, so that they don't overlap
		// - bit ORing to combine the values into one
		var combined int32
		for offset, shift := range []uint{18, 12, 6, 0} {
			v, err := base64ToDec(text[i+offset])
			if err != nil {
				return []byte{}, err
			}

			combined |= v << shift
		}

		// split the 24bit value into three 8bit values by:
		// - bit shifting to the right, so the least significant 8bits are what we want
		// - bit ANDing against 8bits of binary 1s to extract the least significant 8bits
		// - converting to byte so that we get the appropriate ASCII code
		outIndex := i / 4 * 3
		out[outIndex] = byte(combined >> 16 & eightBitsMax)
		out[outIndex+1] = byte(combined >> 8 & eightBitsMax)
		out[outIndex+2] = byte(combined >> 0 & eightBitsMax)
	}

	// remove trailing padding
	out = bytes.TrimRight(out, "\x00")

	return out, nil
}

// HexToBase64 converts hexidecimal encoded text to base64.
func HexToBase64(text []byte) ([]byte, error) {
	raw, err := HexDecode(text)
	if err != nil {
		return []byte{}, err
	}

	return Base64Encode(raw), nil
}

// FixedKeyXOR encrypts some text against a key of the same size.
func FixedKeyXOR(text, key []byte) ([]byte, error) {
	if len(text) != len(key) {
		return []byte{}, fmt.Errorf("text and key must be same size: %d != %d", len(text), len(key))
	}

	return RepeatingKeyXOR(text, key)
}

// RepeatingKeyXOR encrypts some text against a repeating key of a smaller
// size.
func RepeatingKeyXOR(text, key []byte) ([]byte, error) {
	var keyIndex int
	xor := make([]byte, len(text))
	for i := 0; i < len(xor); i++ {
		// repeat the beginning of the key if we've reached the end
		if keyIndex >= len(key) {
			keyIndex = 0
		}

		xor[i] = text[i] ^ key[keyIndex]
		keyIndex++
	}

	return xor, nil
}

// ScoreEnglish returns a score indicating the likelihood that a string
// is comprised of English words by counting the most commonly occurring
// letters in the English language.
func ScoreEnglish(text []byte) int {
	re := regexp.MustCompile("(?i)[ETAOIN SHRDLU]")
	matches := re.FindAll(text, -1)

	return len(matches)
}

// KeyScore can be used to keep track of the most likely key.
type KeyScore struct {
	Score     int
	Key, Text []byte
}

// BruteForceSingleByteXOR finds the single byte key that some text has been
// XORed against.
func BruteForceSingleByteXOR(text []byte) (KeyScore, error) {
	var highestScore KeyScore

	// try all printable ASCII characters
	for key := byte(32); key <= byte(127); key++ {
		out, err := RepeatingKeyXOR(text, []byte{key})
		if err != nil {
			return highestScore, err
		}

		if score := ScoreEnglish(out); score > highestScore.Score {
			highestScore = KeyScore{
				Score: score,
				Key:   []byte{key},
				Text:  out,
			}
		}
	}

	return highestScore, nil
}

// HammingDistance returns the number of differences between two byte
// slices: https://en.wikipedia.org/wiki/Hamming_distance
func HammingDistance(one, two []byte) (int, error) {
	if len(one) != len(two) {
		return 0, fmt.Errorf("inputs must be same length: %d != %d", len(one), len(two))
	}

	var dist int
	for i := 0; i < len(one); i++ {
		// find bits that differ
		charXOR := one[i] ^ two[i]
		// select each bit from right to left
		for mask := 1; mask <= 128; mask *= 2 {
			if (charXOR & byte(mask)) > 0 {
				dist++
			}
		}
	}

	return dist, nil
}

// BruteForceMultiByteXOR finds the multi byte key that some text has been
// XORed against.
func BruteForceMultiByteXOR(text []byte) (KeyScore, error) {
	return KeyScore{}, nil
}
<|endoftext|>


<commit_before>package spatial

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

var endianness = binary.LittleEndian

type Point [2]float64

func (p *Point) X() float64 {
	return p[0]
}

func (p *Point) Y() float64 {
	return p[1]
}

type GeomType uint32

const (
	GeomTypePoint      GeomType = 1
	GeomTypeLineString          = 2
	GeomTypePolygon             = 3
	GeomTypeInvalid
)

type Geom struct {
	typ GeomType
	g   interface{}
	b   []byte
}

func NewGeom(g interface{}) (Geom, error) {
	switch g.(type) {
	case [2]float64:
		return Geom{typ: GeomTypePoint, g: g}, nil
	case [][2]float64:
		return Geom{typ: GeomTypeLineString, g: g}, nil
	case [][][2]float64:
		return Geom{typ: GeomTypePolygon, g: g}, nil
	default:
		return Geom{}, errors.New("unknown input geom type")
	}
}

func (g *Geom) UnmarshalJSON(buf []byte) error {
	wg := struct {
		Type        string
		Coordinates json.RawMessage
	}{}
	json.Unmarshal(buf, &wg)

	switch strings.ToLower(wg.Type) {
	case "point":
		g.typ = GeomTypePoint
	case "linestring":
		g.typ = GeomTypeLineString
	case "polygon":
		g.typ = GeomTypePolygon
	default:
		return fmt.Errorf("unsupported geometry type: %s", wg.Type)
	}
	g.b = wg.Coordinates
	return nil
}

func (g *Geom) Typ() GeomType {
	return g.typ
}

func (g *Geom) Point() (Point, error) {
	var p Point
	err := json.Unmarshal(g.b, &p)
	return p, err
}

func (g *Geom) LineString() ([]Point, error) {
	var ls []Point
	err := json.Unmarshal(g.b, &ls)
	return ls, err
}

func (g *Geom) Polygon() ([][]Point, error) {
	var poly [][]Point
	err := json.Unmarshal(g.b, &poly)
	return poly, err
}

type Feature struct {
	Type     string
	Props    map[string]interface{} `json:"properties"`
	Geometry Geom
}

func (f *Feature) MarshalWKB() ([]byte, error) {
	if endianness != binary.LittleEndian {
		return nil, errors.New("only little endian is supported")
	}
	var buf bytes.Buffer
	binary.Write(&buf, endianness, uint8(1))         // little endian
	binary.Write(&buf, endianness, f.Geometry.Typ()) // geometry type

	switch f.Geometry.Typ() {
	case GeomTypePoint:
		p, err := f.Geometry.Point()
		if err != nil {
			return nil, err
		}
		wkbWritePoint(&buf, p)
	case GeomTypeLineString:
		ls, err := f.Geometry.LineString()
		if err != nil {
			return nil, err
		}
		wkbWriteLineString(&buf, ls)
	}
	return buf.Bytes(), nil
}

func (f *Feature) Properties() map[string]interface{} {
	return f.Props
}

type FeatureCollection struct {
	Features []Feature
}
<commit_msg>lib/spatial: parse geojson directly instead of lazily<commit_after>package spatial

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

var endianness = binary.LittleEndian

type Point [2]float64

func (p *Point) X() float64 {
	return p[0]
}

func (p *Point) Y() float64 {
	return p[1]
}

type GeomType uint32

const (
	GeomTypePoint      GeomType = 1
	GeomTypeLineString          = 2
	GeomTypePolygon             = 3
	GeomTypeInvalid
)

type Geom struct {
	typ GeomType
	g   interface{}
}

func NewGeom(g interface{}) (Geom, error) {
	switch g.(type) {
	case [2]float64:
		return Geom{typ: GeomTypePoint, g: g}, nil
	case [][2]float64:
		return Geom{typ: GeomTypeLineString, g: g}, nil
	case [][][2]float64:
		return Geom{typ: GeomTypePolygon, g: g}, nil
	default:
		return Geom{}, errors.New("unknown input geom type")
	}
}

func (g *Geom) UnmarshalJSON(buf []byte) error {
	wg := struct {
		Type        string
		Coordinates json.RawMessage
	}{}
	err := json.Unmarshal(buf, &wg)
	if err != nil {
		return err
	}

	switch strings.ToLower(wg.Type) {
	case "point":
		g.typ = GeomTypePoint
		var p Point
		if err = json.Unmarshal(wg.Coordinates, &p); err != nil {
			return err
		}
		g.g = p
	case "linestring":
		g.typ = GeomTypeLineString
		var ls []Point
		if err = json.Unmarshal(wg.Coordinates, &ls); err != nil {
			return err
		}
		g.g = ls
	case "polygon":
		g.typ = GeomTypePolygon
		var poly [][]Point
		if err = json.Unmarshal(wg.Coordinates, &poly); err != nil {
			return err
		}
		g.g = poly
	default:
		return fmt.Errorf("unsupported geometry type: %s", wg.Type)
	}
	return nil
}

func (g *Geom) Typ() GeomType {
	return g.typ
}

func (g *Geom) Point() (Point, error) {
	geom, ok := g.g.(Point)
	if !ok {
		return Point{}, errors.New("geometry is not a Point")
	}
	return geom, nil
}

func (g *Geom) LineString() ([]Point, error) {
	geom, ok := g.g.([]Point)
	if !ok {
		return nil, errors.New("geometry is not a LineString")
	}
	return geom, nil
}

func (g *Geom) Polygon() ([][]Point, error) {
	geom, ok := g.g.([][]Point)
	if !ok {
		return nil, errors.New("geometry is not a Polygon")
	}
	return geom, nil
}

type Feature struct {
	Type     string
	Props    map[string]interface{} `json:"properties"`
	Geometry Geom
}

func (f *Feature) MarshalWKB() ([]byte, error) {
	if endianness != binary.LittleEndian {
		return nil, errors.New("only little endian is supported")
	}
	var buf bytes.Buffer
	binary.Write(&buf, endianness, uint8(1))         // little endian
	binary.Write(&buf, endianness, f.Geometry.Typ()) // geometry type

	switch f.Geometry.Typ() {
	case GeomTypePoint:
		p, err := f.Geometry.Point()
		if err != nil {
			return nil, err
		}
		wkbWritePoint(&buf, p)
	case GeomTypeLineString:
		ls, err := f.Geometry.LineString()
		if err != nil {
			return nil, err
		}
		wkbWriteLineString(&buf, ls)
	}
	return buf.Bytes(), nil
}

func (f *Feature) Properties() map[string]interface{} {
	return f.Props
}

type FeatureCollection struct {
	Features []Feature
}
<|endoftext|>


<commit_before>package moh

import (
	"io/ioutil"
	"net/http"
)

type Replier struct {
	CloseableServer
}

// NewReplier starts a new HTTP server on addr and returns a pointer to the Replier.
// All request will be replied by the handler function.
func NewReplier(addr string, handler MessageHandler) (*Replier, error) {
	s, err := NewClosableServer(addr)
	if err != nil {
		return nil, err
	}

	s.Mux.HandleFunc("/", makeHttpHandler(handler))
	go s.Serve()
	return &Replier{CloseableServer: *s}, nil
}

func makeHttpHandler(handler MessageHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			panic(err)
		}

		reply := handler(body)

		_, err = w.Write(reply)
		if err != nil {
			panic(err)
		}
	}
}
<commit_msg>return 500 instead of panic<commit_after>package moh

import (
	"io/ioutil"
	"net/http"
)

type Replier struct {
	CloseableServer
}

// NewReplier starts a new HTTP server on addr and returns a pointer to the Replier.
// All request will be replied by the handler function.
func NewReplier(addr string, handler MessageHandler) (*Replier, error) {
	s, err := NewClosableServer(addr)
	if err != nil {
		return nil, err
	}

	s.Mux.HandleFunc("/", makeHttpHandler(handler))
	go s.Serve()
	return &Replier{CloseableServer: *s}, nil
}

func makeHttpHandler(handler MessageHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer r.Body.Close()
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Cannot read request body", 500)
			return
		}

		reply := handler(body)

		_, err = w.Write(reply)
		if err != nil {
			http.Error(w, "Cannot write reply", 500)
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/build"
)

var (
	// globalConfig is used by the cobra package to fill out the configuration
	// variables.
	globalConfig Config
)

// exit codes
// inspired by sysexits.h
const (
	exitCodeGeneral = 1  // Not in sysexits.h, but is standard practice.
	exitCodeUsage   = 64 // EX_USAGE in sysexits.h
)

// The Config struct contains all configurable variables for siad. It is
// compatible with gcfg.
type Config struct {
	// The APIPassword is input by the user after the daemon starts up, if the
	// --authenticate-api flag is set.
	APIPassword string

	// The Siad variables are referenced directly by cobra, and are set
	// according to the flags.
	Siad struct {
		APIaddr      string
		RPCaddr      string
		HostAddr     string
		AllowAPIBind bool

		Modules           string
		NoBootstrap       bool
		RequiredUserAgent string
		AuthenticateAPI   bool

		Profile    bool
		ProfileDir string
		SiaDir     string
	}
}

// die prints its arguments to stderr, then exits the program with the default
// error code.
func die(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(exitCodeGeneral)
}

// versionCmd is a cobra command that prints the version of siad.
func versionCmd(*cobra.Command, []string) {
	switch build.Release {
	case "dev":
		fmt.Println("Sia Daemon v" + build.Version + "-dev")
	case "standard":
		fmt.Println("Sia Daemon v" + build.Version)
	case "testing":
		fmt.Println("Sia Daemon v" + build.Version + "-testing")
	default:
		fmt.Println("Sia Daemon v" + build.Version + "-???")
	}
}

// modulesCmd is a cobra command that prints help info about modules.
func modulesCmd(*cobra.Command, []string) {
	fmt.Println(`Use the -M or --modules flag to only run specific modules. Modules are
independent components of Sia. This flag should only be used by developers or
people who want to reduce overhead from unused modules. Modules are specified by
their first letter. If the -M or --modules flag is not specified the default
modules are run. The default modules are:
	gateway, consensus set, host, miner, renter, transaction pool, wallet
This is equivalent to:
	siad -M cghmrtw
Below is a list of all the modules available.

Gateway (g):
	The gateway maintains a peer to peer connection to the network and
	enables other modules to perform RPC calls on peers.
	The gateway is required by all other modules.
	Example:
		siad -M g
Consensus Set (c):
	The consensus set manages everything related to consensus and keeps the
	blockchain in sync with the rest of the network.
	The consensus set requires the gateway.
	Example:
		siad -M gc
Transaction Pool (t):
	The transaction pool manages unconfirmed transactions.
	The transaction pool requires the consensus set.
	Example:
		siad -M gct
Wallet (w):
	The wallet stores and manages siacoins and siafunds.
	The wallet requires the consensus set and transaction pool.
	Example:
		siad -M gctw
Renter (r):
	The renter manages the user's files on the network.
	The renter requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwr
Host (h):
	The host provides storage from local disks to the network. The host
	negotiates file contracts with remote renters to earn money for storing
	other users' files.
	The host requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwh
Miner (m):
	The miner provides a basic CPU mining implementation as well as an API
	for external miners to use.
	The miner requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwm
Explorer (e):
	The explorer provides statistics about the blockchain and can be
	queried for information about specific transactions or other objects on
	the blockchain.
	The explorer requires the consenus set.
	Example:
		siad -M gce`)
}

// main establishes a set of commands and flags using the cobra package.
func main() {
	if build.DEBUG {
		fmt.Println("Running with debugging enabled")
	}
	root := &cobra.Command{
		Use:   os.Args[0],
		Short: "Sia Daemon v" + build.Version,
		Long:  "Sia Daemon v" + build.Version,
		Run:   startDaemonCmd,
	}

	root.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Print version information",
		Long:  "Print version information about the Sia Daemon",
		Run:   versionCmd,
	})

	root.AddCommand(&cobra.Command{
		Use:   "modules",
		Short: "List available modules for use with -M, --modules flag",
		Long:  "List available modules for use with -M, --modules flag and their uses",
		Run:   modulesCmd,
	})

	// Set default values, which have the lowest priority.
	root.Flags().StringVarP(&globalConfig.Siad.RequiredUserAgent, "agent", "", "Sia-Agent", "required substring for the user agent")
	root.Flags().StringVarP(&globalConfig.Siad.HostAddr, "host-addr", "", ":9982", "which port the host listens on")
	root.Flags().StringVarP(&globalConfig.Siad.ProfileDir, "profile-directory", "", "profiles", "location of the profiling directory")
	root.Flags().StringVarP(&globalConfig.Siad.APIaddr, "api-addr", "", "localhost:9980", "which host:port the API server listens on")
	root.Flags().StringVarP(&globalConfig.Siad.SiaDir, "sia-directory", "d", "", "location of the sia directory")
	root.Flags().BoolVarP(&globalConfig.Siad.NoBootstrap, "no-bootstrap", "", false, "disable bootstrapping on this run")
	root.Flags().BoolVarP(&globalConfig.Siad.Profile, "profile", "", false, "enable profiling")
	root.Flags().StringVarP(&globalConfig.Siad.RPCaddr, "rpc-addr", "", ":9981", "which port the gateway listens on")
	root.Flags().StringVarP(&globalConfig.Siad.Modules, "modules", "M", "cghmrtw", "enabled modules, see 'siad modules' for more info")
	root.Flags().BoolVarP(&globalConfig.Siad.AuthenticateAPI, "authenticate-api", "", false, "enable API password protection")
	root.Flags().BoolVarP(&globalConfig.Siad.AllowAPIBind, "disable-api-security", "", false, "allow siad to listen on a non-localhost address (DANGEROUS)")

	// Parse cmdline flags, overwriting both the default values and the config
	// file values.
	if err := root.Execute(); err != nil {
		// Since no commands return errors (all commands set Command.Run instead of
		// Command.RunE), Command.Execute() should only return an error on an
		// invalid command or flag. Therefore Command.Usage() was called (assuming
		// Command.SilenceUsage is false) and we should exit with exitCodeUsage.
		os.Exit(exitCodeUsage)
	}
}
<commit_msg>remove miner from set of default modules<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/build"
)

var (
	// globalConfig is used by the cobra package to fill out the configuration
	// variables.
	globalConfig Config
)

// exit codes
// inspired by sysexits.h
const (
	exitCodeGeneral = 1  // Not in sysexits.h, but is standard practice.
	exitCodeUsage   = 64 // EX_USAGE in sysexits.h
)

// The Config struct contains all configurable variables for siad. It is
// compatible with gcfg.
type Config struct {
	// The APIPassword is input by the user after the daemon starts up, if the
	// --authenticate-api flag is set.
	APIPassword string

	// The Siad variables are referenced directly by cobra, and are set
	// according to the flags.
	Siad struct {
		APIaddr      string
		RPCaddr      string
		HostAddr     string
		AllowAPIBind bool

		Modules           string
		NoBootstrap       bool
		RequiredUserAgent string
		AuthenticateAPI   bool

		Profile    bool
		ProfileDir string
		SiaDir     string
	}
}

// die prints its arguments to stderr, then exits the program with the default
// error code.
func die(args ...interface{}) {
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(exitCodeGeneral)
}

// versionCmd is a cobra command that prints the version of siad.
func versionCmd(*cobra.Command, []string) {
	switch build.Release {
	case "dev":
		fmt.Println("Sia Daemon v" + build.Version + "-dev")
	case "standard":
		fmt.Println("Sia Daemon v" + build.Version)
	case "testing":
		fmt.Println("Sia Daemon v" + build.Version + "-testing")
	default:
		fmt.Println("Sia Daemon v" + build.Version + "-???")
	}
}

// modulesCmd is a cobra command that prints help info about modules.
func modulesCmd(*cobra.Command, []string) {
	fmt.Println(`Use the -M or --modules flag to only run specific modules. Modules are
independent components of Sia. This flag should only be used by developers or
people who want to reduce overhead from unused modules. Modules are specified by
their first letter. If the -M or --modules flag is not specified the default
modules are run. The default modules are:
	gateway, consensus set, host, miner, renter, transaction pool, wallet
This is equivalent to:
	siad -M cghmrtw
Below is a list of all the modules available.

Gateway (g):
	The gateway maintains a peer to peer connection to the network and
	enables other modules to perform RPC calls on peers.
	The gateway is required by all other modules.
	Example:
		siad -M g
Consensus Set (c):
	The consensus set manages everything related to consensus and keeps the
	blockchain in sync with the rest of the network.
	The consensus set requires the gateway.
	Example:
		siad -M gc
Transaction Pool (t):
	The transaction pool manages unconfirmed transactions.
	The transaction pool requires the consensus set.
	Example:
		siad -M gct
Wallet (w):
	The wallet stores and manages siacoins and siafunds.
	The wallet requires the consensus set and transaction pool.
	Example:
		siad -M gctw
Renter (r):
	The renter manages the user's files on the network.
	The renter requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwr
Host (h):
	The host provides storage from local disks to the network. The host
	negotiates file contracts with remote renters to earn money for storing
	other users' files.
	The host requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwh
Miner (m):
	The miner provides a basic CPU mining implementation as well as an API
	for external miners to use.
	The miner requires the consensus set, transaction pool, and wallet.
	Example:
		siad -M gctwm
Explorer (e):
	The explorer provides statistics about the blockchain and can be
	queried for information about specific transactions or other objects on
	the blockchain.
	The explorer requires the consenus set.
	Example:
		siad -M gce`)
}

// main establishes a set of commands and flags using the cobra package.
func main() {
	if build.DEBUG {
		fmt.Println("Running with debugging enabled")
	}
	root := &cobra.Command{
		Use:   os.Args[0],
		Short: "Sia Daemon v" + build.Version,
		Long:  "Sia Daemon v" + build.Version,
		Run:   startDaemonCmd,
	}

	root.AddCommand(&cobra.Command{
		Use:   "version",
		Short: "Print version information",
		Long:  "Print version information about the Sia Daemon",
		Run:   versionCmd,
	})

	root.AddCommand(&cobra.Command{
		Use:   "modules",
		Short: "List available modules for use with -M, --modules flag",
		Long:  "List available modules for use with -M, --modules flag and their uses",
		Run:   modulesCmd,
	})

	// Set default values, which have the lowest priority.
	root.Flags().StringVarP(&globalConfig.Siad.RequiredUserAgent, "agent", "", "Sia-Agent", "required substring for the user agent")
	root.Flags().StringVarP(&globalConfig.Siad.HostAddr, "host-addr", "", ":9982", "which port the host listens on")
	root.Flags().StringVarP(&globalConfig.Siad.ProfileDir, "profile-directory", "", "profiles", "location of the profiling directory")
	root.Flags().StringVarP(&globalConfig.Siad.APIaddr, "api-addr", "", "localhost:9980", "which host:port the API server listens on")
	root.Flags().StringVarP(&globalConfig.Siad.SiaDir, "sia-directory", "d", "", "location of the sia directory")
	root.Flags().BoolVarP(&globalConfig.Siad.NoBootstrap, "no-bootstrap", "", false, "disable bootstrapping on this run")
	root.Flags().BoolVarP(&globalConfig.Siad.Profile, "profile", "", false, "enable profiling")
	root.Flags().StringVarP(&globalConfig.Siad.RPCaddr, "rpc-addr", "", ":9981", "which port the gateway listens on")
	root.Flags().StringVarP(&globalConfig.Siad.Modules, "modules", "M", "cghrtw", "enabled modules, see 'siad modules' for more info")
	root.Flags().BoolVarP(&globalConfig.Siad.AuthenticateAPI, "authenticate-api", "", false, "enable API password protection")
	root.Flags().BoolVarP(&globalConfig.Siad.AllowAPIBind, "disable-api-security", "", false, "allow siad to listen on a non-localhost address (DANGEROUS)")

	// Parse cmdline flags, overwriting both the default values and the config
	// file values.
	if err := root.Execute(); err != nil {
		// Since no commands return errors (all commands set Command.Run instead of
		// Command.RunE), Command.Execute() should only return an error on an
		// invalid command or flag. Therefore Command.Usage() was called (assuming
		// Command.SilenceUsage is false) and we should exit with exitCodeUsage.
		os.Exit(exitCodeUsage)
	}
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/nlopes/slack"
	"github.com/uber-go/zap"
)

var isImage = regexp.MustCompile("\\.(jpe?g|gif|png)$")
var CdnPath = ""
var CdnPrefix = ""

func fileBytes(f *slack.File) ([]byte, error) {
	req, _ := http.NewRequest("GET", f.URLPrivate, nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	rsp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		return nil, fmt.Errorf(rsp.Status)
	}
	return ioutil.ReadAll(rsp.Body)
}

func handleChannelUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	if buf, err := fileBytes(m.Msg.File); err != nil {
		logger.Info("error downloading file", zap.Error(err))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			logger.Error("error making cdn path", zap.String("path", path))
			return false
		}
		part := &url.URL{Path: m.Msg.File.Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, m.Msg.File.Name)
		if fp, err := os.Create(path); err != nil {
			logger.Error("error creating cdn file", zap.String("path", path))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				logger.Error("error writing to cdn file", zap.String("path", path))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Msg.File.ID)
			if isImage.MatchString(strings.ToLower(m.Msg.File.Name)) {
				rtm.PostMessage(
					m.Channel,
					"",
					slack.PostMessageParameters{
						Text:        "",
						AsUser:      true,
						UnfurlLinks: true,
						UnfurlMedia: true,
						IconEmoji:   ":paperclip:",
						Attachments: []slack.Attachment{
							slack.Attachment{
								Title:     fmt.Sprintf("%s uploaded %s", m.Msg.Username, m.Msg.File.Title),
								TitleLink: fileURL,
								ImageURL:  fileURL,
							},
						},
					})
			} else {
				rtm.SendMessage(&slack.OutgoingMessage{
					ID:      int(time.Now().UnixNano()),
					Channel: m.Channel,
					Text:    fmt.Sprintf("%s uploaded the file *%s*\n%s", m.Msg.Username, m.Msg.File.Title, fileURL),
					Type:    "message",
				})
			}
			logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
			return true
		}

	}
	return false
}

func handleDMUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	if buf, err := fileBytes(m.Msg.File); err != nil {
		logger.Info("error downloading file", zap.Error(err))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			logger.Error("error making cdn path", zap.String("path", path))
			return false
		}
		part := &url.URL{Path: m.Msg.File.Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, m.Msg.File.Name)
		if fp, err := os.Create(path); err != nil {
			logger.Error("error creating cdn file", zap.String("path", path))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				logger.Error("error writing to cdn file", zap.String("path", path))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Msg.File.ID)
			rtm.SendMessage(&slack.OutgoingMessage{
				ID:      int(time.Now().UnixNano()),
				Channel: m.Channel,
				Text:    fmt.Sprintf("Thanks for sending me the file instead of uploading it to a channel or group. You can paste the following link anywhere you want to show the file to others! ```%s```", fileURL),
				Type:    "message",
			})
			logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
		}

	}
	return true
}
<commit_msg>better logging and retrying for CDN messages<commit_after>package bot

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/nlopes/slack"
	"github.com/uber-go/zap"
)

var isImage = regexp.MustCompile("\\.(jpe?g|gif|png)$")
var CdnPath = ""
var CdnPrefix = ""

func fileBytes(f *slack.File) ([]byte, error) {
	req, _ := http.NewRequest("GET", f.URLPrivate, nil)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	rsp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != 200 {
		return nil, fmt.Errorf(rsp.Status)
	}
	return ioutil.ReadAll(rsp.Body)
}

func handleChannelUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	logger.Info("File upload detected", zap.String("username", m.Username), zap.String("filename", m.File.Name))
	if buf, err := fileBytes(m.Msg.File); err != nil {
		logger.Error(
			"error downloading file",
			zap.Error(err),
			zap.String("username", m.Username),
			zap.String("filename", m.File.Name))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			logger.Error(
				"error making cdn path",
				zap.String("path", path),
				zap.String("username", m.Username),
				zap.String("filename", m.File.Name))
			return false
		}
		part := &url.URL{Path: m.Msg.File.Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, m.Msg.File.Name)
		if fp, err := os.Create(path); err != nil {
			logger.Error(
				"error creating cdn file",
				zap.String("path", path),
				zap.String("username", m.Username),
				zap.String("filename", m.File.Name))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				logger.Error(
					"error writing to cdn file",
					zap.String("path", path),
					zap.String("username", m.Username),
					zap.String("filename", m.File.Name))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Msg.File.ID)
			if isImage.MatchString(strings.ToLower(m.Msg.File.Name)) {
				for i := 0; i < 5; i++ {
					_, _, err := rtm.PostMessage(
						m.Channel,
						"",
						slack.PostMessageParameters{
							Text:        "",
							AsUser:      true,
							UnfurlLinks: true,
							UnfurlMedia: true,
							IconEmoji:   ":paperclip:",
							Attachments: []slack.Attachment{
								slack.Attachment{
									Title:     fmt.Sprintf("%s uploaded %s", m.Msg.Username, m.Msg.File.Title),
									TitleLink: fileURL,
									ImageURL:  fileURL,
								},
							},
						})
					if err != nil {
						logger.Error(
							"Failed postting cdn link back to slack",
							zap.String("username", m.Username),
							zap.String("filename", m.File.Name),
							zap.String("url", fileURL))
					} else {
						break
					}
					time.Sleep(time.Second * time.Duration(i))
				}
			} else {
				rtm.SendMessage(&slack.OutgoingMessage{
					ID:      int(time.Now().UnixNano()),
					Channel: m.Channel,
					Text:    fmt.Sprintf("%s uploaded the file *%s*\n%s", m.Msg.Username, m.Msg.File.Title, fileURL),
					Type:    "message",
				})
			}
			logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
			return true
		}

	}
	return false
}

func handleDMUpload(m *slack.MessageEvent) bool {
	if CdnPath == "" || CdnPrefix == "" {
		return false
	}
	if !m.Msg.Upload {
		return false
	}
	if buf, err := fileBytes(m.Msg.File); err != nil {
		logger.Info("error downloading file", zap.Error(err))
	} else {
		path := fmt.Sprintf("%s/%s", CdnPath, time.Now().Format("2006/01/02/15"))
		if err := os.MkdirAll(path, 0755); err != nil {
			logger.Error("error making cdn path", zap.String("path", path))
			return false
		}
		part := &url.URL{Path: m.Msg.File.Name}
		urlPath := fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, part.String())
		path = fmt.Sprintf("%s/%s-%s", path, m.Msg.File.ID, m.Msg.File.Name)
		if fp, err := os.Create(path); err != nil {
			logger.Error("error creating cdn file", zap.String("path", path))
			return false
		} else {
			if _, err := fp.Write(buf); err != nil {
				fp.Close()
				logger.Error("error writing to cdn file", zap.String("path", path))
				return false
			}
			fp.Close()
			fileURL := CdnPrefix + urlPath[len(CdnPath):]
			rtm.DeleteFile(m.Msg.File.ID)
			rtm.SendMessage(&slack.OutgoingMessage{
				ID:      int(time.Now().UnixNano()),
				Channel: m.Channel,
				Text:    fmt.Sprintf("Thanks for sending me the file instead of uploading it to a channel or group. You can paste the following link anywhere you want to show the file to others! ```%s```", fileURL),
				Type:    "message",
			})
			logger.Info("saved CDN file", zap.String("url", fileURL), zap.Int("size", len(buf)))
		}

	}
	return true
}
<|endoftext|>