<commit_before>package main

import (
	"flag"
	"fmt"
	"os/exec"
	"strings"

	"net/http"

	"encoding/json"

	"bytes"
	"os"

	"github.com/alexellis/faas/gateway/requests"
)

func main() {
	// var handler string
	var handler string
	var image string

	var action string
	var functionName string
	var gateway string
	var fprocess string
	var language string
	var replace bool

	flag.StringVar(&handler, "handler", "", "handler for function, i.e. handler.js")
	flag.StringVar(&image, "image", "", "Docker image name to build")
	flag.StringVar(&action, "action", "", "either build or deploy")
	flag.StringVar(&functionName, "name", "", "give the name of your deployed function")
	flag.StringVar(&gateway, "gateway", "http://localhost:8080", "gateway URI - i.e. http://localhost:8080")
	flag.StringVar(&fprocess, "fprocess", "", "fprocess to be run by the watchdog")
	flag.StringVar(&language, "lang", "node", "programming language template, default is: node")
	flag.BoolVar(&replace, "replace", true, "replace any existing function")

	flag.Parse()

	if len(action) == 0 {
		fmt.Println("give either -action= build or deploy")
		return
	}

	if action == "build" {
		if len(image) == 0 {
			fmt.Println("Give a valid -image name for your Docker image.")
			return
		}
		if len(handler) == 0 {
			fmt.Println("Please give the full path to your function's handler.")
			return
		}
		if len(functionName) == 0 {
			fmt.Println("Please give the deployed -name of your function")
			return
		}

		tempPath := createBuildTemplate(functionName, handler, language)

		fmt.Printf("Building: %s with Docker. Please wait..\n", image)

		builder := strings.Split(fmt.Sprintf("docker build -t %s .", image), " ")
		if len(os.Getenv("http_proxy")) > 0 || len(os.Getenv("http_proxy")) > 0 {
			builder = strings.Split(fmt.Sprintf("docker build --build-arg http_proxy=%s --build-arg https_proxy=%s -t %s .", os.Getenv("http_proxy"), os.Getenv("https_proxy"), image), " ")
		}

		fmt.Println(strings.Join(builder, " "))
		targetCmd := exec.Command(builder[0], builder[1:]...)
		targetCmd.Dir = tempPath
		cmdOutput, cmdErr := targetCmd.CombinedOutput()

		if cmdErr != nil {
			fmt.Printf("Error: %s\n" + cmdErr.Error())
		}

		fmt.Println(string(cmdOutput))

		fmt.Printf("Image: %s built.\n", image)
	} else if action == "deploy" {
		if len(image) == 0 {
			fmt.Println("Give an image name to be deployed.")
			return
		}
		if len(functionName) == 0 {
			fmt.Println("Give a -name for your function as it will be deployed on FaaS")
			return
		}

		// Need to alter Gateway to allow nil/empty string as fprocess, to avoid this repetition.
		fprocessTemplate := "node index.js"
		if len(fprocess) > 0 {
			fprocessTemplate = fprocess
		}
		if language == "python" {
			fprocessTemplate = "python index.py"
		}

		// TODO: Extract to function
		if replace {
			deleteFunction(gateway, functionName)
		}

		req := requests.CreateFunctionRequest{
			EnvProcess: fprocessTemplate,
			Image:      image,
			Network:    "func_functions",
			Service:    functionName,
		}

		reqBytes, _ := json.Marshal(&req)
		reader := bytes.NewReader(reqBytes)
		res, err := http.Post(gateway+"/system/functions", "application/json", reader)
		if err != nil {
			fmt.Println("Is FaaS deployed? Do you need to specify the -gateway flag?")
			fmt.Println(err)
			return
		}
		fmt.Println(res.Status)
		deployedUrl := fmt.Sprintf("URL: %s/function/%s\n", gateway, functionName)
		fmt.Println(deployedUrl)

	}
}

func deleteFunction(gateway string, functionName string) {
	delReq := requests.DeleteFunctionRequest{FunctionName: functionName}
	reqBytes, _ := json.Marshal(&delReq)
	reader := bytes.NewReader(reqBytes)

	c := http.Client{}
	req, _ := http.NewRequest("DELETE", gateway+"/system/functions", reader)
	req.Header.Set("Content-Type", "application/json")
	delRes, delErr := c.Do(req)

	if delErr != nil {
		fmt.Println(delErr.Error())
	}
	switch delRes.StatusCode {
	case 200:
		fmt.Println("Removing old service.")
	case 404:
		fmt.Println("No existing service to remove")

	}
}

// createBuildTemplate creates temporary build folder to perform a Docker build with Node template
func createBuildTemplate(functionName string, handler string, language string) string {
	tempPath := fmt.Sprintf("./build/%s/", functionName)
	fmt.Printf("Creating temporary folder: %s\n", tempPath)
	exec.Command("mkdir", "-p", tempPath).Run()

	if language == "node" {
		exec.Command("cp", "./template/node/index.js", tempPath).Run()
		exec.Command("cp", "./template/node/Dockerfile", tempPath).Run()
		exec.Command("cp", "./template/node/package.json", tempPath).Run()
	} else if language == "python" {
		exec.Command("cp", "./template/python/index.py", tempPath).Run()
		exec.Command("cp", "./template/python/Dockerfile", tempPath).Run()
		exec.Command("cp", "./template/python/requirements.txt", tempPath).Run()
	}

	exec.Command("mkdir", "-p", tempPath+"/function").Run()
	exec.Command("cp", "-rf", handler+"/", tempPath+"/function/").Run()
	return tempPath
}
<commit_msg>Pipe Docker to stdin/stdout via Finnian's PR<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package main

import (
	"flag"
	"fmt"
	"os/exec"
	"strings"

	"net/http"

	"encoding/json"

	"bytes"
	"os"

	"github.com/alexellis/faas/gateway/requests"
)

func main() {
	// var handler string
	var handler string
	var image string

	var action string
	var functionName string
	var gateway string
	var fprocess string
	var language string
	var replace bool

	flag.StringVar(&handler, "handler", "", "handler for function, i.e. handler.js")
	flag.StringVar(&image, "image", "", "Docker image name to build")
	flag.StringVar(&action, "action", "", "either build or deploy")
	flag.StringVar(&functionName, "name", "", "give the name of your deployed function")
	flag.StringVar(&gateway, "gateway", "http://localhost:8080", "gateway URI - i.e. http://localhost:8080")
	flag.StringVar(&fprocess, "fprocess", "", "fprocess to be run by the watchdog")
	flag.StringVar(&language, "lang", "node", "programming language template, default is: node")
	flag.BoolVar(&replace, "replace", true, "replace any existing function")

	flag.Parse()

	if len(action) == 0 {
		fmt.Println("give either -action= build or deploy")
		return
	}

	if action == "build" {
		if len(image) == 0 {
			fmt.Println("Give a valid -image name for your Docker image.")
			return
		}
		if len(handler) == 0 {
			fmt.Println("Please give the full path to your function's handler.")
			return
		}
		if len(functionName) == 0 {
			fmt.Println("Please give the deployed -name of your function")
			return
		}

		tempPath := createBuildTemplate(functionName, handler, language)

		fmt.Printf("Building: %s with Docker. Please wait..\n", image)

		builder := strings.Split(fmt.Sprintf("docker build -t %s .", image), " ")
		if len(os.Getenv("http_proxy")) > 0 || len(os.Getenv("http_proxy")) > 0 {
			builder = strings.Split(fmt.Sprintf("docker build --build-arg http_proxy=%s --build-arg https_proxy=%s -t %s .", os.Getenv("http_proxy"), os.Getenv("https_proxy"), image), " ")
		}

		fmt.Println(strings.Join(builder, " "))
		targetCmd := exec.Command(builder[0], builder[1:]...)
		targetCmd.Dir = tempPath
		targetCmd.Stdout = os.Stdout
		targetCmd.Stderr = os.Stderr
		//cmdOutput, cmdErr := targetCmd.CombinedOutput()

		//if cmdErr != nil {
		//	fmt.Printf("Error: %s\n" + cmdErr.Error())
		//}
		targetCmd.Start()
		targetCmd.Wait()

		//fmt.Println(string(cmdOutput))

		fmt.Printf("Image: %s built.\n", image)
	} else if action == "deploy" {
		if len(image) == 0 {
			fmt.Println("Give an image name to be deployed.")
			return
		}
		if len(functionName) == 0 {
			fmt.Println("Give a -name for your function as it will be deployed on FaaS")
			return
		}

		// Need to alter Gateway to allow nil/empty string as fprocess, to avoid this repetition.
		fprocessTemplate := "node index.js"
		if len(fprocess) > 0 {
			fprocessTemplate = fprocess
		}
		if language == "python" {
			fprocessTemplate = "python index.py"
		}

		// TODO: Extract to function
		if replace {
			deleteFunction(gateway, functionName)
		}

		req := requests.CreateFunctionRequest{
			EnvProcess: fprocessTemplate,
			Image:      image,
			Network:    "func_functions",
			Service:    functionName,
		}

		reqBytes, _ := json.Marshal(&req)
		reader := bytes.NewReader(reqBytes)
		res, err := http.Post(gateway+"/system/functions", "application/json", reader)
		if err != nil {
			fmt.Println("Is FaaS deployed? Do you need to specify the -gateway flag?")
			fmt.Println(err)
			return
		}
		fmt.Println(res.Status)
		deployedUrl := fmt.Sprintf("URL: %s/function/%s\n", gateway, functionName)
		fmt.Println(deployedUrl)

	}
}

func deleteFunction(gateway string, functionName string) {
	delReq := requests.DeleteFunctionRequest{FunctionName: functionName}
	reqBytes, _ := json.Marshal(&delReq)
	reader := bytes.NewReader(reqBytes)

	c := http.Client{}
	req, _ := http.NewRequest("DELETE", gateway+"/system/functions", reader)
	req.Header.Set("Content-Type", "application/json")
	delRes, delErr := c.Do(req)

	if delErr != nil {
		fmt.Println(delErr.Error())
	}
	switch delRes.StatusCode {
	case 200:
		fmt.Println("Removing old service.")
	case 404:
		fmt.Println("No existing service to remove")

	}
}

// createBuildTemplate creates temporary build folder to perform a Docker build with Node template
func createBuildTemplate(functionName string, handler string, language string) string {
	tempPath := fmt.Sprintf("./build/%s/", functionName)
	fmt.Printf("Creating temporary folder: %s\n", tempPath)
	exec.Command("mkdir", "-p", tempPath).Run()

	if language == "node" {
		exec.Command("cp", "./template/node/index.js", tempPath).Run()
		exec.Command("cp", "./template/node/Dockerfile", tempPath).Run()
		exec.Command("cp", "./template/node/package.json", tempPath).Run()
	} else if language == "python" {
		exec.Command("cp", "./template/python/index.py", tempPath).Run()
		exec.Command("cp", "./template/python/Dockerfile", tempPath).Run()
		exec.Command("cp", "./template/python/requirements.txt", tempPath).Run()
	}

	exec.Command("mkdir", "-p", tempPath+"/function").Run()
	exec.Command("cp", "-rf", handler+"/", tempPath+"/function/").Run()
	return tempPath
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

// App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function
type App struct {
	// The name of the program. Defaults to os.Args[0]
	Name string
	// Full name of command for help, defaults to Name
	HelpName string
	// Description of the program.
	Usage string
	// Description of the program argument format.
	ArgsUsage string
	// Version of the program
	Version string
	// List of commands to execute
	Commands []Command
	// List of flags to parse
	Flags []Flag
	// Boolean to enable bash completion commands
	EnableBashCompletion bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide built-in version flag
	HideVersion bool
	// An action to execute when the bash-completion flag is set
	BashComplete func(context *Context)
	// An action to execute before any subcommands are run, but after the context is ready
	// If a non-nil error is returned, no subcommands are run
	Before func(context *Context) error
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run even if Action() panics
	After func(context *Context) error
	// The action to execute when no subcommands are specified
	Action func(context *Context)
	// Execute this function if the proper command cannot be found
	CommandNotFound func(context *Context, command string)
	// Compilation date
	Compiled time.Time
	// List of all authors who contributed
	Authors []Author
	// Copyright of the binary if any
	Copyright string
	// Name of Author (Note: Use App.Authors, this is deprecated)
	Author string
	// Email of Author (Note: Use App.Authors, this is deprecated)
	Email string
	// Writer writer to write output to
	Writer io.Writer
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
	info, err := os.Stat(os.Args[0])
	if err != nil {
		return time.Now()
	}
	return info.ModTime()
}

// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
	return &App{
		Name:         os.Args[0],
		HelpName:     os.Args[0],
		Usage:        "A new cli application",
		Version:      "0.0.0",
		BashComplete: DefaultAppComplete,
		Action:       helpCommand.Action,
		Compiled:     compileTime(),
		Writer:       os.Stdout,
	}
}

// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
	if a.Author != "" || a.Email != "" {
		a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
	}

	newCmds := []Command{}
	for _, c := range a.Commands {
		if c.HelpName == "" {
			c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
		}
		newCmds = append(newCmds, c)
	}
	a.Commands = newCmds

	// append help to commands
	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
		a.Commands = append(a.Commands, helpCommand)
		if (HelpFlag != BoolFlag{}) {
			a.appendFlag(HelpFlag)
		}
	}

	//append version/help flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	if !a.HideVersion {
		a.appendFlag(VersionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(arguments[1:])
	nerr := normalizeFlags(a.Flags, set)
	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		context := NewContext(a, set, nil)
		ShowAppHelp(context)
		return nerr
	}
	context := NewContext(a, set, nil)

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowAppHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if !a.HideHelp && checkHelp(context) {
		ShowAppHelp(context)
		return nil
	}

	if !a.HideVersion && checkVersion(context) {
		ShowVersion(context)
		return nil
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)
	return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
	if err := a.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
	// append help to commands
	if len(a.Commands) > 0 {
		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
			a.Commands = append(a.Commands, helpCommand)
			if (HelpFlag != BoolFlag{}) {
				a.appendFlag(HelpFlag)
			}
		}
	}

	newCmds := []Command{}
	for _, c := range a.Commands {
		if c.HelpName == "" {
			c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
		}
		newCmds = append(newCmds, c)
	}
	a.Commands = newCmds

	// append flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(ctx.Args().Tail())
	nerr := normalizeFlags(a.Flags, set)
	context := NewContext(a, set, ctx)

	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		fmt.Fprintln(a.Writer)
		if len(a.Commands) > 0 {
			ShowSubcommandHelp(context)
		} else {
			ShowCommandHelp(ctx, context.Args().First())
		}
		return nerr
	}

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowSubcommandHelp(context)
		return err
	}

	if checkCompletions(context) {
		return nil
	}

	if len(a.Commands) > 0 {
		if checkSubcommandHelp(context) {
			return nil
		}
	} else {
		if checkCommandHelp(ctx, context.Args().First()) {
			return nil
		}
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)

	return nil
}

// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
	for _, c := range a.Commands {
		if c.HasName(name) {
			return &c
		}
	}

	return nil
}

func (a *App) hasFlag(flag Flag) bool {
	for _, f := range a.Flags {
		if flag == f {
			return true
		}
	}

	return false
}

func (a *App) appendFlag(flag Flag) {
	if !a.hasFlag(flag) {
		a.Flags = append(a.Flags, flag)
	}
}

// Author represents someone who has contributed to a cli project.
type Author struct {
	Name  string // The Authors name
	Email string // The Authors email
}

// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a Author) String() string {
	e := ""
	if a.Email != "" {
		e = "<" + a.Email + "> "
	}

	return fmt.Sprintf("%v %v", a.Name, e)
}
<commit_msg>Run check completion before error checking<commit_after>package cli

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

// App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function
type App struct {
	// The name of the program. Defaults to os.Args[0]
	Name string
	// Full name of command for help, defaults to Name
	HelpName string
	// Description of the program.
	Usage string
	// Description of the program argument format.
	ArgsUsage string
	// Version of the program
	Version string
	// List of commands to execute
	Commands []Command
	// List of flags to parse
	Flags []Flag
	// Boolean to enable bash completion commands
	EnableBashCompletion bool
	// Boolean to hide built-in help command
	HideHelp bool
	// Boolean to hide built-in version flag
	HideVersion bool
	// An action to execute when the bash-completion flag is set
	BashComplete func(context *Context)
	// An action to execute before any subcommands are run, but after the context is ready
	// If a non-nil error is returned, no subcommands are run
	Before func(context *Context) error
	// An action to execute after any subcommands are run, but after the subcommand has finished
	// It is run even if Action() panics
	After func(context *Context) error
	// The action to execute when no subcommands are specified
	Action func(context *Context)
	// Execute this function if the proper command cannot be found
	CommandNotFound func(context *Context, command string)
	// Compilation date
	Compiled time.Time
	// List of all authors who contributed
	Authors []Author
	// Copyright of the binary if any
	Copyright string
	// Name of Author (Note: Use App.Authors, this is deprecated)
	Author string
	// Email of Author (Note: Use App.Authors, this is deprecated)
	Email string
	// Writer writer to write output to
	Writer io.Writer
}

// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
	info, err := os.Stat(os.Args[0])
	if err != nil {
		return time.Now()
	}
	return info.ModTime()
}

// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
	return &App{
		Name:         os.Args[0],
		HelpName:     os.Args[0],
		Usage:        "A new cli application",
		Version:      "0.0.0",
		BashComplete: DefaultAppComplete,
		Action:       helpCommand.Action,
		Compiled:     compileTime(),
		Writer:       os.Stdout,
	}
}

// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
	if a.Author != "" || a.Email != "" {
		a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
	}

	newCmds := []Command{}
	for _, c := range a.Commands {
		if c.HelpName == "" {
			c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
		}
		newCmds = append(newCmds, c)
	}
	a.Commands = newCmds

	// append help to commands
	if a.Command(helpCommand.Name) == nil && !a.HideHelp {
		a.Commands = append(a.Commands, helpCommand)
		if (HelpFlag != BoolFlag{}) {
			a.appendFlag(HelpFlag)
		}
	}

	//append version/help flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	if !a.HideVersion {
		a.appendFlag(VersionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(arguments[1:])
	nerr := normalizeFlags(a.Flags, set)
	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		context := NewContext(a, set, nil)
		ShowAppHelp(context)
		return nerr
	}
	context := NewContext(a, set, nil)

	if checkCompletions(context) {
		return nil
	}

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowAppHelp(context)
		return err
	}

	if !a.HideHelp && checkHelp(context) {
		ShowAppHelp(context)
		return nil
	}

	if !a.HideVersion && checkVersion(context) {
		ShowVersion(context)
		return nil
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)
	return nil
}

// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
	if err := a.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
	// append help to commands
	if len(a.Commands) > 0 {
		if a.Command(helpCommand.Name) == nil && !a.HideHelp {
			a.Commands = append(a.Commands, helpCommand)
			if (HelpFlag != BoolFlag{}) {
				a.appendFlag(HelpFlag)
			}
		}
	}

	newCmds := []Command{}
	for _, c := range a.Commands {
		if c.HelpName == "" {
			c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
		}
		newCmds = append(newCmds, c)
	}
	a.Commands = newCmds

	// append flags
	if a.EnableBashCompletion {
		a.appendFlag(BashCompletionFlag)
	}

	// parse flags
	set := flagSet(a.Name, a.Flags)
	set.SetOutput(ioutil.Discard)
	err = set.Parse(ctx.Args().Tail())
	nerr := normalizeFlags(a.Flags, set)
	context := NewContext(a, set, ctx)

	if nerr != nil {
		fmt.Fprintln(a.Writer, nerr)
		fmt.Fprintln(a.Writer)
		if len(a.Commands) > 0 {
			ShowSubcommandHelp(context)
		} else {
			ShowCommandHelp(ctx, context.Args().First())
		}
		return nerr
	}

	if checkCompletions(context) {
		return nil
	}

	if err != nil {
		fmt.Fprintln(a.Writer, "Incorrect Usage.")
		fmt.Fprintln(a.Writer)
		ShowSubcommandHelp(context)
		return err
	}

	if len(a.Commands) > 0 {
		if checkSubcommandHelp(context) {
			return nil
		}
	} else {
		if checkCommandHelp(ctx, context.Args().First()) {
			return nil
		}
	}

	if a.After != nil {
		defer func() {
			afterErr := a.After(context)
			if afterErr != nil {
				if err != nil {
					err = NewMultiError(err, afterErr)
				} else {
					err = afterErr
				}
			}
		}()
	}

	if a.Before != nil {
		err := a.Before(context)
		if err != nil {
			return err
		}
	}

	args := context.Args()
	if args.Present() {
		name := args.First()
		c := a.Command(name)
		if c != nil {
			return c.Run(context)
		}
	}

	// Run default Action
	a.Action(context)

	return nil
}

// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
	for _, c := range a.Commands {
		if c.HasName(name) {
			return &c
		}
	}

	return nil
}

func (a *App) hasFlag(flag Flag) bool {
	for _, f := range a.Flags {
		if flag == f {
			return true
		}
	}

	return false
}

func (a *App) appendFlag(flag Flag) {
	if !a.hasFlag(flag) {
		a.Flags = append(a.Flags, flag)
	}
}

// Author represents someone who has contributed to a cli project.
type Author struct {
	Name  string // The Authors name
	Email string // The Authors email
}

// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a Author) String() string {
	e := ""
	if a.Email != "" {
		e = "<" + a.Email + "> "
	}

	return fmt.Sprintf("%v %v", a.Name, e)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"strings"
)

type App struct {
	ui   *UI
	nav  *Nav
	quit chan bool
}

func newApp() *App {
	ui := newUI()
	nav := newNav(ui.wins[0].h)
	quit := make(chan bool)

	return &App{
		ui:   ui,
		nav:  nav,
		quit: quit,
	}
}

func waitKey() error {
	// TODO: this should be done with termbox somehow

	c := `echo
	      echo -n 'Press any key to continue'
	      old=$(stty -g)
	      stty raw -echo
	      eval "ignore=\$(dd bs=1 count=1 2> /dev/null)"
	      stty $old
	      echo`

	cmd := exec.Command(gOpts.shell, "-c", c)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("waiting key: %s", err)
	}

	return nil
}

func (app *App) handleInp() {
	clientChan := app.ui.readExpr(app)

	var serverChan chan Expr

	c, err := net.Dial("unix", gSocketPath)
	if err != nil {
		msg := fmt.Sprintf("connecting server: %s", err)
		app.ui.message = msg
		log.Printf(msg)
	} else {
		serverChan = readExpr(c)
	}

	for {
		select {
		case <-app.quit:
			log.Print("bye!")

			if gLastDirPath != "" {
				f, err := os.Create(gLastDirPath)
				if err != nil {
					log.Printf("opening last dir file: %s", err)
				}
				defer f.Close()

				dir := app.nav.currDir()

				_, err = f.WriteString(dir.path)
				if err != nil {
					log.Printf("writing last dir file: %s", err)
				}
			}

			return
		case e := <-clientChan:
			for i := 0; i < e.count; i++ {
				e.expr.eval(app, nil)
			}
			app.ui.draw(app.nav)
		case e := <-serverChan:
			e.eval(app, nil)
			app.ui.draw(app.nav)
		}
	}
}

func (app *App) exportVars() {
	var envFile string
	if f, err := app.nav.currFile(); err == nil {
		envFile = f.Path
	}

	marks := app.nav.currMarks()

	envFiles := strings.Join(marks, ":")

	os.Setenv("f", envFile)
	os.Setenv("fs", envFiles)

	if len(marks) == 0 {
		os.Setenv("fx", envFile)
	} else {
		os.Setenv("fx", envFiles)
	}
}

