<commit_before>package main

import (
	"os"
	"fmt"
	"sort"
	"container/list"
	"github.com/wesovilabs/gherkinize/path"
	"github.com/wesovilabs/gherkinize/gherkin"
	"github.com/wesovilabs/gherkinize/util"
	"github.com/wesovilabs/gherkinize/config"
	"github.com/fatih/color"
	"github.com/urfave/cli"
)

func main() {
	app := cli.NewApp()
	app.Name = "Gherkinize"
	app.Usage = "Find the issues in your Gherkin features."
	app.Version = "0.0.1"
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Iván Corrales Solera",
			Email: "developer@wesovilabs.com",
		},
	}
	app.Copyright = "(c) 2017 Wesovilabs"
	app.EnableBashCompletion = true
	var configurationFile string
	var scenariosPath string
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "Load toml configuration from `FILE`",
			Destination: &configurationFile,
		},
		cli.StringFlag{
			Name:  "input, i",
			Usage: "Scenarios directory path",
			Destination: &scenariosPath,
		},
	}
	app.Commands = []cli.Command{
		{
			Name:    "validate",
			Aliases: []string{"v"},
			Usage:   "validate the scenarios",
			Action:  func(c *cli.Context) error {
				if( scenariosPath == ""){
					return cli.NewExitError("Please specify the scenarios path by using option -i", 86)
				}
				if(configurationFile == ""){
					configurationFile = "./config/gherkin-rules.toml"
				}
				validateFiles(scenariosPath,configurationFile)
				return nil
			},
		},
	}

	sort.Sort(cli.FlagsByName(app.Flags))
	app.Run(os.Args)

}

func showInvalidStructureMessage(feature string, lineNumber int){
	fmt.Println()
	util.Print_subtitle(feature)
	fmt.Println()
	util.Print_error("Line: %d", lineNumber)
	util.Print_error("Missing scenario keyword.")
	color.White("[ USAGE ]")
	util.Print_message("\tScenario:")
	util.Print_message("\t   Given ...")
	util.Print_message("\t   When ...")
	util.Print_message("\t   Then ...")
}

func validateFiles(scenarios_path string, config_path string) error {
	gherkin.InitializeTokenMap()
	list_files := path.ReadDirectory(scenarios_path)
	configFile, err := os.Open(config_path)
	if err != nil {
		return nil
	}
	config := config.GetConfig(configFile)
	for _, filePath := range list_files {

		file, err := os.Open(filePath)
		if err != nil {
			return nil
		}

		defer file.Close()
		scanner := gherkin.NewGherkinScanner(file)
		lineNumber :=1
		tokens := list.New()
		fmt.Println()
		fmt.Println()
		util.Print_title("-------------------------------------------------- ")
		util.Print_title("File " + filePath)
		util.Print_title("-------------------------------------------------- ")
		var feature = &gherkin.Feature{}
		var invalidFeature = false
		for {
			token := scanner.Scan(lineNumber)
			switch token.Kind {
				case gherkin.EOF:
					feature.Validate(*config)
				case gherkin.NEW_LINE:
					if(feature.LineNumber == 0) {
						util.Print_error("Missing feature tag.")
						invalidFeature = true
						break
					} else {
						if (feature.Scenarios.Back() != nil){
							step := token.ToStep()
							feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
						}
						lineNumber+=1
					}
				case gherkin.TOKEN_FEATURE:
					*feature = token.ToFeature()
				case gherkin.TOKEN_SCENARIO:
					if(feature.LineNumber == 0) {
						break
					}
					scenario := token.ToScenario()
					feature.Scenarios.PushBack(scenario)
				case gherkin.TOKEN_GIVEN:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
				case gherkin.TOKEN_WHEN:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
				case gherkin.TOKEN_THEN:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
				case gherkin.TOKEN_AND:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
			}
			if(invalidFeature){
				break
			}
			if(token.IsKeyword()){
				tokens.PushBack(token)
				lineNumber++
			}
			if(token.Kind == gherkin.EOF) {
				break
			}
		}

	}

	return nil
}<commit_msg>feat: working around travis release configuration<commit_after>package main

import (
	"os"
	"fmt"
	"sort"
	"container/list"
	"github.com/wesovilabs/gherkinize/path"
	"github.com/wesovilabs/gherkinize/gherkin"
	"github.com/wesovilabs/gherkinize/util"
	"github.com/wesovilabs/gherkinize/config"
	"github.com/fatih/color"
	"github.com/urfave/cli"
)

func main() {
	app := cli.NewApp()
	app.Name = "Gherkinize"
	app.Usage = "A Gherkin utility command-line tool."
	app.Version = "0.0.1"
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Iván Corrales Solera",
			Email: "developer@wesovilabs.com",
		},
	}
	app.Copyright = "(c) 2017 Wesovilabs"
	app.EnableBashCompletion = true
	var configurationFile string
	var scenariosPath string
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "Load toml configuration from `FILE`",
			Destination: &configurationFile,
		},
		cli.StringFlag{
			Name:  "input, i",
			Usage: "Scenarios directory path",
			Destination: &scenariosPath,
		},
	}
	app.Commands = []cli.Command{
		{
			Name:    "validate",
			Aliases: []string{"v"},
			Usage:   "validate the scenarios",
			Action:  func(c *cli.Context) error {
				if( scenariosPath == ""){
					return cli.NewExitError("Please specify the scenarios path by using option -i", 86)
				}
				if(configurationFile == ""){
					configurationFile = "./config/gherkin-rules.toml"
				}
				validateFiles(scenariosPath,configurationFile)
				return nil
			},
		},
	}

	sort.Sort(cli.FlagsByName(app.Flags))
	app.Run(os.Args)

}

func showInvalidStructureMessage(feature string, lineNumber int){
	fmt.Println()
	util.Print_subtitle(feature)
	fmt.Println()
	util.Print_error("Line: %d", lineNumber)
	util.Print_error("Missing scenario keyword.")
	color.White("[ USAGE ]")
	util.Print_message("\tScenario:")
	util.Print_message("\t   Given ...")
	util.Print_message("\t   When ...")
	util.Print_message("\t   Then ...")
}

func validateFiles(scenarios_path string, config_path string) error {
	gherkin.InitializeTokenMap()
	list_files := path.ReadDirectory(scenarios_path)
	configFile, err := os.Open(config_path)
	if err != nil {
		return nil
	}
	config := config.GetConfig(configFile)
	for _, filePath := range list_files {

		file, err := os.Open(filePath)
		if err != nil {
			return nil
		}

		defer file.Close()
		scanner := gherkin.NewGherkinScanner(file)
		lineNumber :=1
		tokens := list.New()
		fmt.Println()
		fmt.Println()
		util.Print_title("-------------------------------------------------- ")
		util.Print_title("File " + filePath)
		util.Print_title("-------------------------------------------------- ")
		var feature = &gherkin.Feature{}
		var invalidFeature = false
		for {
			token := scanner.Scan(lineNumber)
			switch token.Kind {
				case gherkin.EOF:
					feature.Validate(*config)
				case gherkin.NEW_LINE:
					if(feature.LineNumber == 0) {
						util.Print_error("Missing feature tag.")
						invalidFeature = true
						break
					} else {
						if (feature.Scenarios.Back() != nil){
							step := token.ToStep()
							feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
						}
						lineNumber+=1
					}
				case gherkin.TOKEN_FEATURE:
					*feature = token.ToFeature()
				case gherkin.TOKEN_SCENARIO:
					if(feature.LineNumber == 0) {
						break
					}
					scenario := token.ToScenario()
					feature.Scenarios.PushBack(scenario)
				case gherkin.TOKEN_GIVEN:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
				case gherkin.TOKEN_WHEN:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
				case gherkin.TOKEN_THEN:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
				case gherkin.TOKEN_AND:
					step := token.ToStep()
					if(feature.Scenarios.Back() == nil){
						showInvalidStructureMessage(feature.Text, token.LineNumber)
						invalidFeature = true
						break
					} else {
						feature.Scenarios.Back().Value.(gherkin.Scenario).Steps.PushBack(step)
					}
			}
			if(invalidFeature){
				break
			}
			if(token.IsKeyword()){
				tokens.PushBack(token)
				lineNumber++
			}
			if(token.Kind == gherkin.EOF) {
				break
			}
		}

	}

	return nil
}<|endoftext|>


<commit_before>// Package db to connect to mongodb
package db

import (
	"errors"
	"fmt"
	"github.com/MG-RAST/Shock/shock-server/conf"
	mgo "github.com/MG-RAST/Shock/vendor/gopkg.in/mgo.v2"
	"time"
)

const (
	DbTimeout = time.Duration(time.Second * 1200)
)

var (
	Connection connection
)

type connection struct {
	dbname   string
	username string
	password string
	Session  *mgo.Session
	DB       *mgo.Database
}

func Initialize() (err error) {
	c := connection{}
	s, err := mgo.DialWithTimeout(conf.MONGODB_HOSTS, DbTimeout)
	if err != nil {
		e := errors.New(fmt.Sprintf("no reachable mongodb server(s) at %s", conf.MONGODB_HOSTS))
		return e
	}
	c.Session = s
	c.DB = c.Session.DB(conf.MONGODB_DATABASE)
	if conf.MONGODB_USER != "" && conf.MONGODB_PASSWORD != "" {
		c.DB.Login(conf.MONGODB_USER, conf.MONGODB_PASSWORD)
	}
	Connection = c
	return
}

func Drop() error {
	return Connection.DB.DropDatabase()
}
<commit_msg>test mongo connection with short timeout on startup<commit_after>// Package db to connect to mongodb
package db

import (
	"errors"
	"fmt"
	"github.com/MG-RAST/Shock/shock-server/conf"
	mgo "github.com/MG-RAST/Shock/vendor/gopkg.in/mgo.v2"
	"time"
)

const (
	DbTimeout    = time.Duration(time.Second * 1200)
	DialTimeout  = time.Duration(time.Second * 10)
	DialAttempts = 5
)

var (
	Connection connection
)

type connection struct {
	dbname   string
	username string
	password string
	Session  *mgo.Session
	DB       *mgo.Database
}

