<commit_before>package projects

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/ion-channel/ionic/aliases"
	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/tags"
)

const (
	validEmailRegex  = `(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
	validGitURIRegex = `^(?:(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync)+\+ssh\:\/\/|git\+https?:\/\/|git\@|(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync|ssh|file|s3)+s?:\/\/)[^\s]+$`
)

const (
	// CreateProjectEndpoint is a string representation of the current endpoint for creating project
	CreateProjectEndpoint = "v1/project/createProject"
	// CreateProjectsFromCSVEndpoint is a string representation of the current endpoint for creating projects from CSV
	CreateProjectsFromCSVEndpoint = "v1/project/createProjectsCSV"
	// GetProjectEndpoint is a string representation of the current endpoint for getting project
	GetProjectEndpoint = "v1/project/getProject"
	// GetProjectByURLEndpoint is a string representation of the current endpoint for getting project by URL
	GetProjectByURLEndpoint = "v1/project/getProjectByUrl"
	// GetProjectsEndpoint is a string representation of the current endpoint for getting projects
	GetProjectsEndpoint = "v1/project/getProjects"
	// UpdateProjectEndpoint is a string representation of the current endpoint for updating project
	UpdateProjectEndpoint = "v1/project/updateProject"
)

var (
	// ErrInvalidProject is returned when a given project does not pass the
	// standards for a project
	ErrInvalidProject = fmt.Errorf("project has invalid fields")
)

//Project is a representation of a project within the Ion Channel system
type Project struct {
	ID               *string         `json:"id,omitempty"`
	TeamID           *string         `json:"team_id,omitempty"`
	RulesetID        *string         `json:"ruleset_id,omitempty"`
	Name             *string         `json:"name,omitempty"`
	Type             *string         `json:"type,omitempty"`
	Source           *string         `json:"source,omitempty"`
	Branch           *string         `json:"branch,omitempty"`
	Description      *string         `json:"description,omitempty"`
	Active           bool            `json:"active"`
	ChatChannel      string          `json:"chat_channel"`
	CreatedAt        time.Time       `json:"created_at"`
	UpdatedAt        time.Time       `json:"updated_at"`
	DeployKey        string          `json:"deploy_key"`
	Monitor          bool            `json:"should_monitor"`
	MonitorFrequency string          `json:"monitor_frequency"`
	POCName          string          `json:"poc_name"`
	POCEmail         string          `json:"poc_email"`
	Username         string          `json:"username"`
	Password         string          `json:"password"`
	KeyFingerprint   string          `json:"key_fingerprint"`
	Private          bool            `json:"private"`
	Aliases          []aliases.Alias `json:"aliases"`
	Tags             []tags.Tag      `json:"tags"`
}

// String returns a JSON formatted string of the project object
func (p Project) String() string {
	b, err := json.Marshal(p)
	if err != nil {
		return fmt.Sprintf("failed to format project: %v", err.Error())
	}
	return string(b)
}

// Validate takes an http client, baseURL, and token; returns a slice of fields as a string and
// an error. The fields will be a list of fields that did not pass the
// validation. An error will only be returned if any of the fields fail their
// validation.
func (p *Project) Validate(client *http.Client, baseURL *url.URL, token string) (map[string]string, error) {
	invalidFields := make(map[string]string)
	var projErr error

	if p.TeamID == nil {
		invalidFields["team_id"] = "missing team id"
		projErr = ErrInvalidProject
	}

	if p.RulesetID == nil {
		invalidFields["ruleset_id"] = "missing ruleset id"
		projErr = ErrInvalidProject
	}

	if p.Name == nil {
		invalidFields["name"] = "missing name"
		projErr = ErrInvalidProject
	}

	if p.Type == nil {
		invalidFields["type"] = "missing type"
		projErr = ErrInvalidProject
	}

	if p.Source == nil {
		invalidFields["source"] = "missing source"
		projErr = ErrInvalidProject
	}

	if p.Branch == nil && p.Type != nil && strings.ToLower(*p.Type) == "git" {
		invalidFields["branch"] = "missing branch"
		projErr = ErrInvalidProject
	}

	if p.Description == nil {
		invalidFields["description"] = "missing description"
		projErr = ErrInvalidProject
	}

	if p.RulesetID != nil && p.TeamID != nil {
		exists, err := rulesets.RuleSetExists(client, baseURL, *p.RulesetID, *p.TeamID, token)
		if err != nil {
			return nil, fmt.Errorf("failed to determine if ruleset exists: %v", err.Error())
		}

		if !exists {
			invalidFields["ruleset_id"] = "ruleset id does not match to a valid ruleset"
			projErr = ErrInvalidProject
		}
	}

	p.POCEmail = strings.TrimSpace(p.POCEmail)

	r := regexp.MustCompile(validEmailRegex)
	if p.POCEmail != "" && !r.MatchString(p.POCEmail) {
		invalidFields["poc_email"] = "invalid email supplied"
		projErr = ErrInvalidProject
	}

	if p.Type != nil {
		switch strings.ToLower(*p.Type) {
		case "artifact":
			u, err := url.Parse(*p.Source)
			if err != nil {
				invalidFields["source"] = fmt.Sprintf("source must be a valid url: %v", err.Error())
				projErr = ErrInvalidProject
			}

			if u != nil {
				res, err := client.Head(u.String())
				if err != nil {
					invalidFields["source"] = "source failed to return a response"
					projErr = ErrInvalidProject
				}

				if res != nil && res.StatusCode == http.StatusNotFound {
					invalidFields["source"] = "source returned a not found"
					projErr = ErrInvalidProject
				}
			}
		case "git", "svn", "s3":
			r := regexp.MustCompile(validGitURIRegex)
			if p.Source != nil && !r.MatchString(*p.Source) {
				invalidFields["source"] = "source must be a valid uri"
				projErr = ErrInvalidProject
			}
		default:
			invalidFields["type"] = fmt.Sprintf("invalid type value")
			projErr = ErrInvalidProject
		}
	}

	return invalidFields, projErr
}

// Filter represents the available fields to filter a get project request
// with.
type Filter struct {
	ID      *string `sql:"id"`
	TeamID  *string `sql:"team_id"`
	Source  *string `sql:"source"`
	Type    *string `sql:"type"`
	Active  *bool   `sql:"active"`
	Monitor *bool   `sql:"should_monitor"`
}

// ParseParam takes a param string, breaks it apart, and repopulates it into a
// struct for further use. Any invalid or incomplete interpretations of a field
// will be ignored and only valid entries put into the struct.
func ParseParam(param string) *Filter {
	pf := Filter{}

	fvs := strings.Split(param, ",")
	for i := range fvs {
		parts := strings.Split(fvs[i], ":")

		if len(parts) == 2 {
			name := parts[0]
			comp := func(n string) bool { return strings.ToLower(n) == name }

			value := parts[1]

			field, _ := reflect.TypeOf(&pf).Elem().FieldByNameFunc(comp)
			kind := field.Type.Kind()

			if kind == reflect.Ptr {
				kind = field.Type.Elem().Kind()
			}

			switch kind {
			case reflect.String:
				reflect.ValueOf(&pf).Elem().FieldByNameFunc(comp).Set(reflect.ValueOf(&value))
			case reflect.Bool:
				b, err := strconv.ParseBool(value)
				if err == nil {
					reflect.ValueOf(&pf).Elem().FieldByNameFunc(comp).Set(reflect.ValueOf(&b))
				}
			}
		}
	}

	return &pf
}

// Param converts the non nil fields of the Project Filter into a string usable
// for URL query params.
func (pf *Filter) Param() string {
	ps := make([]string, 0)

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		name := strings.ToLower(fields.Field(i).Name)

		switch value.Kind() {
		case reflect.String:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.String()))
		case reflect.Bool:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.Bool()))
		}
	}

	return strings.Join(ps, ",")
}

// SQL takes an identifier and returns the filter as a constructed where clause
// and set of values for use in a query as SQL params. If the identifier is left
// blank it will not be included in the resulting where clause.
func (pf *Filter) SQL(identifier string) (string, []interface{}) {

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	idx := 1
	wheres := make([]string, 0)
	vals := make([]interface{}, 0)
	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		tag, ok := fields.Field(i).Tag.Lookup("sql")
		if !ok {
			tag = fields.Field(i).Name
		}

		ident := ""
		if identifier != "" {
			ident = fmt.Sprintf("%v.", identifier)
		}

		name := strings.ToLower(tag)
		wheres = append(wheres, fmt.Sprintf("%v%v=$%v", ident, name, idx))
		vals = append(vals, value.Interface())
		idx++
	}

	where := strings.Join(wheres, " AND ")
	if where != "" {
		where = fmt.Sprintf(" WHERE %v\n", where)
	}

	return where, vals
}
<commit_msg>fixing split to account for colon in value<commit_after>package projects

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/ion-channel/ionic/aliases"
	"github.com/ion-channel/ionic/rulesets"
	"github.com/ion-channel/ionic/tags"
)

const (
	validEmailRegex  = `(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
	validGitURIRegex = `^(?:(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync)+\+ssh\:\/\/|git\+https?:\/\/|git\@|(?:http|ftp|gopher|mailto|mid|cid|news|nntp|prospero|telnet|rlogin|tn3270|wais|svn|git|rsync|ssh|file|s3)+s?:\/\/)[^\s]+$`
)

const (
	// CreateProjectEndpoint is a string representation of the current endpoint for creating project
	CreateProjectEndpoint = "v1/project/createProject"
	// CreateProjectsFromCSVEndpoint is a string representation of the current endpoint for creating projects from CSV
	CreateProjectsFromCSVEndpoint = "v1/project/createProjectsCSV"
	// GetProjectEndpoint is a string representation of the current endpoint for getting project
	GetProjectEndpoint = "v1/project/getProject"
	// GetProjectByURLEndpoint is a string representation of the current endpoint for getting project by URL
	GetProjectByURLEndpoint = "v1/project/getProjectByUrl"
	// GetProjectsEndpoint is a string representation of the current endpoint for getting projects
	GetProjectsEndpoint = "v1/project/getProjects"
	// UpdateProjectEndpoint is a string representation of the current endpoint for updating project
	UpdateProjectEndpoint = "v1/project/updateProject"
)

var (
	// ErrInvalidProject is returned when a given project does not pass the
	// standards for a project
	ErrInvalidProject = fmt.Errorf("project has invalid fields")
)

//Project is a representation of a project within the Ion Channel system
type Project struct {
	ID               *string         `json:"id,omitempty"`
	TeamID           *string         `json:"team_id,omitempty"`
	RulesetID        *string         `json:"ruleset_id,omitempty"`
	Name             *string         `json:"name,omitempty"`
	Type             *string         `json:"type,omitempty"`
	Source           *string         `json:"source,omitempty"`
	Branch           *string         `json:"branch,omitempty"`
	Description      *string         `json:"description,omitempty"`
	Active           bool            `json:"active"`
	ChatChannel      string          `json:"chat_channel"`
	CreatedAt        time.Time       `json:"created_at"`
	UpdatedAt        time.Time       `json:"updated_at"`
	DeployKey        string          `json:"deploy_key"`
	Monitor          bool            `json:"should_monitor"`
	MonitorFrequency string          `json:"monitor_frequency"`
	POCName          string          `json:"poc_name"`
	POCEmail         string          `json:"poc_email"`
	Username         string          `json:"username"`
	Password         string          `json:"password"`
	KeyFingerprint   string          `json:"key_fingerprint"`
	Private          bool            `json:"private"`
	Aliases          []aliases.Alias `json:"aliases"`
	Tags             []tags.Tag      `json:"tags"`
}

// String returns a JSON formatted string of the project object
func (p Project) String() string {
	b, err := json.Marshal(p)
	if err != nil {
		return fmt.Sprintf("failed to format project: %v", err.Error())
	}
	return string(b)
}

// Validate takes an http client, baseURL, and token; returns a slice of fields as a string and
// an error. The fields will be a list of fields that did not pass the
// validation. An error will only be returned if any of the fields fail their
// validation.
func (p *Project) Validate(client *http.Client, baseURL *url.URL, token string) (map[string]string, error) {
	invalidFields := make(map[string]string)
	var projErr error

	if p.TeamID == nil {
		invalidFields["team_id"] = "missing team id"
		projErr = ErrInvalidProject
	}

	if p.RulesetID == nil {
		invalidFields["ruleset_id"] = "missing ruleset id"
		projErr = ErrInvalidProject
	}

	if p.Name == nil {
		invalidFields["name"] = "missing name"
		projErr = ErrInvalidProject
	}

	if p.Type == nil {
		invalidFields["type"] = "missing type"
		projErr = ErrInvalidProject
	}

	if p.Source == nil {
		invalidFields["source"] = "missing source"
		projErr = ErrInvalidProject
	}

	if p.Branch == nil && p.Type != nil && strings.ToLower(*p.Type) == "git" {
		invalidFields["branch"] = "missing branch"
		projErr = ErrInvalidProject
	}

	if p.Description == nil {
		invalidFields["description"] = "missing description"
		projErr = ErrInvalidProject
	}

	if p.RulesetID != nil && p.TeamID != nil {
		exists, err := rulesets.RuleSetExists(client, baseURL, *p.RulesetID, *p.TeamID, token)
		if err != nil {
			return nil, fmt.Errorf("failed to determine if ruleset exists: %v", err.Error())
		}

		if !exists {
			invalidFields["ruleset_id"] = "ruleset id does not match to a valid ruleset"
			projErr = ErrInvalidProject
		}
	}

	p.POCEmail = strings.TrimSpace(p.POCEmail)

	r := regexp.MustCompile(validEmailRegex)
	if p.POCEmail != "" && !r.MatchString(p.POCEmail) {
		invalidFields["poc_email"] = "invalid email supplied"
		projErr = ErrInvalidProject
	}

	if p.Type != nil {
		switch strings.ToLower(*p.Type) {
		case "artifact":
			u, err := url.Parse(*p.Source)
			if err != nil {
				invalidFields["source"] = fmt.Sprintf("source must be a valid url: %v", err.Error())
				projErr = ErrInvalidProject
			}

			if u != nil {
				res, err := client.Head(u.String())
				if err != nil {
					invalidFields["source"] = "source failed to return a response"
					projErr = ErrInvalidProject
				}

				if res != nil && res.StatusCode == http.StatusNotFound {
					invalidFields["source"] = "source returned a not found"
					projErr = ErrInvalidProject
				}
			}
		case "git", "svn", "s3":
			r := regexp.MustCompile(validGitURIRegex)
			if p.Source != nil && !r.MatchString(*p.Source) {
				invalidFields["source"] = "source must be a valid uri"
				projErr = ErrInvalidProject
			}
		default:
			invalidFields["type"] = fmt.Sprintf("invalid type value")
			projErr = ErrInvalidProject
		}
	}

	return invalidFields, projErr
}

// Filter represents the available fields to filter a get project request
// with.
type Filter struct {
	ID      *string `sql:"id"`
	TeamID  *string `sql:"team_id"`
	Source  *string `sql:"source"`
	Type    *string `sql:"type"`
	Active  *bool   `sql:"active"`
	Monitor *bool   `sql:"should_monitor"`
}

// ParseParam takes a param string, breaks it apart, and repopulates it into a
// struct for further use. Any invalid or incomplete interpretations of a field
// will be ignored and only valid entries put into the struct.
func ParseParam(param string) *Filter {
	pf := Filter{}

	fvs := strings.Split(param, ",")
	for i := range fvs {
		parts := strings.SplitN(fvs[i], ":", 2)

		if len(parts) == 2 {
			name := parts[0]
			comp := func(n string) bool { return strings.ToLower(n) == name }

			value := parts[1]

			field, _ := reflect.TypeOf(&pf).Elem().FieldByNameFunc(comp)
			kind := field.Type.Kind()

			if kind == reflect.Ptr {
				kind = field.Type.Elem().Kind()
			}

			switch kind {
			case reflect.String:
				reflect.ValueOf(&pf).Elem().FieldByNameFunc(comp).Set(reflect.ValueOf(&value))
			case reflect.Bool:
				b, err := strconv.ParseBool(value)
				if err == nil {
					reflect.ValueOf(&pf).Elem().FieldByNameFunc(comp).Set(reflect.ValueOf(&b))
				}
			}
		}
	}

	return &pf
}

// Param converts the non nil fields of the Project Filter into a string usable
// for URL query params.
func (pf *Filter) Param() string {
	ps := make([]string, 0)

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		name := strings.ToLower(fields.Field(i).Name)

		switch value.Kind() {
		case reflect.String:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.String()))
		case reflect.Bool:
			ps = append(ps, fmt.Sprintf("%v:%v", name, value.Bool()))
		}
	}

	return strings.Join(ps, ",")
}