// This function is used to run a command in shell. Following modes are used:
//
// Prefix  Wait  Async  Stdin/Stdout/Stderr  UI action
// $       No    No     Yes                  Pause and then resume
// !       Yes   No     Yes                  Pause and then resume
// &       No    Yes    No                   Do nothing
//
// Waiting async commands are not used for now.
func (app *App) runShell(s string, args []string, wait bool, async bool) {
	app.exportVars()

	if len(gOpts.ifs) != 0 {
		s = fmt.Sprintf("IFS='%s'; %s", gOpts.ifs, s)
	}

	args = append([]string{"-c", s, "--"}, args...)
	cmd := exec.Command(gOpts.shell, args...)

	if !async {
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		app.ui.pause()
		defer app.ui.resume()
		defer app.nav.renew(app.ui.wins[0].h)
	}

	var err error
	if async {
		err = cmd.Start()
	} else {
		err = cmd.Run()
	}

	if err != nil {
		msg := fmt.Sprintf("running shell: %s", err)
		app.ui.message = msg
		log.Print(msg)
	}

	if wait {
		if err := waitKey(); err != nil {
			msg := fmt.Sprintf("waiting shell: %s", err)
			app.ui.message = msg
			log.Print(msg)
		}
	}
}
<commit_msg>pass client id as id variable for shell commands<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

type App struct {
	ui   *UI
	nav  *Nav
	quit chan bool
}

func newApp() *App {
	ui := newUI()
	nav := newNav(ui.wins[0].h)
	quit := make(chan bool)

	return &App{
		ui:   ui,
		nav:  nav,
		quit: quit,
	}
}

func waitKey() error {
	// TODO: this should be done with termbox somehow

	c := `echo
	      echo -n 'Press any key to continue'
	      old=$(stty -g)
	      stty raw -echo
	      eval "ignore=\$(dd bs=1 count=1 2> /dev/null)"
	      stty $old
	      echo`

	cmd := exec.Command(gOpts.shell, "-c", c)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("waiting key: %s", err)
	}

	return nil
}

func (app *App) handleInp() {
	clientChan := app.ui.readExpr(app)

	var serverChan chan Expr

	c, err := net.Dial("unix", gSocketPath)
	if err != nil {
		msg := fmt.Sprintf("connecting server: %s", err)
		app.ui.message = msg
		log.Printf(msg)
	} else {
		serverChan = readExpr(c)
	}

	for {
		select {
		case <-app.quit:
			log.Print("bye!")

			if gLastDirPath != "" {
				f, err := os.Create(gLastDirPath)
				if err != nil {
					log.Printf("opening last dir file: %s", err)
				}
				defer f.Close()

				dir := app.nav.currDir()

				_, err = f.WriteString(dir.path)
				if err != nil {
					log.Printf("writing last dir file: %s", err)
				}
			}

			return
		case e := <-clientChan:
			for i := 0; i < e.count; i++ {
				e.expr.eval(app, nil)
			}
			app.ui.draw(app.nav)
		case e := <-serverChan:
			e.eval(app, nil)
			app.ui.draw(app.nav)
		}
	}
}

func (app *App) exportVars() {
	var envFile string
	if f, err := app.nav.currFile(); err == nil {
		envFile = f.Path
	}

	marks := app.nav.currMarks()

	envFiles := strings.Join(marks, ":")

	os.Setenv("f", envFile)
	os.Setenv("fs", envFiles)

	if len(marks) == 0 {
		os.Setenv("fx", envFile)
	} else {
		os.Setenv("fx", envFiles)
	}

	os.Setenv("id", strconv.Itoa(gClientId))
}

// This function is used to run a command in shell. Following modes are used:
//
// Prefix  Wait  Async  Stdin/Stdout/Stderr  UI action
// $       No    No     Yes                  Pause and then resume
// !       Yes   No     Yes                  Pause and then resume
// &       No    Yes    No                   Do nothing
//
// Waiting async commands are not used for now.
func (app *App) runShell(s string, args []string, wait bool, async bool) {
	app.exportVars()

	if len(gOpts.ifs) != 0 {
		s = fmt.Sprintf("IFS='%s'; %s", gOpts.ifs, s)
	}

	args = append([]string{"-c", s, "--"}, args...)
	cmd := exec.Command(gOpts.shell, args...)

	if !async {
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		app.ui.pause()
		defer app.ui.resume()
		defer app.nav.renew(app.ui.wins[0].h)
	}

	var err error
	if async {
		err = cmd.Start()
	} else {
		err = cmd.Run()
	}

	if err != nil {
		msg := fmt.Sprintf("running shell: %s", err)
		app.ui.message = msg
		log.Print(msg)
	}

	if wait {
		if err := waitKey(); err != nil {
			msg := fmt.Sprintf("waiting shell: %s", err)
			app.ui.message = msg
			log.Print(msg)
		}
	}
}
<|endoftext|>


<commit_before>package kwiscale

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

	"github.com/gorilla/mux"
)

// handlerManager is used to manage handler production and close
type handlerManager struct {

	// the handler type to produce
	handler reflect.Type

	// record closers
	closer chan int

	// record handlers (as interface)
	producer chan interface{}
}

// the full registry
var handlerRegistry = make(map[string]handlerManager)

// Config structure that holds configuration
type Config struct {
	// Root directory where TemplateEngine will get files
	TemplateDir string
	// Port to listen
	Port string
	// Number of handler to prepare
	NbHandlerCache int
	// TemplateEngine to use (default, pango2...)
	TemplateEngine string
	// SessionEngine (default is a file storage)
	SessionsEngine string
	// SessionName is the name of session, eg. Cookie name, default is "kwiscale-session"
	SessionName string
	// A secret string to encrypt cookie
	SessionSecret []byte
	// Static directory (to put css, images, and so on...)
	StaticDir string
	// Activate static in memory cache
	StaticCacheEnabled bool

	// DBDriver should be the name of a
	// registered DB Driver (sqlite3, postgresql, mysql/mariadb...)
	DBDriver string

	// DBURL is the connection path/url to the database
	DBURL string
}

// App handles router and handlers.
type App struct {

	// configuration
	Config *Config

	// session store
	sessionstore ISessionStore

	// Template engine instance.
	templateEngine ITemplate

	// The router that will be used
	router *mux.Router

	// List of handler "names" mapped to route (will be create by a factory)
	handlers map[*mux.Route]string

	// number of handler to keep in a channel
	nbHandlerCache int

	// DB connection
	DB IORM
}

// Initialize config default values if some are not defined
func initConfig(config *Config) *Config {
	if config == nil {
		config = new(Config)
	}

	if config.Port == "" {
		config.Port = ":8000"
	}

	if config.NbHandlerCache == 0 {
		config.NbHandlerCache = 5
	}

	if config.TemplateEngine == "" {
		config.TemplateEngine = "basic"
	}

	if config.SessionsEngine == "" {
		config.SessionsEngine = "default"
	}
	if config.SessionName == "" {
		config.SessionName = "kwiscale-session"
	}
	if config.SessionSecret == nil {
		config.SessionSecret = []byte("A very long secret string you should change")
	}

	/* no default ! use may not want to have a static handler
	//
	if config.StaticDir == "" {
		config.StaticDir = "static"
	}
	/**/
	return config
}

// NewApp Create new *App - App constructor.
func NewApp(config *Config) *App {

	// fill up config for non-set values
	config = initConfig(config)

	if debug {
		log.Printf("%+v\n", config)
	}

	// generate app, assign config, router and handlers map
	a := &App{
		nbHandlerCache: config.NbHandlerCache,
		router:         mux.NewRouter(),
		handlers:       make(map[*mux.Route]string),

		// Get template engine from config
		templateEngine: templateEngine[config.TemplateEngine],
	}

	a.templateEngine.SetTemplateDir(config.TemplateDir)

	// set sessstion store
	a.sessionstore = sessionEngine[config.SessionsEngine]
	a.sessionstore.Name(config.SessionName)
	a.sessionstore.SetSecret(config.SessionSecret)
	a.sessionstore.Init()

	if config.StaticDir != "" {
		a.SetStatic(config.StaticDir)
	}

	if config.DBDriver != "" {
		db := ormDriverRegistry[config.DBDriver]
		if db == nil {
			panic(errors.New("Unable to find driver " + config.DBDriver))
		}
		db.ConnectionString(config.DBURL)
		a.DB = db
	}

	// keep config
	a.Config = config

	return a
}

// ListenAndServe calls http.ListenAndServe method
func (a *App) ListenAndServe() {
	log.Println("Listening", a.Config.Port)
	http.ListenAndServe(a.Config.Port, a)
}

// SetStatic set the route "prefix" to serve files configured in Config.StatiDir
func (a *App) SetStatic(prefix string) {
	a.AddRoute("/"+prefix+"/{file:.*}", staticHandler{})
}

// Implement http.Handler ServeHTTP method
func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	var req interface{}
	for route, handler := range app.handlers {
		var match mux.RouteMatch
		if route.Match(r, &match) {
			// construct handler from its name
			if debug {
				log.Printf("Route matches %#v\n", route)
				log.Println("Handler to fetch", handler)
			}

			// wait for a built handler from registry
			req = <-handlerRegistry[handler].producer

			if debug {
				log.Print("Handler found ", req)
			}

			//assign some vars
			req.(IBaseHandler).setVars(match.Vars, w, r)
			req.(IBaseHandler).setApp(app)
			req.(IBaseHandler).setSessionStore(app.sessionstore)
			break //that's ok, we can continue
		}
		// code hasn't breaked, so we didn't found handler
	}

	if _, ok := req.(IBaseHandler); !ok {
		HandleError(http.StatusNotFound, w, r, nil)
		return
	}

	// Websocket case
	if req, ok := req.(IWSHandler); ok {
		req.upgrade()
		req.Serve()
		return
	}
	if req, ok := req.(IRequestHandler); ok {
		// RequestHandler case
		w.Header().Add("Connection", "close")
		if debug {
			log.Println("Respond to IRequestHandler", r.Method, req)
		}
		if req == nil {
			HandleError(http.StatusNotFound, w, r, nil)
			return
		}

		switch r.Method {
		case "GET":
			req.Get()
		case "PUT":
			req.Put()
		case "POST":
			req.Post()
		case "DELETE":
			req.Delete()
		case "HEAD":
			req.Post()
		case "PATCH":
			req.Patch()
		default:
			HandleError(http.StatusNotImplemented, w, r, nil)
		}
	} else {
		HandleError(http.StatusInternalServerError, w, r, nil)
		if debug {
			log.Printf("RequestWriter: %+v\n", w)
			log.Printf("Reponse: %+v", r)
			log.Printf("KwiscaleHandler: %+v\n", req)
		}
	}
}

// AddRoute appends route mapped to handler. Note that rh parameter should
// implement IRequestHandler (generally a struct composing RequestHandler)
func (app *App) AddRoute(route string, handler interface{}) {
	r := app.router.NewRoute()
	r.Path(route)
	r.Name(route)

	handlerType := reflect.TypeOf(handler)
	// keep in mind that "route" is an pointer
	app.handlers[r] = handlerType.String()
	if debug {
		log.Print("Register ", handlerType.String())
	}

	// register factory channel
	manager := handlerManager{
		handler:  handlerType,
		closer:   make(chan int, 0),
		producer: make(chan interface{}, app.nbHandlerCache),
	}
	// produce handlers
	handlerRegistry[handlerType.String()] = manager
	go app.produceHandlers(manager)
}

// handlerFactory continuously generates new handlers in registry.
// It launches a goroutine to produce those handlers. The number of
// handlers to generate in cache is set by Config.NbHandlerCache.
// Return a chanel to write in to close handler production
func (app *App) produceHandlers(manager handlerManager) {
	// forever produce handlers until closer is called
	for {
		select {
		case manager.producer <- reflect.New(manager.handler).Interface():
			if debug {
				log.Println("Appended handler ", manager.handler.Name())
			}
		case <-manager.closer:
			// Someone closed the factory
			return
		}
	}
}

// HangOut stops each handler manager goroutine (useful for testing)
func (app *App) SoftStop() {
	for name, closer := range handlerRegistry {
		if debug {
			log.Println("Closing ", name)
		}
		closer.closer <- 1
	}
}
<commit_msg>Move handler production from app to handleManager<commit_after>package kwiscale

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

	"github.com/gorilla/mux"
)