func Initialize() (err error) {
	c := connection{}

	// test connection
	canDial := false
	for i := 0; i < DialAttempts; i++ {
		s, err := mgo.DialWithTimeout(conf.MONGODB_HOSTS, DialTimeout)
		s.Close()
		if err == nil {
			canDial = true
			break
		}
	}
	if !canDial {
		return errors.New(fmt.Sprintf("no reachable mongodb server(s) at %s", conf.MONGODB_HOSTS))
	}

	// get handle
	s, err := mgo.DialWithTimeout(conf.MONGODB_HOSTS, DbTimeout)
	if err != nil {
		return errors.New(fmt.Sprintf("no reachable mongodb server(s) at %s", conf.MONGODB_HOSTS))
	}
	c.Session = s
	c.DB = c.Session.DB(conf.MONGODB_DATABASE)
	if conf.MONGODB_USER != "" && conf.MONGODB_PASSWORD != "" {
		c.DB.Login(conf.MONGODB_USER, conf.MONGODB_PASSWORD)
	}
	Connection = c
	return
}

func Drop() error {
	return Connection.DB.DropDatabase()
}
<|endoftext|>


<commit_before>package schema

import (
	"fmt"

	"github.com/hashicorp/terraform/config/configschema"
	"github.com/zclconf/go-cty/cty"
)

// The functions and methods in this file are concerned with the conversion
// of this package's schema model into the slightly-lower-level schema model
// used by Terraform core for configuration parsing.

// CoreConfigSchema lowers the receiver to the schema model expected by
// Terraform core.
//
// This lower-level model has fewer features than the schema in this package,
// describing only the basic structure of configuration and state values we
// expect. The full schemaMap from this package is still required for full
// validation, handling of default values, etc.
//
// This method presumes a schema that passes InternalValidate, and so may
// panic or produce an invalid result if given an invalid schemaMap.
func (m schemaMap) CoreConfigSchema() *configschema.Block {
	if len(m) == 0 {
		// We return an actual (empty) object here, rather than a nil,
		// because a nil result would mean that we don't have a schema at
		// all, rather than that we have an empty one.
		return &configschema.Block{}
	}

	ret := &configschema.Block{
		Attributes: map[string]*configschema.Attribute{},
		BlockTypes: map[string]*configschema.NestedBlock{},
	}

	for name, schema := range m {
		if schema.Elem == nil {
			ret.Attributes[name] = schema.coreConfigSchemaAttribute()
			continue
		}
		switch schema.Elem.(type) {
		case *Schema:
			ret.Attributes[name] = schema.coreConfigSchemaAttribute()
		case *Resource:
			ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
		default:
			// Should never happen for a valid schema
			panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
		}
	}

	return ret
}

// coreConfigSchemaAttribute prepares a configschema.Attribute representation
// of a schema. This is appropriate only for primitives or collections whose
// Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
// whose elem is a whole resource.
func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute {
	return &configschema.Attribute{
		Type:        s.coreConfigSchemaType(),
		Optional:    s.Optional,
		Required:    s.Required,
		Computed:    s.Computed,
		Sensitive:   s.Sensitive,
		Description: s.Description,
	}
}

// coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
// a schema. This is appropriate only for collections whose Elem is an instance
// of Resource, and will panic otherwise.
func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
	ret := &configschema.NestedBlock{}
	if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil {
		ret.Block = *nested
	}
	switch s.Type {
	case TypeList:
		ret.Nesting = configschema.NestingList
	case TypeSet:
		ret.Nesting = configschema.NestingSet
	case TypeMap:
		ret.Nesting = configschema.NestingMap
	default:
		// Should never happen for a valid schema
		panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type))
	}

	ret.MinItems = s.MinItems
	ret.MaxItems = s.MaxItems

	if s.Required && s.MinItems == 0 {
		// configschema doesn't have a "required" representation for nested
		// blocks, but we can fake it by requiring at least one item.
		ret.MinItems = 1
	}

	return ret
}

// coreConfigSchemaType determines the core config schema type that corresponds
// to a particular schema's type.
func (s *Schema) coreConfigSchemaType() cty.Type {
	switch s.Type {
	case TypeString:
		return cty.String
	case TypeBool:
		return cty.Bool
	case TypeInt, TypeFloat:
		// configschema doesn't distinguish int and float, so helper/schema
		// will deal with this as an additional validation step after
		// configuration has been parsed and decoded.
		return cty.Number
	case TypeList, TypeSet, TypeMap:
		var elemType cty.Type
		switch set := s.Elem.(type) {
		case *Schema:
			elemType = set.coreConfigSchemaType()
		case *Resource:
			// In practice we don't actually use this for normal schema
			// construction because we construct a NestedBlock in that
			// case instead. See schemaMap.CoreConfigSchema.
			elemType = set.CoreConfigSchema().ImpliedType()
		default:
			if set != nil {
				// Should never happen for a valid schema
				panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem))
			}
			// Some pre-existing schemas assume string as default, so we need
			// to be compatible with them.
			elemType = cty.String
		}
		switch s.Type {
		case TypeList:
			return cty.List(elemType)
		case TypeSet:
			return cty.Set(elemType)
		case TypeMap:
			return cty.Map(elemType)
		default:
			// can never get here in practice, due to the case we're inside
			panic("invalid collection type")
		}
	default:
		// should never happen for a valid schema
		panic(fmt.Errorf("invalid Schema.Type %s", s.Type))
	}
}

// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
// on the resource's schema.
func (r *Resource) CoreConfigSchema() *configschema.Block {
	return schemaMap(r.Schema).CoreConfigSchema()
}
<commit_msg>helper/schema: CoreConfigSchema method for Backend<commit_after>package schema

import (
	"fmt"

	"github.com/hashicorp/terraform/config/configschema"
	"github.com/zclconf/go-cty/cty"
)

// The functions and methods in this file are concerned with the conversion
// of this package's schema model into the slightly-lower-level schema model
// used by Terraform core for configuration parsing.

// CoreConfigSchema lowers the receiver to the schema model expected by
// Terraform core.
//
// This lower-level model has fewer features than the schema in this package,
// describing only the basic structure of configuration and state values we
// expect. The full schemaMap from this package is still required for full
// validation, handling of default values, etc.
//
// This method presumes a schema that passes InternalValidate, and so may
// panic or produce an invalid result if given an invalid schemaMap.
func (m schemaMap) CoreConfigSchema() *configschema.Block {
	if len(m) == 0 {
		// We return an actual (empty) object here, rather than a nil,
		// because a nil result would mean that we don't have a schema at
		// all, rather than that we have an empty one.
		return &configschema.Block{}
	}

	ret := &configschema.Block{
		Attributes: map[string]*configschema.Attribute{},
		BlockTypes: map[string]*configschema.NestedBlock{},
	}

	for name, schema := range m {
		if schema.Elem == nil {
			ret.Attributes[name] = schema.coreConfigSchemaAttribute()
			continue
		}
		switch schema.Elem.(type) {
		case *Schema:
			ret.Attributes[name] = schema.coreConfigSchemaAttribute()
		case *Resource:
			ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
		default:
			// Should never happen for a valid schema
			panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
		}
	}

	return ret
}

// coreConfigSchemaAttribute prepares a configschema.Attribute representation
// of a schema. This is appropriate only for primitives or collections whose
// Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
// whose elem is a whole resource.
func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute {
	return &configschema.Attribute{
		Type:        s.coreConfigSchemaType(),
		Optional:    s.Optional,
		Required:    s.Required,
		Computed:    s.Computed,
		Sensitive:   s.Sensitive,
		Description: s.Description,
	}
}

// coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
// a schema. This is appropriate only for collections whose Elem is an instance
// of Resource, and will panic otherwise.
func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
	ret := &configschema.NestedBlock{}
	if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil {
		ret.Block = *nested
	}
	switch s.Type {
	case TypeList:
		ret.Nesting = configschema.NestingList
	case TypeSet:
		ret.Nesting = configschema.NestingSet
	case TypeMap:
		ret.Nesting = configschema.NestingMap
	default:
		// Should never happen for a valid schema
		panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type))
	}

	ret.MinItems = s.MinItems
	ret.MaxItems = s.MaxItems

	if s.Required && s.MinItems == 0 {
		// configschema doesn't have a "required" representation for nested
		// blocks, but we can fake it by requiring at least one item.
		ret.MinItems = 1
	}

	return ret
}

// coreConfigSchemaType determines the core config schema type that corresponds
// to a particular schema's type.
func (s *Schema) coreConfigSchemaType() cty.Type {
	switch s.Type {
	case TypeString:
		return cty.String
	case TypeBool:
		return cty.Bool
	case TypeInt, TypeFloat:
		// configschema doesn't distinguish int and float, so helper/schema
		// will deal with this as an additional validation step after
		// configuration has been parsed and decoded.
		return cty.Number
	case TypeList, TypeSet, TypeMap:
		var elemType cty.Type
		switch set := s.Elem.(type) {
		case *Schema:
			elemType = set.coreConfigSchemaType()
		case *Resource:
			// In practice we don't actually use this for normal schema
			// construction because we construct a NestedBlock in that
			// case instead. See schemaMap.CoreConfigSchema.
			elemType = set.CoreConfigSchema().ImpliedType()
		default:
			if set != nil {
				// Should never happen for a valid schema
				panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem))
			}
			// Some pre-existing schemas assume string as default, so we need
			// to be compatible with them.
			elemType = cty.String
		}
		switch s.Type {
		case TypeList:
			return cty.List(elemType)
		case TypeSet:
			return cty.Set(elemType)
		case TypeMap:
			return cty.Map(elemType)
		default:
			// can never get here in practice, due to the case we're inside
			panic("invalid collection type")
		}
	default:
		// should never happen for a valid schema
		panic(fmt.Errorf("invalid Schema.Type %s", s.Type))
	}
}

// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
// on the resource's schema.
func (r *Resource) CoreConfigSchema() *configschema.Block {
	return schemaMap(r.Schema).CoreConfigSchema()
}

// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
// on the backends's schema.
func (r *Backend) CoreConfigSchema() *configschema.Block {
	return schemaMap(r.Schema).CoreConfigSchema()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"os/exec"

	"github.com/herman-rogers/gogeta/config"
	"github.com/herman-rogers/gogeta/logger"
	git "github.com/libgit2/git2go"
	"github.com/satori/go.uuid"
)

const UP_TO_DATE = "UP_TO_DATE"
const NORMAL_MERGE = "NORMAL_MERGE"
const FAST_FORWARD = "FAST_FORWARD"

func GitProcessSQSMessages(gitReq scmServiceRequest) error {
	if gitReq.Usr == "" || gitReq.Project == "" || gitReq.Repo == "" {
		return errors.New("Missing Git Service Properties")
	}
	go GitShallowClone(gitReq)
	return nil
}

