<commit_before>package extra

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/jmoiron/sqlx"

	"github.com/elpinal/coco3/extra/ast"
	"github.com/elpinal/coco3/extra/parser" // Only for ParseError.
	"github.com/elpinal/coco3/extra/typed"
	"github.com/elpinal/coco3/extra/types"
)

type Env struct {
	cmds map[string]typed.Command
	Option
}

type Option struct {
	DB *sqlx.DB
}

func New(opt Option) Env {
	return Env{
		Option: opt,
		cmds: map[string]typed.Command{
			"exec":    execCommand,
			"execenv": execenvCommand, // exec with env
			"cd":      cdCommand,
			"exit":    exitCommand,
			"free":    freeCommand,
			"history": historyCommand,

			"remove": removeCommand,

			"ls":   lsCommand,
			"man":  manCommand,
			"make": makeCommand,

			"git":   gitCommand,
			"cargo": cargoCommand,
			"go":    goCommand,
			"stack": stackCommand,
			"lein":  leinCommand,
			"ocaml": ocamlCommand,

			"vim":    vimCommand,
			"emacs":  emacsCommand,
			"screen": screenCommand,

			"cnp":  cnpCommand,
			"gvmn": gvmnCommand,
			"vvmn": vvmnCommand,
		},
	}
}

func WithoutDefault() Env {
	return Env{cmds: make(map[string]typed.Command)}
}

func (e *Env) Bind(name string, c typed.Command) {
	e.cmds[name] = c
}

func (e *Env) Eval(command *ast.Command) (err error) {
	if command == nil {
		return nil
	}
	tc, found := e.cmds[command.Name.Lit]
	if !found {
		return &parser.ParseError{
			Msg:    fmt.Sprintf("no such typed command: %q", command.Name.Lit),
			Line:   command.Name.Line,
			Column: command.Name.Column,
		}
	}
	if len(command.Args) != len(tc.Params) {
		return &parser.ParseError{
			Msg:    fmt.Sprintf("the length of args (%d) != the one of params (%d)", len(command.Args), len(tc.Params)),
			Line:   command.Name.Line,
			Column: command.Name.Column,
		}
	}
	for i, arg := range command.Args {
		if arg.Type() != tc.Params[i] {
			return &parser.ParseError{
				Msg:    fmt.Sprintf("type mismatch: (%v) (type of %v) does not match with (%v) (expected type)", arg.Type(), arg, tc.Params[i]),
				Line:   command.Name.Line,
				Column: command.Name.Column,
			}
		}
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	defer close(c)
	defer signal.Stop(c)

	defer func() {
		r := recover()
		if r == nil {
			return
		}
		var ok bool
		// may overwrite error of tc.Fn.
		err, ok = r.(error)
		if !ok {
			panic(r)
		}
	}()

	return tc.Fn(command.Args, e.DB)
}

func toSlice(list ast.List) ([]string, error) {
	ret := make([]string, 0, list.Length())
	for {
		switch x := list.(type) {
		case *ast.Cons:
			ret = append(ret, x.Head)
			list = x.Tail
		case *ast.Empty:
			return ret, nil
		default:
			return nil, fmt.Errorf("unexpected list type: %T", x)
		}
	}
}

var execCommand = typed.Command{
	Params: []types.Type{types.String, types.StringList},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "exec")
		}
		cmd := stdCmd(args[0].(*ast.String).Lit, cmdArgs...)
		return cmd.Run()
	},
}

var execenvCommand = typed.Command{
	Params: []types.Type{types.StringList, types.String, types.StringList},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[2].(ast.List))
		if err != nil {
			return errors.Wrap(err, "execenv")
		}
		cmd := stdCmd(args[1].(*ast.String).Lit, cmdArgs...)
		env, err := toSlice(args[0].(ast.List))
		if err != nil {
			return errors.Wrap(err, "execenv")
		}
		for _, e := range env {
			if !strings.Contains(e, "=") {
				return errors.New(`execenv: each item of the first argument must be the form "key=value"`)
			}
		}
		cmd.Env = append(os.Environ(), env...)
		return cmd.Run()
	},
}

var cdCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		return os.Chdir(args[0].(*ast.String).Lit)
	},
}

var exitCommand = typed.Command{
	Params: []types.Type{types.Int},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		n, err := strconv.Atoi(args[0].(*ast.Int).Lit)
		if err != nil {
			return err
		}
		os.Exit(n)
		return nil
	},
}

var freeCommand = typed.Command{
	Params: []types.Type{types.String, types.StringList},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "free")
		}
		name := args[0].(*ast.String).Lit
		cmd := exec.Cmd{Path: name, Args: append([]string{name}, cmdArgs...)}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}

func commandArgs(name string) func([]ast.Expr, *sqlx.DB) error {
	return func(args []ast.Expr, _ *sqlx.DB) error {
		list, err := toSlice(args[0].(ast.List))
		if err != nil {
			return errors.Wrap(err, name)
		}
		return stdCmd(name, list...).Run()
	}
}

func commandsInCommand(name string) func([]ast.Expr, *sqlx.DB) error {
	return func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, name)
		}
		var cmd *exec.Cmd
		switch lit := args[0].(*ast.Ident).Lit; lit {
		case "command":
			cmd = stdCmd(name, cmdArgs...)
		default:
			cmd = stdCmd(name, append([]string{lit}, cmdArgs...)...)
		}
		return cmd.Run()
	}
}

func goCommand1() func([]ast.Expr, *sqlx.DB) error {
	return func(args []ast.Expr, _ *sqlx.DB) error {
		name := "go"
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, name)
		}
		var cmd *exec.Cmd
		switch lit := args[0].(*ast.Ident).Lit; lit {
		case "command":
			cmd = stdCmd(name, cmdArgs...)
		case "testall":
			// I can't be confident in using such
			// a subcommand-specific way.  Another suggestion might
			// be like `go test all`, where 'all' is a postfix
			// operator of './...'.
			cmd = stdCmd(name, append([]string{"test"}, append(cmdArgs, "./...")...)...)
		default:
			cmd = stdCmd(name, append([]string{lit}, cmdArgs...)...)
		}
		return cmd.Run()
	}
}

var gitCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("git"),
}

var cargoCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("cargo"),
}

var goCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     goCommand1(),
}

var stackCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("stack"),
}

var leinCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("lein"),
}

func stdCmd(name string, args ...string) *exec.Cmd {
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	return cmd
}

func stdExec(name string, args ...string) func([]ast.Expr, *sqlx.DB) error {
	return func(_ []ast.Expr, _ *sqlx.DB) error {
		return stdCmd(name, args...).Run()
	}
}

var vimCommand = typed.Command{
	Params: []types.Type{},
	Fn:     stdExec("vim"),
}

var emacsCommand = typed.Command{
	Params: []types.Type{},
	Fn:     stdExec("emacs"),
}

func withEnv(s string, cmd *exec.Cmd) *exec.Cmd {
	if cmd.Env == nil {
		cmd.Env = os.Environ()
	}
	cmd.Env = append(cmd.Env, s)
	return cmd
}

var screenCommand = typed.Command{
	Params: []types.Type{},
	Fn: func(_ []ast.Expr, _ *sqlx.DB) error {
		return withEnv("LANG=en_US.UTF-8", stdCmd("screen")).Run()
	},
}

type execution struct {
	Time time.Time
	Line string
}

var historyCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, db *sqlx.DB) error {
		var jsonFormat bool
		var enc *json.Encoder
		switch format := e[0].(*ast.String).Lit; format {
		case "json":
			jsonFormat = true
		case "lines":
		default:
			return fmt.Errorf("history: format %q is not supported", format)
		}

		buf := bufio.NewWriter(os.Stdout)
		if jsonFormat {
			enc = json.NewEncoder(buf)
		}
		rows, err := db.Queryx("select * from command_info")
		if err != nil {
			return err
		}
		data := execution{}
		for rows.Next() {
			err := rows.StructScan(&data)
			if err != nil {
				return err
			}
			if jsonFormat {
				err := enc.Encode(data)
				if err != nil {
					return err
				}
			} else {
				buf.WriteString(data.Time.Format("Mon, 02 Jan 2006 15:04:05"))
				buf.Write([]byte("  "))
				buf.WriteString(data.Line)
				buf.WriteByte('\n')
			}
		}
		return buf.Flush()
	},
}

var lsCommand = typed.Command{
	Params: []types.Type{},
	Fn:     stdExec("ls", "--show-control-chars", "--color=auto"),
}

var manCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, _ *sqlx.DB) error {
		lit := e[0].(*ast.String).Lit
		return stdCmd("man", lit).Run()
	},
}

var removeCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, _ *sqlx.DB) error {
		lit := e[0].(*ast.String).Lit
		return stdCmd("rm", "-i", lit).Run()
	},
}

func remove(exprs []ast.Expr, _ *sqlx.DB) error {
	s := exprs[0].(*ast.String).Lit
	fmt.Println("remove ", s)
	var ans string
	fmt.Scanf("%s", &ans)
	if ans != "y" {
		return nil
	}
	return os.Remove(s)
}

var cnpCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, _ *sqlx.DB) error {
		lit := e[0].(*ast.String).Lit
		return stdCmd("create-new-project", lit).Run()
	},
}

var gvmnCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("gvmn"),
}

var vvmnCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("vvmn"),
}

var makeCommand = typed.Command{
	Params: []types.Type{types.StringList},
	Fn:     commandArgs("make"),
}

var ocamlCommand = typed.Command{
	Params: []types.Type{types.StringList},
	Fn:     commandArgs("ocaml"),
}
<commit_msg>Replace func with remove<commit_after>package extra

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/jmoiron/sqlx"

	"github.com/elpinal/coco3/extra/ast"
	"github.com/elpinal/coco3/extra/parser" // Only for ParseError.
	"github.com/elpinal/coco3/extra/typed"
	"github.com/elpinal/coco3/extra/types"
)

type Env struct {
	cmds map[string]typed.Command
	Option
}

type Option struct {
	DB *sqlx.DB
}

func New(opt Option) Env {
	return Env{
		Option: opt,
		cmds: map[string]typed.Command{
			"exec":    execCommand,
			"execenv": execenvCommand, // exec with env
			"cd":      cdCommand,
			"exit":    exitCommand,
			"free":    freeCommand,
			"history": historyCommand,

			"remove": removeCommand,

			"ls":   lsCommand,
			"man":  manCommand,
			"make": makeCommand,

			"git":   gitCommand,
			"cargo": cargoCommand,
			"go":    goCommand,
			"stack": stackCommand,
			"lein":  leinCommand,
			"ocaml": ocamlCommand,

			"vim":    vimCommand,
			"emacs":  emacsCommand,
			"screen": screenCommand,

			"cnp":  cnpCommand,
			"gvmn": gvmnCommand,
			"vvmn": vvmnCommand,
		},
	}
}

func WithoutDefault() Env {
	return Env{cmds: make(map[string]typed.Command)}
}

func (e *Env) Bind(name string, c typed.Command) {
	e.cmds[name] = c
}

func (e *Env) Eval(command *ast.Command) (err error) {
	if command == nil {
		return nil
	}
	tc, found := e.cmds[command.Name.Lit]
	if !found {
		return &parser.ParseError{
			Msg:    fmt.Sprintf("no such typed command: %q", command.Name.Lit),
			Line:   command.Name.Line,
			Column: command.Name.Column,
		}
	}
	if len(command.Args) != len(tc.Params) {
		return &parser.ParseError{
			Msg:    fmt.Sprintf("the length of args (%d) != the one of params (%d)", len(command.Args), len(tc.Params)),
			Line:   command.Name.Line,
			Column: command.Name.Column,
		}
	}
	for i, arg := range command.Args {
		if arg.Type() != tc.Params[i] {
			return &parser.ParseError{
				Msg:    fmt.Sprintf("type mismatch: (%v) (type of %v) does not match with (%v) (expected type)", arg.Type(), arg, tc.Params[i]),
				Line:   command.Name.Line,
				Column: command.Name.Column,
			}
		}
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	defer close(c)
	defer signal.Stop(c)

	defer func() {
		r := recover()
		if r == nil {
			return
		}
		var ok bool
		// may overwrite error of tc.Fn.
		err, ok = r.(error)
		if !ok {
			panic(r)
		}
	}()

	return tc.Fn(command.Args, e.DB)
}

func toSlice(list ast.List) ([]string, error) {
	ret := make([]string, 0, list.Length())
	for {
		switch x := list.(type) {
		case *ast.Cons:
			ret = append(ret, x.Head)
			list = x.Tail
		case *ast.Empty:
			return ret, nil
		default:
			return nil, fmt.Errorf("unexpected list type: %T", x)
		}
	}
}

var execCommand = typed.Command{
	Params: []types.Type{types.String, types.StringList},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "exec")
		}
		cmd := stdCmd(args[0].(*ast.String).Lit, cmdArgs...)
		return cmd.Run()
	},
}