// handlerManager is used to manage handler production and close
type handlerManager struct {

	// the handler type to produce
	handler reflect.Type

	// record closers
	closer chan int

	// record handlers (as interface)
	producer chan interface{}
}

// handlerFactory continuously generates new handlers in registry.
// It launches a goroutine to produce those handlers. The number of
// handlers to generate in cache is set by Config.NbHandlerCache.
// Return a chanel to write in to close handler production
func (manager handlerManager) produceHandlers() {
	// forever produce handlers until closer is called
	for {
		select {
		case manager.producer <- reflect.New(manager.handler).Interface():
			if debug {
				log.Println("Appended handler ", manager.handler.Name())
			}
		case <-manager.closer:
			// Someone closed the factory
			return
		}
	}
}

// the full registry
var handlerRegistry = make(map[string]handlerManager)

// Config structure that holds configuration
type Config struct {
	// Root directory where TemplateEngine will get files
	TemplateDir string
	// Port to listen
	Port string
	// Number of handler to prepare
	NbHandlerCache int
	// TemplateEngine to use (default, pango2...)
	TemplateEngine string
	// SessionEngine (default is a file storage)
	SessionsEngine string
	// SessionName is the name of session, eg. Cookie name, default is "kwiscale-session"
	SessionName string
	// A secret string to encrypt cookie
	SessionSecret []byte
	// Static directory (to put css, images, and so on...)
	StaticDir string
	// Activate static in memory cache
	StaticCacheEnabled bool

	// DBDriver should be the name of a
	// registered DB Driver (sqlite3, postgresql, mysql/mariadb...)
	DBDriver string

	// DBURL is the connection path/url to the database
	DBURL string
}

// App handles router and handlers.
type App struct {

	// configuration
	Config *Config

	// session store
	sessionstore ISessionStore

	// Template engine instance.
	templateEngine ITemplate

	// The router that will be used
	router *mux.Router

	// List of handler "names" mapped to route (will be create by a factory)
	handlers map[*mux.Route]string

	// number of handler to keep in a channel
	nbHandlerCache int

	// DB connection
	DB IORM
}

// Initialize config default values if some are not defined
func initConfig(config *Config) *Config {
	if config == nil {
		config = new(Config)
	}

	if config.Port == "" {
		config.Port = ":8000"
	}

	if config.NbHandlerCache == 0 {
		config.NbHandlerCache = 5
	}

	if config.TemplateEngine == "" {
		config.TemplateEngine = "basic"
	}

	if config.SessionsEngine == "" {
		config.SessionsEngine = "default"
	}
	if config.SessionName == "" {
		config.SessionName = "kwiscale-session"
	}
	if config.SessionSecret == nil {
		config.SessionSecret = []byte("A very long secret string you should change")
	}

	/* no default ! use may not want to have a static handler
	//
	if config.StaticDir == "" {
		config.StaticDir = "static"
	}
	/**/
	return config
}

// NewApp Create new *App - App constructor.
func NewApp(config *Config) *App {

	// fill up config for non-set values
	config = initConfig(config)

	if debug {
		log.Printf("%+v\n", config)
	}

	// generate app, assign config, router and handlers map
	a := &App{
		nbHandlerCache: config.NbHandlerCache,
		router:         mux.NewRouter(),
		handlers:       make(map[*mux.Route]string),

		// Get template engine from config
		templateEngine: templateEngine[config.TemplateEngine],
	}

	a.templateEngine.SetTemplateDir(config.TemplateDir)

	// set sessstion store
	a.sessionstore = sessionEngine[config.SessionsEngine]
	a.sessionstore.Name(config.SessionName)
	a.sessionstore.SetSecret(config.SessionSecret)
	a.sessionstore.Init()

	if config.StaticDir != "" {
		a.SetStatic(config.StaticDir)
	}

	if config.DBDriver != "" {
		db := ormDriverRegistry[config.DBDriver]
		if db == nil {
			panic(errors.New("Unable to find driver " + config.DBDriver))
		}
		db.ConnectionString(config.DBURL)
		a.DB = db
	}

	// keep config
	a.Config = config

	return a
}

// ListenAndServe calls http.ListenAndServe method
func (a *App) ListenAndServe() {
	log.Println("Listening", a.Config.Port)
	http.ListenAndServe(a.Config.Port, a)
}

// SetStatic set the route "prefix" to serve files configured in Config.StatiDir
func (a *App) SetStatic(prefix string) {
	a.AddRoute("/"+prefix+"/{file:.*}", staticHandler{})
}

// Implement http.Handler ServeHTTP method
func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	var req interface{}
	for route, handler := range app.handlers {
		var match mux.RouteMatch
		if route.Match(r, &match) {
			// construct handler from its name
			if debug {
				log.Printf("Route matches %#v\n", route)
				log.Println("Handler to fetch", handler)
			}

			// wait for a built handler from registry
			req = <-handlerRegistry[handler].producer

			if debug {
				log.Print("Handler found ", req)
			}

			//assign some vars
			req.(IBaseHandler).setVars(match.Vars, w, r)
			req.(IBaseHandler).setApp(app)
			req.(IBaseHandler).setSessionStore(app.sessionstore)
			break //that's ok, we can continue
		}
		// code hasn't breaked, so we didn't found handler
	}

	if _, ok := req.(IBaseHandler); !ok {
		HandleError(http.StatusNotFound, w, r, nil)
		return
	}

	// Websocket case
	if req, ok := req.(IWSHandler); ok {
		req.upgrade()
		req.Serve()
		return
	}
	if req, ok := req.(IRequestHandler); ok {
		// RequestHandler case
		w.Header().Add("Connection", "close")
		if debug {
			log.Println("Respond to IRequestHandler", r.Method, req)
		}
		if req == nil {
			HandleError(http.StatusNotFound, w, r, nil)
			return
		}

		switch r.Method {
		case "GET":
			req.Get()
		case "PUT":
			req.Put()
		case "POST":
			req.Post()
		case "DELETE":
			req.Delete()
		case "HEAD":
			req.Post()
		case "PATCH":
			req.Patch()
		default:
			HandleError(http.StatusNotImplemented, w, r, nil)
		}
	} else {
		HandleError(http.StatusInternalServerError, w, r, nil)
		if debug {
			log.Printf("RequestWriter: %+v\n", w)
			log.Printf("Reponse: %+v", r)
			log.Printf("KwiscaleHandler: %+v\n", req)
		}
	}
}

// AddRoute appends route mapped to handler. Note that rh parameter should
// implement IRequestHandler (generally a struct composing RequestHandler)
func (app *App) AddRoute(route string, handler interface{}) {
	r := app.router.NewRoute()
	r.Path(route)
	r.Name(route)

	handlerType := reflect.TypeOf(handler)
	// keep in mind that "route" is an pointer
	app.handlers[r] = handlerType.String()
	if debug {
		log.Print("Register ", handlerType.String())
	}

	// register factory channel
	manager := handlerManager{
		handler:  handlerType,
		closer:   make(chan int, 0),
		producer: make(chan interface{}, app.nbHandlerCache),
	}
	// produce handlers
	handlerRegistry[handlerType.String()] = manager
	go manager.produceHandlers()
}

// HangOut stops each handler manager goroutine (useful for testing)
func (app *App) SoftStop() {
	for name, closer := range handlerRegistry {
		if debug {
			log.Println("Closing ", name)
		}
		closer.closer <- 1
	}
}
<|endoftext|>


<commit_before>package mdqi

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"

	"os/exec"

	"github.com/peterh/liner"
	"github.com/pkg/errors"
)

const (
	defaultMdqPath         = "mdq"
	defaultHistoryFilename = ".mdqi_history"
)

var Version string

var (
	ErrSlashCommandNotFound    = errors.New("unknown SlashCommand")
	ErrNotASlashCommand        = errors.New("there are no SlashCommand")
	ErrSlashCommandInvalidArgs = errors.New("invalid args")
	ErrUnknownPrinterName      = errors.New("unknown printer name")
)

type App struct {
	// Alive turns into false, mdqi will exit.
	Alive bool

	// mdqPath is path to mdq command.
	mdqPath string

	// mdqConfigPath is path to configuration file for mdq command.
	mdqConfigPath string

	// historyPath is path to command history file for liner.
	historyPath string

	// slashCommandDefinition holds SlashCommandDefinition.
	// app.slashCommandDefinition[category][name] = SlashCommandDefinition
	slashCommandDefinition map[string]map[string]SlashCommandDefinition

	// tag stores tag value for --tag option of mdq.
	tag string

	// Printer prints results.
	Printer Printer
}

type Result struct {
	Database string
	Columns  []string
	Rows     []map[string]interface{}
}

func init() {
	DefaultOutput = os.Stdout
}

func NewApp(conf Conf) (*App, error) {
	// validate mdq path
	mdqPath := defaultMdqPath
	if path := conf.Mdq.Bin; path != "" {
		Debug.Println("conf.Mdq.Bin =", path)

		abs, err := ExpandPath(path)
		if err != nil {
			return nil, errors.Wrap(err, "failed to expand path to mdq")
		}

		if err := lookMdqPath(abs); err != nil {
			return nil, errors.Wrapf(err, "mdq command not found at %s", path)
		}

		mdqPath = abs
	}

	// mdq config path
	mdqConfPath := conf.Mdq.Config
	if path := conf.Mdq.Config; path != "" {
		Debug.Println("conf.Mdq.Config =", path)

		abs, err := ExpandPath(path)
		if err != nil {
			return nil, errors.Wrap(err, "failed to expand path to mdq conf")
		}

		mdqConfPath = abs
	}

	// create history file
	historyPath := conf.Mdqi.History
	if historyPath == "" {
		Debug.Println("conf.Mdqi.History =", historyPath)

		var err error
		if historyPath, err = defaultHistoryPath(); err != nil {
			return nil, errors.Wrap(err, "failed to create history file at default path")
		}
	}
	{
		abs, err := ExpandPath(historyPath)
		if err != nil {
			return nil, errors.Wrap(err, "failed to expand path to history file")
		}
		historyPath = abs
	}
	if err := createHistoryFile(historyPath); err != nil {
		return nil, errors.Wrapf(err, "failed to create history file at %s", historyPath)
	}

	app := &App{
		Alive: true,

		mdqPath:                mdqPath,
		mdqConfigPath:          mdqConfPath,
		historyPath:            historyPath,
		slashCommandDefinition: map[string]map[string]SlashCommandDefinition{},
		Printer:                HorizontalPrinter{},
	}

	// set default tag
	if tag := conf.Mdqi.DefaultTag; tag != "" {
		app.SetTag(tag)
		Debug.Println("conf.Mdqi.DefaultTag =", tag)
	}

	// set default display
	if display := conf.Mdqi.DefaultDisplay; display != "" {
		if err := app.SetPrinterByName(display); err != nil {
			return nil, errors.Wrap(err, "failed to set default printer")
		}

		Debug.Println("conf.Mdqi.DefaultDisplay =", display)
	}

	return app, nil
}

func createHistoryFile(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		if _, err := os.Create(path); err != nil {
			return errors.Wrapf(err, "failed to create history file at %s", path)
		}
	}

	return nil
}

func defaultHistoryPath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "failed to get current user")
	}

	return filepath.Join(usr.HomeDir, defaultHistoryFilename), err
}

func lookMdqPath(path string) error {
	_, err := exec.LookPath(path)

	return err
}

func (app *App) SlashCommandCategories() []string {
	defs := app.slashCommandDefinition
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) SlashCommandNames(category string) []string {
	defs := app.slashCommandDefinition[category]
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) Run() {
	app.runLiner()
}

