<commit_before>package air

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"path"
	"reflect"
	"runtime"
	"sync"
)

type (
	// Air is the top-level framework struct.
	Air struct {
		pool     *pool
		pregases []GasFunc
		gases    []GasFunc
		router   *router
		binder   *binder
		renderer *renderer

		Config           *Config
		HTTPErrorHandler HTTPErrorHandler
		Logger           *Logger
	}

	// pool represents the pools of `Air`.
	pool struct {
		context        *sync.Pool
		request        *sync.Pool
		response       *sync.Pool
		requestHeader  *sync.Pool
		responseHeader *sync.Pool
		uri            *sync.Pool
	}

	// HandlerFunc defines a function to server HTTP requests.
	HandlerFunc func(*Context) error

	// GasFunc defines a function to process gas.
	GasFunc func(HandlerFunc) HandlerFunc

	// HTTPError represents an error that occurred while handling a request.
	HTTPError struct {
		Code    int
		Message string
	}

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, *Context)
)

// HTTP methods (which follows the REST principle)
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT" // The Air advises you to forget the PATCH.
	DELETE = "DELETE"
)

// For easy for-range
var methods = [4]string{GET, POST, PUT, DELETE}

// MIME types
const (
	MIMEApplicationJSON       = "application/json; charset=utf-8"
	MIMEApplicationJavaScript = "application/javascript; charset=utf-8"
	MIMEApplicationXML        = "application/xml; charset=utf-8"
	MIMEApplicationForm       = "application/x-www-form-urlencoded"
	MIMEApplicationProtobuf   = "application/protobuf"
	MIMEApplicationMsgpack    = "application/msgpack"
	MIMETextHTML              = "text/html; charset=utf-8"
	MIMETextPlain             = "text/plain; charset=utf-8"
	MIMEMultipartForm         = "multipart/form-data"
	MIMEOctetStream           = "application/octet-stream"
)

// Headers
const (
	HeaderAcceptEncoding                = "Accept-Encoding"
	HeaderAllow                         = "Allow"
	HeaderAuthorization                 = "Authorization"
	HeaderContentDisposition            = "Content-Disposition"
	HeaderContentEncoding               = "Content-Encoding"
	HeaderContentLength                 = "Content-Length"
	HeaderContentType                   = "Content-Type"
	HeaderCookie                        = "Cookie"
	HeaderSetCookie                     = "Set-Cookie"
	HeaderIfModifiedSince               = "If-Modified-Since"
	HeaderLastModified                  = "Last-Modified"
	HeaderLocation                      = "Location"
	HeaderUpgrade                       = "Upgrade"
	HeaderVary                          = "Vary"
	HeaderWWWAuthenticate               = "WWW-Authenticate"
	HeaderXForwardedProto               = "X-Forwarded-Proto"
	HeaderXHTTPMethodOverride           = "X-HTTP-Method-Override"
	HeaderXForwardedFor                 = "X-Forwarded-For"
	HeaderXRealIP                       = "X-Real-IP"
	HeaderServer                        = "Server"
	HeaderOrigin                        = "Origin"
	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"
	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"
	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"
	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"

	HeaderStrictTransportSecurity = "Strict-Transport-Security"
	HeaderXContentTypeOptions     = "X-Content-Type-Options"
	HeaderXXSSProtection          = "X-XSS-Protection"
	HeaderXFrameOptions           = "X-Frame-Options"
	HeaderContentSecurityPolicy   = "Content-Security-Policy"
	HeaderXCSRFToken              = "X-CSRF-Token"
)

// Errors
var (
	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized)          // 401
	ErrNotFound                    = NewHTTPError(http.StatusNotFound)              // 404
	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed)      // 405
	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // 413
	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)  // 415

	ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // 500
	ErrBadGateway          = NewHTTPError(http.StatusBadGateway)          // 502
	ErrServiceUnavailable  = NewHTTPError(http.StatusServiceUnavailable)  // 503
	ErrGatewayTimeout      = NewHTTPError(http.StatusGatewayTimeout)      // 504

	ErrInvalidRedirectCode = errors.New("Invalid Redirect Status Code")
	ErrCookieNotFound      = errors.New("Cookie Not Found")
)

// HTTP error handlers
var (
	NotFoundHandler = func(c *Context) error {
		return ErrNotFound
	}

	MethodNotAllowedHandler = func(c *Context) error {
		return ErrMethodNotAllowed
	}
)

// New returns a new instance of `Air`.
func New() *Air {
	a := &Air{}
	a.pool = newPool(a)
	a.router = newRouter(a)
	a.binder = newBinder(a)
	a.renderer = newRenderer(a)
	a.Config = NewConfig("air")
	a.HTTPErrorHandler = defaultHTTPErrorHandler
	a.Logger = NewLogger(a)
	a.Logger.Level = ERROR
	return a
}

// Precontain adds gases to the chain which is run before router.
func (a *Air) Precontain(gases ...GasFunc) {
	a.pregases = append(a.pregases, gases...)
}

// Contain adds gases to the chain which is run after router.
func (a *Air) Contain(gases ...GasFunc) {
	a.gases = append(a.gases, gases...)
}

// Get registers a new GET route for a path with matching handler in the router
// with optional route-level gases.
func (a *Air) Get(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(GET, path, handler, gases...)
}

// Post registers a new POST route for a path with matching handler in the
// router with optional route-level gases.
func (a *Air) Post(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(POST, path, handler, gases...)
}

// Put registers a new PUT route for a path with matching handler in the
// router with optional route-level gases.
func (a *Air) Put(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(PUT, path, handler, gases...)
}

// Delete registers a new DELETE route for a path with matching handler in
// the router with optional route-level gases.
func (a *Air) Delete(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(DELETE, path, handler, gases...)
}

// Static registers a new route with path prefix to serve static files from
// the provided root directory.
func (a *Air) Static(prefix, root string) {
	a.Get(prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Params[c.ParamNames[0]]))
	})
}

// File registers a new route with path to serve a static file.
func (a *Air) File(path, file string) {
	a.Get(path, func(c *Context) error {
		return c.File(file)
	})
}

// add registers a new route for a path with a HTTP method and matching handler
// in the router with optional route-level gases.
func (a *Air) add(method, path string, handler HandlerFunc, gases ...GasFunc) {
	name := handlerName(handler)
	a.router.add(method, path, func(c *Context) error {
		h := handler
		// Chain gases
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}
		return h(c)
	}, a)
	r := route{
		method:  method,
		path:    path,
		handler: name,
	}
	a.router.routes[method+path] = r
}

// SetTemplateFunc sets the f into template func map with a name.
func (a *Air) SetTemplateFunc(name string, f interface{}) {
	a.renderer.templateFuncMap[name] = f
}

// URI returns a URI generated from handler with optional params.
func (a *Air) URI(handler HandlerFunc, params ...interface{}) string {
	uri := new(bytes.Buffer)
	ln := len(params)
	n := 0
	name := handlerName(handler)
	for _, r := range a.router.routes {
		if r.handler == name {
			for i, l := 0, len(r.path); i < l; i++ {
				if r.path[i] == ':' && n < ln {
					for ; i < l && r.path[i] != '/'; i++ {
					}
					uri.WriteString(fmt.Sprintf("%v", params[n]))
					n++
				}
				if i < l {
					uri.WriteByte(r.path[i])
				}
			}
			break
		}
	}
	return uri.String()
}

// Run starts the HTTP server.
func (a *Air) Run() {
	a.renderer.parseTemplates()
	if a.Config.DebugMode {
		a.Logger.Level = DEBUG
		a.Logger.Debug("Running In Debug Mode")
	}
	s := newServer(a)
	a.Logger.Error(s.start())
}

// handlerName returns the handler's func name.
func handlerName(handler HandlerFunc) string {
	t := reflect.ValueOf(handler).Type()
	if t.Kind() == reflect.Func {
		return runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name()
	}
	return t.String()
}

// newPool returnes a new instance of `pool`.
func newPool(a *Air) *pool {
	return &pool{
		context: &sync.Pool{
			New: func() interface{} {
				return newContext(a)
			},
		},
		request: &sync.Pool{
			New: func() interface{} {
				return newRequest(a)
			},
		},
		response: &sync.Pool{
			New: func() interface{} {
				return newResponse(a)
			},
		},
		requestHeader: &sync.Pool{
			New: func() interface{} {
				return &RequestHeader{}
			},
		},
		responseHeader: &sync.Pool{
			New: func() interface{} {
				return &ResponseHeader{}
			},
		},
		uri: &sync.Pool{
			New: func() interface{} {
				return &URI{}
			},
		},
	}
}

// NewHTTPError returns a new instance of `HTTPError`.
func NewHTTPError(code int, msg ...string) *HTTPError {
	he := &HTTPError{Code: code, Message: http.StatusText(code)}
	if len(msg) > 0 {
		he.Message = msg[0]
	}
	return he
}

// Error implements `Error#Error()`.
func (he *HTTPError) Error() string {
	return he.Message
}

// defaultHTTPErrorHandler invokes the default HTTP error handler.
func defaultHTTPErrorHandler(err error, c *Context) {
	he := ErrInternalServerError
	if che, ok := err.(*HTTPError); ok {
		he = che
	}
	if c.Air.Config.DebugMode {
		he.Message = err.Error()
	}
	if !c.Response.Committed {
		c.Data["string"] = he.Message
		c.StatusCode = he.Code
		c.String()
	}
	c.Air.Logger.Error(err)
}
<commit_msg>style: beautify air.go<commit_after>package air

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"path"
	"reflect"
	"runtime"
	"sync"
)

type (
	// Air is the top-level framework struct.
	Air struct {
		pool     *pool
		pregases []GasFunc
		gases    []GasFunc
		router   *router
		binder   *binder
		renderer *renderer

		Config           *Config
		HTTPErrorHandler HTTPErrorHandler
		Logger           *Logger
	}

	// pool represents the pools of `Air`.
	pool struct {
		context        *sync.Pool
		request        *sync.Pool
		response       *sync.Pool
		requestHeader  *sync.Pool
		responseHeader *sync.Pool
		uri            *sync.Pool
	}

	// HandlerFunc defines a function to server HTTP requests.
	HandlerFunc func(*Context) error

	// GasFunc defines a function to process gas.
	GasFunc func(HandlerFunc) HandlerFunc

	// HTTPError represents an error that occurred while handling a request.
	HTTPError struct {
		Code    int
		Message string
	}

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, *Context)
)

// HTTP methods (which follows the REST principle)
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT" // The Air advises you to forget the PATCH.
	DELETE = "DELETE"
)

// For easy for-range
var methods = [4]string{GET, POST, PUT, DELETE}

// MIME types
const (
	MIMEApplicationJSON       = "application/json; charset=utf-8"
	MIMEApplicationJavaScript = "application/javascript; charset=utf-8"
	MIMEApplicationXML        = "application/xml; charset=utf-8"
	MIMEApplicationForm       = "application/x-www-form-urlencoded"
	MIMEApplicationProtobuf   = "application/protobuf"
	MIMEApplicationMsgpack    = "application/msgpack"
	MIMETextHTML              = "text/html; charset=utf-8"
	MIMETextPlain             = "text/plain; charset=utf-8"
	MIMEMultipartForm         = "multipart/form-data"
	MIMEOctetStream           = "application/octet-stream"
)

