<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"go.uber.org/thriftrw/compile"
	"go.uber.org/thriftrw/gen"
	"go.uber.org/thriftrw/internal/plugin"
	"go.uber.org/thriftrw/internal/plugin/builtin/pluginapigen"
	"go.uber.org/thriftrw/version"

	"github.com/jessevdk/go-flags"
	"go.uber.org/multierr"
)

type options struct {
	DisplayVersion bool       `long:"version" short:"v" description:"Show the ThriftRW version number"`
	GOpts          genOptions `group:"Generator Options"`
}

type genOptions struct {
	OutputDirectory string `long:"out" short:"o" value-name:"DIR" description:"Directory to which the generated files will be written."`
	PackagePrefix   string `long:"pkg-prefix" value-name:"PREFIX" description:"Prefix for import paths of generated module. By default, this is based on the output directory's location relative to $GOPATH."`
	ThriftRoot      string `long:"thrift-root" value-name:"DIR" description:"Directory whose descendants contain all Thrift files. The structure of the generated Go packages mirrors the paths to the Thrift files relative to this directory. By default, this is the deepest common ancestor directory of the Thrift files."`

	NoRecurse bool         `long:"no-recurse" description:"Don't generate code for included Thrift files."`
	Plugins   plugin.Flags `long:"plugin" short:"p" value-name:"PLUGIN" description:"Code generation plugin for ThriftRW. This option may be provided multiple times to apply multiple plugins."`

	GeneratePluginAPI bool `long:"generate-plugin-api" hidden:"true" description:"Generates code for the plugin API"`
	NoVersionCheck    bool `long:"no-version-check" hidden:"true" description:"Does not add library version checks to generated code."`
	NoTypes           bool `long:"no-types" description:"Do not generate code for types, implies --no-service-helpers."`
	NoConstants       bool `long:"no-constants" description:"Do not generate code for const declarations."`
	NoServiceHelpers  bool `long:"no-service-helpers" description:"Do not generate service helpers."`
	NoEmbedIDL        bool `long:"no-embed-idl" description:"Do not embed IDLs into the generated code."`

	// TODO(abg): Detailed help with examples of --thrift-root, --pkg-prefix,
	// and --plugin

}

func main() {
	if err := do(); err != nil {
		log.Fatalf("%+v", err)
		os.Exit(1)
	}
	os.Exit(0)
}

func do() (err error) {
	log.SetFlags(0) // don't include timestamps, etc. in the output

	var opts options

	parser := flags.NewParser(&opts, flags.Default)
	parser.Usage = "[OPTIONS] FILE"

	args, err := parser.Parse()
	if err != nil {
		return nil // message already printed by go-flags
	}

	if opts.DisplayVersion {
		fmt.Printf("thriftrw v%s\n", version.Version)
		return nil
	}

	if len(args) != 1 {
		var buffer bytes.Buffer
		parser.WriteHelp(&buffer)
		return errors.New(buffer.String())
	}

	inputFile := args[0]
	if _, err := os.Stat(inputFile); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("File %q does not exist: %v", inputFile, err)
		}
		return fmt.Errorf("Could not stat file %q: %v", inputFile, err)
	}
	gopts := opts.GOpts

	if len(gopts.OutputDirectory) == 0 {
		gopts.OutputDirectory = "."
	}
	gopts.OutputDirectory, err = filepath.Abs(gopts.OutputDirectory)
	if err != nil {
		return fmt.Errorf("Unable to resolve absolute path for %q: %v", gopts.OutputDirectory, err)
	}

	if gopts.PackagePrefix == "" {
		gopts.PackagePrefix, err = determinePackagePrefix(gopts.OutputDirectory)
		if err != nil {
			return fmt.Errorf(
				"Could not determine a package prefix automatically: %v\n"+
					"A package prefix is required to use correct import paths in the generated code.\n"+
					"Use the --pkg-prefix option to provide a package prefix manually.", err)
		}
	}

	module, err := compile.Compile(inputFile)
	if err != nil {
		// TODO(abg): For nested compile errors, split causal chain across
		// multiple lines.
		return fmt.Errorf("Failed to compile %q: %+v", inputFile, err)
	}

	if gopts.ThriftRoot == "" {
		gopts.ThriftRoot, err = findCommonAncestor(module)
		if err != nil {
			return fmt.Errorf(
				"Could not find a common parent directory for %q and the Thrift files "+
					"imported by it.\nThis directory is required to generate a consistent "+
					"hierarchy for generated packages.\nUse the --thrift-root option to "+
					"provide this path.\n\t%v", inputFile, err)
		}
	} else {
		gopts.ThriftRoot, err = filepath.Abs(gopts.ThriftRoot)
		if err != nil {
			return fmt.Errorf("Unable to resolve absolute path for %q: %v", gopts.ThriftRoot, err)
		}
		if err := verifyAncestry(module, gopts.ThriftRoot); err != nil {
			return fmt.Errorf(
				"An included Thrift file is not contained in the %q directory tree: %v",
				gopts.ThriftRoot, err)
		}
	}

	pluginHandle, err := gopts.Plugins.Handle()
	if err != nil {
		return fmt.Errorf("Failed to initialize plugins: %+v", err)
	}

	if gopts.GeneratePluginAPI {
		pluginHandle = append(pluginHandle, pluginapigen.Handle)
	}

	defer func() {
		err = multierr.Append(err, pluginHandle.Close())
	}()

	generatorOptions := gen.Options{
		OutputDir:        gopts.OutputDirectory,
		PackagePrefix:    gopts.PackagePrefix,
		ThriftRoot:       gopts.ThriftRoot,
		NoRecurse:        gopts.NoRecurse,
		NoVersionCheck:   gopts.NoVersionCheck,
		Plugin:           pluginHandle,
		NoTypes:          gopts.NoTypes,
		NoConstants:      gopts.NoConstants,
		NoServiceHelpers: gopts.NoServiceHelpers || gopts.NoTypes,
		NoEmbedIDL:       gopts.NoEmbedIDL,
	}
	if err := gen.Generate(module, &generatorOptions); err != nil {
		return fmt.Errorf("Failed to generate code: %+v", err)
	}
	return nil
}

// verifyAncestry verifies that the Thrift file for the given module and the
// Thrift files for all imported modules are contained within the directory
// tree rooted at the given path.
func verifyAncestry(m *compile.Module, root string) error {
	return m.Walk(func(m *compile.Module) error {
		path, err := filepath.Rel(root, m.ThriftPath)
		if err != nil {
			return fmt.Errorf(
				"could not resolve path for %q: %v", m.ThriftPath, err)
		}

		if strings.HasPrefix(path, "..") {
			return fmt.Errorf(
				"%q is not contained in the %q directory tree",
				m.ThriftPath, root)
		}

		return nil
	})
}

// findCommonAncestor finds the deepest common ancestor for the given module
// and all modules imported by it.
func findCommonAncestor(m *compile.Module) (string, error) {
	var result []string
	var lastString string

	err := m.Walk(func(m *compile.Module) error {
		thriftPath := m.ThriftPath
		if !filepath.IsAbs(thriftPath) {
			return fmt.Errorf(
				"ThriftPath must be absolute: %q is not absolute", thriftPath)
		}

		thriftDir := filepath.Dir(thriftPath)

		// Split("/foo/bar", "/") = ["", "foo", "bar"]
		parts := strings.Split(thriftDir, string(filepath.Separator))
		if result == nil {
			result = parts
			lastString = thriftPath
			return nil
		}

		result = commonPrefix(result, parts)
		if len(result) == 1 && result[0] == "" {
			return fmt.Errorf(
				"%q does not share an ancestor with %q",
				thriftPath, lastString)
		}

		lastString = thriftPath
		return nil
	})
	if err != nil {
		return "", err
	}

	return strings.Join(result, string(filepath.Separator)), nil
}

// commonPrefix finds the shortest common prefix for the two lists.
//
// An empty slice may be returned if the two lists don't have a common prefix.
func commonPrefix(l, r []string) []string {
	var i int
	for i = 0; i < len(l) && i < len(r); i++ {
		if l[i] != r[i] {
			break
		}
	}
	return l[:i]
}

// determinePackagePrefix determines the package prefix for Go packages
// generated in this file.
//
// dir must be an absolute path.
func determinePackagePrefix(dir string) (string, error) {
	gopathList := os.Getenv("GOPATH")
	if gopathList == "" {
		return "", errors.New("$GOPATH is not set")
	}

	for _, gopath := range filepath.SplitList(gopathList) {
		packagePath, err := filepath.Rel(filepath.Join(gopath, "src"), dir)
		if err != nil {
			return "", err
		}

		// The match is valid only if it's within the directory tree.
		if !strings.HasPrefix(packagePath, "..") {
			return packagePath, nil
		}
	}

	return "", fmt.Errorf("directory %q is not inside $GOPATH/src", dir)
}
<commit_msg>main.go: exit non-zero if flag parsing fails (#316)<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"go.uber.org/thriftrw/compile"
	"go.uber.org/thriftrw/gen"
	"go.uber.org/thriftrw/internal/plugin"
	"go.uber.org/thriftrw/internal/plugin/builtin/pluginapigen"
	"go.uber.org/thriftrw/version"

	"github.com/jessevdk/go-flags"
	"go.uber.org/multierr"
)

type options struct {
	DisplayVersion bool       `long:"version" short:"v" description:"Show the ThriftRW version number"`
	GOpts          genOptions `group:"Generator Options"`
}

type genOptions struct {
	OutputDirectory string `long:"out" short:"o" value-name:"DIR" description:"Directory to which the generated files will be written."`
	PackagePrefix   string `long:"pkg-prefix" value-name:"PREFIX" description:"Prefix for import paths of generated module. By default, this is based on the output directory's location relative to $GOPATH."`
	ThriftRoot      string `long:"thrift-root" value-name:"DIR" description:"Directory whose descendants contain all Thrift files. The structure of the generated Go packages mirrors the paths to the Thrift files relative to this directory. By default, this is the deepest common ancestor directory of the Thrift files."`

	NoRecurse bool         `long:"no-recurse" description:"Don't generate code for included Thrift files."`
	Plugins   plugin.Flags `long:"plugin" short:"p" value-name:"PLUGIN" description:"Code generation plugin for ThriftRW. This option may be provided multiple times to apply multiple plugins."`

	GeneratePluginAPI bool `long:"generate-plugin-api" hidden:"true" description:"Generates code for the plugin API"`
	NoVersionCheck    bool `long:"no-version-check" hidden:"true" description:"Does not add library version checks to generated code."`
	NoTypes           bool `long:"no-types" description:"Do not generate code for types, implies --no-service-helpers."`
	NoConstants       bool `long:"no-constants" description:"Do not generate code for const declarations."`
	NoServiceHelpers  bool `long:"no-service-helpers" description:"Do not generate service helpers."`
	NoEmbedIDL        bool `long:"no-embed-idl" description:"Do not embed IDLs into the generated code."`

	// TODO(abg): Detailed help with examples of --thrift-root, --pkg-prefix,
	// and --plugin

}

func main() {
	if err := do(); err != nil {
		log.Fatalf("%+v", err)
		os.Exit(1)
	}
	os.Exit(0)
}

