<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"sort"
	"strconv"
	"syscall"
	"time"

	"github.com/gophergala2016/goad"
	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/nsf/termbox-go"
	"github.com/gophergala2016/goad/queue"
)

var (
	url         string
	concurrency uint
	requests    uint
	timeout     uint
	region      string
)

const coldef = termbox.ColorDefault
const nano = 1000000000

func main() {
	flag.UintVar(&concurrency, "c", 10, "number of concurrent requests")
	flag.UintVar(&requests, "n", 1000, "number of total requests to make")
	flag.UintVar(&timeout, "t", 15, "request timeout in seconds")
	flag.StringVar(&region, "r", "us-east-1", "AWS regions to run in")
	flag.Parse()

	if len(flag.Args()) < 1 {
		fmt.Println("You must specify a URL as a last argument")
		os.Exit(1)
	}

	url = flag.Args()[0]

	test, testerr := goad.NewTest(&goad.TestConfig{
		URL:            url,
		Concurrency:    concurrency,
		TotalRequests:  requests,
		RequestTimeout: time.Duration(timeout) * time.Second,
		Region:         region,
	})
	if testerr != nil {
		fmt.Println(testerr)
		os.Exit(1)
	}

	var finalResult queue.RegionsAggData
	defer printSummary(&finalResult)

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // but interrupts from kbd are blocked by termbox

	start(test, &finalResult, sigChan)
}

func start(test *goad.Test, finalResult *queue.RegionsAggData, sigChan chan os.Signal) {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}

	defer termbox.Close()

	resultChan := test.Start()
	termbox.Sync()
	renderString(0, 0, "Launching on AWS...", coldef, coldef)
	_, h := termbox.Size()
	renderString(0, h-1, "Press ctrl-c to interrupt", coldef, coldef)
	termbox.Flush()

	go func() {
		for {
			event := termbox.PollEvent()
			if event.Key == 3 {
				sigChan <- syscall.SIGINT
			}
		}
	}()

outer:
	for {
		select {
		case result, ok := <-resultChan:
			if !ok {
				break outer
			}
			//		result.Regions["eu-west-1"] = result.Regions["us-east-1"]
			// sort so that regions always appear in the same order
			var regions []string
			for key := range result.Regions {
				regions = append(regions, key)
			}
			sort.Strings(regions)
			y := 3
			totalReqs := 0
			for _, region := range regions {
				data := result.Regions[region]
				totalReqs += data.TotalReqs
				y = renderRegion(data, y)
				y++
			}

			y = 0
			percentDone := float64(totalReqs) / float64(result.TotalExpectedRequests)
			drawProgressBar(percentDone, y)

			termbox.Flush()
			finalResult.Regions = result.Regions

		case <-sigChan:
			break outer
		}
	}
}

// renderRegion returns the y for the next empty line
func renderRegion(data queue.AggData, y int) int {
	x := 0
	renderString(x, y, "Region: ", termbox.ColorWhite, termbox.ColorBlue)
	x += 8
	regionStr := fmt.Sprintf("%-10s", data.Region)
	renderString(x, y, regionStr, termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlue)
	x = 0
	y++
	headingStr := "   TotReqs   TotBytes    AvgTime   AvgReq/s"
	renderString(x, y, headingStr, coldef|termbox.AttrBold, coldef)
	y++
	resultStr := fmt.Sprintf("%10d %10d   %7.3fs %10.2f", data.TotalReqs, data.TotBytesRead, float64(data.AveTimeForReq)/nano, data.AveReqPerSec)
	renderString(x, y, resultStr, coldef, coldef)
	y++
	headingStr = "   Slowest    Fastest   Timeouts  TotErrors"
	renderString(x, y, headingStr, coldef|termbox.AttrBold, coldef)
	y++
	resultStr = fmt.Sprintf("  %7.3fs   %7.3fs %10d %10d", float64(data.Slowest)/nano, float64(data.Fastest)/nano, data.TotalTimedOut, totErrors(&data))
	renderString(x, y, resultStr, coldef, coldef)
	y++

	return y
}

func totErrors(data *queue.AggData) int {
	var okReqs int
	for statusStr, value := range data.Statuses {
		status, _ := strconv.Atoi(statusStr)
		if status >= 200 && status <= 299 {
			okReqs += value
		}
	}
	return data.TotalReqs - okReqs
}

func drawProgressBar(percent float64, y int) {
	x := 0
	percentStr := fmt.Sprintf("%5.1f%%            ", percent*100)
	renderString(x, y, percentStr, coldef, coldef)
	y++

	hashes := int(percent * 50)
	if percent > 0.98 {
		hashes = 50
	}
	renderString(x, y, "[", coldef, coldef)

	for x++; x <= hashes; x++ {
		renderString(x, y, "#", coldef, coldef)
	}
	renderString(51, y, "]", coldef, coldef)
}

func renderString(x int, y int, str string, f termbox.Attribute, b termbox.Attribute) {
	for i, c := range str {
		termbox.SetCell(x+i, y, c, f, b)
	}
}

func boldPrintln(msg string) {
	fmt.Printf("\033[1m%s\033[0m\n", msg)
}

func printData(data *queue.AggData) {
	boldPrintln("   TotReqs   TotBytes    AvgTime   AvgReq/s")
	fmt.Printf("%10d %10d   %7.3fs %10.2f\n", data.TotalReqs, data.TotBytesRead, float64(data.AveTimeForReq)/nano, data.AveReqPerSec)
	boldPrintln("   Slowest    Fastest   Timeouts  TotErrors")
	fmt.Printf("  %7.3fs   %7.3fs %10d %10d", float64(data.Slowest)/nano, float64(data.Fastest)/nano, data.TotalTimedOut, totErrors(data))
	fmt.Println("")
}

func printSummary(result *queue.RegionsAggData) {
	if len(result.Regions) == 0 {
		boldPrintln("No results received")
		return
	}
	boldPrintln("Regional results")
	fmt.Println("")

	for region, data := range result.Regions {
		fmt.Println("Region: " + region)
		printData(&data)
	}

	overall := queue.SumRegionResults(result)

	fmt.Println("")
	boldPrintln("Overall")
	fmt.Println("")
	printData(overall)

	boldPrintln("HTTPStatus   Requests")
	for statusStr, value := range overall.Statuses {
		fmt.Printf("%10s %10d\n", statusStr, value)
	}
	fmt.Println("")
}
<commit_msg>Humanise bytes<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"sort"
	"strconv"
	"syscall"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/gophergala2016/goad"
	"github.com/gophergala2016/goad/Godeps/_workspace/src/github.com/nsf/termbox-go"
	"github.com/gophergala2016/goad/queue"
)

var (
	url         string
	concurrency uint
	requests    uint
	timeout     uint
	region      string
)

const coldef = termbox.ColorDefault
const nano = 1000000000

func main() {
	flag.UintVar(&concurrency, "c", 10, "number of concurrent requests")
	flag.UintVar(&requests, "n", 1000, "number of total requests to make")
	flag.UintVar(&timeout, "t", 15, "request timeout in seconds")
	flag.StringVar(&region, "r", "us-east-1", "AWS regions to run in")
	flag.Parse()

	if len(flag.Args()) < 1 {
		fmt.Println("You must specify a URL as a last argument")
		os.Exit(1)
	}

	url = flag.Args()[0]

	test, testerr := goad.NewTest(&goad.TestConfig{
		URL:            url,
		Concurrency:    concurrency,
		TotalRequests:  requests,
		RequestTimeout: time.Duration(timeout) * time.Second,
		Region:         region,
	})
	if testerr != nil {
		fmt.Println(testerr)
		os.Exit(1)
	}

	var finalResult queue.RegionsAggData
	defer printSummary(&finalResult)

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // but interrupts from kbd are blocked by termbox

	start(test, &finalResult, sigChan)
}

func start(test *goad.Test, finalResult *queue.RegionsAggData, sigChan chan os.Signal) {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}

	defer termbox.Close()

	resultChan := test.Start()
	termbox.Sync()
	renderString(0, 0, "Launching on AWS...", coldef, coldef)
	_, h := termbox.Size()
	renderString(0, h-1, "Press ctrl-c to interrupt", coldef, coldef)
	termbox.Flush()

	go func() {
		for {
			event := termbox.PollEvent()
			if event.Key == 3 {
				sigChan <- syscall.SIGINT
			}
		}
	}()

outer:
	for {
		select {
		case result, ok := <-resultChan:
			if !ok {
				break outer
			}
			//		result.Regions["eu-west-1"] = result.Regions["us-east-1"]
			// sort so that regions always appear in the same order
			var regions []string
			for key := range result.Regions {
				regions = append(regions, key)
			}
			sort.Strings(regions)
			y := 3
			totalReqs := 0
			for _, region := range regions {
				data := result.Regions[region]
				totalReqs += data.TotalReqs
				y = renderRegion(data, y)
				y++
			}

			y = 0
			percentDone := float64(totalReqs) / float64(result.TotalExpectedRequests)
			drawProgressBar(percentDone, y)

			termbox.Flush()
			finalResult.Regions = result.Regions

		case <-sigChan:
			break outer
		}
	}
}

// renderRegion returns the y for the next empty line
func renderRegion(data queue.AggData, y int) int {
	x := 0
	renderString(x, y, "Region: ", termbox.ColorWhite, termbox.ColorBlue)
	x += 8
	regionStr := fmt.Sprintf("%-10s", data.Region)
	renderString(x, y, regionStr, termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlue)
	x = 0
	y++
	headingStr := "   TotReqs   TotBytes    AvgTime   AvgReq/s"
	renderString(x, y, headingStr, coldef|termbox.AttrBold, coldef)
	y++
	resultStr := fmt.Sprintf("%10d %10s   %7.3fs %10.2f", data.TotalReqs, humanize.Bytes(uint64(data.TotBytesRead)), float64(data.AveTimeForReq)/nano, data.AveReqPerSec)
	renderString(x, y, resultStr, coldef, coldef)
	y++
	headingStr = "   Slowest    Fastest   Timeouts  TotErrors"
	renderString(x, y, headingStr, coldef|termbox.AttrBold, coldef)
	y++
	resultStr = fmt.Sprintf("  %7.3fs   %7.3fs %10d %10d", float64(data.Slowest)/nano, float64(data.Fastest)/nano, data.TotalTimedOut, totErrors(&data))
	renderString(x, y, resultStr, coldef, coldef)
	y++

	return y
}

func totErrors(data *queue.AggData) int {
	var okReqs int
	for statusStr, value := range data.Statuses {
		status, _ := strconv.Atoi(statusStr)
		if status >= 200 && status <= 299 {
			okReqs += value
		}
	}
	return data.TotalReqs - okReqs
}

func drawProgressBar(percent float64, y int) {
	x := 0
	percentStr := fmt.Sprintf("%5.1f%%            ", percent*100)
	renderString(x, y, percentStr, coldef, coldef)
	y++

	hashes := int(percent * 50)
	if percent > 0.98 {
		hashes = 50
	}
	renderString(x, y, "[", coldef, coldef)

	for x++; x <= hashes; x++ {
		renderString(x, y, "#", coldef, coldef)
	}
	renderString(51, y, "]", coldef, coldef)
}

func renderString(x int, y int, str string, f termbox.Attribute, b termbox.Attribute) {
	for i, c := range str {
		termbox.SetCell(x+i, y, c, f, b)
	}
}

func boldPrintln(msg string) {
	fmt.Printf("\033[1m%s\033[0m\n", msg)
}

func printData(data *queue.AggData) {
	boldPrintln("   TotReqs   TotBytes    AvgTime   AvgReq/s")
	fmt.Printf("%10d %10s   %7.3fs %10.2f\n", data.TotalReqs, humanize.Bytes(uint64(data.TotBytesRead)), float64(data.AveTimeForReq)/nano, data.AveReqPerSec)
	boldPrintln("   Slowest    Fastest   Timeouts  TotErrors")
	fmt.Printf("  %7.3fs   %7.3fs %10d %10d", float64(data.Slowest)/nano, float64(data.Fastest)/nano, data.TotalTimedOut, totErrors(data))
	fmt.Println("")
}

func printSummary(result *queue.RegionsAggData) {
	if len(result.Regions) == 0 {
		boldPrintln("No results received")
		return
	}
	boldPrintln("Regional results")
	fmt.Println("")

	for region, data := range result.Regions {
		fmt.Println("Region: " + region)
		printData(&data)
	}

	overall := queue.SumRegionResults(result)

	fmt.Println("")
	boldPrintln("Overall")
	fmt.Println("")
	printData(overall)

	boldPrintln("HTTPStatus   Requests")
	for statusStr, value := range overall.Statuses {
		fmt.Printf("%10s %10d\n", statusStr, value)
	}
	fmt.Println("")
}
<|endoftext|>


<commit_before>package cli

import "errors"
import "flag"
import "fmt"
import "os"
import "sort"

