<commit_before>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>, Lucas Bremgartner <lucas@bremis.ch>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"strconv"
	"strings"
	"text/template"
)

type context struct {
}

func (c *context) Env() map[string]string {
	env := make(map[string]string)
	for _, i := range os.Environ() {
		sep := strings.Index(i, "=")
		env[i[0:sep]] = i[sep+1:]
	}
	return env
}

func parseTemplate(params []string) (srcPath, dstPath string, mode os.FileMode, err error) {
	length := len(params)
	if length < 1 || length > 3 {
		err = fmt.Errorf("invalid template statement")
		return
	}

	if length > 2 {
		var parsed uint64
		parsed, err = strconv.ParseUint(params[2], 0, 64)
		if err != nil {
			return
		}

		mode = os.FileMode(parsed)
	} else {
		mode = 0755
	}

	dstPath = os.ExpandEnv(params[0])
	srcPath = dstPath
	if length > 1 {
		srcPath = os.ExpandEnv(params[1])
	}

	return
}

func processTemplate(params []string, conf *config) (err error) {
	srcPath, dstPath, mode, err := parseTemplate(params)
	if err != nil {
		return err
	}

	srcPathAbs := srcPath
	if !path.IsAbs(srcPathAbs) {
		srcPathAbs = path.Join(conf.srcDir, srcPath)
	}

	dstPathAbs := dstPath
	if !path.IsAbs(dstPathAbs) {
		dstPathAbs = path.Join(conf.dstDir, dstPath)
	}

	if _, err = os.Stat(srcPathAbs); os.IsNotExist(err) {
		return fmt.Errorf("source path %s does not exist in filesystem", srcPathAbs)
	}

	if err = try(func() error { return createPath(dstPathAbs, conf.flags, mode) }); err != nil {
		return err
	}

	if err = try(func() error { return cleanPath(dstPathAbs, conf.flags) }); err != nil {
		return err
	}

	if conf.flags&flagVerbose != 0 {
		log.Printf("process template %s to %s", srcPathAbs, dstPathAbs)
	}

	t, err := template.New(srcPath).ParseFiles(srcPathAbs)
	if err != nil {
		return err
	}

	f, err := os.Create(dstPathAbs)
	if err != nil {
		return err
	}
	defer func() {
		err = f.Close()
	}()

	return try(func() error {
		return t.Execute(f, &context{})
	})
}
<commit_msg>Don't create a blank template when parsing templates<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>, Lucas Bremgartner <lucas@bremis.ch>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"strconv"
	"strings"
	"text/template"
)

type context struct {
}

func (c *context) Env() map[string]string {
	env := make(map[string]string)
	for _, i := range os.Environ() {
		sep := strings.Index(i, "=")
		env[i[0:sep]] = i[sep+1:]
	}
	return env
}

func parseTemplate(params []string) (srcPath, dstPath string, mode os.FileMode, err error) {
	length := len(params)
	if length < 1 || length > 3 {
		err = fmt.Errorf("invalid template statement")
		return
	}

	if length > 2 {
		var parsed uint64
		parsed, err = strconv.ParseUint(params[2], 0, 64)
		if err != nil {
			return
		}

		mode = os.FileMode(parsed)
	} else {
		mode = 0755
	}

	dstPath = os.ExpandEnv(params[0])
	srcPath = dstPath
	if length > 1 {
		srcPath = os.ExpandEnv(params[1])
	}

	return
}

func processTemplate(params []string, conf *config) (err error) {
	srcPath, dstPath, mode, err := parseTemplate(params)
	if err != nil {
		return err
	}

	srcPathAbs := srcPath
	if !path.IsAbs(srcPathAbs) {
		srcPathAbs = path.Join(conf.srcDir, srcPath)
	}

	dstPathAbs := dstPath
	if !path.IsAbs(dstPathAbs) {
		dstPathAbs = path.Join(conf.dstDir, dstPath)
	}

	if _, err = os.Stat(srcPathAbs); os.IsNotExist(err) {
		return fmt.Errorf("source path %s does not exist in filesystem", srcPathAbs)
	}

	if err = try(func() error { return createPath(dstPathAbs, conf.flags, mode) }); err != nil {
		return err
	}

	if err = try(func() error { return cleanPath(dstPathAbs, conf.flags) }); err != nil {
		return err
	}

	if conf.flags&flagVerbose != 0 {
		log.Printf("process template %s to %s", srcPathAbs, dstPathAbs)
	}

	t, err := template.ParseFiles(srcPathAbs)
	if err != nil {
		return err
	}

	f, err := os.Create(dstPathAbs)
	if err != nil {
		return err
	}
	defer func() {
		err = f.Close()
	}()

	return try(func() error {
		return t.Execute(f, &context{})
	})
}
<|endoftext|>


<commit_before>package cloudformation

import (
	"encoding/json"
	"fmt"
)

// NewTemplate returns a new empty Template initialized with some
// default values.
func NewTemplate() *Template {
	return &Template{
		AWSTemplateFormatVersion: "2010-09-09",
		Mappings:                 map[string]Mapping{},
		Parameters:               map[string]Parameter{},
		Resources:                map[string]Resource{},
		Outputs:                  map[string]Output{},
		Conditions:               map[string]interface{}{},
	}
}

// Template represents a cloudformation template.
type Template struct {
	AWSTemplateFormatVersion string                 `json:",omitempty"`
	Description              string                 `json:",omitempty"`
	Mappings                 map[string]Mapping     `json:",omitempty"`
	Parameters               map[string]Parameter   `json:",omitempty"`
	Resources                map[string]Resource    `json:",omitempty"`
	Outputs                  map[string]Output      `json:",omitempty"`
	Conditions               map[string]interface{} `json:",omitempty"`
}

// AddResource adds the resource to the template as name, displacing
// any resource with the same name that already exists.
func (t *Template) AddResource(name string, resource ResourceProperties) {
	t.Resources[name] = Resource{Properties: resource}
}

// Mapping matches a key to a corresponding set of named values. For example,
// if you want to set values based on a region, you can create a mapping that
// uses the region name as a key and contains the values you want to specify
// for each specific region. You use the Fn::FindInMap intrinsic function to
// retrieve values in a map.
//
// See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html
type Mapping map[string]map[string]string

// Parameter represents a parameter to the template.
//
// You can use the optional Parameters section to pass values into your
// template when you create a stack. With parameters, you can create templates
// that are customized each time you create a stack. Each parameter must
// contain a value when you create a stack. You can specify a default value to
// make the parameter optional.
//
// See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
type Parameter struct {
	Type                  string       `json:",omitempty"`
	Default               string       `json:",omitempty"`
	NoEcho                *BoolExpr    `json:",omitempty"`
	AllowedValues         []string     `json:",omitempty"`
	AllowedPattern        string       `json:",omitempty"`
	MinLength             *IntegerExpr `json:",omitempty"`
	MaxLength             *IntegerExpr `json:",omitempty"`
	MinValue              *IntegerExpr `json:",omitempty"`
	MaxValue              *IntegerExpr `json:",omitempty"`
	Description           string       `json:",omitempty"`
	ConstraintDescription string       `json:",omitempty"`
}

// Output represents a template output
//
// The optional Outputs section declares output values that you want to view from the
// AWS CloudFormation console or that you want to return in response to describe stack calls.
// For example, you can output the Amazon S3 bucket name for a stack so that you can easily find it.
//
// See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
type Output struct {
	Description string      `json:",omitempty"`
	Value       interface{} `json:",omitempty"`
}

// ResourceProperties is an interface that is implemented by resource objects.
type ResourceProperties interface {
	ResourceType() string
}

// Resource represents a resource in a cloudformation template. It contains resource
// metadata and, in Properties, a struct that implements ResourceProperties which
// contains the properties of the resource.
type Resource struct {
	DependsOn  []string
	Properties ResourceProperties
}

// MarshalJSON returns a JSON representation of the object
func (r Resource) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type       string
		DependsOn  []string `json:",omitempty"`
		Properties ResourceProperties
	}{
		Type:       r.Properties.ResourceType(),
		DependsOn:  r.DependsOn,
		Properties: r.Properties,
	})
}

// UnmarshalJSON sets the object from the provided JSON representation
func (r *Resource) UnmarshalJSON(buf []byte) error {
	m := map[string]interface{}{}
	if err := json.Unmarshal(buf, &m); err != nil {
		return err
	}

	typeName := m["Type"].(string)
	r.DependsOn, _ = m["DependsOn"].([]string)

	r.Properties = NewResourceByType(typeName)
	if r.Properties == nil {
		return fmt.Errorf("unknown resource type: %s", typeName)
	}

	propertiesBuf, err := json.Marshal(m["Properties"])
	if err != nil {
		return err
	}
	if err := json.Unmarshal(propertiesBuf, r.Properties); err != nil {
		return err
	}
	return nil
}
<commit_msg>Add `Metadata` field to Resource type<commit_after>package cloudformation

import (
	"encoding/json"
	"fmt"
)

// NewTemplate returns a new empty Template initialized with some
// default values.
func NewTemplate() *Template {
	return &Template{
		AWSTemplateFormatVersion: "2010-09-09",
		Mappings:                 map[string]Mapping{},
		Parameters:               map[string]Parameter{},
		Resources:                map[string]Resource{},
		Outputs:                  map[string]Output{},
		Conditions:               map[string]interface{}{},
	}
}

// Template represents a cloudformation template.
type Template struct {
	AWSTemplateFormatVersion string                 `json:",omitempty"`
	Description              string                 `json:",omitempty"`
	Mappings                 map[string]Mapping     `json:",omitempty"`
	Parameters               map[string]Parameter   `json:",omitempty"`
	Resources                map[string]Resource    `json:",omitempty"`
	Outputs                  map[string]Output      `json:",omitempty"`
	Conditions               map[string]interface{} `json:",omitempty"`
}

// AddResource adds the resource to the template as name, displacing
// any resource with the same name that already exists.
func (t *Template) AddResource(name string, resource ResourceProperties) {
	t.Resources[name] = Resource{Properties: resource}
}

// Mapping matches a key to a corresponding set of named values. For example,
// if you want to set values based on a region, you can create a mapping that
// uses the region name as a key and contains the values you want to specify
// for each specific region. You use the Fn::FindInMap intrinsic function to
// retrieve values in a map.
//
// See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html
type Mapping map[string]map[string]string

// Parameter represents a parameter to the template.
//
// You can use the optional Parameters section to pass values into your
// template when you create a stack. With parameters, you can create templates
// that are customized each time you create a stack. Each parameter must
// contain a value when you create a stack. You can specify a default value to
// make the parameter optional.
//
// See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
type Parameter struct {
	Type                  string       `json:",omitempty"`
	Default               string       `json:",omitempty"`
	NoEcho                *BoolExpr    `json:",omitempty"`
	AllowedValues         []string     `json:",omitempty"`
	AllowedPattern        string       `json:",omitempty"`
	MinLength             *IntegerExpr `json:",omitempty"`
	MaxLength             *IntegerExpr `json:",omitempty"`
	MinValue              *IntegerExpr `json:",omitempty"`
	MaxValue              *IntegerExpr `json:",omitempty"`
	Description           string       `json:",omitempty"`
	ConstraintDescription string       `json:",omitempty"`
}

// Output represents a template output
//
// The optional Outputs section declares output values that you want to view from the
// AWS CloudFormation console or that you want to return in response to describe stack calls.
// For example, you can output the Amazon S3 bucket name for a stack so that you can easily find it.
//
// See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
type Output struct {
	Description string      `json:",omitempty"`
	Value       interface{} `json:",omitempty"`
}

// ResourceProperties is an interface that is implemented by resource objects.
type ResourceProperties interface {
	ResourceType() string
}

// Resource represents a resource in a cloudformation template. It contains resource
// metadata and, in Properties, a struct that implements ResourceProperties which
// contains the properties of the resource.
type Resource struct {
	DependsOn  []string
	Metadata   map[string]interface{}
	Properties ResourceProperties
}

// MarshalJSON returns a JSON representation of the object
func (r Resource) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type       string
		DependsOn  []string               `json:",omitempty"`
		Metadata   map[string]interface{} `json:",omitempty"`
		Properties ResourceProperties
	}{
		Type:       r.Properties.ResourceType(),
		DependsOn:  r.DependsOn,
		Metadata:   r.Metadata,
		Properties: r.Properties,
	})
}

// UnmarshalJSON sets the object from the provided JSON representation
func (r *Resource) UnmarshalJSON(buf []byte) error {
	m := map[string]interface{}{}
	if err := json.Unmarshal(buf, &m); err != nil {
		return err
	}

	typeName := m["Type"].(string)
	r.DependsOn, _ = m["DependsOn"].([]string)
	r.Metadata, _ = m["Metadata"].(map[string]interface{})
	r.Properties = NewResourceByType(typeName)
	if r.Properties == nil {
		return fmt.Errorf("unknown resource type: %s", typeName)
	}

	propertiesBuf, err := json.Marshal(m["Properties"])
	if err != nil {
		return err
	}
	if err := json.Unmarshal(propertiesBuf, r.Properties); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"text/template"
)