func do() (err error) {
	log.SetFlags(0) // don't include timestamps, etc. in the output

	var opts options

	parser := flags.NewParser(&opts, flags.Default & ^flags.PrintErrors)
	parser.Usage = "[OPTIONS] FILE"

	args, err := parser.Parse()
	if ferr, ok := err.(*flags.Error); ok && ferr.Type == flags.ErrHelp {
		parser.WriteHelp(os.Stdout)
		return nil
	} else if err != nil {
		return err
	}

	if opts.DisplayVersion {
		fmt.Printf("thriftrw v%s\n", version.Version)
		return nil
	}

	if len(args) != 1 {
		var buffer bytes.Buffer
		parser.WriteHelp(&buffer)
		return errors.New(buffer.String())
	}

	inputFile := args[0]
	if _, err := os.Stat(inputFile); err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("File %q does not exist: %v", inputFile, err)
		}
		return fmt.Errorf("Could not stat file %q: %v", inputFile, err)
	}
	gopts := opts.GOpts

	if len(gopts.OutputDirectory) == 0 {
		gopts.OutputDirectory = "."
	}
	gopts.OutputDirectory, err = filepath.Abs(gopts.OutputDirectory)
	if err != nil {
		return fmt.Errorf("Unable to resolve absolute path for %q: %v", gopts.OutputDirectory, err)
	}

	if gopts.PackagePrefix == "" {
		gopts.PackagePrefix, err = determinePackagePrefix(gopts.OutputDirectory)
		if err != nil {
			return fmt.Errorf(
				"Could not determine a package prefix automatically: %v\n"+
					"A package prefix is required to use correct import paths in the generated code.\n"+
					"Use the --pkg-prefix option to provide a package prefix manually.", err)
		}
	}

	module, err := compile.Compile(inputFile)
	if err != nil {
		// TODO(abg): For nested compile errors, split causal chain across
		// multiple lines.
		return fmt.Errorf("Failed to compile %q: %+v", inputFile, err)
	}

	if gopts.ThriftRoot == "" {
		gopts.ThriftRoot, err = findCommonAncestor(module)
		if err != nil {
			return fmt.Errorf(
				"Could not find a common parent directory for %q and the Thrift files "+
					"imported by it.\nThis directory is required to generate a consistent "+
					"hierarchy for generated packages.\nUse the --thrift-root option to "+
					"provide this path.\n\t%v", inputFile, err)
		}
	} else {
		gopts.ThriftRoot, err = filepath.Abs(gopts.ThriftRoot)
		if err != nil {
			return fmt.Errorf("Unable to resolve absolute path for %q: %v", gopts.ThriftRoot, err)
		}
		if err := verifyAncestry(module, gopts.ThriftRoot); err != nil {
			return fmt.Errorf(
				"An included Thrift file is not contained in the %q directory tree: %v",
				gopts.ThriftRoot, err)
		}
	}

	pluginHandle, err := gopts.Plugins.Handle()
	if err != nil {
		return fmt.Errorf("Failed to initialize plugins: %+v", err)
	}

	if gopts.GeneratePluginAPI {
		pluginHandle = append(pluginHandle, pluginapigen.Handle)
	}

	defer func() {
		err = multierr.Append(err, pluginHandle.Close())
	}()

	generatorOptions := gen.Options{
		OutputDir:        gopts.OutputDirectory,
		PackagePrefix:    gopts.PackagePrefix,
		ThriftRoot:       gopts.ThriftRoot,
		NoRecurse:        gopts.NoRecurse,
		NoVersionCheck:   gopts.NoVersionCheck,
		Plugin:           pluginHandle,
		NoTypes:          gopts.NoTypes,
		NoConstants:      gopts.NoConstants,
		NoServiceHelpers: gopts.NoServiceHelpers || gopts.NoTypes,
		NoEmbedIDL:       gopts.NoEmbedIDL,
	}
	if err := gen.Generate(module, &generatorOptions); err != nil {
		return fmt.Errorf("Failed to generate code: %+v", err)
	}
	return nil
}

// verifyAncestry verifies that the Thrift file for the given module and the
// Thrift files for all imported modules are contained within the directory
// tree rooted at the given path.
func verifyAncestry(m *compile.Module, root string) error {
	return m.Walk(func(m *compile.Module) error {
		path, err := filepath.Rel(root, m.ThriftPath)
		if err != nil {
			return fmt.Errorf(
				"could not resolve path for %q: %v", m.ThriftPath, err)
		}

		if strings.HasPrefix(path, "..") {
			return fmt.Errorf(
				"%q is not contained in the %q directory tree",
				m.ThriftPath, root)
		}

		return nil
	})
}

// findCommonAncestor finds the deepest common ancestor for the given module
// and all modules imported by it.
func findCommonAncestor(m *compile.Module) (string, error) {
	var result []string
	var lastString string

	err := m.Walk(func(m *compile.Module) error {
		thriftPath := m.ThriftPath
		if !filepath.IsAbs(thriftPath) {
			return fmt.Errorf(
				"ThriftPath must be absolute: %q is not absolute", thriftPath)
		}

		thriftDir := filepath.Dir(thriftPath)

		// Split("/foo/bar", "/") = ["", "foo", "bar"]
		parts := strings.Split(thriftDir, string(filepath.Separator))
		if result == nil {
			result = parts
			lastString = thriftPath
			return nil
		}

		result = commonPrefix(result, parts)
		if len(result) == 1 && result[0] == "" {
			return fmt.Errorf(
				"%q does not share an ancestor with %q",
				thriftPath, lastString)
		}

		lastString = thriftPath
		return nil
	})
	if err != nil {
		return "", err
	}

	return strings.Join(result, string(filepath.Separator)), nil
}

// commonPrefix finds the shortest common prefix for the two lists.
//
// An empty slice may be returned if the two lists don't have a common prefix.
func commonPrefix(l, r []string) []string {
	var i int
	for i = 0; i < len(l) && i < len(r); i++ {
		if l[i] != r[i] {
			break
		}
	}
	return l[:i]
}

// determinePackagePrefix determines the package prefix for Go packages
// generated in this file.
//
// dir must be an absolute path.
func determinePackagePrefix(dir string) (string, error) {
	gopathList := os.Getenv("GOPATH")
	if gopathList == "" {
		return "", errors.New("$GOPATH is not set")
	}

	for _, gopath := range filepath.SplitList(gopathList) {
		packagePath, err := filepath.Rel(filepath.Join(gopath, "src"), dir)
		if err != nil {
			return "", err
		}

		// The match is valid only if it's within the directory tree.
		if !strings.HasPrefix(packagePath, "..") {
			return packagePath, nil
		}
	}

	return "", fmt.Errorf("directory %q is not inside $GOPATH/src", dir)
}
<|endoftext|>


<commit_before>// Package main generates web project.
package main

import (
	"flag"
	"github.com/go-bootstrap/go-bootstrap/helpers"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)

func chDir(dir string) {
	err := os.Chdir(dir)
	exitOnError(err, "")
}

func exitOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s\n%s", msg, err.Error())
	}
}

func main() {
	dir := flag.String("dir", "", "Project directory relative to $GOPATH/src/")
	flag.Parse()

	if *dir == "" {
		log.Fatal("dir option is missing.")
	}

	// There can be more than one path, separated by colon.
	gopaths := strings.Split(os.ExpandEnv("$GOPATH"), ":")
	gopath := gopaths[0]

	fullpath := filepath.Join(gopath, "src", *dir)
	migrationsPath := filepath.Join(fullpath, "migrations")
	dirChunks := strings.Split(*dir, "/")
	repoName := dirChunks[len(dirChunks)-3]
	repoUser := dirChunks[len(dirChunks)-2]
	projectName := dirChunks[len(dirChunks)-1]
	dbName := projectName
	testDbName := projectName + "-test"
	currentUser, _ := user.Current()

	// 1. Create target directory
	log.Print("Creating " + fullpath + "...")
	err := os.MkdirAll(fullpath, 0755)
	exitOnError(err, "")

	// 2. Copy everything under blank directory to target directory.
	log.Print("Copying a blank project to " + fullpath + "...")
	blankDir := os.ExpandEnv(filepath.Join("$GOPATH", "src", "github.com", "go-bootstrap", "go-bootstrap", "blank"))
	currDir, err := os.Getwd()
	exitOnError(err, "Can't get current path!")

	chDir(blankDir)

	output, err := exec.Command("cp", "-rf", ".", fullpath).CombinedOutput()
	exitOnError(err, string(output))

	chDir(currDir)

	// 3. Interpolate placeholder variables on the new project.
	log.Print("Replacing placeholder variables on " + repoUser + "/" + projectName + "...")
	replacers := make(map[string]string)
	replacers["$GO_BOOTSTRAP_REPO_NAME"] = repoName
	replacers["$GO_BOOTSTRAP_REPO_USER"] = repoUser
	replacers["$GO_BOOTSTRAP_PROJECT_NAME"] = projectName
	replacers["$GO_BOOTSTRAP_COOKIE_SECRET"] = helpers.RandString(16)
	replacers["$GO_BOOTSTRAP_CURRENT_USER"] = currentUser.Username
	replacers["$GO_BOOTSTRAP_DOCKERFILE_DSN"] = helpers.DefaultPGDSN(dbName)
	err = helpers.RecursiveSearchReplaceFiles(fullpath, replacers)
	exitOnError(err, "")

	// 4. Create PostgreSQL databases.
	for _, name := range []string{dbName, testDbName} {
		log.Print("Creating a database named " + name + "...")
		if exec.Command("createdb", name).Run() != nil {
			log.Print("Unable to create PostgreSQL database: " + name)
		}
	}

	// 5.a. go get github.com/mattes/migrate.
	log.Print("Installing github.com/mattes/migrate...")
	output, err = exec.Command("go", "get", "github.com/mattes/migrate").CombinedOutput()
	exitOnError(err, string(output))

	// 5.b. Run migrations on localhost:5432.
	for _, name := range []string{dbName, testDbName} {
		pgDSN := helpers.DefaultPGDSN(name)

		log.Print("Running database migrations on " + pgDSN + "...")
		output, err := exec.Command("migrate", "-url", pgDSN, "-path", migrationsPath, "up").CombinedOutput()
		exitOnError(err, string(output))
	}

	// 6. Get all application dependencies for the first time.
	log.Print("Running go get ./...")
	cmd := exec.Command("go", "get", "./...")
	cmd.Dir = fullpath
	if output, err := cmd.CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	repoIsGit := strings.HasPrefix(repoName, "git")

	if repoIsGit {
		// Generate Godeps directory. Currently only works on git related repo.
		log.Print("Installing github.com/tools/godep...")
		output, err := exec.Command("go", "get", "github.com/tools/godep").CombinedOutput()
		exitOnError(err, string(output))

		// git init.
		log.Print("Running git init")
		cmd := exec.Command("git", "init")
		cmd.Dir = fullpath
		output, err = cmd.CombinedOutput()
		exitOnError(err, string(output))

		// godep save ./...
		log.Print("Running godep save ./...")
		cmd = exec.Command("godep", "save", "./...")
		cmd.Dir = fullpath
		output, err = cmd.CombinedOutput()
		exitOnError(err, string(output))

		// Run tests on newly generated app.
		log.Print("Running godep go test ./...")
		cmd = exec.Command("godep", "go", "test", "./...")
		cmd.Dir = fullpath
		output, _ = cmd.CombinedOutput()
		log.Print(string(output))

	} else {
		// Run tests on newly generated app.
		log.Print("Running go test ./...")
		cmd = exec.Command("go", "test", "./...")
		cmd.Dir = fullpath
		output, _ := cmd.CombinedOutput()
		log.Print(string(output))
	}
}
<commit_msg>Re-integrate after rebase.<commit_after>// Package main generates web project.
package main

import (
	"flag"
	"github.com/go-bootstrap/go-bootstrap/helpers"
	"log"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)

func chDir(dir string) {
	err := os.Chdir(dir)
	exitOnError(err, "")
}

func exitOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s\n%s", msg, err.Error())
	}
}