// Headers
const (
	HeaderAcceptEncoding                = "Accept-Encoding"
	HeaderAllow                         = "Allow"
	HeaderAuthorization                 = "Authorization"
	HeaderContentDisposition            = "Content-Disposition"
	HeaderContentEncoding               = "Content-Encoding"
	HeaderContentLength                 = "Content-Length"
	HeaderContentType                   = "Content-Type"
	HeaderCookie                        = "Cookie"
	HeaderSetCookie                     = "Set-Cookie"
	HeaderIfModifiedSince               = "If-Modified-Since"
	HeaderLastModified                  = "Last-Modified"
	HeaderLocation                      = "Location"
	HeaderUpgrade                       = "Upgrade"
	HeaderVary                          = "Vary"
	HeaderWWWAuthenticate               = "WWW-Authenticate"
	HeaderXForwardedProto               = "X-Forwarded-Proto"
	HeaderXHTTPMethodOverride           = "X-HTTP-Method-Override"
	HeaderXForwardedFor                 = "X-Forwarded-For"
	HeaderXRealIP                       = "X-Real-IP"
	HeaderServer                        = "Server"
	HeaderOrigin                        = "Origin"
	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"
	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"
	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"
	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"

	HeaderStrictTransportSecurity = "Strict-Transport-Security"
	HeaderXContentTypeOptions     = "X-Content-Type-Options"
	HeaderXXSSProtection          = "X-XSS-Protection"
	HeaderXFrameOptions           = "X-Frame-Options"
	HeaderContentSecurityPolicy   = "Content-Security-Policy"
	HeaderXCSRFToken              = "X-CSRF-Token"
)

// Errors
var (
	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized)          // 401
	ErrNotFound                    = NewHTTPError(http.StatusNotFound)              // 404
	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed)      // 405
	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // 413
	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)  // 415

	ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // 500
	ErrBadGateway          = NewHTTPError(http.StatusBadGateway)          // 502
	ErrServiceUnavailable  = NewHTTPError(http.StatusServiceUnavailable)  // 503
	ErrGatewayTimeout      = NewHTTPError(http.StatusGatewayTimeout)      // 504

	ErrInvalidRedirectCode = errors.New("Invalid Redirect Status Code")
	ErrCookieNotFound      = errors.New("Cookie Not Found")
)

// HTTP error handlers
var (
	NotFoundHandler = func(c *Context) error {
		return ErrNotFound
	}

	MethodNotAllowedHandler = func(c *Context) error {
		return ErrMethodNotAllowed
	}
)

// New returns a new instance of `Air`.
func New() *Air {
	a := &Air{}
	a.pool = newPool(a)
	a.router = newRouter(a)
	a.binder = newBinder(a)
	a.renderer = newRenderer(a)
	a.Config = NewConfig("air")
	a.HTTPErrorHandler = defaultHTTPErrorHandler
	a.Logger = NewLogger(a)
	a.Logger.Level = ERROR
	return a
}

// Precontain adds gases to the chain which is run before router.
func (a *Air) Precontain(gases ...GasFunc) {
	a.pregases = append(a.pregases, gases...)
}

// Contain adds gases to the chain which is run after router.
func (a *Air) Contain(gases ...GasFunc) {
	a.gases = append(a.gases, gases...)
}

// Get registers a new GET route for a path with matching handler in the router
// with optional route-level gases.
func (a *Air) Get(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(GET, path, handler, gases...)
}

// Post registers a new POST route for a path with matching handler in the
// router with optional route-level gases.
func (a *Air) Post(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(POST, path, handler, gases...)
}

// Put registers a new PUT route for a path with matching handler in the
// router with optional route-level gases.
func (a *Air) Put(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(PUT, path, handler, gases...)
}

// Delete registers a new DELETE route for a path with matching handler in
// the router with optional route-level gases.
func (a *Air) Delete(path string, handler HandlerFunc, gases ...GasFunc) {
	a.add(DELETE, path, handler, gases...)
}

// Static registers a new route with path prefix to serve static files from
// the provided root directory.
func (a *Air) Static(prefix, root string) {
	a.Get(prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Params[c.ParamNames[0]]))
	})
}

// File registers a new route with path to serve a static file.
func (a *Air) File(path, file string) {
	a.Get(path, func(c *Context) error {
		return c.File(file)
	})
}

// add registers a new route for a path with a HTTP method and matching handler
// in the router with optional route-level gases.
func (a *Air) add(method, path string, handler HandlerFunc, gases ...GasFunc) {
	name := handlerName(handler)
	a.router.add(method, path, func(c *Context) error {
		h := handler
		// Chain gases
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}
		return h(c)
	}, a)
	r := route{
		method:  method,
		path:    path,
		handler: name,
	}
	a.router.routes[method+path] = r
}

// URI returns a URI generated from handler with optional params.
func (a *Air) URI(handler HandlerFunc, params ...interface{}) string {
	uri := new(bytes.Buffer)
	ln := len(params)
	n := 0
	name := handlerName(handler)
	for _, r := range a.router.routes {
		if r.handler == name {
			for i, l := 0, len(r.path); i < l; i++ {
				if r.path[i] == ':' && n < ln {
					for ; i < l && r.path[i] != '/'; i++ {
					}
					uri.WriteString(fmt.Sprintf("%v", params[n]))
					n++
				}
				if i < l {
					uri.WriteByte(r.path[i])
				}
			}
			break
		}
	}
	return uri.String()
}

// SetTemplateFunc sets the f into template func map with a name.
func (a *Air) SetTemplateFunc(name string, f interface{}) {
	a.renderer.templateFuncMap[name] = f
}

// Run starts the HTTP server.
func (a *Air) Run() {
	a.renderer.parseTemplates()
	if a.Config.DebugMode {
		a.Logger.Level = DEBUG
		a.Logger.Debug("Running In Debug Mode")
	}
	s := newServer(a)
	a.Logger.Error(s.start())
}

// handlerName returns the handler's func name.
func handlerName(handler HandlerFunc) string {
	t := reflect.ValueOf(handler).Type()
	if t.Kind() == reflect.Func {
		return runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name()
	}
	return t.String()
}

// newPool returnes a new instance of `pool`.
func newPool(a *Air) *pool {
	return &pool{
		context: &sync.Pool{
			New: func() interface{} {
				return newContext(a)
			},
		},
		request: &sync.Pool{
			New: func() interface{} {
				return newRequest(a)
			},
		},
		response: &sync.Pool{
			New: func() interface{} {
				return newResponse(a)
			},
		},
		requestHeader: &sync.Pool{
			New: func() interface{} {
				return &RequestHeader{}
			},
		},
		responseHeader: &sync.Pool{
			New: func() interface{} {
				return &ResponseHeader{}
			},
		},
		uri: &sync.Pool{
			New: func() interface{} {
				return &URI{}
			},
		},
	}
}

// NewHTTPError returns a new instance of `HTTPError`.
func NewHTTPError(code int, msg ...string) *HTTPError {
	he := &HTTPError{Code: code, Message: http.StatusText(code)}
	if len(msg) > 0 {
		he.Message = msg[0]
	}
	return he
}

// Error implements `Error#Error()`.
func (he *HTTPError) Error() string {
	return he.Message
}

// defaultHTTPErrorHandler invokes the default HTTP error handler.
func defaultHTTPErrorHandler(err error, c *Context) {
	he := ErrInternalServerError
	if che, ok := err.(*HTTPError); ok {
		he = che
	}
	if c.Air.Config.DebugMode {
		he.Message = err.Error()
	}
	if !c.Response.Committed {
		c.Data["string"] = he.Message
		c.StatusCode = he.Code
		c.String()
	}
	c.Air.Logger.Error(err)
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"fmt"
	"os"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/order"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/skel"
	"github.com/getgauge/gauge/track"
	"github.com/getgauge/gauge/util"
	"github.com/getgauge/gauge/validation"
	"github.com/spf13/cobra"
)

var (
	GaugeCmd = &cobra.Command{
		Use: "gauge <command> [flags] [args]",
		Example: `  gauge run specs/
  gauge run --parallel specs/`,
		Run: func(cmd *cobra.Command, args []string) {
			if gaugeVersion {
				printVersion()
				return
			}
			if len(args) < 1 {
				cmd.Help()
			}
		},
		DisableAutoGenTag: true,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			initLogger(cmd.Name())
			skel.CreateSkelFilesIfRequired()
			track.Init()
			config.SetProjectRoot(args)
			setGlobalFlags()
			initPackageFlags()
		},
	}
	logLevel        string
	dir             string
	machineReadable bool
	gaugeVersion    bool
)

func initLogger(n string) {
	if lsp {
		logger.Initialize(logLevel, logger.LSP)
	} else if n == "daemon" {
		logger.Initialize(logLevel, logger.API)
	} else {
		logger.Initialize(logLevel, logger.CLI)
	}
}

func init() {
	GaugeCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
  {{.UseLine}}{{end}}{{if gt (len .Aliases) 0}}

Aliases:
  {{.NameAndAliases}}{{end}}{{if .HasExample}}

Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}

Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}