// SQL takes an identifier and returns the filter as a constructed where clause
// and set of values for use in a query as SQL params. If the identifier is left
// blank it will not be included in the resulting where clause.
func (pf *Filter) SQL(identifier string) (string, []interface{}) {

	fields := reflect.TypeOf(pf)
	values := reflect.ValueOf(pf)

	if fields.Kind() == reflect.Ptr {
		fields = fields.Elem()
		values = values.Elem()
	}

	idx := 1
	wheres := make([]string, 0)
	vals := make([]interface{}, 0)
	for i := 0; i < fields.NumField(); i++ {
		value := values.Field(i)

		if value.IsNil() {
			continue
		}

		if value.Kind() == reflect.Ptr {
			value = value.Elem()
		}

		tag, ok := fields.Field(i).Tag.Lookup("sql")
		if !ok {
			tag = fields.Field(i).Name
		}

		ident := ""
		if identifier != "" {
			ident = fmt.Sprintf("%v.", identifier)
		}

		name := strings.ToLower(tag)
		wheres = append(wheres, fmt.Sprintf("%v%v=$%v", ident, name, idx))
		vals = append(vals, value.Interface())
		idx++
	}

	where := strings.Join(wheres, " AND ")
	if where != "" {
		where = fmt.Sprintf(" WHERE %v\n", where)
	}

	return where, vals
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"

	"strings"

	"github.com/codegangsta/cli"
	"github.com/landaire/pbo"
)

const (
	path    = "/Users/lander/Documents/pbo/map_altis_data_layers.pbo"
	version = "0.0.1"
)

var pboFile *pbo.Pbo

func main() {

	app := cli.NewApp()
	app.Name = "pboextractor"
	app.Usage = "Extract PBO archives used in games such as Arma 3"
	app.Author = "Lander Brandt"
	app.Email = "@landaire"
	app.Version = version

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "pbo",
			Usage: "PBO file to read",
		},
	}

	app.Commands = []cli.Command{
		cli.Command{
			Name:      "extract",
			ShortName: "e",
			Usage:     "Extract the PBO to the given output directory",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "outdir, o",
					Usage: "Output directory",
				},
			},
			Before: LoadPbo,
		},
		cli.Command{
			Name:   "header",
			Usage:  "Print header information to stdout",
			Before: LoadPbo,
			Action: PrintHeader,
		},
	}

	err := app.Run(os.Args)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
	}

}

func LoadPbo(c *cli.Context) error {
	if c.GlobalString("pbo") == "" {
		return errors.New("No PBO provided")
	}

	if pboFile != nil {
		return nil
	}

	var err error
	pboFile, err = pbo.NewPbo(c.GlobalString("pbo"))

	if err != nil {
		return err
	}

	return nil
}

func PrintHeader(c *cli.Context) {
	// Print header extension info if it's present
	if pboFile.HeaderExtension != nil {
		fmt.Println("Header Extension:")
		lines := strings.Split(pboFile.HeaderExtension.String(), "\n")

		for _, line := range lines {
			fmt.Println("\t", line)
		}

		fmt.Println()

		fmt.Println("\tExtended Fields:")
		for key, val := range pboFile.HeaderExtension.ExtendedFields {
			fmt.Printf("\t\t%s: %s\n", key, val)
		}

		fmt.Println()
	}

	fmt.Println("Entries:")

	for _, entry := range pboFile.Entries {
		lines := strings.Split(entry.String(), "\n")

		for _, line := range lines {
			fmt.Println("\t", line)
		}

		fmt.Println()
	}
}
<commit_msg>Change version to 0.1.0<commit_after>package main

import (
	"errors"
	"fmt"
	"os"

	"strings"

	"github.com/codegangsta/cli"
	"github.com/landaire/pbo"
)

const (
	path    = "/Users/lander/Documents/pbo/map_altis_data_layers.pbo"
	version = "0.1.0"
)

var pboFile *pbo.Pbo

func main() {

	app := cli.NewApp()
	app.Name = "pboextractor"
	app.Usage = "Extract PBO archives used in games such as Arma 3"
	app.Author = "Lander Brandt"
	app.Email = "@landaire"
	app.Version = version

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "pbo",
			Usage: "PBO file to read",
		},
	}

	app.Commands = []cli.Command{
		cli.Command{
			Name:      "extract",
			ShortName: "e",
			Usage:     "Extract the PBO to the given output directory",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "outdir, o",
					Usage: "Output directory",
				},
			},
			Before: LoadPbo,
		},
		cli.Command{
			Name:   "header",
			Usage:  "Print header information to stdout",
			Before: LoadPbo,
			Action: PrintHeader,
		},
	}

	err := app.Run(os.Args)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
	}

}

func LoadPbo(c *cli.Context) error {
	if c.GlobalString("pbo") == "" {
		return errors.New("No PBO provided")
	}

	if pboFile != nil {
		return nil
	}

	var err error
	pboFile, err = pbo.NewPbo(c.GlobalString("pbo"))

	if err != nil {
		return err
	}

	return nil
}