func main() {
	dir := flag.String("dir", "", "Project directory relative to $GOPATH/src/")
	flag.Parse()

	if *dir == "" {
		log.Fatal("dir option is missing.")
	}

	// There can be more than one path, separated by colon.
	gopaths := strings.Split(os.ExpandEnv("$GOPATH"), ":")
	gopath := gopaths[0]

	fullpath := filepath.Join(gopath, "src", *dir)
	migrationsPath := filepath.Join(fullpath, "migrations")
	dirChunks := strings.Split(*dir, "/")
	repoName := dirChunks[len(dirChunks)-3]
	repoUser := dirChunks[len(dirChunks)-2]
	projectName := dirChunks[len(dirChunks)-1]
	dbName := projectName
	testDbName := projectName + "-test"
	currentUser, _ := user.Current()

	// 1. Create target directory
	log.Print("Creating " + fullpath + "...")
	err := os.MkdirAll(fullpath, 0755)
	exitOnError(err, "")

	// 2. Copy everything under blank directory to target directory.
	log.Print("Copying a blank project to " + fullpath + "...")
	blankDir := os.ExpandEnv(filepath.Join("$GOPATH", "src", "github.com", "go-bootstrap", "go-bootstrap", "blank"))
	currDir, err := os.Getwd()
	exitOnError(err, "Can't get current path!")

	chDir(blankDir)

	output, err := exec.Command("cp", "-rf", ".", fullpath).CombinedOutput()
	exitOnError(err, string(output))

	chDir(currDir)

	// 3. Interpolate placeholder variables on the new project.
	log.Print("Replacing placeholder variables on " + repoUser + "/" + projectName + "...")
	replacers := make(map[string]string)
	replacers["$GO_BOOTSTRAP_REPO_NAME"] = repoName
	replacers["$GO_BOOTSTRAP_REPO_USER"] = repoUser
	replacers["$GO_BOOTSTRAP_PROJECT_NAME"] = projectName
	replacers["$GO_BOOTSTRAP_COOKIE_SECRET"] = helpers.RandString(16)
	replacers["$GO_BOOTSTRAP_CURRENT_USER"] = currentUser.Username
	replacers["$GO_BOOTSTRAP_DOCKERFILE_DSN"] = helpers.DefaultPGDSN(dbName)
	err = helpers.RecursiveSearchReplaceFiles(fullpath, replacers)
	exitOnError(err, "")

	// 4. Create PostgreSQL databases.
	for _, name := range []string{dbName, testDbName} {
		log.Print("Creating a database named " + name + "...")
		if exec.Command("createdb", name).Run() != nil {
			log.Print("Unable to create PostgreSQL database: " + name)
		}
	}

	// 5.a. go get github.com/mattes/migrate.
	log.Print("Installing github.com/mattes/migrate...")
	output, err = exec.Command("go", "get", "github.com/mattes/migrate").CombinedOutput()
	exitOnError(err, string(output))

	// 5.b. Run migrations on localhost:5432.
	for _, name := range []string{dbName, testDbName} {
		pgDSN := helpers.DefaultPGDSN(name)

		log.Print("Running database migrations on " + pgDSN + "...")
		output, err := exec.Command("migrate", "-url", pgDSN, "-path", migrationsPath, "up").CombinedOutput()
		exitOnError(err, string(output))
	}

	// 6. Get all application dependencies for the first time.
	log.Print("Running go get ./...")
	cmd := exec.Command("go", "get", "./...")
	cmd.Dir = fullpath
	if output, err := cmd.CombinedOutput(); err != nil {
		log.Fatal(string(output))
	}

	repoIsGit := strings.HasPrefix(repoName, "git")

	if repoIsGit {
		// Generate Godeps directory. Currently only works on git related repo.
		log.Print("Installing github.com/tools/godep...")
		output, err := exec.Command("go", "get", "github.com/tools/godep").CombinedOutput()
		exitOnError(err, string(output))

		// git init.
		log.Print("Running git init")
		cmd := exec.Command("git", "init")
		cmd.Dir = fullpath
		output, err := cmd.CombinedOutput()
		exitOnError(err, string(output))

		// godep save ./...
		log.Print("Running godep save ./...")
		cmd = exec.Command("godep", "save", "./...")
		cmd.Dir = fullpath
		output, err = cmd.CombinedOutput()
		exitOnError(err, string(output))

		// Run tests on newly generated app.
		log.Print("Running godep go test ./...")
		cmd = exec.Command("godep", "go", "test", "./...")
		cmd.Dir = fullpath
		output, _ = cmd.CombinedOutput()
		log.Print(string(output))

	} else {
		// Run tests on newly generated app.
		log.Print("Running go test ./...")
		cmd = exec.Command("go", "test", "./...")
		cmd.Dir = fullpath
		output, _ = cmd.CombinedOutput()
		log.Print(string(output))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/clearblade/cblib"
	"os"
)

func main() {
	theArgs := os.Args
	if len(theArgs) < 2 {
		fmt.Printf("No command provided\n")
		os.Exit(1)
	}
	// Special case version command for cb-cli only
	if theArgs[1] == "version" {
		fmt.Printf("%s\n", cbCliVersion)
		os.Exit(0)
	}

	subCommand, err := cblib.GetCommand(theArgs[1])
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	err = subCommand.Execute( /*client,*/ theArgs[2:])
	if err != nil {
		fmt.Printf("Aborting: %s\n", err.Error())
	}
}
<commit_msg>Added help route<commit_after>package main

import (
	"fmt"
	"github.com/clearblade/cblib"
	"os"
)

func main() {
	theArgs := os.Args
	if len(theArgs) < 2 {
		fmt.Printf("No command provided\n")
		os.Exit(1)
	}
	// Special case version command for cb-cli only
	if theArgs[1] == "version" {
		fmt.Printf("%s\n", cbCliVersion)
		os.Exit(0)
	}

	// TODO CBCOMM-192 add root --help

	subCommand, err := cblib.GetCommand(theArgs[1])
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}

	if theArgs[2] == "help" || theArgs[2] == "--help" {
		cblib.PrintHelpFor(subCommand)
		os.Exit(1)
	}

	err = subCommand.Execute( /*client,*/ theArgs[2:])
	if err != nil {
		fmt.Printf("Aborting: %s\n", err.Error())
	}
}
<|endoftext|>


<commit_before>package main

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

	"github.com/gin-gonic/gin"
)

func main() {
	config, err := LoadConfig()

	if err != nil {
		log.Fatal(err)
	}

	if config.ReleaseMode {
		gin.SetMode(gin.ReleaseMode)
	}

	etcd, err := NewEtcd(config.EtcdEndpoint)

	if err != nil {
		log.Fatal(err)
	}

	if !etcd.HasKey("/paus") {
		if err = etcd.Mkdir("/paus"); err != nil {
			log.Fatal(err)
		}
	}

	if !etcd.HasKey("/paus/users") {
		if err = etcd.Mkdir("/paus/users"); err != nil {
			log.Fatal(err)
		}
	}

	r := gin.Default()
	r.LoadHTMLGlob("templates/*")

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"alert":      false,
			"error":      false,
			"message":    "",
			"baseDomain": config.BaseDomain,
		})
	})

	r.GET("/users/:username", func(c *gin.Context) {
		username := c.Param("username")

		if !UserExists(etcd, username) {
			c.HTML(http.StatusNotFound, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("User %s does not exist.", username),
			})

			return
		}

		apps, err := Apps(etcd, username)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		c.HTML(http.StatusOK, "user.tmpl", gin.H{
			"error": false,
			"user":  username,
			"apps":  apps,
		})
	})

	r.POST("/users/:username/apps", func(c *gin.Context) {
		username := c.Param("username")
		appName := c.PostForm("appName")

		err := CreateApp(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "users.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.GET("/users/:username/apps/:appName", func(c *gin.Context) {
		var latestURL string

		username := c.Param("username")

		if !UserExists(etcd, username) {
			c.HTML(http.StatusNotFound, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("User %s does not exist.", username),
			})

			return
		}

		appName := c.Param("appName")

		if !AppExists(etcd, username, appName) {
			c.HTML(http.StatusNotFound, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("Application %s does not exist.", appName),
			})

			return
		}

		urls, err := AppURLs(etcd, config.URIScheme, config.BaseDomain, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		envs, err := EnvironmentVariables(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		buildArgs, err := BuildArgs(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		if len(urls) > 0 {
			latestURL = LatestAppURLOfUser(config.URIScheme, config.BaseDomain, username, appName)
		}

		c.HTML(http.StatusOK, "app.tmpl", gin.H{
			"error":     false,
			"user":      username,
			"app":       appName,
			"latestURL": latestURL,
			"urls":      urls,
			"buildArgs": buildArgs,
			"envs":      envs,
		})
	})

	r.POST("/users/:username/apps/:appName/build-args", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddBuildArg(etcd, username, appName, key, value)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.POST("/users/:username/apps/:appName/envs", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddEnvironmentVariable(etcd, username, appName, key, value)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.POST("/users/:username/apps/:appName/envs/upload", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")

		dotenvFile, _, err := c.Request.FormFile("dotenv")

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		if err = LoadDotenv(etcd, username, appName, dotenvFile); err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": fmt.Sprintf("Error: %s", err.Error()),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.POST("/submit", func(c *gin.Context) {
		username := c.PostForm("username")
		pubKey := c.PostForm("pubKey")

		if UserExists(etcd, username) {
			c.HTML(http.StatusConflict, "index.tmpl", gin.H{
				"alert":      true,
				"error":      true,
				"message":    fmt.Sprintf("User %s already exists.", username),
				"baseDomain": config.BaseDomain,
			})

			return
		}

		err := CreateUser(etcd, username)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "index.tmpl", gin.H{
				"alert":      true,
				"error":      true,
				"message":    fmt.Sprintf("Error: %s", err.Error()),
				"baseDomain": config.BaseDomain,
			})

			return
		}

		out, err := UploadPublicKey(username, pubKey)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "index.tmpl", gin.H{
				"alert":      true,
				"error":      true,
				"message":    fmt.Sprintf("Error: %s", err.Error()),
				"baseDomain": config.BaseDomain,
			})

			return
		}

		c.HTML(http.StatusCreated, "index.tmpl", gin.H{
			"alert":      true,
			"error":      false,
			"message":    fmt.Sprintf("Fingerprint: %s", out),
			"baseDomain": config.BaseDomain,
			"username":   username,
		})
	})

	r.Run()
}
<commit_msg>Flush wrapped logs to stderr<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
)