func GitShallowClone(data scmServiceRequest) {
	repoPath, relativePath := GetRepoPath(data.Project)
	cmd := exec.Command("git", "clone", "--depth", "2", data.Repo, repoPath)

	logfile := logger.GetLogFile()
	defer logfile.Close()

	cmd.Stdout = logfile
	cmd.Stderr = logfile

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Clone")

	cloneErr := cmd.Wait()
	logger.LogError(cloneErr, "Git Clone")
	if cloneErr == nil {
		gitData := &GogetaRepo{
			data.Id,
			data.Usr,
			data.Repo,
			relativePath,
			data.SCMType,
			data.Engine,
			data.Platform,
		}
		go SaveRepo(*gitData)
		go TriggerMrRobotBuild(*gitData)
	}
}

func GetRepoPath(project string) (string, string) {
	var uuid uuid.UUID = uuid.NewV4()
	relativePath := project + "_" + uuid.String()
	repoPath := config.File.RepoPath + relativePath
	return repoPath, relativePath
}

func UpdateGitRepositories() {
	repos := FindAllRepos()
	for i := 0; i < len(repos); i++ {
		folder := config.File.RepoPath + repos[i].Folder
		repo, err := git.OpenRepository(folder)
		if err != nil {
			logger.Error(err.Error())
			continue
		}
		if repo.IsBare() {
			continue
		}
		go GitPull(repos[i], repo)
	}
}

func GitPull(data GogetaRepo, repo *git.Repository) {
	FetchLatestsUpdates(data)
	remoteBranch := GetRemoteBranch(repo)
	remoteBranchID := remoteBranch.Target()

	analysis, err := AnalyzeLatestUpdates(repo, remoteBranch)
	if err != nil {
		logger.Error(err.Error())
	}
	mergeType := CheckMergeAnalysis(analysis)
	msg := "Git Merge " + mergeType

	switch mergeType {
	case UP_TO_DATE:
		break
	case NORMAL_MERGE:
		err := MergeNormal(repo, remoteBranch, remoteBranchID)
		BuildAfterMerge(err, msg, data)
		break
	case FAST_FORWARD:
		err := MergeFastForward(repo, remoteBranchID)
		BuildAfterMerge(err, msg, data)
		break
	}
}

func GetRemoteBranch(repo *git.Repository) *git.Reference {
	remoteBranch, err := repo.References.Lookup("refs/remotes/origin/master")
	logger.LogError(err, "Git Remote Branch")
	return remoteBranch
}

func FetchLatestsUpdates(data GogetaRepo) {
	cmd := exec.Command("git", "fetch", "--depth", "1", "origin", "master")
	cmd.Dir = config.File.RepoPath + data.Folder

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Fetch")

	fetchErr := cmd.Wait()
	logger.LogError(fetchErr, "Git Fetch")
}

func AnalyzeLatestUpdates(repo *git.Repository, remoteBranch *git.Reference) (git.MergeAnalysis, error) {
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		var nullAnalysis git.MergeAnalysis
		return nullAnalysis, err
	}
	mergeHeads := make([]*git.AnnotatedCommit, 1)
	mergeHeads[0] = annotatedCommit
	analysis, _, err := repo.MergeAnalysis(mergeHeads)
	logger.LogError(err, "Git Merge Analysis")
	return analysis, nil
}

func CheckMergeAnalysis(analysis git.MergeAnalysis) string {
	if analysis&git.MergeAnalysisUpToDate != 0 {
		return UP_TO_DATE
	}
	if analysis&git.MergeAnalysisNormal != 0 {
		return NORMAL_MERGE
	}
	return FAST_FORWARD
}

func MergeNormal(repo *git.Repository, remoteBranch *git.Reference, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		return err
	}
	if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil {
		return err
	}
	index, err := repo.Index()
	if err != nil {
		return err
	}
	if index.HasConflicts() {
		return errors.New("Conflicts encountered, Please Resolve Them")
	}
	sig, err := repo.DefaultSignature()
	if err != nil {
		return err
	}
	treeId, err := index.WriteTree()
	if err != nil {
		return err
	}
	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return err
	}
	localCommit, err := repo.LookupCommit(head.Target())
	if err != nil {
		return err
	}
	remoteCommit, err := repo.LookupCommit(remoteBranchID)
	if err != nil {
		return err
	}
	repo.CreateCommit("HEAD", sig, sig, "", tree, localCommit, remoteCommit)
	repo.StateCleanup()
	return nil
}

func MergeFastForward(repo *git.Repository, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	remoteTree, err := repo.LookupTree(remoteBranchID)
	if err != nil {
		return err
	}
	if err := repo.CheckoutTree(remoteTree, nil); err != nil {
		return err
	}
	branchRef, err := repo.References.Lookup("refs/heads/master")
	if err != nil {
		return err
	}
	branchRef.SetTarget(remoteBranchID, "")
	if _, err := head.SetTarget(remoteBranchID, ""); err != nil {
		return err
	}
	return nil
}
<commit_msg>Add ability for Gamebuildr to create git credentials<commit_after>package main

import (
	"errors"
	"os/exec"

	"github.com/herman-rogers/gogeta/config"
	"github.com/herman-rogers/gogeta/logger"
	git "github.com/libgit2/git2go"
	"github.com/satori/go.uuid"
)

const UP_TO_DATE = "UP_TO_DATE"
const NORMAL_MERGE = "NORMAL_MERGE"
const FAST_FORWARD = "FAST_FORWARD"

func GitProcessSQSMessages(gitReq scmServiceRequest) error {
	if gitReq.Usr == "" || gitReq.Project == "" || gitReq.Repo == "" {
		return errors.New("Missing Git Service Properties")
	}
	go GitShallowClone(gitReq)
	return nil
}

func GitShallowClone(data scmServiceRequest) {
	repoPath, relativePath := GetRepoPath(data.Project)
	cmd := exec.Command("git", "clone", "--depth", "2", data.Repo, repoPath)

	logfile := logger.GetLogFile()
	defer logfile.Close()

	cmd.Stdout = logfile
	cmd.Stderr = logfile

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Clone")

	cloneErr := cmd.Wait()
	logger.LogError(cloneErr, "Git Clone")
	if cloneErr == nil {
		gitData := &GogetaRepo{
			data.Id,
			data.Usr,
			data.Repo,
			relativePath,
			data.SCMType,
			data.Engine,
			data.Platform,
		}
		CreateGitCredentials(repoPath)
		go SaveRepo(*gitData)
		go TriggerMrRobotBuild(*gitData)
	}
}

func GetRepoPath(project string) (string, string) {
	var uuid uuid.UUID = uuid.NewV4()
	relativePath := project + "_" + uuid.String()
	repoPath := config.File.RepoPath + relativePath
	return repoPath, relativePath
}

func CreateGitCredentials(repoLocation string) {
	gitConfigUser := exec.Command("sudo", "cd", repoLocation, "&&", "git", "config", "user.name", "Gamebuildr")
	gitConfigEmail := exec.Command("sudo", "cd", repoLocation, "&&", "git", "config", "user.email", "contact@gamebuildr.io")

	gitConfigUserErr := gitConfigUser.Start()
	if gitConfigUserErr != nil {
		logger.LogError(gitConfigUserErr, "Git Config Set User.Name: ")
	}
	gitConfigEmailErr := gitConfigEmail.Start()
	if gitConfigEmailErr != nil {
		logger.LogError(gitConfigEmailErr, "Git Config Email")
	}
}

func UpdateGitRepositories() {
	repos := FindAllRepos()
	for i := 0; i < len(repos); i++ {
		folder := config.File.RepoPath + repos[i].Folder
		repo, err := git.OpenRepository(folder)
		if err != nil {
			logger.Error(err.Error())
			continue
		}
		if repo.IsBare() {
			continue
		}
		go GitPull(repos[i], repo)
	}
}

func GitPull(data GogetaRepo, repo *git.Repository) {
	FetchLatestsUpdates(data)
	remoteBranch := GetRemoteBranch(repo)
	remoteBranchID := remoteBranch.Target()

	analysis, err := AnalyzeLatestUpdates(repo, remoteBranch)
	if err != nil {
		logger.Error(err.Error())
	}
	mergeType := CheckMergeAnalysis(analysis)
	msg := "Git Merge " + mergeType

	switch mergeType {
	case UP_TO_DATE:
		break
	case NORMAL_MERGE:
		err := MergeNormal(repo, remoteBranch, remoteBranchID)
		BuildAfterMerge(err, msg, data)
		break
	case FAST_FORWARD:
		err := MergeFastForward(repo, remoteBranchID)
		BuildAfterMerge(err, msg, data)
		break
	}
}

func GetRemoteBranch(repo *git.Repository) *git.Reference {
	remoteBranch, err := repo.References.Lookup("refs/remotes/origin/master")
	logger.LogError(err, "Git Remote Branch")
	return remoteBranch
}

func FetchLatestsUpdates(data GogetaRepo) {
	cmd := exec.Command("git", "fetch", "--depth", "1", "origin", "master")
	cmd.Dir = config.File.RepoPath + data.Folder

	commandErr := cmd.Start()
	logger.LogError(commandErr, "Git Fetch")

	fetchErr := cmd.Wait()
	logger.LogError(fetchErr, "Git Fetch")
}

func AnalyzeLatestUpdates(repo *git.Repository, remoteBranch *git.Reference) (git.MergeAnalysis, error) {
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		var nullAnalysis git.MergeAnalysis
		return nullAnalysis, err
	}
	mergeHeads := make([]*git.AnnotatedCommit, 1)
	mergeHeads[0] = annotatedCommit
	analysis, _, err := repo.MergeAnalysis(mergeHeads)
	logger.LogError(err, "Git Merge Analysis")
	return analysis, nil
}

func CheckMergeAnalysis(analysis git.MergeAnalysis) string {
	if analysis&git.MergeAnalysisUpToDate != 0 {
		return UP_TO_DATE
	}
	if analysis&git.MergeAnalysisNormal != 0 {
		return NORMAL_MERGE
	}
	return FAST_FORWARD
}