var fileTemplate = template.Must(template.New("file").Parse(`// Generated by protoc-gen-gokit DO NOT EDIT.
package {{.Package}}

import (
	"net/http"
	"errors"
	"strings"
	"io/ioutil"

	"golang.org/x/net/context"

	kithttp "github.com/go-kit/kit/transport/http"
	"github.com/go-kit/kit/endpoint"

	"github.com/AmandaCameron/protoc-gen-gokit/runtime"

{{ range $k, $pkg := .Imports }}
	. "{{ $pkg }}"{{ end }}
)

{{ range $svc := .Services }}

// MakeMux_{{$svc.GoName}} creates a server mux for the {{ $svc.GoName }} service,
// using the passed kithttp.Server as a template for the parameters of the endpoints.
func MakeMux_{{ $svc.GoName }}(cli {{ $svc.GoName }}Client, mw endpoint.Middleware, responseEncoder kithttp.EncodeResponseFunc, options ...kithttp.ServerOption) (http.Handler, error) {
	ret := runtime.NewMux()

{{ range $endp := $svc.Methods }}
	ret.AddEndpoint("{{ $endp.Method }}", "{{ $endp.Path }}", kithttp.NewServer(
	 context.Background(),
	 mw(MakeEndpoint_{{ $svc.GoName }}_{{ $endp.GoName }}(cli)),
	 Decode_{{ $svc.GoName }}_{{ $endp.GoName }},
	 responseEncoder, options...)){{end}}

	return ret, nil
}

{{ range $method := $svc.Methods }}
// Decode_{{ $svc.GoName }}_{{ $method.GoName }} decodes an http.Request into a {{ $method.GoInputType }}.
func Decode_{{ $svc.GoName }}_{{ $method.GoName }}(req *http.Request) (interface{}, error) {
	var ret {{ $method.GoInputType }}

	qry := req.URL.Query()
	_ = qry

{{ if $method.Body }}
	if buff, err := ioutil.ReadAll(req.Body); err == nil {
		if err := runtime.Decode(&ret.{{ $method.GoBodyName }}, string(buff)); err != nil {
			return nil, err
		}
	}
{{- end }}

{{- range $i, $field := $method.Input.Fields }}
	if val := qry.Get("{{ $field.ProtoName }}"); val != "" {
		if err := runtime.Decode(&ret.{{ $field.GoName }}, val); err != nil {
			return nil, err
		}
	}
{{- end }}

	parts := strings.Split(req.URL.Path, "/")
	if len(parts) < {{ len $method.PathArgs }} {
		return nil, errors.New("Missing Parameters.")
	}

{{- range $i, $field := $method.PathArgs }}
	{{- if len $field.GoName }}
	if err := runtime.Decode(&ret.{{ $field.GoName}}, parts[{{ $i }}]); err != nil {
		return nil, err
	}
	{{- end }}
{{- end }}

	return &ret, nil
}

// MakeEndpoint_{{ $svc.GoName }}_{{ $method.GoName }} creates an endpoint function for Go-kit
// that runs the specified service / endpoint on the specified grpc endpoint.
func MakeEndpoint_{{ $svc.GoName }}_{{ $method.GoName }}(cli {{ $svc.GoName }}Client) endpoint.Endpoint {
	endp := func (ctx context.Context, inp interface{}) (interface{}, error) {
		return cli.{{ $method.GoName }}(ctx, inp.(*{{ $method.GoInputType }}))
	}

	return endp
}
{{ end -}}
{{ end }}`))
<commit_msg>Fix template to not cause errors if ioutil is unused.<commit_after>package main

import (
	"text/template"
)

var fileTemplate = template.Must(template.New("file").Parse(`// Generated by protoc-gen-gokit DO NOT EDIT.
package {{.Package}}

import (
	"net/http"
	"errors"
	"strings"
	"io/ioutil"

	"golang.org/x/net/context"

	kithttp "github.com/go-kit/kit/transport/http"
	"github.com/go-kit/kit/endpoint"

	"github.com/AmandaCameron/protoc-gen-gokit/runtime"

{{ range $k, $pkg := .Imports }}
	. "{{ $pkg }}"{{ end }}
)

var _ = ioutil.ReadAll

{{ range $svc := .Services }}

// MakeMux_{{$svc.GoName}} creates a server mux for the {{ $svc.GoName }} service,
// using the passed kithttp.Server as a template for the parameters of the endpoints.
func MakeMux_{{ $svc.GoName }}(cli {{ $svc.GoName }}Client, mw endpoint.Middleware, responseEncoder kithttp.EncodeResponseFunc, options ...kithttp.ServerOption) (http.Handler, error) {
	ret := runtime.NewMux()

{{ range $endp := $svc.Methods }}
	ret.AddEndpoint("{{ $endp.Method }}", "{{ $endp.Path }}", kithttp.NewServer(
	 context.Background(),
	 mw(MakeEndpoint_{{ $svc.GoName }}_{{ $endp.GoName }}(cli)),
	 Decode_{{ $svc.GoName }}_{{ $endp.GoName }},
	 responseEncoder, options...)){{end}}

	return ret, nil
}

{{ range $method := $svc.Methods }}
// Decode_{{ $svc.GoName }}_{{ $method.GoName }} decodes an http.Request into a {{ $method.GoInputType }}.
func Decode_{{ $svc.GoName }}_{{ $method.GoName }}(req *http.Request) (interface{}, error) {
	var ret {{ $method.GoInputType }}

	qry := req.URL.Query()
	_ = qry

{{ if $method.Body }}
	if buff, err := ioutil.ReadAll(req.Body); err == nil {
		if err := runtime.Decode(&ret.{{ $method.GoBodyName }}, string(buff)); err != nil {
			return nil, err
		}
	}
{{- end }}

{{- range $i, $field := $method.Input.Fields }}
	if val := qry.Get("{{ $field.ProtoName }}"); val != "" {
		if err := runtime.Decode(&ret.{{ $field.GoName }}, val); err != nil {
			return nil, err
		}
	}
{{- end }}

	parts := strings.Split(req.URL.Path, "/")
	if len(parts) < {{ len $method.PathArgs }} {
		return nil, errors.New("Missing Parameters.")
	}

{{- range $i, $field := $method.PathArgs }}
	{{- if len $field.GoName }}
	if err := runtime.Decode(&ret.{{ $field.GoName}}, parts[{{ $i }}]); err != nil {
		return nil, err
	}
	{{- end }}
{{- end }}

	return &ret, nil
}

// MakeEndpoint_{{ $svc.GoName }}_{{ $method.GoName }} creates an endpoint function for Go-kit
// that runs the specified service / endpoint on the specified grpc endpoint.
func MakeEndpoint_{{ $svc.GoName }}_{{ $method.GoName }}(cli {{ $svc.GoName }}Client) endpoint.Endpoint {
	endp := func (ctx context.Context, inp interface{}) (interface{}, error) {
		return cli.{{ $method.GoName }}(ctx, inp.(*{{ $method.GoInputType }}))
	}

	return endp
}
{{ end -}}
{{ end }}`))
<|endoftext|>


<commit_before>package tagstash

import (
	"errors"
	"sort"
)

// Entry represents a value-tag associaction.
type Entry struct {

	// Value that a tag belongs to.
	Value string

	// Tag associated with a value.
	Tag string

	// TagIndex marks how strong strong a tag describes a value.
	TagIndex int

	requestTagMatch, requestIndexDelta int
}

// TagLookup when implemented by a storage, can return all tags associated with a value.
type TagLookup interface {
	GetTags(string) ([]string, error)
}

// Storage implementations store value-tag associations.
type Storage interface {

	// Get returns all entries whose tag is listed in the arguments.
	Get([]string) ([]*Entry, error)

	// Set stores a value-tag association. Implementations must make sure that the value-tag combinations
	// are unique.
	Set(*Entry) error

	// Remove deletes a single value-tag association.
	Remove(*Entry) error

	// Delete deletes all associations with the provided tag.
	Delete(string) error

	// Close releases any resources taken by the storage implementation.
	Close()
}

// StorageOptions are used by the default storage implementation.
type StorageOptions struct {

	// DriverName specifies which data base driver to use. Currently supported: postgres, sqlite3. The
	// default value is sqlite3.
	DriverName string

	// DataSourceName specifies the data source for the storage. In case of postgresql, it is the postgresql
	// connection string, while in case of sqlite3, it is a path to a new or existing file. When not
	// specified and the driver is sqlite3, ./data.sqlite will be used.
	//
	// When PostgreSQL is used, please refer to the driver implementation's documentation for configuration
	// details: https://github.com/lib/pq.
	DataSourceName string
}

// CacheOptions are used by the default cache implementation.
type CacheOptions struct {

	// CacheSize defines the maximum memory usage of the cache. Defaults to 1G.
	CacheSize int

	// ExpectedItemSize provides a hint for the cache about the expected median size of the stored values.
	//
	// This option exists only for optimization, there is no good rule of thumb. Too high values will result
	// in worse memory utilization, while too low values may affect the individual lookup performance.
	// Generally, it is better to err for the smaller values.
	ExpectedItemSize int
}

// Options are used to initialization tagstash.
type Options struct {

	// Custom storage implementation. By default, a builtin storage is used.
	Storage Storage

	// Custom cache implementation. By default, a builtin cache is used.
	Cache Storage

	// CacheOptions define options for the default persistent storage implementation when not replaced by a custom
	// storage.
	StorageOptions StorageOptions

	// CacheOptions define options for the default cache implementation when not replaced by a custom
	// cache.
	CacheOptions CacheOptions
}

type entrySort struct {
	entries []*Entry
}

// TagStash is used to store tags associated with values and return the best matching value for a set of query
// tags.
type TagStash struct {
	cache, storage Storage
}

// ErrNotSupported is returned when a feature is not supported by the current implementation. E.g. the storage
// doesn't support lookup by value.
var ErrNotSupported = errors.New("not supported")

func (s entrySort) Len() int      { return len(s.entries) }
func (s entrySort) Swap(i, j int) { s.entries[i], s.entries[j] = s.entries[j], s.entries[i] }

func (s entrySort) Less(i, j int) bool {
	left, right := s.entries[i], s.entries[j]

	if left.requestTagMatch == right.requestTagMatch {
		return left.requestIndexDelta < right.requestIndexDelta
	}

	return left.requestTagMatch > right.requestTagMatch
}

// New creates and initializes a tagstash instance.
func New(o Options) (*TagStash, error) {
	if o.Storage == nil {
		s, err := newStorage(o.StorageOptions)
		if err != nil {
			return nil, err
		}

		o.Storage = s
	}

	if o.Cache == nil {
		o.Cache = newCache(o.CacheOptions)
	}

	return &TagStash{
		storage: o.Storage,
		cache:   o.Cache,
	}, nil
}

func setRequestIndex(tags []string, e []*Entry) (notFound []string) {
	for i, t := range tags {
		var found bool
		for _, ei := range e {
			if ei.Tag == t {
				d := i - ei.TagIndex
				if d < 0 {
					d = 0 - d
				}

				ei.requestIndexDelta = d
				found = true
			}
		}

		if !found {
			notFound = append(notFound, t)
		}
	}

	return notFound
}

func uniqueValues(e []*Entry) []*Entry {
	m := make(map[string]*Entry)
	u := make([]*Entry, 0, len(e))
	for _, ei := range e {
		if eim, ok := m[ei.Value]; ok {
			eim.requestTagMatch++
			eim.requestIndexDelta += ei.requestIndexDelta
			continue
		}

		ei.requestTagMatch = 1
		m[ei.Value] = ei
		u = append(u, ei)
	}

	return u
}

func mapEntries(e []*Entry) []string {
	v := make([]string, 0, len(e))
	for _, ei := range e {
		v = append(v, ei.Value)
	}

	return v
}

func (t *TagStash) getAll(tags []string) ([]*Entry, error) {
	entries, err := t.cache.Get(tags)
	if err != nil {
		return nil, err
	}

	notCached := setRequestIndex(tags, entries)

	stored, err := t.storage.Get(notCached)
	if err != nil {
		return nil, err
	}

	for _, e := range stored {
		if err := t.cache.Set(e); err != nil {
			return nil, err
		}
	}

	setRequestIndex(tags, stored)
	entries = append(entries, stored...)

	entries = uniqueValues(entries)
	sort.Sort(entrySort{entries})
	return entries, nil
}