func main() {
	config, err := LoadConfig()

	if err != nil {
		errors.Fprint(os.Stderr, err)
		os.Exit(1)
	}

	if config.ReleaseMode {
		gin.SetMode(gin.ReleaseMode)
	}

	etcd, err := NewEtcd(config.EtcdEndpoint)

	if err != nil {
		errors.Fprint(os.Stderr, err)
		os.Exit(1)
	}

	if !etcd.HasKey("/paus") {
		if err = etcd.Mkdir("/paus"); err != nil {
			errors.Fprint(os.Stderr, err)
			os.Exit(1)
		}
	}

	if !etcd.HasKey("/paus/users") {
		if err = etcd.Mkdir("/paus/users"); err != nil {
			errors.Fprint(os.Stderr, err)
			os.Exit(1)
		}
	}

	r := gin.Default()
	r.LoadHTMLGlob("templates/*")

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"alert":      false,
			"error":      false,
			"message":    "",
			"baseDomain": config.BaseDomain,
		})
	})

	r.GET("/users/:username", func(c *gin.Context) {
		username := c.Param("username")

		if !UserExists(etcd, username) {
			c.HTML(http.StatusNotFound, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("User %s does not exist.", username),
			})

			return
		}

		apps, err := Apps(etcd, username)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "user.tmpl", gin.H{
				"error":   true,
				"message": "Failed to list apps.",
			})

			return
		}

		c.HTML(http.StatusOK, "user.tmpl", gin.H{
			"error": false,
			"user":  username,
			"apps":  apps,
		})
	})

	r.POST("/users/:username/apps", func(c *gin.Context) {
		username := c.Param("username")
		appName := c.PostForm("appName")

		err := CreateApp(etcd, username, appName)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "users.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": "Failed to create app.",
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.GET("/users/:username/apps/:appName", func(c *gin.Context) {
		var latestURL string

		username := c.Param("username")

		if !UserExists(etcd, username) {
			c.HTML(http.StatusNotFound, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("User %s does not exist.", username),
			})

			return
		}

		appName := c.Param("appName")

		if !AppExists(etcd, username, appName) {
			c.HTML(http.StatusNotFound, "user.tmpl", gin.H{
				"error":   true,
				"message": fmt.Sprintf("Application %s does not exist.", appName),
			})

			return
		}

		urls, err := AppURLs(etcd, config.URIScheme, config.BaseDomain, username, appName)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": "Failed to list app URLs.",
			})

			return
		}

		envs, err := EnvironmentVariables(etcd, username, appName)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": "Failed to list environment variables.",
			})

			return
		}

		buildArgs, err := BuildArgs(etcd, username, appName)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": "Failed to list build args.",
			})

			return
		}

		if len(urls) > 0 {
			latestURL = LatestAppURLOfUser(config.URIScheme, config.BaseDomain, username, appName)
		}

		c.HTML(http.StatusOK, "app.tmpl", gin.H{
			"error":     false,
			"user":      username,
			"app":       appName,
			"latestURL": latestURL,
			"urls":      urls,
			"buildArgs": buildArgs,
			"envs":      envs,
		})
	})

	r.POST("/users/:username/apps/:appName/build-args", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddBuildArg(etcd, username, appName, key, value)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": "Failed to add build arg.",
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.POST("/users/:username/apps/:appName/envs", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddEnvironmentVariable(etcd, username, appName, key, value)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": "Failed to add environment variable.",
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.POST("/users/:username/apps/:appName/envs/upload", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")

		dotenvFile, _, err := c.Request.FormFile("dotenv")

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": "Failed to upload dotenv.",
			})

			return
		}

		if err = LoadDotenv(etcd, username, appName, dotenvFile); err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": "Failed to load dotenv.",
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/apps/"+appName)
	})

	r.POST("/submit", func(c *gin.Context) {
		username := c.PostForm("username")
		pubKey := c.PostForm("pubKey")

		if UserExists(etcd, username) {
			c.HTML(http.StatusConflict, "index.tmpl", gin.H{
				"alert":      true,
				"error":      true,
				"message":    fmt.Sprintf("User %s already exists.", username),
				"baseDomain": config.BaseDomain,
			})

			return
		}

		err := CreateUser(etcd, username)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "index.tmpl", gin.H{
				"alert":      true,
				"error":      true,
				"message":    "Failed to create user.",
				"baseDomain": config.BaseDomain,
			})

			return
		}

		out, err := UploadPublicKey(username, pubKey)

		if err != nil {
			errors.Fprint(os.Stderr, err)

			c.HTML(http.StatusInternalServerError, "index.tmpl", gin.H{
				"alert":      true,
				"error":      true,
				"message":    "Failed to upload SSH public key.",
				"baseDomain": config.BaseDomain,
			})

			return
		}

		c.HTML(http.StatusCreated, "index.tmpl", gin.H{
			"alert":      true,
			"error":      false,
			"message":    fmt.Sprintf("Fingerprint: %s", out),
			"baseDomain": config.BaseDomain,
			"username":   username,
		})
	})

	r.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime/debug"
	"strconv"
	"syscall"
	"time"
)

var (
	templates *template.Template
	blaster   *Blaster

	flag_R        = flag.Uint("r", 17, "Red GPIO pin")
	flag_G        = flag.Uint("g", 22, "Green GPIO pin")
	flag_B        = flag.Uint("b", 27, "Blue GPIO pin")
	flag_Cooldown = flag.Uint("cooldown", 10, "Milliseconds cooldown between requests")
)

type RGB struct {
	R uint8
	G uint8
	B uint8
}

func (c RGB) String() string {
	return fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B)
}

func mustParseTemplates() {
	var err error
	templates, err = template.ParseGlob("templates/*.html")

	if err != nil {
		log.Fatal(err)
	}
}

func attemptPiBlasterStart() error {
	cmd := exec.Command("pi-blaster")
	return cmd.Run()
}

func mustOpenPiBlaster() *os.File {
	file, err := os.OpenFile("/dev/pi-blaster", os.O_RDWR, os.ModeNamedPipe)

	if err != nil {
		if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ENOENT {
			err = attemptPiBlasterStart()
		}
	}

	if err != nil {
		log.Fatal(err)
	}

	return file
}

type Blaster struct {
	pipe  *os.File
	Input chan RGB
	Color chan chan RGB
	shake chan struct{}

	r, g, b uint8
}

func NewBlaster() *Blaster {
	return &Blaster{
		Input: make(chan RGB),
		Color: make(chan chan RGB),
		shake: make(chan struct{}),
	}
}

func (b *Blaster) Run() {
	b.pipe = mustOpenPiBlaster()
	defer b.pipe.Close()

	for {
		select {
		case c := <-b.Input:
			b.setAll(c)
		case c := <-b.Color:
			go func(c chan RGB) {
				select {
				case c <- RGB{b.r, b.g, b.b}:
					// delivered, everything's OK
				case <-time.After(5 * time.Second):
					log.Fatal("Requested color chan blocked too long.")
				}
			}(c)
		}
	}
}

func (b *Blaster) setPin(pin uint, val float64) error {
	chanCmd := fmt.Sprintf("%d=%f\n", pin, val)

	b.pipe.Write([]byte(chanCmd))
	return nil
}

func (b *Blaster) setChannelInteger(pin uint, val uint8) error {
	switch {
	case val > 255:
		return errors.New("can't go over 255. sorry mate.")
	case val < 0:
		return errors.New("can't go below 0. sorry mate.")
	default:
		fval := float64(val) / 255.0
		return b.setPin(pin, fval)
	}
}

func (b *Blaster) setRed(val uint8) (err error) {
	if err = b.setChannelInteger(*flag_R, val); err != nil {
		b.r = val
	}
	return
}

func (b *Blaster) setGreen(val uint8) (err error) {
	if err = b.setChannelInteger(*flag_G, val); err != nil {
		b.g = val
	}
	return
}

func (b *Blaster) setBlue(val uint8) (err error) {
	if err = b.setChannelInteger(*flag_B, val); err != nil {
		b.b = val
	}
	return
}

func (_ *Blaster) correctColor(c RGB) RGB {
	gcorrection := float64(0x77) / 0xFF
	bcorrection := float64(0x33) / 0xFF

	c.G = uint8(float64(c.G) * gcorrection)
	c.B = uint8(float64(c.B) * bcorrection)

	return c
}

func (b *Blaster) setAll(c RGB) {
	c = b.correctColor(c)
	b.setRed(c.R)
	b.setGreen(c.G)
	b.setBlue(c.B)
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	if err := recover(); err != nil {
		w.WriteHeader(401)

		fmt.Fprintf(w, "Oh...:(\n\n")

		if e, ok := err.(error); ok {
			w.Write([]byte(e.Error()))
			w.Write([]byte{'\n', '\n'})
			w.Write(debug.Stack())
		} else {
			fmt.Fprintf(w, "%s\n\n", err)
		}

		log.Println(
			"panic catched:", err,
			"\nRequest data:", r)
	}
}

func parseUint8OrZero(s string) uint8 {
	i, err := strconv.ParseUint(s, 10, 8)
	if err != nil {
		return 0
	}
	return uint8(i)
}

func withRequestedColor(f func(color RGB)) {
	c := make(chan RGB)
	defer close(c)
	blaster.Color <- c
	f(<-c)
}

func actionHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)

	values, err := url.ParseQuery(r.URL.RawQuery)

	if err != nil {
		panic(err)
	}

	action := values.Get("action")

	switch action {
	case "lighter":
		withRequestedColor(func(c RGB) {
			blaster.Input <- RGB{c.R + 10, c.G + 10, c.B + 10}
		})
	case "darker":
		withRequestedColor(func(c RGB) {
			blaster.Input <- RGB{c.R - 10, c.G - 10, c.B - 10}
		})
	case "off":
		blaster.Input <- RGB{0, 0, 0}
	case "set":
		r := parseUint8OrZero(values.Get("r"))
		g := parseUint8OrZero(values.Get("g"))
		b := parseUint8OrZero(values.Get("b"))

		blaster.Input <- RGB{r, g, b}
	}
}

func currentColorHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	w.Header().Set("Content-Type", "text/plain")

	withRequestedColor(func(c RGB) {
		w.Write([]byte(c.String()))
	})
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	templates.ExecuteTemplate(w, "index.html", nil)
}

func main() {
	flag.Parse()

	blaster = NewBlaster()
	go blaster.Run()

	blaster.Input <- RGB{}

	mustParseTemplates()

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/do", actionHandler)
	http.HandleFunc("/color", currentColorHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("templates"))))

	log.Fatal(http.ListenAndServe(":1337", nil))
}
<commit_msg>Wrongly checked error condition<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"runtime/debug"
	"strconv"
	"syscall"
	"time"
)

var (
	templates *template.Template
	blaster   *Blaster

	flag_R        = flag.Uint("r", 17, "Red GPIO pin")
	flag_G        = flag.Uint("g", 22, "Green GPIO pin")
	flag_B        = flag.Uint("b", 27, "Blue GPIO pin")
	flag_Cooldown = flag.Uint("cooldown", 10, "Milliseconds cooldown between requests")
)

type RGB struct {
	R uint8
	G uint8
	B uint8
}

func (c RGB) String() string {
	return fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B)
}

func mustParseTemplates() {
	var err error
	templates, err = template.ParseGlob("templates/*.html")

	if err != nil {
		log.Fatal(err)
	}
}

func attemptPiBlasterStart() error {
	cmd := exec.Command("pi-blaster")
	return cmd.Run()
}

func mustOpenPiBlaster() *os.File {
	file, err := os.OpenFile("/dev/pi-blaster", os.O_RDWR, os.ModeNamedPipe)

	if err != nil {
		if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ENOENT {
			err = attemptPiBlasterStart()
		}
	}

	if err != nil {
		log.Fatal(err)
	}

	return file
}

type Blaster struct {
	pipe  *os.File
	Input chan RGB
	Color chan chan RGB
	shake chan struct{}

	r, g, b uint8
}

func NewBlaster() *Blaster {
	return &Blaster{
		Input: make(chan RGB),
		Color: make(chan chan RGB),
		shake: make(chan struct{}),
	}
}

func (b *Blaster) Run() {
	b.pipe = mustOpenPiBlaster()
	defer b.pipe.Close()

	for {
		select {
		case c := <-b.Input:
			b.setAll(c)
		case c := <-b.Color:
			go func(c chan RGB) {
				select {
				case c <- RGB{b.r, b.g, b.b}:
					// delivered, everything's OK
				case <-time.After(5 * time.Second):
					log.Fatal("Requested color chan blocked too long.")
				}
			}(c)
		}
	}
}

func (b *Blaster) setPin(pin uint, val float64) error {
	chanCmd := fmt.Sprintf("%d=%f\n", pin, val)

	b.pipe.Write([]byte(chanCmd))
	return nil
}

func (b *Blaster) setChannelInteger(pin uint, val uint8) error {
	switch {
	case val > 255:
		return errors.New("can't go over 255. sorry mate.")
	case val < 0:
		return errors.New("can't go below 0. sorry mate.")
	default:
		fval := float64(val) / 255.0
		return b.setPin(pin, fval)
	}
}

func (b *Blaster) setRed(val uint8) (err error) {
	if err = b.setChannelInteger(*flag_R, val); err == nil {
		b.r = val
	}
	return
}

func (b *Blaster) setGreen(val uint8) (err error) {
	if err = b.setChannelInteger(*flag_G, val); err == nil {
		b.g = val
	}
	return
}

func (b *Blaster) setBlue(val uint8) (err error) {
	if err = b.setChannelInteger(*flag_B, val); err == nil {
		b.b = val
	}
	return
}