var ErrUsage = errors.New("Invalid usage")

type CommandRunner func(string, []string) error

type Command struct {
	*flag.FlagSet
	Name     string
	Synopsis string
	Runner   CommandRunner
}

func NewCommand(name, synopsis string, runner CommandRunner) *Command {
	cmd := &Command{
		FlagSet:  flag.NewFlagSet(name, flag.ContinueOnError),
		Name:     name,
		Synopsis: synopsis,
		Runner:   runner,
	}
	cmd.FlagSet.Usage = cmd.Usage
	return cmd
}

func (cmd *Command) Run() error {
	if !cmd.Parsed() {
		return errors.New("Cannot Run() before Parse()")
	}
	if err := cmd.Runner(cmd.Name, cmd.Args()); err == ErrUsage {
		cmd.Usage()
		os.Exit(2)
		return nil
	} else {
		return err
	}
}

func (cmd *Command) Usage() {
	fmt.Fprintln(os.Stderr, "Usage:", cmd)
	cmd.PrintDefaults()
}

func (cmd *Command) String() string {
	return cmd.Name + " " + cmd.Synopsis
}

type Cli struct {
	*flag.FlagSet
	Name     string
	Commands map[string]*Command
}

func (cli *Cli) Usage() {
	fmt.Fprintf(os.Stderr,
		"Usage: %s [flags] command [args...]\nKnown commands:\n", cli.Name)
	commands := make([]string, 0, len(cli.Commands))
	for cmd := range cli.Commands {
		commands = append(commands, cmd)
	}
	sort.Strings(commands)
	for _, cmd := range commands {
		fmt.Fprintf(os.Stderr, "  %s %s\n", cli.Name, cli.Commands[cmd])
	}
	fmt.Fprintf(os.Stderr, "  %s help [COMMAND]\nGlobal flags:\n", cli.Name)
	cli.PrintDefaults()
}

func NewCli(name string) *Cli {
	if name == "" {
		name = os.Args[0]
	}
	rv := &Cli{
		FlagSet:  flag.NewFlagSet(name, flag.ContinueOnError),
		Name:     name,
		Commands: make(map[string]*Command),
	}
	rv.FlagSet.Usage = rv.Usage
	return rv
}

func (cli *Cli) AddCommand(name, synopsis string, runner CommandRunner) {
	cli.Commands[name] = NewCommand(name, synopsis, runner)
}

func (cli *Cli) MustGetCommand(name string) *Command {
	if cmd, hasCommand := cli.Commands[name]; hasCommand {
		return cmd
	} else {
		fmt.Fprintln(os.Stderr, "Unknown command:", name)
		cli.Usage()
		os.Exit(2)
	}
	return nil
}

func (cli *Cli) Parse(args []string) {
	if args == nil {
		args = os.Args[1:]
	}

	if err := cli.FlagSet.Parse(args); err != nil {
		if err == flag.ErrHelp {
			os.Exit(0)
		} else {
			os.Exit(2)
		}
	}

	if cli.NArg() == 0 || cli.Arg(0) == "help" {
		if cli.NArg() > 1 {
			cli.MustGetCommand(cli.Arg(1)).Usage()
		} else {
			cli.Usage()
		}
		os.Exit(0)
	}

	cli.MustGetCommand(cli.Arg(0))
}

func (cli *Cli) Run() error {
	cmd := cli.Commands[cli.Arg(0)]
	if err := cmd.Parse(cli.Args()[1:]); err != nil {
		if err == flag.ErrHelp {
			os.Exit(0)
		} else {
			os.Exit(2)
		}
	}
	return cmd.Run()
}
<commit_msg>Nicer usage info<commit_after>package cli

import "errors"
import "flag"
import "fmt"
import "os"
import "sort"
import "strings"

var ErrUsage = errors.New("Invalid usage")

type CommandRunner func(string, []string) error

type Command struct {
	*flag.FlagSet
	Name     string
	CliName  string
	Synopsis string
	Runner   CommandRunner
}

func NewCommand(name, synopsis string, runner CommandRunner) *Command {
	cmd := &Command{
		FlagSet:  flag.NewFlagSet(name, flag.ContinueOnError),
		Name:     name,
		Synopsis: synopsis,
		Runner:   runner,
	}
	cmd.FlagSet.Usage = cmd.Usage
	return cmd
}

func (cmd *Command) Run() error {
	if !cmd.Parsed() {
		return errors.New("Cannot Run() before Parse()")
	}
	if err := cmd.Runner(cmd.Name, cmd.Args()); err == ErrUsage {
		cmd.Usage()
		os.Exit(2)
		return nil
	} else {
		return err
	}
}

func (cmd *Command) Usage() {
	fmt.Fprintln(os.Stderr, "Usage:", cmd)
	cmd.PrintDefaults()
}

func (cmd *Command) String() string {
	pieces := []string{}
	if cmd.CliName != "" {
		pieces = append(pieces, cmd.CliName)
	}
	pieces = append(pieces, cmd.Name, cmd.Synopsis)
	return strings.Join(pieces, " ")
}

type Cli struct {
	*flag.FlagSet
	Name     string
	Commands map[string]*Command
}

func (cli *Cli) Usage() {
	fmt.Fprintf(os.Stderr,
		"Usage: %s [flags] command [args...]\nKnown commands:\n  %s help [COMMAND] -- show help\n", cli.Name, cli.Name)
	commands := make([]string, 0, len(cli.Commands))
	for cmd := range cli.Commands {
		commands = append(commands, cmd)
	}
	sort.Strings(commands)
	for _, cmd := range commands {
		fmt.Fprintf(os.Stderr, "  %s\n", cli.Commands[cmd])
	}
	fmt.Fprintln(os.Stderr, "Global flags:")
	cli.PrintDefaults()
}

func NewCli(name string) *Cli {
	if name == "" {
		name = os.Args[0]
	}
	rv := &Cli{
		FlagSet:  flag.NewFlagSet(name, flag.ContinueOnError),
		Name:     name,
		Commands: make(map[string]*Command),
	}
	rv.FlagSet.Usage = rv.Usage
	return rv
}

func (cli *Cli) AddCommand(name, synopsis string, runner CommandRunner) {
	cmd := NewCommand(name, synopsis, runner)
	cmd.CliName = cli.Name
	cli.Commands[name] = cmd
}

func (cli *Cli) MustGetCommand(name string) *Command {
	if cmd, hasCommand := cli.Commands[name]; hasCommand {
		return cmd
	} else {
		fmt.Fprintln(os.Stderr, "Unknown command:", name)
		cli.Usage()
		os.Exit(2)
	}
	return nil
}

func (cli *Cli) Parse(args []string) {
	if args == nil {
		args = os.Args[1:]
	}

	if err := cli.FlagSet.Parse(args); err != nil {
		if err == flag.ErrHelp {
			os.Exit(0)
		} else {
			os.Exit(2)
		}
	}

	if cli.NArg() == 0 || cli.Arg(0) == "help" {
		if cli.NArg() > 1 {
			cli.MustGetCommand(cli.Arg(1)).Usage()
		} else {
			cli.Usage()
		}
		os.Exit(0)
	}

	cli.MustGetCommand(cli.Arg(0))
}

func (cli *Cli) Run() error {
	cmd := cli.Commands[cli.Arg(0)]
	if err := cmd.Parse(cli.Args()[1:]); err != nil {
		if err == flag.ErrHelp {
			os.Exit(0)
		} else {
			os.Exit(2)
		}
	}
	return cmd.Run()
}
<|endoftext|>


<commit_before>package cli

import (
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/elpinal/coco3/config"
	"github.com/elpinal/coco3/eval"
	"github.com/elpinal/coco3/gate"
	"github.com/elpinal/coco3/parser"

	"github.com/elpinal/coco3/extra"
	eparser "github.com/elpinal/coco3/extra/parser"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
)

type CLI struct {
	In  io.Reader
	Out io.Writer
	Err io.Writer

	*config.Config

	db *sqlx.DB

	execute1 func([]byte) (action, error)
}

func (c *CLI) Run(args []string) int {
	f := flag.NewFlagSet("coco3", flag.ContinueOnError)
	f.SetOutput(c.Err)
	f.Usage = func() {
		c.Err.Write([]byte("coco3 is a shell.\n"))
		c.Err.Write([]byte("Usage:\n"))
		f.PrintDefaults()
	}

	if c.Config == nil {
		c.Config = &config.Config{}
	}
	c.Config.Init()
	flagC := f.String("c", "", "take first argument as a command to execute")
	flagE := f.Bool("extra", c.Config.Extra, "switch to extra mode")
	if err := f.Parse(args); err != nil {
		return 2
	}
	return c.run(f.Args(), flagC, flagE)
}

func (c *CLI) run(args []string, flagC *string, flagE *bool) int {
	for _, alias := range c.Config.Alias {
		eval.DefAlias(alias[0], alias[1])
	}

	for k, v := range c.Config.Env {
		err := os.Setenv(k, v)
		if err != nil {
			c.errorln(err)
			return 1
		}
	}

	setpath(c.Config.Paths)

	if *flagE {
		// If -extra flag is on, enable extra mode on any command executions.
		c.execute1 = c.executeExtra
	} else {
		c.execute1 = c.execute
	}

	if len(c.Config.StartUpCommand) > 0 {
		a, err := c.execute1(c.Config.StartUpCommand)
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}

	if *flagC != "" {
		a, err := c.execute1([]byte(*flagC))
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
		return 0
	}

	if len(args) > 0 {
		a, err := c.runFiles(args)
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
		return 0
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	histRunes, err := c.getHistory(c.Config.HistFile)
	if err != nil {
		c.errorln(err)
		return 1
	}
	g := gate.NewContext(ctx, c.Config, c.In, c.Out, c.Err, histRunes)
	for {
		a, err := c.interact(g)
		if err != nil {
			c.printExecError(err)
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}
	return 0
}

func (c *CLI) errorf(s string, a ...interface{}) {
	fmt.Fprintf(c.Err, s, a...)
}

func (c *CLI) errorln(s ...interface{}) {
	fmt.Fprintln(c.Err, s...)
}

func (c *CLI) errorp(s ...interface{}) {
	fmt.Fprint(c.Err, s...)
}

func (c *CLI) getHistory(filename string) ([][]rune, error) {
	db, err := sqlx.Connect("sqlite3", filename)
	if err != nil {
		return nil, errors.Wrap(err, "connecting history file")
	}
	_, err = db.Exec(schema)
	if err != nil {
		return nil, errors.Wrap(err, "initializing history file")
	}
	var history []string
	err = db.Select(&history, "select line from command_info")
	if err != nil {
		return nil, errors.Wrap(err, "restoring history")
	}
	// TODO: Is this way proper?
	c.db = db
	return sanitizeHistory(history), nil
}

func (c *CLI) printExecError(err error) {
	if pe, ok := err.(*eparser.ParseError); ok {
		c.errorln(pe.Verbose())
	} else {
		c.errorln(err)
	}
}

// setpath sets the PATH environment variable.
func setpath(args []string) {
	if len(args) == 0 {
		return
	}
	paths := filepath.SplitList(os.Getenv("PATH"))
	var newPaths []string
	for _, path := range paths {
		if contains(args, path) {
			continue
		}
		newPaths = append(newPaths, path)
	}
	newPaths = append(args, newPaths...)
	os.Setenv("PATH", strings.Join(newPaths, string(filepath.ListSeparator)))
}

func contains(xs []string, s string) bool {
	for _, x := range xs {
		if x == s {
			return true
		}
	}
	return false
}

func sanitizeHistory(history []string) [][]rune {
	histRunes := make([][]rune, 0, len(history))
	for _, line := range history {
		if line == "" {
			continue
		}
		l := len(histRunes)
		s := []rune(line)
		if l > 0 && compareRunes(histRunes[l-1], s) {
			continue
		}
		histRunes = append(histRunes, s)
	}
	return histRunes
}

func compareRunes(r1, r2 []rune) bool {
	if len(r1) != len(r2) {
		return false
	}
	for i, r := range r1 {
		if r2[i] != r {
			return false
		}
	}
	return true
}

func (c *CLI) interact(g gate.Gate) (action, error) {
	r, end, err := c.read(g)
	if err != nil {
		return nil, err
	}
	if end {
		return exit{code: 0}, nil
	}
	ch := c.writeHistory(r)
	a, err := c.execute1([]byte(string(r)))
	if err != nil {
		return a, err
	}
	return a, <-ch
}

func (c *CLI) read(g gate.Gate) ([]rune, bool, error) {
	defer c.Out.Write([]byte{'\n'})
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		return nil, false, err
	}
	defer func() {
		if err := terminal.Restore(0, oldState); err != nil {
			c.errorln(err)
		}
	}()
	r, end, err := g.Read()
	if err != nil {
		return nil, false, err
	}
	return r, end, nil
}

func (c *CLI) writeHistory(r []rune) <-chan error {
	startTime := time.Now()
	ch := make(chan error)
	go func() {
		_, err := c.db.Exec("insert into command_info (time, line) values ($1, $2)", startTime, string(r))
		if err != nil {
			ch <- errors.Wrap(err, "saving history")
			return
		}
		ch <- nil
	}()
	return ch
}

const schema = `
create table if not exists command_info (
    time datetime,
    line text
)`

func (c *CLI) execute(b []byte) (action, error) {
	f, err := parser.ParseSrc(b)
	if err != nil {
		return nil, err
	}
	e := eval.New(c.In, c.Out, c.Err, c.db)
	err = e.Eval(f.Lines)
	select {
	case code := <-e.ExitCh:
		return exit{code: code}, nil
	default:
	}
	return nil, err
}

func (c *CLI) executeExtra(b []byte) (action, error) {
	cmd, err := eparser.Parse(b)
	if err != nil {
		return nil, err
	}
	e := extra.New(extra.Option{DB: c.db})
	err = e.Eval(cmd)
	if err == nil {
		return nil, nil
	}
	if pe, ok := err.(*eparser.ParseError); ok {
		pe.Src = string(b)
	}
	return nil, err
}

func (c *CLI) runFiles(files []string) (action, error) {
	for _, file := range files {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, err
		}
		a, err := c.execute1(b)
		if err != nil {
			return nil, err
		}
		if a != nil {
			return a, nil
		}
	}
	return nil, nil
}