func (app *App) runLiner() {
	line := liner.NewLiner()
	defer line.Close()

	line.SetCtrlCAborts(true)
	app.initHistory(line)

	// If input dosen't be match it, input is not finished.
	rgxLineFinisehd := regexp.MustCompile(";$")

	// If this value is false, input will be continued.
	lineFinished := true

	var input string // input from line
	var err error

LOOP:
	for {
		if !app.Alive {
			fmt.Println("bye")
			break LOOP
		}

		if lineFinished {
			input, err = line.Prompt("mdq> ")
		} else {
			var l string
			l, err = line.Prompt("   | ")
			input = strings.Join([]string{input, l}, " ")
		}

		switch err {
		case nil:
			input = strings.Trim(input, " \n")

			if input == "" {
				continue
			}

			// run slash command
			scmd, _ := ParseSlashCommand(input)
			if scmd != nil {
				app.runSlashCommand(scmd)
				continue
			}

			if lineFinished = rgxLineFinisehd.MatchString(input); lineFinished {
			} else {
				// If line is not finished, read next line as continue.
				continue
			}

			line.AppendHistory(input)

			// run mdq
			results, err := app.RunCmd(input, app.buildCmdArgs()...)
			if err != nil {
				Logger.Println(err.Error())
			}

			Print(app.Printer, results)
		case liner.ErrPromptAborted:
			Logger.Println("aborted")
			break LOOP
		case io.EOF:
			fmt.Println("bye")
			break LOOP
		default:
			Logger.Println("error on reading line: ", err)
			break LOOP
		}

		app.saveHistory(line)
	}
}

func (app *App) initHistory(line *liner.State) {
	if f, err := os.Open(app.historyPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	} else {
		Logger.Printf("failed to read command history at %s: %s", app.historyPath, err)
	}
}

func (app *App) saveHistory(line *liner.State) {
	if f, err := os.Create(app.historyPath); err == nil {
		if _, err := line.WriteHistory(f); err != nil {
			Logger.Println("failed to write history: ", err)
		}

		f.Close()
	} else {
		Logger.Printf("failed to create history file at %s: %s", app.historyPath, err)
	}
}

func (app *App) buildCmdArgs() []string {
	args := []string{}

	// config
	if path := app.mdqConfigPath; path != "" {
		args = append(args, "--config="+path)
	}

	// tag
	if tag := app.tag; tag != "" {
		args = append(args, "--tag="+tag)
	}

	return args
}

func (app *App) runSlashCommand(scmd *SlashCommand) {
	sdef, err := app.FindSlashCommandDefinition(scmd.Category, scmd.Name)

	switch err {
	case nil:
		if err := sdef.Handle(app, scmd); err != nil {
			Logger.Println("failed to handle slash command:", err)
		}
	case ErrSlashCommandNotFound:
		Logger.Println("unknown slash command")
	}

	return
}

func (app *App) SetPrinterByName(name string) error {

	switch name {
	case "horizontal":
		app.Printer = HorizontalPrinter{}
	case "vertical":
		app.Printer = VerticalPrinter{}
	default:
		return ErrUnknownPrinterName
	}

	return nil
}

func (app *App) GetTag() string {
	return app.tag
}

func (app *App) SetTag(tag string) {
	app.tag = tag
}

func (app *App) ClearTag() {
	app.tag = ""
}

// ExpandPath expands file path like `~/path/to/foo`
func ExpandPath(path string) (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "failed to get current user")
	}

	return strings.Replace(path, "~", usr.HomeDir, 1), nil
}
<commit_msg>display current tag in prompt<commit_after>package mdqi

import (
	"fmt"
	"io"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"strings"

	"os/exec"

	"github.com/peterh/liner"
	"github.com/pkg/errors"
)

const (
	defaultMdqPath         = "mdq"
	defaultHistoryFilename = ".mdqi_history"
)

var Version string

var (
	ErrSlashCommandNotFound    = errors.New("unknown SlashCommand")
	ErrNotASlashCommand        = errors.New("there are no SlashCommand")
	ErrSlashCommandInvalidArgs = errors.New("invalid args")
	ErrUnknownPrinterName      = errors.New("unknown printer name")
)

type App struct {
	// Alive turns into false, mdqi will exit.
	Alive bool

	// mdqPath is path to mdq command.
	mdqPath string

	// mdqConfigPath is path to configuration file for mdq command.
	mdqConfigPath string

	// historyPath is path to command history file for liner.
	historyPath string

	// slashCommandDefinition holds SlashCommandDefinition.
	// app.slashCommandDefinition[category][name] = SlashCommandDefinition
	slashCommandDefinition map[string]map[string]SlashCommandDefinition

	// tag stores tag value for --tag option of mdq.
	tag string

	// Printer prints results.
	Printer Printer
}

type Result struct {
	Database string
	Columns  []string
	Rows     []map[string]interface{}
}

func init() {
	DefaultOutput = os.Stdout
}

func NewApp(conf Conf) (*App, error) {
	// validate mdq path
	mdqPath := defaultMdqPath
	if path := conf.Mdq.Bin; path != "" {
		Debug.Println("conf.Mdq.Bin =", path)

		abs, err := ExpandPath(path)
		if err != nil {
			return nil, errors.Wrap(err, "failed to expand path to mdq")
		}

		if err := lookMdqPath(abs); err != nil {
			return nil, errors.Wrapf(err, "mdq command not found at %s", path)
		}

		mdqPath = abs
	}

	// mdq config path
	mdqConfPath := conf.Mdq.Config
	if path := conf.Mdq.Config; path != "" {
		Debug.Println("conf.Mdq.Config =", path)

		abs, err := ExpandPath(path)
		if err != nil {
			return nil, errors.Wrap(err, "failed to expand path to mdq conf")
		}

		mdqConfPath = abs
	}

	// create history file
	historyPath := conf.Mdqi.History
	if historyPath == "" {
		Debug.Println("conf.Mdqi.History =", historyPath)

		var err error
		if historyPath, err = defaultHistoryPath(); err != nil {
			return nil, errors.Wrap(err, "failed to create history file at default path")
		}
	}
	{
		abs, err := ExpandPath(historyPath)
		if err != nil {
			return nil, errors.Wrap(err, "failed to expand path to history file")
		}
		historyPath = abs
	}
	if err := createHistoryFile(historyPath); err != nil {
		return nil, errors.Wrapf(err, "failed to create history file at %s", historyPath)
	}

	app := &App{
		Alive: true,

		mdqPath:                mdqPath,
		mdqConfigPath:          mdqConfPath,
		historyPath:            historyPath,
		slashCommandDefinition: map[string]map[string]SlashCommandDefinition{},
		Printer:                HorizontalPrinter{},
	}

	// set default tag
	if tag := conf.Mdqi.DefaultTag; tag != "" {
		app.SetTag(tag)
		Debug.Println("conf.Mdqi.DefaultTag =", tag)
	}

	// set default display
	if display := conf.Mdqi.DefaultDisplay; display != "" {
		if err := app.SetPrinterByName(display); err != nil {
			return nil, errors.Wrap(err, "failed to set default printer")
		}

		Debug.Println("conf.Mdqi.DefaultDisplay =", display)
	}

	return app, nil
}

func createHistoryFile(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		if _, err := os.Create(path); err != nil {
			return errors.Wrapf(err, "failed to create history file at %s", path)
		}
	}

	return nil
}

func defaultHistoryPath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "failed to get current user")
	}

	return filepath.Join(usr.HomeDir, defaultHistoryFilename), err
}

func lookMdqPath(path string) error {
	_, err := exec.LookPath(path)

	return err
}

func (app *App) SlashCommandCategories() []string {
	defs := app.slashCommandDefinition
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) SlashCommandNames(category string) []string {
	defs := app.slashCommandDefinition[category]
	keys := make([]string, 0, len(defs))

	for key := range defs {
		keys = append(keys, key)
	}

	return keys
}

func (app *App) Run() {
	app.runLiner()
}

func (app *App) runLiner() {
	line := liner.NewLiner()
	defer line.Close()

	line.SetCtrlCAborts(true)
	app.initHistory(line)

	// If input dosen't be match it, input is not finished.
	rgxLineFinisehd := regexp.MustCompile(";$")

	// If this value is false, input will be continued.
	lineFinished := true

	var input string // input from line
	var err error

LOOP:
	for {
		if !app.Alive {
			fmt.Println("bye")
			break LOOP
		}

		if lineFinished {
			input, err = line.Prompt(app.formatPrompt(false))
		} else {
			var l string
			l, err = line.Prompt(app.formatPrompt(true))
			input = strings.Join([]string{input, l}, " ")
		}

		switch err {
		case nil:
			input = strings.Trim(input, " \n")

			if input == "" {
				continue
			}

			// run slash command
			scmd, _ := ParseSlashCommand(input)
			if scmd != nil {
				app.runSlashCommand(scmd)
				continue
			}

			if lineFinished = rgxLineFinisehd.MatchString(input); lineFinished {
			} else {
				// If line is not finished, read next line as continue.
				continue
			}

			line.AppendHistory(input)

			// run mdq
			results, err := app.RunCmd(input, app.buildCmdArgs()...)
			if err != nil {
				Logger.Println(err.Error())
			}

			Print(app.Printer, results)
		case liner.ErrPromptAborted:
			Logger.Println("aborted")
			break LOOP
		case io.EOF:
			fmt.Println("bye")
			break LOOP
		default:
			Logger.Println("error on reading line: ", err)
			break LOOP
		}

		app.saveHistory(line)
	}
}

func (app *App) initHistory(line *liner.State) {
	if f, err := os.Open(app.historyPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	} else {
		Logger.Printf("failed to read command history at %s: %s", app.historyPath, err)
	}
}

func (app *App) saveHistory(line *liner.State) {
	if f, err := os.Create(app.historyPath); err == nil {
		if _, err := line.WriteHistory(f); err != nil {
			Logger.Println("failed to write history: ", err)
		}

		f.Close()
	} else {
		Logger.Printf("failed to create history file at %s: %s", app.historyPath, err)
	}
}

func (app *App) buildCmdArgs() []string {
	args := []string{}

	// config
	if path := app.mdqConfigPath; path != "" {
		args = append(args, "--config="+path)
	}

	// tag
	if tag := app.tag; tag != "" {
		args = append(args, "--tag="+tag)
	}

	return args
}

func (app *App) runSlashCommand(scmd *SlashCommand) {
	sdef, err := app.FindSlashCommandDefinition(scmd.Category, scmd.Name)

	switch err {
	case nil:
		if err := sdef.Handle(app, scmd); err != nil {
			Logger.Println("failed to handle slash command:", err)
		}
	case ErrSlashCommandNotFound:
		Logger.Println("unknown slash command")
	}

	return
}

func (app *App) SetPrinterByName(name string) error {

	switch name {
	case "horizontal":
		app.Printer = HorizontalPrinter{}
	case "vertical":
		app.Printer = VerticalPrinter{}
	default:
		return ErrUnknownPrinterName
	}

	return nil
}

func (app *App) GetTag() string {
	return app.tag
}

func (app *App) SetTag(tag string) {
	app.tag = tag
}

func (app *App) ClearTag() {
	app.tag = ""
}

func (app *App) formatPrompt(continues bool) string {
	if continues {
		return "   | "
	} else {
		if tag := app.GetTag(); tag == "" {
			return "mdq> "
		} else {
			return fmt.Sprintf("mdq[%s]> ", app.GetTag())
		}
	}
}

// ExpandPath expands file path like `~/path/to/foo`
func ExpandPath(path string) (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", errors.Wrap(err, "failed to get current user")
	}

	return strings.Replace(path, "~", usr.HomeDir, 1), nil
}
<|endoftext|>


<commit_before>package goapi

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

	"github.com/gorilla/mux"
	"github.com/tylors/goapi/middleware"
	"github.com/tylors/goapi/router"
)

type App struct {
	DB          sql.DB
	Router      *router.Router
	Middlewares []middleware.Middleware
}

func (a *App) Handle(path string, handler http.Handler) *mux.Route {
	return a.Router.Handle(path, handler)
}

func (a *App) HandleFunc(path string, handler http.HandlerFunc) *mux.Route {
	return a.Router.Handle(path, handler)
}

func (a *App) Use(middlewares ...middleware.Middleware) {
	for _, middleware := range middlewares {
		a.Router.Middlewares = append(a.Middlewares, middleware)
	}
}