Use "{{.CommandPath}} [command] --help" for more information about a command.
Complete manual is available at https://manpage.getgauge.io/.{{end}}
`)
	GaugeCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "info", "Set level of logging to debug, info, warning, error or critical")
	GaugeCmd.PersistentFlags().StringVarP(&dir, "dir", "d", ".", "Set the working directory for the current command, accepts a path relative to current directory")
	GaugeCmd.PersistentFlags().BoolVarP(&machineReadable, "machine-readable", "m", false, "Prints output in JSON format")
	GaugeCmd.Flags().BoolVarP(&gaugeVersion, "version", "v", false, "Print Gauge and plugin versions")
}

func Parse() error {
	InitHelp(GaugeCmd)
	return GaugeCmd.Execute()
}

func InitHelp(c *cobra.Command) {
	c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	if c.HasSubCommands() {
		for _, sc := range c.Commands() {
			InitHelp(sc)
		}
	}
}

func getSpecsDir(args []string) []string {
	if len(args) > 0 {
		return args
	}
	return []string{common.SpecsDirectoryName}
}

func setGlobalFlags() {
	msg := fmt.Sprintf("Gauge Install ID: %s", config.UniqueID())
	logger.Debugf(true, msg)
	util.SetWorkingDir(dir)
}

func initPackageFlags() {
	if parallel {
		simpleConsole = true
		reporter.IsParallel = true
	}
	reporter.SimpleConsoleOutput = simpleConsole
	reporter.Verbose = verbose
	reporter.MachineReadable = machineReadable
	execution.ExecuteTags = tags
	execution.SetTableRows(rows)
	validation.TableRows = rows
	execution.NumberOfExecutionStreams = streams
	execution.InParallel = parallel
	execution.Strategy = strategy
	filter.ExecuteTags = tags
	order.Sorted = sort
	filter.Distribute = group
	filter.NumberOfExecutionStreams = streams
	reporter.NumberOfExecutionStreams = streams
	validation.HideSuggestion = hideSuggestion
	if group != -1 {
		execution.Strategy = execution.Eager
	}
}

func exit(err error, additionalText string) {
	if err != nil {
		logger.Errorf(true, err.Error())
	}
	if additionalText != "" {
		logger.Infof(true, additionalText)
	}
	os.Exit(0)
}
<commit_msg>Initializes logger after project root is set<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"fmt"
	"os"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/order"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/skel"
	"github.com/getgauge/gauge/track"
	"github.com/getgauge/gauge/util"
	"github.com/getgauge/gauge/validation"
	"github.com/spf13/cobra"
)

var (
	GaugeCmd = &cobra.Command{
		Use: "gauge <command> [flags] [args]",
		Example: `  gauge run specs/
  gauge run --parallel specs/`,
		Run: func(cmd *cobra.Command, args []string) {
			if gaugeVersion {
				printVersion()
				return
			}
			if len(args) < 1 {
				cmd.Help()
			}
		},
		DisableAutoGenTag: true,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			skel.CreateSkelFilesIfRequired()
			track.Init()
			config.SetProjectRoot(args)
			initLogger(cmd.Name())
			setGlobalFlags()
			initPackageFlags()
		},
	}
	logLevel        string
	dir             string
	machineReadable bool
	gaugeVersion    bool
)

func initLogger(n string) {
	if lsp {
		logger.Initialize(logLevel, logger.LSP)
	} else if n == "daemon" {
		logger.Initialize(logLevel, logger.API)
	} else {
		logger.Initialize(logLevel, logger.CLI)
	}
}

func init() {
	GaugeCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
  {{.UseLine}}{{end}}{{if gt (len .Aliases) 0}}

Aliases:
  {{.NameAndAliases}}{{end}}{{if .HasExample}}

Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}

Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}

Use "{{.CommandPath}} [command] --help" for more information about a command.
Complete manual is available at https://manpage.getgauge.io/.{{end}}
`)
	GaugeCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "info", "Set level of logging to debug, info, warning, error or critical")
	GaugeCmd.PersistentFlags().StringVarP(&dir, "dir", "d", ".", "Set the working directory for the current command, accepts a path relative to current directory")
	GaugeCmd.PersistentFlags().BoolVarP(&machineReadable, "machine-readable", "m", false, "Prints output in JSON format")
	GaugeCmd.Flags().BoolVarP(&gaugeVersion, "version", "v", false, "Print Gauge and plugin versions")
}

func Parse() error {
	InitHelp(GaugeCmd)
	return GaugeCmd.Execute()
}

func InitHelp(c *cobra.Command) {
	c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	if c.HasSubCommands() {
		for _, sc := range c.Commands() {
			InitHelp(sc)
		}
	}
}

func getSpecsDir(args []string) []string {
	if len(args) > 0 {
		return args
	}
	return []string{common.SpecsDirectoryName}
}

func setGlobalFlags() {
	msg := fmt.Sprintf("Gauge Install ID: %s", config.UniqueID())
	logger.Debugf(true, msg)
	util.SetWorkingDir(dir)
}

func initPackageFlags() {
	if parallel {
		simpleConsole = true
		reporter.IsParallel = true
	}
	reporter.SimpleConsoleOutput = simpleConsole
	reporter.Verbose = verbose
	reporter.MachineReadable = machineReadable
	execution.ExecuteTags = tags
	execution.SetTableRows(rows)
	validation.TableRows = rows
	execution.NumberOfExecutionStreams = streams
	execution.InParallel = parallel
	execution.Strategy = strategy
	filter.ExecuteTags = tags
	order.Sorted = sort
	filter.Distribute = group
	filter.NumberOfExecutionStreams = streams
	reporter.NumberOfExecutionStreams = streams
	validation.HideSuggestion = hideSuggestion
	if group != -1 {
		execution.Strategy = execution.Eager
	}
}

func exit(err error, additionalText string) {
	if err != nil {
		logger.Errorf(true, err.Error())
	}
	if additionalText != "" {
		logger.Infof(true, additionalText)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/deis/deisctl/client"
	"github.com/deis/deisctl/config"
	"github.com/deis/deisctl/constant"
	"github.com/deis/deisctl/update"
	"github.com/deis/deisctl/utils"
	"github.com/docopt/docopt-go"
)

const (
	PlatformInstallCommand string = "platform"
)

var (
	DefaultDataContainers = []string{
		"database-data",
		"registry-data",
		"logger-data",
		"builder-data",
	}
)

func ListUnits(c client.Client) error {
	err := c.ListUnits()
	return err
}

func ListUnitFiles(c client.Client) error {
	err := c.ListUnitFiles()
	return err
}

func Scale(c client.Client, targets []string) error {
	for _, target := range targets {
		component, num, err := splitScaleTarget(target)
		if err != nil {
			return err
		}
		err = c.Scale(component, num)
		if err != nil {
			return err
		}
	}
	return nil
}

func Start(c client.Client, targets []string) error {
	// if target is platform, start all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return StartPlatform(c)
	}
	return c.Start(targets)
}

func StartPlatform(c client.Client) error {
	fmt.Println("Starting Platform...")
	if err := startDataContainers(c); err != nil {
		return err
	}
	if err := startDefaultServices(c); err != nil {
		return err
	}
	fmt.Println("Platform started.")
	return nil
}

func startDataContainers(c client.Client) error {
	fmt.Println("Launching data containers...")
	if err := c.Start(DefaultDataContainers); err != nil {
		return err
	}
	fmt.Println("Data containers launched.")
	return nil
}

func startDefaultServices(c client.Client) error {
	fmt.Println("Launching service containers...")
	if err := Start(c, []string{"logger"}); err != nil {
		return err
	}
	if err := Start(c, []string{"cache", "router", "database", "controller", "registry", "builder"}); err != nil {
		return err
	}
	fmt.Println("Service containers launched.")
	return nil
}

func Stop(c client.Client, targets []string) error {
	// if target is platform, stop all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return StopPlatform(c)
	}
	return c.Stop(targets)
}

func StopPlatform(c client.Client) error {
	fmt.Println("Stopping Platform...")
	if err := stopDefaultServices(c); err != nil {
		return err
	}
	fmt.Println("Platform stopped.")
	return nil
}

func stopDefaultServices(c client.Client) error {
	fmt.Println("Stopping service containers...")
	if err := Stop(c, []string{"builder", "registry", "controller", "database", "cache", "router", "logger"}); err != nil {
		return err
	}
	fmt.Println("Service containers stopped.")
	return nil
}

func Restart(c client.Client, targets []string) error {
	if err := c.Stop(targets); err != nil {
		return err
	}
	return c.Start(targets)
}

func Status(c client.Client, targets []string) error {
	for _, target := range targets {
		if err := c.Status(target); err != nil {
			return err
		}
	}
	return nil
}

func Journal(c client.Client, targets []string) error {
	for _, target := range targets {
		if err := c.Journal(target); err != nil {
			return err
		}
	}
	return nil
}

func Install(c client.Client, targets []string) error {
	// if target is platform, install all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return InstallPlatform(c)
	}
	// otherwise create the specific targets
	return c.Create(targets)
}

func InstallPlatform(c client.Client) error {
	if err := installDataContainers(c); err != nil {
		return err
	}
	return installDefaultServices(c)
}

func installDataContainers(c client.Client) error {
	fmt.Println("Scheduling data containers...")
	if err := c.Create(DefaultDataContainers); err != nil {
		return err
	}
	fmt.Println("Data containers scheduled.")
	return nil
}

func installDefaultServices(c client.Client) error {
	// start service containers
	targets := []string{
		"database=1",
		"cache=1",
		"logger=1",
		"registry=1",
		"controller=1",
		"builder=1",
		"router=1"}
	fmt.Println("Scheduling service containers...")
	if err := Scale(c, targets); err != nil {
		return err
	}
	fmt.Println("Service containers scheduled.")
	return nil
}

func Uninstall(c client.Client, targets []string) error {
	// if target is platform, uninstall all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return uninstallAllServices(c)
	}
	// uninstall the specific target
	return c.Destroy(targets)
}

func uninstallAllServices(c client.Client) error {
	targets := []string{
		"database=0",
		"cache=0",
		"logger=0",
		"registry=0",
		"controller=0",
		"builder=0",
		"router=0"}
	fmt.Println("Destroying service containers...")
	err := Scale(c, targets)
	fmt.Println("Service containers destroyed.")
	return err
}

func splitScaleTarget(target string) (c string, num int, err error) {
	r := regexp.MustCompile(`([a-z-]+)=([\d]+)`)
	match := r.FindStringSubmatch(target)
	if len(match) == 0 {
		err = fmt.Errorf("Could not parse: %v", target)
		return
	}
	c = match[1]
	num, err = strconv.Atoi(match[2])
	if err != nil {
		return
	}
	return
}

func Config() error {
	if err := config.Config(); err != nil {
		return err
	}
	return nil
}

func Update() error {
	if err := utils.Execute(constant.HooksDir + "pre-update"); err != nil {
		fmt.Println("pre-updatehook failed")
		return err
	}
	if err := update.Update(); err != nil {
		fmt.Println("update engine failed")
		return err
	}
	if err := utils.Execute(constant.HooksDir + "post-update"); err != nil {
		fmt.Println("post-updatehook failed")
		return err
	}
	return nil
}

func RefreshUnits() error {
	usage := `Refreshes local unit files from the master repository.

deisctl looks for unit files in these directories, in this order:
- the $DEISCTL_UNITS environment variable, if set
- $HOME/.deis/units
- /var/lib/deis/units

Usage:
  deisctl refresh-units [-p <target>]

Options:
  -p --path=<target>   where to save unit files [default: /var/lib/deis/units]
`
	// parse command-line arguments
	args, err := docopt.Parse(usage, nil, true, "", false)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(2)
	}
	dir, _ := client.ExpandUser(args["--path"].(string))
	// create the target dir if necessary
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	// download and save the unit files to the specified path
	rootURL := "https://raw.githubusercontent.com/deis/deisctl/"
	branch := "master"
	units := []string{
		"deis-builder.service",
		"deis-builder-data.service",
		"deis-cache.service",
		"deis-controller.service",
		"deis-database.service",
		"deis-database-data.service",
		"deis-logger.service",
		"deis-logger-data.service",
		"deis-registry.service",
		"deis-registry-data.service",
		"deis-router.service",
	}
	for _, unit := range units {
		src := rootURL + branch + "/units/" + unit
		dest := filepath.Join(dir, unit)
		res, err := http.Get(src)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		data, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return err
		}
		if err = ioutil.WriteFile(dest, data, 0600); err != nil {
			return err
		}
		fmt.Printf("Refreshed %s from %s\n", unit, branch)
	}
	return nil
}
<commit_msg>fix(cmd): append "@1" if none supplied to install<commit_after>package cmd

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"github.com/deis/deisctl/client"
	"github.com/deis/deisctl/config"
	"github.com/deis/deisctl/constant"
	"github.com/deis/deisctl/update"
	"github.com/deis/deisctl/utils"
	"github.com/docopt/docopt-go"
)

const (
	PlatformInstallCommand string = "platform"
)

var (
	DefaultDataContainers = []string{
		"database-data",
		"registry-data",
		"logger-data",
		"builder-data",
	}
)