type action interface {
	act()
}

type exit struct {
	code int
}

func (e exit) act() {}
<commit_msg>Defer the declaration of context<commit_after>package cli

import (
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/elpinal/coco3/config"
	"github.com/elpinal/coco3/eval"
	"github.com/elpinal/coco3/gate"
	"github.com/elpinal/coco3/parser"

	"github.com/elpinal/coco3/extra"
	eparser "github.com/elpinal/coco3/extra/parser"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
)

type CLI struct {
	In  io.Reader
	Out io.Writer
	Err io.Writer

	*config.Config

	db *sqlx.DB

	execute1 func([]byte) (action, error)
}

func (c *CLI) Run(args []string) int {
	f := flag.NewFlagSet("coco3", flag.ContinueOnError)
	f.SetOutput(c.Err)
	f.Usage = func() {
		c.Err.Write([]byte("coco3 is a shell.\n"))
		c.Err.Write([]byte("Usage:\n"))
		f.PrintDefaults()
	}

	if c.Config == nil {
		c.Config = &config.Config{}
	}
	c.Config.Init()
	flagC := f.String("c", "", "take first argument as a command to execute")
	flagE := f.Bool("extra", c.Config.Extra, "switch to extra mode")
	if err := f.Parse(args); err != nil {
		return 2
	}
	return c.run(f.Args(), flagC, flagE)
}

func (c *CLI) run(args []string, flagC *string, flagE *bool) int {
	for _, alias := range c.Config.Alias {
		eval.DefAlias(alias[0], alias[1])
	}

	for k, v := range c.Config.Env {
		err := os.Setenv(k, v)
		if err != nil {
			c.errorln(err)
			return 1
		}
	}

	setpath(c.Config.Paths)

	if *flagE {
		// If -extra flag is on, enable extra mode on any command executions.
		c.execute1 = c.executeExtra
	} else {
		c.execute1 = c.execute
	}

	if len(c.Config.StartUpCommand) > 0 {
		a, err := c.execute1(c.Config.StartUpCommand)
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}

	if *flagC != "" {
		a, err := c.execute1([]byte(*flagC))
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
		return 0
	}

	if len(args) > 0 {
		a, err := c.runFiles(args)
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
		return 0
	}

	histRunes, err := c.getHistory(c.Config.HistFile)
	if err != nil {
		c.errorln(err)
		return 1
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	g := gate.NewContext(ctx, c.Config, c.In, c.Out, c.Err, histRunes)
	for {
		a, err := c.interact(g)
		if err != nil {
			c.printExecError(err)
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}
	return 0
}

func (c *CLI) errorf(s string, a ...interface{}) {
	fmt.Fprintf(c.Err, s, a...)
}

func (c *CLI) errorln(s ...interface{}) {
	fmt.Fprintln(c.Err, s...)
}

func (c *CLI) errorp(s ...interface{}) {
	fmt.Fprint(c.Err, s...)
}

func (c *CLI) getHistory(filename string) ([][]rune, error) {
	db, err := sqlx.Connect("sqlite3", filename)
	if err != nil {
		return nil, errors.Wrap(err, "connecting history file")
	}
	_, err = db.Exec(schema)
	if err != nil {
		return nil, errors.Wrap(err, "initializing history file")
	}
	var history []string
	err = db.Select(&history, "select line from command_info")
	if err != nil {
		return nil, errors.Wrap(err, "restoring history")
	}
	// TODO: Is this way proper?
	c.db = db
	return sanitizeHistory(history), nil
}

func (c *CLI) printExecError(err error) {
	if pe, ok := err.(*eparser.ParseError); ok {
		c.errorln(pe.Verbose())
	} else {
		c.errorln(err)
	}
}

// setpath sets the PATH environment variable.
func setpath(args []string) {
	if len(args) == 0 {
		return
	}
	paths := filepath.SplitList(os.Getenv("PATH"))
	var newPaths []string
	for _, path := range paths {
		if contains(args, path) {
			continue
		}
		newPaths = append(newPaths, path)
	}
	newPaths = append(args, newPaths...)
	os.Setenv("PATH", strings.Join(newPaths, string(filepath.ListSeparator)))
}

func contains(xs []string, s string) bool {
	for _, x := range xs {
		if x == s {
			return true
		}
	}
	return false
}

func sanitizeHistory(history []string) [][]rune {
	histRunes := make([][]rune, 0, len(history))
	for _, line := range history {
		if line == "" {
			continue
		}
		l := len(histRunes)
		s := []rune(line)
		if l > 0 && compareRunes(histRunes[l-1], s) {
			continue
		}
		histRunes = append(histRunes, s)
	}
	return histRunes
}

func compareRunes(r1, r2 []rune) bool {
	if len(r1) != len(r2) {
		return false
	}
	for i, r := range r1 {
		if r2[i] != r {
			return false
		}
	}
	return true
}

func (c *CLI) interact(g gate.Gate) (action, error) {
	r, end, err := c.read(g)
	if err != nil {
		return nil, err
	}
	if end {
		return exit{code: 0}, nil
	}
	ch := c.writeHistory(r)
	a, err := c.execute1([]byte(string(r)))
	if err != nil {
		return a, err
	}
	return a, <-ch
}

func (c *CLI) read(g gate.Gate) ([]rune, bool, error) {
	defer c.Out.Write([]byte{'\n'})
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		return nil, false, err
	}
	defer func() {
		if err := terminal.Restore(0, oldState); err != nil {
			c.errorln(err)
		}
	}()
	r, end, err := g.Read()
	if err != nil {
		return nil, false, err
	}
	return r, end, nil
}

func (c *CLI) writeHistory(r []rune) <-chan error {
	startTime := time.Now()
	ch := make(chan error)
	go func() {
		_, err := c.db.Exec("insert into command_info (time, line) values ($1, $2)", startTime, string(r))
		if err != nil {
			ch <- errors.Wrap(err, "saving history")
			return
		}
		ch <- nil
	}()
	return ch
}

const schema = `
create table if not exists command_info (
    time datetime,
    line text
)`

func (c *CLI) execute(b []byte) (action, error) {
	f, err := parser.ParseSrc(b)
	if err != nil {
		return nil, err
	}
	e := eval.New(c.In, c.Out, c.Err, c.db)
	err = e.Eval(f.Lines)
	select {
	case code := <-e.ExitCh:
		return exit{code: code}, nil
	default:
	}
	return nil, err
}

func (c *CLI) executeExtra(b []byte) (action, error) {
	cmd, err := eparser.Parse(b)
	if err != nil {
		return nil, err
	}
	e := extra.New(extra.Option{DB: c.db})
	err = e.Eval(cmd)
	if err == nil {
		return nil, nil
	}
	if pe, ok := err.(*eparser.ParseError); ok {
		pe.Src = string(b)
	}
	return nil, err
}

func (c *CLI) runFiles(files []string) (action, error) {
	for _, file := range files {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, err
		}
		a, err := c.execute1(b)
		if err != nil {
			return nil, err
		}
		if a != nil {
			return a, nil
		}
	}
	return nil, nil
}

type action interface {
	act()
}

type exit struct {
	code int
}

func (e exit) act() {}
<|endoftext|>


<commit_before>package openstack

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

	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
	"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
	"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
)

// LogRoundTripper satisfies the http.RoundTripper interface and is used to
// customize the default http client RoundTripper to allow for logging.
type LogRoundTripper struct {
	Rt      http.RoundTripper
	OsDebug bool
}

// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
	defer func() {
		if request.Body != nil {
			request.Body.Close()
		}
	}()

	// for future reference, this is how to access the Transport struct:
	//tlsconfig := lrt.Rt.(*http.Transport).TLSClientConfig

	var err error

	if lrt.OsDebug {
		log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL)
		log.Printf("[DEBUG] Openstack Request Headers:\n%s", FormatHeaders(request.Header, "\n"))

		if request.Body != nil {
			request.Body, err = lrt.logRequest(request.Body, request.Header.Get("Content-Type"))
			if err != nil {
				return nil, err
			}
		}
	}

	response, err := lrt.Rt.RoundTrip(request)
	if response == nil {
		return nil, err
	}

	if lrt.OsDebug {
		log.Printf("[DEBUG] Openstack Response Code: %d", response.StatusCode)
		log.Printf("[DEBUG] Openstack Response Headers:\n%s", FormatHeaders(response.Header, "\n"))

		response.Body, err = lrt.logResponse(response.Body, response.Header.Get("Content-Type"))
	}

	return response, err
}

// logRequest will log the HTTP Request details.
// If the body is JSON, it will attempt to be pretty-formatted.
func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType string) (io.ReadCloser, error) {
	defer original.Close()

	var bs bytes.Buffer
	_, err := io.Copy(&bs, original)
	if err != nil {
		return nil, err
	}

	// Handle request contentType
	if strings.HasPrefix(contentType, "application/json") {
		debugInfo := lrt.formatJSON(bs.Bytes())
		log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo)
	} else {
		log.Printf("[DEBUG] OpenStack Request Body: %s", bs.String())
	}

	return ioutil.NopCloser(strings.NewReader(bs.String())), nil
}

// logResponse will log the HTTP Response details.
// If the body is JSON, it will attempt to be pretty-formatted.
func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType string) (io.ReadCloser, error) {
	if strings.HasPrefix(contentType, "application/json") {
		var bs bytes.Buffer
		defer original.Close()
		_, err := io.Copy(&bs, original)
		if err != nil {
			return nil, err
		}
		debugInfo := lrt.formatJSON(bs.Bytes())
		if debugInfo != "" {
			log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo)
		}
		return ioutil.NopCloser(strings.NewReader(bs.String())), nil
	}

	log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON")
	return original, nil
}

// formatJSON will try to pretty-format a JSON body.
// It will also mask known fields which contain sensitive information.
func (lrt *LogRoundTripper) formatJSON(raw []byte) string {
	var data map[string]interface{}

	err := json.Unmarshal(raw, &data)
	if err != nil {
		log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err)
		return string(raw)
	}

	// Mask known password fields
	if v, ok := data["auth"].(map[string]interface{}); ok {
		if v, ok := v["identity"].(map[string]interface{}); ok {
			if v, ok := v["password"].(map[string]interface{}); ok {
				if v, ok := v["user"].(map[string]interface{}); ok {
					v["password"] = "***"
				}
			}
		}
	}

	// Ignore the catalog
	if v, ok := data["token"].(map[string]interface{}); ok {
		if _, ok := v["catalog"]; ok {
			return ""
		}
	}

	pretty, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err)
		return string(raw)
	}

	return string(pretty)
}

// Firewall is an OpenStack firewall.
type Firewall struct {
	firewalls.Firewall
	routerinsertion.FirewallExt
}

// FirewallCreateOpts represents the attributes used when creating a new firewall.
type FirewallCreateOpts struct {
	firewalls.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToFirewallCreateMap casts a CreateOptsExt struct to a map.
// It overrides firewalls.ToFirewallCreateMap to add the ValueSpecs field.
func (opts FirewallCreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "firewall")
}

//FirewallUpdateOpts
type FirewallUpdateOpts struct {
	firewalls.UpdateOptsBuilder
}

func (opts FirewallUpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "firewall")
}

// FloatingIPCreateOpts represents the attributes used when creating a new floating ip.
type FloatingIPCreateOpts struct {
	floatingips.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToFloatingIPCreateMap casts a CreateOpts struct to a map.
// It overrides floatingips.ToFloatingIPCreateMap to add the ValueSpecs field.
func (opts FloatingIPCreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "floatingip")
}