var execenvCommand = typed.Command{
	Params: []types.Type{types.StringList, types.String, types.StringList},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[2].(ast.List))
		if err != nil {
			return errors.Wrap(err, "execenv")
		}
		cmd := stdCmd(args[1].(*ast.String).Lit, cmdArgs...)
		env, err := toSlice(args[0].(ast.List))
		if err != nil {
			return errors.Wrap(err, "execenv")
		}
		for _, e := range env {
			if !strings.Contains(e, "=") {
				return errors.New(`execenv: each item of the first argument must be the form "key=value"`)
			}
		}
		cmd.Env = append(os.Environ(), env...)
		return cmd.Run()
	},
}

var cdCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		return os.Chdir(args[0].(*ast.String).Lit)
	},
}

var exitCommand = typed.Command{
	Params: []types.Type{types.Int},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		n, err := strconv.Atoi(args[0].(*ast.Int).Lit)
		if err != nil {
			return err
		}
		os.Exit(n)
		return nil
	},
}

var freeCommand = typed.Command{
	Params: []types.Type{types.String, types.StringList},
	Fn: func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "free")
		}
		name := args[0].(*ast.String).Lit
		cmd := exec.Cmd{Path: name, Args: append([]string{name}, cmdArgs...)}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}

func commandArgs(name string) func([]ast.Expr, *sqlx.DB) error {
	return func(args []ast.Expr, _ *sqlx.DB) error {
		list, err := toSlice(args[0].(ast.List))
		if err != nil {
			return errors.Wrap(err, name)
		}
		return stdCmd(name, list...).Run()
	}
}

func commandsInCommand(name string) func([]ast.Expr, *sqlx.DB) error {
	return func(args []ast.Expr, _ *sqlx.DB) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, name)
		}
		var cmd *exec.Cmd
		switch lit := args[0].(*ast.Ident).Lit; lit {
		case "command":
			cmd = stdCmd(name, cmdArgs...)
		default:
			cmd = stdCmd(name, append([]string{lit}, cmdArgs...)...)
		}
		return cmd.Run()
	}
}

func goCommand1() func([]ast.Expr, *sqlx.DB) error {
	return func(args []ast.Expr, _ *sqlx.DB) error {
		name := "go"
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, name)
		}
		var cmd *exec.Cmd
		switch lit := args[0].(*ast.Ident).Lit; lit {
		case "command":
			cmd = stdCmd(name, cmdArgs...)
		case "testall":
			// I can't be confident in using such
			// a subcommand-specific way.  Another suggestion might
			// be like `go test all`, where 'all' is a postfix
			// operator of './...'.
			cmd = stdCmd(name, append([]string{"test"}, append(cmdArgs, "./...")...)...)
		default:
			cmd = stdCmd(name, append([]string{lit}, cmdArgs...)...)
		}
		return cmd.Run()
	}
}

var gitCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("git"),
}

var cargoCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("cargo"),
}

var goCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     goCommand1(),
}

var stackCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("stack"),
}

var leinCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("lein"),
}

func stdCmd(name string, args ...string) *exec.Cmd {
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	return cmd
}

func stdExec(name string, args ...string) func([]ast.Expr, *sqlx.DB) error {
	return func(_ []ast.Expr, _ *sqlx.DB) error {
		return stdCmd(name, args...).Run()
	}
}

var vimCommand = typed.Command{
	Params: []types.Type{},
	Fn:     stdExec("vim"),
}

var emacsCommand = typed.Command{
	Params: []types.Type{},
	Fn:     stdExec("emacs"),
}

func withEnv(s string, cmd *exec.Cmd) *exec.Cmd {
	if cmd.Env == nil {
		cmd.Env = os.Environ()
	}
	cmd.Env = append(cmd.Env, s)
	return cmd
}

var screenCommand = typed.Command{
	Params: []types.Type{},
	Fn: func(_ []ast.Expr, _ *sqlx.DB) error {
		return withEnv("LANG=en_US.UTF-8", stdCmd("screen")).Run()
	},
}

type execution struct {
	Time time.Time
	Line string
}

var historyCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, db *sqlx.DB) error {
		var jsonFormat bool
		var enc *json.Encoder
		switch format := e[0].(*ast.String).Lit; format {
		case "json":
			jsonFormat = true
		case "lines":
		default:
			return fmt.Errorf("history: format %q is not supported", format)
		}

		buf := bufio.NewWriter(os.Stdout)
		if jsonFormat {
			enc = json.NewEncoder(buf)
		}
		rows, err := db.Queryx("select * from command_info")
		if err != nil {
			return err
		}
		data := execution{}
		for rows.Next() {
			err := rows.StructScan(&data)
			if err != nil {
				return err
			}
			if jsonFormat {
				err := enc.Encode(data)
				if err != nil {
					return err
				}
			} else {
				buf.WriteString(data.Time.Format("Mon, 02 Jan 2006 15:04:05"))
				buf.Write([]byte("  "))
				buf.WriteString(data.Line)
				buf.WriteByte('\n')
			}
		}
		return buf.Flush()
	},
}

var lsCommand = typed.Command{
	Params: []types.Type{},
	Fn:     stdExec("ls", "--show-control-chars", "--color=auto"),
}

var manCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, _ *sqlx.DB) error {
		lit := e[0].(*ast.String).Lit
		return stdCmd("man", lit).Run()
	},
}

var removeCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn:     remove,
}

func remove(exprs []ast.Expr, _ *sqlx.DB) error {
	s := exprs[0].(*ast.String).Lit
	fmt.Println("remove ", s)
	var ans string
	fmt.Scanf("%s", &ans)
	if ans != "y" {
		return nil
	}
	return os.Remove(s)
}

var cnpCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(e []ast.Expr, _ *sqlx.DB) error {
		lit := e[0].(*ast.String).Lit
		return stdCmd("create-new-project", lit).Run()
	},
}

var gvmnCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("gvmn"),
}

var vvmnCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn:     commandsInCommand("vvmn"),
}

var makeCommand = typed.Command{
	Params: []types.Type{types.StringList},
	Fn:     commandArgs("make"),
}

var ocamlCommand = typed.Command{
	Params: []types.Type{types.StringList},
	Fn:     commandArgs("ocaml"),
}
<|endoftext|>


<commit_before>package agollo

import (
	"os"
	"strconv"
	"time"
	"fmt"
	"net/url"
	"github.com/cihub/seelog"
)

const appConfigFileName  ="app.properties"

var (
	refresh_interval = 5 *time.Minute //5m
	refresh_interval_key = "apollo.refreshInterval"  //

	long_poll_interval = 5 *time.Second //5s
	long_poll_connect_timeout  = 1 * time.Minute //1m

	connect_timeout  = 1 * time.Second //1s
	read_timeout     = 5 * time.Second //5s
	//for on error retry
	on_error_retry_interval = 1 * time.Second //1s
	//for typed config cache of parser result, e.g. integer, double, long, etc.
	max_config_cache_size    = 500             //500 cache key
	config_cache_expire_time = 1 * time.Minute //1 minute

	//max retries connect apollo
	max_retries=5

	//appconfig
	appConfig *AppConfig
)

func init() {
	//init common
	initCommon()

	//init config
	initConfig()
}

func initCommon()  {

	initRefreshInterval()
}

func initConfig() {
	var err error
	//init config file
	appConfig,err = loadJsonConfig(appConfigFileName)

	if err!=nil{
		panic(err)
	}

	go func(appConfig *AppConfig) {
		apolloConfig:=&ApolloConfig{}
		apolloConfig.AppId=appConfig.AppId
		apolloConfig.Cluster=appConfig.Cluster
		apolloConfig.NamespaceName=appConfig.NamespaceName

		updateApolloConfig(apolloConfig)
	}(appConfig)
}

func GetAppConfig()*AppConfig  {
	return appConfig
}

type AppConfig struct {
	AppId string `json:"appId"`
	Cluster string `json:"cluster"`
	NamespaceName string `json:"namespaceName"`
	Ip string `json:"ip"`
}

func initRefreshInterval() error {
	customizedRefreshInterval:=os.Getenv(refresh_interval_key)
	if isNotEmpty(customizedRefreshInterval){
		interval,err:=strconv.Atoi(customizedRefreshInterval)
		if isNotNil(err) {
			seelog.Errorf("Config for apollo.refreshInterval is invalid:%s",customizedRefreshInterval)
			return err
		}
		refresh_interval=time.Duration(interval)
	}
	return nil
}

func getConfigUrl(config *AppConfig) string{
	current:=GetCurrentApolloConfig()
	return fmt.Sprintf("http://%s/configs/%s/%s/%s?releaseKey=%s&ip=%s",
		config.Ip,
		url.QueryEscape(config.AppId),
		url.QueryEscape(config.Cluster),
		url.QueryEscape(config.NamespaceName),
		url.QueryEscape(current.ReleaseKey),
		getInternal())
}

func getNotifyUrl(notifications string,config *AppConfig) string{
	return fmt.Sprintf("http://%s/notifications/v2?appId=%s&cluster=%s&notifications=%s",
		config.Ip,
		url.QueryEscape(config.AppId),
		url.QueryEscape(config.Cluster),
		url.QueryEscape(notifications))
}

func getServicesConfigUrl(config *AppConfig) string{
	return fmt.Sprintf("http://%s/services/config?appId=%s&ip=%s",
		config.Ip,
		url.QueryEscape(config.AppId),
		getInternal())
}<commit_msg>add sync ip list<commit_after>package agollo

import (
	"os"
	"strconv"
	"time"
	"fmt"
	"net/url"
	"github.com/cihub/seelog"
	"errors"
	"net/http"
	"io/ioutil"
)

const appConfigFileName  ="app.properties"

var (
	refresh_interval = 5 *time.Minute //5m
	refresh_interval_key = "apollo.refreshInterval"  //

	long_poll_interval = 5 *time.Second //5s
	long_poll_connect_timeout  = 1 * time.Minute //1m

	connect_timeout  = 1 * time.Second //1s
	read_timeout     = 5 * time.Second //5s
	//for on error retry
	on_error_retry_interval = 1 * time.Second //1s
	//for typed config cache of parser result, e.g. integer, double, long, etc.
	max_config_cache_size    = 500             //500 cache key
	config_cache_expire_time = 1 * time.Minute //1 minute

	//max retries connect apollo
	max_retries=5

	//refresh ip list
	refresh_ip_list_interval=20 *time.Minute //20m

	//appconfig
	appConfig *AppConfig
)

type AppConfig struct {
	AppId string `json:"appId"`
	Cluster string `json:"cluster"`
	NamespaceName string `json:"namespaceName"`
	Ip string `json:"ip"`
}

func init() {
	//init common
	initCommon()

	//init config
	initConfig()
}

func initCommon()  {

	initRefreshInterval()
}

func initConfig() {
	var err error
	//init config file
	appConfig,err = loadJsonConfig(appConfigFileName)

	if err!=nil{
		panic(err)
	}

	go func(appConfig *AppConfig) {
		apolloConfig:=&ApolloConfig{}
		apolloConfig.AppId=appConfig.AppId
		apolloConfig.Cluster=appConfig.Cluster
		apolloConfig.NamespaceName=appConfig.NamespaceName

		updateApolloConfig(apolloConfig)
	}(appConfig)
}

//set timer for update ip list
//interval : 20m
func initServerIpList() {
	t2 := time.NewTimer(refresh_ip_list_interval)
	for {
		select {
		case <-t2.C:
			syncServerIpList()
			t2.Reset(refresh_ip_list_interval)
		}
	}
}

//sync ip list from server
//then
//1.update cache
//2.store in disk
func syncServerIpList() {
	client := &http.Client{
		Timeout:connect_timeout,
	}

	appConfig:=GetAppConfig()
	if appConfig==nil{
		panic("can not find apollo config!please confirm!")
	}
	url:=getServicesConfigUrl(appConfig)
	seelog.Debug("url:",url)

	retry:=0
	var responseBody []byte
	var err error
	var res *http.Response
	for{
		retry++

		if retry>max_retries{
			break
		}

		res,err=client.Get(url)

		if res==nil||err!=nil{
			seelog.Error("Connect Apollo Server Fail,Error:",err)
			continue
		}

		//not modified break
		switch res.StatusCode {
		case http.StatusOK:
			responseBody, err = ioutil.ReadAll(res.Body)
			if err!=nil{
				seelog.Error("Connect Apollo Server Fail,Error:",err)
				continue
			}
			return
		default:
			seelog.Error("Connect Apollo Server Fail,Error:",err)
			if res!=nil{
				seelog.Error("Connect Apollo Server Fail,StatusCode:",res.StatusCode)
			}
			// if error then sleep
			time.Sleep(on_error_retry_interval)
			continue
		}
	}

	seelog.Debug(responseBody)

	seelog.Error("Over Max Retry Still Error,Error:",err)
	if err==nil{
		err=errors.New("Over Max Retry Still Error!")
	}
}