func (_ *Blaster) correctColor(c RGB) RGB {
	gcorrection := float64(0x77) / 0xFF
	bcorrection := float64(0x33) / 0xFF

	c.G = uint8(float64(c.G) * gcorrection)
	c.B = uint8(float64(c.B) * bcorrection)

	return c
}

func (b *Blaster) setAll(c RGB) {
	c = b.correctColor(c)
	b.setRed(c.R)
	b.setGreen(c.G)
	b.setBlue(c.B)
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	if err := recover(); err != nil {
		w.WriteHeader(401)

		fmt.Fprintf(w, "Oh...:(\n\n")

		if e, ok := err.(error); ok {
			w.Write([]byte(e.Error()))
			w.Write([]byte{'\n', '\n'})
			w.Write(debug.Stack())
		} else {
			fmt.Fprintf(w, "%s\n\n", err)
		}

		log.Println(
			"panic catched:", err,
			"\nRequest data:", r)
	}
}

func parseUint8OrZero(s string) uint8 {
	i, err := strconv.ParseUint(s, 10, 8)
	if err != nil {
		return 0
	}
	return uint8(i)
}

func withRequestedColor(f func(color RGB)) {
	c := make(chan RGB)
	defer close(c)
	blaster.Color <- c
	f(<-c)
}

func actionHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)

	values, err := url.ParseQuery(r.URL.RawQuery)

	if err != nil {
		panic(err)
	}

	action := values.Get("action")

	switch action {
	case "lighter":
		withRequestedColor(func(c RGB) {
			blaster.Input <- RGB{c.R + 10, c.G + 10, c.B + 10}
		})
	case "darker":
		withRequestedColor(func(c RGB) {
			blaster.Input <- RGB{c.R - 10, c.G - 10, c.B - 10}
		})
	case "off":
		blaster.Input <- RGB{0, 0, 0}
	case "set":
		r := parseUint8OrZero(values.Get("r"))
		g := parseUint8OrZero(values.Get("g"))
		b := parseUint8OrZero(values.Get("b"))

		blaster.Input <- RGB{r, g, b}
	}
}

func currentColorHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	w.Header().Set("Content-Type", "text/plain")

	withRequestedColor(func(c RGB) {
		w.Write([]byte(c.String()))
	})
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	defer errorHandler(w, r)
	templates.ExecuteTemplate(w, "index.html", nil)
}

func main() {
	flag.Parse()

	blaster = NewBlaster()
	go blaster.Run()

	blaster.Input <- RGB{}

	mustParseTemplates()

	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/do", actionHandler)
	http.HandleFunc("/color", currentColorHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("templates"))))

	log.Fatal(http.ListenAndServe(":1337", nil))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/boomlinde/acidforth/collection"
	"github.com/boomlinde/acidforth/machine"
	"github.com/boomlinde/acidforth/midi"
	"github.com/boomlinde/acidforth/synth"
	"github.com/gordonklaus/portaudio"
	"github.com/rakyll/portmidi"
	"io/ioutil"
	"log"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"sync"
)

const sfreq = 44100

func main() {
	var listMidi bool
	var midiInterface int
	var m *midi.Midi
	var prompt float64

	flag.BoolVar(&listMidi, "l", false, "List MIDI interfaces")
	flag.IntVar(&midiInterface, "m", -1, "Connect to MIDI interface ID")
	flag.Parse()
	args := flag.Args()

	if listMidi {
		portmidi.Initialize()
		defer portmidi.Terminate()
		deviceCount := portmidi.CountDevices()
		for i := 0; i < deviceCount; i++ {
			fmt.Println(i, portmidi.GetDeviceInfo(portmidi.DeviceId(i)))
		}
		os.Exit(0)
	} else if midiInterface != -1 {
		portmidi.Initialize()
		defer portmidi.Terminate()
		in, err := portmidi.NewInputStream(portmidi.DeviceId(midiInterface), 1024)
		if err != nil {
			log.Fatal(err)
		}
		defer in.Close()
		m = midi.NewMidi(in.Listen())
	} else {
		m = midi.NewMidi(make(chan portmidi.Event))
	}

	pl := &sync.Mutex{}

	col := collection.NewCollection()
	m.Register(col)
	addComponents(sfreq, col, args[:len(args)-1])

	col.Machine.Register("prompt", func(s *machine.Stack) {
		pl.Lock()
		s.Push(prompt)
		pl.Unlock()
	})

	data, err := ioutil.ReadFile(args[len(args)-1])
	chk(err)

	tokens := machine.TokenizeBytes(data)
	tokens = machine.StripComments(tokens)

	go m.Listen()

	tokens, err = machine.ExpandMacros(tokens)
	chk(err)

	chk(col.Machine.Compile(tokens))
	log.Println("Running")

	portaudio.Initialize()
	defer portaudio.Terminate()
	stream, err := portaudio.OpenDefaultStream(0, 2, sfreq, 0, col.Callback)
	chk(err)
	defer stream.Close()
	stream.Start()

	reader := bufio.NewReader(os.Stdin)
	numberRe, err := regexp.Compile("[0-9]+\\.?[0-9]*")
	chk(err)

	for {
		text, err := reader.ReadString('\n')
		chk(err)
		found := numberRe.FindString(text)
		if found == "" {
			col.Playing = !col.Playing
			if col.Playing == true {
				log.Println("Starting sequencer")
			} else {
				log.Print("Stopping sequencer")
			}
		} else {
			tpr, err := strconv.ParseFloat(found, 64)
			if err != nil {
				log.Println(err)
				continue
			}
			pl.Lock()
			prompt = tpr
			pl.Unlock()
		}
	}
}

func addComponents(srate float64, c *collection.Collection, samples []string) {
	for i := 1; i < 9; i++ {
		_ = synth.NewOperator(fmt.Sprintf("op%d", i), c, srate)
		_ = synth.NewEnvelope(fmt.Sprintf("env%d", i), c, srate)
	}
	for _, r := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
		_ = synth.NewRegister(string(r), c)
	}
	for i := 1; i < 5; i++ {
		_ = synth.NewAccumulator(fmt.Sprintf("mix%d", i), c)
		_ = synth.NewDelay(fmt.Sprintf("delay%d", i), c, srate)
	}
	for i := 1; i < 9; i++ {
		_ = synth.NewDSeq(fmt.Sprintf("dseq%d", i), c)
	}
	for _, v := range samples {
		s, err := os.Stat(v)
		if err != nil {
			log.Fatal(err)
		}
		if s.Mode().IsDir() {
			files, err := filepath.Glob(filepath.Join(v, "*.wav"))
			if err != nil {
				log.Fatal(err)
			}
			for _, f := range files {
				_ = synth.NewSampler(f, c, srate)
			}
		} else {
			_ = synth.NewSampler(v, c, srate)
		}
	}

	_ = synth.NewSeq("seq", c, srate)

	synth.NewWaveTables(c)

	c.Machine.Register("srate", func(s *machine.Stack) { s.Push(srate) })
	c.Machine.Register("m2f", func(s *machine.Stack) {
		s.Push(440 * math.Pow(2, (s.Pop()-69)/12))
	})
}

func chk(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>cleanup<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/boomlinde/acidforth/collection"
	"github.com/boomlinde/acidforth/machine"
	"github.com/boomlinde/acidforth/midi"
	"github.com/boomlinde/acidforth/synth"
	"github.com/gordonklaus/portaudio"
	"github.com/rakyll/portmidi"
	"io/ioutil"
	"log"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"sync"
)

const sfreq = 44100

func main() {
	var listMidi bool
	var midiInterface int
	var m *midi.Midi
	var prompt float64

	flag.BoolVar(&listMidi, "l", false, "List MIDI interfaces")
	flag.IntVar(&midiInterface, "m", -1, "Connect to MIDI interface ID")
	flag.Parse()
	args := flag.Args()

	if listMidi {
		portmidi.Initialize()
		defer portmidi.Terminate()
		deviceCount := portmidi.CountDevices()
		for i := 0; i < deviceCount; i++ {
			fmt.Println(i, portmidi.GetDeviceInfo(portmidi.DeviceId(i)))
		}
		os.Exit(0)
	} else if midiInterface != -1 {
		portmidi.Initialize()
		defer portmidi.Terminate()
		in, err := portmidi.NewInputStream(portmidi.DeviceId(midiInterface), 1024)
		if err != nil {
			log.Fatal(err)
		}
		defer in.Close()
		m = midi.NewMidi(in.Listen())
	} else {
		m = midi.NewMidi(make(chan portmidi.Event))
	}

	pl := &sync.Mutex{}

	col := collection.NewCollection()

	m.Register(col)
	go m.Listen()

	addComponents(sfreq, col, args[:len(args)-1])

	col.Machine.Register("prompt", func(s *machine.Stack) {
		pl.Lock()
		s.Push(prompt)
		pl.Unlock()
	})

	data, err := ioutil.ReadFile(args[len(args)-1])
	chk(err)

	tokens := machine.TokenizeBytes(data)
	tokens = machine.StripComments(tokens)
	tokens, err = machine.ExpandMacros(tokens)
	chk(err)

	chk(col.Machine.Compile(tokens))
	log.Println("Running")

	portaudio.Initialize()
	defer portaudio.Terminate()
	stream, err := portaudio.OpenDefaultStream(0, 2, sfreq, 0, col.Callback)
	chk(err)
	defer stream.Close()
	stream.Start()

	reader := bufio.NewReader(os.Stdin)
	numberRe, err := regexp.Compile("[0-9]+\\.?[0-9]*")
	chk(err)

	for {
		text, err := reader.ReadString('\n')
		chk(err)
		found := numberRe.FindString(text)
		if found == "" {
			col.Playing = !col.Playing
			if col.Playing == true {
				log.Println("Starting sequencer")
			} else {
				log.Print("Stopping sequencer")
			}
		} else {
			tpr, err := strconv.ParseFloat(found, 64)
			if err != nil {
				log.Println(err)
				continue
			}
			pl.Lock()
			prompt = tpr
			pl.Unlock()
		}
	}
}

func addComponents(srate float64, c *collection.Collection, samples []string) {
	for i := 1; i < 9; i++ {
		_ = synth.NewOperator(fmt.Sprintf("op%d", i), c, srate)
		_ = synth.NewEnvelope(fmt.Sprintf("env%d", i), c, srate)
	}
	for _, r := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
		_ = synth.NewRegister(string(r), c)
	}
	for i := 1; i < 5; i++ {
		_ = synth.NewAccumulator(fmt.Sprintf("mix%d", i), c)
		_ = synth.NewDelay(fmt.Sprintf("delay%d", i), c, srate)
	}
	for i := 1; i < 9; i++ {
		_ = synth.NewDSeq(fmt.Sprintf("dseq%d", i), c)
	}
	for _, v := range samples {
		s, err := os.Stat(v)
		if err != nil {
			log.Fatal(err)
		}
		if s.Mode().IsDir() {
			files, err := filepath.Glob(filepath.Join(v, "*.wav"))
			if err != nil {
				log.Fatal(err)
			}
			for _, f := range files {
				_ = synth.NewSampler(f, c, srate)
			}
		} else {
			_ = synth.NewSampler(v, c, srate)
		}
	}

	_ = synth.NewSeq("seq", c, srate)

	synth.NewWaveTables(c)

	c.Machine.Register("srate", func(s *machine.Stack) { s.Push(srate) })
	c.Machine.Register("m2f", func(s *machine.Stack) {
		s.Push(440 * math.Pow(2, (s.Pop()-69)/12))
	})
}

func chk(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/googollee/go-socket.io"

	"github.com/jesusrmoreno/toto/domain"
	"github.com/jesusrmoreno/toto/implementations"
	"github.com/jesusrmoreno/toto/utils"

	"github.com/BurntSushi/toml"
	"github.com/jesusrmoreno/sad-squid"

	"github.com/codegangsta/cli"
)

// Version ...
var Version = "No Version Specified"