func MergeNormal(repo *git.Repository, remoteBranch *git.Reference, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	annotatedCommit, err := repo.AnnotatedCommitFromRef(remoteBranch)
	if err != nil {
		return err
	}
	if err := repo.Merge([]*git.AnnotatedCommit{annotatedCommit}, nil, nil); err != nil {
		return err
	}
	index, err := repo.Index()
	if err != nil {
		return err
	}
	if index.HasConflicts() {
		return errors.New("Conflicts encountered, Please Resolve Them")
	}
	sig, err := repo.DefaultSignature()
	if err != nil {
		return err
	}
	treeId, err := index.WriteTree()
	if err != nil {
		return err
	}
	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return err
	}
	localCommit, err := repo.LookupCommit(head.Target())
	if err != nil {
		return err
	}
	remoteCommit, err := repo.LookupCommit(remoteBranchID)
	if err != nil {
		return err
	}
	repo.CreateCommit("HEAD", sig, sig, "", tree, localCommit, remoteCommit)
	repo.StateCleanup()
	return nil
}

func MergeFastForward(repo *git.Repository, remoteBranchID *git.Oid) error {
	head, err := repo.Head()
	if err != nil {
		return err
	}
	remoteTree, err := repo.LookupTree(remoteBranchID)
	if err != nil {
		return err
	}
	if err := repo.CheckoutTree(remoteTree, nil); err != nil {
		return err
	}
	branchRef, err := repo.References.Lookup("refs/heads/master")
	if err != nil {
		return err
	}
	branchRef.SetTarget(remoteBranchID, "")
	if _, err := head.SetTarget(remoteBranchID, ""); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package logmein

import (
	"net"
	"sync"
	"time"
)

// StatusValue is the Computer status
type StatusValue int

// Status enum
const (
	StatusOffline StatusValue = 0
	StatusOnline              = 1
	StatusSession             = 4
)

type computerRecord struct {
	hostID    uint64
	timestamp time.Time
	name      string
	ipAddress net.IP
	status    StatusValue
}

// Computer is a LogMeIn computer
type Computer struct {
	changes ChangeSet
	now     *computerRecord
	prev    *computerRecord
	lock    sync.RWMutex
}

// FIXME: right now we have no way to notice that a computer has been removed, aside
//   from skating through the []Computer to find old timestamps.  We should be detecting
//   deleted Computers in some efficient manner.

func (lmi *LMI) processComputerRaw(record computerRecord, event chan<- *Computer) {
	lmi.lock.Lock()
	defer lmi.lock.Unlock()

	computer, ok := lmi.computers[record.hostID]
	if !ok {
		// a new Computer
		computer = &Computer{
			now:     &record,
			changes: record.Diff(nil),
		}
		lmi.computers[computer.now.hostID] = computer
		if event != nil {
			event <- computer
		}
	} else {
		// an existing Computer
		func() {
			computer.lock.Lock()
			defer computer.lock.Unlock()
			changes := record.Diff(computer.now)
			if changes != 0 {
				// something changed
				computer.changes = changes
				computer.prev = computer.now
				computer.now = &record
				if event != nil {
					event <- computer
				}
			}
		}()
	}
}

// ChangeSet ...
type ChangeSet uint

// ChangeType ...
type ChangeType uint

// ChangeTypes...
const (
	ChangedNone ChangeType = 0
	IsNewRecord ChangeType = 1 << iota
	IsDeletedRecord
	ChangedHostID // ??? this one probably shouldn't ever happen since we key off of this value.
	ChangedName
	ChangedIPAddress
	ChangedStatus
)

// Diff returns a list of ChangeTypes based on a comparison of two computerRecords
// a = new record, b = previous record.  if b is nil, it's a new record.  if a is nil, maybe it's a deleted record?
func (a *computerRecord) Diff(b *computerRecord) ChangeSet {
	// note: we don't care about the timestamp here

	var changes ChangeSet
	if a == nil && b == nil {
		// why would this happen?
		return changes
	}
	selectAllChanges := false
	if a == nil {
		selectAllChanges = true
		changes |= ChangeSet(IsDeletedRecord)
	}
	if b == nil {
		selectAllChanges = true
		changes |= ChangeSet(IsNewRecord)
	}
	if selectAllChanges || a.hostID != b.hostID {
		changes |= ChangeSet(ChangedHostID)
	}
	if selectAllChanges || a.name != b.name {
		changes |= ChangeSet(ChangedName)
	}
	if selectAllChanges || !a.ipAddress.Equal(b.ipAddress) {
		changes |= ChangeSet(ChangedIPAddress)
	}
	if selectAllChanges || a.status != b.status {
		changes |= ChangeSet(ChangedStatus)
	}
	return changes
}

// Unchanged returns if this ChangeSet indicates no changes
func (cs ChangeSet) Unchanged() bool {
	return cs == 0
}

// Unchanged returns if this Computer is unchanged
// ???: when does this get set?  never?
func (c *Computer) Unchanged() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.Unchanged()
}

// IsDeleted returns if this ChangeSet indicates a deleted record
func (cs ChangeSet) IsDeleted() bool {
	return cs&ChangeSet(IsDeletedRecord) == ChangeSet(IsDeletedRecord)
}

// IsDeleted returns if this Computer was deleted
func (c *Computer) IsDeleted() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsDeleted()
}

// IsNew returns if this ChangeSet indicates a new record
func (cs ChangeSet) IsNew() bool {
	return cs&ChangeSet(IsNewRecord) == ChangeSet(IsNewRecord)
}

// IsNew returns if this Computer is new
func (c *Computer) IsNew() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsNew()
}

// IsChangedHostID returns if this ChangeSet indicates a changed hostID
func (cs ChangeSet) IsChangedHostID() bool {
	return cs&ChangeSet(ChangedHostID) == ChangeSet(ChangedHostID)
}

// IsChangedHostID returns if this Computer's hostID field changed
func (c *Computer) IsChangedHostID() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedHostID()
}

// IsChangedName returns if this ChangeSet indicates a changed name
func (cs ChangeSet) IsChangedName() bool {
	return cs&ChangeSet(ChangedName) == ChangeSet(ChangedName)
}

// IsChangedName returns if this Computer's name field changed
func (c *Computer) IsChangedName() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedName()
}

// IsChangedIPAddress returns if this ChangeSet indicates a changed IP
func (cs ChangeSet) IsChangedIPAddress() bool {
	return cs&ChangeSet(ChangedIPAddress) == ChangeSet(ChangedIPAddress)
}

// IsChangedIPAddress returns if this Computer's IP field changed
func (c *Computer) IsChangedIPAddress() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedIPAddress()
}

// IsChangedStatus returns if this ChangeSet indicates a changed status
func (cs ChangeSet) IsChangedStatus() bool {
	return cs&ChangeSet(ChangedStatus) == ChangeSet(ChangedStatus)
}

// IsChangedStatus returns if this Computer's status field changed
func (c *Computer) IsChangedStatus() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedStatus()
}

// HostID returns the current HostID of the computer
func (c *Computer) HostID() uint64 {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.hostID
}

// OldHostID returns the former HostID of the computer
func (c *Computer) OldHostID() uint64 {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return 0
	}
	return c.prev.hostID
}

// Name returns the current Name of the computer
func (c *Computer) Name() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.name
}

// OldName returns the former Name of the computer
func (c *Computer) OldName() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return ""
	}
	return c.prev.name
}

// IPAddress returns the current IPAddress of the computer
func (c *Computer) IPAddress() net.IP {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.ipAddress
}

// OldIPAddress returns the former IPAddress of the computer
func (c *Computer) OldIPAddress() net.IP {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return nil
	}
	return c.prev.ipAddress
}

// Status returns the current Status of the computer
func (c *Computer) Status() StatusValue {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.status
}

// OldStatus returns the former Status of the computer
func (c *Computer) OldStatus() StatusValue {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return 0
	}
	return c.prev.status
}

// Timestamp returns the current Timestamp of the computer
func (c *Computer) Timestamp() time.Time {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.timestamp
}

// OldTimestamp returns the former Timestamp of the computer
func (c *Computer) OldTimestamp() time.Time {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return time.Time{}
	}
	return c.prev.timestamp
}
<commit_msg>Added Computers() and GetChangeSet()<commit_after>package logmein

import (
	"net"
	"sync"
	"time"
)

// StatusValue is the Computer status
type StatusValue int

// Status enum
const (
	StatusOffline StatusValue = 0
	StatusOnline              = 1
	StatusSession             = 4
)

type computerRecord struct {
	hostID    uint64
	timestamp time.Time
	name      string
	ipAddress net.IP
	status    StatusValue
}

// Computer is a LogMeIn computer
type Computer struct {
	changes ChangeSet
	now     *computerRecord
	prev    *computerRecord
	lock    sync.RWMutex
}

// FIXME: right now we have no way to notice that a computer has been removed, aside
//   from skating through the []Computer to find old timestamps.  We should be detecting
//   deleted Computers in some efficient manner.

func (lmi *LMI) processComputerRaw(record computerRecord, event chan<- *Computer) {
	lmi.lock.Lock()
	defer lmi.lock.Unlock()

	computer, ok := lmi.computers[record.hostID]
	if !ok {
		// a new Computer
		computer = &Computer{
			now:     &record,
			changes: record.Diff(nil),
		}
		lmi.computers[computer.now.hostID] = computer
		if event != nil {
			event <- computer
		}
	} else {
		// an existing Computer
		func() {
			computer.lock.Lock()
			defer computer.lock.Unlock()
			changes := record.Diff(computer.now)
			if changes != 0 {
				// something changed
				computer.changes = changes
				computer.prev = computer.now
				computer.now = &record
				if event != nil {
					event <- computer
				}
			}
		}()
	}
}

// ChangeSet ...
type ChangeSet uint

// ChangeType ...
type ChangeType uint

// ChangeTypes...
const (
	ChangedNone ChangeType = 0
	IsNewRecord ChangeType = 1 << iota
	IsDeletedRecord
	ChangedHostID // ??? this one probably shouldn't ever happen since we key off of this value.
	ChangedName
	ChangedIPAddress
	ChangedStatus
)

// Computers returns all computers
func (lmi *LMI) Computers() map[uint64]*Computer {
	return lmi.computers
}

// Diff returns a list of ChangeTypes based on a comparison of two computerRecords
// a = new record, b = previous record.  if b is nil, it's a new record.  if a is nil, maybe it's a deleted record?
func (a *computerRecord) Diff(b *computerRecord) ChangeSet {
	// note: we don't care about the timestamp here

	var changes ChangeSet
	if a == nil && b == nil {
		// why would this happen?
		return changes
	}
	selectAllChanges := false
	if a == nil {
		selectAllChanges = true
		changes |= ChangeSet(IsDeletedRecord)
	}
	if b == nil {
		selectAllChanges = true
		changes |= ChangeSet(IsNewRecord)
	}
	if selectAllChanges || a.hostID != b.hostID {
		changes |= ChangeSet(ChangedHostID)
	}
	if selectAllChanges || a.name != b.name {
		changes |= ChangeSet(ChangedName)
	}
	if selectAllChanges || !a.ipAddress.Equal(b.ipAddress) {
		changes |= ChangeSet(ChangedIPAddress)
	}
	if selectAllChanges || a.status != b.status {
		changes |= ChangeSet(ChangedStatus)
	}
	return changes
}