func GetAppConfig()*AppConfig  {
	return appConfig
}

func initRefreshInterval() error {
	customizedRefreshInterval:=os.Getenv(refresh_interval_key)
	if isNotEmpty(customizedRefreshInterval){
		interval,err:=strconv.Atoi(customizedRefreshInterval)
		if isNotNil(err) {
			seelog.Errorf("Config for apollo.refreshInterval is invalid:%s",customizedRefreshInterval)
			return err
		}
		refresh_interval=time.Duration(interval)
	}
	return nil
}

func getConfigUrl(config *AppConfig) string{
	current:=GetCurrentApolloConfig()
	return fmt.Sprintf("http://%s/configs/%s/%s/%s?releaseKey=%s&ip=%s",
		config.Ip,
		url.QueryEscape(config.AppId),
		url.QueryEscape(config.Cluster),
		url.QueryEscape(config.NamespaceName),
		url.QueryEscape(current.ReleaseKey),
		getInternal())
}

func getNotifyUrl(notifications string,config *AppConfig) string{
	return fmt.Sprintf("http://%s/notifications/v2?appId=%s&cluster=%s&notifications=%s",
		config.Ip,
		url.QueryEscape(config.AppId),
		url.QueryEscape(config.Cluster),
		url.QueryEscape(notifications))
}

func getServicesConfigUrl(config *AppConfig) string{
	return fmt.Sprintf("http://%s/services/config?appId=%s&ip=%s",
		config.Ip,
		url.QueryEscape(config.AppId),
		getInternal())
}<|endoftext|>


<commit_before>package aws

import (
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/service/ec2"
	awsbase "github.com/hashicorp/aws-sdk-go-base"
)

func TestAWSClientPartitionHostname(t *testing.T) {
	testCases := []struct {
		Name      string
		AWSClient *AWSClient
		Prefix    string
		Expected  string
	}{
		{
			Name: "AWS Commercial",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com",
			},
			Prefix:   "test",
			Expected: "test.amazonaws.com",
		},
		{
			Name: "AWS China",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com.cn",
			},
			Prefix:   "test",
			Expected: "test.amazonaws.com.cn",
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.Name, func(t *testing.T) {
			got := testCase.AWSClient.PartitionHostname(testCase.Prefix)

			if got != testCase.Expected {
				t.Errorf("got %s, expected %s", got, testCase.Expected)
			}
		})
	}
}

func TestAWSClientRegionalHostname(t *testing.T) {
	testCases := []struct {
		Name      string
		AWSClient *AWSClient
		Prefix    string
		Expected  string
	}{
		{
			Name: "AWS Commercial",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com",
				region:    "us-west-2",
			},
			Prefix:   "test",
			Expected: "test.us-west-2.amazonaws.com",
		},
		{
			Name: "AWS China",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com.cn",
				region:    "cn-northwest-1",
			},
			Prefix:   "test",
			Expected: "test.cn-northwest-1.amazonaws.com.cn",
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.Name, func(t *testing.T) {
			got := testCase.AWSClient.RegionalHostname(testCase.Prefix)

			if got != testCase.Expected {
				t.Errorf("got %s, expected %s", got, testCase.Expected)
			}
		})
	}
}

func TestGetSupportedEC2Platforms(t *testing.T) {
	ec2Endpoints := []*awsbase.MockEndpoint{
		{
			Request: &awsbase.MockRequest{
				Method: "POST",
				Uri:    "/",
				Body:   "Action=DescribeAccountAttributes&AttributeName.1=supported-platforms&Version=2016-11-15",
			},
			Response: &awsbase.MockResponse{
				StatusCode:  200,
				Body:        test_ec2_describeAccountAttributes_response,
				ContentType: "text/xml",
			},
		},
	}
	closeFunc, sess, err := awsbase.GetMockedAwsApiSession("EC2", ec2Endpoints)
	if err != nil {
		t.Fatal(err)
	}
	defer closeFunc()
	conn := ec2.New(sess)

	platforms, err := GetSupportedEC2Platforms(conn)
	if err != nil {
		t.Fatalf("Expected no error, received: %s", err)
	}
	expectedPlatforms := []string{"VPC", "EC2"}
	if !reflect.DeepEqual(platforms, expectedPlatforms) {
		t.Fatalf("Received platforms: %q\nExpected: %q\n", platforms, expectedPlatforms)
	}
}

var test_ec2_describeAccountAttributes_response = `<DescribeAccountAttributesResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
  <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
  <accountAttributeSet>
    <item>
      <attributeName>supported-platforms</attributeName>
      <attributeValueSet>
        <item>
          <attributeValue>VPC</attributeValue>
        </item>
        <item>
          <attributeValue>EC2</attributeValue>
        </item>
      </attributeValueSet>
    </item>
  </accountAttributeSet>
</DescribeAccountAttributesResponse>`
<commit_msg>tests/config: Fix hardcoded regions<commit_after>package aws

import (
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/service/ec2"
	awsbase "github.com/hashicorp/aws-sdk-go-base"
)

func TestAWSClientPartitionHostname(t *testing.T) {
	testCases := []struct {
		Name      string
		AWSClient *AWSClient
		Prefix    string
		Expected  string
	}{
		{
			Name: "AWS Commercial",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com",
			},
			Prefix:   "test",
			Expected: "test.amazonaws.com",
		},
		{
			Name: "AWS China",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com.cn",
			},
			Prefix:   "test",
			Expected: "test.amazonaws.com.cn",
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.Name, func(t *testing.T) {
			got := testCase.AWSClient.PartitionHostname(testCase.Prefix)

			if got != testCase.Expected {
				t.Errorf("got %s, expected %s", got, testCase.Expected)
			}
		})
	}
}

func TestAWSClientRegionalHostname(t *testing.T) {
	testCases := []struct {
		Name      string
		AWSClient *AWSClient
		Prefix    string
		Expected  string
	}{
		{
			Name: "AWS Commercial",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com",
				region:    "us-west-2", //lintignore:AWSAT003
			},
			Prefix:   "test",
			Expected: "test.us-west-2.amazonaws.com", //lintignore:AWSAT003
		},
		{
			Name: "AWS China",
			AWSClient: &AWSClient{
				dnsSuffix: "amazonaws.com.cn",
				region:    "cn-northwest-1", //lintignore:AWSAT003
			},
			Prefix:   "test",
			Expected: "test.cn-northwest-1.amazonaws.com.cn", //lintignore:AWSAT003
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.Name, func(t *testing.T) {
			got := testCase.AWSClient.RegionalHostname(testCase.Prefix)

			if got != testCase.Expected {
				t.Errorf("got %s, expected %s", got, testCase.Expected)
			}
		})
	}
}

func TestGetSupportedEC2Platforms(t *testing.T) {
	ec2Endpoints := []*awsbase.MockEndpoint{
		{
			Request: &awsbase.MockRequest{
				Method: "POST",
				Uri:    "/",
				Body:   "Action=DescribeAccountAttributes&AttributeName.1=supported-platforms&Version=2016-11-15",
			},
			Response: &awsbase.MockResponse{
				StatusCode:  200,
				Body:        test_ec2_describeAccountAttributes_response,
				ContentType: "text/xml",
			},
		},
	}
	closeFunc, sess, err := awsbase.GetMockedAwsApiSession("EC2", ec2Endpoints)
	if err != nil {
		t.Fatal(err)
	}
	defer closeFunc()
	conn := ec2.New(sess)

	platforms, err := GetSupportedEC2Platforms(conn)
	if err != nil {
		t.Fatalf("Expected no error, received: %s", err)
	}
	expectedPlatforms := []string{"VPC", "EC2"}
	if !reflect.DeepEqual(platforms, expectedPlatforms) {
		t.Fatalf("Received platforms: %q\nExpected: %q\n", platforms, expectedPlatforms)
	}
}

var test_ec2_describeAccountAttributes_response = `<DescribeAccountAttributesResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
  <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
  <accountAttributeSet>
    <item>
      <attributeName>supported-platforms</attributeName>
      <attributeValueSet>
        <item>
          <attributeValue>VPC</attributeValue>
        </item>
        <item>
          <attributeValue>EC2</attributeValue>
        </item>
      </attributeValueSet>
    </item>
  </accountAttributeSet>
</DescribeAccountAttributesResponse>`
<|endoftext|>


<commit_before>package azure

import (
	"fmt"
	"os"
	"reflect"
	"testing"

	"github.com/cheekybits/is"
	"github.com/graymeta/stow"
	"github.com/graymeta/stow/test"
)

var (
	azureaccount = os.Getenv("AZUREACCOUNT")
	azurekey     = os.Getenv("AZUREKEY")
)

func TestStow(t *testing.T) {
	cfg := stow.ConfigMap{"account": azureaccount, "key": azurekey}
	test.All(t, "azure", cfg)
}

func TestEtagCleanup(t *testing.T) {
	etagValue := "9c51403a2255f766891a1382288dece4"
	permutations := []string{
		`"%s"`,       // Enclosing quotations
		`W/\"%s\"`,   // Weak tag identifier with escapted quotes
		`W/"%s"`,     // Weak tag identifier with quotes
		`"\"%s"\"`,   // Double quotes, inner escaped
		`""%s""`,     // Double quotes,
		`"W/"%s""`,   // Double quotes with weak identifier
		`"W/\"%s\""`, // Double quotes with weak identifier, inner escaped
	}
	for index, p := range permutations {
		testStr := fmt.Sprintf(p, etagValue)
		cleanTestStr := cleanEtag(testStr)
		if etagValue != cleanTestStr {
			t.Errorf(`Failure at permutation #%d (%s), result: %s`,
				index, permutations[index], cleanTestStr)
		}
	}
}

func TestPrepMetadataSuccess(t *testing.T) {
	is := is.New(t)

	m := make(map[string]string)
	m["one"] = "two"
	m["3"] = "4"
	m["ninety-nine"] = "100"

	m2 := make(map[string]interface{})
	for key, value := range m {
		m2[key] = value
	}

	//returns map[string]interface
	returnedMap, err := prepMetadata(m2)
	is.NoErr(err)

	if !reflect.DeepEqual(returnedMap, m) {
		t.Errorf("Expected map (%+v) and returned map (%+v) are not equal.", m, returnedMap)
	}
}

func TestPrepMetadataFailureWithNonStringValues(t *testing.T) {
	is := is.New(t)

	m := make(map[string]interface{})
	m["float"] = 8.9
	m["number"] = 9

	_, err := prepMetadata(m)
	is.Err(err)
}
<commit_msg>azure: skip tests if no credentials<commit_after>package azure

import (
	"fmt"
	"os"
	"reflect"
	"testing"

	"github.com/cheekybits/is"
	"github.com/graymeta/stow"
	"github.com/graymeta/stow/test"
)

var (
	azureaccount = os.Getenv("AZUREACCOUNT")
	azurekey     = os.Getenv("AZUREKEY")
)

func TestStow(t *testing.T) {
	if azureaccount == "" || azurekey == "" {
		t.Skip("skipping test because missing either AZUREACCOUNT or AZUREKEY")
	}

	cfg := stow.ConfigMap{"account": azureaccount, "key": azurekey}
	test.All(t, "azure", cfg)
}

func TestEtagCleanup(t *testing.T) {
	etagValue := "9c51403a2255f766891a1382288dece4"
	permutations := []string{
		`"%s"`,       // Enclosing quotations
		`W/\"%s\"`,   // Weak tag identifier with escapted quotes
		`W/"%s"`,     // Weak tag identifier with quotes
		`"\"%s"\"`,   // Double quotes, inner escaped
		`""%s""`,     // Double quotes,
		`"W/"%s""`,   // Double quotes with weak identifier
		`"W/\"%s\""`, // Double quotes with weak identifier, inner escaped
	}
	for index, p := range permutations {
		testStr := fmt.Sprintf(p, etagValue)
		cleanTestStr := cleanEtag(testStr)
		if etagValue != cleanTestStr {
			t.Errorf(`Failure at permutation #%d (%s), result: %s`,
				index, permutations[index], cleanTestStr)
		}
	}
}

func TestPrepMetadataSuccess(t *testing.T) {
	is := is.New(t)

	m := make(map[string]string)
	m["one"] = "two"
	m["3"] = "4"
	m["ninety-nine"] = "100"

	m2 := make(map[string]interface{})
	for key, value := range m {
		m2[key] = value
	}

	//returns map[string]interface
	returnedMap, err := prepMetadata(m2)
	is.NoErr(err)

	if !reflect.DeepEqual(returnedMap, m) {
		t.Errorf("Expected map (%+v) and returned map (%+v) are not equal.", m, returnedMap)
	}
}