// Get returns the best matching value for a set of tags. When there are overlapping tags and values, it
// prioritizes first those values that match more tags from the arguments. When there are matches with the same
// number of matching tags, it prioritizes those that whose tag order matches the closer the order of the tags
// in the arguments. The tag order means the order of tags at the time of the definition (Set()).
func (t *TagStash) Get(tags ...string) (string, error) {
	entries, err := t.getAll(tags)
	if err != nil {
		return "", err
	}

	if len(entries) == 0 {
		return "", nil
	}

	v := mapEntries(entries[:1])
	return v[0], nil
}

// GetAll returns all matches for a set of tags, sorted by the same rules that are used for prioritization when
// calling Get().
func (t *TagStash) GetAll(tags ...string) ([]string, error) {
	entries, err := t.getAll(tags)
	if err != nil {
		return nil, err
	}

	return mapEntries(entries), nil
}

// GetTags returns the tags associated with the provided value or ErrNotSupported if the storage implementation
// doesn't support this query.
func (t *TagStash) GetTags(value string) ([]string, error) {
	if tl, ok := t.storage.(TagLookup); ok {
		return tl.GetTags(value)
	}

	return nil, ErrNotSupported
}

// Set stores tags associated with a value. The order of the tags is taken into account when there are
// overlapping matches during retrieval.
func (t *TagStash) Set(value string, tags ...string) error {
	for i, ti := range tags {
		e := &Entry{
			Value:    value,
			Tag:      ti,
			TagIndex: i,
		}

		if err := t.storage.Set(e); err != nil {
			return err
		}

		if err := t.cache.Set(e); err != nil {
			return err
		}
	}

	return nil
}

// Remove deletes a value-tag association.
func (t *TagStash) Remove(value string, tag string) error {
	e := &Entry{Value: value, Tag: tag}

	if err := t.cache.Remove(e); err != nil {
		return err
	}

	if err := t.storage.Remove(e); err != nil {
		return err
	}

	return nil
}

// Delete deletes all associations of a tag.
func (t *TagStash) Delete(tag string) error {
	if err := t.cache.Delete(tag); err != nil {
		return err
	}

	if err := t.storage.Delete(tag); err != nil {
		return err
	}

	return nil
}

// Close releases all resources.
func (t *TagStash) Close() {
	t.cache.Close()
	t.storage.Close()
}
<commit_msg>find first instead of sort when not required<commit_after>package tagstash

import (
	"errors"
	"sort"
)

// Entry represents a value-tag associaction.
type Entry struct {

	// Value that a tag belongs to.
	Value string

	// Tag associated with a value.
	Tag string

	// TagIndex marks how strong strong a tag describes a value.
	TagIndex int

	requestTagMatch, requestIndexDelta int
}

// TagLookup when implemented by a storage, can return all tags associated with a value.
type TagLookup interface {
	GetTags(string) ([]string, error)
}

// Storage implementations store value-tag associations.
type Storage interface {

	// Get returns all entries whose tag is listed in the arguments.
	Get([]string) ([]*Entry, error)

	// Set stores a value-tag association. Implementations must make sure that the value-tag combinations
	// are unique.
	Set(*Entry) error

	// Remove deletes a single value-tag association.
	Remove(*Entry) error

	// Delete deletes all associations with the provided tag.
	Delete(string) error

	// Close releases any resources taken by the storage implementation.
	Close()
}

// StorageOptions are used by the default storage implementation.
type StorageOptions struct {

	// DriverName specifies which data base driver to use. Currently supported: postgres, sqlite3. The
	// default value is sqlite3.
	DriverName string

	// DataSourceName specifies the data source for the storage. In case of postgresql, it is the postgresql
	// connection string, while in case of sqlite3, it is a path to a new or existing file. When not
	// specified and the driver is sqlite3, ./data.sqlite will be used.
	//
	// When PostgreSQL is used, please refer to the driver implementation's documentation for configuration
	// details: https://github.com/lib/pq.
	DataSourceName string
}

// CacheOptions are used by the default cache implementation.
type CacheOptions struct {

	// CacheSize defines the maximum memory usage of the cache. Defaults to 1G.
	CacheSize int

	// ExpectedItemSize provides a hint for the cache about the expected median size of the stored values.
	//
	// This option exists only for optimization, there is no good rule of thumb. Too high values will result
	// in worse memory utilization, while too low values may affect the individual lookup performance.
	// Generally, it is better to err for the smaller values.
	ExpectedItemSize int
}

// Options are used to initialization tagstash.
type Options struct {

	// Custom storage implementation. By default, a builtin storage is used.
	Storage Storage

	// Custom cache implementation. By default, a builtin cache is used.
	Cache Storage

	// CacheOptions define options for the default persistent storage implementation when not replaced by a custom
	// storage.
	StorageOptions StorageOptions

	// CacheOptions define options for the default cache implementation when not replaced by a custom
	// cache.
	CacheOptions CacheOptions
}

type entrySort struct {
	entries []*Entry
}

// TagStash is used to store tags associated with values and return the best matching value for a set of query
// tags.
type TagStash struct {
	cache, storage Storage
}

// ErrNotSupported is returned when a feature is not supported by the current implementation. E.g. the storage
// doesn't support lookup by value.
var ErrNotSupported = errors.New("not supported")

func less(left, right *Entry) bool {
	if left.requestTagMatch == right.requestTagMatch {
		return left.requestIndexDelta < right.requestIndexDelta
	}

	return left.requestTagMatch > right.requestTagMatch
}

func (s entrySort) Len() int      { return len(s.entries) }
func (s entrySort) Swap(i, j int) { s.entries[i], s.entries[j] = s.entries[j], s.entries[i] }

func (s entrySort) Less(i, j int) bool {
	left, right := s.entries[i], s.entries[j]
	return less(left, right)
}

func (s entrySort) First() *Entry {
	l := len(s.entries)
	if l == 0 {
		return nil
	}

	first := s.entries[0]
	for _, e := range s.entries[1:] {
		if less(e, first) {
			first = e
		}
	}

	return first
}

// New creates and initializes a tagstash instance.
func New(o Options) (*TagStash, error) {
	if o.Storage == nil {
		s, err := newStorage(o.StorageOptions)
		if err != nil {
			return nil, err
		}

		o.Storage = s
	}

	if o.Cache == nil {
		o.Cache = newCache(o.CacheOptions)
	}

	return &TagStash{
		storage: o.Storage,
		cache:   o.Cache,
	}, nil
}

func setRequestIndex(tags []string, e []*Entry) (notFound []string) {
	for i, t := range tags {
		var found bool
		for _, ei := range e {
			if ei.Tag == t {
				d := i - ei.TagIndex
				if d < 0 {
					d = 0 - d
				}

				ei.requestIndexDelta = d
				found = true
			}
		}

		if !found {
			notFound = append(notFound, t)
		}
	}

	return notFound
}

func uniqueValues(e []*Entry) []*Entry {
	m := make(map[string]*Entry)
	u := make([]*Entry, 0, len(e))
	for _, ei := range e {
		if eim, ok := m[ei.Value]; ok {
			eim.requestTagMatch++
			eim.requestIndexDelta += ei.requestIndexDelta
			continue
		}

		ei.requestTagMatch = 1
		m[ei.Value] = ei
		u = append(u, ei)
	}

	return u
}

func mapEntries(e ...*Entry) []string {
	v := make([]string, 0, len(e))
	for _, ei := range e {
		v = append(v, ei.Value)
	}

	return v
}

func (t *TagStash) getAll(tags []string) ([]*Entry, error) {
	entries, err := t.cache.Get(tags)
	if err != nil {
		return nil, err
	}

	notCached := setRequestIndex(tags, entries)

	stored, err := t.storage.Get(notCached)
	if err != nil {
		return nil, err
	}

	for _, e := range stored {
		if err := t.cache.Set(e); err != nil {
			return nil, err
		}
	}

	setRequestIndex(tags, stored)
	entries = append(entries, stored...)

	return uniqueValues(entries), nil
}

// Get returns the best matching value for a set of tags. When there are overlapping tags and values, it
// prioritizes first those values that match more tags from the arguments. When there are matches with the same
// number of matching tags, it prioritizes those that whose tag order matches the closer the order of the tags
// in the arguments. The tag order means the order of tags at the time of the definition (Set()).
func (t *TagStash) Get(tags ...string) (string, error) {
	entries, err := t.getAll(tags)
	if err != nil {
		return "", err
	}

	if len(entries) == 0 {
		return "", nil
	}

	e := entrySort{entries}.First()
	return mapEntries(e)[0], nil
}

// GetAll returns all matches for a set of tags, sorted by the same rules that are used for prioritization when
// calling Get().
func (t *TagStash) GetAll(tags ...string) ([]string, error) {
	entries, err := t.getAll(tags)
	if err != nil {
		return nil, err
	}

	sort.Sort(entrySort{entries})
	return mapEntries(entries...), nil
}

// GetTags returns the tags associated with the provided value or ErrNotSupported if the storage implementation
// doesn't support this query.
func (t *TagStash) GetTags(value string) ([]string, error) {
	if tl, ok := t.storage.(TagLookup); ok {
		return tl.GetTags(value)
	}

	return nil, ErrNotSupported
}

// Set stores tags associated with a value. The order of the tags is taken into account when there are
// overlapping matches during retrieval.
func (t *TagStash) Set(value string, tags ...string) error {
	for i, ti := range tags {
		e := &Entry{
			Value:    value,
			Tag:      ti,
			TagIndex: i,
		}

		if err := t.storage.Set(e); err != nil {
			return err
		}

		if err := t.cache.Set(e); err != nil {
			return err
		}
	}

	return nil
}

// Remove deletes a value-tag association.
func (t *TagStash) Remove(value string, tag string) error {
	e := &Entry{Value: value, Tag: tag}

	if err := t.cache.Remove(e); err != nil {
		return err
	}

	if err := t.storage.Remove(e); err != nil {
		return err
	}

	return nil
}

// Delete deletes all associations of a tag.
func (t *TagStash) Delete(tag string) error {
	if err := t.cache.Delete(tag); err != nil {
		return err
	}

	if err := t.storage.Delete(tag); err != nil {
		return err
	}

	return nil
}

// Close releases all resources.
func (t *TagStash) Close() {
	t.cache.Close()
	t.storage.Close()
}
<|endoftext|>


<commit_before>package tak

import (
	"errors"
	"sort"
)

type MoveType byte

const (
	Pass MoveType = 1 + iota
	PlaceFlat
	PlaceStanding
	PlaceCapstone
	SlideLeft
	SlideRight
	SlideUp
	SlideDown
)

const TypeMask MoveType = 0xf

type Move struct {
	X, Y   int8
	Type   MoveType
	Slides Slides
}

func (m *Move) Equal(rhs *Move) bool {
	if m.X != rhs.X || m.Y != rhs.Y {
		return false
	}
	if m.Type != rhs.Type {
		return false
	}
	if !m.IsSlide() {
		return true
	}
	if m.Slides != rhs.Slides {
		return false
	}
	return true
}

func (m *Move) IsSlide() bool {
	return m.Type >= SlideLeft
}

func (m *Move) Dest() (int8, int8) {
	switch m.Type {
	case PlaceFlat, PlaceStanding, PlaceCapstone:
		return m.X, m.Y
	case SlideLeft:
		return m.X - int8(m.Slides.Len()), m.Y
	case SlideRight:
		return m.X + int8(m.Slides.Len()), m.Y
	case SlideUp:
		return m.X, m.Y + int8(m.Slides.Len())
	case SlideDown:
		return m.X, m.Y - int8(m.Slides.Len())
	}
	panic("bad type")
}

var (
	ErrOccupied       = errors.New("position is occupied")
	ErrIllegalSlide   = errors.New("illegal slide")
	ErrNoCapstone     = errors.New("capstone has already been played")
	ErrIllegalOpening = errors.New("illegal opening move")
)

func (p *Position) Move(m *Move) (*Position, error) {
	return p.MovePreallocated(m, nil)
}