// KeyPairCreateOpts represents the attributes used when creating a new keypair.
type KeyPairCreateOpts struct {
	keypairs.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToKeyPairCreateMap casts a CreateOpts struct to a map.
// It overrides keypairs.ToKeyPairCreateMap to add the ValueSpecs field.
func (opts KeyPairCreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "keypair")
}

// NetworkCreateOpts represents the attributes used when creating a new network.
type NetworkCreateOpts struct {
	networks.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToNetworkCreateMap casts a CreateOpts struct to a map.
// It overrides networks.ToNetworkCreateMap to add the ValueSpecs field.
func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "network")
}

// PolicyCreateOpts represents the attributes used when creating a new firewall policy.
type PolicyCreateOpts struct {
	policies.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToPolicyCreateMap casts a CreateOpts struct to a map.
// It overrides policies.ToFirewallPolicyCreateMap to add the ValueSpecs field.
func (opts PolicyCreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "firewall_policy")
}

// PortCreateOpts represents the attributes used when creating a new port.
type PortCreateOpts struct {
	ports.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToPortCreateMap casts a CreateOpts struct to a map.
// It overrides ports.ToPortCreateMap to add the ValueSpecs field.
func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "port")
}

// RecordSetCreateOpts represents the attributes used when creating a new DNS record set.
type RecordSetCreateOpts struct {
	recordsets.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToRecordSetCreateMap casts a CreateOpts struct to a map.
// It overrides recordsets.ToRecordSetCreateMap to add the ValueSpecs field.
func (opts RecordSetCreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "")
	if err != nil {
		return nil, err
	}

	if m, ok := b[""].(map[string]interface{}); ok {
		return m, nil
	}

	return nil, fmt.Errorf("Expected map but got %T", b[""])
}

// RouterCreateOpts represents the attributes used when creating a new router.
type RouterCreateOpts struct {
	routers.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToRouterCreateMap casts a CreateOpts struct to a map.
// It overrides routers.ToRouterCreateMap to add the ValueSpecs field.
func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "router")
}

// RuleCreateOpts represents the attributes used when creating a new firewall rule.
type RuleCreateOpts struct {
	rules.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToRuleCreateMap casts a CreateOpts struct to a map.
// It overrides rules.ToRuleCreateMap to add the ValueSpecs field.
func (opts RuleCreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "firewall_rule")
	if err != nil {
		return nil, err
	}

	if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" {
		m["protocol"] = nil
	}

	return b, nil
}

// ServerGroupCreateOpts represents the attributes used when creating a new router.
type ServerGroupCreateOpts struct {
	servergroups.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToServerGroupCreateMap casts a CreateOpts struct to a map.
// It overrides routers.ToServerGroupCreateMap to add the ValueSpecs field.
func (opts ServerGroupCreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "server_group")
}

// SubnetCreateOpts represents the attributes used when creating a new subnet.
type SubnetCreateOpts struct {
	subnets.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToSubnetCreateMap casts a CreateOpts struct to a map.
// It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field.
func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "subnet")
	if err != nil {
		return nil, err
	}

	if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" {
		m["gateway_ip"] = nil
	}

	return b, nil
}

// ZoneCreateOpts represents the attributes used when creating a new DNS zone.
type ZoneCreateOpts struct {
	zones.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToZoneCreateMap casts a CreateOpts struct to a map.
// It overrides zones.ToZoneCreateMap to add the ValueSpecs field.
func (opts ZoneCreateOpts) ToZoneCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "")
	if err != nil {
		return nil, err
	}

	if m, ok := b[""].(map[string]interface{}); ok {
		if opts.TTL > 0 {
			m["ttl"] = opts.TTL
		}

		return m, nil
	}

	return nil, fmt.Errorf("Expected map but got %T", b[""])
}

// SubnetPoolCreateOpts represents the attributes used when creating a new subnet pool.
type SubnetPoolCreateOpts struct {
	subnetpools.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}
<commit_msg>Logging: Don't print non-JSON requests<commit_after>package openstack

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

	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
	"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets"
	"github.com/gophercloud/gophercloud/openstack/dns/v2/zones"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
	"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
)

// LogRoundTripper satisfies the http.RoundTripper interface and is used to
// customize the default http client RoundTripper to allow for logging.
type LogRoundTripper struct {
	Rt      http.RoundTripper
	OsDebug bool
}

// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
	defer func() {
		if request.Body != nil {
			request.Body.Close()
		}
	}()

	// for future reference, this is how to access the Transport struct:
	//tlsconfig := lrt.Rt.(*http.Transport).TLSClientConfig

	var err error

	if lrt.OsDebug {
		log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL)
		log.Printf("[DEBUG] Openstack Request Headers:\n%s", FormatHeaders(request.Header, "\n"))

		if request.Body != nil {
			request.Body, err = lrt.logRequest(request.Body, request.Header.Get("Content-Type"))
			if err != nil {
				return nil, err
			}
		}
	}

	response, err := lrt.Rt.RoundTrip(request)
	if response == nil {
		return nil, err
	}

	if lrt.OsDebug {
		log.Printf("[DEBUG] Openstack Response Code: %d", response.StatusCode)
		log.Printf("[DEBUG] Openstack Response Headers:\n%s", FormatHeaders(response.Header, "\n"))

		response.Body, err = lrt.logResponse(response.Body, response.Header.Get("Content-Type"))
	}

	return response, err
}

// logRequest will log the HTTP Request details.
// If the body is JSON, it will attempt to be pretty-formatted.
func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType string) (io.ReadCloser, error) {
	defer original.Close()

	var bs bytes.Buffer
	_, err := io.Copy(&bs, original)
	if err != nil {
		return nil, err
	}

	// Handle request contentType
	if strings.HasPrefix(contentType, "application/json") {
		debugInfo := lrt.formatJSON(bs.Bytes())
		log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo)
	}

	return ioutil.NopCloser(strings.NewReader(bs.String())), nil
}

// logResponse will log the HTTP Response details.
// If the body is JSON, it will attempt to be pretty-formatted.
func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType string) (io.ReadCloser, error) {
	if strings.HasPrefix(contentType, "application/json") {
		var bs bytes.Buffer
		defer original.Close()
		_, err := io.Copy(&bs, original)
		if err != nil {
			return nil, err
		}
		debugInfo := lrt.formatJSON(bs.Bytes())
		if debugInfo != "" {
			log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo)
		}
		return ioutil.NopCloser(strings.NewReader(bs.String())), nil
	}

	log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON")
	return original, nil
}

// formatJSON will try to pretty-format a JSON body.
// It will also mask known fields which contain sensitive information.
func (lrt *LogRoundTripper) formatJSON(raw []byte) string {
	var data map[string]interface{}

	err := json.Unmarshal(raw, &data)
	if err != nil {
		log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err)
		return string(raw)
	}

	// Mask known password fields
	if v, ok := data["auth"].(map[string]interface{}); ok {
		if v, ok := v["identity"].(map[string]interface{}); ok {
			if v, ok := v["password"].(map[string]interface{}); ok {
				if v, ok := v["user"].(map[string]interface{}); ok {
					v["password"] = "***"
				}
			}
		}
	}

	// Ignore the catalog
	if v, ok := data["token"].(map[string]interface{}); ok {
		if _, ok := v["catalog"]; ok {
			return ""
		}
	}

	pretty, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err)
		return string(raw)
	}

	return string(pretty)
}

// Firewall is an OpenStack firewall.
type Firewall struct {
	firewalls.Firewall
	routerinsertion.FirewallExt
}

// FirewallCreateOpts represents the attributes used when creating a new firewall.
type FirewallCreateOpts struct {
	firewalls.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToFirewallCreateMap casts a CreateOptsExt struct to a map.
// It overrides firewalls.ToFirewallCreateMap to add the ValueSpecs field.
func (opts FirewallCreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "firewall")
}

//FirewallUpdateOpts
type FirewallUpdateOpts struct {
	firewalls.UpdateOptsBuilder
}

func (opts FirewallUpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "firewall")
}

// FloatingIPCreateOpts represents the attributes used when creating a new floating ip.
type FloatingIPCreateOpts struct {
	floatingips.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToFloatingIPCreateMap casts a CreateOpts struct to a map.
// It overrides floatingips.ToFloatingIPCreateMap to add the ValueSpecs field.
func (opts FloatingIPCreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "floatingip")
}

// KeyPairCreateOpts represents the attributes used when creating a new keypair.
type KeyPairCreateOpts struct {
	keypairs.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToKeyPairCreateMap casts a CreateOpts struct to a map.
// It overrides keypairs.ToKeyPairCreateMap to add the ValueSpecs field.
func (opts KeyPairCreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "keypair")
}

// NetworkCreateOpts represents the attributes used when creating a new network.
type NetworkCreateOpts struct {
	networks.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToNetworkCreateMap casts a CreateOpts struct to a map.
// It overrides networks.ToNetworkCreateMap to add the ValueSpecs field.
func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "network")
}

// PolicyCreateOpts represents the attributes used when creating a new firewall policy.
type PolicyCreateOpts struct {
	policies.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToPolicyCreateMap casts a CreateOpts struct to a map.
// It overrides policies.ToFirewallPolicyCreateMap to add the ValueSpecs field.
func (opts PolicyCreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "firewall_policy")
}

// PortCreateOpts represents the attributes used when creating a new port.
type PortCreateOpts struct {
	ports.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToPortCreateMap casts a CreateOpts struct to a map.
// It overrides ports.ToPortCreateMap to add the ValueSpecs field.
func (opts PortCreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "port")
}

// RecordSetCreateOpts represents the attributes used when creating a new DNS record set.
type RecordSetCreateOpts struct {
	recordsets.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToRecordSetCreateMap casts a CreateOpts struct to a map.
// It overrides recordsets.ToRecordSetCreateMap to add the ValueSpecs field.
func (opts RecordSetCreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "")
	if err != nil {
		return nil, err
	}

	if m, ok := b[""].(map[string]interface{}); ok {
		return m, nil
	}

	return nil, fmt.Errorf("Expected map but got %T", b[""])
}

// RouterCreateOpts represents the attributes used when creating a new router.
type RouterCreateOpts struct {
	routers.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToRouterCreateMap casts a CreateOpts struct to a map.
// It overrides routers.ToRouterCreateMap to add the ValueSpecs field.
func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "router")
}

// RuleCreateOpts represents the attributes used when creating a new firewall rule.
type RuleCreateOpts struct {
	rules.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToRuleCreateMap casts a CreateOpts struct to a map.
// It overrides rules.ToRuleCreateMap to add the ValueSpecs field.
func (opts RuleCreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "firewall_rule")
	if err != nil {
		return nil, err
	}

	if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" {
		m["protocol"] = nil
	}

	return b, nil
}

// ServerGroupCreateOpts represents the attributes used when creating a new router.
type ServerGroupCreateOpts struct {
	servergroups.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToServerGroupCreateMap casts a CreateOpts struct to a map.
// It overrides routers.ToServerGroupCreateMap to add the ValueSpecs field.
func (opts ServerGroupCreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
	return BuildRequest(opts, "server_group")
}

// SubnetCreateOpts represents the attributes used when creating a new subnet.
type SubnetCreateOpts struct {
	subnets.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToSubnetCreateMap casts a CreateOpts struct to a map.
// It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field.
func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "subnet")
	if err != nil {
		return nil, err
	}

	if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" {
		m["gateway_ip"] = nil
	}

	return b, nil
}

// ZoneCreateOpts represents the attributes used when creating a new DNS zone.
type ZoneCreateOpts struct {
	zones.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}

// ToZoneCreateMap casts a CreateOpts struct to a map.
// It overrides zones.ToZoneCreateMap to add the ValueSpecs field.
func (opts ZoneCreateOpts) ToZoneCreateMap() (map[string]interface{}, error) {
	b, err := BuildRequest(opts, "")
	if err != nil {
		return nil, err
	}

	if m, ok := b[""].(map[string]interface{}); ok {
		if opts.TTL > 0 {
			m["ttl"] = opts.TTL
		}

		return m, nil
	}

	return nil, fmt.Errorf("Expected map but got %T", b[""])
}

// SubnetPoolCreateOpts represents the attributes used when creating a new subnet pool.
type SubnetPoolCreateOpts struct {
	subnetpools.CreateOpts
	ValueSpecs map[string]string `json:"value_specs,omitempty"`
}
<|endoftext|>


<commit_before>package colorutil

import (
	"github.com/lucasb-eyer/go-colorful"
	"google.golang.org/api/slides/v1"
)