func TestPrepMetadataFailureWithNonStringValues(t *testing.T) {
	is := is.New(t)

	m := make(map[string]interface{})
	m["float"] = 8.9
	m["number"] = 9

	_, err := prepMetadata(m)
	is.Err(err)
}
<|endoftext|>


<commit_before>package mail

import (
	"container/list"
	"io"
	"mime"
	"net/textproto"
	"strings"

	"github.com/emersion/go-messages"
)

// A PartHeader is a mail part header. It contains convenience functions to get
// and set header fields.
type PartHeader interface {
	// Add adds the key, value pair to the header.
	Add(key, value string)
	// Del deletes the values associated with key.
	Del(key string)
	// Get gets the first value associated with the given key. If there are no
	// values associated with the key, Get returns "".
	Get(key string) string
	// Set sets the header entries associated with key to the single element
	// value. It replaces any existing values associated with key.
	Set(key, value string)
}

// A Part is either a mail text or an attachment. Header is either a TextHeader
// or an AttachmentHeader.
type Part struct {
	Header PartHeader
	Body   io.Reader
}

// A Reader reads mail parts.
type Reader struct {
	Header  Header

	e       *messages.Entity
	readers *list.List
}

// NewReader creates a new mail reader.
func NewReader(e *messages.Entity) *Reader {
	mr := e.MultipartReader()
	if mr == nil {
		// Artificially create a multipart entity
		h := make(textproto.MIMEHeader)
		h.Set("Content-Type", "multipart/mixed")
		mr = messages.NewMultipart(h, []*messages.Entity{e}).MultipartReader()
	}

	l := list.New()
	l.PushBack(mr)

	return &Reader{Header{e.Header}, e, l}
}

// CreateReader reads a mail header from r and returns a new mail reader.
func CreateReader(r io.Reader) (*Reader, error) {
	e, err := messages.Read(r)
	if err != nil {
		return nil, err
	}

	return NewReader(e), nil
}

// NextPart returns the next mail part. If there is no more part, io.EOF is
// returned as error.
func (r *Reader) NextPart() (*Part, error) {
	for r.readers.Len() > 0 {
		e := r.readers.Back()
		mr := e.Value.(messages.MultipartReader)

		p, err := mr.NextPart()
		if err == io.EOF {
			// This whole multipart entity has been read, continue with the next one
			r.readers.Remove(e)
			continue
		} else if err != nil {
			return nil, err
		}

		if pmr := p.MultipartReader(); pmr != nil {
			// This is a multipart part, read it
			r.readers.PushBack(pmr)
		} else {
			// This is a non-multipart part, return a mail part
			mp := &Part{Body: p.Body}
			disp, _, _ := mime.ParseMediaType(p.Header.Get("Content-Disposition"))
			if strings.HasPrefix(p.Header.Get("Content-Type"), "text/") && disp != "attachment" {
				mp.Header = TextHeader{p.Header}
			} else {
				mp.Header = AttachmentHeader{p.Header}
			}
			return mp, nil
		}
	}

	return nil, io.EOF
}

// Close finishes the reader.
func (r *Reader) Close() error {
	for r.readers.Len() > 0 {
		e := r.readers.Back()
		mr := e.Value.(messages.MultipartReader)

		if err := mr.Close(); err != nil {
			return err
		}

		r.readers.Remove(e)
	}

	return nil
}
<commit_msg>mail: go fmt<commit_after>package mail

import (
	"container/list"
	"io"
	"mime"
	"net/textproto"
	"strings"

	"github.com/emersion/go-messages"
)

// A PartHeader is a mail part header. It contains convenience functions to get
// and set header fields.
type PartHeader interface {
	// Add adds the key, value pair to the header.
	Add(key, value string)
	// Del deletes the values associated with key.
	Del(key string)
	// Get gets the first value associated with the given key. If there are no
	// values associated with the key, Get returns "".
	Get(key string) string
	// Set sets the header entries associated with key to the single element
	// value. It replaces any existing values associated with key.
	Set(key, value string)
}

// A Part is either a mail text or an attachment. Header is either a TextHeader
// or an AttachmentHeader.
type Part struct {
	Header PartHeader
	Body   io.Reader
}

// A Reader reads mail parts.
type Reader struct {
	Header Header

	e       *messages.Entity
	readers *list.List
}

// NewReader creates a new mail reader.
func NewReader(e *messages.Entity) *Reader {
	mr := e.MultipartReader()
	if mr == nil {
		// Artificially create a multipart entity
		h := make(textproto.MIMEHeader)
		h.Set("Content-Type", "multipart/mixed")
		mr = messages.NewMultipart(h, []*messages.Entity{e}).MultipartReader()
	}

	l := list.New()
	l.PushBack(mr)

	return &Reader{Header{e.Header}, e, l}
}

// CreateReader reads a mail header from r and returns a new mail reader.
func CreateReader(r io.Reader) (*Reader, error) {
	e, err := messages.Read(r)
	if err != nil {
		return nil, err
	}

	return NewReader(e), nil
}

// NextPart returns the next mail part. If there is no more part, io.EOF is
// returned as error.
func (r *Reader) NextPart() (*Part, error) {
	for r.readers.Len() > 0 {
		e := r.readers.Back()
		mr := e.Value.(messages.MultipartReader)

		p, err := mr.NextPart()
		if err == io.EOF {
			// This whole multipart entity has been read, continue with the next one
			r.readers.Remove(e)
			continue
		} else if err != nil {
			return nil, err
		}

		if pmr := p.MultipartReader(); pmr != nil {
			// This is a multipart part, read it
			r.readers.PushBack(pmr)
		} else {
			// This is a non-multipart part, return a mail part
			mp := &Part{Body: p.Body}
			disp, _, _ := mime.ParseMediaType(p.Header.Get("Content-Disposition"))
			if strings.HasPrefix(p.Header.Get("Content-Type"), "text/") && disp != "attachment" {
				mp.Header = TextHeader{p.Header}
			} else {
				mp.Header = AttachmentHeader{p.Header}
			}
			return mp, nil
		}
	}

	return nil, io.EOF
}

// Close finishes the reader.
func (r *Reader) Close() error {
	for r.readers.Len() > 0 {
		e := r.readers.Back()
		mr := e.Value.(messages.MultipartReader)

		if err := mr.Close(); err != nil {
			return err
		}

		r.readers.Remove(e)
	}

	return nil
}
<|endoftext|>


<commit_before>package signalfx

import (
	"log"
	"net/http"
	"net/http/httptest"
	"runtime"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestBackgrounding(t *testing.T) {
	Convey("Testing background reporting", t, func(c C) {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			c.So(r.Header.Get(TokenHeader), ShouldEqual, "abcdefg")
			w.Write([]byte(`"OK"`))
		}))
		defer ts.Close()

		config := NewConfig()
		So(config, ShouldNotBeNil)

		config.URL = ts.URL
		config.AuthToken = "abcdefg"

		reporter := NewReporter(config, nil)
		So(reporter, ShouldNotBeNil)

		So(reporter.datapoints.Len(), ShouldEqual, 0)
		So(len(reporter.buckets), ShouldEqual, 0)

		// FIXME: it should be easier to override a client's transport…
		tw := transportWrapper{wrapped: reporter.client.tr}
		reporter.client.tr = &tw
		reporter.client.client = &http.Client{Transport: &tw}

		So(tw.counter, ShouldBeZeroValue)
		var count int
		counter := reporter.NewCounter("count", Value(count), nil)
		counter.Set(1)
		_, err := reporter.Report(nil)
		So(err, ShouldBeNil)
		So(tw.counter, ShouldEqual, 1)

		rj := Background(time.Second*5, func() {
			if _, err := reporter.Report(nil); err != nil {
				log.Printf("[ERR] background reporting: %s", err)
			}
		})
		counter.Set(2)
		time.Sleep(time.Second * 7)
		err = rj.Pause()
		So(err, ShouldBeNil)
		So(tw.counter, ShouldEqual, 2)
		// proves that it's paused
		counter.Set(3)
		time.Sleep(time.Second * 7)
		So(tw.counter, ShouldEqual, 2)
		// this Do should execute, but not restart the ticker
		err = rj.Do()
		So(err, ShouldBeNil)
		runtime.Gosched()
		So(tw.counter, ShouldEqual, 3)

		err = rj.Resume()
		So(err, ShouldBeNil)
		counter.Set(4)
		time.Sleep(time.Second * 7)
		So(tw.counter, ShouldEqual, 4)
		counter.Set(5)
		err = rj.Do()
		So(err, ShouldBeNil)
		runtime.Gosched()
		So(tw.counter, ShouldEqual, 5)
		err = rj.Stop()
		So(err, ShouldBeNil)
		So(tw.counter, ShouldEqual, 5)
		time.Sleep(time.Second * 7)
		// proves that it's stopped
		So(tw.counter, ShouldEqual, 5)
		So(rj, ShouldBeNil)

		// test that messages to a stopped job fail
		err = rj.Pause()
		So(err, ShouldNotBeNil)
		err = rj.Resume()
		So(err, ShouldNotBeNil)
		err = rj.Do()
		So(err, ShouldNotBeNil)
	})
}

type transportWrapper struct {
	wrapped http.RoundTripper
	counter int
}

func (tw *transportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
	tw.counter++
	return tw.wrapped.RoundTrip(req)
}

func TestBackgroundJob(t *testing.T) {
	Convey("Background jobs should work", t, func() {

		var x int
		job := Background(time.Second*2, func() {
			x++
		})
		time.Sleep(time.Second * 3)
		job.Stop()
		So(x, ShouldEqual, 1)
		job.Stop()
	})
}
<commit_msg>Replace runtime.Gosched with time.Sleep in tests<commit_after>package signalfx

import (
	"log"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestBackgrounding(t *testing.T) {
	Convey("Testing background reporting", t, func(c C) {
		ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			c.So(r.Header.Get(TokenHeader), ShouldEqual, "abcdefg")
			w.Write([]byte(`"OK"`))
		}))
		defer ts.Close()

		config := NewConfig()
		So(config, ShouldNotBeNil)

		config.URL = ts.URL
		config.AuthToken = "abcdefg"

		reporter := NewReporter(config, nil)
		So(reporter, ShouldNotBeNil)

		So(reporter.datapoints.Len(), ShouldEqual, 0)
		So(len(reporter.buckets), ShouldEqual, 0)

		// FIXME: it should be easier to override a client's transport…
		tw := transportWrapper{wrapped: reporter.client.tr}
		reporter.client.tr = &tw
		reporter.client.client = &http.Client{Transport: &tw}

		So(tw.counter, ShouldBeZeroValue)
		var count int
		counter := reporter.NewCounter("count", Value(count), nil)
		counter.Set(1)
		_, err := reporter.Report(nil)
		So(err, ShouldBeNil)
		So(tw.counter, ShouldEqual, 1)

		rj := Background(time.Second*5, func() {
			if _, err := reporter.Report(nil); err != nil {
				log.Printf("[ERR] background reporting: %s", err)
			}
		})
		counter.Set(2)
		time.Sleep(time.Second * 7)
		err = rj.Pause()
		So(err, ShouldBeNil)
		So(tw.counter, ShouldEqual, 2)
		// proves that it's paused
		counter.Set(3)
		time.Sleep(time.Second * 7)
		So(tw.counter, ShouldEqual, 2)
		// this Do should execute, but not restart the ticker
		err = rj.Do()
		So(err, ShouldBeNil)
		time.Sleep(time.Millisecond * 250)
		So(tw.counter, ShouldEqual, 3)

		err = rj.Resume()
		So(err, ShouldBeNil)
		counter.Set(4)
		time.Sleep(time.Second * 7)
		So(tw.counter, ShouldEqual, 4)
		counter.Set(5)
		err = rj.Do()
		So(err, ShouldBeNil)
		time.Sleep(time.Millisecond * 250)
		So(tw.counter, ShouldEqual, 5)
		err = rj.Stop()
		So(err, ShouldBeNil)
		So(tw.counter, ShouldEqual, 5)
		time.Sleep(time.Second * 7)
		// proves that it's stopped
		So(tw.counter, ShouldEqual, 5)
		So(rj, ShouldBeNil)

		// test that messages to a stopped job fail
		err = rj.Pause()
		So(err, ShouldNotBeNil)
		err = rj.Resume()
		So(err, ShouldNotBeNil)
		err = rj.Do()
		So(err, ShouldNotBeNil)
	})
}

type transportWrapper struct {
	wrapped http.RoundTripper
	counter int
}

func (tw *transportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
	tw.counter++
	return tw.wrapped.RoundTrip(req)
}