// GetChangeSet returns the ChangeSet between the current and prev version of this Computer
func (c *Computer) GetChangeSet() ChangeSet {
	return c.now.Diff(c.prev)
}

// Unchanged returns if this ChangeSet indicates no changes
func (cs ChangeSet) Unchanged() bool {
	return cs == 0
}

// Unchanged returns if this Computer is unchanged
// ???: when does this get set?  never?
func (c *Computer) Unchanged() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.Unchanged()
}

// IsDeleted returns if this ChangeSet indicates a deleted record
func (cs ChangeSet) IsDeleted() bool {
	return cs&ChangeSet(IsDeletedRecord) == ChangeSet(IsDeletedRecord)
}

// IsDeleted returns if this Computer was deleted
func (c *Computer) IsDeleted() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsDeleted()
}

// IsNew returns if this ChangeSet indicates a new record
func (cs ChangeSet) IsNew() bool {
	return cs&ChangeSet(IsNewRecord) == ChangeSet(IsNewRecord)
}

// IsNew returns if this Computer is new
func (c *Computer) IsNew() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsNew()
}

// IsChangedHostID returns if this ChangeSet indicates a changed hostID
func (cs ChangeSet) IsChangedHostID() bool {
	return cs&ChangeSet(ChangedHostID) == ChangeSet(ChangedHostID)
}

// IsChangedHostID returns if this Computer's hostID field changed
func (c *Computer) IsChangedHostID() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedHostID()
}

// IsChangedName returns if this ChangeSet indicates a changed name
func (cs ChangeSet) IsChangedName() bool {
	return cs&ChangeSet(ChangedName) == ChangeSet(ChangedName)
}

// IsChangedName returns if this Computer's name field changed
func (c *Computer) IsChangedName() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedName()
}

// IsChangedIPAddress returns if this ChangeSet indicates a changed IP
func (cs ChangeSet) IsChangedIPAddress() bool {
	return cs&ChangeSet(ChangedIPAddress) == ChangeSet(ChangedIPAddress)
}

// IsChangedIPAddress returns if this Computer's IP field changed
func (c *Computer) IsChangedIPAddress() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedIPAddress()
}

// IsChangedStatus returns if this ChangeSet indicates a changed status
func (cs ChangeSet) IsChangedStatus() bool {
	return cs&ChangeSet(ChangedStatus) == ChangeSet(ChangedStatus)
}

// IsChangedStatus returns if this Computer's status field changed
func (c *Computer) IsChangedStatus() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.changes.IsChangedStatus()
}

// HostID returns the current HostID of the computer
func (c *Computer) HostID() uint64 {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.hostID
}

// OldHostID returns the former HostID of the computer
func (c *Computer) OldHostID() uint64 {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return 0
	}
	return c.prev.hostID
}

// Name returns the current Name of the computer
func (c *Computer) Name() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.name
}

// OldName returns the former Name of the computer
func (c *Computer) OldName() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return ""
	}
	return c.prev.name
}

// IPAddress returns the current IPAddress of the computer
func (c *Computer) IPAddress() net.IP {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.ipAddress
}

// OldIPAddress returns the former IPAddress of the computer
func (c *Computer) OldIPAddress() net.IP {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return nil
	}
	return c.prev.ipAddress
}

// Status returns the current Status of the computer
func (c *Computer) Status() StatusValue {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.status
}

// OldStatus returns the former Status of the computer
func (c *Computer) OldStatus() StatusValue {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return 0
	}
	return c.prev.status
}

// Timestamp returns the current Timestamp of the computer
func (c *Computer) Timestamp() time.Time {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.now.timestamp
}

// OldTimestamp returns the former Timestamp of the computer
func (c *Computer) OldTimestamp() time.Time {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if c.prev == nil {
		return time.Time{}
	}
	return c.prev.timestamp
}
<|endoftext|>


<commit_before>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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 handlers

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/venicegeo/pzsvc-pdal/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/venicegeo/pzsvc-pdal/functions"
	"github.com/venicegeo/pzsvc-pdal/objects"
	"github.com/venicegeo/pzsvc-pdal/utils"
)

type functionFunc func(http.ResponseWriter, *http.Request,
	*objects.JobOutput, objects.JobInput)

// makeFunction wraps the individual PDAL functions.
// Parse the input and output filenames, creating files as needed. Download the
// input data and upload the output data.
func makeFunction(fn func(http.ResponseWriter, *http.Request,
	*objects.JobOutput, objects.JobInput, string, string)) functionFunc {
	return func(w http.ResponseWriter, r *http.Request, res *objects.JobOutput,
		msg objects.JobInput) {
		var inputName, outputName string
		var fileIn, fileOut *os.File

		// Split the source S3 key string, interpreting the last element as the
		// input filename. Create the input file, throwing 500 on error.
		keySlice := strings.Split(msg.Source.Key, "/")
		inputName = keySlice[len(keySlice)-1]
		fileIn, err := os.Create(inputName)
		if err != nil {
			utils.InternalError(w, r, *res, err.Error())
			return
		}
		defer fileIn.Close()

		// If provided, split the destination S3 key string, interpreting the last
		// element as the output filename. Create the output file, throwing 500 on
		// error.
		if len(msg.Destination.Key) > 0 {
			keySlice = strings.Split(msg.Destination.Key, "/")
			outputName = keySlice[len(keySlice)-1]
			fileOut, err = os.Create(outputName)
			if err != nil {
				utils.InternalError(w, r, *res, err.Error())
				return
			}
			defer fileOut.Close()
		}

		// Download the source data from S3, throwing 500 on error.
		err = utils.S3Download(fileIn, msg.Source.Bucket, msg.Source.Key)
		if err != nil {
			utils.InternalError(w, r, *res, err.Error())
			return
		}

		// Run the PDAL function.
		fn(w, r, res, msg, inputName, outputName)

		// If an output has been created, upload the destination data to S3,
		// throwing 500 on error.
		if len(msg.Destination.Key) > 0 {
			err = utils.S3Upload(fileOut, msg.Destination.Bucket, msg.Destination.Key)
			if err != nil {
				utils.InternalError(w, r, *res, err.Error())
				return
			}
		}
	}
}

// PdalHandler handles PDAL jobs.
func PdalHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	// Create the job output message. No matter what happens, we should always be
	// able to populate the StartedAt field.
	var res objects.JobOutput
	res.StartedAt = time.Now()

	// There should always be a body, else how are we to know what to do? Throw
	// 400 if missing.
	if r.Body == nil {
		utils.BadRequest(w, r, res, "No JSON")
		return
	}

	// Throw 500 if we cannot read the body.
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		utils.InternalError(w, r, res, err.Error())
		return
	}

	// Throw 400 if we cannot unmarshal the body as a valid JobInput.
	var msg objects.JobInput
	if err := json.Unmarshal(b, &msg); err != nil {
		utils.BadRequest(w, r, res, err.Error())
		return
	}

	// Throw 400 if the JobInput does not specify a function.
	if msg.Function == nil {
		utils.BadRequest(w, r, res, "Must provide a function")
		return
	}

	// If everything is okay up to this point, we will echo the JobInput in the
	// JobOutput and mark the job as Running.
	res.Input = msg
	utils.UpdateJobManager(objects.Running, r)

	// Make/execute the requested function.
	switch *msg.Function {
	case "info":
		makeFunction(functions.InfoFunction)(w, r, &res, msg)

	case "ground":
		makeFunction(functions.GroundFunction)(w, r, &res, msg)

	case "height":
		makeFunction(functions.HeightFunction)(w, r, &res, msg)

	case "dtm":
		makeFunction(functions.DtmFunction)(w, r, &res, msg)

	case "dart":
		makeFunction(functions.DartFunction)(w, r, &res, msg)

	case "list":
		out := []byte(`{"functions":["info","ground","height","dtm","dart","list"]}`)

		if err := json.Unmarshal(out, &res.Response); err != nil {
			log.Fatal(err)
		}

	// list options for named function
	case "options":
		// start with only info options as a test
		foo := functions.NewGroundOptions()
		bar, _ := json.Marshal(foo)
		if err := json.Unmarshal(bar, &res.Response); err != nil {
			log.Fatal(err)
		}

	// An unrecognized function will result in 400 error, with message explaining
	// how to list available functions.
	default:
		utils.BadRequest(w, r, res, "")
		return
	}

	// If we made it here, we can record the FinishedAt time, notify the job
	// manager of success, and return 200.
	res.FinishedAt = time.Now()
	utils.Okay(w, r, res, "Success!")
}
<commit_msg>Dynamically populate options, just need to settle on a way to request<commit_after>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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 handlers

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/venicegeo/pzsvc-pdal/Godeps/_workspace/src/github.com/julienschmidt/httprouter"
	"github.com/venicegeo/pzsvc-pdal/functions"
	"github.com/venicegeo/pzsvc-pdal/objects"
	"github.com/venicegeo/pzsvc-pdal/utils"
)

type functionFunc func(http.ResponseWriter, *http.Request,
	*objects.JobOutput, objects.JobInput)

// makeFunction wraps the individual PDAL functions.
// Parse the input and output filenames, creating files as needed. Download the
// input data and upload the output data.
func makeFunction(fn func(http.ResponseWriter, *http.Request,
	*objects.JobOutput, objects.JobInput, string, string)) functionFunc {
	return func(w http.ResponseWriter, r *http.Request, res *objects.JobOutput,
		msg objects.JobInput) {
		var inputName, outputName string
		var fileIn, fileOut *os.File

		// Split the source S3 key string, interpreting the last element as the
		// input filename. Create the input file, throwing 500 on error.
		keySlice := strings.Split(msg.Source.Key, "/")
		inputName = keySlice[len(keySlice)-1]
		fileIn, err := os.Create(inputName)
		if err != nil {
			utils.InternalError(w, r, *res, err.Error())
			return
		}
		defer fileIn.Close()

		// If provided, split the destination S3 key string, interpreting the last
		// element as the output filename. Create the output file, throwing 500 on
		// error.
		if len(msg.Destination.Key) > 0 {
			keySlice = strings.Split(msg.Destination.Key, "/")
			outputName = keySlice[len(keySlice)-1]
			fileOut, err = os.Create(outputName)
			if err != nil {
				utils.InternalError(w, r, *res, err.Error())
				return
			}
			defer fileOut.Close()
		}

		// Download the source data from S3, throwing 500 on error.
		err = utils.S3Download(fileIn, msg.Source.Bucket, msg.Source.Key)
		if err != nil {
			utils.InternalError(w, r, *res, err.Error())
			return
		}

		// Run the PDAL function.
		fn(w, r, res, msg, inputName, outputName)

		// If an output has been created, upload the destination data to S3,
		// throwing 500 on error.
		if len(msg.Destination.Key) > 0 {
			err = utils.S3Upload(fileOut, msg.Destination.Bucket, msg.Destination.Key)
			if err != nil {
				utils.InternalError(w, r, *res, err.Error())
				return
			}
		}
	}
}