func ListUnits(c client.Client) error {
	err := c.ListUnits()
	return err
}

func ListUnitFiles(c client.Client) error {
	err := c.ListUnitFiles()
	return err
}

func Scale(c client.Client, targets []string) error {
	for _, target := range targets {
		component, num, err := splitScaleTarget(target)
		if err != nil {
			return err
		}
		err = c.Scale(component, num)
		if err != nil {
			return err
		}
	}
	return nil
}

func Start(c client.Client, targets []string) error {
	// if target is platform, start all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return StartPlatform(c)
	}
	return c.Start(targets)
}

func StartPlatform(c client.Client) error {
	fmt.Println("Starting Platform...")
	if err := startDataContainers(c); err != nil {
		return err
	}
	if err := startDefaultServices(c); err != nil {
		return err
	}
	fmt.Println("Platform started.")
	return nil
}

func startDataContainers(c client.Client) error {
	fmt.Println("Launching data containers...")
	if err := c.Start(DefaultDataContainers); err != nil {
		return err
	}
	fmt.Println("Data containers launched.")
	return nil
}

func startDefaultServices(c client.Client) error {
	fmt.Println("Launching service containers...")
	if err := Start(c, []string{"logger"}); err != nil {
		return err
	}
	if err := Start(c, []string{"cache", "router", "database", "controller", "registry", "builder"}); err != nil {
		return err
	}
	fmt.Println("Service containers launched.")
	return nil
}

func Stop(c client.Client, targets []string) error {
	// if target is platform, stop all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return StopPlatform(c)
	}
	return c.Stop(targets)
}

func StopPlatform(c client.Client) error {
	fmt.Println("Stopping Platform...")
	if err := stopDefaultServices(c); err != nil {
		return err
	}
	fmt.Println("Platform stopped.")
	return nil
}

func stopDefaultServices(c client.Client) error {
	fmt.Println("Stopping service containers...")
	if err := Stop(c, []string{"builder", "registry", "controller", "database", "cache", "router", "logger"}); err != nil {
		return err
	}
	fmt.Println("Service containers stopped.")
	return nil
}

func Restart(c client.Client, targets []string) error {
	if err := c.Stop(targets); err != nil {
		return err
	}
	return c.Start(targets)
}

func Status(c client.Client, targets []string) error {
	for _, target := range targets {
		if err := c.Status(target); err != nil {
			return err
		}
	}
	return nil
}

func Journal(c client.Client, targets []string) error {
	for _, target := range targets {
		if err := c.Journal(target); err != nil {
			return err
		}
	}
	return nil
}

func Install(c client.Client, targets []string) error {
	// if target is platform, install all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return InstallPlatform(c)
	}
	// otherwise create the specific targets
	for i, target := range targets {
		// if we're installing a component without a number attached,
		// consider the user doesn't know better
		if !strings.Contains(target, "@") {
			targets[i] += "@1"
		}
	}
	return c.Create(targets)
}

func InstallPlatform(c client.Client) error {
	if err := installDataContainers(c); err != nil {
		return err
	}
	return installDefaultServices(c)
}

func installDataContainers(c client.Client) error {
	fmt.Println("Scheduling data containers...")
	if err := c.Create(DefaultDataContainers); err != nil {
		return err
	}
	fmt.Println("Data containers scheduled.")
	return nil
}

func installDefaultServices(c client.Client) error {
	// start service containers
	targets := []string{
		"database=1",
		"cache=1",
		"logger=1",
		"registry=1",
		"controller=1",
		"builder=1",
		"router=1"}
	fmt.Println("Scheduling service containers...")
	if err := Scale(c, targets); err != nil {
		return err
	}
	fmt.Println("Service containers scheduled.")
	return nil
}

func Uninstall(c client.Client, targets []string) error {
	// if target is platform, uninstall all services
	if len(targets) == 1 && targets[0] == PlatformInstallCommand {
		return uninstallAllServices(c)
	}
	// uninstall the specific target
	return c.Destroy(targets)
}

func uninstallAllServices(c client.Client) error {
	targets := []string{
		"database=0",
		"cache=0",
		"logger=0",
		"registry=0",
		"controller=0",
		"builder=0",
		"router=0"}
	fmt.Println("Destroying service containers...")
	err := Scale(c, targets)
	fmt.Println("Service containers destroyed.")
	return err
}

func splitScaleTarget(target string) (c string, num int, err error) {
	r := regexp.MustCompile(`([a-z-]+)=([\d]+)`)
	match := r.FindStringSubmatch(target)
	if len(match) == 0 {
		err = fmt.Errorf("Could not parse: %v", target)
		return
	}
	c = match[1]
	num, err = strconv.Atoi(match[2])
	if err != nil {
		return
	}
	return
}

func Config() error {
	if err := config.Config(); err != nil {
		return err
	}
	return nil
}

func Update() error {
	if err := utils.Execute(constant.HooksDir + "pre-update"); err != nil {
		fmt.Println("pre-updatehook failed")
		return err
	}
	if err := update.Update(); err != nil {
		fmt.Println("update engine failed")
		return err
	}
	if err := utils.Execute(constant.HooksDir + "post-update"); err != nil {
		fmt.Println("post-updatehook failed")
		return err
	}
	return nil
}

func RefreshUnits() error {
	usage := `Refreshes local unit files from the master repository.

deisctl looks for unit files in these directories, in this order:
- the $DEISCTL_UNITS environment variable, if set
- $HOME/.deis/units
- /var/lib/deis/units

Usage:
  deisctl refresh-units [-p <target>]

Options:
  -p --path=<target>   where to save unit files [default: /var/lib/deis/units]
`
	// parse command-line arguments
	args, err := docopt.Parse(usage, nil, true, "", false)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(2)
	}
	dir, _ := client.ExpandUser(args["--path"].(string))
	// create the target dir if necessary
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	// download and save the unit files to the specified path
	rootURL := "https://raw.githubusercontent.com/deis/deisctl/"
	branch := "master"
	units := []string{
		"deis-builder.service",
		"deis-builder-data.service",
		"deis-cache.service",
		"deis-controller.service",
		"deis-database.service",
		"deis-database-data.service",
		"deis-logger.service",
		"deis-logger-data.service",
		"deis-registry.service",
		"deis-registry-data.service",
		"deis-router.service",
	}
	for _, unit := range units {
		src := rootURL + branch + "/units/" + unit
		dest := filepath.Join(dir, unit)
		res, err := http.Get(src)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		data, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return err
		}
		if err = ioutil.WriteFile(dest, data, 0600); err != nil {
			return err
		}
		fmt.Printf("Refreshed %s from %s\n", unit, branch)
	}
	return nil
}
<|endoftext|>


<commit_before>// Package cmd used for command line options for the complete tool
package cmd

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/posener/complete/cmd/install"
)

// CLI for command line
type CLI struct {
	Name          string
	InstallName   string
	UninstallName string

	install   bool
	uninstall bool
	yes       bool
}

const (
	defaultInstallName   = "install"
	defaultUninstallName = "uninstall"
)

// Run is used when running complete in command line mode.
// this is used when the complete is not completing words, but to
// install it or uninstall it.
func (f *CLI) Run() bool {

	fs := flag.NewFlagSet(f.Name, 0)
	// add flags and parse them in case they were not added and parsed
	// by the main program
	f.AddFlags(fs)
	fs.Parse(os.Args[1:])

	err := f.validate()
	if err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}

	switch {
	case f.install:
		f.prompt()
		err = install.Install(f.Name)
	case f.uninstall:
		f.prompt()
		err = install.Uninstall(f.Name)
	default:
		// non of the action flags matched,
		// returning false should make the real program execute
		return false
	}

	if err != nil {
		fmt.Printf("%s failed! %s\n", f.action(), err)
		os.Exit(3)
	}
	fmt.Println("Done!")
	return true
}

// prompt use for approval
// exit if approval was not given
func (f *CLI) prompt() {
	defer fmt.Println(f.action() + "ing...")
	if f.yes {
		return
	}
	fmt.Printf("%s completion for %s? ", f.action(), f.Name)
	var answer string
	fmt.Scanln(&answer)

	switch strings.ToLower(answer) {
	case "y", "yes":
		return
	default:
		fmt.Println("Cancelling...")
		os.Exit(1)
	}
}

// AddFlags adds the CLI flags to the flag set.
// If flags is nil, the default command line flags will be taken.
// Pass non-empty strings as installName and uninstallName to override the default
// flag names.
func (f *CLI) AddFlags(flags *flag.FlagSet) {
	if flags == nil {
		flags = flag.CommandLine
	}

	if f.InstallName == "" {
		f.InstallName = defaultInstallName
	}
	if f.UninstallName == "" {
		f.UninstallName = defaultUninstallName
	}

	if flags.Lookup(f.InstallName) == nil {
		flags.BoolVar(&f.install, f.InstallName, false,
			fmt.Sprintf("Install completion for %s command", f.Name))
	}
	if flags.Lookup(f.UninstallName) == nil {
		flags.BoolVar(&f.uninstall, f.UninstallName, false,
			fmt.Sprintf("Uninstall completion for %s command", f.Name))
	}
	if flags.Lookup("y") == nil {
		flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes'")
	}
}

// validate the CLI
func (f *CLI) validate() error {
	if f.install && f.uninstall {
		return errors.New("Install and uninstall are mutually exclusive")
	}
	return nil
}

// action name according to the CLI values.
func (f *CLI) action() string {
	switch {
	case f.install:
		return "Install"
	case f.uninstall:
		return "Uninstall"
	default:
		return "unknown"
	}
}
<commit_msg>cmd: remove adding of flags from run<commit_after>// Package cmd used for command line options for the complete tool
package cmd

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/posener/complete/cmd/install"
)

// CLI for command line
type CLI struct {
	Name          string
	InstallName   string
	UninstallName string

	install   bool
	uninstall bool
	yes       bool
}

const (
	defaultInstallName   = "install"
	defaultUninstallName = "uninstall"
)

// Run is used when running complete in command line mode.
// this is used when the complete is not completing words, but to
// install it or uninstall it.
func (f *CLI) Run() bool {
	err := f.validate()
	if err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}

	switch {
	case f.install:
		f.prompt()
		err = install.Install(f.Name)
	case f.uninstall:
		f.prompt()
		err = install.Uninstall(f.Name)
	default:
		// non of the action flags matched,
		// returning false should make the real program execute
		return false
	}

	if err != nil {
		fmt.Printf("%s failed! %s\n", f.action(), err)
		os.Exit(3)
	}
	fmt.Println("Done!")
	return true
}

// prompt use for approval
// exit if approval was not given
func (f *CLI) prompt() {
	defer fmt.Println(f.action() + "ing...")
	if f.yes {
		return
	}
	fmt.Printf("%s completion for %s? ", f.action(), f.Name)
	var answer string
	fmt.Scanln(&answer)

	switch strings.ToLower(answer) {
	case "y", "yes":
		return
	default:
		fmt.Println("Cancelling...")
		os.Exit(1)
	}
}