func TestBackgroundJob(t *testing.T) {
	Convey("Background jobs should work", t, func() {

		var x int
		job := Background(time.Second*2, func() {
			x++
		})
		time.Sleep(time.Second * 3)
		job.Stop()
		So(x, ShouldEqual, 1)
		job.Stop()
	})
}
<|endoftext|>


<commit_before>package narcissus

import (
	"fmt"
	"log"
	"testing"

	"honnef.co/go/augeas"
)

func TestHost(t *testing.T) {
	aug, err := augeas.New(fakeroot, "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	host := &Host{
		augeasPath: "/files/etc/hosts/1",
	}
	err = n.Parse(host)
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	if host.IPAddress != "127.0.0.1" {
		t.Errorf("Expected IP to be 127.0.0.1, got %s", host.IPAddress)
	}
}

func TestHosts(t *testing.T) {
	aug, err := augeas.New(fakeroot, "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	hosts, err := n.NewHosts()
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	if len(hosts.Hosts) != 5 {
		t.Errorf("Expected 5 hosts, got %v", len(hosts.Hosts))
	}

	if hosts.Hosts[2].IPAddress != "::1" {
		t.Errorf("Expected IP to be ::1, got %s", hosts.Hosts[2].IPAddress)
	}

	if len(hosts.Hosts[2].Aliases) != 2 {
		t.Errorf("Expected 2 aliases, got %v", len(hosts.Hosts[2].Aliases))
	}
}

func ExampleNarcissus_NewHosts() {
	aug, err := augeas.New(fakeroot, "", augeas.None)
	if err != nil {
		log.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	hosts, err := n.NewHosts()
	if err != nil {
		log.Fatalf("Expected no error, got %v", err)
	}

	fmt.Printf("IP=%v", hosts.Hosts[0].IPAddress)
	// Output: IP=127.0.0.1
}
<commit_msg>Add TestWriteHosts<commit_after>package narcissus

import (
	"fmt"
	"log"
	"os"
	"testing"

	"honnef.co/go/augeas"
)

func TestParseHost(t *testing.T) {
	aug, err := augeas.New(fakeroot, "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	host := &Host{
		augeasPath: "/files/etc/hosts/1",
	}
	err = n.Parse(host)
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	if host.IPAddress != "127.0.0.1" {
		t.Errorf("Expected IP to be 127.0.0.1, got %s", host.IPAddress)
	}
}

func TestParseHosts(t *testing.T) {
	aug, err := augeas.New(fakeroot, "", augeas.None)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	hosts, err := n.NewHosts()
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	if len(hosts.Hosts) != 5 {
		t.Errorf("Expected 5 hosts, got %v", len(hosts.Hosts))
	}

	if hosts.Hosts[2].IPAddress != "::1" {
		t.Errorf("Expected IP to be ::1, got %s", hosts.Hosts[2].IPAddress)
	}

	if len(hosts.Hosts[2].Aliases) != 2 {
		t.Errorf("Expected 2 aliases, got %v", len(hosts.Hosts[2].Aliases))
	}
}

func TestWriteHosts(t *testing.T) {
	// Use augeas.SaveNewFile once https://github.com/dominikh/go-augeas/issues/6 is fixed
	aug, err := augeas.New(fakeroot, "", 2)
	if err != nil {
		t.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	// Cleanup
	os.Remove(fakeroot + "/etc/hosts.augnew")

	hosts, err := n.NewHosts()
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}
	errStr, _ := aug.Get("/augeas//error/message")
	if errStr != "" {
		t.Errorf("Failed with %s", errStr)
	}

	err = n.Write(hosts)
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}
	err = aug.Save()
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	errStr, _ = aug.Get("/augeas//error/message")
	if errStr != "" {
		t.Errorf("Failed with %s", errStr)
	}

	// check that file is unchanged
	if f, err := os.Stat(fakeroot + "/etc/hosts.augnew"); err == nil && f.Mode().IsRegular() {
		t.Errorf("Expected augnew file to be absent, was present")
	}

	hosts.Hosts[0].Canonical = "foo"

	err = n.Write(hosts)
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}
	err = aug.Save()
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}

	errStr, _ = aug.Get("/augeas//error/message")
	if errStr != "" {
		t.Errorf("Failed with %s", errStr)
	}

	// check that file is changed
	expectedDiff := `--- orig
+++ new
@@ -1 +1 @@
-127.0.0.1	localhost
+127.0.0.1	foo
`
	diff, err := diffNewContent("/etc/hosts")
	if err != nil {
		t.Errorf("Failed to compute diff: %v", err)
	} else if diff != expectedDiff {
		t.Errorf("Expected diff %s, got %s", expectedDiff, diff)
	}
}

func ExampleNarcissus_NewHosts() {
	aug, err := augeas.New(fakeroot, "", augeas.None)
	if err != nil {
		log.Fatal("Failed to create Augeas handler")
	}
	n := New(&aug)

	hosts, err := n.NewHosts()
	if err != nil {
		log.Fatalf("Expected no error, got %v", err)
	}

	fmt.Printf("IP=%v", hosts.Hosts[0].IPAddress)
	// Output: IP=127.0.0.1
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"math/rand"
	"strconv"
)

type nakedSingleTechnique struct {
	*basicSolveTechnique
}

type hiddenSingleTechnique struct {
	*basicSolveTechnique
}

type obviousInCollectionTechnique struct {
	*basicSolveTechnique
}

func newFillSolveStep(cell *Cell, num int, technique SolveTechnique) *SolveStep {
	cellArr := []*Cell{cell}
	numArr := []int{num}
	return &SolveStep{cellArr, nil, numArr, nil, technique}
}

func (self *obviousInCollectionTechnique) Description(step *SolveStep) string {
	if len(step.TargetNums) == 0 {
		return ""
	}
	num := step.TargetNums[0]
	groupName := "<NONE>"
	groupNumber := 0
	switch self.groupType {
	case GROUP_BLOCK:
		groupName = "block"
		groupNumber = step.TargetCells.Block()
	case GROUP_COL:
		groupName = "column"
		groupNumber = step.TargetCells.Col()
	case GROUP_ROW:
		groupName = "row"
		groupNumber = step.TargetCells.Row()
	}

	return fmt.Sprintf("%s is the only cell in %s %d that is unfilled, and it must be %d", step.TargetCells.Description(), groupName, groupNumber, num)
}

func (self *obviousInCollectionTechnique) Find(grid *Grid) []*SolveStep {
	//TODO: implement this.
	return nil
}

func (self *nakedSingleTechnique) Description(step *SolveStep) string {
	if len(step.TargetNums) == 0 {
		return ""
	}
	num := step.TargetNums[0]
	return fmt.Sprintf("%d is the only remaining valid number for that cell", num)
}

func (self *nakedSingleTechnique) Find(grid *Grid) []*SolveStep {
	//TODO: test that this will find multiple if they exist.
	var results []*SolveStep
	getter := grid.queue.NewGetter()
	for {
		obj := getter.GetSmallerThan(2)
		if obj == nil {
			//There weren't any cells with one option left.
			//If there weren't any, period, then results is still nil already.
			return results
		}
		cell := obj.(*Cell)
		result := newFillSolveStep(cell, cell.implicitNumber(), self)
		if result.IsUseful(grid) {
			results = append(results, result)
		}
	}
}

func (self *hiddenSingleTechnique) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.TargetNums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.TargetNums[0]

	var groupName string
	var otherGroupName string
	var groupNum int
	var otherGroupNum string
	switch self.groupType {
	case GROUP_BLOCK:
		groupName = "block"
		otherGroupName = "cell"
		groupNum = step.TargetCells.Block()
		otherGroupNum = step.TargetCells.Description()
	case GROUP_ROW:
		groupName = "row"
		otherGroupName = "column"
		groupNum = step.TargetCells.Row()
		otherGroupNum = strconv.Itoa(cell.Col)
	case GROUP_COL:
		groupName = "column"
		otherGroupName = "row"
		groupNum = step.TargetCells.Col()
		otherGroupNum = strconv.Itoa(cell.Row)
	default:
		groupName = "<NONE>"
		otherGroupName = "<NONE>"
		groupNum = -1
		otherGroupNum = "<NONE>"
	}

	return fmt.Sprintf("%d is required in the %d %s, and %s is the only %s it fits", num, groupNum, groupName, otherGroupNum, otherGroupName)
}

func (self *hiddenSingleTechnique) Find(grid *Grid) []*SolveStep {
	//TODO: test that if there are multiple we find them both.
	return necessaryInCollection(grid, self, self.getter(grid))
}

func necessaryInCollection(grid *Grid, technique SolveTechnique, collectionGetter func(index int) CellList) []*SolveStep {
	//This will be a random item
	indexes := rand.Perm(DIM)

	var results []*SolveStep

	for _, i := range indexes {
		seenInCollection := make([]int, DIM)
		collection := collectionGetter(i)
		for _, cell := range collection {
			for _, possibility := range cell.Possibilities() {
				seenInCollection[possibility-1]++
			}
		}
		seenIndexes := rand.Perm(DIM)
		for _, index := range seenIndexes {
			seen := seenInCollection[index]
			if seen == 1 {
				//Okay, we know our target number. Which cell was it?
				for _, cell := range collection {
					if cell.Possible(index + 1) {
						//Found it... just make sure it's useful (it would be rare for it to not be).
						result := newFillSolveStep(cell, index+1, technique)
						if result.IsUseful(grid) {
							results = append(results, result)
							break
						}
						//Hmm, wasn't useful. Keep trying...
					}
				}
			}
		}
	}
	return results
}
<commit_msg>Implemented obviousInCollection Find function. No tests specific to it yet.<commit_after>package sudoku

import (
	"fmt"
	"log"
	"math/rand"
	"strconv"
)

type nakedSingleTechnique struct {
	*basicSolveTechnique
}

type hiddenSingleTechnique struct {
	*basicSolveTechnique
}

type obviousInCollectionTechnique struct {
	*basicSolveTechnique
}

func newFillSolveStep(cell *Cell, num int, technique SolveTechnique) *SolveStep {
	cellArr := []*Cell{cell}
	numArr := []int{num}
	return &SolveStep{cellArr, nil, numArr, nil, technique}
}

func (self *obviousInCollectionTechnique) Description(step *SolveStep) string {
	if len(step.TargetNums) == 0 {
		return ""
	}
	num := step.TargetNums[0]
	groupName := "<NONE>"
	groupNumber := 0
	switch self.groupType {
	case GROUP_BLOCK:
		groupName = "block"
		groupNumber = step.TargetCells.Block()
	case GROUP_COL:
		groupName = "column"
		groupNumber = step.TargetCells.Col()
	case GROUP_ROW:
		groupName = "row"
		groupNumber = step.TargetCells.Row()
	}

	return fmt.Sprintf("%s is the only cell in %s %d that is unfilled, and it must be %d", step.TargetCells.Description(), groupName, groupNumber, num)
}

func (self *obviousInCollectionTechnique) Find(grid *Grid) []*SolveStep {
	return obviousInCollection(grid, self, self.getter(grid))
}

func obviousInCollection(grid *Grid, technique SolveTechnique, collectionGetter func(index int) CellList) []*SolveStep {
	//TODO: test this!
	indexes := rand.Perm(DIM)
	var results []*SolveStep
	for _, index := range indexes {
		collection := collectionGetter(index)
		openCells := collection.FilterByHasPossibilities()
		if len(openCells) == 1 {
			//Okay, only one cell in this collection has an opening, which must mean it has one possibilty.
			cell := openCells[0]
			possibilities := cell.Possibilities()
			if len(possibilities) != 1 {
				log.Fatalln("Expected the cell to only have one possibility")
			} else {
				possibility := possibilities[0]
				step := newFillSolveStep(cell, possibility, technique)
				if step.IsUseful(grid) {
					results = append(results, step)
				}
			}

		}
	}
	return results
}

func (self *nakedSingleTechnique) Description(step *SolveStep) string {
	if len(step.TargetNums) == 0 {
		return ""
	}
	num := step.TargetNums[0]
	return fmt.Sprintf("%d is the only remaining valid number for that cell", num)
}

func (self *nakedSingleTechnique) Find(grid *Grid) []*SolveStep {
	//TODO: test that this will find multiple if they exist.
	var results []*SolveStep
	getter := grid.queue.NewGetter()
	for {
		obj := getter.GetSmallerThan(2)
		if obj == nil {
			//There weren't any cells with one option left.
			//If there weren't any, period, then results is still nil already.
			return results
		}
		cell := obj.(*Cell)
		result := newFillSolveStep(cell, cell.implicitNumber(), self)
		if result.IsUseful(grid) {
			results = append(results, result)
		}
	}
}