// PdalHandler handles PDAL jobs.
func PdalHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	// Create the job output message. No matter what happens, we should always be
	// able to populate the StartedAt field.
	var res objects.JobOutput
	res.StartedAt = time.Now()

	// There should always be a body, else how are we to know what to do? Throw
	// 400 if missing.
	if r.Body == nil {
		utils.BadRequest(w, r, res, "No JSON")
		return
	}

	// Throw 500 if we cannot read the body.
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		utils.InternalError(w, r, res, err.Error())
		return
	}

	// Throw 400 if we cannot unmarshal the body as a valid JobInput.
	var msg objects.JobInput
	if err := json.Unmarshal(b, &msg); err != nil {
		utils.BadRequest(w, r, res, err.Error())
		return
	}

	// Throw 400 if the JobInput does not specify a function.
	if msg.Function == nil {
		utils.BadRequest(w, r, res, "Must provide a function")
		return
	}

	// If everything is okay up to this point, we will echo the JobInput in the
	// JobOutput and mark the job as Running.
	res.Input = msg
	utils.UpdateJobManager(objects.Running, r)

	// Make/execute the requested function.
	switch *msg.Function {
	case "info":
		makeFunction(functions.InfoFunction)(w, r, &res, msg)

	case "ground":
		makeFunction(functions.GroundFunction)(w, r, &res, msg)

	case "height":
		makeFunction(functions.HeightFunction)(w, r, &res, msg)

	case "dtm":
		makeFunction(functions.DtmFunction)(w, r, &res, msg)

	case "dart":
		makeFunction(functions.DartFunction)(w, r, &res, msg)

	case "list":
		out := []byte(`{"functions":["info","ground","height","dtm","dart","list"]}`)

		if err := json.Unmarshal(out, &res.Response); err != nil {
			log.Fatal(err)
		}

	// list options for named function
	case "options":
		type AllOptions struct {
			Ground *functions.GroundOptions `json:"ground,omitempty"`
			Info   *functions.InfoOptions   `json:"info,omitempty"`
			Dtm    *functions.DtmOptions    `json:"dtm,omitempty"`
		}
		var a AllOptions
		a.Ground = functions.NewGroundOptions()
		a.Info = functions.NewInfoOptions()
		a.Dtm = functions.NewDtmOptions()
		bar, _ := json.Marshal(a)
		if err := json.Unmarshal(bar, &res.Response); err != nil {
			log.Fatal(err)
		}

	// An unrecognized function will result in 400 error, with message explaining
	// how to list available functions.
	default:
		utils.BadRequest(w, r, res, "")
		return
	}

	// If we made it here, we can record the FinishedAt time, notify the job
	// manager of success, and return 200.
	res.FinishedAt = time.Now()
	utils.Okay(w, r, res, "Success!")
}
<|endoftext|>


<commit_before>package slackapi

// SearchArgs defines the data to send to the API service.
type SearchArgs struct {
	Query     string `json:"query"`
	Count     int    `json:"count"`
	Highlight bool   `json:"highlight"`
	Page      int    `json:"page"`
	Sort      string `json:"sort"`
	SortDir   string `json:"sort_dir"`
}

type ResponseSearch struct {
	Response
	Query    string         `json:"query"`
	Files    SearchFiles    `json:"files"`
	Posts    SearchPosts    `json:"posts"`
	Messages SearchMessages `json:"messages"`
}

type SearchMessages struct {
	Matches    []SearchMatches `json:"matches"`
	Pagination Pagination      `json:"pagination"`
	Paging     Paging          `json:"paging"`
	Total      int             `json:"total"`
}

type SearchFiles struct {
	Matches    []File     `json:"matches"`
	Pagination Pagination `json:"pagination"`
	Paging     Paging     `json:"paging"`
	Total      int        `json:"total"`
}

type SearchPosts struct {
	Matches    []interface{} `json:"matches"`
	Pagination Pagination    `json:"pagination"`
	Paging     Paging        `json:"paging"`
	Total      int           `json:"total"`
}

type SearchMatches struct {
	// TODO(cixtor): find a way to convert this into a Message. Currently, the
	// problem is that Channel is not a string, which is what we get from the
	// API when we get a Message.
	Attachments []Attachment  `json:"attachments"`
	Channel     SearchChannel `json:"channel"`
	IID         string        `json:"iid"`
	Permalink   string        `json:"permalink"`
	Team        string        `json:"team"`
	Text        string        `json:"text"`
	Timestamp   string        `json:"ts"`
	Type        string        `json:"type"`
	User        string        `json:"user"`
	Username    string        `json:"username"`
}

type SearchChannel struct {
	ID                 string        `json:"id"`
	IsExtShared        bool          `json:"is_ext_shared"`
	IsMpim             bool          `json:"is_mpim"`
	IsOrgShared        bool          `json:"is_org_shared"`
	IsPendingExtShared bool          `json:"is_pending_ext_shared"`
	IsPrivate          bool          `json:"is_private"`
	IsShared           bool          `json:"is_shared"`
	Name               string        `json:"name"`
	PendingShared      []interface{} `json:"pending_shared"`
}

func (s *SlackAPI) searchStuff(action string, data SearchArgs) ResponseSearch {
	// set default value for optional field.
	if data.Sort != "timestamp" {
		data.Sort = "score"
	}

	// set default value for optional field.
	if data.SortDir != "asc" {
		data.SortDir = "desc"
	}

	var response ResponseSearch
	s.getRequest(&response, action, data)
	return response
}

// SearchAll searches for messages and files matching a query.
func (s *SlackAPI) SearchAll(data SearchArgs) ResponseSearch {
	return s.searchStuff("search.all", data)
}

// SearchFiles searches for files matching a query.
func (s *SlackAPI) SearchFiles(data SearchArgs) ResponseSearch {
	return s.searchStuff("search.files", data)
}

// SearchMessages searches for messages matching a query.
func (s *SlackAPI) SearchMessages(data SearchArgs) ResponseSearch {
	return s.searchStuff("search.messages", data)
}
<commit_msg>Add field to channel search to know if it is really a channel<commit_after>package slackapi

// SearchArgs defines the data to send to the API service.
type SearchArgs struct {
	Query     string `json:"query"`
	Count     int    `json:"count"`
	Highlight bool   `json:"highlight"`
	Page      int    `json:"page"`
	Sort      string `json:"sort"`
	SortDir   string `json:"sort_dir"`
}

type ResponseSearch struct {
	Response
	Query    string         `json:"query"`
	Files    SearchFiles    `json:"files"`
	Posts    SearchPosts    `json:"posts"`
	Messages SearchMessages `json:"messages"`
}

type SearchMessages struct {
	Matches    []SearchMatches `json:"matches"`
	Pagination Pagination      `json:"pagination"`
	Paging     Paging          `json:"paging"`
	Total      int             `json:"total"`
}

type SearchFiles struct {
	Matches    []File     `json:"matches"`
	Pagination Pagination `json:"pagination"`
	Paging     Paging     `json:"paging"`
	Total      int        `json:"total"`
}

type SearchPosts struct {
	Matches    []interface{} `json:"matches"`
	Pagination Pagination    `json:"pagination"`
	Paging     Paging        `json:"paging"`
	Total      int           `json:"total"`
}

type SearchMatches struct {
	// TODO(cixtor): find a way to convert this into a Message. Currently, the
	// problem is that Channel is not a string, which is what we get from the
	// API when we get a Message.
	Attachments []Attachment  `json:"attachments"`
	Channel     SearchChannel `json:"channel"`
	IID         string        `json:"iid"`
	Permalink   string        `json:"permalink"`
	Team        string        `json:"team"`
	Text        string        `json:"text"`
	Timestamp   string        `json:"ts"`
	Type        string        `json:"type"`
	User        string        `json:"user"`
	Username    string        `json:"username"`
}

type SearchChannel struct {
	ID                 string        `json:"id"`
	IsChannel          bool          `json:"is_channel"`
	IsExtShared        bool          `json:"is_ext_shared"`
	IsMpim             bool          `json:"is_mpim"`
	IsOrgShared        bool          `json:"is_org_shared"`
	IsPendingExtShared bool          `json:"is_pending_ext_shared"`
	IsPrivate          bool          `json:"is_private"`
	IsShared           bool          `json:"is_shared"`
	Name               string        `json:"name"`
	PendingShared      []interface{} `json:"pending_shared"`
}

func (s *SlackAPI) searchStuff(action string, data SearchArgs) ResponseSearch {
	// set default value for optional field.
	if data.Sort != "timestamp" {
		data.Sort = "score"
	}

	// set default value for optional field.
	if data.SortDir != "asc" {
		data.SortDir = "desc"
	}

	var response ResponseSearch
	s.getRequest(&response, action, data)
	return response
}

// SearchAll searches for messages and files matching a query.
func (s *SlackAPI) SearchAll(data SearchArgs) ResponseSearch {
	return s.searchStuff("search.all", data)
}

// SearchFiles searches for files matching a query.
func (s *SlackAPI) SearchFiles(data SearchArgs) ResponseSearch {
	return s.searchStuff("search.files", data)
}

// SearchMessages searches for messages matching a query.
func (s *SlackAPI) SearchMessages(data SearchArgs) ResponseSearch {
	return s.searchStuff("search.messages", data)
}
<|endoftext|>


<commit_before>package cuckoo

const MAX_SEARCH_DEPTH int = 10

type mv struct {
	key  keyt
	from int
	to   int
	tobn int
}

func (m *cmap) search(bins ...int) []mv {
	for depth := 1; depth < MAX_SEARCH_DEPTH; depth++ {
		for _, b := range bins {
			path := m.find(nil, b, depth)
			if path != nil {
				return path
			}
		}
	}
	return nil
}