// AddFlags adds the CLI flags to the flag set.
// If flags is nil, the default command line flags will be taken.
// Pass non-empty strings as installName and uninstallName to override the default
// flag names.
func (f *CLI) AddFlags(flags *flag.FlagSet) {
	if flags == nil {
		flags = flag.CommandLine
	}

	if f.InstallName == "" {
		f.InstallName = defaultInstallName
	}
	if f.UninstallName == "" {
		f.UninstallName = defaultUninstallName
	}

	if flags.Lookup(f.InstallName) == nil {
		flags.BoolVar(&f.install, f.InstallName, false,
			fmt.Sprintf("Install completion for %s command", f.Name))
	}
	if flags.Lookup(f.UninstallName) == nil {
		flags.BoolVar(&f.uninstall, f.UninstallName, false,
			fmt.Sprintf("Uninstall completion for %s command", f.Name))
	}
	if flags.Lookup("y") == nil {
		flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes'")
	}
}

// validate the CLI
func (f *CLI) validate() error {
	if f.install && f.uninstall {
		return errors.New("Install and uninstall are mutually exclusive")
	}
	return nil
}

// action name according to the CLI values.
func (f *CLI) action() string {
	switch {
	case f.install:
		return "Install"
	case f.uninstall:
		return "Uninstall"
	default:
		return "unknown"
	}
}
<|endoftext|>


<commit_before>package cmd

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

	flag "github.com/spf13/pflag"
)

var commands = map[string]*Command{}

// inspired by https://github.com/constabulary/gb/blob/master/cmd/cmd.go

// Command represents a subcommand, or plugin that is executed
type Command struct {
	// Name of the command
	name string

	// UsageLine demonstrates how to use this command
	usageLine string

	// Single line description of the purpose of the command
	short string

	// Description of this command
	long string

	// Run is invoked with arguments left over after flag parsing.
	run func(args []string) error

	// FlagSet for that command only
	fs *flag.FlagSet
	// FlagSet for that command and all sub-commands
	gfs *flag.FlagSet
	// FlagSet combined (command only and all sub-commands)
	afs *flag.FlagSet
	// output buffer for afs
	abuf *bytes.Buffer

	// function for adding FlagSet for that command
	ffs func(*flag.FlagSet)

	// function for adding flags for that command and any sub-Command FlagSet
	fgfs func(*flag.FlagSet)

	// Parent Command
	parent parent

	// Subcommands
	subcmds map[string]*Command

	// Args passed when running command
	args []string
}

type parent interface {
	fullCommand() string
	parseFlags()
}

func NewCommand(name, usageLine, short, long string, run func([]string) error, parent *Command) *Command {
	cmd := &Command{
		name:      name,
		usageLine: usageLine,
		short:     short,
		long:      long,
		run:       run,
		fs:        flag.NewFlagSet(name, flag.ExitOnError),
		gfs:       flag.NewFlagSet(name, flag.ExitOnError),
		afs:       flag.NewFlagSet(name, flag.ContinueOnError),
		subcmds:   make(map[string]*Command),
		args:      []string{},
	}
	cmd.abuf = new(bytes.Buffer)
	cmd.afs.SetOutput(cmd.abuf)
	cmd.afs.Usage = cmd.FUsage
	// fmt.Printf("New Command '%s', nil parent: %v\n", cmd.name, cmd.parent == nil)
	if parent == nil {
		// This is a root command
		commands[name] = cmd
	} else {
		cmd.parent = *parent
	}
	// fmt.Printf("registered parent for name '%s': %v\n", name, commands)
	return cmd
}

// Set function for adding flags for that command and any sub-Command FlagSet
func (cmd *Command) SetGFS(fgfs func(*flag.FlagSet)) {
	cmd.fgfs = fgfs
	fgfs(cmd.afs)
	fgfs(cmd.gfs)
}

// Set function for adding flags for that command FlagSet only
func (cmd *Command) SetFS(ffs func(*flag.FlagSet)) {
	cmd.ffs = ffs
	ffs(cmd.afs)
	ffs(cmd.gfs)
}

// Runnable indicates this is a command that can be involved.
// Non runnable commands are only informational.
func (c *Command) Runnable() bool { return c.run != nil }

// RunCommand parses flags and runs the Command.
func RunCommand(args []string) error {
	// fmt.Printf("args %+v, c %v '%v'\n", os.Args, commands == nil, commands)
	cmd, err := commandFromArgs(os.Args)
	// fmt.Printf("cmd nil?? %v, args %+v, err='%v'\n", cmd == nil, os.Args, err)
	if err != nil {
		return err
	}
	cmd.parseFlags()
	if cmd.run == nil {
		return nil
	}
	return cmd.run(cmd.afs.Args())
}

func (cmd Command) parseFlags() {
	if cmd.parent != nil {
		cmd.parent.parseFlags()
	}
	if err := cmd.afs.Parse(cmd.args); err != nil {
		fmt.Printf("Incorrect usage of %s:\n", cmd.fullCommand())
		fmt.Printf("%s", cmd.abuf.String())
		os.Exit(1)
	}
}

func (cmd Command) fullCommand() string {
	res := cmd.name
	if cmd.parent != nil {
		res = cmd.parent.fullCommand() + " " + res
	}
	return res
}

func commandFromArgs(args []string) (*Command, error) {
	var cmd *Command
	// fmt.Printf("len %v\n", len(args))
	for i, arg := range args {
		if i == 0 {
			arg = filepath.Base(arg)
			ext := filepath.Ext(arg)
			if ext != "" {
				arg = arg[:len(arg)-len(ext)]
			}
		}
		// fmt.Printf("arg='%s'\n", arg)
		if arg == "--" {
			cmd.args = append(cmd.args, args[i:]...)
			return cmd, nil
		}
		if strings.HasPrefix(arg, "-") {
			cmd.args = append(cmd.args, arg)
			continue
		}
		var subcmd *Command
		// fmt.Printf("cmd nil %v, reg %v\n", cmd == nil, commands)
		if cmd == nil {
			cmd = commands[arg]
			if cmd == nil {
				return nil, fmt.Errorf("Unknown command '%s'", arg)
			}
		} else {
			subcmd = cmd.subcmds[arg]
			if subcmd == nil {
				cmd.args = append(cmd.args, arg)
			} else {
				cmd = subcmd
			}
		}
	}
	if cmd == nil {
		return nil, fmt.Errorf("Unknown command from args '%v'", args)
	}
	return cmd, nil
}

func (c *Command) FUsage() {
	s := strings.Split(c.abuf.String(), "\n")[0]
	c.abuf.Truncate(len(s))
	fmt.Fprintf(c.abuf, "\n%s", c.Usage())
}

func (c *Command) Usage() string {
	u := ""
	u = u + "local flags:\n"
	u = u + c.fs.FlagUsages()
	u = u + "global flags:\n"
	u = u + c.gfs.FlagUsages()
	return u
}
<commit_msg>cmd.go: differential usageFlags() from Usage()<commit_after>package cmd

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

	flag "github.com/spf13/pflag"
)

var commands = map[string]*Command{}

// inspired by https://github.com/constabulary/gb/blob/master/cmd/cmd.go

// Command represents a subcommand, or plugin that is executed
type Command struct {
	// Name of the command
	name string

	// UsageLine demonstrates how to use this command
	usageLine string

	// Single line description of the purpose of the command
	short string

	// Description of this command
	long string

	// Run is invoked with arguments left over after flag parsing.
	run func(args []string) error

	// FlagSet for that command only
	fs *flag.FlagSet
	// FlagSet for that command and all sub-commands
	gfs *flag.FlagSet
	// FlagSet combined (command only and all sub-commands)
	afs *flag.FlagSet
	// output buffer for afs
	abuf *bytes.Buffer

	// function for adding FlagSet for that command
	ffs func(*flag.FlagSet)

	// function for adding flags for that command and any sub-Command FlagSet
	fgfs func(*flag.FlagSet)

	// Parent Command
	parent parent

	// Subcommands
	subcmds map[string]*Command

	// Args passed when running command
	args []string
}

type parent interface {
	fullCommand() string
	parseFlags()
}

func NewCommand(name, usageLine, short, long string, run func([]string) error, parent *Command) *Command {
	cmd := &Command{
		name:      name,
		usageLine: usageLine,
		short:     short,
		long:      long,
		run:       run,
		fs:        flag.NewFlagSet(name, flag.ExitOnError),
		gfs:       flag.NewFlagSet(name, flag.ExitOnError),
		afs:       flag.NewFlagSet(name, flag.ContinueOnError),
		subcmds:   make(map[string]*Command),
		args:      []string{},
	}
	cmd.abuf = new(bytes.Buffer)
	cmd.afs.SetOutput(cmd.abuf)
	cmd.afs.Usage = cmd.fUsage
	// fmt.Printf("New Command '%s', nil parent: %v\n", cmd.name, cmd.parent == nil)
	if parent == nil {
		// This is a root command
		commands[name] = cmd
	} else {
		cmd.parent = *parent
	}
	// fmt.Printf("registered parent for name '%s': %v\n", name, commands)
	return cmd
}

// Set function for adding flags for that command and any sub-Command FlagSet
func (cmd *Command) SetGFS(fgfs func(*flag.FlagSet)) {
	cmd.fgfs = fgfs
	fgfs(cmd.afs)
	fgfs(cmd.gfs)
}

// Set function for adding flags for that command FlagSet only
func (cmd *Command) SetFS(ffs func(*flag.FlagSet)) {
	cmd.ffs = ffs
	ffs(cmd.afs)
	ffs(cmd.gfs)
}

// Runnable indicates this is a command that can be involved.
// Non runnable commands are only informational.
func (c *Command) Runnable() bool { return c.run != nil }

// RunCommand parses flags and runs the Command.
func RunCommand(args []string) error {
	// fmt.Printf("args %+v, c %v '%v'\n", os.Args, commands == nil, commands)
	cmd, err := commandFromArgs(os.Args)
	// fmt.Printf("cmd nil?? %v, args %+v, err='%v'\n", cmd == nil, os.Args, err)
	if err != nil {
		return err
	}
	cmd.parseFlags()
	if cmd.run == nil {
		return nil
	}
	return cmd.run(cmd.afs.Args())
}

func (cmd Command) parseFlags() {
	if cmd.parent != nil {
		cmd.parent.parseFlags()
	}
	if err := cmd.afs.Parse(cmd.args); err != nil {
		fmt.Printf("Incorrect usage of %s:\n", cmd.fullCommand())
		fmt.Printf("%s", cmd.abuf.String())
		os.Exit(1)
	}
}

func (cmd Command) fullCommand() string {
	res := cmd.name
	if cmd.parent != nil {
		res = cmd.parent.fullCommand() + " " + res
	}
	return res
}

func commandFromArgs(args []string) (*Command, error) {
	var cmd *Command
	// fmt.Printf("len %v\n", len(args))
	for i, arg := range args {
		if i == 0 {
			arg = filepath.Base(arg)
			ext := filepath.Ext(arg)
			if ext != "" {
				arg = arg[:len(arg)-len(ext)]
			}
		}
		// fmt.Printf("arg='%s'\n", arg)
		if arg == "--" {
			cmd.args = append(cmd.args, args[i:]...)
			return cmd, nil
		}
		if strings.HasPrefix(arg, "-") {
			cmd.args = append(cmd.args, arg)
			continue
		}
		var subcmd *Command
		// fmt.Printf("cmd nil %v, reg %v\n", cmd == nil, commands)
		if cmd == nil {
			cmd = commands[arg]
			if cmd == nil {
				return nil, fmt.Errorf("Unknown command '%s'", arg)
			}
		} else {
			subcmd = cmd.subcmds[arg]
			if subcmd == nil {
				cmd.args = append(cmd.args, arg)
			} else {
				cmd = subcmd
			}
		}
	}
	if cmd == nil {
		return nil, fmt.Errorf("Unknown command from args '%v'", args)
	}
	return cmd, nil
}