func (a *App) RegisterBlueprint(blueprint BluePrint, prefix string) {
	blueprint.RegisterRouter(a, prefix)
	blueprint.Initialize()
	blueprint.Routes()
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.Router.ServeHTTP(w, r)
}

func (a *App) Run(addr string) {
	fmt.Println("Running server on:", addr)
	log.Fatal(http.ListenAndServe(addr, a))
}

func New() *App {
	app := &App{
		Router: router.New(),
	}
	app.Use(middleware.Logger, middleware.Recovery)
	return app
}
<commit_msg>Fixed blueprint registration<commit_after>package goapi

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

	"github.com/gorilla/mux"
	"github.com/tylors/goapi/middleware"
	"github.com/tylors/goapi/router"
)

type App struct {
	DB          sql.DB
	Router      *router.Router
	Middlewares []middleware.Middleware
}

func (a *App) Handle(path string, handler http.Handler) *mux.Route {
	return a.Router.Handle(path, handler)
}

func (a *App) HandleFunc(path string, handler http.HandlerFunc) *mux.Route {
	return a.Router.Handle(path, handler)
}

func (a *App) Use(middlewares ...middleware.Middleware) {
	for _, middleware := range middlewares {
		a.Router.Middlewares = append(a.Middlewares, middleware)
	}
}

func (a *App) RegisterBlueprint(blueprint BluePrint, prefix string) {
	blueprint.RegisterRouter(a, prefix)
	blueprint.Routes()
	blueprint.Initialize()
}

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.Router.ServeHTTP(w, r)
}

func (a *App) Run(addr string) {
	fmt.Println("Running server on:", addr)
	log.Fatal(http.ListenAndServe(addr, a))
}

func New() *App {
	app := &App{
		Router: router.New(),
	}
	app.Use(middleware.Logger, middleware.Recovery)
	return app
}
<|endoftext|>


<commit_before>// This source file is part of the Packet Guardian project.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package middleware

import (
	"net/http"
	"strings"

	"github.com/lfkeitel/verbose"
	"github.com/usi-lfkeitel/packet-guardian/src/common"
	"github.com/usi-lfkeitel/packet-guardian/src/models"
)

func BlacklistCheck(e *common.Environment, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Public assets are available to everyone
		if strings.HasPrefix(r.URL.Path, "/public") || strings.HasPrefix(r.URL.Path, "/login") {
			next.ServeHTTP(w, r)
			return
		}

		// Admin user's bypass the blacklist
		sessionUser := models.GetUserFromContext(r)
		if sessionUser.Can(models.BypassBlacklist) {
			next.ServeHTTP(w, r)
			return
		}

		ip := common.GetIPFromContext(r)
		lease, err := models.GetLeaseStore(e).GetLeaseByIP(ip)
		if err == nil && lease.ID != 0 {
			device, err := models.GetDeviceByMAC(e, lease.MAC)
			if err != nil {
				e.Log.WithFields(verbose.Fields{
					"error":   err,
					"package": "middlware:blacklist",
					"mac":     lease.MAC.String(),
				}).Critical("Error getting device")
			} else if device.IsBlacklisted() {
				e.Views.NewView("blacklisted", r).Render(w, nil)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}
<commit_msg>Allow /logout on blacklisted device<commit_after>// This source file is part of the Packet Guardian project.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package middleware

import (
	"net/http"
	"strings"

	"github.com/lfkeitel/verbose"
	"github.com/usi-lfkeitel/packet-guardian/src/common"
	"github.com/usi-lfkeitel/packet-guardian/src/models"
)

func BlacklistCheck(e *common.Environment, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Public assets are available to everyone
		if strings.HasPrefix(r.URL.Path, "/public") ||
			strings.HasPrefix(r.URL.Path, "/login") ||
			strings.HasPrefix(r.URL.Path, "/logout") {
			next.ServeHTTP(w, r)
			return
		}

		// Admin user's bypass the blacklist
		sessionUser := models.GetUserFromContext(r)
		if sessionUser.Can(models.BypassBlacklist) {
			next.ServeHTTP(w, r)
			return
		}

		ip := common.GetIPFromContext(r)
		lease, err := models.GetLeaseStore(e).GetLeaseByIP(ip)
		if err == nil && lease.ID != 0 {
			device, err := models.GetDeviceByMAC(e, lease.MAC)
			if err != nil {
				e.Log.WithFields(verbose.Fields{
					"error":   err,
					"package": "middlware:blacklist",
					"mac":     lease.MAC.String(),
				}).Critical("Error getting device")
			} else if device.IsBlacklisted() {
				e.Views.NewView("blacklisted", r).Render(w, nil)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package avs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
)

const (
	EndpointURL   = "https://avs-alexa-na.amazon.com/v20160207"
	DirectivesURL = EndpointURL + "/directives"
	EventsURL     = EndpointURL + "/events"
	PingURL       = EndpointURL + "/ping"
)

// Request to AVS.
type Request struct {
	// Access token for the user that this request should be made for.
	AccessToken string         `json:"-"`
	Audio       io.Reader      `json:"-"`
	Context     []TypedMessage `json:"context"`
	Event       TypedMessage   `json:"event"`
}

func NewRequest(accessToken string) *Request {
	return &Request{
		AccessToken: accessToken,
		Context:     []TypedMessage{},
	}
}

func (r *Request) AddContext(m TypedMessage) {
	r.Context = append(r.Context, m.GetMessage())
}

// Representation of an AVS response.
type Response struct {
	// The Amazon request id (for debugging purposes).
	RequestId string
	// All the directives in the response.
	Directives []TypedMessage
	// Attachments (usually audio). Key is the Content-ID header value.
	Content map[string][]byte
}

// Multipart object returned by AVS.
type responsePart struct {
	Directive *Message
}

// An AVS client.
type Client struct {
}

var DefaultClient = &Client{}

// Posts a request to the AVS service.
func (c *Client) Do(request *Request) (*Response, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	err := writeJSON(writer, "metadata", request)
	if err != nil {
		return nil, err
	}
	if request.Audio != nil {
		p, err := writer.CreateFormFile("audio", "audio.wav")
		if err != nil {
			return nil, err
		}
		// TODO: Write the audio data directly to the HTTP/2 socket for faster delivery.
		_, err = io.Copy(p, request.Audio)
		if err != nil {
			return nil, err
		}
		err = writer.Close()
		if err != nil {
			return nil, err
		}
	}
	// Send the request to AVS.
	req, err := http.NewRequest("POST", EventsURL, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", request.AccessToken))
	req.Header.Add("Content-Type", writer.FormDataContentType())
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		// Attempt to parse the response as a System.Exception message.
		data, _ := ioutil.ReadAll(resp.Body)
		var exception Exception
		json.Unmarshal(data, &exception)
		if exception.Payload.Code != "" {
			return nil, &exception
		}
		// Fallback error.
		return nil, fmt.Errorf("request failed with %s", resp.Status)
	}
	// Parse the multipart response.
	mr, err := newMultipartReaderFromResponse(resp)
	if err != nil {
		return nil, err
	}
	response := &Response{
		RequestId:  resp.Header.Get("x-amzn-requestid"),
		Directives: []TypedMessage{},
		Content:    map[string][]byte{},
	}
	for {
		p, err := mr.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		mediatype, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
		if err != nil {
			return nil, err
		}
		data, err := ioutil.ReadAll(p)
		if err != nil {
			return nil, err
		}
		if contentId := p.Header.Get("Content-ID"); contentId != "" {
			// This part is a referencable piece of content.
			// XXX: Content-ID generally always has angle brackets, but there may be corner cases?
			response.Content[contentId[1:len(contentId)-1]] = data
		} else if mediatype == "application/json" {
			// This is a directive.
			var resp responsePart
			err = json.Unmarshal(data, &resp)
			if err != nil {
				return nil, err
			}
			if resp.Directive == nil {
				return nil, fmt.Errorf("missing directive %s", string(data))
			}
			response.Directives = append(response.Directives, resp.Directive)
		} else {
			return nil, fmt.Errorf("unhandled part %s", p)
		}
	}
	return response, nil
}

func (c *Client) Ping(accessToken string) error {
	// TODO: Once Go supports sending PING frames, that would be a better alternative.
	req, err := http.NewRequest("GET", PingURL, nil)
	if err != nil {
		return err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<commit_msg>Fix bug with AddContext method<commit_after>package avs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
)

const (
	EndpointURL   = "https://avs-alexa-na.amazon.com/v20160207"
	DirectivesURL = EndpointURL + "/directives"
	EventsURL     = EndpointURL + "/events"
	PingURL       = EndpointURL + "/ping"
)

// Request to AVS.
type Request struct {
	// Access token for the user that this request should be made for.
	AccessToken string         `json:"-"`
	Audio       io.Reader      `json:"-"`
	Context     []TypedMessage `json:"context"`
	Event       TypedMessage   `json:"event"`
}

func NewRequest(accessToken string) *Request {
	return &Request{
		AccessToken: accessToken,
		Context:     []TypedMessage{},
	}
}

func (r *Request) AddContext(m TypedMessage) {
	r.Context = append(r.Context, m)
}

// Representation of an AVS response.
type Response struct {
	// The Amazon request id (for debugging purposes).
	RequestId string
	// All the directives in the response.
	Directives []TypedMessage
	// Attachments (usually audio). Key is the Content-ID header value.
	Content map[string][]byte
}

// Multipart object returned by AVS.
type responsePart struct {
	Directive *Message
}

// An AVS client.
type Client struct {
}

var DefaultClient = &Client{}

// Posts a request to the AVS service.
func (c *Client) Do(request *Request) (*Response, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	err := writeJSON(writer, "metadata", request)
	if err != nil {
		return nil, err
	}
	if request.Audio != nil {
		p, err := writer.CreateFormFile("audio", "audio.wav")
		if err != nil {
			return nil, err
		}
		// TODO: Write the audio data directly to the HTTP/2 socket for faster delivery.
		_, err = io.Copy(p, request.Audio)
		if err != nil {
			return nil, err
		}
		err = writer.Close()
		if err != nil {
			return nil, err
		}
	}
	// Send the request to AVS.
	req, err := http.NewRequest("POST", EventsURL, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", request.AccessToken))
	req.Header.Add("Content-Type", writer.FormDataContentType())
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		// Attempt to parse the response as a System.Exception message.
		data, _ := ioutil.ReadAll(resp.Body)
		var exception Exception
		json.Unmarshal(data, &exception)
		if exception.Payload.Code != "" {
			return nil, &exception
		}
		// Fallback error.
		return nil, fmt.Errorf("request failed with %s", resp.Status)
	}
	// Parse the multipart response.
	mr, err := newMultipartReaderFromResponse(resp)
	if err != nil {
		return nil, err
	}
	response := &Response{
		RequestId:  resp.Header.Get("x-amzn-requestid"),
		Directives: []TypedMessage{},
		Content:    map[string][]byte{},
	}
	for {
		p, err := mr.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		mediatype, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
		if err != nil {
			return nil, err
		}
		data, err := ioutil.ReadAll(p)
		if err != nil {
			return nil, err
		}
		if contentId := p.Header.Get("Content-ID"); contentId != "" {
			// This part is a referencable piece of content.
			// XXX: Content-ID generally always has angle brackets, but there may be corner cases?
			response.Content[contentId[1:len(contentId)-1]] = data
		} else if mediatype == "application/json" {
			// This is a directive.
			var resp responsePart
			err = json.Unmarshal(data, &resp)
			if err != nil {
				return nil, err
			}
			if resp.Directive == nil {
				return nil, fmt.Errorf("missing directive %s", string(data))
			}
			response.Directives = append(response.Directives, resp.Directive)
		} else {
			return nil, fmt.Errorf("unhandled part %s", p)
		}
	}
	return response, nil
}

func (c *Client) Ping(accessToken string) error {
	// TODO: Once Go supports sending PING frames, that would be a better alternative.
	req, err := http.NewRequest("GET", PingURL, nil)
	if err != nil {
		return err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"sort"
)

var (
	unreserved = make([]bool, 128)
	hex        = "0123456789ABCDEF"
)

func init() {
	// RFC3986
	u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
	for _, c := range u {
		unreserved[c] = true
	}
}

// from https://launchpad.net/goamz
func encode(s string) string {
	encode := false
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			encode = true
			break
		}
	}
	if !encode {
		return s
	}
	e := make([]byte, len(s)*3)
	ei := 0
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			e[ei] = '%'
			e[ei+1] = hex[c>>4]
			e[ei+2] = hex[c&0xF]
			ei += 3
		} else {
			e[ei] = c
			ei += 1
		}
	}
	return string(e[:ei])
}

// http://docs.amazonwebservices.com/general/latest/gr/sigv4-create-canonical-request.html
func CreateCanonicalRequest(r *http.Request) ([]byte, error) {

	fmt.Println(r)

	var crb bytes.Buffer // canonical request buffer

	// 1
	_, err := crb.Write([]byte(r.Method))
	if err != nil {
		return nil, err
	}
	err = crb.WriteByte('\n')
	if err != nil {
		return nil, err
	}

	// 2
	_, err = crb.Write([]byte(r.URL.Path))
	if err != nil {
		return nil, err
	}
	err = crb.WriteByte('\n')
	if err != nil {
		return nil, err
	}

	// 3
	// TODO another buffer to avoid all the string allocation
	query, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		return nil, err
	}
	keys := make([]string, 0)
	for i := range query {
		keys = append(keys, i)
	}
	sort.Strings(keys)
	var cqs string // canonical query string
	for i := range keys {
		if i > 0 {
			cqs = cqs + "&"
		}
		parameters := query[keys[i]]
		sort.Strings(parameters)
		for j := range parameters {
			if j > 0 {
				cqs = cqs + "&"
			}
			cqs = cqs + encode(keys[i]) + "=" + encode(parameters[j])
		}
	}
	if len(cqs) > 0 {
		_, err = crb.Write([]byte(cqs))
		if err != nil {
			return nil, err
		}
		err = crb.WriteByte('\n')
		if err != nil {
			return nil, err
		}
	}

	return crb.Bytes(), nil
}
<commit_msg>Added canonical headers. Still untested.<commit_after>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
)