// Events that are exposed to the client
const (
	groupAssignment = "group-assignment"
	roomMessage     = "room-message"
	leaveGroup      = "leave-group"
	joinGroup       = "join-group"
	joinGame        = "join-game"
	getPeers        = "get-peers"
	makeMove        = "make-move"
	moveMade        = "move-made"
	inQueue         = "in-queue"

	serverError = "server-error"
	clientError = "client-error"
)

// Response ...
type Response struct {
	Kind string                 `json:"kind"`
	Data map[string]interface{} `json:"data"`
}

// Method for creating errors more quickly.
func errorResponse(kind, err string) Response {
	d := map[string]interface{}{}
	d["error"] = err
	r := Response{
		Kind: kind,
		Data: d,
	}
	return r
}

// GamesInfo serves to store the metadata for different games
type GamesInfo struct {
	// These must be thread safe so we use the ConcurrentMap types
	TurnMap *utils.ConcurrentStringIntMap
	// Maps the player id to the room
	RoomMap *utils.ConcurrentStringMap
}

// ReadGameFiles reads the provided directory for files that conform to the
// game struct definition, these must be json files, and loads them into our
// game map.
func ReadGameFiles(gameDir string) (domain.GameMap, error) {
	files, err := filepath.Glob(gameDir + "/*.toml")
	gm := domain.GameMap{}
	if err != nil {
		return nil, err
	}
	for _, f := range files {
		raw, err := os.Open(f)
		defer raw.Close()
		if err != nil {
			return nil, err
		}
		r := io.Reader(raw)
		dummy := domain.Game{}
		if meta, err := toml.DecodeReader(r, &dummy); err != nil {
			log.Println(meta)
			return nil, errors.New("Invalid configuration in file: " + f)
		}
		g := domain.Game{
			Players: dummy.Players,
			Title:   dummy.Title,
			UUID:    dummy.UUID,
			Lobby: impl.Lobby{
				// We implement our lobby as a buffered channel that takes the number of
				// players specified in the config file for the game and makes that the
				// cap.
				Queue: make(chan domain.Player, dummy.Players),
			},
		}
		g.FileName = f
		if _, exists := gm[g.UUID]; exists {
			return nil, errors.New("uniqueKey conflict between: " + f + " and " + gm[g.UUID].FileName)
		}
		gm[g.UUID] = g
	}
	return gm, nil
}

// QueuePlayers adds players to the game's lobby to wait for a partner.
// Players are queued on a first come first serve basis.
func QueuePlayers(g domain.Game, p domain.Player) {
	data := map[string]interface{}{}
	data["message"] = "You are in the queue for game: " + g.Title
	r := Response{
		Kind: inQueue,
		Data: data,
	}
	p.Comm.Emit(inQueue, r)
	pq := g.Lobby
	pq.AddToQueue(p)
}

// GroupPlayers creates groups of players of size PlayerSize as defined in the
// game files.
func GroupPlayers(g domain.Game, server *socketio.Server, gi *GamesInfo) {
	pq := g.Lobby
	n := g.Players
	for {
		select {
		default:
			if g.Lobby.Size() >= n {
				team := []domain.Player{}
				// Generate a room id
				roomName := squid.GenerateSimpleID()
				for i := 0; i < n; i++ {
					p := pq.PopFromQueue()
					pID := p.Comm.Id()
					team = append(team, p)

					// Set the metadata
					gi.RoomMap.Set(pID, roomName)
					gi.TurnMap.Set(roomName+":"+pID, i)

					// Place the player in the created room.
					p.Comm.Join(roomName)

					// Create the response
					data := map[string]interface{}{}
					data["roomName"] = roomName
					data["turnNumber"] = i
					r := Response{
						Kind: inQueue,
						Data: data,
					}
					p.Comm.Emit(groupAssignment, r)
				}

				// Create the response
				data := map[string]interface{}{}
				data["message"] = fmt.Sprintf("Welcome to %s", roomName)
				r := Response{
					Kind: roomMessage,
					Data: data,
				}
				server.BroadcastTo(roomName, roomMessage, r)
			}
		}
	}
}

func (s customServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Credentials", "true")
	origin := r.Header.Get("Origin")
	w.Header().Set("Access-Control-Allow-Origin", origin)
	s.Server.ServeHTTP(w, r)
}

type customServer struct {
	Server *socketio.Server
}

// StartServer ...
func StartServer(c *cli.Context) {
	games, err := ReadGameFiles("./games")
	for key, game := range games {
		log.Println("Loaded:", key, "from", game.FileName)
	}
	if err != nil {
		log.Fatal(err)
	}

	server, err := socketio.NewServer(nil)
	s := customServer{
		Server: server,
	}

	if err != nil {
		log.Fatal(err)
	}

	info := GamesInfo{
		RoomMap: utils.NewConcurrentStringMap(),
		TurnMap: utils.NewConcurrentStringIntMap(),
	}
	for key := range games {
		go GroupPlayers(games[key], server, &info)
	}
	server.On("connection", func(so socketio.Socket) {
		// Makes it so that the player joins a room with his/her unique id.
		so.Join(so.Id())
		log.Println("Connection from", so.Id())
		so.On(joinGame, func(req json.RawMessage) {
			m := map[string]string{}
			if err := json.Unmarshal(req, &m); err != nil {
				log.Println("Invalid JSON from", so.Id())
				so.Emit(clientError, errorResponse(clientError, "Invalid JSON"))
			}
			gameID, exists := m["gameId"]
			if !exists {
				log.Println("No game id from", so.Id())
				so.Emit(clientError, errorResponse(clientError, "Must include GameID"))
			}
			log.Println(so.Id(), "attempted to join game", gameID)
			// If the player attempts to connect to a game we first have to make
			// sure that they are joining a game that is registered with our server.
			if g, exists := games[gameID]; exists {
				QueuePlayers(g, domain.Player{
					Comm: so,
				})
			} else {
				log.Println("Invalid GameId from", so.Id())
				so.Emit(clientError, errorResponse(clientError, "Invalid GameID"))
			}
		})
		so.On(makeMove, func(move json.RawMessage) {
			room, exists := info.RoomMap.Get(so.Id())
			if exists {
				m := map[string]interface{}{}
				if err := json.Unmarshal(move, &m); err != nil {
					log.Println("Invalid JSON from", so.Id())
					so.Emit(clientError, errorResponse(clientError, "Invalid JSON"))
				}
				turn, exists := info.TurnMap.Get(room + ":" + so.Id())
				if !exists {
					log.Println("No turn assigned", so.Id())
					so.Emit(serverError, errorResponse(serverError, "No turn assigned"))
				}
				// Overwrites who's turn it is using the turn map assigned at join.
				m["madeBy"] = turn
				m["madeById"] = so.Id()
				r := Response{
					Kind: moveMade,
					Data: m,
				}
				so.BroadcastTo(room, moveMade, r)
			} else {
				log.Println("No room assigned for", so.Id())
				so.Emit(serverError, errorResponse(serverError, "Not in any Room"))
			}
		})
	})

	port := c.String("port")

	http.Handle("/socket.io/", s)
	http.Handle("/", http.FileServer(http.Dir("./asset")))
	log.Println("Serving at localhost:" + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func main() {
	app := cli.NewApp()
	app.Name = "Toto"
	app.Usage = "a server for creating quick prototype websocket based games."
	app.Action = StartServer
	app.Version = Version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "port, p",
			Value: "3000",
			Usage: "The port to run the server on",
		},
	}
	app.Run(os.Args)
}
<commit_msg>:art: :books: Add documentation for customServer and shuffle code around to make it easier to read<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/googollee/go-socket.io"

	"github.com/jesusrmoreno/toto/domain"
	"github.com/jesusrmoreno/toto/implementations"
	"github.com/jesusrmoreno/toto/utils"

	"github.com/BurntSushi/toml"
	"github.com/jesusrmoreno/sad-squid"

	"github.com/codegangsta/cli"
)

// Version ...
var Version = "No Version Specified"

// Events that are exposed to the client
const (
	groupAssignment = "group-assignment"
	roomMessage     = "room-message"
	leaveGroup      = "leave-group"
	joinGroup       = "join-group"
	joinGame        = "join-game"
	getPeers        = "get-peers"
	makeMove        = "make-move"
	moveMade        = "move-made"
	inQueue         = "in-queue"

	serverError = "server-error"
	clientError = "client-error"
)

// Response ...
type Response struct {
	Kind string                 `json:"kind"`
	Data map[string]interface{} `json:"data"`
}

// Method for creating errors more quickly.
func errorResponse(kind, err string) Response {
	d := map[string]interface{}{}
	d["error"] = err
	r := Response{
		Kind: kind,
		Data: d,
	}
	return r
}

// GamesInfo serves to store the metadata for different games
type GamesInfo struct {
	// These must be thread safe so we use the ConcurrentMap types
	TurnMap *utils.ConcurrentStringIntMap
	// Maps the player id to the room
	RoomMap *utils.ConcurrentStringMap
}

// ReadGameFiles reads the provided directory for files that conform to the
// game struct definition, these must be json files, and loads them into our
// game map.
func ReadGameFiles(gameDir string) (domain.GameMap, error) {
	files, err := filepath.Glob(gameDir + "/*.toml")
	gm := domain.GameMap{}
	if err != nil {
		return nil, err
	}
	for _, f := range files {
		raw, err := os.Open(f)
		defer raw.Close()
		if err != nil {
			return nil, err
		}
		r := io.Reader(raw)
		dummy := domain.Game{}
		if meta, err := toml.DecodeReader(r, &dummy); err != nil {
			log.Println(meta)
			return nil, errors.New("Invalid configuration in file: " + f)
		}
		g := domain.Game{
			Players: dummy.Players,
			Title:   dummy.Title,
			UUID:    dummy.UUID,
			Lobby: impl.Lobby{
				// We implement our lobby as a buffered channel that takes the number of
				// players specified in the config file for the game and makes that the
				// cap.
				Queue: make(chan domain.Player, dummy.Players),
			},
		}
		g.FileName = f
		if _, exists := gm[g.UUID]; exists {
			return nil, errors.New("uniqueKey conflict between: " + f + " and " + gm[g.UUID].FileName)
		}
		gm[g.UUID] = g
	}
	return gm, nil
}

// QueuePlayers adds players to the game's lobby to wait for a partner.
// Players are queued on a first come first serve basis.
func QueuePlayers(g domain.Game, p domain.Player) {
	data := map[string]interface{}{}
	data["message"] = "You are in the queue for game: " + g.Title
	r := Response{
		Kind: inQueue,
		Data: data,
	}
	p.Comm.Emit(inQueue, r)
	pq := g.Lobby
	pq.AddToQueue(p)
}

// GroupPlayers creates groups of players of size PlayerSize as defined in the
// game files.
func GroupPlayers(g domain.Game, server *socketio.Server, gi *GamesInfo) {
	pq := g.Lobby
	n := g.Players
	for {
		select {
		default:
			if g.Lobby.Size() >= n {
				team := []domain.Player{}
				// Generate a room id
				roomName := squid.GenerateSimpleID()
				for i := 0; i < n; i++ {
					p := pq.PopFromQueue()
					pID := p.Comm.Id()
					team = append(team, p)

					// Set the metadata
					gi.RoomMap.Set(pID, roomName)
					gi.TurnMap.Set(roomName+":"+pID, i)

					// Place the player in the created room.
					p.Comm.Join(roomName)

					// Create the response
					data := map[string]interface{}{}
					data["roomName"] = roomName
					data["turnNumber"] = i
					r := Response{
						Kind: inQueue,
						Data: data,
					}
					p.Comm.Emit(groupAssignment, r)
				}

				// Create the response
				data := map[string]interface{}{}
				data["message"] = fmt.Sprintf("Welcome to %s", roomName)
				r := Response{
					Kind: roomMessage,
					Data: data,
				}
				server.BroadcastTo(roomName, roomMessage, r)
			}
		}
	}
}