func (c *Command) fUsage() {
	s := strings.Split(c.abuf.String(), "\n")[0]
	c.abuf.Truncate(len(s))
	fmt.Fprintf(c.abuf, "\n%s", c.UsageFlags())
}

func (c *Command) UsageFlags() string {
	u := ""
	u = u + "local flags:\n"
	u = u + c.fs.FlagUsages()
	u = u + "global flags:\n"
	u = u + c.gfs.FlagUsages()
	return u
}

func (c *Command) Usage() string {
	u := ""
	u = u + c.name + ": "
	u = u + c.short + "\n\n"
	u = u + c.usageLine + "\n"
	u = u + c.long + "\n\n"
	u = u + c.UsageFlags()
	return u
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"

	"github.com/megamsys/libgo/fs"
	"github.com/sajari/fuzzy"
	"launchpad.net/gnuflag"
)

var ErrAbortCommand = errors.New("")
var errUnauthorized = errors.New("unauthorized")

type exiter interface {
	Exit(int)
}

type osExiter struct{}

func (e osExiter) Exit(code int) {
	os.Exit(code)
}

type Lookup func(context *Context) error

type Mode func(modelvl int)

type Manager struct {
	Commands map[string]Command
	topics   map[string]string
	name     string
	stdout   io.Writer
	stderr   io.Writer
	stdin    io.Reader
	version  string
	e        exiter
	original string
	wrong    bool
	lookup   Lookup
	contexts []*Context
	mode     Mode
}

func NewManager(name, ver string, stdout, stderr io.Writer, stdin io.Reader, lookup Lookup, mode Mode) *Manager {
	manager := &Manager{name: name, version: ver, stdout: stdout, stderr: stderr, stdin: stdin, lookup: lookup, mode: mode}
	manager.Register(&help{manager})
	manager.Register(&version{manager})
	return manager
}

func BuildBaseManager(name, version string, lookup Lookup, mode Mode) *Manager {
	m := NewManager(name, version, os.Stdout, os.Stderr, os.Stdin, lookup, mode)
	return m
}

func (m *Manager) Register(command Command) {
	if m.Commands == nil {
		m.Commands = make(map[string]Command)
	}
	name := command.Info().Name
	_, found := m.Commands[name]
	if found {
		panic(fmt.Sprintf("command already registered: %s", name))
	}
	m.Commands[name] = command
}

func (m *Manager) RegisterDeprecated(command Command, oldName string) {
	if m.Commands == nil {
		m.Commands = make(map[string]Command)
	}
	name := command.Info().Name
	_, found := m.Commands[name]
	if found {
		panic(fmt.Sprintf("command already registered: %s", name))
	}
	m.Commands[name] = command
	m.Commands[oldName] = &DeprecatedCommand{Command: command, oldName: oldName}
}

func (m *Manager) RegisterTopic(name, content string) {
	if m.topics == nil {
		m.topics = make(map[string]string)
	}
	_, found := m.topics[name]
	if found {
		panic(fmt.Sprintf("topic already registered: %s", name))
	}
	m.topics[name] = content
}

func (m *Manager) Run(args []string) {
	var (
		status         int
		verbosity      int
		displayHelp    bool
		displayVersion bool
	)
	if len(args) == 0 {
		args = append(args, "help")
	}
	flagset := gnuflag.NewFlagSet("megamd flags", gnuflag.ContinueOnError)
	flagset.SetOutput(m.stderr)
	flagset.IntVar(&verbosity, "verbosity", 0, "Verbosity level: 1 => print debug")
	flagset.IntVar(&verbosity, "v", 0, "Verbosity level: 1 => print debug")
	flagset.BoolVar(&displayHelp, "help", false, "Display help and exit")
	flagset.BoolVar(&displayHelp, "h", false, "Display help and exit")
	flagset.BoolVar(&displayVersion, "version", false, "Print version and exit")
	parseErr := flagset.Parse(false, args)
	if parseErr != nil {
		fmt.Fprint(m.stderr, parseErr)
		m.finisher().Exit(2)
		return
	}
	args = flagset.Args()
	if displayHelp {
		args = append([]string{"help"}, args...)
	} else if displayVersion {
		args = []string{"version"}
	}
	name := args[0]
	command, ok := m.Commands[name]
	if !ok {
		if m.lookup != nil {
			context := m.newContext(args, m.stdout, m.stderr, m.stdin)
			err := m.lookup(context)
			if err != nil {
				msg := ""
				if os.IsNotExist(err) {
					msg = fmt.Sprintf("%s: %q is not a megamd command. See %q.\n", os.Args[0], args[0], "megamd help")
					var keys []string
					for key := range m.Commands {
						keys = append(keys, key)
					}
					sort.Strings(keys)
					for _, key := range keys {
						levenshtein := fuzzy.Levenshtein(&key, &args[0])
						if levenshtein < 3 || strings.Contains(key, args[0]) {
							if !strings.Contains(msg, "Did you mean?") {
								msg += fmt.Sprintf("\nDid you mean?\n")
							}
							msg += fmt.Sprintf("\t%s\n", key)
						}
					}
				} else {
					msg = err.Error()
				}
				fmt.Fprint(m.stderr, msg)
				m.finisher().Exit(1)
			}
			return
		}
		fmt.Fprintf(m.stderr, "Error: command %q does not exist\n", args[0])
		m.finisher().Exit(1)
		return
	}
	args = args[1:]
	info := command.Info()
	command, args, err := m.handleFlags(command, name, args)
	if err != nil {
		fmt.Fprint(m.stderr, err)
		m.finisher().Exit(1)
		return
	}
	if length := len(args); (length < info.MinArgs || (info.MaxArgs > 0 && length > info.MaxArgs)) &&
		name != "help" {
		m.wrong = true
		m.original = info.Name
		command = m.Commands["help"]
		args = []string{name}
		status = 1
	}
	context := m.newContext(args, m.stdout, m.stderr, m.stdin)

	if m.mode != nil {
		m.mode(verbosity)
	}

	err = command.Run(context)
	if err != nil {
		errorMsg := err.Error()
		if !strings.HasSuffix(errorMsg, "\n") {
			errorMsg += "\n"
		}
		if err != ErrAbortCommand {
			io.WriteString(m.stderr, "Error: "+errorMsg)
		}
		status = 1
	}

	m.finisher().Exit(status)
}

func (m *Manager) newContext(args []string, stdout io.Writer, stderr io.Writer, stdin io.Reader) *Context {
	ctx := &Context{args, stdout, stderr, stdin}
	m.contexts = append(m.contexts, ctx)
	return ctx
}

func (m *Manager) handleFlags(command Command, name string, args []string) (Command, []string, error) {
	var flagset *gnuflag.FlagSet
	if flagged, ok := command.(FlaggedCommand); ok {
		flagset = flagged.Flags()
	} else {
		flagset = gnuflag.NewFlagSet(name, gnuflag.ExitOnError)
	}
	var helpRequested bool
	flagset.SetOutput(m.stderr)
	if flagset.Lookup("help") == nil {
		flagset.BoolVar(&helpRequested, "help", false, "Display help and exit")
	}
	if flagset.Lookup("h") == nil {
		flagset.BoolVar(&helpRequested, "h", false, "Display help and exit")
	}
	err := flagset.Parse(true, args)
	if err != nil {
		return nil, nil, err
	}
	if helpRequested {
		command = m.Commands["help"]
		args = []string{name}
	} else {
		args = flagset.Args()
	}
	return command, args, nil
}

func (m *Manager) finisher() exiter {
	/*	if writer, ok := m.stdout.(*io.Writer); ok {
			writer.close()
		}
		for _, ctx := range m.contexts {
			if writer, ok := ctx.Stdout.(*io.Writer); ok {
				writer.close()
			}
		}*/
	if m.e == nil {
		m.e = osExiter{}
	}
	return m.e
}

type Command interface {
	Info() *Info
	Run(context *Context) error
}

type FlaggedCommand interface {
	Command
	Flags() *gnuflag.FlagSet
}

type DeprecatedCommand struct {
	Command
	oldName string
}

func (c *DeprecatedCommand) Run(context *Context) error {
	fmt.Fprintf(context.Stderr, "WARNING: %q has been deprecated, please use %q instead.\n\n", c.oldName, c.Command.Info().Name)
	return c.Command.Run(context)
}

func (c *DeprecatedCommand) Flags() *gnuflag.FlagSet {
	if cmd, ok := c.Command.(FlaggedCommand); ok {
		return cmd.Flags()
	}
	return gnuflag.NewFlagSet("", gnuflag.ContinueOnError)
}

type Context struct {
	Args   []string
	Stdout io.Writer
	Stderr io.Writer
	Stdin  io.Reader
}

type Info struct {
	Name    string
	MinArgs int
	MaxArgs int
	Usage   string
	Desc    string
}

// Implementing the Commandable interface allows extending
// the megamd command line interface
type Commandable interface {
	Commands() []Command
}

// Implementing the AdminCommandable interface allows extending
// the megamd admin command line interface
type AdminCommandable interface {
	AdminCommands() []Command
}

type help struct {
	manager *Manager
}

func (c *help) Info() *Info {
	return &Info{Name: "help", Usage: "command [args]"}
}