// ChartColor1 is the color palette for Google Charts as collected by
// Craig Davis here: https://gist.github.com/there4/2579834
var ChartColor1 = [...]string{
	"#3366CC",
	"#DC3912",
	"#FF9900",
	"#109618",
	"#990099",
	"#3B3EAC",
	"#0099C6",
	"#DD4477",
	"#66AA00",
	"#B82E2E",
	"#316395",
	"#994499",
	"#22AA99",
	"#AAAA11",
	"#6633CC",
	"#E67300",
	"#8B0707",
	"#329262",
	"#5574A6",
	"#3B3EAC",
}

func GoogleSlidesRgbColorParseHex(hexColor string) (*slides.RgbColor, error) {
	c, err := colorful.Hex(hexColor)
	if err != nil {
		return nil, err
	}
	return &slides.RgbColor{Red: c.R, Green: c.G, Blue: c.B}, nil
}
<commit_msg>update time<commit_after><|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"bytes"
	"fmt"
	"os"
	"path"
	"sort"
	"strings"

	"go/doc"
	"io/ioutil"

	"github.com/ibelie/tygo"
)

var (
	FMT_PKG   = map[string]string{"fmt": ""}
	LOCAL_PKG = map[string]string{
		"fmt":  "",
		"sync": "",
		"github.com/ibelie/ruid": "",
		"github.com/ibelie/tygo": "",
	}
	PROP_PRE = []tygo.Type{tygo.SimpleType_UINT64, tygo.SimpleType_UINT64}
	DELEGATE = "Delegate"
)

func ReplaceEntity(dir string, filename string, pkgname string, types []tygo.Type) {
	for _, t := range types {
		if object, ok := isService(t); ok {
			object.Parent.Object = &tygo.Object{
				Name:   "Entity",
				Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
			}
		}
	}
}

func Inject(dir string, filename string, pkgname string, types []tygo.Type) {
	ReplaceEntity(dir, filename, pkgname, types)
	var services []*tygo.Object
	for _, t := range types {
		if object, ok := isService(t); ok {
			services = append(services, object)
		}
	}
	tygo.PROP_PRE = PROP_PRE
	tygo.DELEGATE = DELEGATE
	tygo.Inject(dir, filename, pkgname, types)
	tygo.PROP_PRE = nil
	tygo.DELEGATE = ""
	injectfile := path.Join(SRC_PATH, dir, strings.Replace(filename, ".go", ".rpc.go", 1))
	if len(services) == 0 {
		os.Remove(injectfile)
		return
	}

	objects := make(map[string]*doc.Type)
	if d := packageDoc(dir); d != nil {
		for _, t := range d.Types {
			for _, s := range services {
				if t.Name == s.Name {
					objects[t.Name] = t
				}
			}
		}
	}

	var head bytes.Buffer
	var body bytes.Buffer
	head.Write([]byte(fmt.Sprintf(`// Generated by ibelie-rpc.  DO NOT EDIT!

package %s
`, pkgname)))
	body.Write([]byte(`
`))

	var pkgs map[string]string
	for _, service := range services {
		var methods []string
		local_m, local_s, local_p := injectServiceLocal(service, objects[service.Name])
		property_s, property_p := injectServiceProperty(service)
		for _, method := range service.Methods {
			method_s, method_p := injectProcedureCaller(service.Name+DELEGATE, service.Name, method, local_m)
			pkgs = update(pkgs, method_p)
			methods = append(methods, method_s)
		}

		body.Write([]byte(local_s))
		body.Write([]byte(property_s))
		body.Write([]byte(fmt.Sprintf(`
type %sDelegate Entity

func (e *Entity) %s() *%sDelegate {
	return (*%sDelegate)(e)
}
%s`, service.Name, service.Name, service.Name, service.Name, strings.Join(methods, ""))))
		pkgs = update(pkgs, local_p)
		pkgs = update(pkgs, property_p)
	}

	var sortedPkg []string
	for pkg, _ := range pkgs {
		sortedPkg = append(sortedPkg, pkg)
	}
	sort.Strings(sortedPkg)
	for _, pkg := range sortedPkg {
		head.Write([]byte(fmt.Sprintf(`
import %s"%s"`, pkgs[pkg], pkg)))
	}

	head.Write(body.Bytes())
	ioutil.WriteFile(injectfile, head.Bytes(), 0666)
}

func injectProcedureCallee(service *tygo.Object, method *tygo.Method) (string, map[string]string) {
	var param string
	var params_list []string
	for i, _ := range method.Params {
		params_list = append(params_list, fmt.Sprintf("p%d", i))
	}
	if len(params_list) > 0 {
		param = fmt.Sprintf(` else if %s, e := service.Deserialize%sParam(param); e != nil {
			err = e
		}`,
			strings.Join(params_list, ", "), method.Name)
	}

	var result string
	if len(method.Results) > 0 {
		result = fmt.Sprintf(` else {
			result = service.Serialize%sResult(service.%s(%s))
		}`,
			method.Name, method.Name, strings.Join(params_list, ", "))
	} else {
		result = fmt.Sprintf(` else {
			service.%s(%s)
		}`, method.Name, strings.Join(params_list, ", "))
	}

	return fmt.Sprintf(`
	case SYMBOL_%s:
		methodName = "%s"
		if service, exist := s.services[i]; !exist {
			err = fmt.Errorf("[%s] Service %s RUID not exists: %%v", i)
		}%s%s
`, method.Name, method.Name, service.Name, method.Name, param, result), FMT_PKG
}

func injectServiceLocal(service *tygo.Object, object *doc.Type) (string, string, map[string]string) {
	var onCreate string
	if hasMethod(object, &tygo.Method{Name: "onCreate"}) {
		onCreate = `
			s.services[i].onCreate()`
	}

	serviceTemp := "_"
	var onDestroy string
	if hasMethod(object, &tygo.Method{Name: "onDestroy"}) {
		serviceTemp = "service"
		onDestroy = `
			service.onDestroy()`
	}

	pkgs := LOCAL_PKG
	var cases []string
	for _, m := range service.Methods {
		if !hasMethod(object, m) {
			continue
		}
		case_s, case_p := injectProcedureCallee(service, m)
		cases = append(cases, case_s)
		pkgs = update(pkgs, case_p)
	}

	return fmt.Sprintf("_%sService.services", service.Name), fmt.Sprintf(`
type %sService struct {
	mutex    sync.Mutex
	services map[ruid.RUID]*%s
}

var _%sService = %sService{services: make(map[ruid.RUID]*%s)}

func %sRegister(server IServer, symbols map[string]uint64) *%sService {
	InitializeServer(server, symbols)
	return _%sService
}

func (s *%sService) Procedure(i ruid.RUID, method uint64, param []byte) (result []byte, err error) {
	var methodName string
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("[%s] Procedure %%d(%%s) %%v panic:\n>>>>%%v", method, methodName, i, e)
		}
	}()

	switch method {
	case SYMBOL_CREATE:
		methodName = "Create"
		s.mutex.Lock()
		defer s.mutex.Unlock()
		if _, exist := s.services[i]; exist {
			err = fmt.Errorf("[%s] Service create RUID already exists: %%v", i)
		} else {
			input := &tygo.ProtoBuf{Buffer: param}
			var k, t uint64
			if k, err = input.ReadVarint(); err != nil {
				return
			} else if t, err = input.ReadVarint(); err != nil {
				return
			}
			s.services[i] = &%s{Entity: &Entity{RUID: i, Key: k, Type: t}}%s
		}
	case SYMBOL_DESTROY:
		methodName = "Destroy"
		s.mutex.Lock()
		defer s.mutex.Unlock()
		if %s, exist := s.services[i]; !exist {
			err = fmt.Errorf("[%s] Service destroy RUID not exists: %%v", i)
		} else {%s
			delete(s.services, i)
		}
	case SYMBOL_SYNCHRON:
		methodName = "Synchron"
		if service, exist := s.services[i]; !exist {
			err = fmt.Errorf("[%s] Service synchron RUID not exists: %%v", i)
		} else {
			result = make([]byte, service.ByteSize())
			service.Serialize(&tygo.ProtoBuf{Buffer: result})
		}%s
	}
	return
}
`, service.Name, service.Name, service.Name, service.Name, service.Name, service.Name,
		service.Name, service.Name, service.Name, service.Name, service.Name, service.Name,
		onCreate, serviceTemp, service.Name, onDestroy, service.Name, strings.Join(cases, "")), pkgs
}

func injectProcedureCaller(owner string, service string, method *tygo.Method, local string) (string, map[string]string) {
	var pkgs map[string]string
	var param string
	var params_list []string
	var params_declare []string
	for i, p := range method.Params {
		param_s, param_p := p.Go()
		pkgs = update(pkgs, param_p)
		params_list = append(params_list, fmt.Sprintf("p%d", i))
		params_declare = append(params_declare, fmt.Sprintf("p%d %s", i, param_s))
	}
	if len(params_list) > 0 {
		param = fmt.Sprintf("s.Serialize%sParam(%s)", method.Name, strings.Join(params_list, ", "))
	} else {
		param = "nil"
	}

	var result_local string
	var result_remote string
	var results_list []string
	var results_declare []string
	for i, r := range method.Results {
		result_s, result_p := r.Go()
		pkgs = update(pkgs, result_p)
		results_list = append(results_list, fmt.Sprintf("r%d", i))
		results_declare = append(results_declare, fmt.Sprintf("r%d %s", i, result_s))
	}
	results_declare = append(results_declare, "err error")

	if len(results_list) > 0 {
		result_local = fmt.Sprintf(`
		%s = local.%s(%s)`, strings.Join(results_list, ", "), method.Name, strings.Join(params_list, ", "))
		result_remote = fmt.Sprintf(`
	var result string
	if result, err = Server.Procedure(s.RUID, s.Key, SYMBOL_%s, SYMBOL_%s, %s); err != nil {
		return
	}
	%s, err = s.Deserialize%sResult(result)`, service, method.Name, param,
			strings.Join(results_list, ", "), method.Name)
	} else {
		result_local = fmt.Sprintf(`
		local.%s(%s)`, method.Name, strings.Join(params_list, ", "))
		result_remote = fmt.Sprintf(`
	_, err = Server.Procedure(s.RUID, s.Key, SYMBOL_%s, SYMBOL_%s, %s)`,
			service, method.Name, param)
	}

	var checkLocal string
	if local != "" {
		checkLocal = fmt.Sprintf(`
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("[%s] Procedure %s panic:\n>>>>%%v", e)
		}
	}()
	if local, exist := %s[s.RUID]; exist {%s
		return
	}`, service, method.Name, local, result_local)
		pkgs = update(pkgs, FMT_PKG)
	}

	return fmt.Sprintf(`
func (s *%s) %s(%s) (%s) {%s%s
	return
}
`, owner, method.Name, strings.Join(params_declare, ", "), strings.Join(results_declare, ", "),
		checkLocal, result_remote), pkgs
}

func injectServiceProperty(service *tygo.Object) (string, map[string]string) {
	var pkgs map[string]string
	var codes []string
	for _, property := range service.Fields {
		property_s, property_p := property.Go()
		pkgs = update(pkgs, property_p)

		switch property.Type.(type) {
		case *tygo.ListType:
			codes = append(codes, fmt.Sprintf(`
func (s *%s) extend%s(x %s) error {
	if x == nil || len(x) <= 0 {
		return
	}
	s.%s = append(s.%s, x...)
	return Server.Distribute(e.RUID, e.Key, e.Type, SYMBOL_NOTIFY, s.Serialize%s(SYMBOL_%s, SYMBOL_%s, x), nil)
}
`, service.Name, strings.Title(property.Name), property_s, property.Name, property.Name,
				property.Name, service.Name, property.Name))
		case *tygo.DictType:
			codes = append(codes, fmt.Sprintf(`
func (s *%s) update%s(x %s) error {
	if x == nil || len(x) <= 0 {
		return
	} else if s.%s == nil {
		s.%s = make(%s)
	}
	for k, v := range x {
		s.%s[k] = v
	}
	return Server.Distribute(e.RUID, e.Key, e.Type, SYMBOL_NOTIFY, s.Serialize%s(SYMBOL_%s, SYMBOL_%s, x), nil)
}
`, service.Name, strings.Title(property.Name), property_s, property.Name, property.Name,
				property_s, property.Name, property.Name, service.Name, property.Name))
		default:
			codes = append(codes, fmt.Sprintf(`
func (s *%s) set%s(x %s) error {
	s.%s = x
	return Server.Distribute(e.RUID, e.Key, e.Type, SYMBOL_NOTIFY, s.Serialize%s(SYMBOL_%s, SYMBOL_%s, x), nil)
}
`, service.Name, strings.Title(property.Name), property_s, property.Name, property.Name,
				service.Name, property.Name))
		}

	}

	return strings.Join(codes, ""), pkgs
}
<commit_msg>commit all<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"bytes"
	"fmt"
	"os"
	"path"
	"sort"
	"strings"

	"go/doc"
	"io/ioutil"

	"github.com/ibelie/tygo"
)