func PrintHeader(c *cli.Context) {
	// Print header extension info if it's present
	if pboFile.HeaderExtension != nil {
		fmt.Println("Header Extension:")
		lines := strings.Split(pboFile.HeaderExtension.String(), "\n")

		for _, line := range lines {
			fmt.Println("\t", line)
		}

		fmt.Println()

		fmt.Println("\tExtended Fields:")
		for key, val := range pboFile.HeaderExtension.ExtendedFields {
			fmt.Printf("\t\t%s: %s\n", key, val)
		}

		fmt.Println()
	}

	fmt.Println("Entries:")

	for _, entry := range pboFile.Entries {
		lines := strings.Split(entry.String(), "\n")

		for _, line := range lines {
			fmt.Println("\t", line)
		}

		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package test

import (
	"libxml"
	"testing"
	"libxml/help"
)

func TestHtmlFragment(t *testing.T) {
	doc := libxml.XmlParseString("<meta name=\"format-detection\" content=\"telephone=no\">")
	root := doc.RootElement()
	child := doc.NewElement("child")
	root.AppendChildNode(child)
	Equal(t, root.String(), "<meta name=\"format-detection\" content=\"telephone=no\"><child/></meta>")
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}

func TestHtmlFragment2(t *testing.T) {
	doc := libxml.HtmlParseFragment("
")
	f := doc.RootElement().First()
	Equal(t, f.Name(), "body")
	Equal(t, f.First().Name(), "div")
	Equal(t, f.String(), "
")
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}

func TestHtmlFragment3(t *testing.T) {
	doc := libxml.HtmlParseFragment("

")
	f := doc.RootElement().First()
	Equal(t, f.Name(), "h1")
	Equal(t, f.First().Name(), "div")
	Equal(t, f.String(), "

")
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}
<commit_msg>You should be able to parse a document, then get its content. This is stopping its usage in Snow.<commit_after>package test

import (
	"libxml"
	"testing"
	"libxml/help"
)

func TestHtmlFragment(t *testing.T) {
	doc := libxml.XmlParseString("<meta name=\"format-detection\" content=\"telephone=no\">")
	root := doc.RootElement()
	child := doc.NewElement("child")
	root.AppendChildNode(child)
	Equal(t, root.String(), "<meta name=\"format-detection\" content=\"telephone=no\"><child/></meta>")
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}

func TestHtmlFragment2(t *testing.T) {
	doc := libxml.HtmlParseFragment("
")
	f := doc.RootElement().First()
	Equal(t, f.Name(), "body")
	Equal(t, f.First().Name(), "div")
	Equal(t, f.String(), "
")
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}

func TestHtmlFragment3(t *testing.T) {
	doc := libxml.HtmlParseFragment("

")
	f := doc.RootElement().First()
	Equal(t, f.Name(), "h1")
	Equal(t, f.First().Name(), "div")
	Equal(t, f.String(), "

")
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}

func TestHtmlFragmentNewlinesNokogiri(t *testing.T) {
	html := "
"
	doc := libxml.HtmlParseFragment(html)
	Equal(t, doc.Content(), html)
	doc.Free()

	help.XmlCleanUpParser()
	if help.XmlMemoryAllocation() != 0 {
		t.Errorf("Memeory leaks %d!!!", help.XmlMemoryAllocation())
		help.XmlMemoryLeakReport()
	}
}
<|endoftext|>


<commit_before>package helper

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

import "log"

var ErrHelperInvalidPathLength = errors.New("Invalid Path Length")

func ParseUrl(url *url.URL) ([]string, int64, float32, error) {
	path := strings.Split(url.Path, "/")
	l := len(path)

	if l < 4 {
		return nil, 0, 0, ErrHelperInvalidPathLength
	}

	//XXX: ugly
	if path[0] == "" { // won't it always? eg: /a/path => ["" "a" "path"]
		path = path[1:]
		l = len(path)
	}

	t, err := strconv.ParseInt(path[l-2], 10, 64)
	if err != nil {
		return nil, 0, 0, err
	}

	//TODO: validate time

	v, err2 := strconv.ParseFloat(path[l-1], 32)
	if err2 != nil {
		return nil, 0, 0, err
	}

	return path[0 : l-2], t, float32(v), nil
}
<commit_msg>remove unused package<commit_after>package helper

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

var ErrHelperInvalidPathLength = errors.New("Invalid Path Length")

func ParseUrl(url *url.URL) ([]string, int64, float32, error) {
	path := strings.Split(url.Path, "/")
	l := len(path)

	if l < 4 {
		return nil, 0, 0, ErrHelperInvalidPathLength
	}

	//XXX: ugly
	if path[0] == "" { // won't it always? eg: /a/path => ["" "a" "path"]
		path = path[1:]
		l = len(path)
	}

	t, err := strconv.ParseInt(path[l-2], 10, 64)
	if err != nil {
		return nil, 0, 0, err
	}

	//TODO: validate time

	v, err2 := strconv.ParseFloat(path[l-1], 32)
	if err2 != nil {
		return nil, 0, 0, err
	}

	return path[0 : l-2], t, float32(v), nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebitenutil

import (
	"image"
	"image/color"
	"image/color/palette"
	"image/draw"
	"image/gif"
	"io"
	"sync"

	"github.com/hajimehoshi/ebiten/v2"
)

type recorder struct {
	inner        func(screen *ebiten.Image) error
	writer       io.Writer
	frameNum     int
	skips        int
	gif          *gif.GIF
	currentFrame int
	wg           sync.WaitGroup
}

var cheapPalette color.Palette

func init() {
	cs := []color.Color{}
	for _, r := range []uint8{0x00, 0x80, 0xff} {
		for _, g := range []uint8{0x00, 0x80, 0xff} {
			for _, b := range []uint8{0x00, 0x80, 0xff} {
				cs = append(cs, color.RGBA{r, g, b, 0xff})
			}
		}
	}
	cheapPalette = color.Palette(cs)
}

func (r *recorder) delay() int {
	delay := 100 * r.skips / ebiten.MaxTPS()
	if delay < 2 {
		return 2
	}
	return delay
}

func (r *recorder) palette() color.Palette {
	if 1 < (r.frameNum-1)/r.skips+1 {
		return cheapPalette
	}
	return palette.Plan9
}

func (r *recorder) update(screen *ebiten.Image) error {
	if err := r.inner(screen); err != nil {
		return err
	}
	if r.currentFrame == r.frameNum {
		return nil
	}
	if r.currentFrame%r.skips == 0 {
		if r.gif == nil {
			num := (r.frameNum-1)/r.skips + 1
			r.gif = &gif.GIF{
				Image:     make([]*image.Paletted, num),
				Delay:     make([]int, num),
				LoopCount: -1,
			}
		}
		s := image.NewNRGBA(screen.Bounds())
		draw.Draw(s, s.Bounds(), screen, screen.Bounds().Min, draw.Src)

		img := image.NewPaletted(s.Bounds(), r.palette())
		f := r.currentFrame / r.skips
		r.wg.Add(1)
		go func() {
			defer r.wg.Done()
			draw.FloydSteinberg.Draw(img, img.Bounds(), s, s.Bounds().Min)
			r.gif.Image[f] = img
			r.gif.Delay[f] = r.delay()
		}()
	}

	r.currentFrame++
	if r.currentFrame == r.frameNum {
		r.wg.Wait()
		if err := gif.EncodeAll(r.writer, r.gif); err != nil {
			return err
		}
	}
	return nil
}

// RecordScreenAsGIF returns updating function with recording the screen as an animation GIF image.
//
// Deprecated: (as of 1.6.0) Do not use this.
//
// This encodes each screen at each frame and may slows the application.
//
// Here is the example to record initial 120 frames of your game:
//
//     func update(screen *ebiten.Image) error {
//         // ...
//     }
//
//     func main() {
//         out, err := os.Create("output.gif")
//         if err != nil {
//             log.Fatal(err)
//         }
//         defer out.Close()
//
//         update := RecordScreenAsGIF(update, out, 120)
//         if err := ebiten.Run(update, 320, 240, 2, "Your game's title"); err != nil {
//             log.Fatal(err)
//         }
//     }
func RecordScreenAsGIF(update func(*ebiten.Image) error, out io.Writer, frameNum int) func(*ebiten.Image) error {
	r := &recorder{
		inner:    update,
		writer:   out,
		frameNum: frameNum,
		skips:    10,
	}
	return r.update
}
<commit_msg>ebitenutil: Remove RecordScreenAsGIF<commit_after><|endoftext|>


<commit_before>// File operations. Retrieve players from csv

package main

import (
	"strconv"

	"github.com/topher200/baseutil"
)

func ParsePlayers(inputFilename string) []Player {
	mappedRows := baseutil.MapReader(inputFilename)
	players := make([]Player, len(mappedRows))
	for i, row := range mappedRows {
		firstName := row["First Name"]
		lastName := row["Last Name"]
		gender, err := StringToGender(row["Gender"])
		baseutil.Check(err)
		rating, err := strconv.ParseFloat(row["Balanced Rating"], 32)
		baseutil.Check(err)
		players[i] = Player{
			Name{firstName, lastName}, float32(rating), gender, uint8(0), Name{}}
	}
	return players
}

// ParseBaggages has the side effect of setting the .baggage for all Players
func ParseBaggages(inputFilename string, players []Player) {
	for _, baggage := range baseutil.MapReader(inputFilename) {
		playerPointer, err := FindPlayer(
			players, Name{baggage["firstname1"], baggage["lastname1"]})
		baseutil.Check(err)
		if playerPointer.HasBaggage() {
			newLog.Panicf("Player %v already has baggage %v",
				*playerPointer, playerPointer.baggage)
		}
		playerPointer.baggage = Name{baggage["firstname2"], baggage["lastname2"]}
		newLog.Info("Found baggage of %v for %v",
			playerPointer.baggage, playerPointer.String())
	}
}
<commit_msg>baggage .Info -> .Debug<commit_after>// File operations. Retrieve players from csv

package main

import (
	"strconv"

	"github.com/topher200/baseutil"
)

func ParsePlayers(inputFilename string) []Player {
	mappedRows := baseutil.MapReader(inputFilename)
	players := make([]Player, len(mappedRows))
	for i, row := range mappedRows {
		firstName := row["First Name"]
		lastName := row["Last Name"]
		gender, err := StringToGender(row["Gender"])
		baseutil.Check(err)
		rating, err := strconv.ParseFloat(row["Balanced Rating"], 32)
		baseutil.Check(err)
		players[i] = Player{
			Name{firstName, lastName}, float32(rating), gender, uint8(0), Name{}}
	}
	return players
}

// ParseBaggages has the side effect of setting the .baggage for all Players
func ParseBaggages(inputFilename string, players []Player) {
	for _, baggage := range baseutil.MapReader(inputFilename) {
		playerPointer, err := FindPlayer(
			players, Name{baggage["firstname1"], baggage["lastname1"]})
		baseutil.Check(err)
		if playerPointer.HasBaggage() {
			newLog.Panicf("Player %v already has baggage %v",
				*playerPointer, playerPointer.baggage)
		}
		playerPointer.baggage = Name{baggage["firstname2"], baggage["lastname2"]}
		newLog.Debug("Found baggage of %v for %v",
			playerPointer.baggage, playerPointer.String())
	}
}
<|endoftext|>


<commit_before>package core

// BoolType represents a boolean values in the language.
type BoolType bool

// True is a true value.
var True = NewBool(true)

// False is a false value.
var False = NewBool(false)

// NewBool converts a Go boolean value into BoolType.
func NewBool(b bool) *Thunk {
	return Normal(rawBool(b))
}

func rawBool(b bool) BoolType {
	return BoolType(b)
}

// If returns the second argument when the first one is true or the third one
// otherwise.
var If = NewLazyFunction(
	NewSignature(nil, nil, "conds", nil, nil, ""),
	func(ts ...*Thunk) Value {
		v := ts[0].Eval()
		l, ok := v.(ListType)

		if !ok {
			return NotListError(v)
		}

		ts, err := l.ToThunks()

		if err != nil {
			return err
		}

		if len(ts)%2 == 0 {
			return argumentError("Number of arguments of if function must be even but %v.", len(ts))
		}

		for i := 0; i < len(ts)-2; i += 2 {
			v := ts[i].Eval()
			b, ok := v.(BoolType)

			if !ok {
				return NotBoolError(v)
			}

			if b {
				return ts[i+1]
			}
		}

		return ts[len(ts)-1]
	})

func (b BoolType) compare(c comparable) int {
	if b == c.(BoolType) {
		return 0
	} else if b {
		return 1
	}

	return -1
}

func (b BoolType) string() Value {
	return sprint(b)
}
<commit_msg>Return existing thunks from NewBool function<commit_after>package core

// BoolType represents a boolean values in the language.
type BoolType bool

// True is a true value.
var True = Normal(BoolType(true))

// False is a false value.
var False = Normal(BoolType(false))

// NewBool converts a Go boolean value into BoolType.
func NewBool(b bool) *Thunk {
	if b {
		return True
	}

	return False
}

// If returns the second argument when the first one is true or the third one
// otherwise.
var If = NewLazyFunction(
	NewSignature(nil, nil, "conds", nil, nil, ""),
	func(ts ...*Thunk) Value {
		v := ts[0].Eval()
		l, ok := v.(ListType)

		if !ok {
			return NotListError(v)
		}

		ts, err := l.ToThunks()

		if err != nil {
			return err
		}

		if len(ts)%2 == 0 {
			return argumentError("Number of arguments of if function must be even but %v.", len(ts))
		}

		for i := 0; i < len(ts)-2; i += 2 {
			v := ts[i].Eval()
			b, ok := v.(BoolType)

			if !ok {
				return NotBoolError(v)
			}

			if b {
				return ts[i+1]
			}
		}

		return ts[len(ts)-1]
	})

func (b BoolType) compare(c comparable) int {
	if b == c.(BoolType) {
		return 0
	} else if b {
		return 1
	}

	return -1
}

func (b BoolType) string() Value {
	return sprint(b)
}
<|endoftext|>


<commit_before>package scipipe

import (
	"fmt"
	"os/exec"
	re "regexp"
	str "strings"
	// "time"
)

type ShellTask struct {
	task
	_OutOnly     bool
	InPorts      map[string]chan *FileTarget
	InPaths      map[string]string
	OutPorts     map[string]chan *FileTarget
	OutPathFuncs map[string]func() string
	Command      string
}

func NewShellTask(command string) *ShellTask {
	return &ShellTask{
		Command:      command,
		InPorts:      make(map[string]chan *FileTarget),
		InPaths:      make(map[string]string),
		OutPorts:     make(map[string]chan *FileTarget),
		OutPathFuncs: make(map[string]func() string),
	}
}

func Sh(cmd string) *ShellTask {
	// Create task
	t := NewShellTask(cmd)

	// Find in/out port names, and set up in port lists
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		typ := m[1]
		name := m[2]
		if typ == "o" {
			t.OutPorts[name] = make(chan *FileTarget, BUFSIZE)
		} else if typ == "i" {
			// Set up a channel on the inports, even though this is
			// often replaced by another tasks output port channel.
			// It might be nice to have it init'ed with a channel
			// anyways, for use cases when we want to send FileTargets
			// on the inport manually.
			t.InPorts[name] = make(chan *FileTarget, BUFSIZE)
		}
	}
	return t
}

func (t *ShellTask) Run() {
	// Close output channels
	for _, ochan := range t.OutPorts {
		defer close(ochan)
	}

	// Main loop
	for {
		breakLoop := false
		// If there are no inports, we know we should exit the loop
		// directly after executing the command, and sending the outputs
		if len(t.InPorts) == 0 {
			breakLoop = true
		}

		// Read input targets on in-ports and set up path mappings
		for iname, ichan := range t.InPorts {
			infile, open := <-ichan
			if !open {
				fmt.Println("Setting breakLoop to true")
				breakLoop = true
				continue
			}
			fmt.Println("Infile:", infile.GetPath())
			t.InPaths[iname] = infile.GetPath()
		}

		// Execute command
		t.formatAndExecute(t.Command)

		// Send output targets on out ports
		for oname, ochan := range t.OutPorts {
			fn := t.OutPathFuncs[oname]
			baseName := fn()
			nf := NewFileTarget(baseName)
			fmt.Println("Sending file:", nf.GetPath())
			ochan <- nf
		}

		if breakLoop {
			fmt.Println("Exiting main loop of task", t.Command)
			break
		}
	}
}

func (t *ShellTask) formatAndExecute(cmd string) {
	cmd = t.ReplacePortDefsInCmd(cmd)
	fmt.Println("ShellTask: Executing command: ", cmd)
	_, err := exec.Command("bash", "-c", cmd).Output()
	Check(err)
}

func (t *ShellTask) ReplacePortDefsInCmd(cmd string) string {
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		whole := m[0]
		typ := m[1]
		name := m[2]
		newstr := "REPLACE_FAILED_FOR_PORT_" + name + "_CHECK_YOUR_CODE"
		if typ == "o" {
			newstr = t.OutPathFuncs[name]()
		} else if typ == "i" {
			newstr = t.InPaths[name]
		}
		cmd = str.Replace(cmd, whole, newstr, -1)
	}
	return cmd
}

func (t *ShellTask) GetInPath(inPort string) string {
	inPath := t.InPaths[inPort]
	return inPath
}
<commit_msg>Better error checking and messages in ShellTask<commit_after>package scipipe

import (
	"fmt"
	"os/exec"
	re "regexp"
	str "strings"
	// "time"
	"errors"
)

type ShellTask struct {
	task
	_OutOnly     bool
	InPorts      map[string]chan *FileTarget
	InPaths      map[string]string
	OutPorts     map[string]chan *FileTarget
	OutPathFuncs map[string]func() string
	Command      string
}

func NewShellTask(command string) *ShellTask {
	return &ShellTask{
		Command:      command,
		InPorts:      make(map[string]chan *FileTarget),
		InPaths:      make(map[string]string),
		OutPorts:     make(map[string]chan *FileTarget),
		OutPathFuncs: make(map[string]func() string),
	}
}

func Sh(cmd string) *ShellTask {
	// Create task
	t := NewShellTask(cmd)

	// Find in/out port names, and set up in port lists
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		if len(m) < 3 {
			Check(errors.New("Too few matches"))
		}
		typ := m[1]
		name := m[2]
		if typ == "o" {
			t.OutPorts[name] = make(chan *FileTarget, BUFSIZE)
		} else if typ == "i" {
			// Set up a channel on the inports, even though this is
			// often replaced by another tasks output port channel.
			// It might be nice to have it init'ed with a channel
			// anyways, for use cases when we want to send FileTargets
			// on the inport manually.
			t.InPorts[name] = make(chan *FileTarget, BUFSIZE)
		}
	}
	return t
}

func (t *ShellTask) Run() {
	fmt.Println("Entering task: ", t.Command)
	// Close output channels
	for _, ochan := range t.OutPorts {
		defer close(ochan)
	}

	// Main loop
	for {
		breakLoop := false
		breakLoopAtEnd := false
		// If there are no inports, we know we should exit the loop
		// directly after executing the command, and sending the outputs
		if len(t.InPorts) == 0 {
			breakLoopAtEnd = true
		}

		// Read input targets on in-ports and set up path mappings
		for iname, ichan := range t.InPorts {
			infile, open := <-ichan
			if !open {
				breakLoop = true
				continue
			}
			fmt.Println("Receiving file:", infile.GetPath())
			t.InPaths[iname] = infile.GetPath()
		}
		if breakLoop {
			break
		}

		// Execute command
		t.formatAndExecute(t.Command)

		// Send output targets on out ports
		for oname, ochan := range t.OutPorts {
			fn := t.OutPathFuncs[oname]
			baseName := fn()
			nf := NewFileTarget(baseName)
			fmt.Println("Sending file:  ", nf.GetPath())
			ochan <- nf
		}

		if breakLoopAtEnd {
			fmt.Println("Exiting task:  ", t.Command)
			break
		}
	}
}

func (t *ShellTask) formatAndExecute(cmd string) {
	cmd = t.ReplacePortDefsInCmd(cmd)
	fmt.Println("Executing cmd: ", cmd)
	_, err := exec.Command("bash", "-c", cmd).Output()
	Check(err)
}

func (t *ShellTask) ReplacePortDefsInCmd(cmd string) string {
	r, err := re.Compile("{(o|i):([^{}:]+)}")
	Check(err)
	ms := r.FindAllStringSubmatch(cmd, -1)
	for _, m := range ms {
		whole := m[0]
		typ := m[1]
		name := m[2]
		newstr := "REPLACE_FAILED_FOR_PORT_" + name + "_CHECK_YOUR_CODE"
		if typ == "o" {
			if t.OutPathFuncs[name] != nil {
				newstr = t.OutPathFuncs[name]()
			} else {
				msg := fmt.Sprint("Missing outpath function for outport '", name, "' of shell task '", t.Command, "'")
				Check(errors.New(msg))
			}
		} else if typ == "i" {
			if t.InPaths[name] != "" {
				newstr = t.InPaths[name]
			} else {
				msg := fmt.Sprint("Missing inpath for inport '", name, "' of shell task '", t.Command, "'")
				Check(errors.New(msg))
			}
		}
		cmd = str.Replace(cmd, whole, newstr, -1)
	}
	return cmd
}

func (t *ShellTask) GetInPath(inPort string) string {
	var inPath string
	if t.InPaths[inPort] != "" {
		inPath = t.InPaths[inPort]
	} else {
		msg := fmt.Sprint("Missing inpath for inport '", inPort, "' of shell task '", t.Command, "'")
		Check(errors.New(msg))
	}
	return inPath
}
<|endoftext|>


<commit_before>package smail

import(
	"net/smtp"
	"strings"
	"errors"
)

type Smail struct {
	username_ string
	auth_ smtp.Auth
	fullServerAddr_ string
}

/*
	Makes a new Smail object with the given
*/
func NewSmail(server, port, username, password string) (*Smail, error) {
	//make sure parameters given were not empty
	if server == "" { return nil, errors.New("No server specified") }
	if port == "" { return nil, errors.New("No port specified") }
	if username == "" { return nil, errors.New("No username specified") }
	if password == "" { return nil, errors.New("No password specified") }

	//make a PlainAuth object
	auth := smtp.PlainAuth("", username, password, server)

	//make a smail object and return a pointer to it
	return &Smail{username, auth, server + ":" + port}, nil
}

/*
	Takes a string of comma-seperated email addresses (with or without spaces), and splits it into a
	slice of email adresses
*/
func ToAddrSlice(addresses string) []string {
	//remove all spaces from the string, and then split the string on all commas
	return strings.Split(strings.Replace(addresses, " ", "", -1), ",")
}

/*
	Takes a slice of email adresses, and combines them into a comma seperated list of email adresses,
	with spaces trailing each comma
*/
func ToAddrList(addresses []string) string {
	var ret = ""
	//loop through all but the last element in the address slice, appending them to the ret string with a comma afterwards
	for _, v := range addresses[:len(addresses)-1] {
		ret += v + ", "
	}
	//append the last address to the ret string & return the list of addresses
	ret += addresses[len(addresses)-1]
	return ret
}

/*
	Sends a plaintext email to the specified recipient email adress(es), with the given subject and body.
	NOTE: it is assumed that the given email adresses are valid.
*/
func (s *Smail) SendPlaintextEmail(recipients []string, subject string, body string) error {

	emailBody := "To: "+ ToAddrList(recipients) + "\r\nSubject: " + subject + "\r\n\r\n" + body

	err := smtp.SendMail(s.fullServerAddr_, s.auth_, s.username_, recipients, []byte(emailBody))

	return err
}

/*
	Sends an HTML email to the specified recipient email adress(es), with the given subject and body.
	NOTE: it is assumend that the given email adresses ware valid.
*/
func (s *Smail) SendHTMLEmail(recipients []string, subject string, body string) error {

	mime := "MIME-version: 1.0;\r\nContent-Type: text/html; charset=\"UTF-8\";\r\n\r\n";

	emailBody := "To: "+ ToAddrList(recipients) + "\r\nSubject: " + subject + "\r\n" + mime + body

	err := smtp.SendMail(s.fullServerAddr_, s.auth_, s.username_, recipients, []byte(emailBody))

	return err
}
<commit_msg>refactored to use a new type for passing email addresses to Smail functions, as well as better safety checks<commit_after>package smail

import(
	"net/smtp"
	"strings"
	"errors"
)

type Smail struct {
	username_ string
	auth_ smtp.Auth
	fullServerAddr_ string
}

/*
	Makes a new Smail object with the given
*/
func NewSmail(server, port, username, password string) (*Smail, error) {
	//make sure parameters given were not empty
	if server == "" { return nil, errors.New("No server specified") }
	if port == "" { return nil, errors.New("No port specified") }
	if username == "" { return nil, errors.New("No username specified") }
	if password == "" { return nil, errors.New("No password specified") }

	//make a PlainAuth object
	auth := smtp.PlainAuth("", username, password, server)

	//make a smail object and return a pointer to it
	return &Smail{username, auth, server + ":" + port}, nil
}

/*
	Sends a plaintext email to the specified recipient email adress(es), with the given subject and body.
	NOTE: it is assumed that the given email adresses are valid.
*/
func (s *Smail) SendPlaintextEmail(recipients *AddrList, subject string, body string) error {

	if recipients.Empty() {
		return errors.New("Empty AddrList given")
	}

	emailBody := "To: "+ recipients.csv + "\r\nSubject: " + subject + "\r\n\r\n" + body

	err := smtp.SendMail(s.fullServerAddr_, s.auth_, s.username_, recipients.slice, []byte(emailBody))

	return err
}

/*
	Sends an HTML email to the specified recipient email adress(es), with the given subject and body.
	NOTE: it is assumend that the given email adresses ware valid.
*/
func (s *Smail) SendHTMLEmail(recipients *AddrList, subject string, body string) error {

	if recipients.Empty() {
		return errors.New("Empty AddrList given")
	}

	mime := "MIME-version: 1.0;\r\nContent-Type: text/html; charset=\"UTF-8\";\r\n\r\n";

	emailBody := "To: "+ recipients.csv + "\r\nSubject: " + subject + "\r\n" + mime + body

	err := smtp.SendMail(s.fullServerAddr_, s.auth_, s.username_, recipients.slice, []byte(emailBody))

	return err
}

/*
	Takes a string of comma-seperated email addresses (with or without spaces), and splits it into a
	slice of email adresses
*/
func toAddrSlice(addresses string) []string {
	if addresses == "" {
		return make([]string, 0, 0)
	}

	//remove all spaces from the string, and then split the string on all commas
	return strings.Split(strings.Replace(addresses, " ", "", -1), ",")
}

/*
	Takes a slice of email adresses, and combines them into a comma seperated list of email adresses,
	with spaces trailing each comma
*/
func toAddrString(addresses []string) string {

	if len(addresses) < 1 {
		return ""
	}

	var ret = ""
	//loop through all but the last element in the address slice, appending them to the ret string with a comma afterwards
	for _, v := range addresses[:len(addresses)-1] {
		ret += v + ", "
	}
	//append the last address to the ret string & return the list of addresses
	ret += addresses[len(addresses)-1]
	return ret
}

type AddrList struct {
	slice []string
	csv string
}

/*
	Makes an empty AddrList
*/
func NewAddrList() *AddrList {
	//return a reference to a new AddrList with an empty slice (with capacity 1), and an empty string
	return &AddrList{make([]string, 0, 1), ""}
}

/*
	Makes an AddrList containing the comma-seperated email addresses in the given string
*/
func NewAddrListFromString(list string) *AddrList {
	//If we're given an empty string, just return an empty AddrList
	if list == "" {
		return NewAddrList()
	}

	return &AddrList{toAddrSlice(list), list}
}

/*
	Makes an AddrList containing the email addresses in the given slice
*/
func NewAddrListFromSlice(slice []string) *AddrList {
	//if we're given an empty slice, just return an empty AddrList
	if len(slice) == 0 {
		return NewAddrList()
	}

	return &AddrList{slice, toAddrString(slice)}
}

/*
	Adds a single address to the AddrList
*/
func (al *AddrList) AddAddress(address string) {
	al.slice = append(al.slice, address)

	if al.csv != "" {
		al.csv += ", "
	}

	al.csv += address
}

/*
	Adds multiple addresses to the AddrList
*/
func (al *AddrList) AddAddresses(addresses []string) {
	al.slice = append(al.slice, addresses...)

	//re-build the comma-seperated string of addresses
	al.csv = toAddrString(al.slice)

}

/*
	Removes a single address from the AddrList. If the address is not in the AddrList, does nothing.
	Worst Case: O(n), where n = # of addresses already in the list
*/
func (al *AddrList) RemoveAddress(address string) {

	al.slice = removeStringFromSlice(address, al.slice)

	//re-build the comma-seperated string of addresses
	al.csv = toAddrString(al.slice)

}

/*
	Removes multiple addresses from the AddrList. If not all addresses given exist in the AddrList,
	it will only remove those that do.
	Worst Case: O(kn), where k = # of addresses given to remove, n = # of adresses in the list
*/
func (al *AddrList) RemoveAddresses(addresses []string) {

	for _, v := range addresses {
		al.slice = removeStringFromSlice(v, al.slice)
	}

	//re-build the comma-seperated string of addresses
	al.csv = toAddrString(al.slice)

}

/*
	Removes the first instance of the given string from the given slice. If not instance of the
	string extist in the slice, the original slice given is returned
	Note: This function is case sensetive
	Worst Case: O(n), where n = # of elements in the slice
*/
func removeStringFromSlice(str string, slice []string) []string {
	for i, v := range slice {
		if v == str {
			//append the subslice of all elements after this one, to the sublice of all elements before this one
			return append(slice[:i], slice[i+1:]...)
		}
	}

	//if the string was not present, just return the slice back
	return slice
}

/*
	Predicate function to check if an AddrList is empty (ie. it contains to addresses)
*/
func (al *AddrList) Empty() bool {
	return len(al.slice) == 0 && al.csv == ""
}

/*
	Get a string representation of the AddrList
*/
func (al *AddrList) String() string {
	return al.csv
}

<|endoftext|>


<commit_before>// Copyright 2014 Christoph Berger. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// See the file README.md about usage of the start package.
package start

import (
	flag "github.com/ogier/pflag"
)

var cfg *ConfigFile
var cfgFileName string = ""
var customName bool = false

func UseConfigFile(fn string) {
	cfgFileName = fn
	customName = true
}

func Parse() error {
	cfg = NewConfigFile(cfgFileName)
	flag.VisitAll(func(f *flag.Flag) {
		f.Value.Set(cfg.String(f.Name))
	})
	return nil
}

func Up() error {
	return nil
}
<commit_msg>Added package comment Added function comment for Parse() Implemented Parse() (initial draft)<commit_after>// Package start combines four common tasks for setting up an
// commandline application:
//
// * Reading settings from a configuration file
// * Reading environment variables
// * Reading command line flags
// * Defining commands and subcommands
//
// See the file README.md about usage of the start package.
//
// Copyright 2014 Christoph Berger. All rights reserved.
// Use of this source code is governed by the BSD (3-Clause)
// License that can be found in the LICENSE.txt file.
//
// This source code may use third-party source code whose
// licenses are provided in the respective license files.
package start

import (
	flag "github.com/ogier/pflag"
	"os"
)

var cfg *ConfigFile
var cfgFileName string = ""
var customName bool = false

func UseConfigFile(fn string) {
	cfgFileName = fn
	customName = true
}

// Parse initializes all flag variables from command line flags, environment variables, configuration file entries, or default values.
// After this, each flag variable has a value either -
// - from a command line flag, or
// - from an environment variable, if the flag is not set, or
// - from an entry in the config file, if the environment variable is not set, or
// - from its default value, if there is no entry in the config file.
func Parse() error {
	cfg = NewConfigFile(cfgFileName)
	flag.VisitAll(func(f *flag.Flag) {
		// first, set the values from the config file:
		f.Value.Set(cfg.String(f.Name))
		// then, find an apply environment variables:
		envVar := os.Getenv(f.Name)
		if len(envVar) > 0 {
			f.Value.Set(envVar)
		}
	})
	// finally, parse the command line flags:
	flag.Parse()
	return nil
}

func Up() error {
	return nil
}
<|endoftext|>


<commit_before>package sudoku

// Holds the global Sudoku board state

// Communication is handled by four major mechanics:
// * A channel to distribute the current board state
// * A channel to notify threads of updates
// * A channel to update known & possible values

import "math"

const (
	boardRow    = 0
	boardCol    = 1
	boardSquare = 2
)

// coord contains x and y elements for a given position
type coord struct {
	x int
	y int
}

// A cell holds all of the required knowledge for a cell.
// It contains it's own address on the board (guarenteed unique)
// if actual is set to 0, actual value is unknown
type cell struct {
	location coord
	actual   int
	possible []int
}

type cluster []cell

type board struct {
	size     int
	clusters []cluster
}

/*
type Cluster interface {
	Len() int
	Swap(i, j int)
	Less(i, j int)
}
*/

func (c Cluster) Len() int {
	return len(c)
}
func (c Cluster) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}
func (c Cluster) Less(i, j int) bool {
	if c[i].x < c[j].x {
		return true
	}
	if c[i].x == c[j].x && c[i].y < c[j].y {
		return true
	}
	return false
}

func createBoard(size int) board {
	var newBoard board
	newBoard.size = size
	for i := 0; i < size*size; i++ {
		for j := 0; j < size*size; j++ {
			board[coord{x: i, y: j}].location = coord{x: i, y: j}
			for k := 1; k <= size*size; k++ {
				board[coord{x: i, y: j}].possible = append(board[coord{x: i, y: j}].possible, k)
			}
		}
	}
	return newBoard
}

func getPos(position coord, orientation, size int) (int, error) {
	if position.x >= size*size {
		return -1, errors.New("x position is larger than the board")
	}
	if position.y >= size*size {
		return -1, errors.New("y position is larger than the board")
	}
	switch orientation {
	case boardRow:
		return position.x
	case boardCol:
		return position.y
	case boardSquare:
		return ((position.y / size) * size) + (position.x / size)
	default:
		return -1, errors.New("bad position")
	}
}

func clusterPicker(in board, orient int, position coord) (cluster, error) {
	if position.x >= len(in) {
		return cluster{}, errors.New("x coord out of range")
	} else if position.y >= len(in) {
		return cluster{}, errors.New("y coord out of range")
	}
	switch orient {
	case boardRow:
		return in[position.y], nil
	case boardCol:
		var result cluster
		for _, each := range in {
			result = append(result, each[position.x])
		}
		return outCluster, nil
	case boardSquare:
		var result cluster
		boardSize := int(math.Sqrt(len(board)))
		startX := (position.x / boardSize) * boardSize
		endX := ((position.x / boardSize) + 1) * boardSize
		startY := (position.y / boardSize) * boardSize
		endY := ((position.y / boardSize) + 1) * boardSize
		for x := startX; x < endX; x++ {
			for y := startY; y < endY; y++ {
				result = append(result, in[x][y])
			}
		}
		return result, nil
	default:
		return cluster{}, errors.New("bad orientation")
	}
}

func clusterFilter(update <-chan coord, in <-chan board, out [][]chan<- cluster, status [][]<-chan struct{}) {

	var toWork cluster
	defer closeArrArrChan(out)
	// don't actually do anything until you have a board state to work with
	<-in
	for {
		select {
		case changed, more := <-update:
			if !more {
				return
			}
			for i = boardRow; i <= boardSquare; i++ {
				curBoard := <-in
				position := getPos(changed, i, curBoard.size)
				if _, open := <-status[i][position]; !open {
					// skip this update if the cluster is already solved
					continue
				}
				curCluster, err := clusterPicker(curBoard, i, changed)
				if err != nil {
					panic(err) // #TODO# replace this panic
				}
				out[i][position] <- curCluster
			}
		}
	}
}

// takes an input and sends it out as it can
// exits when in is closed
// closes out on exit
func clusterSticky(in <-chan cluster, out chan<- cluster) {
	var more bool
	defer close(out)

	// preload the locCluster - don't do anything until you have that
	locCluster := <-in
	for {
		select {
		case locCluster, more = <-in:
			// if you get an update from upstream, do that first
			if !more {
				return
			}
		// pass the update downstream then block till you get another update from upstream
		case out <- locCluster:
			locCluster = <-in
		}
	}
}

// like a buffered channel, but no limit to the buffer size
// closes out on exit
// exits when in is closed
func updateBuffer(in <-chan cell, out chan<- cell) {
	var updates []interface{}
	var singleUpdate interface{}
	var open bool
	defer close(out)

	for {
		if len(updates) < 1 {
			// if you currently don't have anything to pass, WAIT FOR SOMETHING
			singleUpdate, open = <-in
			if !open {
				return
			}
			updates = append(updates, singleUpdate)
			continue
		}
		select {
		case singleUpdate, open = <-in:
			if !open {
				return
			}
			updates = append(updates, singleUpdate)
		case out <- updates[0]:
			updates = updates[1:]
		}
	}
}

// boardCache serves a given (or newer) update out as many times as requested
// closes `out` on exit
// exits when in `is` closed
func boardCache(in <-chan board, out chan<- board) {
	currentBoard := <-in
	var done bool
	defer close(out)

	for {
		select {
		case currentBoard, done = <-in:
			if done {
				return
			}
		case out <- currentBoard:
		}
	}
}

// looks for a send from all status channels
// if recieved or closed on all status chan, send on `idle`
// if all status is closed, close idle and exits
func idleCheck(status [][]<-chan interface{}, idle chan<- interface{}) {
	var more, isIdle, isSolved bool
	defer close(idle)

	for {
	start:
		isSolved, isIdle = true, true
		for _, middle := range status {
			for _, inner := range status {
				select {
				case _, more = <-inner:
					if more {
						isSolved = false
					}
				default:
					isSolved = false
					isIdle = false
					continue start
				}
			}
		}
		if isSolved {
			return
		} else if isIdle {
			idle <- nil
		}
	}
}

// takes a given cluster, and runs it through all of the moves
// exits when one of the conditions is met, or when the update channel is closed
// closes the status channel on exit
func clusterWorker(in <-chan cluster, status chan<- struct{}, updates chan<- cell, problems chan<- error) {
	defer close(status)

	var more bool
	var newCluster cluster
	var index indexedCluster
	var changes, newChanges []cell

	for {
		select {
		case newCluster, more = <-in:
			if !more {
				// if the channel is closed, exit
				return
			}
			if clusterSolved(cluster) {
				// if the cell is solved, exit
				return
			}

			newChanges = solvedNoPossible(cluster.cells)
			changes = append(changes, newChanges)

			newChanges = eliminateKnowns(cluster.cells)
			changes = append(changes, newChanges)

			newChanges = singleValueSolver(cluster.cells)
			changes = append(changes, newChanges)

			newChanges = cellLimiter(cluster.cells)
			changes = append(changes, newChanges)

			index = indexCluster(cluster.cells)

			newChanges = singleCellSolver(index, cluster.cells)
			changes = append(changes, newChanges)

			newChanges = valueLimiter(index, cluster.cells)
			changes = append(changes, newChanges)

			// feed all those changes into the update queue
			for len(changes) > 1 {
				updates <- changes[0]
				changes = changes[1:]
			}
		case status <- nil:
			// report idle only if there is nothing to do - order matters
		}
	}
}
<commit_msg>started writing the update processor<commit_after>package sudoku

// Holds the global Sudoku board state

// Communication is handled by four major mechanics:
// * A channel to distribute the current board state
// * A channel to notify threads of updates
// * A channel to update known & possible values

import "math"

const (
	boardRow    = 0
	boardCol    = 1
	boardSquare = 2
)

// coord contains x and y elements for a given position
type coord struct {
	x int
	y int
}

// A cell holds all of the required knowledge for a cell.
// It contains it's own address on the board (guarenteed unique)
// if actual is set to 0, actual value is unknown
type cell struct {
	location coord
	actual   int
	possible []int
}

type cluster []cell

type board struct {
	size     int
	clusters []cluster
}

/*
type Cluster interface {
	Len() int
	Swap(i, j int)
	Less(i, j int)
}
*/

func (c Cluster) Len() int {
	return len(c)
}
func (c Cluster) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}
func (c Cluster) Less(i, j int) bool {
	if c[i].x < c[j].x {
		return true
	}
	if c[i].x == c[j].x && c[i].y < c[j].y {
		return true
	}
	return false
}

func createBoard(size int) board {
	var newBoard board
	newBoard.size = size
	for i := 0; i < size*size; i++ {
		for j := 0; j < size*size; j++ {
			board[coord{x: i, y: j}].location = coord{x: i, y: j}
			for k := 1; k <= size*size; k++ {
				board[coord{x: i, y: j}].possible = append(board[coord{x: i, y: j}].possible, k)
			}
		}
	}
	return newBoard
}

func getPos(position coord, orientation, size int) (int, error) {
	if position.x >= size*size {
		return -1, errors.New("x position is larger than the board")
	}
	if position.y >= size*size {
		return -1, errors.New("y position is larger than the board")
	}
	switch orientation {
	case boardRow:
		return position.x
	case boardCol:
		return position.y
	case boardSquare:
		return ((position.y / size) * size) + (position.x / size)
	default:
		return -1, errors.New("bad position")
	}
}

func clusterPicker(in board, orient int, position coord) (cluster, error) {
	if position.x >= len(in) {
		return cluster{}, errors.New("x coord out of range")
	} else if position.y >= len(in) {
		return cluster{}, errors.New("y coord out of range")
	}
	switch orient {
	case boardRow:
		return in[position.y], nil
	case boardCol:
		var result cluster
		for _, each := range in {
			result = append(result, each[position.x])
		}
		return outCluster, nil
	case boardSquare:
		var result cluster
		boardSize := int(math.Sqrt(len(board)))
		startX := (position.x / boardSize) * boardSize
		endX := ((position.x / boardSize) + 1) * boardSize
		startY := (position.y / boardSize) * boardSize
		endY := ((position.y / boardSize) + 1) * boardSize
		for x := startX; x < endX; x++ {
			for y := startY; y < endY; y++ {
				result = append(result, in[x][y])
			}
		}
		return result, nil
	default:
		return cluster{}, errors.New("bad orientation")
	}
}

func clusterFilter(update <-chan coord, in <-chan board, out [][]chan<- cluster, status [][]<-chan struct{}) {

	var toWork cluster
	defer closeArrArrChan(out)
	// don't actually do anything until you have a board state to work with
	<-in
	for {
		select {
		case changed, more := <-update:
			if !more {
				return
			}
			for i = boardRow; i <= boardSquare; i++ {
				curBoard := <-in
				position := getPos(changed, i, curBoard.size)
				if _, open := <-status[i][position]; !open {
					// skip this update if the cluster is already solved
					continue
				}
				curCluster, err := clusterPicker(curBoard, i, changed)
				if err != nil {
					panic(err) // #TODO# replace this panic
				}
				out[i][position] <- curCluster
			}
		}
	}
}

// takes an input and sends it out as it can
// exits when in is closed
// closes out on exit
func clusterSticky(in <-chan cluster, out chan<- cluster) {
	var more bool
	defer close(out)

	// preload the locCluster - don't do anything until you have that
	locCluster := <-in
	for {
		select {
		case locCluster, more = <-in:
			// if you get an update from upstream, do that first
			if !more {
				return
			}
		// pass the update downstream then block till you get another update from upstream
		case out <- locCluster:
			locCluster = <-in
		}
	}
}

// like a buffered channel, but no limit to the buffer size
// closes out on exit
// exits when in is closed
func updateBuffer(in <-chan cell, out chan<- cell) {
	var updates []interface{}
	var singleUpdate interface{}
	var open bool
	defer close(out)

	for {
		if len(updates) < 1 {
			// if you currently don't have anything to pass, WAIT FOR SOMETHING
			singleUpdate, open = <-in
			if !open {
				return
			}
			updates = append(updates, singleUpdate)
			continue
		}
		select {
		case singleUpdate, open = <-in:
			if !open {
				return
			}
			updates = append(updates, singleUpdate)
		case out <- updates[0]:
			updates = updates[1:]
		}
	}
}

// boardCache serves a given (or newer) update out as many times as requested
// closes `out` on exit
// exits when in `is` closed
func boardCache(in chan board, out chan<- board) {
	currentBoard := <-in
	var done bool
	defer close(out)

	for {
		select {
		case currentBoard, done = <-in:
			if done {
				return
			}
		case in <- currentBoard:
		case out <- currentBoard:
		}
	}
}

// looks for a send from all status channels
// if recieved or closed on all status chan, send on `idle`
// if all status is closed, close idle and exits
func idleCheck(status [][]<-chan interface{}, idle chan<- interface{}) {
	var more, isIdle, isSolved bool
	defer close(idle)

	for {
	start:
		isSolved, isIdle = true, true
		for _, middle := range status {
			for _, inner := range status {
				select {
				case _, more = <-inner:
					if more {
						isSolved = false
					}
				default:
					isSolved = false
					isIdle = false
					continue start
				}
			}
		}
		if isSolved {
			return
		} else if isIdle {
			idle <- nil
		}
	}
}

// takes a given cluster, and runs it through all of the moves
// exits when one of the conditions is met, or when the update channel is closed
// closes the status channel on exit
func clusterWorker(in <-chan cluster, status chan<- struct{}, updates chan<- cell, problems chan<- error) {
	defer close(status)

	var more bool
	var newCluster cluster
	var index indexedCluster
	var changes, newChanges []cell

	for {
		select {
		case newCluster, more = <-in:
			if !more {
				// if the channel is closed, exit
				return
			}
			if clusterSolved(cluster) {
				// if the cell is solved, exit
				return
			}

			newChanges = solvedNoPossible(cluster.cells)
			changes = append(changes, newChanges)

			newChanges = eliminateKnowns(cluster.cells)
			changes = append(changes, newChanges)

			newChanges = singleValueSolver(cluster.cells)
			changes = append(changes, newChanges)

			newChanges = cellLimiter(cluster.cells)
			changes = append(changes, newChanges)

			index = indexCluster(cluster.cells)

			newChanges = singleCellSolver(index, cluster.cells)
			changes = append(changes, newChanges)

			newChanges = valueLimiter(index, cluster.cells)
			changes = append(changes, newChanges)

			// feed all those changes into the update queue
			for len(changes) > 1 {
				updates <- changes[0]
				changes = changes[1:]
			}
		case status <- nil:
			// report idle only if there is nothing to do - order matters
		}
	}
}

//
func updateProcessor(curBoard chan board, status <-chan struct{}, updates <-chan cell, posChange chan<- coord, problems <-chan error) {
	defer close(curBoard)
	defer close(posChange)

}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/davecgh/go-spew/spew"
	"github.com/hashicorp/terraform/terraform"
)

func fetchState(dir string) (*terraform.State, error) {
	src, err := openState(dir)
	if err != nil {
		return nil, err
	}
	defer src.Close()

	state, err := terraform.ReadState(src)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Unable to read state from src"))
	}
	return state, nil
}

func openState(dir string) (io.ReadCloser, error) {
	file := filepath.Join(dir, "terraform.tfstate")
	spew.Dump(file)
	f, err := os.Open(file)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Unable to open '%s'", file))
	}
	return f, nil
}
<commit_msg>Remove leftover debugging spew.Dump()<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/hashicorp/terraform/terraform"
)

func fetchState(dir string) (*terraform.State, error) {
	src, err := openState(dir)
	if err != nil {
		return nil, err
	}
	defer src.Close()

	state, err := terraform.ReadState(src)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Unable to read state from src"))
	}
	return state, nil
}

func openState(dir string) (io.ReadCloser, error) {
	file := filepath.Join(dir, "terraform.tfstate")
	f, err := os.Open(file)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Unable to open '%s'", file))
	}
	return f, nil
}
<|endoftext|>