func (c *help) Run(context *Context) error {
	const deprecatedMsg = "WARNING: %q is deprecated. Showing help for %q instead.\n\n"
	output := fmt.Sprintf("%s version %s.\n\n",
		Colorfy(c.manager.name, "yellow", "", "bold"),
		Colorfy(c.manager.version, "blue", "", "bold"))
	if c.manager.wrong {
		output += fmt.Sprintf("%s: %s.\n\n", Colorfy("ERROR", "red", "", "bold"),
			Colorfy("wrong number of arguments", "white", "", "bold"))
	}
	if len(context.Args) > 0 {
		if cmd, ok := c.manager.Commands[context.Args[0]]; ok {
			if deprecated, ok := cmd.(*DeprecatedCommand); ok {
				fmt.Fprintf(context.Stderr, deprecatedMsg, deprecated.oldName, cmd.Info().Name)
			}
			info := cmd.Info()
			output += fmt.Sprintf("Usage: %s %s\n", c.manager.name, info.Usage)
			output += fmt.Sprintf("\n%s\n", info.Desc)
			flags := c.parseFlags(cmd)
			if flags != "" {
				output += fmt.Sprintf("\n%s", flags)
			}
			if info.MinArgs > 0 {
				output += fmt.Sprintf("\nMinimum # of arguments: %d", info.MinArgs)
			}
			if info.MaxArgs > 0 {
				output += fmt.Sprintf("\nMaximum # of arguments: %d", info.MaxArgs)
			}
			output += fmt.Sprint("\n")
		} else if topic, ok := c.manager.topics[context.Args[0]]; ok {
			output += topic
		} else {
			return fmt.Errorf("command %q does not exist.", context.Args[0])
		}
	} else {
		output += fmt.Sprintf("Usage: %s %s\n\nAvailable commands:\n", c.manager.name, c.Info().Usage)
		var commands []string
		for name, cmd := range c.manager.Commands {
			if _, ok := cmd.(*DeprecatedCommand); !ok {
				commands = append(commands, name)
			}
		}
		sort.Strings(commands)
		maxCmdSize := 20
		for _, command := range commands {
			if len(command) > maxCmdSize {
				maxCmdSize = len(command)
			}
		}
		for _, command := range commands {
			description := c.manager.Commands[command].Info().Desc
			description = strings.Split(description, "\n")[0]
			description = strings.Split(description, ".")[0]
			if len(description) > 2 {
				description = strings.ToUpper(description[:1]) + description[1:]
			}
			fmtStr := fmt.Sprintf("  %%-%ds %%s\n", maxCmdSize)
			output += fmt.Sprintf(fmtStr, command, description)
		}
		output += fmt.Sprintf("\nUse %s help <commandname> to get more information about a command.\n", c.manager.name)
		if len(c.manager.topics) > 0 {
			output += fmt.Sprintln("\nAvailable topics:")
			for topic := range c.manager.topics {
				output += fmt.Sprintf("  %s\n", topic)
			}
			output += fmt.Sprintf("\nUse %s help <topicname> to get more information about a topic.\n", c.manager.name)
		}
	}
	io.WriteString(context.Stdout, output)
	return nil
}

func (c *help) parseFlags(command Command) string {
	var output string
	if cmd, ok := command.(FlaggedCommand); ok {
		var buf bytes.Buffer
		flagset := cmd.Flags()
		flagset.SetOutput(&buf)
		flagset.PrintDefaults()
		if buf.String() != "" {
			output = fmt.Sprintf("Flags:\n\n%s", buf.String())
		}
	}
	return strings.Replace(output, "\n", "\n  ", -1)
}

type version struct {
	manager *Manager
}

func (c *version) Info() *Info {
	return &Info{
		Name:    "version",
		MinArgs: 0,
		Usage:   "version",
		Desc:    "display the current version",
	}
}

func (c *version) Run(context *Context) error {
	fmt.Fprintf(context.Stdout, "%s version %s.\n", c.manager.name, c.manager.version)
	return nil
}

func ExtractProgramName(path string) string {
	parts := strings.Split(path, "/")
	return parts[len(parts)-1]
}

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}
<commit_msg>minor colored help<commit_after>package cmd

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"

	"github.com/megamsys/libgo/fs"
	"github.com/sajari/fuzzy"
	"launchpad.net/gnuflag"
)

var ErrAbortCommand = errors.New("")
var errUnauthorized = errors.New("unauthorized")

type exiter interface {
	Exit(int)
}

type osExiter struct{}

func (e osExiter) Exit(code int) {
	os.Exit(code)
}

type Lookup func(context *Context) error

type Mode func(modelvl int)

type Manager struct {
	Commands map[string]Command
	topics   map[string]string
	name     string
	stdout   io.Writer
	stderr   io.Writer
	stdin    io.Reader
	version  string
	e        exiter
	original string
	wrong    bool
	lookup   Lookup
	contexts []*Context
	mode     Mode
}

func NewManager(name, ver string, stdout, stderr io.Writer, stdin io.Reader, lookup Lookup, mode Mode) *Manager {
	manager := &Manager{name: name, version: ver, stdout: stdout, stderr: stderr, stdin: stdin, lookup: lookup, mode: mode}
	manager.Register(&help{manager})
	manager.Register(&version{manager})
	return manager
}

func BuildBaseManager(name, version string, lookup Lookup, mode Mode) *Manager {
	m := NewManager(name, version, os.Stdout, os.Stderr, os.Stdin, lookup, mode)
	return m
}

func (m *Manager) Register(command Command) {
	if m.Commands == nil {
		m.Commands = make(map[string]Command)
	}
	name := command.Info().Name
	_, found := m.Commands[name]
	if found {
		panic(fmt.Sprintf("command already registered: %s", name))
	}
	m.Commands[name] = command
}

func (m *Manager) RegisterDeprecated(command Command, oldName string) {
	if m.Commands == nil {
		m.Commands = make(map[string]Command)
	}
	name := command.Info().Name
	_, found := m.Commands[name]
	if found {
		panic(fmt.Sprintf("command already registered: %s", name))
	}
	m.Commands[name] = command
	m.Commands[oldName] = &DeprecatedCommand{Command: command, oldName: oldName}
}

func (m *Manager) RegisterTopic(name, content string) {
	if m.topics == nil {
		m.topics = make(map[string]string)
	}
	_, found := m.topics[name]
	if found {
		panic(fmt.Sprintf("topic already registered: %s", name))
	}
	m.topics[name] = content
}

func (m *Manager) Run(args []string) {
	var (
		status         int
		verbosity      int
		displayHelp    bool
		displayVersion bool
	)
	if len(args) == 0 {
		args = append(args, "help")
	}
	flagset := gnuflag.NewFlagSet("megamd flags", gnuflag.ContinueOnError)
	flagset.SetOutput(m.stderr)
	flagset.IntVar(&verbosity, "verbosity", 0, "Verbosity level: 1 => print debug")
	flagset.IntVar(&verbosity, "v", 0, "Verbosity level: 1 => print debug")
	flagset.BoolVar(&displayHelp, "help", false, "Display help and exit")
	flagset.BoolVar(&displayHelp, "h", false, "Display help and exit")
	flagset.BoolVar(&displayVersion, "version", false, "Print version and exit")
	parseErr := flagset.Parse(false, args)
	if parseErr != nil {
		fmt.Fprint(m.stderr, parseErr)
		m.finisher().Exit(2)
		return
	}
	args = flagset.Args()
	if displayHelp {
		args = append([]string{"help"}, args...)
	} else if displayVersion {
		args = []string{"version"}
	}
	name := args[0]
	command, ok := m.Commands[name]
	if !ok {
		if m.lookup != nil {
			context := m.newContext(args, m.stdout, m.stderr, m.stdin)
			err := m.lookup(context)
			if err != nil {
				msg := ""
				if os.IsNotExist(err) {
					msg = fmt.Sprintf("%s: %q is not a megamd command. See %q.\n", os.Args[0], args[0], "megamd help")
					var keys []string
					for key := range m.Commands {
						keys = append(keys, key)
					}
					sort.Strings(keys)
					for _, key := range keys {
						levenshtein := fuzzy.Levenshtein(&key, &args[0])
						if levenshtein < 3 || strings.Contains(key, args[0]) {
							if !strings.Contains(msg, "Did you mean?") {
								msg += fmt.Sprintf("\nDid you mean?\n")
							}
							msg += fmt.Sprintf("\t%s\n", key)
						}
					}
				} else {
					msg = err.Error()
				}
				fmt.Fprint(m.stderr, msg)
				m.finisher().Exit(1)
			}
			return
		}
		fmt.Fprintf(m.stderr, "Error: command %q does not exist\n", args[0])
		m.finisher().Exit(1)
		return
	}
	args = args[1:]
	info := command.Info()
	command, args, err := m.handleFlags(command, name, args)
	if err != nil {
		fmt.Fprint(m.stderr, err)
		m.finisher().Exit(1)
		return
	}
	if length := len(args); (length < info.MinArgs || (info.MaxArgs > 0 && length > info.MaxArgs)) &&
		name != "help" {
		m.wrong = true
		m.original = info.Name
		command = m.Commands["help"]
		args = []string{name}
		status = 1
	}
	context := m.newContext(args, m.stdout, m.stderr, m.stdin)

	if m.mode != nil {
		m.mode(verbosity)
	}

	err = command.Run(context)
	if err != nil {
		errorMsg := err.Error()
		if !strings.HasSuffix(errorMsg, "\n") {
			errorMsg += "\n"
		}
		if err != ErrAbortCommand {
			io.WriteString(m.stderr, "Error: "+errorMsg)
		}
		status = 1
	}

	m.finisher().Exit(status)
}

func (m *Manager) newContext(args []string, stdout io.Writer, stderr io.Writer, stdin io.Reader) *Context {
	ctx := &Context{args, stdout, stderr, stdin}
	m.contexts = append(m.contexts, ctx)
	return ctx
}

func (m *Manager) handleFlags(command Command, name string, args []string) (Command, []string, error) {
	var flagset *gnuflag.FlagSet
	if flagged, ok := command.(FlaggedCommand); ok {
		flagset = flagged.Flags()
	} else {
		flagset = gnuflag.NewFlagSet(name, gnuflag.ExitOnError)
	}
	var helpRequested bool
	flagset.SetOutput(m.stderr)
	if flagset.Lookup("help") == nil {
		flagset.BoolVar(&helpRequested, "help", false, "Display help and exit")
	}
	if flagset.Lookup("h") == nil {
		flagset.BoolVar(&helpRequested, "h", false, "Display help and exit")
	}
	err := flagset.Parse(true, args)
	if err != nil {
		return nil, nil, err
	}
	if helpRequested {
		command = m.Commands["help"]
		args = []string{name}
	} else {
		args = flagset.Args()
	}
	return command, args, nil
}

func (m *Manager) finisher() exiter {
	/*	if writer, ok := m.stdout.(*io.Writer); ok {
			writer.close()
		}
		for _, ctx := range m.contexts {
			if writer, ok := ctx.Stdout.(*io.Writer); ok {
				writer.close()
			}
		}*/
	if m.e == nil {
		m.e = osExiter{}
	}
	return m.e
}

type Command interface {
	Info() *Info
	Run(context *Context) error
}

type FlaggedCommand interface {
	Command
	Flags() *gnuflag.FlagSet
}

type DeprecatedCommand struct {
	Command
	oldName string
}

func (c *DeprecatedCommand) Run(context *Context) error {
	fmt.Fprintf(context.Stderr, "WARNING: %q has been deprecated, please use %q instead.\n\n", c.oldName, c.Command.Info().Name)
	return c.Command.Run(context)
}

func (c *DeprecatedCommand) Flags() *gnuflag.FlagSet {
	if cmd, ok := c.Command.(FlaggedCommand); ok {
		return cmd.Flags()
	}
	return gnuflag.NewFlagSet("", gnuflag.ContinueOnError)
}

type Context struct {
	Args   []string
	Stdout io.Writer
	Stderr io.Writer
	Stdin  io.Reader
}

type Info struct {
	Name    string
	MinArgs int
	MaxArgs int
	Usage   string
	Desc    string
}

// Implementing the Commandable interface allows extending
// the megamd command line interface
type Commandable interface {
	Commands() []Command
}

// Implementing the AdminCommandable interface allows extending
// the megamd admin command line interface
type AdminCommandable interface {
	AdminCommands() []Command
}

type help struct {
	manager *Manager
}

func (c *help) Info() *Info {
	return &Info{Name: "help", Usage: "command [args]"}
}