func (p *Position) MovePreallocated(m *Move, next *Position) (*Position, error) {
	if next == nil {
		next = alloc(p)
	} else {
		copyPosition(p, next)
	}
	next.move++
	var place Piece
	var dx, dy int8
	switch m.Type {
	case Pass:
		next.analyze()
		return next, nil
	case PlaceFlat:
		place = MakePiece(p.ToMove(), Flat)
	case PlaceStanding:
		place = MakePiece(p.ToMove(), Standing)
	case PlaceCapstone:
		place = MakePiece(p.ToMove(), Capstone)
	case SlideLeft:
		dx = -1
	case SlideRight:
		dx = 1
	case SlideUp:
		dy = 1
	case SlideDown:
		dy = -1
	default:
		return nil, errors.New("invalid move type")
	}
	if p.move < 2 {
		if place.Kind() != Flat {
			return nil, ErrIllegalOpening
		}
		place = MakePiece(place.Color().Flip(), place.Kind())
	}
	i := uint(m.X + m.Y*int8(p.Size()))
	if place != 0 {
		if (p.White|p.Black)&(1<<i) != 0 {
			return nil, ErrOccupied
		}

		var stones *byte
		switch place.Kind() {
		case Capstone:
			if p.ToMove() == Black {
				stones = &next.blackCaps
			} else {
				stones = &next.whiteCaps
			}
			next.Caps |= (1 << i)
		case Standing:
			next.Standing |= (1 << i)
			fallthrough
		case Flat:
			if place.Color() == Black {
				stones = &next.blackStones
			} else {
				stones = &next.whiteStones
			}
		}
		if *stones <= 0 {
			return nil, ErrNoCapstone
		}
		*stones--
		if place.Color() == White {
			next.White |= (1 << i)
		} else {
			next.Black |= (1 << i)
		}
		next.Height[i]++
		next.analyze()
		return next, nil
	}

	ct := uint(0)
	for it, ok := m.Slides.Iterator(); ok; it, ok = it.Next() {
		c := it.Elem()
		if c == 0 {
			return nil, ErrIllegalSlide
		}
		ct += uint(c)
	}
	if ct > uint(p.cfg.Size) || ct < 1 || ct > uint(p.Height[i]) {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == White && p.White&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == Black && p.Black&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}

	top := p.Top(int(m.X), int(m.Y))
	stack := p.Stacks[i] << 1
	if top.Color() == Black {
		stack |= 1
	}

	next.Caps &= ^(1 << i)
	next.Standing &= ^(1 << i)
	if uint(next.Height[i]) == ct {
		next.White &= ^(1 << i)
		next.Black &= ^(1 << i)
	} else {
		if stack&(1<<ct) == 0 {
			next.White |= (1 << i)
			next.Black &= ^(1 << i)
		} else {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		}
	}
	next.hash ^= next.hashAt(i)
	next.Stacks[i] >>= ct
	next.Height[i] -= uint8(ct)
	next.hash ^= next.hashAt(i)

	x, y := m.X, m.Y
	for it, ok := m.Slides.Iterator(); ok; it, ok = it.Next() {
		c := uint(it.Elem())
		x += dx
		y += dy
		if x < 0 || x >= int8(next.cfg.Size) ||
			y < 0 || y >= int8(next.cfg.Size) {
			return nil, ErrIllegalSlide
		}
		if int(c) < 1 || uint(c) > ct {
			return nil, ErrIllegalSlide
		}
		i = uint(x + y*int8(p.Size()))
		switch {
		case next.Caps&(1<<i) != 0:
			return nil, ErrIllegalSlide
		case next.Standing&(1<<i) != 0:
			if ct != 1 || top.Kind() != Capstone {
				return nil, ErrIllegalSlide
			}
			next.Standing &= ^(1 << i)
		}
		next.hash ^= next.hashAt(i)
		if next.White&(1<<i) != 0 {
			next.Stacks[i] <<= 1
		} else if next.Black&(1<<i) != 0 {
			next.Stacks[i] <<= 1
			next.Stacks[i] |= 1
		}
		drop := (stack >> (ct - uint(c-1))) & ((1 << (c - 1)) - 1)
		next.Stacks[i] = next.Stacks[i]<<(c-1) | drop
		next.Height[i] += uint8(c)
		next.hash ^= next.hashAt(i)
		if stack&(1<<(ct-uint(c))) != 0 {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		} else {
			next.Black &= ^(1 << i)
			next.White |= (1 << i)
		}
		ct -= uint(c)
		if ct == 0 {
			switch top.Kind() {
			case Capstone:
				next.Caps |= (1 << i)
			case Standing:
				next.Standing |= (1 << i)
			}
		}
	}

	next.analyze()
	return next, nil
}

var slides [][]Slides

type byLen []Slides

func (b byLen) Len() int           { return len(b) }
func (b byLen) Less(i, j int) bool { return b[i].Len() < b[j].Len() }
func (b byLen) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }

func init() {
	slides = make([][]Slides, 10)
	for s := 1; s <= 8; s++ {
		slides[s] = calculateSlides(s)
	}
}

func calculateSlides(stack int) []Slides {
	var out []Slides
	for i := byte(1); i <= byte(stack); i++ {
		out = append(out, MkSlides(int(i)))
		for _, sub := range slides[stack-int(i)] {
			out = append(out, sub.Prepend(int(i)))
		}
	}
	sort.Sort(byLen(out))
	return out
}

func (p *Position) AllMoves(moves []Move) []Move {
	next := p.ToMove()
	cap := false
	if next == White {
		cap = p.whiteCaps > 0
	} else {
		cap = p.blackCaps > 0
	}

	for x := 0; x < p.cfg.Size; x++ {
		for y := 0; y < p.cfg.Size; y++ {
			i := uint(y*p.cfg.Size + x)
			if p.Height[i] == 0 {
				moves = append(moves, Move{X: int8(x), Y: int8(y), Type: PlaceFlat})
				if p.move >= 2 {
					moves = append(moves, Move{X: int8(x), Y: int8(y), Type: PlaceStanding})
					if cap {
						moves = append(moves, Move{X: int8(x), Y: int8(y), Type: PlaceCapstone})
					}
				}
				continue
			}
			if p.move < 2 {
				continue
			}
			if next == White && p.White&(1<<i) == 0 {
				continue
			} else if next == Black && p.Black&(1<<i) == 0 {
				continue
			}

			type dircnt struct {
				d MoveType
				c int
			}
			dirs := [4]dircnt{
				{SlideLeft, x},
				{SlideRight, p.cfg.Size - x - 1},
				{SlideDown, y},
				{SlideUp, p.cfg.Size - y - 1},
			}
			for _, d := range dirs {
				h := p.Height[i]
				if h > uint8(p.cfg.Size) {
					h = uint8(p.cfg.Size)
				}
				for _, s := range slides[h] {
					if s.Len() > d.c {
						break
					}
					moves = append(moves, Move{X: int8(x), Y: int8(y), Type: d.d, Slides: s})
				}
			}
		}
	}

	return moves
}
<commit_msg>don't sort<commit_after>package tak

import "errors"

type MoveType byte

const (
	Pass MoveType = 1 + iota
	PlaceFlat
	PlaceStanding
	PlaceCapstone
	SlideLeft
	SlideRight
	SlideUp
	SlideDown
)

const TypeMask MoveType = 0xf

type Move struct {
	X, Y   int8
	Type   MoveType
	Slides Slides
}

func (m *Move) Equal(rhs *Move) bool {
	if m.X != rhs.X || m.Y != rhs.Y {
		return false
	}
	if m.Type != rhs.Type {
		return false
	}
	if !m.IsSlide() {
		return true
	}
	if m.Slides != rhs.Slides {
		return false
	}
	return true
}

func (m *Move) IsSlide() bool {
	return m.Type >= SlideLeft
}

func (m *Move) Dest() (int8, int8) {
	switch m.Type {
	case PlaceFlat, PlaceStanding, PlaceCapstone:
		return m.X, m.Y
	case SlideLeft:
		return m.X - int8(m.Slides.Len()), m.Y
	case SlideRight:
		return m.X + int8(m.Slides.Len()), m.Y
	case SlideUp:
		return m.X, m.Y + int8(m.Slides.Len())
	case SlideDown:
		return m.X, m.Y - int8(m.Slides.Len())
	}
	panic("bad type")
}

var (
	ErrOccupied       = errors.New("position is occupied")
	ErrIllegalSlide   = errors.New("illegal slide")
	ErrNoCapstone     = errors.New("capstone has already been played")
	ErrIllegalOpening = errors.New("illegal opening move")
)

func (p *Position) Move(m *Move) (*Position, error) {
	return p.MovePreallocated(m, nil)
}

func (p *Position) MovePreallocated(m *Move, next *Position) (*Position, error) {
	if next == nil {
		next = alloc(p)
	} else {
		copyPosition(p, next)
	}
	next.move++
	var place Piece
	var dx, dy int8
	switch m.Type {
	case Pass:
		next.analyze()
		return next, nil
	case PlaceFlat:
		place = MakePiece(p.ToMove(), Flat)
	case PlaceStanding:
		place = MakePiece(p.ToMove(), Standing)
	case PlaceCapstone:
		place = MakePiece(p.ToMove(), Capstone)
	case SlideLeft:
		dx = -1
	case SlideRight:
		dx = 1
	case SlideUp:
		dy = 1
	case SlideDown:
		dy = -1
	default:
		return nil, errors.New("invalid move type")
	}
	if p.move < 2 {
		if place.Kind() != Flat {
			return nil, ErrIllegalOpening
		}
		place = MakePiece(place.Color().Flip(), place.Kind())
	}
	i := uint(m.X + m.Y*int8(p.Size()))
	if place != 0 {
		if (p.White|p.Black)&(1<<i) != 0 {
			return nil, ErrOccupied
		}

		var stones *byte
		switch place.Kind() {
		case Capstone:
			if p.ToMove() == Black {
				stones = &next.blackCaps
			} else {
				stones = &next.whiteCaps
			}
			next.Caps |= (1 << i)
		case Standing:
			next.Standing |= (1 << i)
			fallthrough
		case Flat:
			if place.Color() == Black {
				stones = &next.blackStones
			} else {
				stones = &next.whiteStones
			}
		}
		if *stones <= 0 {
			return nil, ErrNoCapstone
		}
		*stones--
		if place.Color() == White {
			next.White |= (1 << i)
		} else {
			next.Black |= (1 << i)
		}
		next.Height[i]++
		next.analyze()
		return next, nil
	}

	ct := uint(0)
	for it, ok := m.Slides.Iterator(); ok; it, ok = it.Next() {
		c := it.Elem()
		if c == 0 {
			return nil, ErrIllegalSlide
		}
		ct += uint(c)
	}
	if ct > uint(p.cfg.Size) || ct < 1 || ct > uint(p.Height[i]) {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == White && p.White&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}
	if p.ToMove() == Black && p.Black&(1<<i) == 0 {
		return nil, ErrIllegalSlide
	}

	top := p.Top(int(m.X), int(m.Y))
	stack := p.Stacks[i] << 1
	if top.Color() == Black {
		stack |= 1
	}

	next.Caps &= ^(1 << i)
	next.Standing &= ^(1 << i)
	if uint(next.Height[i]) == ct {
		next.White &= ^(1 << i)
		next.Black &= ^(1 << i)
	} else {
		if stack&(1<<ct) == 0 {
			next.White |= (1 << i)
			next.Black &= ^(1 << i)
		} else {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		}
	}
	next.hash ^= next.hashAt(i)
	next.Stacks[i] >>= ct
	next.Height[i] -= uint8(ct)
	next.hash ^= next.hashAt(i)

	x, y := m.X, m.Y
	for it, ok := m.Slides.Iterator(); ok; it, ok = it.Next() {
		c := uint(it.Elem())
		x += dx
		y += dy
		if x < 0 || x >= int8(next.cfg.Size) ||
			y < 0 || y >= int8(next.cfg.Size) {
			return nil, ErrIllegalSlide
		}
		if int(c) < 1 || uint(c) > ct {
			return nil, ErrIllegalSlide
		}
		i = uint(x + y*int8(p.Size()))
		switch {
		case next.Caps&(1<<i) != 0:
			return nil, ErrIllegalSlide
		case next.Standing&(1<<i) != 0:
			if ct != 1 || top.Kind() != Capstone {
				return nil, ErrIllegalSlide
			}
			next.Standing &= ^(1 << i)
		}
		next.hash ^= next.hashAt(i)
		if next.White&(1<<i) != 0 {
			next.Stacks[i] <<= 1
		} else if next.Black&(1<<i) != 0 {
			next.Stacks[i] <<= 1
			next.Stacks[i] |= 1
		}
		drop := (stack >> (ct - uint(c-1))) & ((1 << (c - 1)) - 1)
		next.Stacks[i] = next.Stacks[i]<<(c-1) | drop
		next.Height[i] += uint8(c)
		next.hash ^= next.hashAt(i)
		if stack&(1<<(ct-uint(c))) != 0 {
			next.Black |= (1 << i)
			next.White &= ^(1 << i)
		} else {
			next.Black &= ^(1 << i)
			next.White |= (1 << i)
		}
		ct -= uint(c)
		if ct == 0 {
			switch top.Kind() {
			case Capstone:
				next.Caps |= (1 << i)
			case Standing:
				next.Standing |= (1 << i)
			}
		}
	}

	next.analyze()
	return next, nil
}

var slides [][]Slides

func init() {
	slides = make([][]Slides, 10)
	for s := 1; s <= 8; s++ {
		slides[s] = calculateSlides(s)
	}
}

func calculateSlides(stack int) []Slides {
	var out []Slides
	for i := byte(1); i <= byte(stack); i++ {
		out = append(out, MkSlides(int(i)))
		for _, sub := range slides[stack-int(i)] {
			out = append(out, sub.Prepend(int(i)))
		}
	}
	return out
}