// Custom server is used to add cross-origin request capabilities to the socket
// server. It wraps the socketio.Server
type customServer struct {
	Server *socketio.Server
}

// ServeHTTP is implemented to add the needed header for CORS in socketio.
// This must be named ServeHTTP and take the
// (http.ResponseWriter, r *http.Request) to satisfy the http.Handler interface
func (s customServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Credentials", "true")
	origin := r.Header.Get("Origin")
	w.Header().Set("Access-Control-Allow-Origin", origin)
	s.Server.ServeHTTP(w, r)
}

// StartServer ...
func StartServer(c *cli.Context) {
	games, err := ReadGameFiles("./games")
	for key, game := range games {
		log.Println("Loaded:", key, "from", game.FileName)
	}
	if err != nil {
		log.Fatal(err)
	}

	server, err := socketio.NewServer(nil)
	s := customServer{
		Server: server,
	}

	if err != nil {
		log.Fatal(err)
	}

	info := GamesInfo{
		RoomMap: utils.NewConcurrentStringMap(),
		TurnMap: utils.NewConcurrentStringIntMap(),
	}
	for key := range games {
		go GroupPlayers(games[key], server, &info)
	}
	server.On("connection", func(so socketio.Socket) {
		// Makes it so that the player joins a room with his/her unique id.
		so.Join(so.Id())
		log.Println("Connection from", so.Id())
		so.On(joinGame, func(req json.RawMessage) {
			m := map[string]string{}
			if err := json.Unmarshal(req, &m); err != nil {
				log.Println("Invalid JSON from", so.Id())
				so.Emit(clientError, errorResponse(clientError, "Invalid JSON"))
			}
			gameID, exists := m["gameId"]
			if !exists {
				log.Println("No game id from", so.Id())
				so.Emit(clientError, errorResponse(clientError, "Must include GameID"))
			}
			log.Println(so.Id(), "attempted to join game", gameID)
			// If the player attempts to connect to a game we first have to make
			// sure that they are joining a game that is registered with our server.
			if g, exists := games[gameID]; exists {
				QueuePlayers(g, domain.Player{
					Comm: so,
				})
			} else {
				log.Println("Invalid GameId from", so.Id())
				so.Emit(clientError, errorResponse(clientError, "Invalid GameID"))
			}
		})
		so.On(makeMove, func(move json.RawMessage) {
			room, exists := info.RoomMap.Get(so.Id())
			if exists {
				m := map[string]interface{}{}
				if err := json.Unmarshal(move, &m); err != nil {
					log.Println("Invalid JSON from", so.Id())
					so.Emit(clientError, errorResponse(clientError, "Invalid JSON"))
				}
				turn, exists := info.TurnMap.Get(room + ":" + so.Id())
				if !exists {
					log.Println("No turn assigned", so.Id())
					so.Emit(serverError, errorResponse(serverError, "No turn assigned"))
				}
				// Overwrites who's turn it is using the turn map assigned at join.
				m["madeBy"] = turn
				m["madeById"] = so.Id()
				r := Response{
					Kind: moveMade,
					Data: m,
				}
				so.BroadcastTo(room, moveMade, r)
			} else {
				log.Println("No room assigned for", so.Id())
				so.Emit(serverError, errorResponse(serverError, "Not in any Room"))
			}
		})
	})

	port := c.String("port")

	http.Handle("/socket.io/", s)
	http.Handle("/", http.FileServer(http.Dir("./asset")))
	log.Println("Serving at localhost:" + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func main() {
	app := cli.NewApp()
	app.Name = "Toto"
	app.Usage = "a server for creating quick prototype websocket based games."
	app.Action = StartServer
	app.Version = Version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "port, p",
			Value: "3000",
			Usage: "The port to run the server on",
		},
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"

	"github.com/russross/blackfriday"
)

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

type mode int

const (
	mNormal mode = iota
	mIns
	mDel
	mSub
	mComment
	mHighligh
)

var ops = map[mode]byte{
	mNormal:   '{',
	mIns:      '+',
	mDel:      '-',
	mSub:      '~',
	mComment:  '<',
	mHighligh: '=',
}

type context struct {
	mode         mode
	insTagged    bool
	insMultiline bool
}

func isOp(ctx context, c byte) bool {
	if c == ops[ctx.mode] {
		return true
	}
	if ctx.mode == mIns && !ctx.insTagged {
		return c != '\n' && c != '\r'
	}
	return false
}

// Critic converts critic markup into HTML
func Critic(w io.Writer, r io.Reader) (int, error) {
	rbuf := make([]byte, 16) // actual buffer
	buf := rbuf[2:]          // buf used for reading
	read := 0                // total bytes read
	bi := 2                  // index of 1st byte of rbuf which is a data
	ctx := context{mNormal, false, false}

	// bi allows to keep some bytes from an iteration to an other
main: // main iteration (1 loop = 1 read)
	for {
		ri, errr := r.Read(buf)
		read += ri
		if ri == 0 && errr != nil {
			if bi < 2 {
				// there are some bytes saved from the last iteration
				if _, err := w.Write(rbuf[bi:2]); err != nil {
					return read, err
				}
			}
			if errr != io.EOF {
				return read, errr
			}
			return read, nil
		}
		data := rbuf[bi : 2+ri]
		offset := 0

	sub: // iteration on the data read
		for offset < len(data) {
			i := offset
			// copy non-special chars
			for offset < len(data) && !isOp(ctx, data[offset]) {
				offset++
			}
			if _, err := w.Write(data[i:offset]); err != nil {
				return read, err
			}
			if ctx.mode == mIns && offset > i {
				ctx.insMultiline = true
			}
			if offset >= len(data) {
				bi = 2
				continue main
			}
			// if there are not enough chars to make op, save them for later
			// (actually there is an op of 2 chars only (`~>`) but it can't
			// be used at the EOF because it needs to be followed by `~~}`,
			// so we can store it for the next iteration and risk to not
			// handle it as an op if reaching EOF on the next read)
			if offset > len(data)-2 {
				rbuf[1] = data[offset]
				bi = 1
				continue main
			}
			if offset > len(data)-3 {
				rbuf[0] = data[offset]
				rbuf[1] = data[offset+1]
				bi = 0
				continue main
			}
			// there are more than 3 chars and it could be an op
			switch string(data[offset : offset+3]) {
			case "{++":
				ctx.mode = mIns
				ctx.insTagged = false
				ctx.insMultiline = false
				// the  tag will be writen after having read all
				// `\n` following the `{++` tag.
				offset += 3
			case "++}":
				var s string
				if !ctx.insTagged {
					if ctx.insMultiline {
						s = " \n"
					} else {
						s = " "
					}
				} else {
					s = ""
				}
				ctx.mode = mNormal
				ctx.insTagged = false
				ctx.insMultiline = false
				if _, err := w.Write([]byte(s)); err != nil {
					return read, err
				}
				offset += 3
			case "{--":
				ctx.mode = mDel
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
			case "--}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
			case "{~~":
				ctx.mode = mSub
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
			case "~~}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
			case "{==":
				ctx.mode = mHighligh
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
			case "==}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
			case "{>>":
				ctx.mode = mComment
				if _, err := w.Write([]byte(`<span class="critic comment">`)); err != nil {
					return read, err
				}
				offset += 3
			case "<<}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte(`</span>`)); err != nil {
					return read, err
				}
				offset += 3
			default:
				if ctx.mode == mIns && !ctx.insTagged {
					if _, err := w.Write([]byte("")); err != nil {
						return read, err
					}
					ctx.insTagged = true
				}
				if ctx.mode == mSub && string(data[offset:offset+2]) == "~>" {
					if _, err := w.Write([]byte(`</del><ins>`)); err != nil {
						return read, err
					}
					offset += 2
					continue sub
				}
				if _, err := w.Write(data[offset : offset+1]); err != nil {
					return read, err
				}
				offset++
				continue sub
			}
		}
	}
}

func ex1(ext int) {
	critic := `lacus{++ est++} Pra{e}sent.`
	exp := `
lacus est Pra{e}sent.

`
	md := bytes.NewBuffer(make([]byte, 0))
	_, err := Critic(md, bytes.NewBufferString(critic))
	if err != nil {
		fmt.Printf("failed: %s\n", err.Error())
		return
	}
	readb := blackfriday.MarkdownHtml(md.Bytes(), ext)
	real := string(readb)
	fmt.Printf("critic  : ---%s---\n", critic)
	fmt.Printf("md      : ---%s---\n", md)
	// fmt.Printf("real    : ---%v---\n", real[:len(real)-1])
	// fmt.Printf("expected: ---%v---\n", exp[:len(exp)-1])
	fmt.Printf("\n%v\n", real == exp)
}

func ex2(ext int) {
	critic := `Don't go around saying{-- to people that--} the world owes you
a living. The world owes you nothing. It was here first. {~~One~>Only one~~}
thing is impossible for God: To find {++any++} sense in any copyright law
on the planet. {==Truth is stranger than fiction==}{>>strange but true<<},
but it is because Fiction is obliged to stick to possibilities; Truth isn't.`
	exp := `
Don't go around saying to people that the world owes you
a living. The world owes you nothing. It was here first. OneOnly one
thing is impossible for God: To find any sense in any copyright law
on the planet. Truth is stranger than fictionstrange but true,
but it is because Fiction is obliged to stick to possibilities; Truth isn't.

`
	md := bytes.NewBuffer(make([]byte, 0))
	_, err := Critic(md, bytes.NewBufferString(critic))
	if err != nil {
		fmt.Printf("failed: %s\n", err.Error())
		return
	}
	readb := blackfriday.MarkdownHtml(md.Bytes(), ext)
	real := string(readb)
	fmt.Printf("critic  : ---%s---\n", critic)
	fmt.Printf("md      : ---%s---\n", md)
	// fmt.Printf("real    : ---%v---\n", real[:len(real)-1])
	// fmt.Printf("expected: ---%v---\n", exp[:len(exp)-1])
	fmt.Printf("\n%v\n", real == exp)
}

func ex3(ext int) {
	critic := `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum at orci magna. Phasellus augue justo, sodales eu pulvinar ac, vulputate eget nulla. Mauris massa sem, tempor sed cursus et, semper tincidunt lacus.{++
++}Praesent sagittis, quam id egestas consequat, nisl orci vehicula libero, quis ultricies nulla magna interdum sem. Maecenas eget orci vitae eros accumsan mollis. Cras mi mi, rutrum id aliquam in, aliquet vitae tellus. Sed neque justo, cursus in commodo eget, facilisis eget nunc. Cras tincidunt auctor varius.`
	exp := `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum at orci magna. Phasellus augue justo, sodales eu pulvinar ac, vulputate eget nulla. Mauris massa sem, tempor sed cursus et, semper tincidunt lacus.

Praesent sagittis, quam id egestas consequat, nisl orci vehicula libero, quis ultricies nulla magna interdum sem. Maecenas eget orci vitae eros accumsan mollis. Cras mi mi, rutrum id aliquam in, aliquet vitae tellus. Sed neque justo, cursus in commodo eget, facilisis eget nunc. Cras tincidunt auctor varius.

`
	md := bytes.NewBuffer(make([]byte, 0))
	_, err := Critic(md, bytes.NewBufferString(critic))
	if err != nil {
		fmt.Printf("failed: %s\n", err.Error())
		return
	}
	readb := blackfriday.MarkdownHtml(md.Bytes(), ext)
	real := string(readb)
	fmt.Printf("critic  : ---%s---\n", critic)
	fmt.Printf("md      : ---%s---\n", md)
	// fmt.Printf("real    : ---%v---\n", real[:len(real)-1])
	// fmt.Printf("expected: ---%v---\n", exp[:len(exp)-1])
	fmt.Printf("\n%v\n", real == exp)
}

func main() {
	ext := blackfriday.CommonExtensions // | blackfriday.EXTENSION_CRITIC
	ex1(ext)
	ex2(ext)
	ex3(ext)
}
<commit_msg>Fix ops now reset bi<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"

	"github.com/russross/blackfriday"
)

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