var (
	unreserved = make([]bool, 128)
	hex        = "0123456789ABCDEF"
)

func init() {
	// RFC3986
	u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
	for _, c := range u {
		unreserved[c] = true
	}
}

// from https://launchpad.net/goamz
func encode(s string) string {
	encode := false
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			encode = true
			break
		}
	}
	if !encode {
		return s
	}
	e := make([]byte, len(s)*3)
	ei := 0
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			e[ei] = '%'
			e[ei+1] = hex[c>>4]
			e[ei+2] = hex[c&0xF]
			ei += 3
		} else {
			e[ei] = c
			ei += 1
		}
	}
	return string(e[:ei])
}

// http://docs.amazonwebservices.com/general/latest/gr/sigv4-create-canonical-request.html
func CreateCanonicalRequest(r *http.Request) ([]byte, error) {

	fmt.Println(r)

	var crb bytes.Buffer // canonical request buffer

	// 1
	_, err := crb.Write([]byte(r.Method))
	if err != nil {
		return nil, err
	}
	err = crb.WriteByte('\n')
	if err != nil {
		return nil, err
	}

	// 2
	_, err = crb.Write([]byte(r.URL.Path))
	if err != nil {
		return nil, err
	}
	err = crb.WriteByte('\n')
	if err != nil {
		return nil, err
	}

	// 3
	// TODO another buffer to avoid all the string allocation
	query, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		return nil, err
	}
	keys := make([]string, 0)
	for i := range query {
		keys = append(keys, i)
	}
	sort.Strings(keys)
	var cqs string // canonical query string
	for i := range keys {
		if i > 0 {
			cqs = cqs + "&"
		}
		parameters := query[keys[i]]
		sort.Strings(parameters)
		for j := range parameters {
			if j > 0 {
				cqs = cqs + "&"
			}
			cqs = cqs + encode(keys[i]) + "=" + encode(parameters[j])
		}
	}
	if len(cqs) > 0 {
		_, err = crb.Write([]byte(cqs))
		if err != nil {
			return nil, err
		}
		err = crb.WriteByte('\n')
		if err != nil {
			return nil, err
		}
	}

	// 4
	headers := make([]string, 0)
	headersMap := make(map[string]string)
	for i := range r.Header {
		header := strings.ToLower(strings.TrimSpace(i))
		headers = append(headers, header)
		headersMap[header] = i
	}
	sort.Strings(headers)
	for i := range headers {
		_, err = crb.Write([]byte(headers[i]))
		if err != nil {
			return nil, err
		}
		err = crb.WriteByte(':')
		if err != nil {
			return nil, err
		}
		value := strings.Join(r.Header[headersMap[headers[i]]], ",")
		_, err := crb.Write([]byte(value))
		err = crb.WriteByte('\n')
		if err != nil {
			return nil, err
		}
	}

	return crb.Bytes(), nil
}
<|endoftext|>


<commit_before>package mpb

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb/v4/decor"
)

// Filler interface.
// Bar renders by calling Filler's Fill method. You can literally have
// any bar kind, by implementing this interface and passing it to the
// Add method.
type Filler interface {
	Fill(w io.Writer, width int, stat *decor.Statistics)
}

// FillerFunc is function type adapter to convert function into Filler.
type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)

func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
	f(w, width, stat)
}

// Bar represents a progress Bar.
type Bar struct {
	priority int
	index    int

	cacheState   *bState
	operateState chan func(*bState)
	bFrameCh     chan *bFrame
	syncTableCh  chan [][]chan int
	completed    chan bool
	forceRefresh chan<- time.Time

	// done is closed by Bar's goroutine, after cacheState is written
	done chan struct{}
	// shutdown is closed from master Progress goroutine only
	shutdown chan struct{}

	arbitraryCurrent struct {
		sync.Mutex
		current int64
	}

	dlogger *log.Logger
}

type (
	bState struct {
		filler             Filler
		extender           Filler
		id                 int
		width              int
		total              int64
		current            int64
		trimSpace          bool
		toComplete         bool
		removeOnComplete   bool
		barClearOnComplete bool
		completeFlushed    bool
		aDecorators        []decor.Decorator
		pDecorators        []decor.Decorator
		amountReceivers    []decor.AmountReceiver
		shutdownListeners  []decor.ShutdownListener
		bufP, bufB, bufA   *bytes.Buffer
		bufE               *bytes.Buffer
		panicMsg           string

		// priority overrides *Bar's priority, if set
		priority int
		// runningBar is a key for *pState.parkedBars
		runningBar *Bar
	}
	bFrame struct {
		rd               io.Reader
		extendedLines    int
		toShutdown       bool
		removeOnComplete bool
	}
)

func newBar(
	ctx context.Context,
	wg *sync.WaitGroup,
	forceRefresh chan<- time.Time,
	bs *bState,
	dlogger *log.Logger,
) *Bar {

	bs.bufP = bytes.NewBuffer(make([]byte, 0, bs.width))
	bs.bufB = bytes.NewBuffer(make([]byte, 0, bs.width))
	bs.bufA = bytes.NewBuffer(make([]byte, 0, bs.width))
	if bs.extender != nil {
		bs.bufE = bytes.NewBuffer(make([]byte, 0, bs.width))
	}

	bar := &Bar{
		priority:     bs.priority,
		operateState: make(chan func(*bState)),
		bFrameCh:     make(chan *bFrame, 1),
		syncTableCh:  make(chan [][]chan int),
		completed:    make(chan bool),
		done:         make(chan struct{}),
		shutdown:     make(chan struct{}),
		forceRefresh: forceRefresh,
		dlogger:      dlogger,
	}

	go bar.serve(ctx, wg, bs)
	return bar
}

// RemoveAllPrependers removes all prepend functions.
func (b *Bar) RemoveAllPrependers() {
	select {
	case b.operateState <- func(s *bState) { s.pDecorators = nil }:
	case <-b.done:
	}
}

// RemoveAllAppenders removes all append functions.
func (b *Bar) RemoveAllAppenders() {
	select {
	case b.operateState <- func(s *bState) { s.aDecorators = nil }:
	case <-b.done:
	}
}

// ProxyReader wraps r with metrics required for progress tracking.
func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
	if r == nil {
		return nil
	}
	rc, ok := r.(io.ReadCloser)
	if !ok {
		rc = ioutil.NopCloser(r)
	}
	return &proxyReader{rc, b, time.Now()}
}

// ID returs id of the bar.
func (b *Bar) ID() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- s.id }:
		return <-result
	case <-b.done:
		return b.cacheState.id
	}
}

// Current returns bar's current number, in other words sum of all increments.
func (b *Bar) Current() int64 {
	result := make(chan int64)
	select {
	case b.operateState <- func(s *bState) { result <- s.current }:
		return <-result
	case <-b.done:
		return b.cacheState.current
	}
}

// SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(amount int64) {
	b.operateState <- func(s *bState) {
		if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
			f.SetRefill(amount)
		}
	}
}

// SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event.
func (b *Bar) SetTotal(total int64, final bool) bool {
	select {
	case b.operateState <- func(s *bState) {
		if total > 0 {
			s.total = total
		}
		if final && !s.toComplete {
			s.current = s.total
			s.toComplete = true
			go b.refreshNowTillShutdown()
		}
	}:
		return true
	case <-b.done:
		return false
	}
}

// SetCurrent sets progress' current to arbitrary amount.
func (b *Bar) SetCurrent(current int64, wdd ...time.Duration) {
	if current <= 0 {
		return
	}
	b.arbitraryCurrent.Lock()
	last := b.arbitraryCurrent.current
	b.IncrBy(int(current-last), wdd...)
	b.arbitraryCurrent.current = current
	b.arbitraryCurrent.Unlock()
}

// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
	b.IncrBy(1)
}

// IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start), which expected
// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
	select {
	case b.operateState <- func(s *bState) {
		s.current += int64(n)
		if s.current >= s.total && !s.toComplete {
			s.current = s.total
			s.toComplete = true
			go b.refreshNowTillShutdown()
		}
		for _, ar := range s.amountReceivers {
			ar.NextAmount(n, wdd...)
		}
	}:
	case <-b.done:
	}
}

// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
	// omit select here, because primary usage of the method is for loop
	// condition, like for !bar.Completed() {...} so when toComplete=true
	// it is called once (at which time, the bar is still alive), then
	// quits the loop and never suppose to be called afterwards.
	return <-b.completed
}

func (b *Bar) wSyncTable() [][]chan int {
	select {
	case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
		return <-b.syncTableCh
	case <-b.done:
		return b.cacheState.wSyncTable()
	}
}

func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
	defer wg.Done()
	cancel := ctx.Done()
	for {
		select {
		case op := <-b.operateState:
			op(s)
		case b.completed <- s.toComplete:
		case <-cancel:
			s.toComplete = true
			cancel = nil
		case <-b.shutdown:
			b.cacheState = s
			close(b.done)
			// Notifying decorators about shutdown event
			for _, sl := range s.shutdownListeners {
				sl.Shutdown()
			}
			return
		}
	}
}

func (b *Bar) render(debugOut io.Writer, tw int) {
	select {
	case b.operateState <- func(s *bState) {
		defer func() {
			// recovering if user defined decorator panics for example
			if p := recover(); p != nil {
				s.panicMsg = fmt.Sprintf("panic: %v", p)
				b.dlogger.Println(s.panicMsg)
				b.bFrameCh <- &bFrame{
					rd:         strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
					toShutdown: true,
				}
			}
		}()
		r := s.draw(tw)
		var extendedLines int
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			extendedLines = countLines(s.bufE.Bytes())
			r = io.MultiReader(r, s.bufE)
		}
		b.bFrameCh <- &bFrame{
			rd:               r,
			extendedLines:    extendedLines,
			toShutdown:       s.toComplete && !s.completeFlushed,
			removeOnComplete: s.removeOnComplete,
		}
		s.completeFlushed = s.toComplete
	}:
	case <-b.done:
		s := b.cacheState
		r := s.draw(tw)
		var extendedLines int
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			extendedLines = countLines(s.bufE.Bytes())
			r = io.MultiReader(r, s.bufE)
		}
		b.bFrameCh <- &bFrame{
			rd:            r,
			extendedLines: extendedLines,
		}
	}
}