func (self *hiddenSingleTechnique) Description(step *SolveStep) string {
	//TODO: format the text to say "first/second/third/etc"
	if len(step.TargetCells) == 0 || len(step.TargetNums) == 0 {
		return ""
	}
	cell := step.TargetCells[0]
	num := step.TargetNums[0]

	var groupName string
	var otherGroupName string
	var groupNum int
	var otherGroupNum string
	switch self.groupType {
	case GROUP_BLOCK:
		groupName = "block"
		otherGroupName = "cell"
		groupNum = step.TargetCells.Block()
		otherGroupNum = step.TargetCells.Description()
	case GROUP_ROW:
		groupName = "row"
		otherGroupName = "column"
		groupNum = step.TargetCells.Row()
		otherGroupNum = strconv.Itoa(cell.Col)
	case GROUP_COL:
		groupName = "column"
		otherGroupName = "row"
		groupNum = step.TargetCells.Col()
		otherGroupNum = strconv.Itoa(cell.Row)
	default:
		groupName = "<NONE>"
		otherGroupName = "<NONE>"
		groupNum = -1
		otherGroupNum = "<NONE>"
	}

	return fmt.Sprintf("%d is required in the %d %s, and %s is the only %s it fits", num, groupNum, groupName, otherGroupNum, otherGroupName)
}

func (self *hiddenSingleTechnique) Find(grid *Grid) []*SolveStep {
	//TODO: test that if there are multiple we find them both.
	return necessaryInCollection(grid, self, self.getter(grid))
}

func necessaryInCollection(grid *Grid, technique SolveTechnique, collectionGetter func(index int) CellList) []*SolveStep {
	//This will be a random item
	indexes := rand.Perm(DIM)

	var results []*SolveStep

	for _, i := range indexes {
		seenInCollection := make([]int, DIM)
		collection := collectionGetter(i)
		for _, cell := range collection {
			for _, possibility := range cell.Possibilities() {
				seenInCollection[possibility-1]++
			}
		}
		seenIndexes := rand.Perm(DIM)
		for _, index := range seenIndexes {
			seen := seenInCollection[index]
			if seen == 1 {
				//Okay, we know our target number. Which cell was it?
				for _, cell := range collection {
					if cell.Possible(index + 1) {
						//Found it... just make sure it's useful (it would be rare for it to not be).
						result := newFillSolveStep(cell, index+1, technique)
						if result.IsUseful(grid) {
							results = append(results, result)
							break
						}
						//Hmm, wasn't useful. Keep trying...
					}
				}
			}
		}
	}
	return results
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/itchyny/maze"
	"github.com/mattn/go-isatty"
	"github.com/nsf/termbox-go"
)

// Config is the command configuration
type Config struct {
	Width       int
	Height      int
	Start       *maze.Point
	Goal        *maze.Point
	Interactive bool
	Image       bool
	Scale       int
	Solution    bool
	Format      *maze.Format
	Seed        int64
	Output      io.Writer
}

func makeConfig(ctx *cli.Context) (*Config, []error) {
	var errs []error

	if ctx.GlobalBool("help") {
		errs = append(errs, errors.New(""))
		return nil, errs
	}

	termWidth, termHeight := termbox.Size()

	width := ctx.GlobalInt("width")
	if width <= 0 {
		width = (termWidth - 4) / 4
	}

	height := ctx.GlobalInt("height")
	if height <= 0 {
		height = (termHeight - 5) / 2
	}

	start := &maze.Point{0, 0}
	starts := strings.Split(ctx.GlobalString("start"), ",")
	if len(starts) > 0 {
		if value, err := strconv.Atoi(starts[0]); err == nil {
			if 0 <= value && value < height {
				start.X = value
			}
		}
	}
	if len(starts) > 1 {
		if value, err := strconv.Atoi(starts[1]); err == nil {
			if 0 <= value && value < width {
				start.Y = value
			}
		}
	}

	goal := &maze.Point{height - 1, width - 1}
	goals := strings.Split(ctx.GlobalString("goal"), ",")
	if len(goals) > 0 {
		if value, err := strconv.Atoi(goals[0]); err == nil {
			if 0 <= value && value < height {
				goal.X = value
			}
		}
	}
	if len(goals) > 1 {
		if value, err := strconv.Atoi(goals[1]); err == nil {
			if 0 <= value && value < width {
				goal.Y = value
			}
		}
	}

	interactive := ctx.GlobalBool("interactive")

	image := ctx.GlobalBool("image")

	scale := ctx.GlobalInt("scale")
	if image && scale == 0 {
		scale = 1
	}

	solution := ctx.GlobalBool("solution")

	format := maze.Default
	if ctx.GlobalString("format") == "color" {
		format = maze.Color
	}

	seed := int64(ctx.GlobalInt("seed"))
	if !ctx.IsSet("seed") {
		seed = time.Now().UnixNano()
	}

	output := ctx.App.Writer
	outfile := ctx.GlobalString("output")
	if outfile != "" {
		file, err := os.Create(outfile)
		if err != nil {
			errs = append(errs, errors.New("Cannot create the output file: "+outfile+"\n\n"))
		} else {
			output = file
		}
	}

	if image {
		if file, ok := output.(*os.File); ok && isatty.IsTerminal(file.Fd()) {
			errs = append(errs, errors.New("Cannot write binary data into the terminal. Use -output flag\n\n"))
		}
	}

	if len(errs) > 0 {
		return nil, errs
	}

	return &Config{
		Width:       width,
		Height:      height,
		Start:       start,
		Goal:        goal,
		Interactive: interactive,
		Image:       image,
		Scale:       scale,
		Solution:    solution,
		Format:      format,
		Seed:        seed,
		Output:      output,
	}, nil
}
<commit_msg>sort the codes of config in the order of flags<commit_after>package main

import (
	"errors"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/codegangsta/cli"
	"github.com/itchyny/maze"
	"github.com/mattn/go-isatty"
	"github.com/nsf/termbox-go"
)

// Config is the command configuration
type Config struct {
	Width       int
	Height      int
	Start       *maze.Point
	Goal        *maze.Point
	Interactive bool
	Image       bool
	Scale       int
	Solution    bool
	Format      *maze.Format
	Seed        int64
	Output      io.Writer
}

func makeConfig(ctx *cli.Context) (*Config, []error) {
	var errs []error

	if ctx.GlobalBool("help") {
		errs = append(errs, errors.New(""))
		return nil, errs
	}

	termWidth, termHeight := termbox.Size()

	width := ctx.GlobalInt("width")
	if width <= 0 {
		width = (termWidth - 4) / 4
	}

	height := ctx.GlobalInt("height")
	if height <= 0 {
		height = (termHeight - 5) / 2
	}

	start := &maze.Point{0, 0}
	starts := strings.Split(ctx.GlobalString("start"), ",")
	if len(starts) > 0 {
		if value, err := strconv.Atoi(starts[0]); err == nil {
			if 0 <= value && value < height {
				start.X = value
			}
		}
	}
	if len(starts) > 1 {
		if value, err := strconv.Atoi(starts[1]); err == nil {
			if 0 <= value && value < width {
				start.Y = value
			}
		}
	}

	goal := &maze.Point{height - 1, width - 1}
	goals := strings.Split(ctx.GlobalString("goal"), ",")
	if len(goals) > 0 {
		if value, err := strconv.Atoi(goals[0]); err == nil {
			if 0 <= value && value < height {
				goal.X = value
			}
		}
	}
	if len(goals) > 1 {
		if value, err := strconv.Atoi(goals[1]); err == nil {
			if 0 <= value && value < width {
				goal.Y = value
			}
		}
	}

	interactive := ctx.GlobalBool("interactive")

	solution := ctx.GlobalBool("solution")

	format := maze.Default
	if ctx.GlobalString("format") == "color" {
		format = maze.Color
	}

	output := ctx.App.Writer
	outfile := ctx.GlobalString("output")
	if outfile != "" {
		file, err := os.Create(outfile)
		if err != nil {
			errs = append(errs, errors.New("Cannot create the output file: "+outfile+"\n\n"))
		} else {
			output = file
		}
	}

	image := ctx.GlobalBool("image")
	if image {
		if file, ok := output.(*os.File); ok && isatty.IsTerminal(file.Fd()) {
			errs = append(errs, errors.New("Cannot write binary data into the terminal. Use -output flag\n\n"))
		}
	}

	scale := ctx.GlobalInt("scale")
	if image && scale == 0 {
		scale = 1
	}

	seed := int64(ctx.GlobalInt("seed"))
	if !ctx.IsSet("seed") {
		seed = time.Now().UnixNano()
	}

	if len(errs) > 0 {
		return nil, errs
	}

	return &Config{
		Width:       width,
		Height:      height,
		Start:       start,
		Goal:        goal,
		Interactive: interactive,
		Image:       image,
		Scale:       scale,
		Solution:    solution,
		Format:      format,
		Seed:        seed,
		Output:      output,
	}, nil
}
<|endoftext|>


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

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
)

// you will notice that I suck at parsers. That said, here is the method to my madness.
// The language of ip is not super consistent and has lots of convenience shortcuts.
// The BNF it shows you doesn't show them.
// The inputs is just the set of args.
// It's very short.
// Each token is just a string and we need not produce terminals with them -- they can
// just be the terminals and we can switch on them.
// The cursor is always our current token pointer. We do a dumb recursive descent parser
// and accumulate information into a global set of variables. At any point we can see into the
// whole set of args and see where we are. We can indicate at each point what we're expecting so
// that in usage() or recover() we can tell the user exactly what we wanted, unlike IP,
// which just barfs a whole (incorrect) BNF at you when you do anything wrong.
// To handle errors in too few arguments, we just do a recover block. That lets us blindly
// reference the arg[] array without having to check the length everywhere.

var (
	// Cursor is out next token pointer.
	// The language of this command doesn't require much more.
	cursor    int
	arg       []string
	whatIWant = "addr|route|link"
	l         = log.New(os.Stdout, "ip: ", 0)
)

// the pattern:
// at each level parse off arg[0]. If it matches, continue. If it does not, all error with how far you got, what arg you saw,
// and why it did not work out.

func usage() {
	log.Fatalf("This was fine: '%v', and this was left, '%v', and this was not understood, '%v'; only options are '%v'",
		arg[0:cursor], arg[cursor:], arg[cursor], whatIWant)
}

func adddelip(op, ip, dev string) error {
	addr, network, err := net.ParseCIDR(ip)
	if err != nil {
		l.Fatalf("%v is not in CIDR format: %v", ip, err)
	}
	iface, err := net.InterfaceByName(dev)
	if err != nil {
		l.Fatalf("%v not found: %v", dev, err)
		return err
	}

	switch op {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", ip, dev, err)
		}
	default:
		l.Fatalf("%v is not supported yet", op)
	}
	return nil

}

// in the ip command, turns out 'dev' is a noise word.
// The BNF is not right there either.
// Always make it optional.
func dev() *net.Interface {
	cursor++
	whatIWant = "dev|device name"
	if arg[cursor] == "dev" {
		cursor++
	}
	whatIWant = "device name"
	iface, err := net.InterfaceByName(arg[cursor])
	if err != nil {
		usage()
	}
	return iface
}

func showips() {
	ifaces, err := net.Interfaces()
	if err != nil {
		l.Fatalf("Can't enumerate interfaces? %v", err)
	}
	for _, v := range ifaces {
		addrs, err := v.Addrs()
		if err != nil {
			l.Printf("Can't enumerate addresses")
		}
		l.Printf("%v: %v", v, addrs)
	}
}
func addrip() {
	var err error
	var addr net.IP
	var network *net.IPNet
	if len(arg) == 1 {
		showips()
		return
	}
	cursor++
	whatIWant = "add|del"
	cmd := arg[cursor]

	switch cmd {
	case "add", "del":
		cursor++
		whatIWant = "CIDR format address"
		addr, network, err = net.ParseCIDR(arg[cursor])
		if err != nil {
			usage()
		}
	default:
		usage()
	}
	iface := dev()
	switch cmd {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", arg[1], arg[2], err)
		}
	case "del":
		if err := NetworkLinkDelIp(iface, addr, network); err != nil {
			l.Fatalf("Deleting %v from %v failed: %v", arg[1], arg[2], err)
		}
	default:
		l.Fatalf("devip: arg[0] changed: can't happen")
	}
	return

}

func linkshow() {
	cursor++
	whatIWant = "<nothing>|<device name>"
	if len(arg[cursor:]) == 0 {
		showips()
	}
}

func linkset() {
	iface := dev()
	cursor++
	whatIWant = "up|down"
	switch arg[cursor] {
	case "up":
		if err := NetworkLinkUp(iface); err != nil {
			l.Fatalf("%v can't make it up: %v", dev, err)
		}
	case "down":
		if err := NetworkLinkDown(iface); err != nil {
			l.Fatalf("%v can't make it down: %v", dev, err)
		}
	default:
		usage()
	}
}

func link() {
	cursor++
	whatIWant = "show|set"
	cmd := arg[cursor]

	switch cmd {
	case "show":
		linkshow()
	case "set":
		linkset()
	default:
		usage()
	}
	return
}