func (p *Position) AllMoves(moves []Move) []Move {
	next := p.ToMove()
	cap := false
	if next == White {
		cap = p.whiteCaps > 0
	} else {
		cap = p.blackCaps > 0
	}

	for x := 0; x < p.cfg.Size; x++ {
		for y := 0; y < p.cfg.Size; y++ {
			i := uint(y*p.cfg.Size + x)
			if p.Height[i] == 0 {
				moves = append(moves, Move{X: int8(x), Y: int8(y), Type: PlaceFlat})
				if p.move >= 2 {
					moves = append(moves, Move{X: int8(x), Y: int8(y), Type: PlaceStanding})
					if cap {
						moves = append(moves, Move{X: int8(x), Y: int8(y), Type: PlaceCapstone})
					}
				}
				continue
			}
			if p.move < 2 {
				continue
			}
			if next == White && p.White&(1<<i) == 0 {
				continue
			} else if next == Black && p.Black&(1<<i) == 0 {
				continue
			}

			type dircnt struct {
				d MoveType
				c int
			}
			dirs := [4]dircnt{
				{SlideLeft, x},
				{SlideRight, p.cfg.Size - x - 1},
				{SlideDown, y},
				{SlideUp, p.cfg.Size - y - 1},
			}
			for _, d := range dirs {
				h := p.Height[i]
				if h > uint8(p.cfg.Size) {
					h = uint8(p.cfg.Size)
				}
				mask := ^Slides((1 << (4 * uint(d.c))) - 1)
				for _, s := range slides[h] {
					if s&mask == 0 {
						moves = append(moves, Move{X: int8(x), Y: int8(y), Type: d.d, Slides: s})
					}
				}
			}
		}
	}

	return moves
}
<|endoftext|>


<commit_before>package searchers

import aw "github.com/deanishe/awgo"

type searcher = func(wf *aw.Workflow, query string) error

var SearchersByServiceId map[string]searcher = map[string]searcher{
	"ec2":                SearchEC2Instances,
	"ec2_instances":      SearchEC2Instances,
	"ec2_securityGroups": SearchEC2SecurityGroups,
	"elasticbeanstalk":   SearchElasticBeanstalkEnvironments,
}
<commit_msg>fixed beanstalk subsearcher<commit_after>package searchers

import aw "github.com/deanishe/awgo"

type searcher = func(wf *aw.Workflow, query string) error

var SearchersByServiceId map[string]searcher = map[string]searcher{
	"ec2":                           SearchEC2Instances,
	"ec2_instances":                 SearchEC2Instances,
	"ec2_securityGroups":            SearchEC2SecurityGroups,
	"elasticbeanstalk":              SearchElasticBeanstalkEnvironments,
	"elasticbeanstalk_environments": SearchElasticBeanstalkEnvironments,
}
<|endoftext|>


<commit_before>package tasks

// NOTE we do not log anything in this file! Leave it to the http handler to handle errors.

import (
	"database/sql"
	"time"
)

// TODO add done, done_by, c_time (maybe m_time?)
type Task struct {
	Key        int64     `json:"key"`
	ShortDesc  string    `json:"short_desc"`
	Done       bool      `json:"done"`
	DoneBy     string    `json:"done_by"`
	CreateTime time.Time `json:"c_time"`
	ChoreId    int64     `json:"chore_id"`
}

func setChoresNowStmt(db *sql.DB) (*sql.Stmt, error) {
	return db.Prepare("INSERT INTO tasks(chore_id, c_time) SELECT chore_id, NULL from chores where (morning = ? OR night = ?) AND ((dwm = 'd') OR (dwm = 'w' AND day = ?) OR (dwm = 'm' AND date = ?))")
}

func (h *handler) setChoresNow(t time.Time) error {
	var (
		isMorning int
		isNight   int
	)
	if t.Hour() >= 5 && t.Hour() < 17 {
		isMorning = 1
		isNight = -1
	} else {
		isMorning = -1
		isNight = 1
	}
	_, err := h.setChoresNowStmt.Exec(isMorning, isNight, t.Weekday(), t.Day())
	return err
}

func getAllTasksStmt(db *sql.DB) (*sql.Stmt, error) {
	return db.Prepare("SELECT task_id, chores.short_desc, done, done_by, tasks.c_time, tasks.chore_id from tasks left join chores on (tasks.chore_id = chores.chore_id)")
}