<commit_before>package stemp

import (
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
)

// Stemplate is the main object of the package.
// The users will interface only with this one.
type Stemplate struct {
	// LiveReload set to TRUE make the changes to html pages aviable without restarting the server
	// [Default]: false
	LiveReload bool

	templatesDir string
	templates    map[string]*template.Template
}

//NewStemplate create a new Istance of Stemplate obj
func NewStemplate(templatesDirectory string) (*Stemplate, error) {
	var st Stemplate

	st.templatesDir = templatesDirectory
	st.LiveReload = false
	st.templates = make(map[string]*template.Template)

	if err := st.load(); err != nil {
		return nil, err
	}

	return &st, nil
}

// load function will store in RAM the , just compiled, templates.
func (st *Stemplate) load() error {

	templates, terr := filepath.Glob(st.templatesDir + "*.tmpl")
	if terr != nil {
		return terr
	}

	contents, err := filepath.Glob(st.templatesDir + "*.html")
	if err != nil {
		return err
	}

	for _, c := range contents {
		current := append(templates, c)
		st.templates[filepath.Base(c)] = template.Must(template.ParseFiles(current...))
	}

	return nil

}

// Reload is an utily function that allow to recompile templates at run time
func (st *Stemplate) Reload() {

	stReloaded, err := NewStemplate(st.templatesDir)
	if err != nil {
		fmt.Println("[Reloading templates]: ERROR")
		return
	}

	stReloaded.LiveReload = st.LiveReload

	st = stReloaded
}

