<commit_before>package command

import (
	"bufio"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"path/filepath"
	"strings"
)

type Command struct {
	// Our structured data/object for Command
	alias      string
	command    string
	verbose    bool
	workingdir string
}

const PATH_DRUSH = "/usr/local/bin/drush"

// Creates a new container for []Command objects
func NewDrushCommand() *Command {
	return &Command{}
}

// Changes all given values for a Drush command object.
func (drush *Command) Set(alias string, command string, verbose bool) {
	drush.alias = alias
	drush.command = command
	drush.verbose = verbose
	drush.workingdir = "."
}

// Returns the specified working directory used with executed Drush commands.
func (drush *Command) GetWorkingDir() string {
	return drush.workingdir
}

// Sets the specified working directory used with executed Drush commands.
func (drush *Command) SetWorkingDir(value string) {
	drush.workingdir = value
}

// Returns the alias used to executed Drush commands.
func (drush *Command) GetAlias() string {
	return drush.alias
}

// Changes the alias used to executed Drush commands.
func (drush *Command) SetAlias(value string) {
	drush.alias = value
}

// Returns the command string on executed Drush commands.
func (drush *Command) GetCommand() string {
	return drush.command
}

// Changes the command string on executed Drush commands.
func (drush *Command) SetCommand(value string) {
	drush.command = value
}

// Returns the verbosity setting on executed Drush commands.
func (drush *Command) GetVerbose() bool {
	return drush.verbose
}

// Changes the verbosity setting on executed Drush commands.
func (drush *Command) SetVerbose(value bool) {
	drush.verbose = value
}

// Returns, and prints the live output of the executing program
// This will wait for completion before proceeding.
func (drush *Command) LiveOutput() error {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)

	comm := new(exec.Cmd)
	comm = exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+PATH_DRUSH+" "+args)
	Pipe, _ := comm.StderrPipe()
	scanner := bufio.NewScanner(Pipe)
	go func() {
		for scanner.Scan() {
			if strings.Contains(scanner.Text(), "[error]") || strings.Contains(scanner.Text(), "[warning]") {
				log.Warnf("%s", scanner.Text())
			} else {
				log.Infof("%s", scanner.Text())
			}
		}
	}()
	err := comm.Start()
	err = comm.Wait()
	return err
}

// Gets the output from a single Command object, does not support []Command items.
func (drush *Command) Output() ([]string, error) {
	comm, err := drush.Run()
	response := filepath.SplitList(string(comm))
	return response, err
}

func (drush *Command) CombinedOutput() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	} else {
		comm, err := exec.Command("sh", "-c", PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	}
}

// Run an individual Command object, does not support []Command items.
func (drush *Command) Run() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	} else {
		comm, err := exec.Command("sh", "-c", PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	}
}

// Executes a database synchronisation task from a source to destination with the use of Drush.
func DrushDatabaseSync(srcAlias, destAlias string) {
	/* So our binary and this function combined support two-way traffic...  */
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("sql-sync @%v @%v -y", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Syncronised databases complete.")
	} else {
		log.Errorln("Could not syncronise databases.")
	}
}

// Executes a file synchronisation task from a source to destination with the use of Drush.
func DrushFilesSync(srcAlias, destAlias string) {
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%files @%v:%%files", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Synced public file system.")
	} else {
		log.Warnln("Public file system has not been synced.")
	}
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%private @%v:%%private", srcAlias, destAlias), true)
	_, err = x.Output()
	if err == nil {
		log.Infoln("Synced private file system.")
	} else {
		log.Warnln("Private file system has not been synced.")
	}
}

// Performs a cache clear task on an input site alias with the use of Drush.
func DrushClearCache(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "cc all", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not clear caches.", err)
	} else {
		log.Infoln("Caches cleared.")
	}
}

// Performs a registry rebuild task on an input site alias with the use of Drush.
func DrushRebuildRegistry(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "rr", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not rebuild registry.", err)
	} else {
		log.Infoln("Rebuilt registry.")
	}
}

// Performs a database update task on an input site alias with the use of Drush.
func DrushUpdateDatabase(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "updb -y", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not update database.", err)
	} else {
		log.Infoln("Updated database where possible.")
	}
}

// Performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToPath(path, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set("", "pm-download --yes "+project+" --destination="+path, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// Performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToAlias(alias, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "pm-download --yes "+project, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// Performs all drush commands associated to specifying a solr core.
func DrushSolrSetup(Alias, CoreName string) {

	DrushCommand := NewDrushCommand()
	DrushCommand.Set(Alias, `eval "module_load_include('inc', 'apachesolr', 'apachesolr.index');apachesolr_index_delete_index('$ENVID');"`, true)
	DrushCommandOut, DrushCommandErr := DrushCommand.Output()

	DrushCommand.Set(Alias, "vdel apachesolr_site_hash  --yes", true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()

	DrushCommand.Set(Alias, "vdel apachesolr_multisitesearch_metadata  --yes", true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()

	DrushCommand.Set(Alias, `eval "module_load_include('inc', 'apachesolr', 'apachesolr.index');apachesolr_site_hash();"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()

	//var Hash []string
	SolrHashCmd := NewDrushCommand()
	SolrHashCmd.Set(Alias, "vget apachesolr_site_hash", true)
	DrushCommandOut, DrushCommandErr = SolrHashCmd.Output()
	if DrushCommandErr != nil {
		log.Warnln("Could not obtain hash")
	} else {
		//Hash = DrushCommandOut
	}

	DrushCommand.Set(Alias, "vdel apachesolr_multisitesearch_metadata --yes", true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	//DrushCommand.Set(Alias, `eval apachesolr_environment_variable_set('$ENVID', 'multisitesearch_sites', array('$SITE_HASH'=>'`+Hash+`'));`, true)
	//DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	//log.Println(DrushCommandOut)

	DrushCommand.Set(Alias, `eval "apachesolr_environment_variable_set('$ENVID', 'multisitesearch', 1);"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	DrushCommand.Set(Alias, `eval ""module_load_include('inc', 'apachesolr_multisitesearch', 'apachesolr_multisitesearch.admin');apachesolr_multisitesearch_refresh_metadata_now();"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	DrushCommand.Set(Alias, `solr-set-env-url "http://localhost:8983/solr/drupal-`+CoreName+`"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	//drush "@${FULLNAME}"  ;
	//drush "@${SHORTNAME}.${LOCDOMAIN}" solr-index;
}

// Performs all drush commands associated to specifying a solr core.
func DrushSolrIndex() {
	//drush "@${FULLNAME}" solr-set-env-url "http://localhost:8983/solr/drupal-${SHORTNAME}";
	//drush "@${SHORTNAME}.${LOCDOMAIN}" solr-index;
}
<commit_msg>Remove the use of sh -c<commit_after>package command

import (
	"bufio"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"path/filepath"
	"strings"
)

type Command struct {
	// Our structured data/object for Command
	alias      string
	command    string
	verbose    bool
	workingdir string
}

const PATH_DRUSH = "/usr/local/bin/drush"

// Creates a new container for []Command objects
func NewDrushCommand() *Command {
	return &Command{}
}

// Changes all given values for a Drush command object.
func (drush *Command) Set(alias string, command string, verbose bool) {
	drush.alias = alias
	drush.command = command
	drush.verbose = verbose
	drush.workingdir = "."
}

// Returns the specified working directory used with executed Drush commands.
func (drush *Command) GetWorkingDir() string {
	return drush.workingdir
}

// Sets the specified working directory used with executed Drush commands.
func (drush *Command) SetWorkingDir(value string) {
	drush.workingdir = value
}

// Returns the alias used to executed Drush commands.
func (drush *Command) GetAlias() string {
	return drush.alias
}

// Changes the alias used to executed Drush commands.
func (drush *Command) SetAlias(value string) {
	drush.alias = value
}

// Returns the command string on executed Drush commands.
func (drush *Command) GetCommand() string {
	return drush.command
}

// Changes the command string on executed Drush commands.
func (drush *Command) SetCommand(value string) {
	drush.command = value
}

// Returns the verbosity setting on executed Drush commands.
func (drush *Command) GetVerbose() bool {
	return drush.verbose
}

// Changes the verbosity setting on executed Drush commands.
func (drush *Command) SetVerbose(value bool) {
	drush.verbose = value
}

// Returns, and prints the live output of the executing program
// This will wait for completion before proceeding.
func (drush *Command) LiveOutput() error {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)

	comm := new(exec.Cmd)
	comm = exec.Command("cd " + drush.workingdir + " && " + PATH_DRUSH + " " + args)
	Pipe, _ := comm.StderrPipe()
	scanner := bufio.NewScanner(Pipe)
	go func() {
		for scanner.Scan() {
			if strings.Contains(scanner.Text(), "[error]") || strings.Contains(scanner.Text(), "[warning]") {
				log.Warnf("%s", scanner.Text())
			} else {
				log.Infof("%s", scanner.Text())
			}
		}
	}()
	err := comm.Start()
	err = comm.Wait()
	return err
}

// Gets the output from a single Command object, does not support []Command items.
func (drush *Command) Output() ([]string, error) {
	comm, err := drush.Run()
	response := filepath.SplitList(string(comm))
	return response, err
}

func (drush *Command) CombinedOutput() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	} else {
		comm, err := exec.Command("sh", "-c", PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	}
}

// Run an individual Command object, does not support []Command items.
func (drush *Command) Run() ([]byte, error) {
	if strings.Contains(drush.alias, "@") == true {
		drush.alias = strings.Replace(drush.alias, "@", "", -1)
	}
	if drush.alias != "" {
		drush.alias = fmt.Sprintf("@%v", drush.alias)
	}
	if drush.verbose == true {
		drush.alias = fmt.Sprintf("%v --verbose", drush.alias)
	}
	args := fmt.Sprintf("%v %v", drush.alias, drush.command)
	if drush.GetWorkingDir() != "." {
		comm, err := exec.Command("sh", "-c", "cd "+drush.workingdir+" && "+PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	} else {
		comm, err := exec.Command("sh", "-c", PATH_DRUSH+" "+args).CombinedOutput()
		return comm, err
	}
}

// Executes a database synchronisation task from a source to destination with the use of Drush.
func DrushDatabaseSync(srcAlias, destAlias string) {
	/* So our binary and this function combined support two-way traffic...  */
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("sql-sync @%v @%v -y", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Syncronised databases complete.")
	} else {
		log.Errorln("Could not syncronise databases.")
	}
}

// Executes a file synchronisation task from a source to destination with the use of Drush.
func DrushFilesSync(srcAlias, destAlias string) {
	x := NewDrushCommand()
	srcAlias = strings.Replace(srcAlias, "@", "", -1)
	destAlias = strings.Replace(destAlias, "@", "", -1)
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%files @%v:%%files", srcAlias, destAlias), true)
	_, err := x.Output()
	if err == nil {
		log.Infoln("Synced public file system.")
	} else {
		log.Warnln("Public file system has not been synced.")
	}
	x.Set("", fmt.Sprintf("--yes rsync --exclude-other-sites --exclude-conf @%v:%%private @%v:%%private", srcAlias, destAlias), true)
	_, err = x.Output()
	if err == nil {
		log.Infoln("Synced private file system.")
	} else {
		log.Warnln("Private file system has not been synced.")
	}
}

// Performs a cache clear task on an input site alias with the use of Drush.
func DrushClearCache(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "cc all", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not clear caches.", err)
	} else {
		log.Infoln("Caches cleared.")
	}
}

// Performs a registry rebuild task on an input site alias with the use of Drush.
func DrushRebuildRegistry(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "rr", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not rebuild registry.", err)
	} else {
		log.Infoln("Rebuilt registry.")
	}
}

// Performs a database update task on an input site alias with the use of Drush.
func DrushUpdateDatabase(alias string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "updb -y", false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not update database.", err)
	} else {
		log.Infoln("Updated database where possible.")
	}
}

// Performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToPath(path, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set("", "pm-download --yes "+project+" --destination="+path, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// Performs a database update task on an input site alias with the use of Drush.
func DrushDownloadToAlias(alias, project string) {
	drushCommand := NewDrushCommand()
	drushCommand.Set(alias, "pm-download --yes "+project, false)
	_, err := drushCommand.Output()
	if err != nil {
		log.Warnln("Could not download module ", project, err)
	} else {
		log.Infoln("Downloaded module", project)
	}
}

// Performs all drush commands associated to specifying a solr core.
func DrushSolrSetup(Alias, CoreName string) {

	DrushCommand := NewDrushCommand()
	DrushCommand.Set(Alias, `eval "module_load_include('inc', 'apachesolr', 'apachesolr.index');apachesolr_index_delete_index('$ENVID');"`, true)
	DrushCommandOut, DrushCommandErr := DrushCommand.Output()

	DrushCommand.Set(Alias, "vdel apachesolr_site_hash  --yes", true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()

	DrushCommand.Set(Alias, "vdel apachesolr_multisitesearch_metadata  --yes", true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()

	DrushCommand.Set(Alias, `eval "module_load_include('inc', 'apachesolr', 'apachesolr.index');apachesolr_site_hash();"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()

	//var Hash []string
	SolrHashCmd := NewDrushCommand()
	SolrHashCmd.Set(Alias, "vget apachesolr_site_hash", true)
	DrushCommandOut, DrushCommandErr = SolrHashCmd.Output()
	if DrushCommandErr != nil {
		log.Warnln("Could not obtain hash")
	} else {
		//Hash = DrushCommandOut
	}

	DrushCommand.Set(Alias, "vdel apachesolr_multisitesearch_metadata --yes", true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	//DrushCommand.Set(Alias, `eval apachesolr_environment_variable_set('$ENVID', 'multisitesearch_sites', array('$SITE_HASH'=>'`+Hash+`'));`, true)
	//DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	//log.Println(DrushCommandOut)

	DrushCommand.Set(Alias, `eval "apachesolr_environment_variable_set('$ENVID', 'multisitesearch', 1);"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	DrushCommand.Set(Alias, `eval ""module_load_include('inc', 'apachesolr_multisitesearch', 'apachesolr_multisitesearch.admin');apachesolr_multisitesearch_refresh_metadata_now();"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	DrushCommand.Set(Alias, `solr-set-env-url "http://localhost:8983/solr/drupal-`+CoreName+`"`, true)
	DrushCommandOut, DrushCommandErr = DrushCommand.Output()
	log.Println(DrushCommandOut)

	//drush "@${FULLNAME}"  ;
	//drush "@${SHORTNAME}.${LOCDOMAIN}" solr-index;
}

// Performs all drush commands associated to specifying a solr core.
func DrushSolrIndex() {
	//drush "@${FULLNAME}" solr-set-env-url "http://localhost:8983/solr/drupal-${SHORTNAME}";
	//drush "@${SHORTNAME}.${LOCDOMAIN}" solr-index;
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/cli"
)

const (
	// updateWait is the amount of time to wait between status
	// updates. Because the monitor is poll-based, we use this
	// delay to avoid overwhelming the API server.
	updateWait = time.Second
)

// evalState is used to store the current "state of the world"
// in the context of monitoring an evaluation.
type evalState struct {
	status     string
	desc       string
	node       string
	deployment string
	job        string
	allocs     map[string]*allocState
	wait       time.Duration
	index      uint64
}

// newEvalState creates and initializes a new monitorState
func newEvalState() *evalState {
	return &evalState{
		status: structs.EvalStatusPending,
		allocs: make(map[string]*allocState),
	}
}

// allocState is used to track the state of an allocation
type allocState struct {
	id          string
	group       string
	node        string
	desired     string
	desiredDesc string
	client      string
	clientDesc  string
	index       uint64
}

// monitor wraps an evaluation monitor and holds metadata and
// state information.
type monitor struct {
	ui     cli.Ui
	client *api.Client
	state  *evalState

	// length determines the number of characters for identifiers in the ui.
	length int

	sync.Mutex
}

// newMonitor returns a new monitor. The returned monitor will
// write output information to the provided ui. The length parameter determines
// the number of characters for identifiers in the ui.
func newMonitor(ui cli.Ui, client *api.Client, length int) *monitor {
	mon := &monitor{
		ui: &cli.PrefixedUi{
			InfoPrefix:   "==> ",
			OutputPrefix: "    ",
			ErrorPrefix:  "==> ",
			Ui:           ui,
		},
		client: client,
		state:  newEvalState(),
		length: length,
	}
	return mon
}

// update is used to update our monitor with new state. It can be
// called whether the passed information is new or not, and will
// only dump update messages when state changes.
func (m *monitor) update(update *evalState) {
	m.Lock()
	defer m.Unlock()

	existing := m.state

	// Swap in the new state at the end
	defer func() {
		m.state = update
	}()

	// Check if the evaluation was triggered by a node
	if existing.node == "" && update.node != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by node %q",
			limit(update.node, m.length)))
	}

	// Check if the evaluation was triggered by a job
	if existing.job == "" && update.job != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by job %q", update.job))
	}

	// Check if the evaluation was triggered by a deployment
	if existing.deployment == "" && update.deployment != "" {
		m.ui.Output(fmt.Sprintf("Evaluation within deployment: %q", limit(update.deployment, m.length)))
	}

	// Check the allocations
	for allocID, alloc := range update.allocs {
		if existing, ok := existing.allocs[allocID]; !ok {
			switch {
			case alloc.index < update.index:
				// New alloc with create index lower than the eval
				// create index indicates modification
				m.ui.Output(fmt.Sprintf(
					"Allocation %q modified: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))

			case alloc.desired == structs.AllocDesiredStatusRun:
				// New allocation with desired status running
				m.ui.Output(fmt.Sprintf(
					"Allocation %q created: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))
			}
		} else {
			switch {
			case existing.client != alloc.client:
				description := ""
				if alloc.clientDesc != "" {
					description = fmt.Sprintf(" (%s)", alloc.clientDesc)
				}
				// Allocation status has changed
				m.ui.Output(fmt.Sprintf(
					"Allocation %q status changed: %q -> %q%s",
					limit(alloc.id, m.length), existing.client, alloc.client, description))
			}
		}
	}

	// Check if the status changed. We skip any transitions to pending status.
	if existing.status != "" &&
		update.status != structs.AllocClientStatusPending &&
		existing.status != update.status {
		m.ui.Output(fmt.Sprintf("Evaluation status changed: %q -> %q",
			existing.status, update.status))
	}
}

// monitor is used to start monitoring the given evaluation ID. It
// writes output directly to the monitor's ui, and returns the
// exit code for the command. If allowPrefix is false, monitor will only accept
// exact matching evalIDs.
//
// The return code will be 0 on successful evaluation. If there are
// problems scheduling the job (impossible constraints, resources
// exhausted, etc), then the return code will be 2. For any other
// failures (API connectivity, internal errors, etc), the return code
// will be 1.
func (m *monitor) monitor(evalID string, allowPrefix bool) int {
	// Track if we encounter a scheduling failure. This can only be
	// detected while querying allocations, so we use this bool to
	// carry that status into the return code.
	var schedFailure bool

	// The user may have specified a prefix as eval id. We need to lookup the
	// full id from the database first. Since we do this in a loop we need a
	// variable to keep track if we've already written the header message.
	var headerWritten bool

	// Add the initial pending state
	m.update(newEvalState())

	for {
		// Query the evaluation
		eval, _, err := m.client.Evaluations().Info(evalID, nil)
		if err != nil {
			if !allowPrefix {
				m.ui.Error(fmt.Sprintf("No evaluation with id %q found", evalID))
				return 1
			}
			if len(evalID) == 1 {
				m.ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
				return 1
			}

			evalID = sanitizeUUIDPrefix(evalID)
			evals, _, err := m.client.Evaluations().PrefixList(evalID)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
				return 1
			}
			if len(evals) == 0 {
				m.ui.Error(fmt.Sprintf("No evaluation(s) with prefix or id %q found", evalID))
				return 1
			}
			if len(evals) > 1 {
				// Format the evaluations
				out := make([]string, len(evals)+1)
				out[0] = "ID|Priority|Type|Triggered By|Status"
				for i, eval := range evals {
					out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%s",
						limit(eval.ID, m.length),
						eval.Priority,
						eval.Type,
						eval.TriggeredBy,
						eval.Status)
				}
				m.ui.Output(fmt.Sprintf("Prefix matched multiple evaluations\n\n%s", formatList(out)))
				return 0
			}
			// Prefix lookup matched a single evaluation
			eval, _, err = m.client.Evaluations().Info(evals[0].ID, nil)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
			}
		}

		if !headerWritten {
			m.ui.Info(fmt.Sprintf("Monitoring evaluation %q", limit(eval.ID, m.length)))
			headerWritten = true
		}

		// Create the new eval state.
		state := newEvalState()
		state.status = eval.Status
		state.desc = eval.StatusDescription
		state.node = eval.NodeID
		state.job = eval.JobID
		state.deployment = eval.DeploymentID
		state.wait = eval.Wait
		state.index = eval.CreateIndex

		// Query the allocations associated with the evaluation
		allocs, _, err := m.client.Evaluations().Allocations(eval.ID, nil)
		if err != nil {
			m.ui.Error(fmt.Sprintf("Error reading allocations: %s", err))
			return 1
		}

		// Add the allocs to the state
		for _, alloc := range allocs {
			state.allocs[alloc.ID] = &allocState{
				id:          alloc.ID,
				group:       alloc.TaskGroup,
				node:        alloc.NodeID,
				desired:     alloc.DesiredStatus,
				desiredDesc: alloc.DesiredDescription,
				client:      alloc.ClientStatus,
				clientDesc:  alloc.ClientDescription,
				index:       alloc.CreateIndex,
			}
		}

		// Update the state
		m.update(state)

		switch eval.Status {
		case structs.EvalStatusComplete, structs.EvalStatusFailed, structs.EvalStatusCancelled:
			if len(eval.FailedTGAllocs) == 0 {
				m.ui.Info(fmt.Sprintf("Evaluation %q finished with status %q",
					limit(eval.ID, m.length), eval.Status))
			} else {
				// There were failures making the allocations
				schedFailure = true
				m.ui.Info(fmt.Sprintf("Evaluation %q finished with status %q but failed to place all allocations:",
					limit(eval.ID, m.length), eval.Status))

				// Print the failures per task group
				for tg, metrics := range eval.FailedTGAllocs {
					noun := "allocation"
					if metrics.CoalescedFailures > 0 {
						noun += "s"
					}
					m.ui.Output(fmt.Sprintf("Task Group %q (failed to place %d %s):", tg, metrics.CoalescedFailures+1, noun))
					metrics := formatAllocMetrics(metrics, false, "  ")
					for _, line := range strings.Split(metrics, "\n") {
						m.ui.Output(line)
					}
				}

				if eval.BlockedEval != "" {
					m.ui.Output(fmt.Sprintf("Evaluation %q waiting for additional capacity to place remainder",
						limit(eval.BlockedEval, m.length)))
				}
			}
		default:
			// Wait for the next update
			time.Sleep(updateWait)
			continue
		}

		// Monitor the next eval in the chain, if present
		if eval.NextEval != "" {
			if eval.Wait.Nanoseconds() != 0 {
				m.ui.Info(fmt.Sprintf(
					"Monitoring next evaluation %q in %s",
					limit(eval.NextEval, m.length), eval.Wait))

				// Skip some unnecessary polling
				time.Sleep(eval.Wait)
			}

			// Reset the state and monitor the new eval
			m.state = newEvalState()
			return m.monitor(eval.NextEval, allowPrefix)
		}
		break
	}

	// Treat scheduling failures specially using a dedicated exit code.
	// This makes it easier to detect failures from the CLI.
	if schedFailure {
		return 2
	}

	return 0
}

func formatAllocMetrics(metrics *api.AllocationMetric, scores bool, prefix string) string {
	// Print a helpful message if we have an eligibility problem
	var out string
	if metrics.NodesEvaluated == 0 {
		out += fmt.Sprintf("%s* No nodes were eligible for evaluation\n", prefix)
	}

	// Print a helpful message if the user has asked for a DC that has no
	// available nodes.
	for dc, available := range metrics.NodesAvailable {
		if available == 0 {
			out += fmt.Sprintf("%s* No nodes are available in datacenter %q\n", prefix, dc)
		}
	}

	// Print filter info
	for class, num := range metrics.ClassFiltered {
		out += fmt.Sprintf("%s* Class %q filtered %d nodes\n", prefix, class, num)
	}
	for cs, num := range metrics.ConstraintFiltered {
		out += fmt.Sprintf("%s* Constraint %q filtered %d nodes\n", prefix, cs, num)
	}

	// Print exhaustion info
	if ne := metrics.NodesExhausted; ne > 0 {
		out += fmt.Sprintf("%s* Resources exhausted on %d nodes\n", prefix, ne)
	}
	for class, num := range metrics.ClassExhausted {
		out += fmt.Sprintf("%s* Class %q exhausted on %d nodes\n", prefix, class, num)
	}
	for dim, num := range metrics.DimensionExhausted {
		out += fmt.Sprintf("%s* Dimension %q exhausted on %d nodes\n", prefix, dim, num)
	}

	// Print quota info
	for _, dim := range metrics.QuotaExhausted {
		out += fmt.Sprintf("%s* Quota limit hit %q\n", prefix, dim)
	}

	// Print scores
	if scores {
		for name, score := range metrics.Scores {
			out += fmt.Sprintf("%s* Score %q = %f\n", prefix, name, score)
		}
	}

	out = strings.TrimSuffix(out, "\n")
	return out
}
<commit_msg>cli: remove info color on monitored output<commit_after>package command

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad/structs"
	"github.com/mitchellh/cli"
)

const (
	// updateWait is the amount of time to wait between status
	// updates. Because the monitor is poll-based, we use this
	// delay to avoid overwhelming the API server.
	updateWait = time.Second
)

// evalState is used to store the current "state of the world"
// in the context of monitoring an evaluation.
type evalState struct {
	status     string
	desc       string
	node       string
	deployment string
	job        string
	allocs     map[string]*allocState
	wait       time.Duration
	index      uint64
}

// newEvalState creates and initializes a new monitorState
func newEvalState() *evalState {
	return &evalState{
		status: structs.EvalStatusPending,
		allocs: make(map[string]*allocState),
	}
}

// allocState is used to track the state of an allocation
type allocState struct {
	id          string
	group       string
	node        string
	desired     string
	desiredDesc string
	client      string
	clientDesc  string
	index       uint64
}

// monitor wraps an evaluation monitor and holds metadata and
// state information.
type monitor struct {
	ui     cli.Ui
	client *api.Client
	state  *evalState

	// length determines the number of characters for identifiers in the ui.
	length int

	sync.Mutex
}

// newMonitor returns a new monitor. The returned monitor will
// write output information to the provided ui. The length parameter determines
// the number of characters for identifiers in the ui.
func newMonitor(ui cli.Ui, client *api.Client, length int) *monitor {
	if colorUi, ok := ui.(*cli.ColoredUi); ok {
		// Disable Info color for monitored output
		ui = &cli.ColoredUi{
			ErrorColor: colorUi.ErrorColor,
			WarnColor:  colorUi.WarnColor,
			InfoColor:  cli.UiColorNone,
			Ui:         colorUi.Ui,
		}
	}
	mon := &monitor{
		ui: &cli.PrefixedUi{
			InfoPrefix:   "==> ",
			OutputPrefix: "    ",
			ErrorPrefix:  "==> ",
			Ui:           ui,
		},
		client: client,
		state:  newEvalState(),
		length: length,
	}
	return mon
}

// update is used to update our monitor with new state. It can be
// called whether the passed information is new or not, and will
// only dump update messages when state changes.
func (m *monitor) update(update *evalState) {
	m.Lock()
	defer m.Unlock()

	existing := m.state

	// Swap in the new state at the end
	defer func() {
		m.state = update
	}()

	// Check if the evaluation was triggered by a node
	if existing.node == "" && update.node != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by node %q",
			limit(update.node, m.length)))
	}

	// Check if the evaluation was triggered by a job
	if existing.job == "" && update.job != "" {
		m.ui.Output(fmt.Sprintf("Evaluation triggered by job %q", update.job))
	}

	// Check if the evaluation was triggered by a deployment
	if existing.deployment == "" && update.deployment != "" {
		m.ui.Output(fmt.Sprintf("Evaluation within deployment: %q", limit(update.deployment, m.length)))
	}

	// Check the allocations
	for allocID, alloc := range update.allocs {
		if existing, ok := existing.allocs[allocID]; !ok {
			switch {
			case alloc.index < update.index:
				// New alloc with create index lower than the eval
				// create index indicates modification
				m.ui.Output(fmt.Sprintf(
					"Allocation %q modified: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))

			case alloc.desired == structs.AllocDesiredStatusRun:
				// New allocation with desired status running
				m.ui.Output(fmt.Sprintf(
					"Allocation %q created: node %q, group %q",
					limit(alloc.id, m.length), limit(alloc.node, m.length), alloc.group))
			}
		} else {
			switch {
			case existing.client != alloc.client:
				description := ""
				if alloc.clientDesc != "" {
					description = fmt.Sprintf(" (%s)", alloc.clientDesc)
				}
				// Allocation status has changed
				m.ui.Output(fmt.Sprintf(
					"Allocation %q status changed: %q -> %q%s",
					limit(alloc.id, m.length), existing.client, alloc.client, description))
			}
		}
	}

	// Check if the status changed. We skip any transitions to pending status.
	if existing.status != "" &&
		update.status != structs.AllocClientStatusPending &&
		existing.status != update.status {
		m.ui.Output(fmt.Sprintf("Evaluation status changed: %q -> %q",
			existing.status, update.status))
	}
}

// monitor is used to start monitoring the given evaluation ID. It
// writes output directly to the monitor's ui, and returns the
// exit code for the command. If allowPrefix is false, monitor will only accept
// exact matching evalIDs.
//
// The return code will be 0 on successful evaluation. If there are
// problems scheduling the job (impossible constraints, resources
// exhausted, etc), then the return code will be 2. For any other
// failures (API connectivity, internal errors, etc), the return code
// will be 1.
func (m *monitor) monitor(evalID string, allowPrefix bool) int {
	// Track if we encounter a scheduling failure. This can only be
	// detected while querying allocations, so we use this bool to
	// carry that status into the return code.
	var schedFailure bool

	// The user may have specified a prefix as eval id. We need to lookup the
	// full id from the database first. Since we do this in a loop we need a
	// variable to keep track if we've already written the header message.
	var headerWritten bool

	// Add the initial pending state
	m.update(newEvalState())

	for {
		// Query the evaluation
		eval, _, err := m.client.Evaluations().Info(evalID, nil)
		if err != nil {
			if !allowPrefix {
				m.ui.Error(fmt.Sprintf("No evaluation with id %q found", evalID))
				return 1
			}
			if len(evalID) == 1 {
				m.ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
				return 1
			}

			evalID = sanitizeUUIDPrefix(evalID)
			evals, _, err := m.client.Evaluations().PrefixList(evalID)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
				return 1
			}
			if len(evals) == 0 {
				m.ui.Error(fmt.Sprintf("No evaluation(s) with prefix or id %q found", evalID))
				return 1
			}
			if len(evals) > 1 {
				// Format the evaluations
				out := make([]string, len(evals)+1)
				out[0] = "ID|Priority|Type|Triggered By|Status"
				for i, eval := range evals {
					out[i+1] = fmt.Sprintf("%s|%d|%s|%s|%s",
						limit(eval.ID, m.length),
						eval.Priority,
						eval.Type,
						eval.TriggeredBy,
						eval.Status)
				}
				m.ui.Output(fmt.Sprintf("Prefix matched multiple evaluations\n\n%s", formatList(out)))
				return 0
			}
			// Prefix lookup matched a single evaluation
			eval, _, err = m.client.Evaluations().Info(evals[0].ID, nil)
			if err != nil {
				m.ui.Error(fmt.Sprintf("Error reading evaluation: %s", err))
			}
		}

		if !headerWritten {
			m.ui.Info(fmt.Sprintf("Monitoring evaluation %q", limit(eval.ID, m.length)))
			headerWritten = true
		}

		// Create the new eval state.
		state := newEvalState()
		state.status = eval.Status
		state.desc = eval.StatusDescription
		state.node = eval.NodeID
		state.job = eval.JobID
		state.deployment = eval.DeploymentID
		state.wait = eval.Wait
		state.index = eval.CreateIndex

		// Query the allocations associated with the evaluation
		allocs, _, err := m.client.Evaluations().Allocations(eval.ID, nil)
		if err != nil {
			m.ui.Error(fmt.Sprintf("Error reading allocations: %s", err))
			return 1
		}

		// Add the allocs to the state
		for _, alloc := range allocs {
			state.allocs[alloc.ID] = &allocState{
				id:          alloc.ID,
				group:       alloc.TaskGroup,
				node:        alloc.NodeID,
				desired:     alloc.DesiredStatus,
				desiredDesc: alloc.DesiredDescription,
				client:      alloc.ClientStatus,
				clientDesc:  alloc.ClientDescription,
				index:       alloc.CreateIndex,
			}
		}

		// Update the state
		m.update(state)

		switch eval.Status {
		case structs.EvalStatusComplete, structs.EvalStatusFailed, structs.EvalStatusCancelled:
			if len(eval.FailedTGAllocs) == 0 {
				m.ui.Info(fmt.Sprintf("Evaluation %q finished with status %q",
					limit(eval.ID, m.length), eval.Status))
			} else {
				// There were failures making the allocations
				schedFailure = true
				m.ui.Info(fmt.Sprintf("Evaluation %q finished with status %q but failed to place all allocations:",
					limit(eval.ID, m.length), eval.Status))

				// Print the failures per task group
				for tg, metrics := range eval.FailedTGAllocs {
					noun := "allocation"
					if metrics.CoalescedFailures > 0 {
						noun += "s"
					}
					m.ui.Output(fmt.Sprintf("Task Group %q (failed to place %d %s):", tg, metrics.CoalescedFailures+1, noun))
					metrics := formatAllocMetrics(metrics, false, "  ")
					for _, line := range strings.Split(metrics, "\n") {
						m.ui.Output(line)
					}
				}

				if eval.BlockedEval != "" {
					m.ui.Output(fmt.Sprintf("Evaluation %q waiting for additional capacity to place remainder",
						limit(eval.BlockedEval, m.length)))
				}
			}
		default:
			// Wait for the next update
			time.Sleep(updateWait)
			continue
		}

		// Monitor the next eval in the chain, if present
		if eval.NextEval != "" {
			if eval.Wait.Nanoseconds() != 0 {
				m.ui.Info(fmt.Sprintf(
					"Monitoring next evaluation %q in %s",
					limit(eval.NextEval, m.length), eval.Wait))

				// Skip some unnecessary polling
				time.Sleep(eval.Wait)
			}

			// Reset the state and monitor the new eval
			m.state = newEvalState()
			return m.monitor(eval.NextEval, allowPrefix)
		}
		break
	}

	// Treat scheduling failures specially using a dedicated exit code.
	// This makes it easier to detect failures from the CLI.
	if schedFailure {
		return 2
	}

	return 0
}

func formatAllocMetrics(metrics *api.AllocationMetric, scores bool, prefix string) string {
	// Print a helpful message if we have an eligibility problem
	var out string
	if metrics.NodesEvaluated == 0 {
		out += fmt.Sprintf("%s* No nodes were eligible for evaluation\n", prefix)
	}

	// Print a helpful message if the user has asked for a DC that has no
	// available nodes.
	for dc, available := range metrics.NodesAvailable {
		if available == 0 {
			out += fmt.Sprintf("%s* No nodes are available in datacenter %q\n", prefix, dc)
		}
	}

	// Print filter info
	for class, num := range metrics.ClassFiltered {
		out += fmt.Sprintf("%s* Class %q filtered %d nodes\n", prefix, class, num)
	}
	for cs, num := range metrics.ConstraintFiltered {
		out += fmt.Sprintf("%s* Constraint %q filtered %d nodes\n", prefix, cs, num)
	}

	// Print exhaustion info
	if ne := metrics.NodesExhausted; ne > 0 {
		out += fmt.Sprintf("%s* Resources exhausted on %d nodes\n", prefix, ne)
	}
	for class, num := range metrics.ClassExhausted {
		out += fmt.Sprintf("%s* Class %q exhausted on %d nodes\n", prefix, class, num)
	}
	for dim, num := range metrics.DimensionExhausted {
		out += fmt.Sprintf("%s* Dimension %q exhausted on %d nodes\n", prefix, dim, num)
	}

	// Print quota info
	for _, dim := range metrics.QuotaExhausted {
		out += fmt.Sprintf("%s* Quota limit hit %q\n", prefix, dim)
	}

	// Print scores
	if scores {
		for name, score := range metrics.Scores {
			out += fmt.Sprintf("%s* Score %q = %f\n", prefix, name, score)
		}
	}

	out = strings.TrimSuffix(out, "\n")
	return out
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"os"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
)

var (
	colorErr = color.New(color.FgRed).SprintFunc()("Error")

	// errAction specifies what should happen when an error occurs
	errAction = func() {
		os.Exit(1)
	}
)

func checkErr(err error, cmd ...*cobra.Command) {
	if err == nil {
		return
	}

	if len(cmd) > 0 {
		cmd[0].Help()
	}

	fmt.Fprintf(color.Output, "\n%s: %v\n", colorErr, err)

	errAction()
}
<commit_msg>only print json errors with json output<commit_after>package commands

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	colorErr = color.New(color.FgRed).SprintFunc()("Error")

	// errAction specifies what should happen when an error occurs
	errAction = func() {
		os.Exit(1)
	}
)

type outputErrors struct {
	Errors []outputError `json:"errors"`
}

type outputError struct {
	Detail string `json:"detail"`
}

func checkErr(err error, cmd ...*cobra.Command) {
	if err == nil {
		return
	}

	output := viper.GetString("output")

	switch output {
	default:
		if len(cmd) > 0 {
			cmd[0].Help()
		}
		fmt.Fprintf(color.Output, "\n%s: %v\n", colorErr, err)
	case "json":
		es := outputErrors{
			Errors: []outputError{
				{Detail: err.Error()},
			},
		}

		b, _ := json.Marshal(&es)
		fmt.Println(string(b))
	}

	errAction()
}
<|endoftext|>


<commit_before>// Copyright 2015 The tgbot 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 commands

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"regexp"
	"strings"
)

type cmdQuotes struct {
	description string
	syntax      string
	re          *regexp.Regexp
	w           io.Writer
	config      QuotesConfig
}

type QuotesConfig struct {
	Enabled  bool
	Endpoint string
	User     string
	Password string
}

func NewCmdQuotes(w io.Writer, config QuotesConfig) Command {
	return &cmdQuotes{
		syntax:      "!q [message]",
		description: "If message, add a quote. Otherwise, return a random one",
		re:          regexp.MustCompile(`^!q($| .+$)`),
		w:           w,
		config:      config,
	}
}

func (cmd *cmdQuotes) Enabled() bool {
	return cmd.config.Enabled
}

func (cmd *cmdQuotes) Syntax() string {
	return cmd.syntax
}

func (cmd *cmdQuotes) Description() string {
	return cmd.description
}

func (cmd *cmdQuotes) Match(text string) bool {
	return cmd.re.MatchString(text)
}

func (cmd *cmdQuotes) Run(title, from, text string) error {
	var (
		msg string
		err error
	)

	quoteText := strings.TrimSpace(strings.TrimPrefix(text, "!q"))
	if quoteText == "" {
		msg, err = cmd.randomQuote(title)
	} else {
		msg, err = cmd.addQuote(title, quoteText)
	}
	if err != nil {
		fmt.Fprintf(cmd.w, "msg %v error: cannot get or send quote\n", title)
		return err
	}

	fmt.Fprintf(cmd.w, "msg %v %v\n", title, msg)
	return nil
}

func (cmd *cmdQuotes) Shutdown() error {
	return nil
}

func (cmd *cmdQuotes) randomQuote(title string) (msg string, err error) {
	req, err := http.NewRequest("GET", cmd.config.Endpoint, nil)
	if err != nil {
		return "", err
	}
	req.SetBasicAuth(cmd.config.User, cmd.config.Password)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	res, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return "", fmt.Errorf("cannot get quote (%v)", res.StatusCode)
	}

	quotes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	lines := strings.Split(string(quotes), "\n")
	if len(lines) <= 1 { // If there aren't quotes, lines == []string{""}
		return "", errors.New("no quotes")
	}

	rndInt := rand.Intn(len(lines) - 1)
	rndQuote := lines[rndInt]

	return fmt.Sprintf("Random quote: %v", rndQuote), nil
}

func (cmd *cmdQuotes) addQuote(title string, text string) (msg string, err error) {
	r := strings.NewReader(text)
	req, err := http.NewRequest("POST", cmd.config.Endpoint, r)
	if err != nil {
		return "", err
	}
	req.SetBasicAuth(cmd.config.User, cmd.config.Password)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	res, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return "", fmt.Errorf("cannot add quote (%v - %v: %v)", res.StatusCode, title, text)
	}

	return fmt.Sprintf("New quote added: %v", text), nil
}
<commit_msg>Quote search support added<commit_after>// Copyright 2015 The tgbot 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 commands

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"regexp"
	"strings"
)

type cmdQuotes struct {
	description string
	syntax      string
	re          *regexp.Regexp
	w           io.Writer
	config      QuotesConfig
}

type QuotesConfig struct {
	Enabled  bool
	Endpoint string
	User     string
	Password string
}

func NewCmdQuotes(w io.Writer, config QuotesConfig) Command {
	return &cmdQuotes{
		syntax:      "!q(a) [search|addquote]",
		description: "Return a random quote. If search is defined, a random quote matching with the search pattern will be returned. If addquote is defined, a new quote will be added",
		re:          regexp.MustCompile(`^!q|qa($| .+$)`),
		w:           w,
		config:      config,
	}
}

func (cmd *cmdQuotes) Enabled() bool {
	return cmd.config.Enabled
}

func (cmd *cmdQuotes) Syntax() string {
	return cmd.syntax
}

func (cmd *cmdQuotes) Description() string {
	return cmd.description
}

func (cmd *cmdQuotes) Match(text string) bool {
	return cmd.re.MatchString(text)
}

func (cmd *cmdQuotes) Run(title, from, text string) error {
	var (
		msg string
		err error
	)

	if strings.HasPrefix(text,"!qa ") {
		quoteText := strings.TrimSpace(strings.TrimPrefix(text, "!qa"))
		if quoteText != "" {
			msg, err = cmd.addQuote(title, quoteText)
		}
	} else {
       		quoteText := strings.TrimSpace(strings.TrimPrefix(text, "!q"))
		if quoteText == "" {
			msg, err = cmd.randomQuote(title)
        	} else {
			msg, err = cmd.searchQuote(title, quoteText)
        	}
	}

	if err != nil {
		fmt.Fprintf(cmd.w, "msg %v error: cannot get or send quote\n", title)
		return err
	}

	fmt.Fprintf(cmd.w, "msg %v %v\n", title, msg)
	return nil
}

func (cmd *cmdQuotes) Shutdown() error {
	return nil
}

func (cmd *cmdQuotes) randomQuote(title string) (msg string, err error) {
	req, err := http.NewRequest("GET", cmd.config.Endpoint, nil)
	if err != nil {
		return "", err
	}
	req.SetBasicAuth(cmd.config.User, cmd.config.Password)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	res, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return "", fmt.Errorf("cannot get quote (%v)", res.StatusCode)
	}

	quotes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}
	lines := strings.Split(string(quotes), "\n")
	if len(lines) <= 1 { // If there aren't quotes, lines == []string{""}
		return "", errors.New("no quotes")
	}

	rndInt := rand.Intn(len(lines) - 1)
	rndQuote := lines[rndInt]

	return fmt.Sprintf("Random quote: %v", rndQuote), nil
}

func (cmd *cmdQuotes) searchQuote(title string, text string) (msg string, err error) {
        req, err := http.NewRequest("GET", cmd.config.Endpoint, nil)
        if err != nil {
                return "", err
        }
        req.SetBasicAuth(cmd.config.User, cmd.config.Password)
        tr := &http.Transport{
                TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        }
        client := &http.Client{Transport: tr}
        res, err := client.Do(req)
        if err != nil {
                return "", err
        }
        defer res.Body.Close()

        if res.StatusCode != http.StatusOK {
                return "", fmt.Errorf("cannot get quote (%v)", res.StatusCode)
        }

        quotes, err := ioutil.ReadAll(res.Body)
        if err != nil {
                return "", err
        }
	filterWords := strings.Fields(text)
        lines := strings.Split(string(quotes), "\n")

	linesFiltered := make([]string, 0)
	for _, line := range lines {
		if strings.Contains(strings.ToLower(line), strings.ToLower(filterWords[0])) {
			linesFiltered = append (linesFiltered, line)
		}
	}

        if len(linesFiltered) < 1 { // If there aren't quotes, linesFiltered == []string{""}
                return "", errors.New("no quotes")
        }

	rndInt := 0
	if len(linesFiltered) > 1  {
		rndInt = rand.Intn(len(linesFiltered) - 1)
	}
	rndQuote := linesFiltered[rndInt]
        return fmt.Sprintf("Searched quote: %v", rndQuote), nil
}

func (cmd *cmdQuotes) addQuote(title string, text string) (msg string, err error) {
	r := strings.NewReader(text)
	req, err := http.NewRequest("POST", cmd.config.Endpoint, r)
	if err != nil {
		return "", err
	}
	req.SetBasicAuth(cmd.config.User, cmd.config.Password)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	res, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		return "", fmt.Errorf("cannot add quote (%v - %v: %v)", res.StatusCode, title, text)
	}

	return fmt.Sprintf("New quote added: %v", text), nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 WALLIX

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package commands

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/wallix/awless/aws/services"
	"github.com/wallix/awless/config"
	"github.com/wallix/awless/logger"
)

var (
	showIdsOnlyFlag, showIdOnlyFlag bool
)

func init() {
	RootCmd.AddCommand(searchCmd)

	awsImagesCmd.Flags().BoolVar(&showIdsOnlyFlag, "ids-only", false, "Returns only the id of all AMIs matching your query")
	awsImagesCmd.Flags().BoolVar(&showIdOnlyFlag, "id-only", false, "Returns only one (the latest) AMI id matching your query")

	searchCmd.AddCommand(awsImagesCmd)
}

var searchCmd = &cobra.Command{
	Use:   "search",
	Short: "Perform various searches and resolution",
}

var awsImagesCmd = &cobra.Command{
	Use:               "images",
	PersistentPreRun:  applyHooks(initAwlessEnvHook, initLoggerHook, initCloudServicesHook, firstInstallDoneHook),
	PersistentPostRun: applyHooks(networkMonitorHook),
	Short:             fmt.Sprintf("Find corresponding images according to an image query, ordering by latest first. Supported owners: %s", strings.Join(awsservices.SupportedAMIOwners, ", ")),
	Long:              fmt.Sprintf("Find corresponding images according to an image query, ordering by latest first.\n\nQuery string specification is the following column separated format:\n\n\t\t%s\n\nEverything optional expect for the 'owner'. Supported owners: %s", awsservices.ImageQuerySpec, strings.Join(awsservices.SupportedAMIOwners, ", ")),
	Example:           "  awless search images redhat:rhel:7.2\n  awless search images debian::jessie\n  awless search images canonical --id-only\n  awless search images amazonlinux:::::instance-store --ids-only",

	Run: func(cmd *cobra.Command, args []string) {
		if len(args) < 1 {
			exitOn(fmt.Errorf("expecting image query string. Expecting: %s (with everything optional expect for the owner)", awsservices.ImageQuerySpec))
		}

		resolver := &awsservices.ImageResolver{InfraService: awsservices.InfraService.(*awsservices.Infra)}

		query, err := awsservices.ParseImageQuery(args[0])
		exitOn(err)

		logger.Infof("launching search for image in '%s' region. Query: '%s'", config.GetAWSRegion(), query)
		imgs, err := resolver.Resolve(query)
		exitOn(err)

		var ids []string
		for _, img := range imgs {
			ids = append(ids, img.Id)
		}

		if showIdsOnlyFlag {
			for _, id := range ids {
				fmt.Println(id)
			}
			return
		}

		if showIdOnlyFlag {
			for i, id := range ids {
				fmt.Println(id)
				if i == 0 {
					break
				}
			}
			return
		}

		b, err := json.MarshalIndent(imgs, "", " ")
		exitOn(err)

		fmt.Fprintln(os.Stdout, string(b))
	},
}
<commit_msg>Image search: deprecate flag `id-only` for `latest-id`<commit_after>/*
Copyright 2017 WALLIX

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package commands

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"github.com/wallix/awless/aws/services"
	"github.com/wallix/awless/config"
	"github.com/wallix/awless/logger"
)

var (
	showIdsOnlyFlag, showIdOnlyFlag, showLatestIdOnly bool
)

func init() {
	RootCmd.AddCommand(searchCmd)

	awsImagesCmd.Flags().BoolVar(&showLatestIdOnly, "latest-id", false, "Returns the id only of the latest AMI matching your query")
	awsImagesCmd.Flags().BoolVar(&showIdOnlyFlag, "id-only", false, "(DEPRECATED, use latest-id) Returns only one (the latest) AMI id matching the query")

	searchCmd.AddCommand(awsImagesCmd)
}

var searchCmd = &cobra.Command{
	Use:   "search",
	Short: "Perform various searches and resolution",
}

var awsImagesCmd = &cobra.Command{
	Use:               "images",
	PersistentPreRun:  applyHooks(initAwlessEnvHook, initLoggerHook, initCloudServicesHook, firstInstallDoneHook),
	PersistentPostRun: applyHooks(networkMonitorHook),
	Short:             fmt.Sprintf("Find corresponding bare images according to a bare image query, ordering by latest first. Supported owners: %s", strings.Join(awsservices.SupportedAMIOwners, ", ")),
	Long:              fmt.Sprintf("Find corresponding bare images according to a bare image query, ordering by latest first.\n\nQuery string specification is the following column separated format:\n\n\t\t%s\n\nEverything optional expect for the 'owner'. Supported owners: %s", awsservices.ImageQuerySpec, strings.Join(awsservices.SupportedAMIOwners, ", ")),
	Example:           "  awless search images redhat:rhel:7.2\n  awless search images debian::jessie\n  awless search images canonical --latest-id\n  awless search images amazonlinux:::::instance-store",

	Run: func(cmd *cobra.Command, args []string) {
		if len(args) < 1 {
			exitOn(fmt.Errorf("expecting image query string. Expecting: %s (with everything optional expect for the owner)", awsservices.ImageQuerySpec))
		}

		resolver := &awsservices.ImageResolver{InfraService: awsservices.InfraService.(*awsservices.Infra)}

		query, err := awsservices.ParseImageQuery(args[0])
		exitOn(err)

		logger.Infof("launching search for image in '%s' region. Query: '%s'", config.GetAWSRegion(), query)
		imgs, err := resolver.Resolve(query)
		exitOn(err)

		var ids []string
		for _, img := range imgs {
			ids = append(ids, img.Id)
		}

		if showIdsOnlyFlag {
			for _, id := range ids {
				fmt.Println(id)
			}
			return
		}

		if showLatestIdOnly || showIdOnlyFlag {
			for i, id := range ids {
				fmt.Println(id)
				if i == 0 {
					break
				}
			}
			return
		}

		b, err := json.MarshalIndent(imgs, "", " ")
		exitOn(err)

		fmt.Fprintln(os.Stdout, string(b))
	},
}
<|endoftext|>


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

package cl

import (
	"go/constant"
	"go/types"
	"log"
	"math/big"
	"reflect"
	"strconv"

	"github.com/goplus/gop/ast"
	"github.com/goplus/gop/token"
	"github.com/goplus/gox"
)

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

func toFuncType(ctx *blockCtx, typ *ast.FuncType, recv *types.Var) *types.Signature {
	params, variadic := toParams(ctx, typ.Params.List)
	results := toResults(ctx, typ.Results)
	return types.NewSignature(recv, params, results, variadic)
}

func toRecv(ctx *blockCtx, recv *ast.FieldList) *types.Var {
	v := recv.List[0]
	var name string
	if len(v.Names) > 0 {
		name = v.Names[0].Name
	}
	return ctx.pkg.NewParam(v.Pos(), name, toType(ctx, v.Type))
}

func getRecvTypeName(ctx *pkgCtx, recv *ast.FieldList, handleErr bool) (string, bool) {
	typ := recv.List[0].Type
	if t, ok := typ.(*ast.StarExpr); ok {
		typ = t.X
	}
	if t, ok := typ.(*ast.Ident); ok {
		return t.Name, true
	}
	if handleErr {
		src, pos := ctx.LoadExpr(typ)
		ctx.handleCodeErrorf(&pos, "invalid receiver type %v (%v is not a defined type)", src, src)
	}
	return "", false
}

func toResults(ctx *blockCtx, in *ast.FieldList) *types.Tuple {
	if in == nil {
		return nil
	}
	flds := in.List
	n := len(flds)
	args := make([]*types.Var, 0, n)
	for _, fld := range flds {
		args = toParam(ctx, fld, args)
	}
	return types.NewTuple(args...)
}

func toParams(ctx *blockCtx, flds []*ast.Field) (typ *types.Tuple, variadic bool) {
	n := len(flds)
	if n == 0 {
		return nil, false
	}
	args := make([]*types.Var, 0, n)
	for _, fld := range flds {
		args = toParam(ctx, fld, args)
	}
	_, ok := flds[n-1].Type.(*ast.Ellipsis)
	return types.NewTuple(args...), ok
}

func toParam(ctx *blockCtx, fld *ast.Field, args []*gox.Param) []*gox.Param {
	typ := toType(ctx, fld.Type)
	pkg := ctx.pkg
	if len(fld.Names) == 0 {
		return append(args, pkg.NewParam(fld.Pos(), "", typ))
	}
	for _, name := range fld.Names {
		args = append(args, pkg.NewParam(name.Pos(), name.Name, typ))
	}
	return args
}

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

func toType(ctx *blockCtx, typ ast.Expr) types.Type {
	switch v := typ.(type) {
	case *ast.Ident:
		return toIdentType(ctx, v)
	case *ast.StarExpr:
		elem := toType(ctx, v.X)
		return types.NewPointer(elem)
	case *ast.ArrayType:
		return toArrayType(ctx, v)
	case *ast.InterfaceType:
		return toInterfaceType(ctx, v)
	case *ast.Ellipsis:
		elem := toType(ctx, v.Elt)
		return types.NewSlice(elem)
	case *ast.MapType:
		return toMapType(ctx, v)
	case *ast.StructType:
		return toStructType(ctx, v)
	case *ast.ChanType:
		return toChanType(ctx, v)
	case *ast.FuncType:
		return toFuncType(ctx, v, nil)
	case *ast.SelectorExpr:
		return toExternalType(ctx, v)
	case *ast.ParenExpr:
		return toType(ctx, v.X)
	}
	log.Panicln("toType: unknown -", reflect.TypeOf(typ))
	return nil
}

var (
	typesChanDirs = [...]types.ChanDir{
		ast.RECV:            types.RecvOnly,
		ast.SEND:            types.SendOnly,
		ast.SEND | ast.RECV: types.SendRecv,
	}
)

func toChanType(ctx *blockCtx, v *ast.ChanType) *types.Chan {
	return types.NewChan(typesChanDirs[v.Dir], toType(ctx, v.Value))
}

func toExternalType(ctx *blockCtx, v *ast.SelectorExpr) types.Type {
	name := v.X.(*ast.Ident).Name
	if pr, ok := ctx.findImport(name); ok {
		o := pr.TryRef(v.Sel.Name)
		if t, ok := o.(*types.TypeName); ok {
			return t.Type()
		}
		panic(ctx.newCodeErrorf(v.Pos(), "%s.%s is not a type", name, v.Sel.Name))
	}
	panic(ctx.newCodeErrorf(v.Pos(), "undefined: %s", name))
}

/*-----------------------------------------------------------------------------

Name context:
- type
- pkgRef.type
- spx.type

// ---------------------------------------------------------------------------*/

func toIdentType(ctx *blockCtx, ident *ast.Ident) types.Type {
	v, builtin := lookupType(ctx, ident.Name)
	if isBuiltin(builtin) {
		panic(ctx.newCodeErrorf(ident.Pos(), "use of builtin %s not in function call", ident.Name))
	}
	if t, ok := v.(*types.TypeName); ok {
		return t.Type()
	}
	if v, _ := lookupPkgRef(ctx, nil, ident); v != nil {
		if t, ok := v.(*types.TypeName); ok {
			return t.Type()
		}
	}
	panic(ctx.newCodeErrorf(ident.Pos(), "%s is not a type", ident.Name))
}

// TODO: optimization
func lookupType(ctx *blockCtx, name string) (types.Object, types.Object) {
	at, o := ctx.cb.Scope().LookupParent(name, token.NoPos)
	if o != nil && at != types.Universe {
		if debugLookup {
			log.Println("==> LookupParent", name, "=>", o)
		}
		return o, nil
	}
	if ctx.loadSymbol(name) {
		if v := ctx.pkg.Types.Scope().Lookup(name); v != nil {
			if debugLookup {
				log.Println("==> Lookup (LoadSymbol)", name, "=>", v)
			}
			return v, nil
		}
	}
	if obj := ctx.pkg.Builtin().TryRef(name); obj != nil {
		return obj, o
	}
	return o, o
}

func toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {
	pkg := ctx.pkg.Types
	fieldList := v.Fields.List
	fields := make([]*types.Var, 0, len(fieldList))
	tags := make([]string, 0, len(fieldList))
	names := make(map[string]token.Pos)
	chkRedecl := func(name string, pos token.Pos) bool {
		if opos, ok := names[name]; ok {
			npos := ctx.Position(pos)
			ctx.handleCodeErrorf(&npos, "%v redeclared\n\t%v other declaration of %v",
				name, ctx.Position(opos), name)
			return true
		}
		names[name] = pos
		return false
	}
	for _, field := range fieldList {
		typ := toType(ctx, field.Type)
		if field.Names == nil { // embedded
			name := getTypeName(typ)
			if chkRedecl(name, field.Type.Pos()) {
				continue
			}
			fld := types.NewField(token.NoPos, pkg, name, typ, true)
			fields = append(fields, fld)
			tags = append(tags, toFieldTag(field.Tag))
			continue
		}
		for _, name := range field.Names {
			if chkRedecl(name.Name, name.NamePos) {
				continue
			}
			fld := types.NewField(token.NoPos, pkg, name.Name, typ, false)
			fields = append(fields, fld)
			tags = append(tags, toFieldTag(field.Tag))
		}
	}
	return types.NewStruct(fields, tags)
}

func toFieldTag(v *ast.BasicLit) string {
	if v != nil {
		tag, err := strconv.Unquote(v.Value)
		if err != nil {
			log.Panicln("TODO: toFieldTag -", err)
		}
		return tag
	}
	return ""
}

func getTypeName(typ types.Type) string {
	if t, ok := typ.(*types.Pointer); ok {
		typ = t.Elem()
	}
	switch t := typ.(type) {
	case *types.Named:
		return t.Obj().Name()
	case *types.Basic:
		return t.Name()
	default:
		panic("TODO: getTypeName")
	}
}

func toMapType(ctx *blockCtx, v *ast.MapType) *types.Map {
	key := toType(ctx, v.Key)
	val := toType(ctx, v.Value)
	return types.NewMap(key, val)
}

func toArrayType(ctx *blockCtx, v *ast.ArrayType) types.Type {
	elem := toType(ctx, v.Elt)
	if v.Len == nil {
		return types.NewSlice(elem)
	}
	if _, ok := v.Len.(*ast.Ellipsis); ok {
		return types.NewArray(elem, -1) // A negative length indicates an unknown length
	}
	return types.NewArray(elem, toInt64(ctx, v.Len, "non-constant array bound %s"))
}

func toInt64(ctx *blockCtx, e ast.Expr, emsg string) int64 {
	cb := ctx.pkg.ConstStart()
	compileExpr(ctx, e)
	tv := cb.EndConst()
	if val := tv.CVal; val != nil {
		if val.Kind() == constant.Float {
			if v, ok := constant.Val(val).(*big.Rat); ok && v.IsInt() {
				return v.Num().Int64()
			}
		} else if v, ok := constant.Int64Val(val); ok {
			return v
		}
	}
	src, pos := ctx.LoadExpr(e)
	panic(newCodeErrorf(&pos, emsg, src))
}

func toInterfaceType(ctx *blockCtx, v *ast.InterfaceType) types.Type {
	methodsList := v.Methods.List
	if methodsList == nil {
		return types.NewInterfaceType(nil, nil)
	}
	var pkg = ctx.pkg.Types
	var methods []*types.Func
	var embeddeds []types.Type
	for _, m := range methodsList {
		if m.Names == nil { // embedded
			embeddeds = append(embeddeds, toType(ctx, m.Type))
			continue
		}
		name := m.Names[0].Name
		sig := toFuncType(ctx, m.Type.(*ast.FuncType), nil)
		methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
	}
	intf := types.NewInterfaceType(methods, embeddeds).Complete()
	return intf
}

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

func toString(l *ast.BasicLit) string {
	if l.Kind == token.STRING {
		s, err := strconv.Unquote(l.Value)
		if err == nil {
			return s
		}
	}
	panic("TODO: toString - convert ast.BasicLit to string failed")
}

// -----------------------------------------------------------------------------
<commit_msg>cl: toStructType check redecl skip _<commit_after>/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cl

import (
	"go/constant"
	"go/types"
	"log"
	"math/big"
	"reflect"
	"strconv"

	"github.com/goplus/gop/ast"
	"github.com/goplus/gop/token"
	"github.com/goplus/gox"
)

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

func toFuncType(ctx *blockCtx, typ *ast.FuncType, recv *types.Var) *types.Signature {
	params, variadic := toParams(ctx, typ.Params.List)
	results := toResults(ctx, typ.Results)
	return types.NewSignature(recv, params, results, variadic)
}

func toRecv(ctx *blockCtx, recv *ast.FieldList) *types.Var {
	v := recv.List[0]
	var name string
	if len(v.Names) > 0 {
		name = v.Names[0].Name
	}
	return ctx.pkg.NewParam(v.Pos(), name, toType(ctx, v.Type))
}

func getRecvTypeName(ctx *pkgCtx, recv *ast.FieldList, handleErr bool) (string, bool) {
	typ := recv.List[0].Type
	if t, ok := typ.(*ast.StarExpr); ok {
		typ = t.X
	}
	if t, ok := typ.(*ast.Ident); ok {
		return t.Name, true
	}
	if handleErr {
		src, pos := ctx.LoadExpr(typ)
		ctx.handleCodeErrorf(&pos, "invalid receiver type %v (%v is not a defined type)", src, src)
	}
	return "", false
}

func toResults(ctx *blockCtx, in *ast.FieldList) *types.Tuple {
	if in == nil {
		return nil
	}
	flds := in.List
	n := len(flds)
	args := make([]*types.Var, 0, n)
	for _, fld := range flds {
		args = toParam(ctx, fld, args)
	}
	return types.NewTuple(args...)
}

func toParams(ctx *blockCtx, flds []*ast.Field) (typ *types.Tuple, variadic bool) {
	n := len(flds)
	if n == 0 {
		return nil, false
	}
	args := make([]*types.Var, 0, n)
	for _, fld := range flds {
		args = toParam(ctx, fld, args)
	}
	_, ok := flds[n-1].Type.(*ast.Ellipsis)
	return types.NewTuple(args...), ok
}

func toParam(ctx *blockCtx, fld *ast.Field, args []*gox.Param) []*gox.Param {
	typ := toType(ctx, fld.Type)
	pkg := ctx.pkg
	if len(fld.Names) == 0 {
		return append(args, pkg.NewParam(fld.Pos(), "", typ))
	}
	for _, name := range fld.Names {
		args = append(args, pkg.NewParam(name.Pos(), name.Name, typ))
	}
	return args
}

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

func toType(ctx *blockCtx, typ ast.Expr) types.Type {
	switch v := typ.(type) {
	case *ast.Ident:
		return toIdentType(ctx, v)
	case *ast.StarExpr:
		elem := toType(ctx, v.X)
		return types.NewPointer(elem)
	case *ast.ArrayType:
		return toArrayType(ctx, v)
	case *ast.InterfaceType:
		return toInterfaceType(ctx, v)
	case *ast.Ellipsis:
		elem := toType(ctx, v.Elt)
		return types.NewSlice(elem)
	case *ast.MapType:
		return toMapType(ctx, v)
	case *ast.StructType:
		return toStructType(ctx, v)
	case *ast.ChanType:
		return toChanType(ctx, v)
	case *ast.FuncType:
		return toFuncType(ctx, v, nil)
	case *ast.SelectorExpr:
		return toExternalType(ctx, v)
	case *ast.ParenExpr:
		return toType(ctx, v.X)
	}
	log.Panicln("toType: unknown -", reflect.TypeOf(typ))
	return nil
}

var (
	typesChanDirs = [...]types.ChanDir{
		ast.RECV:            types.RecvOnly,
		ast.SEND:            types.SendOnly,
		ast.SEND | ast.RECV: types.SendRecv,
	}
)

func toChanType(ctx *blockCtx, v *ast.ChanType) *types.Chan {
	return types.NewChan(typesChanDirs[v.Dir], toType(ctx, v.Value))
}

func toExternalType(ctx *blockCtx, v *ast.SelectorExpr) types.Type {
	name := v.X.(*ast.Ident).Name
	if pr, ok := ctx.findImport(name); ok {
		o := pr.TryRef(v.Sel.Name)
		if t, ok := o.(*types.TypeName); ok {
			return t.Type()
		}
		panic(ctx.newCodeErrorf(v.Pos(), "%s.%s is not a type", name, v.Sel.Name))
	}
	panic(ctx.newCodeErrorf(v.Pos(), "undefined: %s", name))
}

/*-----------------------------------------------------------------------------

Name context:
- type
- pkgRef.type
- spx.type

// ---------------------------------------------------------------------------*/

func toIdentType(ctx *blockCtx, ident *ast.Ident) types.Type {
	v, builtin := lookupType(ctx, ident.Name)
	if isBuiltin(builtin) {
		panic(ctx.newCodeErrorf(ident.Pos(), "use of builtin %s not in function call", ident.Name))
	}
	if t, ok := v.(*types.TypeName); ok {
		return t.Type()
	}
	if v, _ := lookupPkgRef(ctx, nil, ident); v != nil {
		if t, ok := v.(*types.TypeName); ok {
			return t.Type()
		}
	}
	panic(ctx.newCodeErrorf(ident.Pos(), "%s is not a type", ident.Name))
}

// TODO: optimization
func lookupType(ctx *blockCtx, name string) (types.Object, types.Object) {
	at, o := ctx.cb.Scope().LookupParent(name, token.NoPos)
	if o != nil && at != types.Universe {
		if debugLookup {
			log.Println("==> LookupParent", name, "=>", o)
		}
		return o, nil
	}
	if ctx.loadSymbol(name) {
		if v := ctx.pkg.Types.Scope().Lookup(name); v != nil {
			if debugLookup {
				log.Println("==> Lookup (LoadSymbol)", name, "=>", v)
			}
			return v, nil
		}
	}
	if obj := ctx.pkg.Builtin().TryRef(name); obj != nil {
		return obj, o
	}
	return o, o
}

func toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {
	pkg := ctx.pkg.Types
	fieldList := v.Fields.List
	fields := make([]*types.Var, 0, len(fieldList))
	tags := make([]string, 0, len(fieldList))
	names := make(map[string]token.Pos)
	chkRedecl := func(name string, pos token.Pos) bool {
		if name == "_" {
			return false
		}
		if opos, ok := names[name]; ok {
			npos := ctx.Position(pos)
			ctx.handleCodeErrorf(&npos, "%v redeclared\n\t%v other declaration of %v",
				name, ctx.Position(opos), name)
			return true
		}
		names[name] = pos
		return false
	}
	for _, field := range fieldList {
		typ := toType(ctx, field.Type)
		if field.Names == nil { // embedded
			name := getTypeName(typ)
			if chkRedecl(name, field.Type.Pos()) {
				continue
			}
			fld := types.NewField(token.NoPos, pkg, name, typ, true)
			fields = append(fields, fld)
			tags = append(tags, toFieldTag(field.Tag))
			continue
		}
		for _, name := range field.Names {
			if chkRedecl(name.Name, name.NamePos) {
				continue
			}
			fld := types.NewField(token.NoPos, pkg, name.Name, typ, false)
			fields = append(fields, fld)
			tags = append(tags, toFieldTag(field.Tag))
		}
	}
	return types.NewStruct(fields, tags)
}

func toFieldTag(v *ast.BasicLit) string {
	if v != nil {
		tag, err := strconv.Unquote(v.Value)
		if err != nil {
			log.Panicln("TODO: toFieldTag -", err)
		}
		return tag
	}
	return ""
}

func getTypeName(typ types.Type) string {
	if t, ok := typ.(*types.Pointer); ok {
		typ = t.Elem()
	}
	switch t := typ.(type) {
	case *types.Named:
		return t.Obj().Name()
	case *types.Basic:
		return t.Name()
	default:
		panic("TODO: getTypeName")
	}
}

func toMapType(ctx *blockCtx, v *ast.MapType) *types.Map {
	key := toType(ctx, v.Key)
	val := toType(ctx, v.Value)
	return types.NewMap(key, val)
}

func toArrayType(ctx *blockCtx, v *ast.ArrayType) types.Type {
	elem := toType(ctx, v.Elt)
	if v.Len == nil {
		return types.NewSlice(elem)
	}
	if _, ok := v.Len.(*ast.Ellipsis); ok {
		return types.NewArray(elem, -1) // A negative length indicates an unknown length
	}
	return types.NewArray(elem, toInt64(ctx, v.Len, "non-constant array bound %s"))
}

func toInt64(ctx *blockCtx, e ast.Expr, emsg string) int64 {
	cb := ctx.pkg.ConstStart()
	compileExpr(ctx, e)
	tv := cb.EndConst()
	if val := tv.CVal; val != nil {
		if val.Kind() == constant.Float {
			if v, ok := constant.Val(val).(*big.Rat); ok && v.IsInt() {
				return v.Num().Int64()
			}
		} else if v, ok := constant.Int64Val(val); ok {
			return v
		}
	}
	src, pos := ctx.LoadExpr(e)
	panic(newCodeErrorf(&pos, emsg, src))
}

func toInterfaceType(ctx *blockCtx, v *ast.InterfaceType) types.Type {
	methodsList := v.Methods.List
	if methodsList == nil {
		return types.NewInterfaceType(nil, nil)
	}
	var pkg = ctx.pkg.Types
	var methods []*types.Func
	var embeddeds []types.Type
	for _, m := range methodsList {
		if m.Names == nil { // embedded
			embeddeds = append(embeddeds, toType(ctx, m.Type))
			continue
		}
		name := m.Names[0].Name
		sig := toFuncType(ctx, m.Type.(*ast.FuncType), nil)
		methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
	}
	intf := types.NewInterfaceType(methods, embeddeds).Complete()
	return intf
}

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

func toString(l *ast.BasicLit) string {
	if l.Kind == token.STRING {
		s, err := strconv.Unquote(l.Value)
		if err == nil {
			return s
		}
	}
	panic("TODO: toString - convert ast.BasicLit to string failed")
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>package print

import (
	"code.google.com/p/go.text/encoding/japanese"
	"code.google.com/p/go.text/transform"
	"fmt"
	"github.com/monochromegane/the_platinum_searcher/search/match"
	"github.com/monochromegane/the_platinum_searcher/search/option"
	"github.com/monochromegane/the_platinum_searcher/search/pattern"
	"io"
	"os"
	"strings"
)

const (
	ColorReset      = "\x1b[0m\x1b[K"
	ColorLineNumber = "\x1b[1;33m"  /* yellow with black background */
	ColorPath       = "\x1b[1;32m"  /* bold green */
	ColorMatch      = "\x1b[30;43m" /* black with yellow background */
)

type Params struct {
	Pattern *pattern.Pattern
	Path    string
	Matches []*match.Match
}

type Printer struct {
	In     chan *Params
	Done   chan bool
	Option *option.Option
	writer io.Writer
}

func NewPrinter(in chan *Params, done chan bool, option *option.Option) *Printer {
	return &Printer{in, done, option, createWriter(option)}
}

func (self *Printer) Print() {
	var fileMatchCount, matchCount uint
	fileMatchCount = 0
	matchCount     = 0
	for arg := range self.In {

		if self.Option.FilesWithRegexp != "" {
			self.printPath(arg.Path)
			fmt.Println()
			fileMatchCount++
			continue
		}

		if len(arg.Matches) == 0 {
			continue
		}

		if self.Option.FilesWithMatches {
			self.printPath(arg.Path)
			fmt.Println()
			fileMatchCount++
			continue
		}
		if !self.Option.NoGroup {
			self.printPath(arg.Path)
			fmt.Println()
			fileMatchCount++
		}
		lastLineNum := 0
		enableContext := self.Option.Before > 0 || self.Option.After > 0
		for _, v := range arg.Matches {
			if v == nil {
				continue
			}
			if enableContext {
				if lastLineNum > 0 && lastLineNum+1 != v.FirstLineNum() {
					fmt.Println("--")
				}
				lastLineNum = v.LastLineNum()
			}
			if self.Option.NoGroup {
				self.printPath(arg.Path)
			}
			self.printContext(v.Befores)
			self.printMatch(arg.Pattern, v.Line)
			matchCount++
			fmt.Println()
			self.printContext(v.Afters)
		}
		if !self.Option.NoGroup {
			fmt.Println()
		}
	}
	if self.Option.Stats {
		fmt.Printf("%d Files Matched\n", fileMatchCount)
		fmt.Printf("%d Total Text Matches\n", matchCount)
	}
	self.Done <- true
}

func (self *Printer) printPath(path string) {
	if self.Option.EnableColor {
		fmt.Fprintf(self.writer, "%s%s%s", ColorPath, path, ColorReset)
	} else {
		fmt.Fprintf(self.writer, "%s", path)
	}
	if !self.Option.FilesWithMatches && self.Option.FilesWithRegexp == "" {
		fmt.Fprintf(self.writer, ":")
	}
}
func (self *Printer) printLineNumber(lineNum int, sep string) {
	if self.Option.EnableColor {
		fmt.Fprintf(self.writer, "%s%d%s%s", ColorLineNumber, lineNum, ColorReset, sep)
	} else {
		fmt.Fprintf(self.writer, "%d%s", lineNum, sep)
	}
}
func (self *Printer) printMatch(pattern *pattern.Pattern, line *match.Line) {
	self.printLineNumber(line.Num, ":")
	if !self.Option.EnableColor {
		fmt.Fprintf(self.writer, "%s", line.Str)
	} else if pattern.IgnoreCase {
		fmt.Fprintf(self.writer, "%s", pattern.Regexp.ReplaceAllString(line.Str, ColorMatch+"${1}"+ColorReset))
	} else {
		fmt.Fprintf(self.writer, "%s", strings.Replace(line.Str, pattern.Pattern, ColorMatch+pattern.Pattern+ColorReset, -1))
	}
}

func (self *Printer) printContext(lines []*match.Line) {
	for _, line := range lines {
		self.printLineNumber(line.Num, "-")
		fmt.Fprintf(self.writer, "%s", line.Str)
		fmt.Fprintln(self.writer)
	}
}

func createWriter(option *option.Option) io.Writer {
	switch option.OutputEncode {
	case "sjis":
		return transform.NewWriter(os.Stdout, japanese.ShiftJIS.NewEncoder())
	case "euc":
		return transform.NewWriter(os.Stdout, japanese.EUCJP.NewEncoder())
	case "jis":
		return transform.NewWriter(os.Stdout, japanese.ISO2022JP.NewEncoder())
	default:
		return os.Stdout
	}
}
<commit_msg>Color shown in Windows command prompt<commit_after>package print

import (
	"code.google.com/p/go.text/encoding/japanese"
	"code.google.com/p/go.text/transform"
	"fmt"
	"github.com/monochromegane/the_platinum_searcher/search/match"
	"github.com/monochromegane/the_platinum_searcher/search/option"
	"github.com/monochromegane/the_platinum_searcher/search/pattern"
	"github.com/shiena/ansicolor"
	"io"
	"os"
	"strings"
)

const (
	ColorReset      = "\x1b[0m\x1b[K"
	ColorLineNumber = "\x1b[1;33m"  /* yellow with black background */
	ColorPath       = "\x1b[1;32m"  /* bold green */
	ColorMatch      = "\x1b[30;43m" /* black with yellow background */
)

type Params struct {
	Pattern *pattern.Pattern
	Path    string
	Matches []*match.Match
}

type Printer struct {
	In     chan *Params
	Done   chan bool
	Option *option.Option
	writer io.Writer
}

func NewPrinter(in chan *Params, done chan bool, option *option.Option) *Printer {
	return &Printer{in, done, option, createWriter(option)}
}

func (self *Printer) Print() {
	var fileMatchCount, matchCount uint
	fileMatchCount = 0
	matchCount     = 0
	for arg := range self.In {

		if self.Option.FilesWithRegexp != "" {
			self.printPath(arg.Path)
			fmt.Println()
			fileMatchCount++
			continue
		}

		if len(arg.Matches) == 0 {
			continue
		}

		if self.Option.FilesWithMatches {
			self.printPath(arg.Path)
			fmt.Println()
			fileMatchCount++
			continue
		}
		if !self.Option.NoGroup {
			self.printPath(arg.Path)
			fmt.Println()
			fileMatchCount++
		}
		lastLineNum := 0
		enableContext := self.Option.Before > 0 || self.Option.After > 0
		for _, v := range arg.Matches {
			if v == nil {
				continue
			}
			if enableContext {
				if lastLineNum > 0 && lastLineNum+1 != v.FirstLineNum() {
					fmt.Println("--")
				}
				lastLineNum = v.LastLineNum()
			}
			if self.Option.NoGroup {
				self.printPath(arg.Path)
			}
			self.printContext(v.Befores)
			self.printMatch(arg.Pattern, v.Line)
			matchCount++
			fmt.Println()
			self.printContext(v.Afters)
		}
		if !self.Option.NoGroup {
			fmt.Println()
		}
	}
	if self.Option.Stats {
		fmt.Printf("%d Files Matched\n", fileMatchCount)
		fmt.Printf("%d Total Text Matches\n", matchCount)
	}
	self.Done <- true
}

func (self *Printer) printPath(path string) {
	if self.Option.EnableColor {
		fmt.Fprintf(self.writer, "%s%s%s", ColorPath, path, ColorReset)
	} else {
		fmt.Fprintf(self.writer, "%s", path)
	}
	if !self.Option.FilesWithMatches && self.Option.FilesWithRegexp == "" {
		fmt.Fprintf(self.writer, ":")
	}
}
func (self *Printer) printLineNumber(lineNum int, sep string) {
	if self.Option.EnableColor {
		fmt.Fprintf(self.writer, "%s%d%s%s", ColorLineNumber, lineNum, ColorReset, sep)
	} else {
		fmt.Fprintf(self.writer, "%d%s", lineNum, sep)
	}
}
func (self *Printer) printMatch(pattern *pattern.Pattern, line *match.Line) {
	self.printLineNumber(line.Num, ":")
	if !self.Option.EnableColor {
		fmt.Fprintf(self.writer, "%s", line.Str)
	} else if pattern.IgnoreCase {
		fmt.Fprintf(self.writer, "%s", pattern.Regexp.ReplaceAllString(line.Str, ColorMatch+"${1}"+ColorReset))
	} else {
		fmt.Fprintf(self.writer, "%s", strings.Replace(line.Str, pattern.Pattern, ColorMatch+pattern.Pattern+ColorReset, -1))
	}
}

func (self *Printer) printContext(lines []*match.Line) {
	for _, line := range lines {
		self.printLineNumber(line.Num, "-")
		fmt.Fprintf(self.writer, "%s", line.Str)
		fmt.Fprintln(self.writer)
	}
}

func createWriter(option *option.Option) io.Writer {
	encoder := func() io.Writer {
		switch option.OutputEncode {
		case "sjis":
			return transform.NewWriter(os.Stdout, japanese.ShiftJIS.NewEncoder())
		case "euc":
			return transform.NewWriter(os.Stdout, japanese.EUCJP.NewEncoder())
		case "jis":
			return transform.NewWriter(os.Stdout, japanese.ISO2022JP.NewEncoder())
		default:
			return os.Stdout
		}
	}()
	if option.EnableColor {
		return ansicolor.NewAnsiColorWriter(encoder)
	}
	return encoder
}
<|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package efhsim

import (
	"log"

	"my/itto/verify/packet/itto"
	"my/itto/verify/sim"
)

type TobLogger struct {
	lastMessage  *sim.IttoDbMessage
	lastOptionId itto.OptionId
	consumeOps   int
	curOps       int
	ittoSeconds  uint32
	bid          tob
	ask          tob
}
type tob struct {
	Check bool
	Side  itto.MarketSide
	Old   sim.PriceLevel
	New   sim.PriceLevel
}

func NewTobLogger() *TobLogger {
	l := &TobLogger{
		bid: tob{Side: itto.MarketSideBid},
		ask: tob{Side: itto.MarketSideAsk},
	}
	return l
}

func (l *TobLogger) MessageArrived(idm *sim.IttoDbMessage) {
	l.lastMessage = idm
	l.bid.Check, l.ask.Check = false, false
	switch m := l.lastMessage.Pam.Layer().(type) {
	case
		*itto.IttoMessageAddOrder,
		*itto.IttoMessageSingleSideExecuted,
		*itto.IttoMessageSingleSideExecutedWithPrice,
		*itto.IttoMessageOrderCancel,
		*itto.IttoMessageSingleSideDelete,
		*itto.IttoMessageBlockSingleSideDelete:
		l.consumeOps = 1
	case
		*itto.IttoMessageSingleSideReplace,
		*itto.IttoMessageSingleSideUpdate:
		l.consumeOps = 2
	case
		*itto.IttoMessageAddQuote,
		*itto.IttoMessageQuoteDelete:
		l.consumeOps = 2
		l.bid.Check, l.ask.Check = true, true
	case
		*itto.IttoMessageQuoteReplace:
		l.consumeOps = 4
		l.bid.Check, l.ask.Check = true, true
	case *itto.IttoMessageSeconds:
		l.ittoSeconds = m.Second
	default:
		log.Println("wrong message type ", l.lastMessage.Pam.Layer())
		return
	}
	l.curOps = 0
}

func (*TobLogger) OperationAppliedToOrders(sim.IttoOperation) {}

func (l *TobLogger) BeforeBookUpdate(book sim.Book, operation sim.IttoOperation) {
	if l.curOps > 0 {
		return
	}
	l.lastOptionId = operation.GetOptionId()
	if l.lastOptionId.Invalid() {
		return
	}
	switch operation.GetSide() {
	case itto.MarketSideBid:
		l.bid.Check = true
	case itto.MarketSideAsk:
		l.ask.Check = true
	default:
		log.Fatalln("wrong operation side")
	}
	l.bid.update(book, l.lastOptionId, TobUpdateOld)
	l.ask.update(book, l.lastOptionId, TobUpdateOld)
}

func (l *TobLogger) AfterBookUpdate(book sim.Book, operation sim.IttoOperation, tobUpdate TobUpdate) bool {
	l.curOps++
	if l.curOps < l.consumeOps {
		return false
	}
	l.curOps = 0
	if l.lastOptionId.Invalid() {
		return false
	}
	l.bid.update(book, l.lastOptionId, tobUpdate)
	l.ask.update(book, l.lastOptionId, tobUpdate)

	return l.bid.updated() || l.ask.updated()
}

type TobUpdate byte

const (
	TobUpdateOld TobUpdate = iota
	TobUpdateNew
	TobUpdateNewForce
)

func (tob *tob) update(book sim.Book, oid itto.OptionId, u TobUpdate) {
	pl := &tob.New
	if u == TobUpdateOld {
		pl = &tob.Old
	}
	*pl = sim.PriceLevel{}
	if tob.Check || u == TobUpdateNewForce {
		if pls := book.GetTop(oid, tob.Side, 1); len(pls) > 0 {
			*pl = pls[0]
		}
	}
}

func (tob *tob) updated() bool {
	return tob.Check && tob.Old != tob.New
}
<commit_msg>TobLogger: track sequence number<commit_after>// Copyright (c) Ilia Kravets, 2015. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package efhsim

import (
	"log"

	"code.google.com/p/gopacket"

	"my/itto/verify/packet/itto"
	"my/itto/verify/sim"
)

type TobLogger struct {
	lastMessage  *sim.IttoDbMessage
	lastOptionId itto.OptionId
	consumeOps   int
	curOps       int
	ittoSeconds  uint32
	bid          tob
	ask          tob
	seqNum       map[gopacket.Flow]uint64
}
type tob struct {
	Check bool
	Side  itto.MarketSide
	Old   sim.PriceLevel
	New   sim.PriceLevel
}

func NewTobLogger() *TobLogger {
	l := &TobLogger{
		bid:    tob{Side: itto.MarketSideBid},
		ask:    tob{Side: itto.MarketSideAsk},
		seqNum: make(map[gopacket.Flow]uint64),
	}
	return l
}

func (l *TobLogger) MessageArrived(idm *sim.IttoDbMessage) {
	l.lastMessage = idm

	flow := l.lastMessage.Pam.Flow()
	seq := l.lastMessage.Pam.SequenceNumber()
	if prevSeq, ok := l.seqNum[flow]; ok && prevSeq+1 != seq {
		log.Printf("seqNum gap; expected %d actual %d\n", prevSeq+1, seq)
	}
	l.seqNum[flow] = seq

	l.bid.Check, l.ask.Check = false, false
	switch m := l.lastMessage.Pam.Layer().(type) {
	case
		*itto.IttoMessageAddOrder,
		*itto.IttoMessageSingleSideExecuted,
		*itto.IttoMessageSingleSideExecutedWithPrice,
		*itto.IttoMessageOrderCancel,
		*itto.IttoMessageSingleSideDelete,
		*itto.IttoMessageBlockSingleSideDelete:
		l.consumeOps = 1
	case
		*itto.IttoMessageSingleSideReplace,
		*itto.IttoMessageSingleSideUpdate:
		l.consumeOps = 2
	case
		*itto.IttoMessageAddQuote,
		*itto.IttoMessageQuoteDelete:
		l.consumeOps = 2
		l.bid.Check, l.ask.Check = true, true
	case
		*itto.IttoMessageQuoteReplace:
		l.consumeOps = 4
		l.bid.Check, l.ask.Check = true, true
	case *itto.IttoMessageSeconds:
		l.ittoSeconds = m.Second
	default:
		log.Println("wrong message type ", l.lastMessage.Pam.Layer())
		return
	}
	l.curOps = 0
}

func (*TobLogger) OperationAppliedToOrders(sim.IttoOperation) {}

func (l *TobLogger) BeforeBookUpdate(book sim.Book, operation sim.IttoOperation) {
	if l.curOps > 0 {
		return
	}
	l.lastOptionId = operation.GetOptionId()
	if l.lastOptionId.Invalid() {
		return
	}
	switch operation.GetSide() {
	case itto.MarketSideBid:
		l.bid.Check = true
	case itto.MarketSideAsk:
		l.ask.Check = true
	default:
		log.Fatalln("wrong operation side")
	}
	l.bid.update(book, l.lastOptionId, TobUpdateOld)
	l.ask.update(book, l.lastOptionId, TobUpdateOld)
}

func (l *TobLogger) AfterBookUpdate(book sim.Book, operation sim.IttoOperation, tobUpdate TobUpdate) bool {
	l.curOps++
	if l.curOps < l.consumeOps {
		return false
	}
	l.curOps = 0
	if l.lastOptionId.Invalid() {
		return false
	}
	l.bid.update(book, l.lastOptionId, tobUpdate)
	l.ask.update(book, l.lastOptionId, tobUpdate)

	return l.bid.updated() || l.ask.updated()
}

type TobUpdate byte

const (
	TobUpdateOld TobUpdate = iota
	TobUpdateNew
	TobUpdateNewForce
)

func (tob *tob) update(book sim.Book, oid itto.OptionId, u TobUpdate) {
	pl := &tob.New
	if u == TobUpdateOld {
		pl = &tob.Old
	}
	*pl = sim.PriceLevel{}
	if tob.Check || u == TobUpdateNewForce {
		if pls := book.GetTop(oid, tob.Side, 1); len(pls) > 0 {
			*pl = pls[0]
		}
	}
}

func (tob *tob) updated() bool {
	return tob.Check && tob.Old != tob.New
}
<|endoftext|>