func (h *handler) getAllTasks() ([]*Task, error) {
	rows, err := h.getAllTasksStmt.Query()
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var (
		taskList    []*Task
		task_id     int64
		short_desc  string
		done        bool
		done_by_raw sql.NullString
		done_by     string
		chore_id    int64
		c_time      time.Time
	)
	for rows.Next() {
		err := rows.Scan(&task_id, &short_desc, &done, &done_by_raw, &c_time, &chore_id)
		if err != nil {
			return nil, err
		}

		if done_by_raw.Valid {
			done_by = done_by_raw.String
		} else {
			done_by = ""
		}

		t := &Task{task_id, short_desc, done, done_by, c_time, chore_id}
		taskList = append(taskList, t)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	return taskList, nil
}

func modifyTaskStmt(db *sql.DB) (*sql.Stmt, error) {
	return db.Prepare("UPDATE tasks set done = ?, done_by = ? where task_id = ?")
}

func (h *handler) modifyTask(task *Task) error {
	_, err := h.modifyTaskStmt.Exec(
		task.Done,
		task.DoneBy,
	)
	return err
}
<commit_msg>todo about task completion wars<commit_after>package tasks

// NOTE we do not log anything in this file! Leave it to the http handler to handle errors.

import (
	"database/sql"
	"time"
)

// TODO add done, done_by, c_time (maybe m_time?)
type Task struct {
	Key        int64     `json:"key"`
	ShortDesc  string    `json:"short_desc"`
	Done       bool      `json:"done"`
	DoneBy     string    `json:"done_by"`
	CreateTime time.Time `json:"c_time"`
	ChoreId    int64     `json:"chore_id"`
}

func setChoresNowStmt(db *sql.DB) (*sql.Stmt, error) {
	return db.Prepare("INSERT INTO tasks(chore_id, c_time) SELECT chore_id, NULL from chores where (morning = ? OR night = ?) AND ((dwm = 'd') OR (dwm = 'w' AND day = ?) OR (dwm = 'm' AND date = ?))")
}

func (h *handler) setChoresNow(t time.Time) error {
	var (
		isMorning int
		isNight   int
	)
	if t.Hour() >= 5 && t.Hour() < 17 {
		isMorning = 1
		isNight = -1
	} else {
		isMorning = -1
		isNight = 1
	}
	_, err := h.setChoresNowStmt.Exec(isMorning, isNight, t.Weekday(), t.Day())
	return err
}

func getAllTasksStmt(db *sql.DB) (*sql.Stmt, error) {
	return db.Prepare("SELECT task_id, chores.short_desc, done, done_by, tasks.c_time, tasks.chore_id from tasks left join chores on (tasks.chore_id = chores.chore_id)")
}

func (h *handler) getAllTasks() ([]*Task, error) {
	rows, err := h.getAllTasksStmt.Query()
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var (
		taskList    []*Task
		task_id     int64
		short_desc  string
		done        bool
		done_by_raw sql.NullString
		done_by     string
		chore_id    int64
		c_time      time.Time
	)
	for rows.Next() {
		err := rows.Scan(&task_id, &short_desc, &done, &done_by_raw, &c_time, &chore_id)
		if err != nil {
			return nil, err
		}

		if done_by_raw.Valid {
			done_by = done_by_raw.String
		} else {
			done_by = ""
		}

		t := &Task{task_id, short_desc, done, done_by, c_time, chore_id}
		taskList = append(taskList, t)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	return taskList, nil
}

// TODO prevent cheekiness: could have done_by contention
func modifyTaskStmt(db *sql.DB) (*sql.Stmt, error) {
	return db.Prepare("UPDATE tasks set done = ?, done_by = ? where task_id = ?")
}

func (h *handler) modifyTask(task *Task) error {
	_, err := h.modifyTaskStmt.Exec(
		task.Done,
		task.DoneBy,
	)
	return err
}
<|endoftext|>


<commit_before>package cluster

import (
	"context"
	"sync"
	"time"

	log "gopkg.in/inconshreveable/log15.v2"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/endpoints"
	"github.com/lxc/lxd/lxd/events"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
)

// eventHubMinHosts is the minimum number of members that must have the event-hub role to trigger switching into
// event-hub mode (where cluster members will only connect to event-hub members rather than all members when
// operating in the normal full-mesh mode).
const eventHubMinHosts = 2

// EventMode indicates the event distribution mode.
type EventMode string

// EventModeFullMesh is when every cluster member connects to every other cluster member to pull events.
const EventModeFullMesh EventMode = "full-mesh"

// EventModeHubServer is when the cluster is operating in event-hub mode and this server is designated as a hub
// server, meaning that it will only connect to the other event-hub members and not other members.
const EventModeHubServer EventMode = "hub-server"

// EventModeHubClient is when the cluster is operating in event-hub mode and this member is designated as a hub
// client, meaning that it is expected to connect to the event-hub members.
const EventModeHubClient EventMode = "hub-client"

// eventListenerClient stores both the event listener and its associated client.
type eventListenerClient struct {
	*lxd.EventListener

	client        lxd.InstanceServer
	hubPushCancel context.CancelFunc
}

// Disconnect disconnects both the listener and the client.
func (lc *eventListenerClient) Disconnect() {
	if lc.hubPushCancel != nil {
		lc.hubPushCancel()
	}

	lc.EventListener.Disconnect()
	lc.client.Disconnect()
}

// SetEventMode applies the specified eventMode of the local server to the listener.
// If the eventMode is EventModeHubClient then a go routine is started that consumes events from eventHubPushCh and
// pushes them to the remote server. If the eventMode is anything else then the go routine is stopped if running.
func (lc *eventListenerClient) SetEventMode(eventMode EventMode, eventHubPushCh chan api.Event) {
	if eventMode == EventModeHubClient {
		if lc.hubPushCancel != nil || !lc.IsActive() {
			return
		}

		ctx, cancel := context.WithCancel(context.Background())

		go func() {
			lc.hubPushCancel = cancel
			info, _ := lc.client.GetConnectionInfo()
			logger.Info("Event hub client started", log.Ctx{"remote": info.URL})
			defer logger.Info("Event hub client stopped", log.Ctx{"remote": info.URL})
			defer func() {
				cancel()
				lc.hubPushCancel = nil
			}()

			for {
				select {
				case event, more := <-eventHubPushCh:
					if !more {
						return
					}

					err := lc.client.SendEvent(event)
					if err != nil {
						//  Send failed, something is wrong with this hub server.
						lc.Disconnect() // Disconnect listener and client.

						// Try and put event back onto event hub push queue for consumption
						// by another consumer.
						ctx, cancel := context.WithTimeout(context.Background(), eventHubPushChTimeout)
						defer cancel()

						select {
						case eventHubPushCh <- event:
						case <-ctx.Done(): // Don't block if all consumers are slow/down.
						}

						return
					}
				case <-ctx.Done():
					return
				}
			}
		}()
	} else if lc.hubPushCancel != nil {
		lc.hubPushCancel()
		lc.hubPushCancel = nil
	}

	return
}

var eventMode EventMode = EventModeFullMesh
var eventHubAddresses []string
var eventHubPushCh = make(chan api.Event, 10) // Buffer size to accommodate slow consumers before dropping events.
var eventHubPushChTimeout = time.Duration(time.Second)
var listeners = map[string]*eventListenerClient{}
var listenersNotify = map[chan struct{}][]string{}
var listenersLock sync.Mutex
var listenersUpdateLock sync.Mutex

// ServerEventMode returns the event distribution mode that this local server is operating in.
func ServerEventMode() EventMode {
	listenersLock.Lock()
	defer listenersLock.Unlock()

	return eventMode
}

// RoleInSlice returns whether or not the rule is within the roles list.
func RoleInSlice(role db.ClusterRole, roles []db.ClusterRole) bool {
	for _, r := range roles {
		if r == role {
			return true
		}
	}

	return false
}

// EventListenerWait waits for there to be listener connected to the specified address, or one of the event hubs
// if operating in event hub mode.
func EventListenerWait(ctx context.Context, address string) error {
	// Check if there is already a listener.
	listenersLock.Lock()
	listener, found := listeners[address]
	if found && listener.IsActive() {
		listenersLock.Unlock()
		return nil
	}

	listenAddresses := []string{address}

	// Check if operating in event hub mode and if one of the event hub connections is available.
	// If so then we are ready to receive events from all members.
	if eventMode != EventModeFullMesh {
		for _, eventHubAddress := range eventHubAddresses {
			listener, found := listeners[eventHubAddress]
			if found && listener.IsActive() {
				listenersLock.Unlock()
				return nil
			}

			listenAddresses = append(listenAddresses, eventHubAddress)
		}
	}

	// If not setup a notification for when the desired address or any of the event hubs connect.
	connected := make(chan struct{})
	listenersNotify[connected] = listenAddresses
	listenersLock.Unlock()

	defer func() {
		listenersLock.Lock()
		delete(listenersNotify, connected)
		listenersLock.Unlock()
	}()

	// Wait for the connected channel to be closed (indicating a new listener has been connected), and return.
	select {
	case <-connected:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

// hubAddresses returns the addresses of members with event-hub role, and the event mode of the server.
// The event mode will only be hub-server or hub-client if at least eventHubMinHosts have an event-hub role.
// Otherwise the mode will be full-mesh.
func hubAddresses(localAddress string, members map[int64]APIHeartbeatMember) ([]string, EventMode) {
	var hubAddresses []string
	var localHasHubRole bool

	// Do a first pass of members to count the members with event-hub role, and whether we are a hub server.
	for _, member := range members {
		if RoleInSlice(db.ClusterRoleEventHub, member.Roles) {
			hubAddresses = append(hubAddresses, member.Address)

			if member.Address == localAddress {
				localHasHubRole = true
			}
		}
	}

	eventMode := EventModeFullMesh
	if len(hubAddresses) >= eventHubMinHosts {
		if localHasHubRole {
			eventMode = EventModeHubServer
		} else {
			eventMode = EventModeHubClient
		}
	}

	return hubAddresses, eventMode
}

// EventsUpdateListeners refreshes the cluster event listener connections.
func EventsUpdateListeners(endpoints *endpoints.Endpoints, cluster *db.Cluster, serverCert func() *shared.CertInfo, members map[int64]APIHeartbeatMember, inject events.InjectFunc) {
	listenersUpdateLock.Lock()
	defer listenersUpdateLock.Unlock()

	// If no heartbeat members provided, populate from global database.
	if members == nil {
		var dbMembers []db.NodeInfo
		var offlineThreshold time.Duration

		err := cluster.Transaction(func(tx *db.ClusterTx) error {
			var err error

			dbMembers, err = tx.GetNodes()
			if err != nil {
				return err
			}

			offlineThreshold, err = tx.GetNodeOfflineThreshold()
			if err != nil {
				return err
			}

			return nil
		})
		if err != nil {
			logger.Warn("Failed to get current cluster members", log.Ctx{"err": err})
			return
		}

		members = make(map[int64]APIHeartbeatMember, len(dbMembers))
		for _, dbMember := range dbMembers {
			members[dbMember.ID] = APIHeartbeatMember{
				ID:            dbMember.ID,
				Name:          dbMember.Name,
				Address:       dbMember.Address,
				LastHeartbeat: dbMember.Heartbeat,
				Online:        !dbMember.IsOffline(offlineThreshold),
				Roles:         dbMember.Roles,
			}
		}
	}

	localAddress := endpoints.NetworkAddress()
	hubAddresses, localEventMode := hubAddresses(localAddress, members)

	// Store event hub addresses in global slice.
	listenersLock.Lock()
	eventHubAddresses = hubAddresses
	eventMode = localEventMode
	listenersLock.Unlock()

	keepListeners := make(map[string]struct{})
	wg := sync.WaitGroup{}
	for _, member := range members {
		// Don't bother trying to connect to ourselves or offline members.
		if member.Address == localAddress || !member.Online {
			continue
		}

		if localEventMode != EventModeFullMesh && !RoleInSlice(db.ClusterRoleEventHub, member.Roles) {
			continue // Skip non-event-hub members if we are operating in event-hub mode.
		}

		listenersLock.Lock()
		listener, ok := listeners[member.Address]

		// If the member already has a listener associated to it, check that the listener is still active.
		// If it is, just move on to next member, but if not then we'll try to connect again.
		if ok {
			if listener.IsActive() {
				keepListeners[member.Address] = struct{}{} // Add to current listeners list.
				listener.SetEventMode(localEventMode, eventHubPushCh)
				listenersLock.Unlock()
				continue
			}

			// Disconnect and delete listener, but don't delete any listenersNotify entry as there
			// might be something waiting for a future connection.
			listener.Disconnect()
			delete(listeners, member.Address)
			listenersLock.Unlock()

			// Log after releasing listenersLock to avoid deadlock on listenersLock with EventHubPush.
			logger.Info("Removed inactive member event listener client", log.Ctx{"local": localAddress, "remote": member.Address})
		} else {
			listenersLock.Unlock()
		}

		keepListeners[member.Address] = struct{}{} // Add to current listeners list.

		// Connect to remote concurrently and add to active listeners if successful.
		wg.Add(1)
		go func(m APIHeartbeatMember) {
			logger := logging.AddContext(logger.Log, log.Ctx{"local": localAddress, "remote": m.Address})

			defer wg.Done()
			listener, err := eventsConnect(m.Address, endpoints.NetworkCert(), serverCert())
			if err != nil {
				logger.Warn("Failed adding member event listener client", log.Ctx{"err": err})
				return
			}

			listener.AddHandler(nil, func(event api.Event) {
				// Inject event received via pull as forwarded so that its not forwarded again
				// onto other members.
				inject(event, events.EventSourcePull)
			})

			listener.SetEventMode(localEventMode, eventHubPushCh)

			listenersLock.Lock()
			listeners[m.Address] = listener

			// Indicate to any notifiers waiting for this member's address that it is connected.
			for connected, notifyAddresses := range listenersNotify {
				if shared.StringInSlice(m.Address, notifyAddresses) {
					close(connected)
					delete(listenersNotify, connected)
				}
			}

			listenersLock.Unlock()

			// Log after releasing listenersLock to avoid deadlock on listenersLock with EventHubPush.
			logger.Info("Added member event listener client")
		}(member)
	}
	wg.Wait()

	// Disconnect and delete any out of date listeners and their notifiers.
	var removedAddresses []string

	listenersLock.Lock()
	for address, listener := range listeners {
		if _, found := keepListeners[address]; !found {
			listener.Disconnect()
			delete(listeners, address)

			// Record address removed, but don't log it here as this could cause a deadlock on
			// listenersLock with EventHubPush
			removedAddresses = append(removedAddresses, address)
		}
	}
	listenersLock.Unlock()

	// Log the listeners removed after releasing listenersLock.
	for _, removedAddress := range removedAddresses {
		logger.Info("Removed old member event listener client", log.Ctx{"local": localAddress, "remote": removedAddress})
	}

	if len(members) > 1 && len(keepListeners) <= 0 {
		logger.Error("No active cluster event listener clients")
	}
}

// Establish a client connection to get events from the given node.
func eventsConnect(address string, networkCert *shared.CertInfo, serverCert *shared.CertInfo) (*eventListenerClient, error) {
	client, err := Connect(address, networkCert, serverCert, nil, true)
	if err != nil {
		return nil, err
	}

	revert := revert.New()
	revert.Add(func() {
		client.Disconnect()
	})

	listener, err := client.GetEventsAllProjects()
	if err != nil {
		return nil, err
	}

	revert.Success()

	lc := &eventListenerClient{
		EventListener: listener,
		client:        client,
	}

	return lc, nil
}

// EventHubPush pushes the event to the event hub members if local server is an event-hub client.
func EventHubPush(event api.Event) {
	listenersLock.Lock()
	// If the local server isn't an event-hub client, then we don't need to push messages as the other
	// members should be connected to us via a pull event listener and so will receive the event that way.
	// Also if there are no listeners available then there's no point in pushing to the eventHubPushCh as it
	// will have no consumers reading from it (this allows somewhat graceful handling of the situation where
	// all event-hub members are down by dropping events rather than slowing down the local system).
	if eventMode != EventModeHubClient || len(listeners) <= 0 {
		listenersLock.Unlock()
		return
	}
	listenersLock.Unlock()

	// Run in a go routine so as not to delay caller of this function as we try and deliver it.
	go func() {
		ctx, cancel := context.WithTimeout(context.Background(), eventHubPushChTimeout)
		defer cancel()

		select {
		case eventHubPushCh <- event:
		case <-ctx.Done(): // Don't block if all consumers are slow/down.
		}
	}()
}
<commit_msg>lxd/cluster/events: Move state update in EventsUpdateListeners to end<commit_after>package cluster

import (
	"context"
	"sync"
	"time"

	log "gopkg.in/inconshreveable/log15.v2"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/endpoints"
	"github.com/lxc/lxd/lxd/events"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
)

// eventHubMinHosts is the minimum number of members that must have the event-hub role to trigger switching into
// event-hub mode (where cluster members will only connect to event-hub members rather than all members when
// operating in the normal full-mesh mode).
const eventHubMinHosts = 2

// EventMode indicates the event distribution mode.
type EventMode string

// EventModeFullMesh is when every cluster member connects to every other cluster member to pull events.
const EventModeFullMesh EventMode = "full-mesh"

// EventModeHubServer is when the cluster is operating in event-hub mode and this server is designated as a hub
// server, meaning that it will only connect to the other event-hub members and not other members.
const EventModeHubServer EventMode = "hub-server"

// EventModeHubClient is when the cluster is operating in event-hub mode and this member is designated as a hub
// client, meaning that it is expected to connect to the event-hub members.
const EventModeHubClient EventMode = "hub-client"

// eventListenerClient stores both the event listener and its associated client.
type eventListenerClient struct {
	*lxd.EventListener

	client        lxd.InstanceServer
	hubPushCancel context.CancelFunc
}

// Disconnect disconnects both the listener and the client.
func (lc *eventListenerClient) Disconnect() {
	if lc.hubPushCancel != nil {
		lc.hubPushCancel()
	}

	lc.EventListener.Disconnect()
	lc.client.Disconnect()
}

// SetEventMode applies the specified eventMode of the local server to the listener.
// If the eventMode is EventModeHubClient then a go routine is started that consumes events from eventHubPushCh and
// pushes them to the remote server. If the eventMode is anything else then the go routine is stopped if running.
func (lc *eventListenerClient) SetEventMode(eventMode EventMode, eventHubPushCh chan api.Event) {
	if eventMode == EventModeHubClient {
		if lc.hubPushCancel != nil || !lc.IsActive() {
			return
		}

		ctx, cancel := context.WithCancel(context.Background())

		go func() {
			lc.hubPushCancel = cancel
			info, _ := lc.client.GetConnectionInfo()
			logger.Info("Event hub client started", log.Ctx{"remote": info.URL})
			defer logger.Info("Event hub client stopped", log.Ctx{"remote": info.URL})
			defer func() {
				cancel()
				lc.hubPushCancel = nil
			}()

			for {
				select {
				case event, more := <-eventHubPushCh:
					if !more {
						return
					}

					err := lc.client.SendEvent(event)
					if err != nil {
						//  Send failed, something is wrong with this hub server.
						lc.Disconnect() // Disconnect listener and client.

						// Try and put event back onto event hub push queue for consumption
						// by another consumer.
						ctx, cancel := context.WithTimeout(context.Background(), eventHubPushChTimeout)
						defer cancel()

						select {
						case eventHubPushCh <- event:
						case <-ctx.Done(): // Don't block if all consumers are slow/down.
						}

						return
					}
				case <-ctx.Done():
					return
				}
			}
		}()
	} else if lc.hubPushCancel != nil {
		lc.hubPushCancel()
		lc.hubPushCancel = nil
	}

	return
}

var eventMode EventMode = EventModeFullMesh
var eventHubAddresses []string
var eventHubPushCh = make(chan api.Event, 10) // Buffer size to accommodate slow consumers before dropping events.
var eventHubPushChTimeout = time.Duration(time.Second)
var listeners = map[string]*eventListenerClient{}
var listenersNotify = map[chan struct{}][]string{}
var listenersLock sync.Mutex
var listenersUpdateLock sync.Mutex

// ServerEventMode returns the event distribution mode that this local server is operating in.
func ServerEventMode() EventMode {
	listenersLock.Lock()
	defer listenersLock.Unlock()

	return eventMode
}

// RoleInSlice returns whether or not the rule is within the roles list.
func RoleInSlice(role db.ClusterRole, roles []db.ClusterRole) bool {
	for _, r := range roles {
		if r == role {
			return true
		}
	}

	return false
}

// EventListenerWait waits for there to be listener connected to the specified address, or one of the event hubs
// if operating in event hub mode.
func EventListenerWait(ctx context.Context, address string) error {
	// Check if there is already a listener.
	listenersLock.Lock()
	listener, found := listeners[address]
	if found && listener.IsActive() {
		listenersLock.Unlock()
		return nil
	}

	listenAddresses := []string{address}

	// Check if operating in event hub mode and if one of the event hub connections is available.
	// If so then we are ready to receive events from all members.
	if eventMode != EventModeFullMesh {
		for _, eventHubAddress := range eventHubAddresses {
			listener, found := listeners[eventHubAddress]
			if found && listener.IsActive() {
				listenersLock.Unlock()
				return nil
			}

			listenAddresses = append(listenAddresses, eventHubAddress)
		}
	}

	// If not setup a notification for when the desired address or any of the event hubs connect.
	connected := make(chan struct{})
	listenersNotify[connected] = listenAddresses
	listenersLock.Unlock()

	defer func() {
		listenersLock.Lock()
		delete(listenersNotify, connected)
		listenersLock.Unlock()
	}()

	// Wait for the connected channel to be closed (indicating a new listener has been connected), and return.
	select {
	case <-connected:
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

// hubAddresses returns the addresses of members with event-hub role, and the event mode of the server.
// The event mode will only be hub-server or hub-client if at least eventHubMinHosts have an event-hub role.
// Otherwise the mode will be full-mesh.
func hubAddresses(localAddress string, members map[int64]APIHeartbeatMember) ([]string, EventMode) {
	var hubAddresses []string
	var localHasHubRole bool

	// Do a first pass of members to count the members with event-hub role, and whether we are a hub server.
	for _, member := range members {
		if RoleInSlice(db.ClusterRoleEventHub, member.Roles) {
			hubAddresses = append(hubAddresses, member.Address)

			if member.Address == localAddress {
				localHasHubRole = true
			}
		}
	}

	eventMode := EventModeFullMesh
	if len(hubAddresses) >= eventHubMinHosts {
		if localHasHubRole {
			eventMode = EventModeHubServer
		} else {
			eventMode = EventModeHubClient
		}
	}

	return hubAddresses, eventMode
}

// EventsUpdateListeners refreshes the cluster event listener connections.
func EventsUpdateListeners(endpoints *endpoints.Endpoints, cluster *db.Cluster, serverCert func() *shared.CertInfo, members map[int64]APIHeartbeatMember, inject events.InjectFunc) {
	listenersUpdateLock.Lock()
	defer listenersUpdateLock.Unlock()

	// If no heartbeat members provided, populate from global database.
	if members == nil {
		var dbMembers []db.NodeInfo
		var offlineThreshold time.Duration

		err := cluster.Transaction(func(tx *db.ClusterTx) error {
			var err error

			dbMembers, err = tx.GetNodes()
			if err != nil {
				return err
			}

			offlineThreshold, err = tx.GetNodeOfflineThreshold()
			if err != nil {
				return err
			}

			return nil
		})
		if err != nil {
			logger.Warn("Failed to get current cluster members", log.Ctx{"err": err})
			return
		}

		members = make(map[int64]APIHeartbeatMember, len(dbMembers))
		for _, dbMember := range dbMembers {
			members[dbMember.ID] = APIHeartbeatMember{
				ID:            dbMember.ID,
				Name:          dbMember.Name,
				Address:       dbMember.Address,
				LastHeartbeat: dbMember.Heartbeat,
				Online:        !dbMember.IsOffline(offlineThreshold),
				Roles:         dbMember.Roles,
			}
		}
	}

	localAddress := endpoints.NetworkAddress()
	hubAddresses, localEventMode := hubAddresses(localAddress, members)

	keepListeners := make(map[string]struct{})
	wg := sync.WaitGroup{}
	for _, member := range members {
		// Don't bother trying to connect to ourselves or offline members.
		if member.Address == localAddress || !member.Online {
			continue
		}

		if localEventMode != EventModeFullMesh && !RoleInSlice(db.ClusterRoleEventHub, member.Roles) {
			continue // Skip non-event-hub members if we are operating in event-hub mode.
		}

		listenersLock.Lock()
		listener, ok := listeners[member.Address]

		// If the member already has a listener associated to it, check that the listener is still active.
		// If it is, just move on to next member, but if not then we'll try to connect again.
		if ok {
			if listener.IsActive() {
				keepListeners[member.Address] = struct{}{} // Add to current listeners list.
				listener.SetEventMode(localEventMode, eventHubPushCh)
				listenersLock.Unlock()
				continue
			}

			// Disconnect and delete listener, but don't delete any listenersNotify entry as there
			// might be something waiting for a future connection.
			listener.Disconnect()
			delete(listeners, member.Address)
			listenersLock.Unlock()

			// Log after releasing listenersLock to avoid deadlock on listenersLock with EventHubPush.
			logger.Info("Removed inactive member event listener client", log.Ctx{"local": localAddress, "remote": member.Address})
		} else {
			listenersLock.Unlock()
		}

		keepListeners[member.Address] = struct{}{} // Add to current listeners list.

		// Connect to remote concurrently and add to active listeners if successful.
		wg.Add(1)
		go func(m APIHeartbeatMember) {
			logger := logging.AddContext(logger.Log, log.Ctx{"local": localAddress, "remote": m.Address})

			defer wg.Done()
			listener, err := eventsConnect(m.Address, endpoints.NetworkCert(), serverCert())
			if err != nil {
				logger.Warn("Failed adding member event listener client", log.Ctx{"err": err})
				return
			}

			listener.AddHandler(nil, func(event api.Event) {
				// Inject event received via pull as forwarded so that its not forwarded again
				// onto other members.
				inject(event, events.EventSourcePull)
			})

			listener.SetEventMode(localEventMode, eventHubPushCh)

			listenersLock.Lock()
			listeners[m.Address] = listener

			// Indicate to any notifiers waiting for this member's address that it is connected.
			for connected, notifyAddresses := range listenersNotify {
				if shared.StringInSlice(m.Address, notifyAddresses) {
					close(connected)
					delete(listenersNotify, connected)
				}
			}

			listenersLock.Unlock()

			// Log after releasing listenersLock to avoid deadlock on listenersLock with EventHubPush.
			logger.Info("Added member event listener client")
		}(member)
	}
	wg.Wait()

	// Disconnect and delete any out of date listeners and their notifiers.
	var removedAddresses []string

	listenersLock.Lock()
	for address, listener := range listeners {
		if _, found := keepListeners[address]; !found {
			listener.Disconnect()
			delete(listeners, address)

			// Record address removed, but don't log it here as this could cause a deadlock on
			// listenersLock with EventHubPush
			removedAddresses = append(removedAddresses, address)
		}
	}

	// Store event hub addresses in global slice late in the function after all event connections have been
	// opened above. This way the reported state by this server won't be updated until its ready.
	eventHubAddresses = hubAddresses
	eventMode = localEventMode

	listenersLock.Unlock()

	// Log the listeners removed after releasing listenersLock.
	for _, removedAddress := range removedAddresses {
		logger.Info("Removed old member event listener client", log.Ctx{"local": localAddress, "remote": removedAddress})
	}

	if len(members) > 1 && len(keepListeners) <= 0 {
		logger.Error("No active cluster event listener clients")
	}
}

// Establish a client connection to get events from the given node.
func eventsConnect(address string, networkCert *shared.CertInfo, serverCert *shared.CertInfo) (*eventListenerClient, error) {
	client, err := Connect(address, networkCert, serverCert, nil, true)
	if err != nil {
		return nil, err
	}

	revert := revert.New()
	revert.Add(func() {
		client.Disconnect()
	})

	listener, err := client.GetEventsAllProjects()
	if err != nil {
		return nil, err
	}

	revert.Success()

	lc := &eventListenerClient{
		EventListener: listener,
		client:        client,
	}

	return lc, nil
}

// EventHubPush pushes the event to the event hub members if local server is an event-hub client.
func EventHubPush(event api.Event) {
	listenersLock.Lock()
	// If the local server isn't an event-hub client, then we don't need to push messages as the other
	// members should be connected to us via a pull event listener and so will receive the event that way.
	// Also if there are no listeners available then there's no point in pushing to the eventHubPushCh as it
	// will have no consumers reading from it (this allows somewhat graceful handling of the situation where
	// all event-hub members are down by dropping events rather than slowing down the local system).
	if eventMode != EventModeHubClient || len(listeners) <= 0 {
		listenersLock.Unlock()
		return
	}
	listenersLock.Unlock()

	// Run in a go routine so as not to delay caller of this function as we try and deliver it.
	go func() {
		ctx, cancel := context.WithTimeout(context.Background(), eventHubPushChTimeout)
		defer cancel()

		select {
		case eventHubPushCh <- event:
		case <-ctx.Done(): // Don't block if all consumers are slow/down.
		}
	}()
}
<|endoftext|>


<commit_before>/*
Copyright 2015 CoreOS Inc.

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

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

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

package machine1

import (
	"fmt"
	"os/exec"
	"testing"
)

var (
	conn        *Conn
	machineName = "testMachine"
)

func createTestProcess() (pid int, err error) {
	systemdRun, lookErr := exec.LookPath("systemd-run")
	if lookErr != nil {
		return -1, lookErr
	}
	sleep, lookErr := exec.LookPath("sleep")
	if lookErr != nil {
		return -1, lookErr
	}
	cmd := exec.Command(systemdRun, sleep, "5000")
	err = cmd.Run()
	if err != nil {
		return -1, err
	}
	return cmd.Process.Pid + 1, nil
}

// TestNew ensures that New() works without errors.
func TestNew(t *testing.T) {
	c, err := New()
	conn = c
	if err != nil {
		t.Fatal(err)
	}
}

func TestRegister(t *testing.T) {
	leader, lErr := createTestProcess()
	if lErr != nil {
		t.Error(lErr)
	}
	t.Log(leader)
	regErr := conn.RegisterMachine(machineName, nil, "go-systemd", "container", leader, "")
	if regErr != nil {
		t.Error(regErr)
	}
}

func TestGet(t *testing.T) {
	machine, machineErr := conn.GetMachine(machineName)
	if machineErr != nil {
		t.Error(machineErr)
	}
	if len(machine) == 0 {
		t.Error(fmt.Errorf("did not find machine named %s", machineName))
	}
}

func TestTerminate(t *testing.T) {
	tErr := conn.TerminateMachine(machineName)
	if tErr != nil {
		t.Error(tErr)
	}
}
<commit_msg>machine: consolidate tests<commit_after>/*
Copyright 2015 CoreOS Inc.

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

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

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

package machine1

import (
	"os/exec"
	"testing"
)

var (
	machineName = "testMachine"
)

func mustCreateTestProcess() (pid int) {
	systemdRun, err := exec.LookPath("systemd-run")
	if err != nil {
		panic(err.Error())
	}
	sleep, err := exec.LookPath("sleep")
	if err != nil {
		panic(err.Error())
	}
	cmd := exec.Command(systemdRun, sleep, "5000")
	err = cmd.Run()
	if err != nil {
		panic(err.Error())
	}
	return cmd.Process.Pid + 1
}

func TestMachine(t *testing.T) {
	leader := mustCreateTestProcess()
	t.Log(leader)

	conn, newErr := New()
	if newErr != nil {
		t.Fatal(newErr)
	}

	regErr := conn.RegisterMachine(machineName, nil, "go-systemd", "container", leader, "")
	if regErr != nil {
		t.Fatal(regErr)
	}

	machine, getErr := conn.GetMachine(machineName)
	if getErr != nil {
		t.Fatal(getErr)
	}
	if len(machine) == 0 {
		t.Fatalf("did not find machine named %s", machineName)
	}

	tErr := conn.TerminateMachine(machineName)
	if tErr != nil {
		t.Fatal(tErr)
	}

	machine, getErr = conn.GetMachine(machineName)
	if len(machine) != 0 {
		t.Fatalf("unexpectedly found machine named %s", machineName)
	} else if getErr == nil {
		t.Fatal("expected error but got nil")
	}
}
<|endoftext|>


<commit_before>package models

import (
	"testing"

	"bitbucket.org/mundipagg/boletoapi/test"
	. "github.com/smartystreets/goconvey/convey"
)

func TestDocument(t *testing.T) {
	Convey("Espera que o tipo de documento passado seja um CPF", t, func() {
		document := Document{Number: "13245678901ssa", Type: "CPF"}
		So(document.IsCPF(), ShouldBeTrue)
		document.Type = "cPf"
		So(document.IsCPF(), ShouldBeTrue)
		Convey("Espera que o CPF seja válido", func() {
			err := document.ValidateCPF()
			So(err, ShouldBeNil)
			So(len(document.Number), ShouldEqual, 11)
		})
		Convey("Espera que o CPF seja inválido", func() {
			document.Number = "lasjdlf019239098adjal9390jflsadjf9309jfsl"
			err := document.ValidateCPF()
			So(err, ShouldNotBeNil)
		})
	})
	Convey("Espera que o tipo de documento seja um CNPJ", t, func() {
		document := Document{Number: "12345678901326asdfad", Type: "CNPJ"}
		So(document.IsCNPJ(), ShouldBeTrue)
		document.Type = "cnPj"
		So(document.IsCNPJ(), ShouldBeTrue)
		Convey("Espera que o CNPJ seja válido", func() {
			err := document.ValidateCNPJ()
			So(err, ShouldBeNil)
			So(len(document.Number), ShouldEqual, 14)
		})
		Convey("Espera que o CNPJ seja inválido", func() {
			document.Number = "lasjdlf019239098adjal9390jflsadjf9309jfsl"
			err := document.ValidateCNPJ()
			So(err, ShouldNotBeNil)
		})
	})
}

func TestTitle(t *testing.T) {
	Convey("O DocumentNumber deve conter 10 dígitos", t, func() {
		h := Title{DocumentNumber: "1234567891011"}
		err := h.ValidateDocumentNumber()
		So(err, ShouldBeNil)
		So(len(h.DocumentNumber), ShouldEqual, 10)

		Convey("O DocumentNumber mesmo com menos de 10 dígitos deve possuir 10 dígitos após ser validado com 0 a esquerda", func() {
			h.DocumentNumber = "123x"
			h.ValidateDocumentNumber()
			So(len(h.DocumentNumber), ShouldEqual, 10)
		})

		Convey("O DocumentNumber quando não possuir dígitos deve ser vazio", func() {
			h.DocumentNumber = "xx"
			h.ValidateDocumentNumber()
			So(h.DocumentNumber, ShouldBeEmpty)
		})

		Convey("O DocumentNumber quando for vazio deve permanecer vazio", func() {
			h.DocumentNumber = ""
			h.ValidateDocumentNumber()
			So(h.DocumentNumber, ShouldBeEmpty)
		})
	})

	Convey("As instruções devem ser válidas", t, func() {
		h := Title{Instructions: "Some instructions"}
		err := h.ValidateInstructionsLength(100)
		So(err, ShouldBeNil)
		Convey("As instruções devem ser inválidas", func() {
			err = h.ValidateInstructionsLength(1)
			So(err, ShouldNotBeNil)
		})

	})
}

func TestShouldReturnBankNumberIsValid(t *testing.T) {
	var b BankNumber = 237

	if b.IsBankNumberValid() == false {
		t.Fail()
	}
}

func TestShouldAppendCollectionOfErrrors(t *testing.T) {
	e := NewErrorCollection(ErrorResponse{Code: "200", Message: "Hue2"})
	e.Append("100", "Hue")
	test.ExpectTrue(len(e) == 2, t)
}

func TestShouldCreateNewSingleErrorCollection(t *testing.T) {
	e := NewSingleErrorCollection("200", "Hue2")
	test.ExpectTrue(len(e) == 1, t)
}

func TestIsAgencyValid(t *testing.T) {
	Convey("Deve retornar um erro para a agência inválida", t, func() {
		a := Agreement{
			Agency: "234-2222a",
		}
		err := a.IsAgencyValid()
		So(err, ShouldNotBeNil)

		Convey("Deve ajustar a agência para ter a quantidade certa de dígitos", func() {
			a.Agency = "321"
			err := a.IsAgencyValid()
			So(a.Agency, ShouldEqual, "0321")
			So(err, ShouldBeNil)
		})
	})
}

func TestCalculateAgencyDigit(t *testing.T) {
	Convey("Deve ajustar o dígito da Agência quando ela tiver caracteres inválidos", t, func() {
		a := new(Agreement)
		a.AgencyDigit = "2sssss"
		c := func(s string) string {
			return "1"
		}
		a.CalculateAgencyDigit(c)
		So(a.AgencyDigit, ShouldEqual, "2")
		Convey("Deve calcular o dígito da Agência quando o fornecido for errado", func() {
			a.AgencyDigit = "332sssss"
			a.CalculateAgencyDigit(c)
			So(a.AgencyDigit, ShouldEqual, "1")
		})
	})
}

func TestCalculateAccountDigit(t *testing.T) {
	Convey("Deve ajustar o dígito da Conta quando ela tiver caracteres inválidos", t, func() {
		a := new(Agreement)
		a.AccountDigit = "2sssss"
		c := func(s, y string) string {
			return "1"
		}
		a.CalculateAccountDigit(c)
		So(a.AccountDigit, ShouldEqual, "2")
		Convey("Deve calcular o dígito da Conta quando o fornecido for errado", func() {
			a.AccountDigit = "332sssss"
			a.CalculateAccountDigit(c)
			So(a.AccountDigit, ShouldEqual, "1")
		})
	})
}

func TestIsAccountValid(t *testing.T) {
	Convey("Verifica se a conta é valida e formata para o tamanho correto", t, func() {
		a := Agreement{
			Account: "1234fff",
		}
		err := a.IsAccountValid(8)
		So(err, ShouldBeNil)
		So(a.Account, ShouldEqual, "00001234")
		Convey("Verifica se a conta é valida e retorna um erro", func() {
			a.Account = "654654654654654654654654654564"
			err := a.IsAccountValid(8)
			So(err, ShouldNotBeNil)
		})
	})
}
<commit_msg>:white_check_mark: Coloca em 100% o coverage do Title<commit_after>package models

import (
	"testing"

	"time"

	"bitbucket.org/mundipagg/boletoapi/test"
	. "github.com/smartystreets/goconvey/convey"
)

func TestDocument(t *testing.T) {
	Convey("Espera que o tipo de documento passado seja um CPF", t, func() {
		document := Document{Number: "13245678901ssa", Type: "CPF"}
		So(document.IsCPF(), ShouldBeTrue)
		document.Type = "cPf"
		So(document.IsCPF(), ShouldBeTrue)
		Convey("Espera que o CPF seja válido", func() {
			err := document.ValidateCPF()
			So(err, ShouldBeNil)
			So(len(document.Number), ShouldEqual, 11)
		})
		Convey("Espera que o CPF seja inválido", func() {
			document.Number = "lasjdlf019239098adjal9390jflsadjf9309jfsl"
			err := document.ValidateCPF()
			So(err, ShouldNotBeNil)
		})
	})
	Convey("Espera que o tipo de documento seja um CNPJ", t, func() {
		document := Document{Number: "12345678901326asdfad", Type: "CNPJ"}
		So(document.IsCNPJ(), ShouldBeTrue)
		document.Type = "cnPj"
		So(document.IsCNPJ(), ShouldBeTrue)
		Convey("Espera que o CNPJ seja válido", func() {
			err := document.ValidateCNPJ()
			So(err, ShouldBeNil)
			So(len(document.Number), ShouldEqual, 14)
		})
		Convey("Espera que o CNPJ seja inválido", func() {
			document.Number = "lasjdlf019239098adjal9390jflsadjf9309jfsl"
			err := document.ValidateCNPJ()
			So(err, ShouldNotBeNil)
		})
	})
}

func TestTitle(t *testing.T) {
	Convey("O DocumentNumber deve conter 10 dígitos", t, func() {
		h := Title{DocumentNumber: "1234567891011"}
		err := h.ValidateDocumentNumber()
		So(err, ShouldBeNil)
		So(len(h.DocumentNumber), ShouldEqual, 10)

		Convey("O DocumentNumber mesmo com menos de 10 dígitos deve possuir 10 dígitos após ser validado com 0 a esquerda", func() {
			h.DocumentNumber = "123x"
			h.ValidateDocumentNumber()
			So(len(h.DocumentNumber), ShouldEqual, 10)
		})

		Convey("O DocumentNumber quando não possuir dígitos deve ser vazio", func() {
			h.DocumentNumber = "xx"
			h.ValidateDocumentNumber()
			So(h.DocumentNumber, ShouldBeEmpty)
		})

		Convey("O DocumentNumber quando for vazio deve permanecer vazio", func() {
			h.DocumentNumber = ""
			h.ValidateDocumentNumber()
			So(h.DocumentNumber, ShouldBeEmpty)
		})
	})

	Convey("As instruções devem ser válidas", t, func() {
		h := Title{Instructions: "Some instructions"}
		err := h.ValidateInstructionsLength(100)
		So(err, ShouldBeNil)
		Convey("As instruções devem ser inválidas", func() {
			err = h.ValidateInstructionsLength(1)
			So(err, ShouldNotBeNil)
		})
	})

	Convey("A valor em centavos deve ser válido", t, func() {
		h := Title{AmountInCents: 100}
		err := h.IsAmountInCentsValid()
		So(err, ShouldBeNil)

		Convey("O valor em centavos deve ser inválido", func() {
			h.AmountInCents = 0
			err = h.IsAmountInCentsValid()
			So(err, ShouldNotBeNil)
		})
	})

	Convey("Deve transformar uma string no padrão 'AAAA-MM-DD' para um tipo time.Time", t, func() {
		t, err := parseDate("2017-06-23")
		So(err, ShouldBeNil)
		y, m, d := t.Date()
		So(d, ShouldEqual, 23)
		So(m, ShouldEqual, 6)
		So(y, ShouldEqual, 2017)

		Convey("Deve retornar um erro porque o padrão de data estará errado", func() {
			_, err := parseDate("2015/09/26")
			So(err, ShouldNotBeNil)
		})
	})

	Convey("O ExpireDate deve ser válido", t, func() {
		c := "2006-01-02"
		t := time.Now()
		h := Title{ExpireDate: t.AddDate(0, 0, 5).Format(c)}
		err := h.IsExpireDateValid()
		So(err, ShouldBeNil)

		Convey("O ExpireDate deve ser inválido com uma data menor do que a data de hoje", func() {
			h.ExpireDate = t.AddDate(0, 0, -5).Format(c)
			err := h.IsExpireDateValid()
			So(err, ShouldNotBeNil)
		})

		Convey("O ExpireDate deve ser inválido, mas com um formato em string inválido (diferente de 'AAAA-MM-DD'", func() {
			h.ExpireDate = "1994/09/26"
			err := h.IsExpireDateValid()
			So(err, ShouldNotBeNil)
		})
	})
}

func TestShouldReturnBankNumberIsValid(t *testing.T) {
	var b BankNumber = 237

	if b.IsBankNumberValid() == false {
		t.Fail()
	}
}

func TestShouldAppendCollectionOfErrrors(t *testing.T) {
	e := NewErrorCollection(ErrorResponse{Code: "200", Message: "Hue2"})
	e.Append("100", "Hue")
	test.ExpectTrue(len(e) == 2, t)
}

func TestShouldCreateNewSingleErrorCollection(t *testing.T) {
	e := NewSingleErrorCollection("200", "Hue2")
	test.ExpectTrue(len(e) == 1, t)
}

func TestIsAgencyValid(t *testing.T) {
	Convey("Deve retornar um erro para a agência inválida", t, func() {
		a := Agreement{
			Agency: "234-2222a",
		}
		err := a.IsAgencyValid()
		So(err, ShouldNotBeNil)

		Convey("Deve ajustar a agência para ter a quantidade certa de dígitos", func() {
			a.Agency = "321"
			err := a.IsAgencyValid()
			So(a.Agency, ShouldEqual, "0321")
			So(err, ShouldBeNil)
		})
	})
}

func TestCalculateAgencyDigit(t *testing.T) {
	Convey("Deve ajustar o dígito da Agência quando ela tiver caracteres inválidos", t, func() {
		a := new(Agreement)
		a.AgencyDigit = "2sssss"
		c := func(s string) string {
			return "1"
		}
		a.CalculateAgencyDigit(c)
		So(a.AgencyDigit, ShouldEqual, "2")
		Convey("Deve calcular o dígito da Agência quando o fornecido for errado", func() {
			a.AgencyDigit = "332sssss"
			a.CalculateAgencyDigit(c)
			So(a.AgencyDigit, ShouldEqual, "1")
		})
	})
}

func TestCalculateAccountDigit(t *testing.T) {
	Convey("Deve ajustar o dígito da Conta quando ela tiver caracteres inválidos", t, func() {
		a := new(Agreement)
		a.AccountDigit = "2sssss"
		c := func(s, y string) string {
			return "1"
		}
		a.CalculateAccountDigit(c)
		So(a.AccountDigit, ShouldEqual, "2")
		Convey("Deve calcular o dígito da Conta quando o fornecido for errado", func() {
			a.AccountDigit = "332sssss"
			a.CalculateAccountDigit(c)
			So(a.AccountDigit, ShouldEqual, "1")
		})
	})
}

func TestIsAccountValid(t *testing.T) {
	Convey("Verifica se a conta é valida e formata para o tamanho correto", t, func() {
		a := Agreement{
			Account: "1234fff",
		}
		err := a.IsAccountValid(8)
		So(err, ShouldBeNil)
		So(a.Account, ShouldEqual, "00001234")
		Convey("Verifica se a conta é valida e retorna um erro", func() {
			a.Account = "654654654654654654654654654564"
			err := a.IsAccountValid(8)
			So(err, ShouldNotBeNil)
		})
	})
}
<|endoftext|>