type mode int

const (
	mNormal mode = iota
	mIns
	mDel
	mSub
	mComment
	mHighligh
)

var ops = map[mode]byte{
	mNormal:   '{',
	mIns:      '+',
	mDel:      '-',
	mSub:      '~',
	mComment:  '<',
	mHighligh: '=',
}

type context struct {
	mode         mode
	insTagged    bool
	insMultiline bool
}

func isOp(ctx context, c byte) bool {
	if c == ops[ctx.mode] {
		return true
	}
	if ctx.mode == mIns && !ctx.insTagged {
		return c != '\n' && c != '\r'
	}
	return false
}

// Critic converts critic markup into HTML
func Critic(w io.Writer, r io.Reader) (int, error) {
	rbuf := make([]byte, 32) // actual buffer
	buf := rbuf[2:]          // buf used for reading
	read := 0                // total bytes read
	bi := 2                  // index of 1st byte of rbuf which is a data
	ctx := context{mNormal, false, false}

	// bi allows to keep some bytes from an iteration to an other
main: // main iteration (1 loop = 1 read)
	for {
		ri, errr := r.Read(buf)
		read += ri
		if ri == 0 && errr != nil {
			if bi < 2 {
				// there are some bytes saved from the last iteration
				if _, err := w.Write(rbuf[bi:2]); err != nil {
					return read, err
				}
			}
			if errr != io.EOF {
				return read, errr
			}
			return read, nil
		}
		data := rbuf[bi : 2+ri]
		offset := 0

	sub: // iteration on the data read
		for offset < len(data) {
			i := offset
			// copy non-special chars
			for offset < len(data) && !isOp(ctx, data[offset]) {
				offset++
			}
			if _, err := w.Write(data[i:offset]); err != nil {
				return read, err
			}
			if ctx.mode == mIns && offset > i {
				ctx.insMultiline = true
			}
			if offset >= len(data) {
				bi = 2
				continue main
			}
			// if there are not enough chars to make op, save them for later
			// (actually there is an op of 2 chars only (`~>`) but it can't
			// be used at the EOF because it needs to be followed by `~~}`,
			// so we can store it for the next iteration and risk to not
			// handle it as an op if reaching EOF on the next read)
			if offset > len(data)-2 {
				rbuf[1] = data[offset]
				bi = 1
				continue main
			}
			if offset > len(data)-3 {
				rbuf[0] = data[offset]
				rbuf[1] = data[offset+1]
				bi = 0
				continue main
			}
			// there are more than 3 chars and it could be an op
			switch string(data[offset : offset+3]) {
			case "{++":
				ctx.mode = mIns
				ctx.insTagged = false
				ctx.insMultiline = false
				// the  tag will be writen after having read all
				// `\n` following the `{++` tag.
				offset += 3
				bi = 2
			case "++}":
				var s string
				if !ctx.insTagged {
					if ctx.insMultiline {
						s = " \n"
					} else {
						s = " "
					}
				} else {
					s = ""
				}
				ctx.mode = mNormal
				ctx.insTagged = false
				ctx.insMultiline = false
				if _, err := w.Write([]byte(s)); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "{--":
				ctx.mode = mDel
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "--}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "{~~":
				ctx.mode = mSub
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "~~}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "{==":
				ctx.mode = mHighligh
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "==}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte("")); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "{>>":
				ctx.mode = mComment
				if _, err := w.Write([]byte(`<span class="critic comment">`)); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			case "<<}":
				ctx.mode = mNormal
				if _, err := w.Write([]byte(`</span>`)); err != nil {
					return read, err
				}
				offset += 3
				bi = 2
			default:
				if ctx.mode == mIns && !ctx.insTagged {
					if _, err := w.Write([]byte("")); err != nil {
						return read, err
					}
					ctx.insTagged = true
				}
				if ctx.mode == mSub && string(data[offset:offset+2]) == "~>" {
					if _, err := w.Write([]byte(`</del><ins>`)); err != nil {
						return read, err
					}
					offset += 2
					bi = 2
					continue sub
				}
				if _, err := w.Write(data[offset : offset+1]); err != nil {
					return read, err
				}
				offset++
				bi = 2
				continue sub
			}
		}
	}
}

func ex(num int, critic string, exp string) {
	md := bytes.NewBuffer(make([]byte, 0))
	_, err := Critic(md, bytes.NewBufferString(critic))
	if err != nil {
		fmt.Printf("failed: %s\n", err.Error())
		return
	}
	readb := blackfriday.MarkdownHtml(md.Bytes(), blackfriday.CommonExtensions)
	real := string(readb)
	ok := real == exp
	if !ok {
		fmt.Printf("[%d] critic  : ---%s---\n", num, critic)
		fmt.Printf("[%d] md      : ---%s---\n", num, md)
		fmt.Printf("[%d] real    : ---%v---\n", num, real[:len(real)-1])
		fmt.Printf("[%d] expected: ---%v---\n", num, exp[:len(exp)-1])
	}
	fmt.Printf("[%d] %v\n", num, ok)
}

func main() {
	ex(
		1,
		`lacus{++ est++} Pra{e}sent.`,
		`
lacus est Pra{e}sent.

`,
	)

	ex(
		2,
		`Don't go around saying{-- to people that--} the world owes you
a living. The world owes you nothing. It was here first. {~~One~>Only one~~}
thing is impossible for God: To find {++any++} sense in any copyright law
on the planet. {==Truth is stranger than fiction==}{>>strange but true<<},
but it is because Fiction is obliged to stick to possibilities; Truth isn't.`,
		`
Don't go around saying to people that the world owes you
a living. The world owes you nothing. It was here first. OneOnly one
thing is impossible for God: To find any sense in any copyright law
on the planet. Truth is stranger than fictionstrange but true,
but it is because Fiction is obliged to stick to possibilities; Truth isn't.

`,
	)

	// 	ex(
	// 		3,
	// 		`Lorem ipsum dolor sit amet, consectetur adipiscing elit.{++
	// ++}Vestibulum at orci magna. Phasellus augue justo, sodales eu pulvinar ac,
	// vulputate eget nulla. Mauris massa sem, tempor sed cursus et, semper tincidunt
	// lacus. Praesent sagittis, quam id egestas consequat, nisl orci vehicula{--
	// --}libero, quis ultricies nulla magna interdum sem. Maecenas eget orci vitae
	// eros accumsan mollis. Cras mi mi, rutrum id aliquam in, {~~aliquet vitae~>
	// ~~}tellus. Sed neque justo, cursus in commodo eget, facilisis eget nunc.
	// Cras tincidunt auctor varius.`,
	// 		`
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
	//
	// Vestibulum at orci magna. Phasellus augue justo, sodales eu pulvinar ac,
	// vulputate eget nulla. Mauris massa sem, tempor sed cursus et, semper tincidunt
	// lacus. Praesent sagittis, quam id egestas consequat, nisl orci vehicula libero, quis ultricies nulla magna interdum sem. Maecenas eget orci vitae
	// eros accumsan mollis. Cras mi mi, rutrum id aliquam in, aliquet vitae
	//
	// tellus. Sed neque justo, cursus in commodo eget, facilisis eget nunc.
	// Cras tincidunt auctor varius.
`,
	// 	)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"os"
	"strings"
)

const staticVersion = "v0.0.4+"

var version string

func setupFlag() {
	viper.SetConfigName("config")
	viper.AddConfigPath("/etc/xdg/vv")
	viper.AddConfigPath("$HOME/.config/vv")
	pflag.String("mpd.host", "localhost", "mpd server hostname to connect")
	pflag.String("mpd.port", "6600", "mpd server TCP port to connect")
	pflag.String("mpd.music_directory", "", "set music_directory in mpd.conf value to search album cover image")
	pflag.String("server.port", "8080", "this app serving TCP port")
	pflag.Parse()
	viper.BindPFlags(pflag.CommandLine)
}

func getMusicDirectory(confpath string) (string, error) {
	f, err := os.Open(confpath)
	if err != nil {
		return "", err
	}
	defer f.Close()
	sc := bufio.NewScanner(f)
	for i := 1; sc.Scan(); i++ {
		if err := sc.Err(); err != nil {
			return "", err
		}
		l := sc.Text()
		if strings.HasPrefix(l, "music_directory") {
			q := strings.TrimSpace(strings.TrimPrefix(l, "music_directory"))
			if strings.HasPrefix(q, "\"") && strings.HasSuffix(q, "\"") {
				return strings.Trim(q, "\""), nil
			}
		}
	}
	return "", nil
}

//go:generate go-bindata assets
func main() {
	setupFlag()
	err := viper.ReadInConfig()
	if err != nil {
		if _, notfound := err.(viper.ConfigFileNotFoundError); !notfound {
			fmt.Printf("faied to load config file: %s\n", err)
			os.Exit(1)
		}
	}
	musicDirectory := viper.GetString("mpd.music_directory")
	if len(musicDirectory) == 0 && viper.GetString("mpd.host") == "localhost" {
		dir, err := getMusicDirectory("/etc/mpd.conf")
		if err == nil {
			musicDirectory = dir
		}
	}
	addr := viper.GetString("mpd.host") + ":" + viper.GetString("mpd.port")
	player, err := Dial("tcp", addr, "", musicDirectory)
	defer player.Close()
	if err != nil {
		fmt.Printf("faied to connect/initialize mpd: %s\n", err)
		os.Exit(1)
	}
	Serve(player, musicDirectory, viper.GetString("server.port"))
}
<commit_msg>v0.1.0+<commit_after>package main

import (
	"bufio"
	"fmt"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"os"
	"strings"
)

const staticVersion = "v0.1.0+"

var version string

func setupFlag() {
	viper.SetConfigName("config")
	viper.AddConfigPath("/etc/xdg/vv")
	viper.AddConfigPath("$HOME/.config/vv")
	pflag.String("mpd.host", "localhost", "mpd server hostname to connect")
	pflag.String("mpd.port", "6600", "mpd server TCP port to connect")
	pflag.String("mpd.music_directory", "", "set music_directory in mpd.conf value to search album cover image")
	pflag.String("server.port", "8080", "this app serving TCP port")
	pflag.Parse()
	viper.BindPFlags(pflag.CommandLine)
}

func getMusicDirectory(confpath string) (string, error) {
	f, err := os.Open(confpath)
	if err != nil {
		return "", err
	}
	defer f.Close()
	sc := bufio.NewScanner(f)
	for i := 1; sc.Scan(); i++ {
		if err := sc.Err(); err != nil {
			return "", err
		}
		l := sc.Text()
		if strings.HasPrefix(l, "music_directory") {
			q := strings.TrimSpace(strings.TrimPrefix(l, "music_directory"))
			if strings.HasPrefix(q, "\"") && strings.HasSuffix(q, "\"") {
				return strings.Trim(q, "\""), nil
			}
		}
	}
	return "", nil
}

//go:generate go-bindata assets
func main() {
	setupFlag()
	err := viper.ReadInConfig()
	if err != nil {
		if _, notfound := err.(viper.ConfigFileNotFoundError); !notfound {
			fmt.Printf("faied to load config file: %s\n", err)
			os.Exit(1)
		}
	}
	musicDirectory := viper.GetString("mpd.music_directory")
	if len(musicDirectory) == 0 && viper.GetString("mpd.host") == "localhost" {
		dir, err := getMusicDirectory("/etc/mpd.conf")
		if err == nil {
			musicDirectory = dir
		}
	}
	addr := viper.GetString("mpd.host") + ":" + viper.GetString("mpd.port")
	player, err := Dial("tcp", addr, "", musicDirectory)
	defer player.Close()
	if err != nil {
		fmt.Printf("faied to connect/initialize mpd: %s\n", err)
		os.Exit(1)
	}
	Serve(player, musicDirectory, viper.GetString("server.port"))
}
<|endoftext|>