var (
	FMT_PKG   = map[string]string{"fmt": ""}
	LOCAL_PKG = map[string]string{
		"fmt":  "",
		"sync": "",
		"github.com/ibelie/ruid": "",
		"github.com/ibelie/tygo": "",
	}
	PROP_PRE = []tygo.Type{tygo.SimpleType_UINT64, tygo.SimpleType_UINT64}
	DELEGATE = "Delegate"
)

func ReplaceEntity(dir string, filename string, pkgname string, types []tygo.Type) {
	for _, t := range types {
		if object, ok := isService(t); ok {
			object.Parent.Object = &tygo.Object{
				Name:   "Entity",
				Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
			}
		}
	}
}

func Inject(dir string, filename string, pkgname string, types []tygo.Type) {
	ReplaceEntity(dir, filename, pkgname, types)
	var services []*tygo.Object
	for _, t := range types {
		if object, ok := isService(t); ok {
			services = append(services, object)
		}
	}
	tygo.PROP_PRE = PROP_PRE
	tygo.DELEGATE = DELEGATE
	tygo.Inject(dir, filename, pkgname, types)
	tygo.PROP_PRE = nil
	tygo.DELEGATE = ""
	injectfile := path.Join(SRC_PATH, dir, strings.Replace(filename, ".go", ".rpc.go", 1))
	if len(services) == 0 {
		os.Remove(injectfile)
		return
	}

	objects := make(map[string]*doc.Type)
	if d := packageDoc(dir); d != nil {
		for _, t := range d.Types {
			for _, s := range services {
				if t.Name == s.Name {
					objects[t.Name] = t
				}
			}
		}
	}

	var head bytes.Buffer
	var body bytes.Buffer
	head.Write([]byte(fmt.Sprintf(`// Generated by ibelie-rpc.  DO NOT EDIT!

package %s
`, pkgname)))
	body.Write([]byte(`
`))

	var pkgs map[string]string
	for _, service := range services {
		var methods []string
		local_m, local_s, local_p := injectServiceLocal(service, objects[service.Name])
		property_s, property_p := injectServiceProperty(service)
		for _, method := range service.Methods {
			method_s, method_p := injectProcedureCaller(service.Name+DELEGATE, service.Name, method, local_m)
			pkgs = update(pkgs, method_p)
			methods = append(methods, method_s)
		}

		body.Write([]byte(local_s))
		body.Write([]byte(property_s))
		body.Write([]byte(fmt.Sprintf(`
type %sDelegate Entity

func (e *Entity) %s() *%sDelegate {
	return (*%sDelegate)(e)
}
%s`, service.Name, service.Name, service.Name, service.Name, strings.Join(methods, ""))))
		pkgs = update(pkgs, local_p)
		pkgs = update(pkgs, property_p)
	}

	var sortedPkg []string
	for pkg, _ := range pkgs {
		sortedPkg = append(sortedPkg, pkg)
	}
	sort.Strings(sortedPkg)
	for _, pkg := range sortedPkg {
		head.Write([]byte(fmt.Sprintf(`
import %s"%s"`, pkgs[pkg], pkg)))
	}

	head.Write(body.Bytes())
	ioutil.WriteFile(injectfile, head.Bytes(), 0666)
}

func injectProcedureCallee(service *tygo.Object, method *tygo.Method) (string, map[string]string) {
	var param string
	var params_list []string
	for i, _ := range method.Params {
		params_list = append(params_list, fmt.Sprintf("p%d", i))
	}
	if len(params_list) > 0 {
		param = fmt.Sprintf(` else if %s, e := service.Deserialize%sParam(param); e != nil {
			err = e
		}`,
			strings.Join(params_list, ", "), method.Name)
	}

	var result string
	if len(method.Results) > 0 {
		result = fmt.Sprintf(` else {
			result = service.Serialize%sResult(service.%s(%s))
		}`,
			method.Name, method.Name, strings.Join(params_list, ", "))
	} else {
		result = fmt.Sprintf(` else {
			service.%s(%s)
		}`, method.Name, strings.Join(params_list, ", "))
	}

	return fmt.Sprintf(`
	case SYMBOL_%s:
		methodName = "%s"
		if service, exist := s.services[i]; !exist {
			err = fmt.Errorf("[%s] Service %s RUID not exists: %%v", i)
		}%s%s
`, method.Name, method.Name, service.Name, method.Name, param, result), FMT_PKG
}

func injectServiceLocal(service *tygo.Object, object *doc.Type) (string, string, map[string]string) {
	var onCreate string
	if hasMethod(object, &tygo.Method{Name: "onCreate"}) {
		onCreate = `
			s.services[i].onCreate()`
	}

	serviceTemp := "_"
	var onDestroy string
	if hasMethod(object, &tygo.Method{Name: "onDestroy"}) {
		serviceTemp = "service"
		onDestroy = `
			service.onDestroy()`
	}

	pkgs := LOCAL_PKG
	var cases []string
	for _, m := range service.Methods {
		if !hasMethod(object, m) {
			continue
		}
		case_s, case_p := injectProcedureCallee(service, m)
		cases = append(cases, case_s)
		pkgs = update(pkgs, case_p)
	}

	return fmt.Sprintf("_%sService.services", service.Name), fmt.Sprintf(`
type %sService struct {
	mutex    sync.Mutex
	services map[ruid.RUID]*%s
}

var _%sService = %sService{services: make(map[ruid.RUID]*%s)}

func %sRegister(server IServer, symbols map[string]uint64) (uint64, *%sService) {
	InitializeServer(server, symbols)
	return SYMBOL_%s, _%sService
}

func (s *%sService) Procedure(i ruid.RUID, method uint64, param []byte) (result []byte, err error) {
	var methodName string
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("[%s] Procedure %%d(%%s) %%v panic:\n>>>>%%v", method, methodName, i, e)
		}
	}()

	switch method {
	case SYMBOL_CREATE:
		methodName = "Create"
		s.mutex.Lock()
		defer s.mutex.Unlock()
		if _, exist := s.services[i]; exist {
			err = fmt.Errorf("[%s] Service create RUID already exists: %%v", i)
		} else {
			input := &tygo.ProtoBuf{Buffer: param}
			var k, t uint64
			if k, err = input.ReadVarint(); err != nil {
				return
			} else if t, err = input.ReadVarint(); err != nil {
				return
			}
			s.services[i] = &%s{Entity: &Entity{RUID: i, Key: k, Type: t}}%s
		}
	case SYMBOL_DESTROY:
		methodName = "Destroy"
		s.mutex.Lock()
		defer s.mutex.Unlock()
		if %s, exist := s.services[i]; !exist {
			err = fmt.Errorf("[%s] Service destroy RUID not exists: %%v", i)
		} else {%s
			delete(s.services, i)
		}
	case SYMBOL_SYNCHRON:
		methodName = "Synchron"
		if service, exist := s.services[i]; !exist {
			err = fmt.Errorf("[%s] Service synchron RUID not exists: %%v", i)
		} else {
			result = make([]byte, service.ByteSize())
			service.Serialize(&tygo.ProtoBuf{Buffer: result})
		}%s
	}
	return
}
`, service.Name, service.Name, service.Name, service.Name, service.Name, service.Name, service.Name,
		service.Name, service.Name, service.Name, service.Name, service.Name, service.Name,
		onCreate, serviceTemp, service.Name, onDestroy, service.Name, strings.Join(cases, "")), pkgs
}

func injectProcedureCaller(owner string, service string, method *tygo.Method, local string) (string, map[string]string) {
	var pkgs map[string]string
	var param string
	var params_list []string
	var params_declare []string
	for i, p := range method.Params {
		param_s, param_p := p.Go()
		pkgs = update(pkgs, param_p)
		params_list = append(params_list, fmt.Sprintf("p%d", i))
		params_declare = append(params_declare, fmt.Sprintf("p%d %s", i, param_s))
	}
	if len(params_list) > 0 {
		param = fmt.Sprintf("s.Serialize%sParam(%s)", method.Name, strings.Join(params_list, ", "))
	} else {
		param = "nil"
	}

	var result_local string
	var result_remote string
	var results_list []string
	var results_declare []string
	for i, r := range method.Results {
		result_s, result_p := r.Go()
		pkgs = update(pkgs, result_p)
		results_list = append(results_list, fmt.Sprintf("r%d", i))
		results_declare = append(results_declare, fmt.Sprintf("r%d %s", i, result_s))
	}
	results_declare = append(results_declare, "err error")

	if len(results_list) > 0 {
		result_local = fmt.Sprintf(`
		%s = local.%s(%s)`, strings.Join(results_list, ", "), method.Name, strings.Join(params_list, ", "))
		result_remote = fmt.Sprintf(`
	var result string
	if result, err = Server.Procedure(s.RUID, s.Key, SYMBOL_%s, SYMBOL_%s, %s); err != nil {
		return
	}
	%s, err = s.Deserialize%sResult(result)`, service, method.Name, param,
			strings.Join(results_list, ", "), method.Name)
	} else {
		result_local = fmt.Sprintf(`
		local.%s(%s)`, method.Name, strings.Join(params_list, ", "))
		result_remote = fmt.Sprintf(`
	_, err = Server.Procedure(s.RUID, s.Key, SYMBOL_%s, SYMBOL_%s, %s)`,
			service, method.Name, param)
	}

	var checkLocal string
	if local != "" {
		checkLocal = fmt.Sprintf(`
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("[%s] Procedure %s panic:\n>>>>%%v", e)
		}
	}()
	if local, exist := %s[s.RUID]; exist {%s
		return
	}`, service, method.Name, local, result_local)
		pkgs = update(pkgs, FMT_PKG)
	}

	return fmt.Sprintf(`
func (s *%s) %s(%s) (%s) {%s%s
	return
}
`, owner, method.Name, strings.Join(params_declare, ", "), strings.Join(results_declare, ", "),
		checkLocal, result_remote), pkgs
}

func injectServiceProperty(service *tygo.Object) (string, map[string]string) {
	var pkgs map[string]string
	var codes []string
	for _, property := range service.Fields {
		property_s, property_p := property.Go()
		pkgs = update(pkgs, property_p)

		switch property.Type.(type) {
		case *tygo.ListType:
			codes = append(codes, fmt.Sprintf(`
func (s *%s) extend%s(x %s) error {
	if x == nil || len(x) <= 0 {
		return
	}
	s.%s = append(s.%s, x...)
	return Server.Distribute(e.RUID, e.Key, e.Type, SYMBOL_NOTIFY, s.Serialize%s(SYMBOL_%s, SYMBOL_%s, x), nil)
}
`, service.Name, strings.Title(property.Name), property_s, property.Name, property.Name,
				property.Name, service.Name, property.Name))
		case *tygo.DictType:
			codes = append(codes, fmt.Sprintf(`
func (s *%s) update%s(x %s) error {
	if x == nil || len(x) <= 0 {
		return
	} else if s.%s == nil {
		s.%s = make(%s)
	}
	for k, v := range x {
		s.%s[k] = v
	}
	return Server.Distribute(e.RUID, e.Key, e.Type, SYMBOL_NOTIFY, s.Serialize%s(SYMBOL_%s, SYMBOL_%s, x), nil)
}
`, service.Name, strings.Title(property.Name), property_s, property.Name, property.Name,
				property_s, property.Name, property.Name, service.Name, property.Name))
		default:
			codes = append(codes, fmt.Sprintf(`
func (s *%s) set%s(x %s) error {
	s.%s = x
	return Server.Distribute(e.RUID, e.Key, e.Type, SYMBOL_NOTIFY, s.Serialize%s(SYMBOL_%s, SYMBOL_%s, x), nil)
}
`, service.Name, strings.Title(property.Name), property_s, property.Name, property.Name,
				service.Name, property.Name))
		}

	}

	return strings.Join(codes, ""), pkgs
}
<|endoftext|>


<commit_before>// Package golisten allows a user to user http.ListenAndServe with
// any port as root and effectively accept the incomming connection as an other
// un-privileged user.
package golisten

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"strconv"
	"syscall"
)

var listenFD = flag.Int("listen-fd", 0, "open fd for listener")

func lookupUser(username string) (uid, gid int, err error) {
	u, err := user.Lookup(username)
	if err != nil {
		return -1, -1, err
	}
	uid, err = strconv.Atoi(u.Uid)
	if err != nil {
		return -1, -1, err
	}
	gid, err = strconv.Atoi(u.Gid)
	if err != nil {
		return -1, -1, err
	}
	return uid, gid, nil
}

// fdListener implements a listener from a file descriptor.
type fdListener struct {
	file *os.File
}