func (s *bState) draw(termWidth int) io.Reader {
	if s.panicMsg != "" {
		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
	}

	stat := newStatistics(s)

	for _, d := range s.pDecorators {
		s.bufP.WriteString(d.Decor(stat))
	}

	for _, d := range s.aDecorators {
		s.bufA.WriteString(d.Decor(stat))
	}

	if s.barClearOnComplete && s.completeFlushed {
		s.bufA.WriteByte('\n')
		return io.MultiReader(s.bufP, s.bufA)
	}

	prependCount := utf8.RuneCount(s.bufP.Bytes())
	appendCount := utf8.RuneCount(s.bufA.Bytes())

	if !s.trimSpace {
		// reserve space for edge spaces
		termWidth -= 2
		s.bufB.WriteByte(' ')
	}

	calcWidth := s.width
	if prependCount+s.width+appendCount > termWidth {
		calcWidth = termWidth - prependCount - appendCount
	}
	s.filler.Fill(s.bufB, calcWidth, stat)

	if !s.trimSpace {
		s.bufB.WriteByte(' ')
	}

	s.bufA.WriteByte('\n')
	return io.MultiReader(s.bufP, s.bufB, s.bufA)
}

func (s *bState) wSyncTable() [][]chan int {
	columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
	var pCount int
	for _, d := range s.pDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			pCount++
		}
	}
	var aCount int
	for _, d := range s.aDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			aCount++
		}
	}
	table := make([][]chan int, 2)
	table[0] = columns[0:pCount]
	table[1] = columns[pCount : pCount+aCount : pCount+aCount]
	return table
}

func (b *Bar) refreshNowTillShutdown() {
	for {
		select {
		case b.forceRefresh <- time.Now():
		case <-b.shutdown:
			return
		}
	}
}

func newStatistics(s *bState) *decor.Statistics {
	return &decor.Statistics{
		ID:        s.id,
		Completed: s.completeFlushed,
		Total:     s.total,
		Current:   s.current,
	}
}

func countLines(b []byte) int {
	return bytes.Count(b, []byte("\n"))
}
<commit_msg>consice code<commit_after>package mpb

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb/v4/decor"
)

// Filler interface.
// Bar renders by calling Filler's Fill method. You can literally have
// any bar kind, by implementing this interface and passing it to the
// Add method.
type Filler interface {
	Fill(w io.Writer, width int, stat *decor.Statistics)
}

// FillerFunc is function type adapter to convert function into Filler.
type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)

func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
	f(w, width, stat)
}

// Bar represents a progress Bar.
type Bar struct {
	priority int
	index    int

	cacheState   *bState
	operateState chan func(*bState)
	bFrameCh     chan *bFrame
	syncTableCh  chan [][]chan int
	completed    chan bool
	forceRefresh chan<- time.Time

	// done is closed by Bar's goroutine, after cacheState is written
	done chan struct{}
	// shutdown is closed from master Progress goroutine only
	shutdown chan struct{}

	arbitraryCurrent struct {
		sync.Mutex
		current int64
	}

	dlogger *log.Logger
}

type (
	bState struct {
		filler             Filler
		extender           Filler
		id                 int
		width              int
		total              int64
		current            int64
		trimSpace          bool
		toComplete         bool
		removeOnComplete   bool
		barClearOnComplete bool
		completeFlushed    bool
		aDecorators        []decor.Decorator
		pDecorators        []decor.Decorator
		amountReceivers    []decor.AmountReceiver
		shutdownListeners  []decor.ShutdownListener
		bufP, bufB, bufA   *bytes.Buffer
		bufE               *bytes.Buffer
		panicMsg           string

		// priority overrides *Bar's priority, if set
		priority int
		// runningBar is a key for *pState.parkedBars
		runningBar *Bar
	}
	bFrame struct {
		rd               io.Reader
		extendedLines    int
		toShutdown       bool
		removeOnComplete bool
	}
)

func newBar(
	ctx context.Context,
	wg *sync.WaitGroup,
	forceRefresh chan<- time.Time,
	bs *bState,
	dlogger *log.Logger,
) *Bar {

	bs.bufP = bytes.NewBuffer(make([]byte, 0, bs.width))
	bs.bufB = bytes.NewBuffer(make([]byte, 0, bs.width))
	bs.bufA = bytes.NewBuffer(make([]byte, 0, bs.width))
	if bs.extender != nil {
		bs.bufE = bytes.NewBuffer(make([]byte, 0, bs.width))
	}

	bar := &Bar{
		priority:     bs.priority,
		operateState: make(chan func(*bState)),
		bFrameCh:     make(chan *bFrame, 1),
		syncTableCh:  make(chan [][]chan int),
		completed:    make(chan bool),
		done:         make(chan struct{}),
		shutdown:     make(chan struct{}),
		forceRefresh: forceRefresh,
		dlogger:      dlogger,
	}

	go bar.serve(ctx, wg, bs)
	return bar
}

// RemoveAllPrependers removes all prepend functions.
func (b *Bar) RemoveAllPrependers() {
	select {
	case b.operateState <- func(s *bState) { s.pDecorators = nil }:
	case <-b.done:
	}
}

// RemoveAllAppenders removes all append functions.
func (b *Bar) RemoveAllAppenders() {
	select {
	case b.operateState <- func(s *bState) { s.aDecorators = nil }:
	case <-b.done:
	}
}

// ProxyReader wraps r with metrics required for progress tracking.
func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
	if r == nil {
		return nil
	}
	rc, ok := r.(io.ReadCloser)
	if !ok {
		rc = ioutil.NopCloser(r)
	}
	return &proxyReader{rc, b, time.Now()}
}

// ID returs id of the bar.
func (b *Bar) ID() int {
	result := make(chan int)
	select {
	case b.operateState <- func(s *bState) { result <- s.id }:
		return <-result
	case <-b.done:
		return b.cacheState.id
	}
}

// Current returns bar's current number, in other words sum of all increments.
func (b *Bar) Current() int64 {
	result := make(chan int64)
	select {
	case b.operateState <- func(s *bState) { result <- s.current }:
		return <-result
	case <-b.done:
		return b.cacheState.current
	}
}

// SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(amount int64) {
	b.operateState <- func(s *bState) {
		if f, ok := s.filler.(interface{ SetRefill(int64) }); ok {
			f.SetRefill(amount)
		}
	}
}

// SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event.
func (b *Bar) SetTotal(total int64, final bool) bool {
	select {
	case b.operateState <- func(s *bState) {
		if total > 0 {
			s.total = total
		}
		if final && !s.toComplete {
			s.current = s.total
			s.toComplete = true
			go b.refreshNowTillShutdown()
		}
	}:
		return true
	case <-b.done:
		return false
	}
}

// SetCurrent sets progress' current to arbitrary amount.
func (b *Bar) SetCurrent(current int64, wdd ...time.Duration) {
	if current <= 0 {
		return
	}
	b.arbitraryCurrent.Lock()
	last := b.arbitraryCurrent.current
	b.IncrBy(int(current-last), wdd...)
	b.arbitraryCurrent.current = current
	b.arbitraryCurrent.Unlock()
}

// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
	b.IncrBy(1)
}

// IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start), which expected
// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
	select {
	case b.operateState <- func(s *bState) {
		s.current += int64(n)
		if s.current >= s.total && !s.toComplete {
			s.current = s.total
			s.toComplete = true
			go b.refreshNowTillShutdown()
		}
		for _, ar := range s.amountReceivers {
			ar.NextAmount(n, wdd...)
		}
	}:
	case <-b.done:
	}
}

// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
	// omit select here, because primary usage of the method is for loop
	// condition, like for !bar.Completed() {...} so when toComplete=true
	// it is called once (at which time, the bar is still alive), then
	// quits the loop and never suppose to be called afterwards.
	return <-b.completed
}

func (b *Bar) wSyncTable() [][]chan int {
	select {
	case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
		return <-b.syncTableCh
	case <-b.done:
		return b.cacheState.wSyncTable()
	}
}

func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
	defer wg.Done()
	cancel := ctx.Done()
	for {
		select {
		case op := <-b.operateState:
			op(s)
		case b.completed <- s.toComplete:
		case <-cancel:
			s.toComplete = true
			cancel = nil
		case <-b.shutdown:
			b.cacheState = s
			close(b.done)
			// Notifying decorators about shutdown event
			for _, sl := range s.shutdownListeners {
				sl.Shutdown()
			}
			return
		}
	}
}

func (b *Bar) render(debugOut io.Writer, tw int) {
	select {
	case b.operateState <- func(s *bState) {
		defer func() {
			// recovering if user defined decorator panics for example
			if p := recover(); p != nil {
				s.panicMsg = fmt.Sprintf("panic: %v", p)
				b.dlogger.Println(s.panicMsg)
				b.bFrameCh <- &bFrame{
					rd:         strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
					toShutdown: true,
				}
			}
		}()
		frame := &bFrame{
			rd:               s.draw(tw),
			toShutdown:       s.toComplete && !s.completeFlushed,
			removeOnComplete: s.removeOnComplete,
		}
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			frame.extendedLines = countLines(s.bufE.Bytes())
			frame.rd = io.MultiReader(frame.rd, s.bufE)
		}
		b.bFrameCh <- frame
		s.completeFlushed = s.toComplete
	}:
	case <-b.done:
		s := b.cacheState
		frame := &bFrame{
			rd: s.draw(tw),
		}
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			frame.extendedLines = countLines(s.bufE.Bytes())
			frame.rd = io.MultiReader(frame.rd, s.bufE)
		}
		b.bFrameCh <- frame
	}
}

func (s *bState) draw(termWidth int) io.Reader {
	if s.panicMsg != "" {
		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
	}

	stat := newStatistics(s)

	for _, d := range s.pDecorators {
		s.bufP.WriteString(d.Decor(stat))
	}

	for _, d := range s.aDecorators {
		s.bufA.WriteString(d.Decor(stat))
	}

	if s.barClearOnComplete && s.completeFlushed {
		s.bufA.WriteByte('\n')
		return io.MultiReader(s.bufP, s.bufA)
	}

	prependCount := utf8.RuneCount(s.bufP.Bytes())
	appendCount := utf8.RuneCount(s.bufA.Bytes())

	if !s.trimSpace {
		// reserve space for edge spaces
		termWidth -= 2
		s.bufB.WriteByte(' ')
	}

	calcWidth := s.width
	if prependCount+s.width+appendCount > termWidth {
		calcWidth = termWidth - prependCount - appendCount
	}
	s.filler.Fill(s.bufB, calcWidth, stat)

	if !s.trimSpace {
		s.bufB.WriteByte(' ')
	}

	s.bufA.WriteByte('\n')
	return io.MultiReader(s.bufP, s.bufB, s.bufA)
}

func (s *bState) wSyncTable() [][]chan int {
	columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
	var pCount int
	for _, d := range s.pDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			pCount++
		}
	}
	var aCount int
	for _, d := range s.aDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			aCount++
		}
	}
	table := make([][]chan int, 2)
	table[0] = columns[0:pCount]
	table[1] = columns[pCount : pCount+aCount : pCount+aCount]
	return table
}

func (b *Bar) refreshNowTillShutdown() {
	for {
		select {
		case b.forceRefresh <- time.Now():
		case <-b.shutdown:
			return
		}
	}
}

func newStatistics(s *bState) *decor.Statistics {
	return &decor.Statistics{
		ID:        s.id,
		Completed: s.completeFlushed,
		Total:     s.total,
		Current:   s.current,
	}
}

func countLines(b []byte) int {
	return bytes.Count(b, []byte("\n"))
}
<|endoftext|>