// Render will parse and compile
func (st *Stemplate) Render(w *http.ResponseWriter, templateName string) {

	if !st.LiveReload {
		st.templates[templateName].ExecuteTemplate(*w, "base", nil)
	} else {
		template.Must(template.ParseFiles(st.templatesDir+templateName, st.templatesDir+"base.tmpl")).ExecuteTemplate(*w, "base", nil)
	}

}
<commit_msg>Live Reload checks and fixs<commit_after>package stemp

import (
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
)

// Stemplate is the main object of the package.
// The users will interface only with this one.
type Stemplate struct {
	// LiveReload set to TRUE make the changes to html pages aviable without restarting the server
	// [Default]: false
	LiveReload bool

	templatesDir string
	templates    map[string]*template.Template
}

//NewStemplate create a new Istance of Stemplate obj
func NewStemplate(templatesDirectory string) (*Stemplate, error) {
	var st Stemplate

	st.templatesDir = templatesDirectory
	st.LiveReload = false
	st.templates = make(map[string]*template.Template)

	if err := st.load(); err != nil {
		return nil, err
	}

	return &st, nil
}

// load function will store in RAM the , just compiled, templates.
func (st *Stemplate) load() error {

	templates, terr := filepath.Glob(st.templatesDir + "*.tmpl")
	if terr != nil {
		return terr
	}

	contents, err := filepath.Glob(st.templatesDir + "*.html")
	if err != nil {
		return err
	}

	for _, c := range contents {
		current := append(templates, c)
		st.templates[filepath.Base(c)] = template.Must(template.ParseFiles(current...))
	}

	return nil

}