// Accept accepts a connection from the listener.
// TODO: see why net.FileListener does not work. It would allow us t
func (ln *fdListener) Accept() (net.Conn, error) {
	fd, _, err := syscall.Accept(int(ln.file.Fd()))
	if err != nil {
		return nil, err
	}
	conn, err := net.FileConn(os.NewFile(uintptr(fd), ""))
	if err != nil {
		return nil, err
	}
	return conn.(*net.TCPConn), nil
}

// Close closes the listen fd.
func (ln *fdListener) Close() error {
	return ln.file.Close()
}

// Addr returns the local address of the listener.
// TODO: implement this.
func (ln *fdListener) Addr() net.Addr {
	return nil
}

// ListenAndServe wraps `http.ListenAndServe`. Listen as root and accept as `targetUser`.
func ListenAndServe(targetUser, addr string, handler http.Handler) error {
	if !flag.Parsed() {
		flag.Parse()
	}
	u, err := user.Current()
	if err != nil {
		return err
	}
	if u.Uid != "0" && *listenFD == 0 {
		// we are not root and we have no listen fd. Error.
		return fmt.Errorf("need to run as root. Running as %s (%s)", u.Name, u.Uid)
	} else if u.Uid == "0" && *listenFD == 0 {
		// we are root and we have no listen fd. Do the listen.
		l, err := net.Listen("tcp", addr)
		if err != nil {
			return fmt.Errorf("Listen error: %s", err)
		}
		f, err := l.(*net.TCPListener).File()
		if err != nil {
			return err
		}

		uid, gid, err := lookupUser(targetUser)
		if err != nil {
			return err
		}
		// First extra file: fd == 3
		cmd := exec.Command(os.Args[0])
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.ExtraFiles = append(cmd.ExtraFiles, f)
		cmd.Args = append(cmd.Args, []string{"-listen-fd", fmt.Sprint(2 + len(cmd.ExtraFiles))}...)
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(uid),
				Gid: uint32(gid),
			},
		}
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("cmd.Run error: %s", err)
		}
		return nil
	} else if u.Uid != "0" && *listenFD != 0 {
		// We are not root and we have a listen fd. Do the accept.
		ln := &fdListener{file: os.NewFile(uintptr(*listenFD), "net")}
		if err := http.Serve(ln, handler); err != nil {
			return err
		}
	}
	return fmt.Errorf("setuid fail: %s, %d", u.Uid, *listenFD)
}
<commit_msg>Simplify Accept code / add standalone Listen()<commit_after>// Package golisten allows a user to user http.ListenAndServe with
// any port as root and effectively accept the incomming connection as an other
// un-privileged user.
package golisten

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"strconv"
	"syscall"
)

var listenFD = flag.Int("listen-fd", 0, "open fd for listener")

func lookupUser(username string) (uid, gid int, err error) {
	u, err := user.Lookup(username)
	if err != nil {
		return -1, -1, err
	}
	uid, err = strconv.Atoi(u.Uid)
	if err != nil {
		return -1, -1, err
	}
	gid, err = strconv.Atoi(u.Gid)
	if err != nil {
		return -1, -1, err
	}
	return uid, gid, nil
}

// ListenAndServe wraps `http.ListenAndServe`. Listen as root and accept as `targetUser`.
func ListenAndServe(targetUser, addr string, handler http.Handler) error {
	if !flag.Parsed() {
		flag.Parse()
	}
	u, err := user.Current()
	if err != nil {
		return err
	}
	if u.Uid != "0" && *listenFD == 0 {
		// we are not root and we have no listen fd. Error.
		return fmt.Errorf("need to run as root. Running as %s (%s)", u.Name, u.Uid)
	} else if u.Uid == "0" && *listenFD == 0 {
		// we are root and we have no listen fd. Do the listen.
		l, err := net.Listen("tcp", addr)
		if err != nil {
			return fmt.Errorf("Listen error: %s", err)

		}
		f, err := l.(*net.TCPListener).File()
		if err != nil {
			return err
		}

		uid, gid, err := lookupUser(targetUser)
		if err != nil {
			return err
		}
		// First extra file: fd == 3
		cmd := exec.Command(os.Args[0])
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.ExtraFiles = append(cmd.ExtraFiles, f)
		cmd.Args = append(cmd.Args, []string{"-listen-fd", fmt.Sprint(2 + len(cmd.ExtraFiles))}...)
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(uid),
				Gid: uint32(gid),
			},
		}
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("cmd.Run error: %s", err)
		}
		return nil
	} else if u.Uid != "0" && *listenFD != 0 {
		// We are not root and we have a listen fd. Do the accept.
		l, err := net.FileListener(os.NewFile(uintptr(*listenFD), "net"))
		if err != nil {
			return err
		}

		if err := http.Serve(l, handler); err != nil {
			return err
		}
	}
	return fmt.Errorf("setuid fail: %s, %d", u.Uid, *listenFD)
}

// Listen listens to the requested port and de-escalate the privilege to `targetUser`.
func Listen(targetUser, network, addr string) (net.Listener, error) {
	if !flag.Parsed() {
		flag.Parse()
	}
	u, err := user.Current()
	if err != nil {
		return nil, err
	}
	if u.Uid != "0" && *listenFD == 0 {
		// we are not root and we have no listen fd. Error.
		return nil, fmt.Errorf("need to run as root. Running as %s (%s)", u.Name, u.Uid)
	} else if u.Uid == "0" && *listenFD == 0 {
		// we are root and we have no listen fd. Do the listen.
		ln, err := net.Listen(network, addr)
		if err != nil {
			return nil, fmt.Errorf("Listen error: %s", err)

		}
		var f *os.File
		switch ln := ln.(type) {
		case *net.TCPListener:
			f, err = ln.File()
		case *net.UnixListener:
			f, err = ln.File()
		default:
			err = fmt.Errorf("unsupported network: %T", ln)
		}
		if err != nil {
			return nil, err
		}

		uid, gid, err := lookupUser(targetUser)
		if err != nil {
			return nil, err
		}
		// First extra file: fd == 3
		cmd := exec.Command(os.Args[0])
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.ExtraFiles = append(cmd.ExtraFiles, f)
		cmd.Args = append(cmd.Args, []string{"-listen-fd", fmt.Sprint(2 + len(cmd.ExtraFiles))}...)
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Credential: &syscall.Credential{
				Uid: uint32(uid),
				Gid: uint32(gid),
			},
		}
		if err := cmd.Run(); err != nil {
			return nil, fmt.Errorf("cmd.Run error: %s", err)
		}
		return nil, nil
	} else if u.Uid != "0" && *listenFD != 0 {
		// We are not root and we have a listen fd. Do the accept.
		ln, err := net.FileListener(os.NewFile(uintptr(*listenFD), "net"))
		if err != nil {
			return nil, err
		}
		return ln, nil
	}
	return nil, fmt.Errorf("setuid fail: %s, %d", u.Uid, *listenFD)
}
<|endoftext|>


<commit_before>package jingo

import (
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
)

type (
	TemplateLoader interface {
		Load(string) (string, error)
		ListTemplates() interface{}
	}

	BaseLoader struct {
		e              []error
		FileExtensions []string
	}

	DirLoader struct {
		BaseLoader
		Paths []string
	}

	MapLoader struct {
		BaseLoader
		m *map[string]string
	}
)

func (b *BaseLoader) Load(name string) (string, error) {
	return "not implemented", nil
}

func (b *BaseLoader) ListTemplates() interface{} {
	return "not implemented"
}

func (b *BaseLoader) ValidExtension(ext string) bool {
	for _, extension := range b.FileExtensions {
		if extension == ext {
			return true
		}
	}
	return false
}

func NewDirLoader(basepaths ...string) *DirLoader {
	d := &DirLoader{}
	d.FileExtensions = append(d.FileExtensions, ".html", ".jingo")
	for _, p := range basepaths {
		p, err := filepath.Abs(path.Clean(p))
		if err != nil {
			d.e = append(d.e, Errf("path: %s returned error", p))
		}
		d.Paths = append(d.Paths, p)
	}
	return d
}

func (l *DirLoader) Load(name string) (string, error) {
	for _, p := range l.Paths {
		f := filepath.Join(p, name)
		if l.ValidExtension(filepath.Ext(f)) {
			if _, err := os.Stat(f); err == nil {
				file, err := os.Open(f)
				r, err := ioutil.ReadAll(file)
				return string(r), err
			}
		}
	}
	return "", Errf("Template %s does not exist", name)
}

func (l *MapLoader) Load(name string) (string, error) {
	if r, ok := (*l.m)[name]; ok {
		return string(r), nil
	}
	return "", Errf("Template %s does not exist", name)
}
<commit_msg>path/filepath package usage; remove path<commit_after>package jingo

import (
	"io/ioutil"
	"os"
	"path/filepath"
)

type (
	TemplateLoader interface {
		Load(string) (string, error)
		ListTemplates() interface{}
	}

	BaseLoader struct {
		e              []error
		FileExtensions []string
	}

	DirLoader struct {
		BaseLoader
		Paths []string
	}

	MapLoader struct {
		BaseLoader
		m *map[string]string
	}
)

func (b *BaseLoader) Load(name string) (string, error) {
	return "not implemented", nil
}

func (b *BaseLoader) ListTemplates() interface{} {
	return "not implemented"
}

func (b *BaseLoader) ValidExtension(ext string) bool {
	for _, extension := range b.FileExtensions {
		if extension == ext {
			return true
		}
	}
	return false
}

func NewDirLoader(basepaths ...string) *DirLoader {
	d := &DirLoader{}
	d.FileExtensions = append(d.FileExtensions, ".html", ".jingo")
	for _, p := range basepaths {
		p, err := filepath.Abs(filepath.Clean(p))
		if err != nil {
			d.e = append(d.e, Errf("path: %s returned error", p))
		}
		d.Paths = append(d.Paths, p)
	}
	return d
}

func (l *DirLoader) Load(name string) (string, error) {
	for _, p := range l.Paths {
		f := filepath.Join(p, name)
		if l.ValidExtension(filepath.Ext(f)) {
			if _, err := os.Stat(f); err == nil {
				file, err := os.Open(f)
				r, err := ioutil.ReadAll(file)
				return string(r), err
			}
		}
	}
	return "", Errf("Template %s does not exist", name)
}

func (l *MapLoader) Load(name string) (string, error) {
	if r, ok := (*l.m)[name]; ok {
		return string(r), nil
	}
	return "", Errf("Template %s does not exist", name)
}
<|endoftext|>


<commit_before>package lingo

import (
	"errors"
	"net/http"
	"strconv"
	"strings"
)

type Locale struct {
	Lang, Country string
	Qual          float64
}

func (l *Locale) Name() string {
	return l.Lang + "_" + l.Country
}

func ParseLocale(locale string) Locale {
	locsplt := strings.Split(locale, "_")
	resp := Locale{}
	resp.Lang = locsplt[0]
	if len(locsplt) > 1 {
		resp.Country = locsplt[1]
	}
	return resp
}

const (
	accept_language = "Accept-Language"
)

func supportedLocales(alstr string) []Locale {
	locales := make([]Locale, 0)
	alstr = strings.Replace(alstr, " ", "", -1)
	if alstr == "" {
		return locales
	}
	al := strings.Split(alstr, ",")
	for _, lstr := range al {
		locales = append(locales, Locale{
			Lang:    parseLang(lstr),
			Country: parseCountry(lstr),
			Qual:    parseQual(lstr),
		})
	}
	return locales
}

func GetLocales(r *http.Request) []Locale {
	return supportedLocales(r.Header.Get(accept_language))
}

func GetPreferredLocale(r *http.Request) (*Locale, error) {
	locales := GetLocales(r)
	if len(locales) == 0 {
		return &Locale{}, errors.New("No locale found")
	}
	return &locales[0], nil
}

func parseLang(val string) string {
	locale := strings.Split(val, ";")[0]
	lang := strings.Split(locale, "-")[0]
	return lang
}

func parseCountry(val string) string {
	locale := strings.Split(val, ";")[0]
	spl := strings.Split(locale, "-")
	if len(spl) > 1 {
		return spl[1]
	} else {
		return ""
	}
}

func parseQual(val string) float64 {
	spl := strings.Split(val, ";")
	if len(spl) > 1 {
		qual, err := strconv.ParseFloat(strings.Split(spl[1], "=")[1], 64)
		if err != nil {
			return 1
		}
		return qual
	} else {
		return 1
	}
}
<commit_msg>Found a bug where the underscore was being added to the Locale Lang if there was no associated Country.  This was causing a failure to even return the default language.<commit_after>package lingo

import (
	"errors"
	"net/http"
	"strconv"
	"strings"
)

type Locale struct {
	Lang, Country string
	Qual          float64
}

func (l *Locale) Name() string {
	if len(l.Country) > 0 {
		return l.Lang + "_" + l.Country
	} else {
		return l.Lang
	}
}

func ParseLocale(locale string) Locale {
	locsplt := strings.Split(locale, "_")
	resp := Locale{}
	resp.Lang = locsplt[0]
	if len(locsplt) > 1 {
		resp.Country = locsplt[1]
	}
	return resp
}