func (m *cmap) find(path []mv, bin int, depth int) []mv {
	if depth >= 0 {
		for i := 0; i < ASSOCIATIVITY; i++ {
			if !m.bins[bin].vals[i].present() {
				return path
			}

			path_ := make([]mv, len(path)+1)
			for i := range path {
				path_[i] = path[i]
			}

			from := bin
			to := from
			bno := m.bins[bin].vals[i].bno + 1
			key := m.bins[bin].vals[i].key
			for ; bno < int(m.hashes); bno++ {
				to = m.bin(bno, key)
				if to != from {
					break
				}
			}
			if to == from {
				continue
			}

			path_[len(path)] = mv{key, from, to, bno}
			if m.bins[to].available() {
				return path_
			} else {
				return m.find(path_, to, depth-1)
			}
		}
	}
	return nil
}
<commit_msg>Wrap around hashes<commit_after>package cuckoo

const MAX_SEARCH_DEPTH int = 10

type mv struct {
	key  keyt
	from int
	to   int
	tobn int
}

func (m *cmap) search(bins ...int) []mv {
	for depth := 1; depth < MAX_SEARCH_DEPTH; depth++ {
		for _, b := range bins {
			path := m.find(nil, b, depth)
			if path != nil {
				return path
			}
		}
	}
	return nil
}

func (m *cmap) find(path []mv, bin int, depth int) []mv {
	if depth >= 0 {
		for i := 0; i < ASSOCIATIVITY; i++ {
			if !m.bins[bin].vals[i].present() {
				return path
			}

			path_ := make([]mv, len(path)+1)
			for i := range path {
				path_[i] = path[i]
			}

			from := bin
			to := from
			bno := m.bins[bin].vals[i].bno + 1
			key := m.bins[bin].vals[i].key
			for i := 0; i < int(m.hashes); i++ {
				bno = (bno + 1) % int(m.hashes)
				to = m.bin(bno, key)
				// XXX: could potentially try all bins here and
				// check each for available()? extra-broad
				// search...
				if to != from {
					break
				}
			}
			if to == from {
				continue
			}

			path_[len(path)] = mv{key, from, to, bno}
			if m.bins[to].available() {
				return path_
			} else {
				return m.find(path_, to, depth-1)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package crud

import (
	"fmt"
	"strconv"
	"strings"
)

//JoinCon join条件
type JoinCon struct {
	TableName string
	Condition string
}

//JoinCons join条件slice
type JoinCons []JoinCon

//HaveTable join条件中是否已经添加了这张表的join
func (jc JoinCons) HaveTable(tableName string) bool {
	for _, v := range jc {
		if v.TableName == tableName {
			return true
		}
	}
	return false
}

//WhereCon where条件
type WhereCon struct {
	Query string
	Args  []interface{}
}

//Search 搜索结构体
type Search struct {
	table             *Table
	fields            []string
	tableName         string
	joinConditions    JoinCons
	whereConditions   []WhereCon
	orderbyConditions []string
	group             string
	with              string
	having            string
	limit             interface{}
	offset            interface{}

	query string
	args  []interface{}
	raw   bool
}

//Clone 克隆一个当前结构体
func (s *Search) Clone() *Search {
	clone := *s
	return &clone
}

//Fields 需要查询的字段
func (s *Search) Fields(args ...string) *Search {
	s.fields = append(s.fields, args...)
	return s
}

//Where where语法
func (s *Search) Where(query string, values ...interface{}) *Search {
	s.whereConditions = append(s.whereConditions, WhereCon{Query: query, Args: values})
	return s
}

//WhereID id = ?
func (s *Search) WhereID(id interface{}) *Search {
	s.whereConditions = append(s.whereConditions, WhereCon{Query: s.tableName + ".id = ?", Args: []interface{}{id}})
	return s
}

//In in语法
func (s *Search) In(field string, args ...interface{}) *Search {
	//In没有参数的话SQL就会报错
	if len(args) == 0 {
		return s
	}
	s.whereConditions = append(s.whereConditions, WhereCon{Query: fmt.Sprintf("%s IN (%s)", field, placeholder(len(args))), Args: args})
	return s
}

//Joins join语法，自动连表。
func (s *Search) Joins(tablename string, condition ...string) *Search {
	if len(condition) == 1 {
		s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: condition[0]})
	} else {
		if s.table.tableColumns[tablename].HaveColumn(s.tableName + "id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", tablename, s.tableName+"id", s.tableName)})
		} else if s.table.tableColumns[tablename].HaveColumn(s.tableName + "_id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", tablename, s.tableName+"_id", s.tableName)})
		} else if s.table.tableColumns[s.tableName].HaveColumn(tablename + "id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", s.tableName, tablename+"id", tablename)})
		} else if s.table.tableColumns[s.tableName].HaveColumn(tablename + "_id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", s.tableName, tablename+"_id", tablename)})
		}
	}
	return s
}

//OrderBy OrderBy
func (s *Search) OrderBy(field string, isDESC ...bool) *Search {
	if len(isDESC) > 0 && isDESC[0] {
		s.orderbyConditions = append(s.orderbyConditions, field+" DESC")
	} else {
		s.orderbyConditions = append(s.orderbyConditions, field+" ASC")
	}
	return s
}

//TableName tableName
func (s *Search) TableName(name string) *Search {
	s.tableName = name
	return s
}

//Limit LIMIT ?
func (s *Search) Limit(limit interface{}) *Search {
	s.limit = limit
	return s
}

//Offset OFFSET ?
func (s *Search) Offset(offset interface{}) *Search {
	s.offset = offset
	return s
}

//Group GROUP BY
func (s *Search) Group(query string) *Search {
	s.group = query
	return s
}

//Parse 将各个条件整合成可以查询的SQL语句和参数
func (s *Search) Parse() (string, []interface{}) {
	if s.raw == true {
		return s.query, s.args
	}
	var (
		fields       string
		joins        string
		paddingwhere string
		wheres       []string
		orderby      string
		limit        string
		offset       string
	)
	if s.table.tableColumns[s.tableName].HaveColumn(IsDeleted) {
		s.Where("is_deleted = ?", 0)
	}
	s.query = ""
	s.args = []interface{}{}
	if len(s.fields) == 0 {
		fields = "*"
	} else {
		for i := 0; i < len(s.fields); i++ {
			var tableName string
			s.fields[i], tableName, _ = s.warpField(s.fields[i])
			if tableName != s.tableName {
				if !s.joinConditions.HaveTable(tableName) {
					s.Joins(tableName)
				}
			}
		}
		fields = strings.Join(s.fields, ",")
	}
	for _, joincon := range s.joinConditions {
		joins += fmt.Sprintf(" LEFT JOIN %s ON %s", joincon.TableName, joincon.Condition)
	}
	for _, wherecon := range s.whereConditions {
		paddingwhere = " WHERE "
		wheres = append(wheres, wherecon.Query)
		s.args = append(s.args, wherecon.Args...)
	}
	if len(s.orderbyConditions) > 0 {
		orderby = " ORDER BY " + strings.Join(s.orderbyConditions, ",")
	}
	if s.limit != nil {
		limit = " LIMIT ?"
		s.args = append(s.args, s.limit)
	}
	if s.offset != nil {
		offset = " OFFSET ?"
		s.args = append(s.args, s.offset)
	}
	s.query = fmt.Sprintf("SELECT %s FROM `%s`%s%s%s%s%s%s", fields, s.tableName, joins, paddingwhere, strings.Join(wheres, " AND "), orderby, limit, offset)
	s.raw = true
	return s.query, s.args
}

//DISTINCT XX
//DISTICT XXX.XXX AS aaa
//XXX.XXX AS aaa
func (s *Search) warpField(field string) (warpStr string, tablename string, fieldname string) {
	if strings.Contains(field, " ") {
		if strings.Contains(field, "AS") {
			sp := strings.Split(field, " ")
			for i := 0; i < len(sp); i++ {
				if sp[i] == "AS" {
					sp[i-1], tablename, fieldname = s.warpFieldSingel(sp[i-1])
					warpStr = strings.Join(sp, " ")
					break
				}
			}
		} else {
			sp := strings.Split(field, " ")
			sp[len(sp)-1], tablename, fieldname = s.warpFieldSingel(sp[len(sp)-1])
			warpStr = strings.Join(sp, " ")
		}
	} else {
		return s.warpFieldSingel(field)
	}
	return
}

func (s *Search) warpFieldSingel(field string) (warpStr string, tablename string, fieldname string) {

	if strings.Contains(field, ".") {
		sp := strings.Split(field, ".")
		tablename = sp[0]
		fieldname = sp[1]
		if strings.Contains(field, "`") {
			warpStr = field
			return
		}
		warpStr = strings.Replace(field, ".", ".`", 1) + "`"
	} else {
		tablename = s.tableName
		fieldname = field
		if strings.Contains(field, "`") {
			warpStr = field
			return
		}
		switch field {
		case "*", "COUNT(*)":
			warpStr = field
		default:
			warpStr = "`" + field + "`"
		}
	}
	return
}

//结果展示

//RawMap RawMap
func (s *Search) RawMap() RowMap {
	return s.RowMap()
}

//RawsMap RawsMap
func (s *Search) RawsMap() RowsMap {
	return s.RowsMap()
}

//RawsMapInterface RawsMapInterface
func (s *Search) RawsMapInterface() RowsMapInterface {
	return s.RowsMapInterface()
}

//RowMap RowMap
func (s *Search) RowMap() RowMap {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowMap()
}

// func (s *Search) String() string {
// 	return s.table.Query(s.Parse()).String()
// }

//SQLRows SQLRows
func (s *Search) SQLRows() *SQLRows {
	query, args := s.Parse()
	return s.table.Query(query, args...)
}

//RowsMap RowsMap
func (s *Search) RowsMap() RowsMap {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowsMap()
}

//RowMapInterface RowMapInterface
func (s *Search) RowMapInterface() RowMapInterface {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowMapInterface()
}

//RowsMapInterface RowsMapInterface
func (s *Search) RowsMapInterface() RowsMapInterface {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowsMapInterface()
}

//DoubleSlice DoubleSlice
func (s *Search) DoubleSlice() (map[string]int, [][]string) {
	query, args := s.Parse()
	return s.table.Query(query, args...).DoubleSlice()
}