func routeshow() {
	if b, err := ioutil.ReadFile("/proc/net/route"); err == nil {
		l.Printf("%s", string(b))
	} else {
		l.Fatalf("Route show failed: %v", err)
	}
}
func nodespec() string {
	cursor++
	whatIWant = "default|CIDR"
	return arg[cursor]
}

func nexthop() (string, string) {
	cursor++
	whatIWant = "via"
	if arg[cursor] != "via" {
		usage()
	}
	nh := arg[cursor]
	cursor++
	whatIWant = "Gateway CIDR"
	return nh, arg[cursor]
}

func routeadddefault() {
	nh, nhval := nexthop()
	// TODO: NHFLAGS.
	d := dev()
	switch nh {
	case "via":
		l.Printf("Add default route %v via %v", nhval, d)
		AddDefaultGw(nhval, d.Name)
	default:
		usage()
	}
}

func routeadd() {
	ns := nodespec()
	switch ns {
	case "default":
		routeadddefault()
	default:
		usage()
	}
}

func route() {
	cursor++
	if len(arg[cursor:]) == 0 {
		routeshow()
		return
	}

	whatIWant = "show|add"
	switch arg[cursor] {
	case "show":
		routeshow()
	case "add":
		routeadd()
	default:
		usage()
	}

}
func main() {
	flag.Parse()
	arg = flag.Args()
	defer func() {
		switch err := recover().(type) {
		case nil:
		case error:
			if strings.Contains(err.Error(), "index out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else if strings.Contains(err.Error(), "slice bounds out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else {
				l.Fatalf("FUCK: %v", err.Error())
			}
			l.Fatalf("Bummer: %v", err)
		default:
			l.Fatalf("unexpected panic value: %T(%v)", err, err)
		}
	}()
	// The ip command doesn't actually follow the BNF it prints on error.
	// There are lots of handy shortcuts that people will expect.
	switch arg[cursor] {
	case "addr":
		addrip()
	case "link":
		link()
	case "route":
		route()
	default:
		usage()
	}
}
<commit_msg>Add a little comment on why it is written this way.<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
)

// you will notice that I suck at parsers. That said, here is the method to my madness.
// The language of ip is not super consistent and has lots of convenience shortcuts.
// The BNF it shows you doesn't show them.
// The inputs is just the set of args.
// It's very short.
// Each token is just a string and we need not produce terminals with them -- they can
// just be the terminals and we can switch on them.
// The cursor is always our current token pointer. We do a dumb recursive descent parser
// and accumulate information into a global set of variables. At any point we can see into the
// whole set of args and see where we are. We can indicate at each point what we're expecting so
// that in usage() or recover() we can tell the user exactly what we wanted, unlike IP,
// which just barfs a whole (incorrect) BNF at you when you do anything wrong.
// To handle errors in too few arguments, we just do a recover block. That lets us blindly
// reference the arg[] array without having to check the length everywhere.

// Note the plethora of globals. The reason is simple: we parse one command, do it, and quit.
// It doesn't make sense to write this otherwise.
var (
	// Cursor is out next token pointer.
	// The language of this command doesn't require much more.
	cursor    int
	arg       []string
	whatIWant = "addr|route|link"
	l         = log.New(os.Stdout, "ip: ", 0)
)

// the pattern:
// at each level parse off arg[0]. If it matches, continue. If it does not, all error with how far you got, what arg you saw,
// and why it did not work out.

func usage() {
	log.Fatalf("This was fine: '%v', and this was left, '%v', and this was not understood, '%v'; only options are '%v'",
		arg[0:cursor], arg[cursor:], arg[cursor], whatIWant)
}

func adddelip(op, ip, dev string) error {
	addr, network, err := net.ParseCIDR(ip)
	if err != nil {
		l.Fatalf("%v is not in CIDR format: %v", ip, err)
	}
	iface, err := net.InterfaceByName(dev)
	if err != nil {
		l.Fatalf("%v not found: %v", dev, err)
		return err
	}

	switch op {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", ip, dev, err)
		}
	default:
		l.Fatalf("%v is not supported yet", op)
	}
	return nil

}

// in the ip command, turns out 'dev' is a noise word.
// The BNF is not right there either.
// Always make it optional.
func dev() *net.Interface {
	cursor++
	whatIWant = "dev|device name"
	if arg[cursor] == "dev" {
		cursor++
	}
	whatIWant = "device name"
	iface, err := net.InterfaceByName(arg[cursor])
	if err != nil {
		usage()
	}
	return iface
}

func showips() {
	ifaces, err := net.Interfaces()
	if err != nil {
		l.Fatalf("Can't enumerate interfaces? %v", err)
	}
	for _, v := range ifaces {
		addrs, err := v.Addrs()
		if err != nil {
			l.Printf("Can't enumerate addresses")
		}
		l.Printf("%v: %v", v, addrs)
	}
}
func addrip() {
	var err error
	var addr net.IP
	var network *net.IPNet
	if len(arg) == 1 {
		showips()
		return
	}
	cursor++
	whatIWant = "add|del"
	cmd := arg[cursor]

	switch cmd {
	case "add", "del":
		cursor++
		whatIWant = "CIDR format address"
		addr, network, err = net.ParseCIDR(arg[cursor])
		if err != nil {
			usage()
		}
	default:
		usage()
	}
	iface := dev()
	switch cmd {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", arg[1], arg[2], err)
		}
	case "del":
		if err := NetworkLinkDelIp(iface, addr, network); err != nil {
			l.Fatalf("Deleting %v from %v failed: %v", arg[1], arg[2], err)
		}
	default:
		l.Fatalf("devip: arg[0] changed: can't happen")
	}
	return

}

func linkshow() {
	cursor++
	whatIWant = "<nothing>|<device name>"
	if len(arg[cursor:]) == 0 {
		showips()
	}
}

func linkset() {
	iface := dev()
	cursor++
	whatIWant = "up|down"
	switch arg[cursor] {
	case "up":
		if err := NetworkLinkUp(iface); err != nil {
			l.Fatalf("%v can't make it up: %v", dev, err)
		}
	case "down":
		if err := NetworkLinkDown(iface); err != nil {
			l.Fatalf("%v can't make it down: %v", dev, err)
		}
	default:
		usage()
	}
}

func link() {
	cursor++
	whatIWant = "show|set"
	cmd := arg[cursor]

	switch cmd {
	case "show":
		linkshow()
	case "set":
		linkset()
	default:
		usage()
	}
	return
}

func routeshow() {
	if b, err := ioutil.ReadFile("/proc/net/route"); err == nil {
		l.Printf("%s", string(b))
	} else {
		l.Fatalf("Route show failed: %v", err)
	}
}
func nodespec() string {
	cursor++
	whatIWant = "default|CIDR"
	return arg[cursor]
}

func nexthop() (string, string) {
	cursor++
	whatIWant = "via"
	if arg[cursor] != "via" {
		usage()
	}
	nh := arg[cursor]
	cursor++
	whatIWant = "Gateway CIDR"
	return nh, arg[cursor]
}

func routeadddefault() {
	nh, nhval := nexthop()
	// TODO: NHFLAGS.
	d := dev()
	switch nh {
	case "via":
		l.Printf("Add default route %v via %v", nhval, d)
		AddDefaultGw(nhval, d.Name)
	default:
		usage()
	}
}

func routeadd() {
	ns := nodespec()
	switch ns {
	case "default":
		routeadddefault()
	default:
		usage()
	}
}

func route() {
	cursor++
	if len(arg[cursor:]) == 0 {
		routeshow()
		return
	}

	whatIWant = "show|add"
	switch arg[cursor] {
	case "show":
		routeshow()
	case "add":
		routeadd()
	default:
		usage()
	}

}
func main() {
	flag.Parse()
	arg = flag.Args()
	defer func() {
		switch err := recover().(type) {
		case nil:
		case error:
			if strings.Contains(err.Error(), "index out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else if strings.Contains(err.Error(), "slice bounds out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else {
				l.Fatalf("FUCK: %v", err.Error())
			}
			l.Fatalf("Bummer: %v", err)
		default:
			l.Fatalf("unexpected panic value: %T(%v)", err, err)
		}
	}()
	// The ip command doesn't actually follow the BNF it prints on error.
	// There are lots of handy shortcuts that people will expect.
	switch arg[cursor] {
	case "addr":
		addrip()
	case "link":
		link()
	case "route":
		route()
	default:
		usage()
	}
}
<|endoftext|>


<commit_before>package manifest

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strings"

	"github.com/docker-library/go-dockerlibrary/pkg/stripper"

	"pault.ag/go/debian/control"
)

var (
	GitCommitRegex = regexp.MustCompile(`^[0-9a-f]{1,40}$`)
	GitFetchRegex  = regexp.MustCompile(`^refs/(heads|tags)/[^*?:]+$`)
)

type Manifest2822 struct {
	Global  Manifest2822Entry
	Entries []Manifest2822Entry
}

type Manifest2822Entry struct {
	control.Paragraph

	Maintainers []string `delim:"," strip:"\n\r\t "`

	Tags []string `delim:"," strip:"\n\r\t "`

	GitRepo   string
	GitFetch  string
	GitCommit string
	Directory string

	Constraints []string `delim:"," strip:"\n\r\t "`
}

var DefaultManifestEntry = Manifest2822Entry{
	GitFetch:  "refs/heads/master",
	Directory: ".",
}

func (entry Manifest2822Entry) Clone() Manifest2822Entry {
	// SLICES! grr
	entry.Maintainers = append([]string{}, entry.Maintainers...)
	entry.Tags = append([]string{}, entry.Tags...)
	entry.Constraints = append([]string{}, entry.Constraints...)
	return entry
}

const StringSeparator2822 = ", "

func (entry Manifest2822Entry) MaintainersString() string {
	return strings.Join(entry.Maintainers, StringSeparator2822)
}

func (entry Manifest2822Entry) TagsString() string {
	return strings.Join(entry.Tags, StringSeparator2822)
}

func (entry Manifest2822Entry) ConstraintsString() string {
	return strings.Join(entry.Constraints, StringSeparator2822)
}

// if this method returns "true", then a.Tags and b.Tags can safely be combined (for the purposes of building)
func (a Manifest2822Entry) SameBuildArtifacts(b Manifest2822Entry) bool {
	return a.GitRepo == b.GitRepo && a.GitFetch == b.GitFetch && a.GitCommit == b.GitCommit && a.Directory == b.Directory && a.ConstraintsString() == b.ConstraintsString()
}

// returns a new Entry with any of the values that are equal to the values in "defaults" cleared
func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifest2822Entry {
	if entry.MaintainersString() == defaults.MaintainersString() {
		entry.Maintainers = nil
	}
	if entry.TagsString() == defaults.TagsString() {
		entry.Tags = nil
	}
	if entry.GitRepo == defaults.GitRepo {
		entry.GitRepo = ""
	}
	if entry.GitFetch == defaults.GitFetch {
		entry.GitFetch = ""
	}
	if entry.GitCommit == defaults.GitCommit {
		entry.GitCommit = ""
	}
	if entry.Directory == defaults.Directory {
		entry.Directory = ""
	}
	if entry.ConstraintsString() == defaults.ConstraintsString() {
		entry.Constraints = nil
	}
	return entry
}

func (entry Manifest2822Entry) String() string {
	ret := []string{}
	if str := entry.MaintainersString(); str != "" {
		ret = append(ret, "Maintainers: "+str)
	}
	if str := entry.TagsString(); str != "" {
		ret = append(ret, "Tags: "+str)
	}
	if str := entry.GitRepo; str != "" {
		ret = append(ret, "GitRepo: "+str)
	}
	if str := entry.GitFetch; str != "" {
		ret = append(ret, "GitFetch: "+str)
	}
	if str := entry.GitCommit; str != "" {
		ret = append(ret, "GitCommit: "+str)
	}
	if str := entry.Directory; str != "" {
		ret = append(ret, "Directory: "+str)
	}
	if str := entry.ConstraintsString(); str != "" {
		ret = append(ret, "Constraints: "+str)
	}
	return strings.Join(ret, "\n")
}

func (manifest Manifest2822) String() string {
	entries := []Manifest2822Entry{manifest.Global.ClearDefaults(DefaultManifestEntry)}
	entries = append(entries, manifest.Entries...)

	ret := []string{}
	for i, entry := range entries {
		if i > 0 {
			entry = entry.ClearDefaults(manifest.Global)
		}
		ret = append(ret, entry.String())
	}

	return strings.Join(ret, "\n\n")
}

func (entry Manifest2822Entry) HasTag(tag string) bool {
	for _, existingTag := range entry.Tags {
		if tag == existingTag {
			return true
		}
	}
	return false
}