const (
	accept_language = "Accept-Language"
)

func supportedLocales(alstr string) []Locale {
	locales := make([]Locale, 0)
	alstr = strings.Replace(alstr, " ", "", -1)
	if alstr == "" {
		return locales
	}
	al := strings.Split(alstr, ",")
	for _, lstr := range al {
		locales = append(locales, Locale{
			Lang:    parseLang(lstr),
			Country: parseCountry(lstr),
			Qual:    parseQual(lstr),
		})
	}
	return locales
}

func GetLocales(r *http.Request) []Locale {
	return supportedLocales(r.Header.Get(accept_language))
}

func GetPreferredLocale(r *http.Request) (*Locale, error) {
	locales := GetLocales(r)
	if len(locales) == 0 {
		return &Locale{}, errors.New("No locale found")
	}
	return &locales[0], nil
}

func parseLang(val string) string {
	locale := strings.Split(val, ";")[0]
	lang := strings.Split(locale, "-")[0]
	return lang
}

func parseCountry(val string) string {
	locale := strings.Split(val, ";")[0]
	spl := strings.Split(locale, "-")
	if len(spl) > 1 {
		return spl[1]
	} else {
		return ""
	}
}

func parseQual(val string) float64 {
	spl := strings.Split(val, ";")
	if len(spl) > 1 {
		qual, err := strconv.ParseFloat(strings.Split(spl[1], "=")[1], 64)
		if err != nil {
			return 1
		}
		return qual
	} else {
		return 1
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:generate ./COMPILE-PROTOS.sh

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"

	"github.com/golang/protobuf/proto"
	"github.com/googleapis/openapi-compiler/OpenAPIv2"
	"github.com/googleapis/openapi-compiler/compiler"
	plugins "github.com/googleapis/openapi-compiler/plugins"
)

type PluginCall struct {
	Name   string
	Output string
}

func writeFile(name string, bytes []byte) {
	var writer io.Writer
	if name == "-" {
		writer = os.Stdout
	} else {
		file, _ := os.Create(name)
		defer file.Close()
		writer = file
	}
	writer.Write(bytes)
	if name == "-" {
		writer.Write([]byte("\n"))
	}
}

func main() {
	pb_regex, err := regexp.Compile("--pb_out=(.+)")
	json_regex, err := regexp.Compile("--json_out=(.+)")
	text_regex, err := regexp.Compile("--text_out=(.+)")
	errors_regex, err := regexp.Compile("--errors_out=(.+)")
	plugin_regex, err := regexp.Compile("--(.+)_out=(.+)")

	sourceName := ""
	binaryProtoFileName := ""
	jsonProtoFileName := ""
	textProtoFileName := ""
	errorFileName := ""
	keepReferences := false

	var pluginCalls []*PluginCall

	for i, arg := range os.Args {
		if i == 0 {
			continue
		}
		argbytes := []byte(arg)
		var m [][]byte
		if m = pb_regex.FindSubmatch(argbytes); m != nil {
			binaryProtoFileName = string(m[1])
		} else if m = json_regex.FindSubmatch(argbytes); m != nil {
			jsonProtoFileName = string(m[1])
		} else if m = text_regex.FindSubmatch(argbytes); m != nil {
			textProtoFileName = string(m[1])
		} else if m = errors_regex.FindSubmatch(argbytes); m != nil {
			errorFileName = string(m[1])
		} else if m = plugin_regex.FindSubmatch(argbytes); m != nil {
			pluginCall := &PluginCall{Name: string(m[1]), Output: string(m[2])}
			pluginCalls = append(pluginCalls, pluginCall)
		} else if arg == "--keep_refs" {
			keepReferences = true
		} else {
			sourceName = arg
		}
	}

	usage := `
Usage: openapic OPENAPI_SOURCE [OPTIONS]
  OPENAPI_SOURCE is the filename or URL of the OpenAPI description to read.
Options:
  --pb_out=FILENAME       Write a binary proto to a file with the specified name.
  --text_out=FILENAME     Write a text proto to a file with the specified name.
  --json_out=FILENAME     Write a json proto to a file with the specified name.
  --errors_out=FILENAME   Write compilation errors to a file with the specified name.
  --PLUGIN_out=FILENAME   Run the plugin named openapi_PLUGIN and write results to a file with the specified name.
  --keep_refs             Disable resolution of $ref references.
`

	if textProtoFileName == "" &&
		jsonProtoFileName == "" &&
		binaryProtoFileName == "" &&
		errorFileName == "" &&
		len(pluginCalls) == 0 {
		fmt.Printf("Missing output directives.\n%s\n", usage)
		os.Exit(-1)
	}

	if sourceName == "" {
		fmt.Printf("No input specified.\n%s\n", usage)
		os.Exit(-1)
	}

	if errorFileName == "" {
		errorFileName = "-"
	}

	raw, err := compiler.ReadFile(sourceName)
	if err != nil {
		fmt.Printf("Error: %+v\n", err)
		os.Exit(-1)
	}

	document, err := openapi_v2.NewDocument(raw, compiler.NewContext("$root", nil))
	if err != nil {
		writeFile(errorFileName, []byte(err.Error()))
		os.Exit(-1)
	}

	if !keepReferences {
		_, err = document.ResolveReferences(sourceName)
		if err != nil {
			writeFile(errorFileName, []byte(err.Error()))
			os.Exit(-1)
		}
	}

	if textProtoFileName != "" {
		bytes := []byte(proto.MarshalTextString(document))
		writeFile(textProtoFileName, bytes)
	}

	if jsonProtoFileName != "" {
		jsonBytes, _ := json.Marshal(document)
		writeFile(jsonProtoFileName, jsonBytes)
	}

	if binaryProtoFileName != "" {
		protoBytes, _ := proto.Marshal(document)
		writeFile(binaryProtoFileName, protoBytes)
	}

	for _, pluginCall := range pluginCalls {
		if pluginCall.Name != "" {
			request := &plugins.PluginRequest{}
			request.Parameter = ""

			version := &plugins.Version{}
			version.Major = 0
			version.Minor = 1
			version.Patch = 0
			request.CompilerVersion = version

			wrapper := &plugins.Wrapper{}
			wrapper.Name = sourceName
			wrapper.Version = "v2"
			protoBytes, _ := proto.Marshal(document)
			wrapper.Value = protoBytes
			request.Wrapper = []*plugins.Wrapper{wrapper}
			requestBytes, _ := proto.Marshal(request)

			cmd := exec.Command("openapi_" + pluginCall.Name)
			cmd.Stdin = bytes.NewReader(requestBytes)
			output, err := cmd.Output()
			if err != nil {
				fmt.Printf("Error: %+v\n", err)
			}
			response := &plugins.PluginResponse{}
			err = proto.Unmarshal(output, response)

			var writer io.Writer
			if pluginCall.Output == "-" {
				writer = os.Stdout
			} else {
				file, _ := os.Create(pluginCall.Output)
				defer file.Close()
				writer = file
			}
			for _, text := range response.Text {
				writer.Write([]byte(text))
			}
		}
	}
}
<commit_msg>Clean up and simplify the main compiler code.<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:generate ./COMPILE-PROTOS.sh

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"

	"github.com/golang/protobuf/proto"
	"github.com/googleapis/openapi-compiler/OpenAPIv2"
	"github.com/googleapis/openapi-compiler/compiler"
	plugins "github.com/googleapis/openapi-compiler/plugins"
)

type PluginCall struct {
	Name   string
	Output string
}

func (pluginCall *PluginCall) perform(document *openapi_v2.Document, sourceName string) {
	if pluginCall.Name != "" {
		request := &plugins.PluginRequest{}
		request.Parameter = ""

		version := &plugins.Version{}
		version.Major = 0
		version.Minor = 1
		version.Patch = 0
		request.CompilerVersion = version

		wrapper := &plugins.Wrapper{}
		wrapper.Name = sourceName
		wrapper.Version = "v2"
		protoBytes, _ := proto.Marshal(document)
		wrapper.Value = protoBytes
		request.Wrapper = []*plugins.Wrapper{wrapper}
		requestBytes, _ := proto.Marshal(request)

		cmd := exec.Command("openapi_" + pluginCall.Name)
		cmd.Stdin = bytes.NewReader(requestBytes)
		output, err := cmd.Output()
		if err != nil {
			fmt.Printf("Error: %+v\n", err)
		}
		response := &plugins.PluginResponse{}
		err = proto.Unmarshal(output, response)

		var writer io.Writer
		if pluginCall.Output == "-" {
			writer = os.Stdout
		} else {
			file, _ := os.Create(pluginCall.Output)
			defer file.Close()
			writer = file
		}
		for _, text := range response.Text {
			writer.Write([]byte(text))
		}
	}
}

func writeFile(name string, bytes []byte) {
	var writer io.Writer
	if name == "-" {
		writer = os.Stdout
	} else {
		file, _ := os.Create(name)
		defer file.Close()
		writer = file
	}
	writer.Write(bytes)
	if name == "-" {
		writer.Write([]byte("\n"))
	}
}

func main() {
	usage := `
Usage: openapic OPENAPI_SOURCE [OPTIONS]
  OPENAPI_SOURCE is the filename or URL of an OpenAPI description to read.
Options:
  --pb_out=FILENAME       Write a binary proto to a file with the specified name.
  --json_out=FILENAME     Write a json proto to a file with the specified name.
  --text_out=FILENAME     Write a text proto to a file with the specified name.
  --errors_out=FILENAME   Write compilation errors to a file with the specified name.
  --PLUGIN_out=FILENAME   Run the plugin named openapi_PLUGIN and write results to a file with the specified name.
  --keep_refs             Disable resolution of $ref references.
`
	// default values for all options
	sourceName := ""
	binaryProtoFileName := ""
	jsonProtoFileName := ""
	textProtoFileName := ""
	errorFileName := ""
	pluginCalls := make([]*PluginCall, 0)
	keepReferences := false

	// arg processing matches patterns of the form "--PLUGIN_out=PATH"
	plugin_regex, err := regexp.Compile("--(.+)_out=(.+)")

	for i, arg := range os.Args {
		if i == 0 {
			continue // skip the tool name
		}
		var m [][]byte
		if m = plugin_regex.FindSubmatch([]byte(arg)); m != nil {
			pluginName := string(m[1])
			outputName := string(m[2])
			switch pluginName {
			case "pb":
				binaryProtoFileName = outputName
			case "json":
				jsonProtoFileName = outputName
			case "text":
				textProtoFileName = outputName
			case "errors":
				errorFileName = outputName
			default:
				pluginCall := &PluginCall{Name: pluginName, Output: outputName}
				pluginCalls = append(pluginCalls, pluginCall)
			}
		} else if arg == "--keep_refs" {
			keepReferences = true
		} else if arg[0] == '-' {
			fmt.Printf("Unknown option: %s.\n%s\n", arg, usage)
			os.Exit(-1)
		} else {
			sourceName = arg
		}
	}

	if binaryProtoFileName == "" &&
		jsonProtoFileName == "" &&
		textProtoFileName == "" &&
		errorFileName == "" &&
		len(pluginCalls) == 0 {
		fmt.Printf("Missing output directives.\n%s\n", usage)
		os.Exit(-1)
	}

	if sourceName == "" {
		fmt.Printf("No input specified.\n%s\n", usage)
		os.Exit(-1)
	}

	// If we get here and the error output is unspecified, write errors to stdout.
	if errorFileName == "" {
		errorFileName = "-"
	}

	// read and compile the OpenAPI source
	raw, err := compiler.ReadFile(sourceName)
	if err != nil {
		fmt.Printf("Error: %+v\n", err)
		os.Exit(-1)
	}
	document, err := openapi_v2.NewDocument(raw, compiler.NewContext("$root", nil))
	if err != nil {
		writeFile(errorFileName, []byte(err.Error()))
		os.Exit(-1)
	}

	// optionally resolve internal references
	if !keepReferences {
		_, err = document.ResolveReferences(sourceName)
		if err != nil {
			writeFile(errorFileName, []byte(err.Error()))
			os.Exit(-1)
		}
	}

	// perform all specified actions
	if binaryProtoFileName != "" {
		// write proto in binary format
		protoBytes, _ := proto.Marshal(document)
		writeFile(binaryProtoFileName, protoBytes)
	}
	if jsonProtoFileName != "" {
		// write proto in json format
		jsonBytes, _ := json.Marshal(document)
		writeFile(jsonProtoFileName, jsonBytes)
	}
	if textProtoFileName != "" {
		// write proto in text format
		bytes := []byte(proto.MarshalTextString(document))
		writeFile(textProtoFileName, bytes)
	}
	for _, pluginCall := range pluginCalls {
		pluginCall.perform(document, sourceName)
	}
}
<|endoftext|>