//Int 如果指定字段，则返回指定字段的int值，否则返回第一个字段作为int值返回。
func (s *Search) Int(args ...string) int {
	row := s.RowMap()
	if len(args) == 0 {
		for _, v := range row {
			i, _ := strconv.Atoi(v)
			return i
		}
	} else {
		i, _ := strconv.Atoi(row[args[0]])
		return i
	}
	return 0
}

//String like int
func (s *Search) String(args ...string) string {
	row := s.RowMap()
	if len(args) == 0 {
		for _, v := range row {
			return v
		}
	} else {
		return row[args[0]]
	}
	return ""
}

//Finds 将查询的结构放入到结构体当中
func (s *Search) Finds(v interface{}) {
	query, args := s.Parse()
	s.table.FindAll(v, append([]interface{}{query}, args...)...)
}

//Count 计算这次查询结果的个数
func (s *Search) Count() int {
	var count int
	s.fields = []string{"COUNT(*)"}
	query, args := s.Parse()
	s.table.Query(query, args...).Find(&count)
	return count
}
<commit_msg>https://github.com/shesuyo/crud/issues/13<commit_after>package crud

import (
	"fmt"
	"strconv"
	"strings"
)

//JoinCon join条件
type JoinCon struct {
	TableName string
	Condition string
}

//JoinCons join条件slice
type JoinCons []JoinCon

//HaveTable join条件中是否已经添加了这张表的join
func (jc JoinCons) HaveTable(tableName string) bool {
	for _, v := range jc {
		if v.TableName == tableName {
			return true
		}
	}
	return false
}

//WhereCon where条件
type WhereCon struct {
	Query string
	Args  []interface{}
}

//Search 搜索结构体
type Search struct {
	table             *Table
	fields            []string
	tableName         string
	joinConditions    JoinCons
	whereConditions   []WhereCon
	orderbyConditions []string
	group             string
	with              string
	having            string
	limit             interface{}
	offset            interface{}

	query string
	args  []interface{}
	raw   bool
}

//Clone 克隆一个当前结构体
func (s *Search) Clone() *Search {
	clone := *s
	return &clone
}

//Fields 需要查询的字段
func (s *Search) Fields(args ...string) *Search {
	s.fields = append(s.fields, args...)
	return s
}

//Where where语法
func (s *Search) Where(query string, values ...interface{}) *Search {
	s.whereConditions = append(s.whereConditions, WhereCon{Query: query, Args: values})
	return s
}

//WhereID id = ?
func (s *Search) WhereID(id interface{}) *Search {
	s.whereConditions = append(s.whereConditions, WhereCon{Query: s.tableName + ".id = ?", Args: []interface{}{id}})
	return s
}

//In in语法
func (s *Search) In(field string, args ...interface{}) *Search {
	//In没有参数的话SQL就会报错
	if len(args) == 0 {
		return s
	}
	s.whereConditions = append(s.whereConditions, WhereCon{Query: fmt.Sprintf("%s IN (%s)", field, placeholder(len(args))), Args: args})
	return s
}

//Joins join语法，自动连表。
func (s *Search) Joins(tablename string, condition ...string) *Search {
	if len(condition) == 1 {
		s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: condition[0]})
	} else {
		if s.table.tableColumns[tablename].HaveColumn(s.tableName + "id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", tablename, s.tableName+"id", s.tableName)})
		} else if s.table.tableColumns[tablename].HaveColumn(s.tableName + "_id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", tablename, s.tableName+"_id", s.tableName)})
		} else if s.table.tableColumns[s.tableName].HaveColumn(tablename + "id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", s.tableName, tablename+"id", tablename)})
		} else if s.table.tableColumns[s.tableName].HaveColumn(tablename + "_id") {
			s.joinConditions = append(s.joinConditions, JoinCon{TableName: tablename, Condition: fmt.Sprintf("%s.%s = %s.id", s.tableName, tablename+"_id", tablename)})
		}
	}
	return s
}

//OrderBy OrderBy
func (s *Search) OrderBy(field string, isDESC ...bool) *Search {
	if len(isDESC) > 0 && isDESC[0] {
		s.orderbyConditions = append(s.orderbyConditions, field+" DESC")
	} else {
		s.orderbyConditions = append(s.orderbyConditions, field+" ASC")
	}
	return s
}

//TableName tableName
func (s *Search) TableName(name string) *Search {
	s.tableName = name
	return s
}

//Limit LIMIT ?
func (s *Search) Limit(limit interface{}) *Search {
	s.limit = limit
	return s
}

//Offset OFFSET ?
func (s *Search) Offset(offset interface{}) *Search {
	s.offset = offset
	return s
}

//Group GROUP BY
func (s *Search) Group(query string) *Search {
	s.group = query
	return s
}

//Parse 将各个条件整合成可以查询的SQL语句和参数
func (s *Search) Parse() (string, []interface{}) {
	if s.raw == true {
		return s.query, s.args
	}
	var (
		fields       string
		joins        string
		paddingwhere string
		wheres       []string
		orderby      string
		limit        string
		offset       string
	)
	if s.table.tableColumns[s.tableName].HaveColumn(IsDeleted) {
		s.Where("is_deleted = ?", 0)
	}
	s.query = ""
	s.args = []interface{}{}
	if len(s.fields) == 0 {
		fields = "*"
	} else {
		for i := 0; i < len(s.fields); i++ {
			var tableName string
			s.fields[i], tableName, _ = s.warpField(s.fields[i])
			if tableName != s.tableName {
				if !s.joinConditions.HaveTable(tableName) {
					s.Joins(tableName)
				}
			}
		}
		fields = strings.Join(s.fields, ",")
	}
	for _, joincon := range s.joinConditions {
		joins += fmt.Sprintf(" LEFT JOIN %s ON %s", joincon.TableName, joincon.Condition)
	}
	for _, wherecon := range s.whereConditions {
		paddingwhere = " WHERE "
		wheres = append(wheres, wherecon.Query)
		s.args = append(s.args, wherecon.Args...)
	}
	if len(s.orderbyConditions) > 0 {
		orderby = " ORDER BY " + strings.Join(s.orderbyConditions, ",")
	}
	if s.limit != nil {
		limit = " LIMIT ?"
		s.args = append(s.args, s.limit)
	}
	if s.offset != nil {
		offset = " OFFSET ?"
		s.args = append(s.args, s.offset)
	}
	s.query = fmt.Sprintf("SELECT %s FROM `%s`%s%s%s%s%s%s", fields, s.tableName, joins, paddingwhere, strings.Join(wheres, " AND "), orderby, limit, offset)
	// 如果table进行搜索了(table.RowsMap())，那么table下面所有的条件都会一直使用之前的搜索语句。
	// s.raw = true
	return s.query, s.args
}

//DISTINCT XX
//DISTICT XXX.XXX AS aaa
//XXX.XXX AS aaa
func (s *Search) warpField(field string) (warpStr string, tablename string, fieldname string) {
	if strings.Contains(field, " ") {
		if strings.Contains(field, "AS") {
			sp := strings.Split(field, " ")
			for i := 0; i < len(sp); i++ {
				if sp[i] == "AS" {
					sp[i-1], tablename, fieldname = s.warpFieldSingel(sp[i-1])
					warpStr = strings.Join(sp, " ")
					break
				}
			}
		} else {
			sp := strings.Split(field, " ")
			sp[len(sp)-1], tablename, fieldname = s.warpFieldSingel(sp[len(sp)-1])
			warpStr = strings.Join(sp, " ")
		}
	} else {
		return s.warpFieldSingel(field)
	}
	return
}

func (s *Search) warpFieldSingel(field string) (warpStr string, tablename string, fieldname string) {

	if strings.Contains(field, ".") {
		sp := strings.Split(field, ".")
		tablename = sp[0]
		fieldname = sp[1]
		if strings.Contains(field, "`") {
			warpStr = field
			return
		} else if fieldname == "*" {
			warpStr = "*"
			return
		}
		warpStr = strings.Replace(field, ".", ".`", 1) + "`"
	} else {
		tablename = s.tableName
		fieldname = field
		if strings.Contains(field, "`") {
			warpStr = field
			return
		}
		switch field {
		case "*", "COUNT(*)":
			warpStr = field
		default:
			warpStr = "`" + field + "`"
		}
	}
	return
}

//结果展示

//RawMap RawMap
func (s *Search) RawMap() RowMap {
	return s.RowMap()
}

//RawsMap RawsMap
func (s *Search) RawsMap() RowsMap {
	return s.RowsMap()
}

//RawsMapInterface RawsMapInterface
func (s *Search) RawsMapInterface() RowsMapInterface {
	return s.RowsMapInterface()
}

//RowMap RowMap
func (s *Search) RowMap() RowMap {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowMap()
}

// func (s *Search) String() string {
// 	return s.table.Query(s.Parse()).String()
// }

//SQLRows SQLRows
func (s *Search) SQLRows() *SQLRows {
	query, args := s.Parse()
	return s.table.Query(query, args...)
}

//RowsMap RowsMap
func (s *Search) RowsMap() RowsMap {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowsMap()
}

//RowMapInterface RowMapInterface
func (s *Search) RowMapInterface() RowMapInterface {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowMapInterface()
}

//RowsMapInterface RowsMapInterface
func (s *Search) RowsMapInterface() RowsMapInterface {
	query, args := s.Parse()
	return s.table.Query(query, args...).RowsMapInterface()
}

//DoubleSlice DoubleSlice
func (s *Search) DoubleSlice() (map[string]int, [][]string) {
	query, args := s.Parse()
	return s.table.Query(query, args...).DoubleSlice()
}

//Int 如果指定字段，则返回指定字段的int值，否则返回第一个字段作为int值返回。
func (s *Search) Int(args ...string) int {
	row := s.RowMap()
	if len(args) == 0 {
		for _, v := range row {
			i, _ := strconv.Atoi(v)
			return i
		}
	} else {
		i, _ := strconv.Atoi(row[args[0]])
		return i
	}
	return 0
}

//String like int
func (s *Search) String(args ...string) string {
	row := s.RowMap()
	if len(args) == 0 {
		for _, v := range row {
			return v
		}
	} else {
		return row[args[0]]
	}
	return ""
}

//Finds 将查询的结构放入到结构体当中
func (s *Search) Finds(v interface{}) {
	query, args := s.Parse()
	s.table.FindAll(v, append([]interface{}{query}, args...)...)
}

//Count 计算这次查询结果的个数
func (s *Search) Count() int {
	var count int
	s.fields = []string{"COUNT(*)"}
	query, args := s.Parse()
	s.table.Query(query, args...).Find(&count)
	return count
}
<|endoftext|>