// loadTemplate will load at run time the desidered temp
func (st *Stemplate) loadTemplate(tname string) *template.Template {

	templates, terr := filepath.Glob(st.templatesDir + "*.tmpl")
	if terr != nil {
		fmt.Println("[JIT template]: ERROR ~ " + terr.Error())
		return nil
	}

	templates = append(templates, st.templatesDir+tname)

	return template.Must(template.ParseFiles(templates...))
}

// Reload is an utily function that allow to recompile templates at run time
func (st *Stemplate) Reload() {

	// useless if we are using live reload
	if st.LiveReload {
		return
	}

	stReloaded, err := NewStemplate(st.templatesDir)
	if err != nil {
		fmt.Println("[Reloading templates]: ERROR")
		return
	}

	st = stReloaded
}

// Render will parse and compile
func (st *Stemplate) Render(w *http.ResponseWriter, templateName string) {

	if !st.LiveReload {
		st.templates[templateName].ExecuteTemplate(*w, "base", nil)
	} else {
		st.loadTemplate(templateName).ExecuteTemplate(*w, "base", nil)
	}

}
<|endoftext|>


<commit_before>package gcache

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"time"

	"google.golang.org/api/drive/v3"
	"google.golang.org/api/googleapi"
)

var (
	parentFolderID string
	random         *rand.Rand

	folderParams = &drive.File{
		Name:     folderName,
		MimeType: mimeGSuiteFolder,
	}

	folderName       string
	folderPermission *drive.Permission
)

func init() {
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
}

// SetRootFolder sets name and permission to a top folder on Google Drive.
func SetRootFolder(
	name string,
	permission *drive.Permission,
) {
	folderName = name
	folderPermission = permission
}

func getParentFolderID(
	r *http.Request,
) (
	string,
	error,
) {
	if parentFolderID != "" {
		return parentFolderID, nil
	}
	parentFolderID, err := getDriveFolder(r)
	if err != nil {
		return "", err
	}
	return parentFolderID, nil
}

func getDriveFolder(
	r *http.Request,
) (
	string,
	error,
) {

retry:
	service, err := drive.New(createGDriveClient(r))
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		}
		return "", err
	}

	fileList, err := service.Files.List().PageSize(1).Spaces("drive").Q(
		fmt.Sprintf("name='%s' and mimeType='%s'", folderName, mimeGSuiteFolder),
	).Fields(MinimumField).Do()
	if err != nil {
		return "", err
	}
	if len(fileList.Files) == 1 {
		return fileList.Files[0].Id, nil
	}

	return createDriveFolder(r)
}

func createDriveFolder(
	r *http.Request,
) (
	string,
	error,
) {

retry:
	service, err := drive.New(createGDriveClient(r))
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		}
		return "", err
	}

	file, err := service.Files.Create(folderParams).Do()
	if err != nil {
		return "", err
	}
	_, err = service.Permissions.Create(file.Id, folderPermission).Do()
	if err != nil {
		return "", err
	}
	return file.Id, nil
}

// StoreGDrive stores a file to Google Drive.
func StoreGDrive(
	r *http.Request,
	name string,
	payload *[]byte,
) (
	*drive.File,
	error,
) {
	n := 1
retry:
	service, err := drive.New(createGDriveClient(r))
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		} else if IsServerError(err) {
			n, err = sleeping(n)
			if err == nil {
				goto retry
			}
		}
		return nil, err
	}

	folderID, err := getParentFolderID(r)
	if err != nil {
		return nil, err
	}

	file, err := service.Files.Create(&drive.File{
		Name:     name,
		MimeType: mimeGSuiteDoc,
		Parents:  []string{folderID},
	}).Media(bytes.NewReader(*payload), googleapi.ContentType(mimeTxt)).Do()
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		} else if IsServerError(err) {
			n, err = sleeping(n)
			if err == nil {
				goto retry
			}
		}
		return nil, err
	}
	return file, nil
}