func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
	for _, entry := range manifest.Entries {
		if entry.HasTag(tag) {
			return &entry
		}
	}
	return nil
}

func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error {
	if len(entry.Tags) < 1 {
		return fmt.Errorf("missing Tags")
	}
	if entry.GitRepo == "" || entry.GitFetch == "" || entry.GitCommit == "" {
		return fmt.Errorf("Tags %q missing one of GitRepo, GitFetch, or GitCommit", entry.TagsString())
	}
	if !GitFetchRegex.MatchString(entry.GitFetch) {
		return fmt.Errorf(`Tags %q has invalid GitFetch (must be "refs/heads/..." or "refs/tags/..."): %q`, entry.TagsString(), entry.GitFetch)
	}
	if !GitCommitRegex.MatchString(entry.GitCommit) {
		return fmt.Errorf(`Tags %q has invalid GitCommit (must be a commit, not a tag or ref): %q`, entry.TagsString(), entry.GitCommit)
	}
	if invalidMaintainers := entry.InvalidMaintainers(); len(invalidMaintainers) > 0 {
		return fmt.Errorf("Tags %q has invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat)
	}

	for _, tag := range entry.Tags {
		if otherEntry := manifest.GetTag(tag); otherEntry != nil {
			return fmt.Errorf("Tags %q includes duplicate tag %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString())
		}
	}

	for i, existingEntry := range manifest.Entries {
		if existingEntry.SameBuildArtifacts(entry) {
			manifest.Entries[i].Tags = append(existingEntry.Tags, entry.Tags...)
			return nil
		}
	}

	manifest.Entries = append(manifest.Entries, entry)

	return nil
}

const (
	MaintainersNameRegex   = `[^\s<>()][^<>()]*`
	MaintainersEmailRegex  = `[^\s<>()]+`
	MaintainersGitHubRegex = `[^\s<>()]+`

	MaintainersFormat = `Full Name <contact-email-or-url> (@github-handle) OR Full Name (@github-handle)`
)

var (
	MaintainersRegex = regexp.MustCompile(`^(` + MaintainersNameRegex + `)(?:\s+<(` + MaintainersEmailRegex + `)>)?\s+[(]@(` + MaintainersGitHubRegex + `)[)]$`)
)

func (entry Manifest2822Entry) InvalidMaintainers() []string {
	invalid := []string{}
	for _, maintainer := range entry.Maintainers {
		if !MaintainersRegex.MatchString(maintainer) {
			invalid = append(invalid, maintainer)
		}
	}
	return invalid
}

type decoderWrapper struct {
	*control.Decoder
}

func (decoder *decoderWrapper) Decode(entry *Manifest2822Entry) error {
	for {
		err := decoder.Decoder.Decode(entry)
		if err != nil {
			return err
		}
		// ignore empty paragraphs (blank lines at the start, excess blank lines between paragraphs, excess blank lines at EOF)
		if len(entry.Paragraph.Order) > 0 {
			return nil
		}
	}
}

func Parse2822(readerIn io.Reader) (*Manifest2822, error) {
	reader := stripper.NewCommentStripper(readerIn)

	realDecoder, err := control.NewDecoder(bufio.NewReader(reader), nil)
	if err != nil {
		return nil, err
	}
	decoder := decoderWrapper{realDecoder}

	manifest := Manifest2822{
		Global: DefaultManifestEntry.Clone(),
	}

	if err := decoder.Decode(&manifest.Global); err != nil {
		return nil, err
	}
	if len(manifest.Global.Maintainers) < 1 {
		return nil, fmt.Errorf("missing Maintainers")
	}
	if invalidMaintainers := manifest.Global.InvalidMaintainers(); len(invalidMaintainers) > 0 {
		return nil, fmt.Errorf("invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat)
	}
	if len(manifest.Global.Tags) > 0 {
		return nil, fmt.Errorf("global Tags not permitted")
	}

	for {
		entry := manifest.Global.Clone()

		err := decoder.Decode(&entry)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		err = manifest.AddEntry(entry)
		if err != nil {
			return nil, err
		}
	}

	return &manifest, nil
}
<commit_msg>Fix error message formatting to be consistent<commit_after>package manifest

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strings"

	"github.com/docker-library/go-dockerlibrary/pkg/stripper"

	"pault.ag/go/debian/control"
)

var (
	GitCommitRegex = regexp.MustCompile(`^[0-9a-f]{1,40}$`)
	GitFetchRegex  = regexp.MustCompile(`^refs/(heads|tags)/[^*?:]+$`)
)

type Manifest2822 struct {
	Global  Manifest2822Entry
	Entries []Manifest2822Entry
}

type Manifest2822Entry struct {
	control.Paragraph

	Maintainers []string `delim:"," strip:"\n\r\t "`

	Tags []string `delim:"," strip:"\n\r\t "`

	GitRepo   string
	GitFetch  string
	GitCommit string
	Directory string

	Constraints []string `delim:"," strip:"\n\r\t "`
}

var DefaultManifestEntry = Manifest2822Entry{
	GitFetch:  "refs/heads/master",
	Directory: ".",
}

func (entry Manifest2822Entry) Clone() Manifest2822Entry {
	// SLICES! grr
	entry.Maintainers = append([]string{}, entry.Maintainers...)
	entry.Tags = append([]string{}, entry.Tags...)
	entry.Constraints = append([]string{}, entry.Constraints...)
	return entry
}

const StringSeparator2822 = ", "

func (entry Manifest2822Entry) MaintainersString() string {
	return strings.Join(entry.Maintainers, StringSeparator2822)
}

func (entry Manifest2822Entry) TagsString() string {
	return strings.Join(entry.Tags, StringSeparator2822)
}

func (entry Manifest2822Entry) ConstraintsString() string {
	return strings.Join(entry.Constraints, StringSeparator2822)
}

// if this method returns "true", then a.Tags and b.Tags can safely be combined (for the purposes of building)
func (a Manifest2822Entry) SameBuildArtifacts(b Manifest2822Entry) bool {
	return a.GitRepo == b.GitRepo && a.GitFetch == b.GitFetch && a.GitCommit == b.GitCommit && a.Directory == b.Directory && a.ConstraintsString() == b.ConstraintsString()
}

// returns a new Entry with any of the values that are equal to the values in "defaults" cleared
func (entry Manifest2822Entry) ClearDefaults(defaults Manifest2822Entry) Manifest2822Entry {
	if entry.MaintainersString() == defaults.MaintainersString() {
		entry.Maintainers = nil
	}
	if entry.TagsString() == defaults.TagsString() {
		entry.Tags = nil
	}
	if entry.GitRepo == defaults.GitRepo {
		entry.GitRepo = ""
	}
	if entry.GitFetch == defaults.GitFetch {
		entry.GitFetch = ""
	}
	if entry.GitCommit == defaults.GitCommit {
		entry.GitCommit = ""
	}
	if entry.Directory == defaults.Directory {
		entry.Directory = ""
	}
	if entry.ConstraintsString() == defaults.ConstraintsString() {
		entry.Constraints = nil
	}
	return entry
}

func (entry Manifest2822Entry) String() string {
	ret := []string{}
	if str := entry.MaintainersString(); str != "" {
		ret = append(ret, "Maintainers: "+str)
	}
	if str := entry.TagsString(); str != "" {
		ret = append(ret, "Tags: "+str)
	}
	if str := entry.GitRepo; str != "" {
		ret = append(ret, "GitRepo: "+str)
	}
	if str := entry.GitFetch; str != "" {
		ret = append(ret, "GitFetch: "+str)
	}
	if str := entry.GitCommit; str != "" {
		ret = append(ret, "GitCommit: "+str)
	}
	if str := entry.Directory; str != "" {
		ret = append(ret, "Directory: "+str)
	}
	if str := entry.ConstraintsString(); str != "" {
		ret = append(ret, "Constraints: "+str)
	}
	return strings.Join(ret, "\n")
}

func (manifest Manifest2822) String() string {
	entries := []Manifest2822Entry{manifest.Global.ClearDefaults(DefaultManifestEntry)}
	entries = append(entries, manifest.Entries...)

	ret := []string{}
	for i, entry := range entries {
		if i > 0 {
			entry = entry.ClearDefaults(manifest.Global)
		}
		ret = append(ret, entry.String())
	}

	return strings.Join(ret, "\n\n")
}

func (entry Manifest2822Entry) HasTag(tag string) bool {
	for _, existingTag := range entry.Tags {
		if tag == existingTag {
			return true
		}
	}
	return false
}

func (manifest Manifest2822) GetTag(tag string) *Manifest2822Entry {
	for _, entry := range manifest.Entries {
		if entry.HasTag(tag) {
			return &entry
		}
	}
	return nil
}

func (manifest *Manifest2822) AddEntry(entry Manifest2822Entry) error {
	if len(entry.Tags) < 1 {
		return fmt.Errorf("missing Tags")
	}
	if entry.GitRepo == "" || entry.GitFetch == "" || entry.GitCommit == "" {
		return fmt.Errorf("Tags %q missing one of GitRepo, GitFetch, or GitCommit", entry.TagsString())
	}
	if !GitFetchRegex.MatchString(entry.GitFetch) {
		return fmt.Errorf(`Tags %q has invalid GitFetch (must be "refs/heads/..." or "refs/tags/..."): %q`, entry.TagsString(), entry.GitFetch)
	}
	if !GitCommitRegex.MatchString(entry.GitCommit) {
		return fmt.Errorf(`Tags %q has invalid GitCommit (must be a commit, not a tag or ref): %q`, entry.TagsString(), entry.GitCommit)
	}
	if invalidMaintainers := entry.InvalidMaintainers(); len(invalidMaintainers) > 0 {
		return fmt.Errorf("Tags %q has invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat)
	}

	for _, tag := range entry.Tags {
		if otherEntry := manifest.GetTag(tag); otherEntry != nil {
			return fmt.Errorf("Tags %q includes duplicate tag: %q (duplicated in %q)", entry.TagsString(), tag, otherEntry.TagsString())
		}
	}

	for i, existingEntry := range manifest.Entries {
		if existingEntry.SameBuildArtifacts(entry) {
			manifest.Entries[i].Tags = append(existingEntry.Tags, entry.Tags...)
			return nil
		}
	}

	manifest.Entries = append(manifest.Entries, entry)

	return nil
}

const (
	MaintainersNameRegex   = `[^\s<>()][^<>()]*`
	MaintainersEmailRegex  = `[^\s<>()]+`
	MaintainersGitHubRegex = `[^\s<>()]+`

	MaintainersFormat = `Full Name <contact-email-or-url> (@github-handle) OR Full Name (@github-handle)`
)

var (
	MaintainersRegex = regexp.MustCompile(`^(` + MaintainersNameRegex + `)(?:\s+<(` + MaintainersEmailRegex + `)>)?\s+[(]@(` + MaintainersGitHubRegex + `)[)]$`)
)

func (entry Manifest2822Entry) InvalidMaintainers() []string {
	invalid := []string{}
	for _, maintainer := range entry.Maintainers {
		if !MaintainersRegex.MatchString(maintainer) {
			invalid = append(invalid, maintainer)
		}
	}
	return invalid
}

type decoderWrapper struct {
	*control.Decoder
}

func (decoder *decoderWrapper) Decode(entry *Manifest2822Entry) error {
	for {
		err := decoder.Decoder.Decode(entry)
		if err != nil {
			return err
		}
		// ignore empty paragraphs (blank lines at the start, excess blank lines between paragraphs, excess blank lines at EOF)
		if len(entry.Paragraph.Order) > 0 {
			return nil
		}
	}
}

func Parse2822(readerIn io.Reader) (*Manifest2822, error) {
	reader := stripper.NewCommentStripper(readerIn)

	realDecoder, err := control.NewDecoder(bufio.NewReader(reader), nil)
	if err != nil {
		return nil, err
	}
	decoder := decoderWrapper{realDecoder}

	manifest := Manifest2822{
		Global: DefaultManifestEntry.Clone(),
	}

	if err := decoder.Decode(&manifest.Global); err != nil {
		return nil, err
	}
	if len(manifest.Global.Maintainers) < 1 {
		return nil, fmt.Errorf("missing Maintainers")
	}
	if invalidMaintainers := manifest.Global.InvalidMaintainers(); len(invalidMaintainers) > 0 {
		return nil, fmt.Errorf("invalid Maintainers: %q (expected format %q)", strings.Join(invalidMaintainers, ", "), MaintainersFormat)
	}
	if len(manifest.Global.Tags) > 0 {
		return nil, fmt.Errorf("global Tags not permitted")
	}

	for {
		entry := manifest.Global.Clone()

		err := decoder.Decode(&entry)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		err = manifest.AddEntry(entry)
		if err != nil {
			return nil, err
		}
	}

	return &manifest, nil
}
<|endoftext|>