func (c *help) Run(context *Context) error {
	const deprecatedMsg = "WARNING: %q is deprecated. Showing help for %q instead.\n\n"
	output := fmt.Sprintf("%s version %s.\n\n",
		Colorfy(c.manager.name, "green", "", "bold"),
		Colorfy(c.manager.version, "yellow", "", "bold"))
	if c.manager.wrong {
		output += fmt.Sprintf("%s: %s.\n\n", Colorfy("ERROR", "red", "", "bold"),
			Colorfy("wrong number of arguments", "white", "", "bold"))
	}
	if len(context.Args) > 0 {
		if cmd, ok := c.manager.Commands[context.Args[0]]; ok {
			if deprecated, ok := cmd.(*DeprecatedCommand); ok {
				fmt.Fprintf(context.Stderr, deprecatedMsg, deprecated.oldName, cmd.Info().Name)
			}
			info := cmd.Info()
			output += fmt.Sprintf("Usage: %s %s\n", c.manager.name, info.Usage)
			output += fmt.Sprintf("\n%s\n", info.Desc)
			flags := c.parseFlags(cmd)
			if flags != "" {
				output += fmt.Sprintf("\n%s", flags)
			}
			if info.MinArgs > 0 {
				output += fmt.Sprintf("\nMinimum # of arguments: %d", info.MinArgs)
			}
			if info.MaxArgs > 0 {
				output += fmt.Sprintf("\nMaximum # of arguments: %d", info.MaxArgs)
			}
			output += fmt.Sprint("\n")
		} else if topic, ok := c.manager.topics[context.Args[0]]; ok {
			output += topic
		} else {
			return fmt.Errorf("command %q does not exist.", context.Args[0])
		}
	} else {
		output += fmt.Sprintf("Usage: %s %s\n\nAvailable commands:\n", c.manager.name, c.Info().Usage)
		var commands []string
		for name, cmd := range c.manager.Commands {
			if _, ok := cmd.(*DeprecatedCommand); !ok {
				commands = append(commands, name)
			}
		}
		sort.Strings(commands)
		maxCmdSize := 20
		for _, command := range commands {
			if len(command) > maxCmdSize {
				maxCmdSize = len(command)
			}
		}
		for _, command := range commands {
			description := c.manager.Commands[command].Info().Desc
			description = strings.Split(description, "\n")[0]
			description = strings.Split(description, ".")[0]
			if len(description) > 2 {
				description = strings.ToUpper(description[:1]) + description[1:]
			}
			fmtStr := fmt.Sprintf("  %%-%ds %%s\n", maxCmdSize)
			output += fmt.Sprintf(fmtStr, command, description)
		}
		output += fmt.Sprintf("\nUse %s help <commandname> to get more information about a command.\n", c.manager.name)
		if len(c.manager.topics) > 0 {
			output += fmt.Sprintln("\nAvailable topics:")
			for topic := range c.manager.topics {
				output += fmt.Sprintf("  %s\n", topic)
			}
			output += fmt.Sprintf("\nUse %s help <topicname> to get more information about a topic.\n", c.manager.name)
		}
	}
	io.WriteString(context.Stdout, output)
	return nil
}

func (c *help) parseFlags(command Command) string {
	var output string
	if cmd, ok := command.(FlaggedCommand); ok {
		var buf bytes.Buffer
		flagset := cmd.Flags()
		flagset.SetOutput(&buf)
		flagset.PrintDefaults()
		if buf.String() != "" {
			output = fmt.Sprintf("Flags:\n\n%s", buf.String())
		}
	}
	return strings.Replace(output, "\n", "\n  ", -1)
}

type version struct {
	manager *Manager
}

func (c *version) Info() *Info {
	return &Info{
		Name:    "version",
		MinArgs: 0,
		Usage:   "version",
		Desc:    "display the current version",
	}
}

func (c *version) Run(context *Context) error {
	fmt.Fprintf(context.Stdout, "%s version %s.\n", c.manager.name, c.manager.version)
	return nil
}

func ExtractProgramName(path string) string {
	parts := strings.Split(path, "/")
	return parts[len(parts)-1]
}

var fsystem fs.Fs

func filesystem() fs.Fs {
	if fsystem == nil {
		fsystem = fs.OsFs{}
	}
	return fsystem
}
<|endoftext|>


<commit_before>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-ego/ego/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package cmd

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os/exec"
	"runtime"
)

// GetName get params name
func GetName(args ...string) (string, string) {
	var (
		cmdName = "/bin/bash"
		// cmdName = os.Getenv("SHELL")
		params = "-c"
	)

	if runtime.GOOS == "windows" {
		cmdName = "cmd"
		params = "/C"
	}

	if len(args) > 0 {
		cmdName = args[0]
	}

	if len(args) > 1 {
		params = args[1]
	}

	return cmdName, params
}

// Run run cmd shell
func Run(str string, args ...string) (string, string, error) {
	cmdName, params := GetName(args...)

	fmt.Println("cmd run: ", cmdName, params, ": ", str)
	cmd := exec.Command(cmdName, params, str)

	var out, e bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &e

	err := cmd.Run()
	return out.String(), e.String(), err
}

// Exec exex command stdout
func Exec(cmdName string, params ...string) bool {
	cmd := exec.Command(cmdName, params...)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Println("cmd.StdoutPipe error: ", err)
		return false
	}
	err = cmd.Start()
	if err != nil {
		log.Println("cmd.Start error: ", err)
	}

	reader := bufio.NewReader(stdout)
	for {
		line, err2 := reader.ReadString('\n')
		if err2 != nil || io.EOF == err2 {
			break
		}
		fmt.Println(line)
	}

	err = cmd.Wait()
	if err != nil {
		log.Println("cmd.Wait error: ", err)
	}
	return true
}
<commit_msg>add RunSh() function and remove log print<commit_after>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-ego/ego/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package cmd

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os/exec"
	"runtime"
)

// GetName get the params name
func GetName(args ...string) (string, string) {
	var (
		cmdName = "/bin/bash"
		// cmdName = os.Getenv("SHELL")
		params = "-c"
	)

	if runtime.GOOS == "windows" {
		cmdName = "cmd"
		params = "/c"
	}

	if len(args) > 0 {
		cmdName = args[0]
	}

	if len(args) > 1 {
		params = args[1]
	}

	return cmdName, params
}

// Run the command shell return output
func RunSh(path string) ([]byte, error) {
	cmdName := "/bin/bash"
	params := "-c"
	if runtime.GOOS == "windows" {
		cmdName = "cmd"
		params = "/c"
	}

	cmd := exec.Command(cmdName, params, path)
	output, err := cmd.Output()

	return output, err
}

// Run run the cmd shell return error
func Run(str string, args ...string) (string, string, error) {
	cmdName, params := GetName(args...)

	fmt.Println("cmd run: ", cmdName, params, ": ", str)
	cmd := exec.Command(cmdName, params, str)

	var out, e bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &e

	err := cmd.Run()
	return out.String(), e.String(), err
}

// Exec exex the command stdout
func Exec(cmdName string, params ...string) error {
	cmd := exec.Command(cmdName, params...)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		// log.Println("cmd.StdoutPipe error: ", err)
		return err
	}
	err = cmd.Start()
	if err != nil {
		// log.Println("cmd.Start error: ", err)
		return err
	}

	reader := bufio.NewReader(stdout)
	for {
		line, err2 := reader.ReadString('\n')
		if err2 != nil || io.EOF == err2 {
			break
		}
		fmt.Println(line)
	}

	err = cmd.Wait()
	// if err != nil {
	// 	log.Println("cmd.Wait error: ", err)
	// }
	return err
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"

	"h12.me/csv"
)

type MySQLCmd struct {
	Value      interface{}
	TagKey     string
	ExpandPath []string
	DB         string
	Engine     string
	Table      string
}

func (cmd MySQLCmd) CreateDB() string {
	return fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8;\n", cmd.DB)
}

func (cmd MySQLCmd) CreateTable() (string, error) {
	w := new(bytes.Buffer)
	fmt.Fprintf(w, "CREATE TABLE IF NOT EXISTS %s (\n", cmd.Table)
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	var pks []string
	sort.Sort(fields)
	for _, field := range fields {
		if field.Tag.Get("PK") == "yes" {
			pks = append(pks, field.Name)
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	for _, field := range fields {
		if field.Tag.Get("PK") != "yes" {
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	fmt.Fprintf(w, "\tPRIMARY KEY (%s)\n", strings.Join(pks, ","))
	fmt.Fprintf(w, ") ENGINE=%s DEFAULT CHARSET=UTF8;\n", cmd.Engine)
	return w.String(), nil
}

func (cmd MySQLCmd) LoadData() (string, error) {
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("LOAD DATA LOCAL INFILE 'Reader::%%[1]s' REPLACE INTO TABLE %%[1]s (%s);\n", strings.Join(fields.Names(), ", ")), nil
}

func (cmd MySQLCmd) Fields() (csv.Fields, error) {
	enc := csv.NewEncoder(ioutil.Discard).SetTagKey(cmd.TagKey).SetExpandPath(cmd.ExpandPath...)
	if err := enc.Encode(cmd.Value); err != nil {
		return nil, err
	}
	return enc.Fields(), nil
}

func (cmd MySQLCmd) FullTableName() string {
	return cmd.DB + "." + cmd.Table
}
<commit_msg>always use full table name<commit_after>package cmd

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"

	"h12.me/csv"
)

type MySQLCmd struct {
	Value      interface{}
	TagKey     string
	ExpandPath []string
	DB         string
	Engine     string
	Table      string
}

func (cmd MySQLCmd) CreateDB() string {
	return fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8;\n", cmd.DB)
}

func (cmd MySQLCmd) CreateTable() (string, error) {
	w := new(bytes.Buffer)
	fmt.Fprintf(w, "CREATE TABLE IF NOT EXISTS %s (\n", cmd.FullTableName())
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	var pks []string
	sort.Sort(fields)
	for _, field := range fields {
		if field.Tag.Get("PK") == "yes" {
			pks = append(pks, field.Name)
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	for _, field := range fields {
		if field.Tag.Get("PK") != "yes" {
			fmt.Fprintf(w, "\t%s %s,\n", field.Name, field.Tag.Get("TYPE"))
		}
	}
	fmt.Fprintf(w, "\tPRIMARY KEY (%s)\n", strings.Join(pks, ","))
	fmt.Fprintf(w, ") ENGINE=%s DEFAULT CHARSET=UTF8;\n", cmd.Engine)
	return w.String(), nil
}

func (cmd MySQLCmd) LoadData() (string, error) {
	fields, err := cmd.Fields()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("LOAD DATA LOCAL INFILE 'Reader::%[1]s' REPLACE INTO TABLE %[1]s (%s);\n", cmd.FullTableName(), strings.Join(fields.Names(), ", ")), nil
}

func (cmd MySQLCmd) Fields() (csv.Fields, error) {
	enc := csv.NewEncoder(ioutil.Discard).SetTagKey(cmd.TagKey).SetExpandPath(cmd.ExpandPath...)
	if err := enc.Encode(cmd.Value); err != nil {
		return nil, err
	}
	return enc.Fields(), nil
}

func (cmd MySQLCmd) FullTableName() string {
	return cmd.DB + "." + cmd.Table
}
<|endoftext|>