func sleeping(
	n int,
) (
	int,
	error,
) {
	if n > 16 {
		return 0, errors.New("Sleeping Timeout")
	}
	time.Sleep(time.Duration(n)*time.Second + time.Duration(random.Intn(1000))*time.Millisecond)
	return n * 2, nil
}
<commit_msg>handling drive.File<commit_after>package gcache

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"time"

	"google.golang.org/api/drive/v3"
	"google.golang.org/api/googleapi"
)

var (
	parentFolderID string
	random         *rand.Rand

	folderParams = &drive.File{
		Name:     folderName,
		MimeType: mimeGSuiteFolder,
	}

	folderName       string
	folderPermission *drive.Permission
)

func init() {
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
}

// SetRootFolder sets name and permission to a top folder on Google Drive.
func SetRootFolder(
	name string,
	permission *drive.Permission,
) {
	folderName = name
	folderPermission = permission
}

func getParentFolderID(
	r *http.Request,
) (
	string,
	error,
) {
	if parentFolderID != "" {
		return parentFolderID, nil
	}
	parentFolderID, err := getDriveFolder(r)
	if err != nil {
		return "", err
	}
	return parentFolderID, nil
}

func getDriveFolder(
	r *http.Request,
) (
	string,
	error,
) {

retry:
	service, err := drive.New(createGDriveClient(r))
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		}
		return "", err
	}

	fileList, err := service.Files.List().PageSize(1).Spaces("drive").Q(
		fmt.Sprintf("name='%s' and mimeType='%s'", folderName, mimeGSuiteFolder),
	).Fields(MinimumField).Do()
	if err != nil {
		return "", err
	}
	if len(fileList.Files) == 1 {
		return fileList.Files[0].Id, nil
	}

	return createDriveFolder(r)
}

func createDriveFolder(
	r *http.Request,
) (
	string,
	error,
) {

retry:
	service, err := drive.New(createGDriveClient(r))
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		}
		return "", err
	}

	file, err := service.Files.Create(folderParams).Do()
	if err != nil {
		return "", err
	}
	_, err = service.Permissions.Create(file.Id, folderPermission).Do()
	if err != nil {
		return "", err
	}
	return file.Id, nil
}

// StoreGDrive stores a file to Google Drive.
func StoreGDrive(
	r *http.Request,
	file *drive.File,
	payload *[]byte,
) (
	*drive.File,
	error,
) {

	if file.Name == "" {
		return nil, errors.New("`file.Name` must be enough")
	}

	n := 1
retry:
	service, err := drive.New(createGDriveClient(r))
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		} else if IsServerError(err) {
			n, err = sleeping(n)
			if err == nil {
				goto retry
			}
		}
		return nil, err
	}

	folderID, err := getParentFolderID(r)
	if err != nil {
		return nil, err
	}

	file.Parents = append(file.Parents, folderID)
	file.MimeType = mimeGSuiteDoc

	file, err = service.Files.Create(file).Media(bytes.NewReader(*payload), googleapi.ContentType(mimeTxt)).Do()
	if err != nil {
		if IsInvalidSecurityTicket(err) {
			oauth2TokenSource = nil
			goto retry
		} else if IsServerError(err) {
			n, err = sleeping(n)
			if err == nil {
				goto retry
			}
		}
		return nil, err
	}
	return file, nil
}

func sleeping(
	n int,
) (
	int,
	error,
) {
	if n > 16 {
		return 0, errors.New("Sleeping Timeout")
	}
	time.Sleep(time.Duration(n)*time.Second + time.Duration(random.Intn(1000))*time.Millisecond)
	return n * 2, nil
}
<|endoftext|>


<commit_before>package appdash

import (
	"encoding/gob"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"
)

// A Store stores and retrieves spans.
type Store interface {
	Collector

	// Trace gets a trace (a tree of spans) given its trace ID. If no
	// such trace exists, ErrTraceNotFound is returned.
	Trace(ID) (*Trace, error)
}

var (
	// ErrTraceNotFound is returned by Store.GetTrace when no trace is
	// found with the given ID.
	ErrTraceNotFound = errors.New("trace not found")
)

// A Queryer indexes spans and makes them queryable.
type Queryer interface {
	// Traces returns an implementation-defined list of traces. It is
	// a placeholder method that will be removed when other, more
	// useful methods are added to Queryer.
	Traces() ([]*Trace, error)
}

// NewMemoryStore creates a new in-memory store
func NewMemoryStore() *MemoryStore {
	return &MemoryStore{
		trace: map[ID]*Trace{},
		span:  map[ID]map[ID]*Trace{},
	}
}

// A MemoryStore is an in-memory Store that also implements the PersistentStore
// interface.
type MemoryStore struct {
	trace map[ID]*Trace        // trace ID -> trace tree
	span  map[ID]map[ID]*Trace // trace ID -> span ID -> trace (sub)tree

	sync.Mutex // protects trace

	log bool
}

// Compile-time "implements" check.
var _ interface {
	Store
	Queryer
} = (*MemoryStore)(nil)

// Collect implements the Collector interface by collecting the events that
// occured in the span in-memory.
func (ms *MemoryStore) Collect(id SpanID, as ...Annotation) error {
	ms.Lock()
	defer ms.Unlock()

	if ms.log {
		log.Printf("Collect %v", id)
	}

	// Initialize span map if needed.
	if _, present := ms.span[id.Trace]; !present {
		ms.span[id.Trace] = map[ID]*Trace{}
	}

	// Create or update span.
	s, present := ms.span[id.Trace][id.Span]
	if !present {
		s = &Trace{Span: Span{ID: id, Annotations: as}}
		ms.span[id.Trace][id.Span] = s
	} else {
		if ms.log {
			if len(as) > 0 {
				log.Printf("Add %d annotations to %v", len(as), id)
			}
		}
		s.Annotations = append(s.Annotations, as...)
		return nil
	}

	// Create trace tree if it doesn't already exist.
	root, present := ms.trace[id.Trace]
	if !present {
		// Root span hasn't been seen yet, so make this the temporary
		// root (until we collect the actual root).
		if ms.log {
			if id.IsRoot() {
				log.Printf("Create trace %v root %v", id.Trace, id)
			} else {
				log.Printf("Create temporary trace %v root %v", id.Trace, id)
			}
		}
		ms.trace[id.Trace] = s
		root = s
	}

	// If there's a temp root and we just collected the real
	// root, fix up the tree. Or if we're the temp root's
	// parents, set us up as the new temp root.
	if isRoot, isTempRootParent := id.IsRoot(), root.Span.ID.Parent == id.Span; s != root && (isRoot || isTempRootParent) {
		oldRoot := root
		root = s
		if ms.log {
			if isRoot {
				log.Printf("Set real root %v and move temp root %v", root.Span.ID, oldRoot.Span.ID)
			} else {
				log.Printf("Set new temp root %v and move previous temp root %v (child of new temp root)", root.Span.ID, oldRoot.Span.ID)
			}
		}
		ms.trace[id.Trace] = root // set new root
		ms.reattachChildren(root, oldRoot)
		ms.insert(root, oldRoot) // reinsert the old root

		// Move the old temp root's temp children to the new
		// (possibly temp) root.
		var sub2 []*Trace
		for _, c := range oldRoot.Sub {
			if c.Span.ID.Parent != oldRoot.Span.ID.Span {
				if ms.log {
					log.Printf("Move %v from old root %v to new (possibly temp) root %v", c.Span.ID, oldRoot.Span.ID, root.Span.ID)
				}
				root.Sub = append(root.Sub, c)
			} else {
				sub2 = append(sub2, c)
			}
		}
		oldRoot.Sub = sub2
	}

	// Insert into trace tree. (We inserted the trace root span
	// above.)
	if !id.IsRoot() && s != root {
		ms.insert(root, s)
	}

	// See if we're the parent of any of the root's temporary
	// children.
	if s != root {
		ms.reattachChildren(s, root)
	}

	return nil
}

// insert inserts t into the trace tree whose root (or temp root) is
// root.
func (ms *MemoryStore) insert(root, t *Trace) {
	p, present := ms.span[t.ID.Trace][t.ID.Parent]
	if present {
		if ms.log {
			log.Printf("Add %v as a child of parent %v", t.Span.ID, p.Span.ID)
		}
		p.Sub = append(p.Sub, t)
	} else {
		// Add as temporary child of the root for now. When the
		// real parent is added, we'll fix it up later.
		if ms.log {
			log.Printf("Add %v as a temporary child of root %v", t.Span.ID, root.Span.ID)
		}
		root.Sub = append(root.Sub, t)
	}
}

// reattachChildren moves temporary children of src to dst, if dst is
// the node's parent.
func (ms *MemoryStore) reattachChildren(dst, src *Trace) {
	if dst == src {
		panic("dst == src")
	}
	var sub2 []*Trace
	for _, c := range src.Sub {
		if c.Span.ID.Parent == dst.Span.ID.Span {
			if ms.log {
				log.Printf("Move %v from src %v to dst %v", c.Span.ID, src.Span.ID, dst.Span.ID)
			}
			dst.Sub = append(dst.Sub, c)
		} else {
			sub2 = append(sub2, c)
		}
	}
	src.Sub = sub2
}

// Trace implements the Store interface by returning the Trace (a tree of
// spans) for the given trace span ID or, if no such trace exists, by returning
// ErrTraceNotFound.
func (ms *MemoryStore) Trace(id ID) (*Trace, error) {
	ms.Lock()
	defer ms.Unlock()

	return ms.traceNoLock(id)
}

func (ms *MemoryStore) traceNoLock(id ID) (*Trace, error) {
	t, present := ms.trace[id]
	if !present {
		return nil, ErrTraceNotFound
	}
	return t, nil
}

// Traces implements the Queryer interface.
func (ms *MemoryStore) Traces() ([]*Trace, error) {
	ms.Lock()
	defer ms.Unlock()

	var ts []*Trace
	for id := range ms.trace {
		t, err := ms.traceNoLock(id)
		if err != nil {
			return nil, err
		}
		ts = append(ts, t)
	}
	return ts, nil
}

// Delete implements the DeleteStore interface by deleting the traces given by
// their span ID's from this in-memory store.
func (ms *MemoryStore) Delete(traces ...ID) error {
	ms.Lock()
	defer ms.Unlock()

	for _, id := range traces {
		delete(ms.trace, id)
		delete(ms.span, id)
	}
	return nil
}

type memoryStoreData struct {
	Trace map[ID]*Trace
	Span  map[ID]map[ID]*Trace
}

// Write implements the PersistentStore interface by gob-encoding and writing
// ms's internal data structures out to w.
func (ms *MemoryStore) Write(w io.Writer) error {
	ms.Lock()
	defer ms.Unlock()

	data := memoryStoreData{ms.trace, ms.span}
	return gob.NewEncoder(w).Encode(data)
}

// ReadFrom implements the PersistentStore interface by using gob-decoding to
// load ms's internal data structures from the reader r.
func (ms *MemoryStore) ReadFrom(r io.Reader) (int64, error) {
	ms.Lock()
	defer ms.Unlock()

	var data memoryStoreData
	if err := gob.NewDecoder(r).Decode(&data); err != nil {
		return 0, err
	}
	ms.trace = data.Trace
	ms.span = data.Span
	return int64(len(ms.trace)), nil
}

// PersistentStore is a Store that can persist its data and read it
// back in.
type PersistentStore interface {
	Write(io.Writer) error
	ReadFrom(io.Reader) (int64, error)
	Store
}

// PersistEvery persists s's data to a file periodically.
func PersistEvery(s PersistentStore, interval time.Duration, file string) error {
	for {
		time.Sleep(interval)

		f, err := ioutil.TempFile("", "appdash")
		if err != nil {
			return err
		}
		if err := s.Write(f); err != nil {
			f.Close()
			return err
		}
		if err := f.Close(); err != nil {
			return err
		}
		if err := os.Rename(f.Name(), file); err != nil {
			return err
		}
	}
}

// A DeleteStore is a Store that can delete traces.
type DeleteStore interface {
	Store

	// Delete deletes traces given their trace IDs.
	Delete(...ID) error
}

// A RecentStore wraps another store and deletes old traces after a
// specified amount of time.
type RecentStore struct {
	// MinEvictAge is the minimum age of a trace before it is evicted.
	MinEvictAge time.Duration

	// DeleteStore is the underlying store that spans are saved to and
	// deleted from.
	DeleteStore

	// Debug is whether to log debug messages.
	Debug bool

	// created maps trace ID to the UnixNano time it was first seen.
	created map[ID]int64

	// lastEvicted is the last time the eviction process was run.
	lastEvicted time.Time

	mu sync.Mutex // mu guards created and lastEvicted
}

// Collect calls the underlying store's Collect and records the time
// that this trace was first seen.
func (rs *RecentStore) Collect(id SpanID, anns ...Annotation) error {
	rs.mu.Lock()
	if rs.created == nil {
		rs.created = map[ID]int64{}
	}
	if _, present := rs.created[id.Trace]; !present {
		rs.created[id.Trace] = time.Now().UnixNano()
	}
	if time.Since(rs.lastEvicted) > rs.MinEvictAge {
		rs.evictBefore(time.Now().Add(-1 * rs.MinEvictAge))
	}
	rs.mu.Unlock()

	return rs.DeleteStore.Collect(id, anns...)
}

// evictBefore evicts traces that were created before t. The rs.mu lock
// must be held while calling evictBefore.
func (rs *RecentStore) evictBefore(t time.Time) {
	evictStart := time.Now()
	tnano := t.UnixNano()
	var toEvict []ID
	for id, ct := range rs.created {
		if ct < tnano {
			toEvict = append(toEvict, id)
			delete(rs.created, id)
		}
	}
	if len(toEvict) == 0 {
		return
	}

	if rs.Debug {
		log.Printf("RecentStore: deleting %d traces created before %s (age check took %s)", len(toEvict), t, time.Since(evictStart))
	}

	// Spawn separate goroutine so we don't hold the rs.mu lock.
	go func() {
		deleteStart := time.Now()
		if err := rs.DeleteStore.Delete(toEvict...); err != nil {
			log.Printf("RecentStore: failed to delete traces: %s", err)
		}
		if rs.Debug {
			log.Printf("RecentStore: finished deleting %d traces created before %s (took %s)", len(toEvict), t, time.Since(deleteStart))
		}
	}()
}
<commit_msg>Improve RecentStore.Collect performance by ~99%.<commit_after>package appdash

import (
	"encoding/gob"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sync"
	"time"
)

// A Store stores and retrieves spans.
type Store interface {
	Collector

	// Trace gets a trace (a tree of spans) given its trace ID. If no
	// such trace exists, ErrTraceNotFound is returned.
	Trace(ID) (*Trace, error)
}

var (
	// ErrTraceNotFound is returned by Store.GetTrace when no trace is
	// found with the given ID.
	ErrTraceNotFound = errors.New("trace not found")
)

// A Queryer indexes spans and makes them queryable.
type Queryer interface {
	// Traces returns an implementation-defined list of traces. It is
	// a placeholder method that will be removed when other, more
	// useful methods are added to Queryer.
	Traces() ([]*Trace, error)
}

// NewMemoryStore creates a new in-memory store
func NewMemoryStore() *MemoryStore {
	return &MemoryStore{
		trace: map[ID]*Trace{},
		span:  map[ID]map[ID]*Trace{},
	}
}

// A MemoryStore is an in-memory Store that also implements the PersistentStore
// interface.
type MemoryStore struct {
	trace map[ID]*Trace        // trace ID -> trace tree
	span  map[ID]map[ID]*Trace // trace ID -> span ID -> trace (sub)tree

	sync.Mutex // protects trace

	log bool
}

// Compile-time "implements" check.
var _ interface {
	Store
	Queryer
} = (*MemoryStore)(nil)

// Collect implements the Collector interface by collecting the events that
// occured in the span in-memory.
func (ms *MemoryStore) Collect(id SpanID, as ...Annotation) error {
	ms.Lock()
	defer ms.Unlock()

	if ms.log {
		log.Printf("Collect %v", id)
	}

	// Initialize span map if needed.
	if _, present := ms.span[id.Trace]; !present {
		ms.span[id.Trace] = map[ID]*Trace{}
	}

	// Create or update span.
	s, present := ms.span[id.Trace][id.Span]
	if !present {
		s = &Trace{Span: Span{ID: id, Annotations: as}}
		ms.span[id.Trace][id.Span] = s
	} else {
		if ms.log {
			if len(as) > 0 {
				log.Printf("Add %d annotations to %v", len(as), id)
			}
		}
		s.Annotations = append(s.Annotations, as...)
		return nil
	}

	// Create trace tree if it doesn't already exist.
	root, present := ms.trace[id.Trace]
	if !present {
		// Root span hasn't been seen yet, so make this the temporary
		// root (until we collect the actual root).
		if ms.log {
			if id.IsRoot() {
				log.Printf("Create trace %v root %v", id.Trace, id)
			} else {
				log.Printf("Create temporary trace %v root %v", id.Trace, id)
			}
		}
		ms.trace[id.Trace] = s
		root = s
	}

	// If there's a temp root and we just collected the real
	// root, fix up the tree. Or if we're the temp root's
	// parents, set us up as the new temp root.
	if isRoot, isTempRootParent := id.IsRoot(), root.Span.ID.Parent == id.Span; s != root && (isRoot || isTempRootParent) {
		oldRoot := root
		root = s
		if ms.log {
			if isRoot {
				log.Printf("Set real root %v and move temp root %v", root.Span.ID, oldRoot.Span.ID)
			} else {
				log.Printf("Set new temp root %v and move previous temp root %v (child of new temp root)", root.Span.ID, oldRoot.Span.ID)
			}
		}
		ms.trace[id.Trace] = root // set new root
		ms.reattachChildren(root, oldRoot)
		ms.insert(root, oldRoot) // reinsert the old root

		// Move the old temp root's temp children to the new
		// (possibly temp) root.
		var sub2 []*Trace
		for _, c := range oldRoot.Sub {
			if c.Span.ID.Parent != oldRoot.Span.ID.Span {
				if ms.log {
					log.Printf("Move %v from old root %v to new (possibly temp) root %v", c.Span.ID, oldRoot.Span.ID, root.Span.ID)
				}
				root.Sub = append(root.Sub, c)
			} else {
				sub2 = append(sub2, c)
			}
		}
		oldRoot.Sub = sub2
	}

	// Insert into trace tree. (We inserted the trace root span
	// above.)
	if !id.IsRoot() && s != root {
		ms.insert(root, s)
	}

	// See if we're the parent of any of the root's temporary
	// children.
	if s != root {
		ms.reattachChildren(s, root)
	}

	return nil
}

// insert inserts t into the trace tree whose root (or temp root) is
// root.
func (ms *MemoryStore) insert(root, t *Trace) {
	p, present := ms.span[t.ID.Trace][t.ID.Parent]
	if present {
		if ms.log {
			log.Printf("Add %v as a child of parent %v", t.Span.ID, p.Span.ID)
		}
		p.Sub = append(p.Sub, t)
	} else {
		// Add as temporary child of the root for now. When the
		// real parent is added, we'll fix it up later.
		if ms.log {
			log.Printf("Add %v as a temporary child of root %v", t.Span.ID, root.Span.ID)
		}
		root.Sub = append(root.Sub, t)
	}
}

// reattachChildren moves temporary children of src to dst, if dst is
// the node's parent.
func (ms *MemoryStore) reattachChildren(dst, src *Trace) {
	if dst == src {
		panic("dst == src")
	}
	var sub2 []*Trace
	for _, c := range src.Sub {
		if c.Span.ID.Parent == dst.Span.ID.Span {
			if ms.log {
				log.Printf("Move %v from src %v to dst %v", c.Span.ID, src.Span.ID, dst.Span.ID)
			}
			dst.Sub = append(dst.Sub, c)
		} else {
			sub2 = append(sub2, c)
		}
	}
	src.Sub = sub2
}

// Trace implements the Store interface by returning the Trace (a tree of
// spans) for the given trace span ID or, if no such trace exists, by returning
// ErrTraceNotFound.
func (ms *MemoryStore) Trace(id ID) (*Trace, error) {
	ms.Lock()
	defer ms.Unlock()

	return ms.traceNoLock(id)
}

func (ms *MemoryStore) traceNoLock(id ID) (*Trace, error) {
	t, present := ms.trace[id]
	if !present {
		return nil, ErrTraceNotFound
	}
	return t, nil
}

// Traces implements the Queryer interface.
func (ms *MemoryStore) Traces() ([]*Trace, error) {
	ms.Lock()
	defer ms.Unlock()

	var ts []*Trace
	for id := range ms.trace {
		t, err := ms.traceNoLock(id)
		if err != nil {
			return nil, err
		}
		ts = append(ts, t)
	}
	return ts, nil
}

// Delete implements the DeleteStore interface by deleting the traces given by
// their span ID's from this in-memory store.
func (ms *MemoryStore) Delete(traces ...ID) error {
	ms.Lock()
	defer ms.Unlock()

	for _, id := range traces {
		delete(ms.trace, id)
		delete(ms.span, id)
	}
	return nil
}

type memoryStoreData struct {
	Trace map[ID]*Trace
	Span  map[ID]map[ID]*Trace
}

// Write implements the PersistentStore interface by gob-encoding and writing
// ms's internal data structures out to w.
func (ms *MemoryStore) Write(w io.Writer) error {
	ms.Lock()
	defer ms.Unlock()

	data := memoryStoreData{ms.trace, ms.span}
	return gob.NewEncoder(w).Encode(data)
}

// ReadFrom implements the PersistentStore interface by using gob-decoding to
// load ms's internal data structures from the reader r.
func (ms *MemoryStore) ReadFrom(r io.Reader) (int64, error) {
	ms.Lock()
	defer ms.Unlock()

	var data memoryStoreData
	if err := gob.NewDecoder(r).Decode(&data); err != nil {
		return 0, err
	}
	ms.trace = data.Trace
	ms.span = data.Span
	return int64(len(ms.trace)), nil
}

// PersistentStore is a Store that can persist its data and read it
// back in.
type PersistentStore interface {
	Write(io.Writer) error
	ReadFrom(io.Reader) (int64, error)
	Store
}

// PersistEvery persists s's data to a file periodically.
func PersistEvery(s PersistentStore, interval time.Duration, file string) error {
	for {
		time.Sleep(interval)

		f, err := ioutil.TempFile("", "appdash")
		if err != nil {
			return err
		}
		if err := s.Write(f); err != nil {
			f.Close()
			return err
		}
		if err := f.Close(); err != nil {
			return err
		}
		if err := os.Rename(f.Name(), file); err != nil {
			return err
		}
	}
}

// A DeleteStore is a Store that can delete traces.
type DeleteStore interface {
	Store

	// Delete deletes traces given their trace IDs.
	Delete(...ID) error
}

// A RecentStore wraps another store and deletes old traces after a
// specified amount of time.
type RecentStore struct {
	// MinEvictAge is the minimum age of a trace before it is evicted.
	MinEvictAge time.Duration

	// DeleteStore is the underlying store that spans are saved to and
	// deleted from.
	DeleteStore

	// Debug is whether to log debug messages.
	Debug bool

	// created maps trace ID to the UnixNano time it was first seen.
	created map[ID]int64

	// lastEvicted is the last time the eviction process was run.
	lastEvicted time.Time

	mu sync.Mutex // mu guards created and lastEvicted
}

// Collect calls the underlying store's Collect and records the time
// that this trace was first seen.
func (rs *RecentStore) Collect(id SpanID, anns ...Annotation) error {
	rs.mu.Lock()
	if rs.created == nil {
		rs.created = map[ID]int64{}
	}
	if _, present := rs.created[id.Trace]; !present {
		rs.created[id.Trace] = time.Now().UnixNano()
	}
	if time.Since(rs.lastEvicted) > rs.MinEvictAge {
		rs.evictBefore(time.Now().Add(-1 * rs.MinEvictAge))
	}
	rs.mu.Unlock()

	return rs.DeleteStore.Collect(id, anns...)
}

// evictBefore evicts traces that were created before t. The rs.mu lock
// must be held while calling evictBefore.
func (rs *RecentStore) evictBefore(t time.Time) {
	evictStart := time.Now()
	rs.lastEvicted = evictStart
	tnano := t.UnixNano()
	var toEvict []ID
	for id, ct := range rs.created {
		if ct < tnano {
			toEvict = append(toEvict, id)
			delete(rs.created, id)
		}
	}
	if len(toEvict) == 0 {
		return
	}

	if rs.Debug {
		log.Printf("RecentStore: deleting %d traces created before %s (age check took %s)", len(toEvict), t, time.Since(evictStart))
	}

	// Spawn separate goroutine so we don't hold the rs.mu lock.
	go func() {
		deleteStart := time.Now()
		if err := rs.DeleteStore.Delete(toEvict...); err != nil {
			log.Printf("RecentStore: failed to delete traces: %s", err)
		}
		if rs.Debug {
			log.Printf("RecentStore: finished deleting %d traces created before %s (took %s)", len(toEvict), t, time.Since(deleteStart))
		}
	}()
}
<|endoftext|>