<commit_before>package main

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"text/template"

	"github.com/armon/consul-api"
)

type Template struct {
	//
	path string

	//
	dependencies []Dependency

	// Internal variable to represent that a template has been rendered
	rendered bool
}

//
func NewTemplate(path string) (*Template, error) {
	template := &Template{path: path}
	if err := template.init(); err != nil {
		return nil, err
	}

	return template, nil
}

// Path returns the path to this Template
func (t *Template) Path() string {
	return t.path
}

// HashCode returns the map value for this Template
func (t *Template) HashCode() string {
	return fmt.Sprintf("Template|%s", t.path)
}

// Rendered returns true if the template has been executed
func (t *Template) Rendered() bool {
	return t.rendered
}

// GoString returns the detailed format of this object
func (t *Template) GoString() string {
	return fmt.Sprintf("*%#v", *t)
}

// Dependencies returns the dependencies that this template has.
func (t *Template) Dependencies() []Dependency {
	return t.dependencies
}

// Execute takes the given template context and processes the template.
//
// If the TemplateContext is nil, an error will be returned.
//
// If the TemplateContext does not have all required Dependencies, an error will
// be returned.
func (t *Template) Execute(wr io.Writer, c *TemplateContext) error {
	if wr == nil {
		return errors.New("wr must be given")
	}

	if c == nil {
		return errors.New("templateContext must be given")
	}

	// Make sure the context contains everything we need
	if err := t.validateDependencies(c); err != nil {
		return err
	}

	// Render the template
	contents, err := ioutil.ReadFile(t.path)
	if err != nil {
		return err
	}

	tmpl, err := template.New("out").Funcs(template.FuncMap{
		"service":   c.Evaluator(DependencyTypeService),
		"key":       c.Evaluator(DependencyTypeKey),
		"keyPrefix": c.Evaluator(DependencyTypeKeyPrefix),
	}).Parse(string(contents))

	if err != nil {
		return err
	}

	err = tmpl.Execute(wr, c)
	if err != nil {
		return err
	}

	t.rendered = true

	return nil
}

// init reads the template file and parses all the required dependencies into a
// dependencies slice which is then added onto the Template.
func (t *Template) init() error {
	contents, err := ioutil.ReadFile(t.path)
	if err != nil {
		return err
	}

	depsMap := make(map[string]Dependency)

	tmpl, err := template.New("out").Funcs(template.FuncMap{
		"service":   t.dependencyAcc(depsMap, DependencyTypeService),
		"key":       t.dependencyAcc(depsMap, DependencyTypeKey),
		"keyPrefix": t.dependencyAcc(depsMap, DependencyTypeKeyPrefix),
	}).Parse(string(contents))

	if err != nil {
		return err
	}

	err = tmpl.Execute(ioutil.Discard, nil)
	if err != nil {
		return err
	}

	dependencies := make([]Dependency, 0, len(depsMap))
	for _, dep := range depsMap {
		dependencies = append(dependencies, dep)
	}
	depsMap = nil

	t.dependencies = dependencies

	return nil
}

// Helper function that is used by the dependency collecting.
func (t *Template) dependencyAcc(depsMap map[string]Dependency, dt DependencyType) func(string) (interface{}, error) {
	return func(s string) (interface{}, error) {
		switch dt {
		case DependencyTypeService:
			d, err := ParseServiceDependency(s)
			if err != nil {
				return nil, err
			}
			if _, ok := depsMap[d.HashCode()]; !ok {
				depsMap[d.HashCode()] = d
			}

			return []*Service{}, nil
		case DependencyTypeKey:
			d, err := ParseKeyDependency(s)
			if err != nil {
				return nil, err
			}
			if _, ok := depsMap[d.HashCode()]; !ok {
				depsMap[d.HashCode()] = d
			}

			return "", nil
		case DependencyTypeKeyPrefix:
			d, err := ParseKeyPrefixDependency(s)
			if err != nil {
				return nil, err
			}
			if _, ok := depsMap[d.HashCode()]; !ok {
				depsMap[d.HashCode()] = d
			}

			return []*KeyPair{}, nil
		default:
			return nil, fmt.Errorf("unknown DependencyType %#v", dt)
		}
	}
}

// Validates that all required dependencies in t are defined in c.
func (t *Template) validateDependencies(c *TemplateContext) error {
	for _, dep := range t.Dependencies() {
		switch d := dep.(type) {
		case *ServiceDependency:
			if _, ok := c.Services[d.Key()]; !ok {
				return fmt.Errorf("templateContext missing service `%s'", d.Key())
			}
		case *KeyDependency:
			if _, ok := c.Keys[d.Key()]; !ok {
				return fmt.Errorf("templateContext missing key `%s'", d.Key())
			}
		case *KeyPrefixDependency:
			if _, ok := c.KeyPrefixes[d.Key()]; !ok {
				return fmt.Errorf("templateContext missing keyPrefix `%s'", d.Key())
			}
		default:
			return fmt.Errorf("unknown dependency type %#v", d)
		}
	}

	return nil
}

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

// TemplateContext is what Template uses to determine the values that are
// available for template parsing.
type TemplateContext struct {
	Services    map[string][]*Service
	Keys        map[string]string
	KeyPrefixes map[string][]*KeyPair
}

// GoString returns the detailed format of this object
func (c *TemplateContext) GoString() string {
	return fmt.Sprintf("*%#v", *c)
}

// Evaluator takes a DependencyType and returns a function which returns the
// value in the TemplateContext that corresponds to the requested item.
func (c *TemplateContext) Evaluator(dt DependencyType) func(string) (interface{}, error) {
	return func(s string) (interface{}, error) {
		switch dt {
		case DependencyTypeService:
			return c.Services[s], nil
		case DependencyTypeKey:
			return c.Keys[s], nil
		case DependencyTypeKeyPrefix:
			return c.KeyPrefixes[s], nil
		default:
			return nil, fmt.Errorf("unexpected DependencyType %#v", dt)
		}
	}
}

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

type Service struct {
	Node    string
	Address string
	ID      string
	Name    string
	Tags    []string
	Port    uint64
}

// GoString returns the detailed format of this object
func (s *Service) GoString() string {
	return fmt.Sprintf("*%#v", *s)
}

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

type KeyPair struct {
	Key   string
	Value string
}

// GoString returns the detailed format of this object
func (kp *KeyPair) GoString() string {
	return fmt.Sprintf("*%#v", *kp)
}

// NewFromConsul creates a new KeyPair object by parsing the values in the
// consulapi.KVPair. Not all values are transferred.
func (kp KeyPair) NewFromConsul(c *consulapi.KVPair) {
	// TODO: lol
	panic("not done!")
}

// DependencyType is an enum type that says the kind of the dependency.
type DependencyType byte

const (
	DependencyTypeInvalid DependencyType = iota
	DependencyTypeService
	DependencyTypeKey
	DependencyTypeKeyPrefix
)
<commit_msg>Remove unused NewFromConsul method<commit_after>package main

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"text/template"

	"github.com/armon/consul-api"
)

type Template struct {
	//
	path string

	//
	dependencies []Dependency

	// Internal variable to represent that a template has been rendered
	rendered bool
}

//
func NewTemplate(path string) (*Template, error) {
	template := &Template{path: path}
	if err := template.init(); err != nil {
		return nil, err
	}

	return template, nil
}

// Path returns the path to this Template
func (t *Template) Path() string {
	return t.path
}

// HashCode returns the map value for this Template
func (t *Template) HashCode() string {
	return fmt.Sprintf("Template|%s", t.path)
}

// Rendered returns true if the template has been executed
func (t *Template) Rendered() bool {
	return t.rendered
}

// GoString returns the detailed format of this object
func (t *Template) GoString() string {
	return fmt.Sprintf("*%#v", *t)
}

// Dependencies returns the dependencies that this template has.
func (t *Template) Dependencies() []Dependency {
	return t.dependencies
}

// Execute takes the given template context and processes the template.
//
// If the TemplateContext is nil, an error will be returned.
//
// If the TemplateContext does not have all required Dependencies, an error will
// be returned.
func (t *Template) Execute(wr io.Writer, c *TemplateContext) error {
	if wr == nil {
		return errors.New("wr must be given")
	}

	if c == nil {
		return errors.New("templateContext must be given")
	}

	// Make sure the context contains everything we need
	if err := t.validateDependencies(c); err != nil {
		return err
	}

	// Render the template
	contents, err := ioutil.ReadFile(t.path)
	if err != nil {
		return err
	}

	tmpl, err := template.New("out").Funcs(template.FuncMap{
		"service":   c.Evaluator(DependencyTypeService),
		"key":       c.Evaluator(DependencyTypeKey),
		"keyPrefix": c.Evaluator(DependencyTypeKeyPrefix),
	}).Parse(string(contents))

	if err != nil {
		return err
	}

	err = tmpl.Execute(wr, c)
	if err != nil {
		return err
	}

	t.rendered = true

	return nil
}

// init reads the template file and parses all the required dependencies into a
// dependencies slice which is then added onto the Template.
func (t *Template) init() error {
	contents, err := ioutil.ReadFile(t.path)
	if err != nil {
		return err
	}

	depsMap := make(map[string]Dependency)

	tmpl, err := template.New("out").Funcs(template.FuncMap{
		"service":   t.dependencyAcc(depsMap, DependencyTypeService),
		"key":       t.dependencyAcc(depsMap, DependencyTypeKey),
		"keyPrefix": t.dependencyAcc(depsMap, DependencyTypeKeyPrefix),
	}).Parse(string(contents))

	if err != nil {
		return err
	}

	err = tmpl.Execute(ioutil.Discard, nil)
	if err != nil {
		return err
	}

	dependencies := make([]Dependency, 0, len(depsMap))
	for _, dep := range depsMap {
		dependencies = append(dependencies, dep)
	}
	depsMap = nil

	t.dependencies = dependencies

	return nil
}

// Helper function that is used by the dependency collecting.
func (t *Template) dependencyAcc(depsMap map[string]Dependency, dt DependencyType) func(string) (interface{}, error) {
	return func(s string) (interface{}, error) {
		switch dt {
		case DependencyTypeService:
			d, err := ParseServiceDependency(s)
			if err != nil {
				return nil, err
			}
			if _, ok := depsMap[d.HashCode()]; !ok {
				depsMap[d.HashCode()] = d
			}

			return []*Service{}, nil
		case DependencyTypeKey:
			d, err := ParseKeyDependency(s)
			if err != nil {
				return nil, err
			}
			if _, ok := depsMap[d.HashCode()]; !ok {
				depsMap[d.HashCode()] = d
			}

			return "", nil
		case DependencyTypeKeyPrefix:
			d, err := ParseKeyPrefixDependency(s)
			if err != nil {
				return nil, err
			}
			if _, ok := depsMap[d.HashCode()]; !ok {
				depsMap[d.HashCode()] = d
			}

			return []*KeyPair{}, nil
		default:
			return nil, fmt.Errorf("unknown DependencyType %#v", dt)
		}
	}
}

// Validates that all required dependencies in t are defined in c.
func (t *Template) validateDependencies(c *TemplateContext) error {
	for _, dep := range t.Dependencies() {
		switch d := dep.(type) {
		case *ServiceDependency:
			if _, ok := c.Services[d.Key()]; !ok {
				return fmt.Errorf("templateContext missing service `%s'", d.Key())
			}
		case *KeyDependency:
			if _, ok := c.Keys[d.Key()]; !ok {
				return fmt.Errorf("templateContext missing key `%s'", d.Key())
			}
		case *KeyPrefixDependency:
			if _, ok := c.KeyPrefixes[d.Key()]; !ok {
				return fmt.Errorf("templateContext missing keyPrefix `%s'", d.Key())
			}
		default:
			return fmt.Errorf("unknown dependency type %#v", d)
		}
	}

	return nil
}

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

// TemplateContext is what Template uses to determine the values that are
// available for template parsing.
type TemplateContext struct {
	Services    map[string][]*Service
	Keys        map[string]string
	KeyPrefixes map[string][]*KeyPair
}

// GoString returns the detailed format of this object
func (c *TemplateContext) GoString() string {
	return fmt.Sprintf("*%#v", *c)
}

// Evaluator takes a DependencyType and returns a function which returns the
// value in the TemplateContext that corresponds to the requested item.
func (c *TemplateContext) Evaluator(dt DependencyType) func(string) (interface{}, error) {
	return func(s string) (interface{}, error) {
		switch dt {
		case DependencyTypeService:
			return c.Services[s], nil
		case DependencyTypeKey:
			return c.Keys[s], nil
		case DependencyTypeKeyPrefix:
			return c.KeyPrefixes[s], nil
		default:
			return nil, fmt.Errorf("unexpected DependencyType %#v", dt)
		}
	}
}

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

type Service struct {
	Node    string
	Address string
	ID      string
	Name    string
	Tags    []string
	Port    uint64
}

// GoString returns the detailed format of this object
func (s *Service) GoString() string {
	return fmt.Sprintf("*%#v", *s)
}

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

type KeyPair struct {
	Key   string
	Value string
}

// GoString returns the detailed format of this object
func (kp *KeyPair) GoString() string {
	return fmt.Sprintf("*%#v", *kp)
}

// DependencyType is an enum type that says the kind of the dependency.
type DependencyType byte

const (
	DependencyTypeInvalid DependencyType = iota
	DependencyTypeService
	DependencyTypeKey
	DependencyTypeKeyPrefix
)
<|endoftext|>


<commit_before>package game

import (
	"bufio"
	"encoding/gob"
	"fmt"
	"io"
	"log"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/rpg2d"
	"github.com/ghthor/engine/rpg2d/entity"
	"golang.org/x/net/websocket"
)

// Used to determine the next type that's in the
// buffer so we can decode it into a real value.
// We'll decode an encoded type and switch on its
// value so we'll have the correct value to decode
// into.
type EncodedType int

//go:generate stringer -type=EncodedType
const (
	ET_ERROR EncodedType = iota
	ET_DISCONNECT

	ET_REQ_LOGIN
	ET_REQ_CREATE

	ET_RESP_AUTH_FAILED
	ET_RESP_ACTOR_EXISTS
	ET_RESP_ACTOR_DOESNT_EXIST

	ET_RESP_LOGIN_SUCCESS
	ET_RESP_CREATE_SUCCESS

	ET_REQ_CONNECT
	ET_CONNECTED

	ET_WORLD_STATE
	ET_WORLD_STATE_DIFF

	ET_REQ_MOVE
	ET_REQ_USE
	ET_REQ_CHAT
)

type ReqLogin struct{ Name, Password string }
type ReqCreate struct{ Name, Password string }

type RespAuthFailed struct{ Name string }
type RespActorExists struct{ Name string }
type RespActorDoesntExist struct{ Name, Password string }

type RespLoginSuccess struct{ Name string }
type RespCreateSuccess struct{ Name string }

type ReqConnect struct{ Name string }

const RespDisconnect = "disconnected"

func init() {
	// Pre login Request/Response types
	gob.Register(ReqLogin{})
	gob.Register(ReqCreate{})

	gob.Register(RespAuthFailed{})
	gob.Register(RespActorExists{})
	gob.Register(RespActorDoesntExist{})

	gob.Register(RespLoginSuccess{})
	gob.Register(RespCreateSuccess{})

	// ActorEntityState used for response to connect
	gob.Register(ActorEntityState{})

	// Engine types
	gob.Register(rpg2d.WorldState{})
	gob.Register(rpg2d.WorldStateDiff{})
	gob.Register(rpg2d.TerrainMapState{})

	// Other entity states
	gob.Register(SayEntityState{})
	gob.Register(AssailEntityState{})

	// Cmd Requests. They have no responses.
	gob.Register(MoveRequest{})
	gob.Register(UseRequest{})
	gob.Register(ChatRequest{})
}

type GobConn interface {
	EncodeAndSend(EncodedType, interface{}) error
	ReadNextType() (EncodedType, error)
	Decode(interface{}) error
}

type gobConn struct {
	enc  *gob.Encoder
	wbuf *bufio.Writer

	*gob.Decoder
}

func (c gobConn) EncodeAndSend(t EncodedType, ev interface{}) error {
	err := c.enc.Encode(t)
	if err != nil {
		return err
	}

	err = c.enc.Encode(ev)
	if err != nil {
		return err
	}

	return c.wbuf.Flush()
}

func (c gobConn) ReadNextType() (t EncodedType, err error) {
	err = c.Decoder.Decode(&t)
	return
}

func NewGobConn(rw io.ReadWriter) GobConn {
	wbuf := bufio.NewWriter(rw)
	enc := gob.NewEncoder(wbuf)
	dec := gob.NewDecoder(rw)

	return gobConn{
		enc:  enc,
		wbuf: wbuf,

		Decoder: dec,
	}
}

type InputReceiver interface {
	SubmitMoveRequest(MoveRequest)
	SubmitUseRequest(UseRequest)
	SubmitChatRequest(ChatRequest)

	Close()
}

type serverConn struct {
	GobConn

	datastore datastore.Datastore

	newActor func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)
	actor    InputReceiver
}

type ActorConn interface {
	Run() error
}

type stateFn func() (stateFn, error)

func (c *serverConn) handleLogin() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	case ET_REQ_LOGIN:
		return c.handleLoginReq, nil
	case ET_REQ_CREATE:
		return c.handleCreateReq, nil

	default:
		log.Println("unexpected encoded type: ", eType)
	}

	return c.handleLogin, nil
}

func (c *serverConn) handleLoginReq() (stateFn, error) {
	var r ReqLogin
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_DOESNT_EXIST, RespActorDoesntExist{
			r.Name, r.Password,
		})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		err := c.EncodeAndSend(ET_RESP_AUTH_FAILED, RespAuthFailed{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	err = c.EncodeAndSend(ET_RESP_LOGIN_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return c.handleConnect(actor), nil
}

func (c *serverConn) handleCreateReq() (stateFn, error) {
	var r ReqCreate
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_EXISTS, RespActorExists{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		//      we should retry contacting the database a
		//      few times
		return nil, err
	}

	err = c.EncodeAndSend(ET_RESP_CREATE_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return c.handleConnect(actor), nil
}

func (c *serverConn) handleConnect(dsactor datastore.Actor) stateFn {
	var handleConnect stateFn

	handleConnect = func() (stateFn, error) {
		eType, err := c.ReadNextType()
		if err != nil {
			return nil, err
		}

		switch eType {
		default:
			// TODO Send a protocol error to the client
			return handleConnect, nil

		case ET_REQ_CONNECT:
		}

		var r ReqConnect
		err = c.Decode(&r)
		if err != nil {
			return nil, err
		}

		// TODO there is a potential race where the actor
		//      is connected to the server and the initial
		//      world state is sent over the connection before
		//      the ET_CONNECTED packet. Can be solved by deferring
		//      sending the ET_CONNECTED packet till the SendWorldState
		//      method is called and sending them both there in the
		//      correct order.
		actor, entity, initialState := c.connect(dsactor)
		c.actor = actor

		err = c.EncodeAndSend(ET_CONNECTED, entity)
		if err != nil {
			return nil, err
		}

		err = c.EncodeAndSend(ET_WORLD_STATE, <-initialState)
		if err != nil {
			return nil, err
		}

		return c.handleInputReq, nil
	}

	return handleConnect
}

type initialStateWriter struct {
	sendState chan<- rpg2d.WorldState
}

func (c initialStateWriter) WriteWorldState(s rpg2d.WorldState) StateWriter {
	c.sendState <- s

	// TODO Return a conncurent safe StateWriter
	return nil
}

func (c *serverConn) connect(dsactor datastore.Actor) (actor InputReceiver, entity entity.State, initialState <-chan rpg2d.WorldState) {
	initialStateCh := make(chan rpg2d.WorldState)
	actor, entity = c.newActor(dsactor, initialStateWriter{initialStateCh})
	return actor, entity, initialStateCh
}

func (c *serverConn) handleInputReq() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	default:
		return c.handleInputReq, fmt.Errorf("unexpected type %v when handling input", eType)

	case ET_REQ_MOVE:
		return c.handleMoveReq, nil
	case ET_REQ_USE:
		return c.handleUseReq, nil
	case ET_REQ_CHAT:
		return c.handleChatReq, nil
	}

	return c.handleInputReq, nil
}

func (c *serverConn) handleMoveReq() (stateFn, error) {
	var r MoveRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitMoveRequest(r)
	return c.handleInputReq, nil
}

func (c *serverConn) handleUseReq() (stateFn, error) {
	var r UseRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitUseRequest(r)
	return c.handleInputReq, nil
}

func (c *serverConn) handleChatReq() (stateFn, error) {
	var r ChatRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitChatRequest(r)
	return c.handleInputReq, nil
}

func (c serverConn) Run() (err error) {
	f := c.handleLogin
	for f != nil && err == nil {
		f, err = f()
	}

	if c.actor != nil {
		c.actor.Close()
	}

	return
}

func (c serverConn) WriteWorldStateDiff(s rpg2d.WorldStateDiff) error {
	return c.EncodeAndSend(ET_WORLD_STATE_DIFF, s)
}

func NewActorGobConn(
	rw io.ReadWriter,
	ds datastore.Datastore,
	newActor func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)) ActorConn {
	return serverConn{
		GobConn:   NewGobConn(rw),
		datastore: ds,
		newActor:  newActor,
	}
}

func newGobWebsocketHandler(
	ds datastore.Datastore,
	newActor func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)) websocket.Handler {
	return func(ws *websocket.Conn) {
		ws.PayloadType = websocket.BinaryFrame

		c := NewActorGobConn(ws, ds, newActor)

		// Blocks until the connection is disconnected
		err := c.Run()

		if err != nil {
			log.Printf("packet handler terminated: %v", err)
		}
	}
}
<commit_msg>[game] removed this todo which was solved in f09ab38<commit_after>package game

import (
	"bufio"
	"encoding/gob"
	"fmt"
	"io"
	"log"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/rpg2d"
	"github.com/ghthor/engine/rpg2d/entity"
	"golang.org/x/net/websocket"
)

// Used to determine the next type that's in the
// buffer so we can decode it into a real value.
// We'll decode an encoded type and switch on its
// value so we'll have the correct value to decode
// into.
type EncodedType int

//go:generate stringer -type=EncodedType
const (
	ET_ERROR EncodedType = iota
	ET_DISCONNECT

	ET_REQ_LOGIN
	ET_REQ_CREATE

	ET_RESP_AUTH_FAILED
	ET_RESP_ACTOR_EXISTS
	ET_RESP_ACTOR_DOESNT_EXIST

	ET_RESP_LOGIN_SUCCESS
	ET_RESP_CREATE_SUCCESS

	ET_REQ_CONNECT
	ET_CONNECTED

	ET_WORLD_STATE
	ET_WORLD_STATE_DIFF

	ET_REQ_MOVE
	ET_REQ_USE
	ET_REQ_CHAT
)

type ReqLogin struct{ Name, Password string }
type ReqCreate struct{ Name, Password string }

type RespAuthFailed struct{ Name string }
type RespActorExists struct{ Name string }
type RespActorDoesntExist struct{ Name, Password string }

type RespLoginSuccess struct{ Name string }
type RespCreateSuccess struct{ Name string }

type ReqConnect struct{ Name string }

const RespDisconnect = "disconnected"

func init() {
	// Pre login Request/Response types
	gob.Register(ReqLogin{})
	gob.Register(ReqCreate{})

	gob.Register(RespAuthFailed{})
	gob.Register(RespActorExists{})
	gob.Register(RespActorDoesntExist{})

	gob.Register(RespLoginSuccess{})
	gob.Register(RespCreateSuccess{})

	// ActorEntityState used for response to connect
	gob.Register(ActorEntityState{})

	// Engine types
	gob.Register(rpg2d.WorldState{})
	gob.Register(rpg2d.WorldStateDiff{})
	gob.Register(rpg2d.TerrainMapState{})

	// Other entity states
	gob.Register(SayEntityState{})
	gob.Register(AssailEntityState{})

	// Cmd Requests. They have no responses.
	gob.Register(MoveRequest{})
	gob.Register(UseRequest{})
	gob.Register(ChatRequest{})
}

type GobConn interface {
	EncodeAndSend(EncodedType, interface{}) error
	ReadNextType() (EncodedType, error)
	Decode(interface{}) error
}

type gobConn struct {
	enc  *gob.Encoder
	wbuf *bufio.Writer

	*gob.Decoder
}

func (c gobConn) EncodeAndSend(t EncodedType, ev interface{}) error {
	err := c.enc.Encode(t)
	if err != nil {
		return err
	}

	err = c.enc.Encode(ev)
	if err != nil {
		return err
	}

	return c.wbuf.Flush()
}

func (c gobConn) ReadNextType() (t EncodedType, err error) {
	err = c.Decoder.Decode(&t)
	return
}

func NewGobConn(rw io.ReadWriter) GobConn {
	wbuf := bufio.NewWriter(rw)
	enc := gob.NewEncoder(wbuf)
	dec := gob.NewDecoder(rw)

	return gobConn{
		enc:  enc,
		wbuf: wbuf,

		Decoder: dec,
	}
}

type InputReceiver interface {
	SubmitMoveRequest(MoveRequest)
	SubmitUseRequest(UseRequest)
	SubmitChatRequest(ChatRequest)

	Close()
}

type serverConn struct {
	GobConn

	datastore datastore.Datastore

	newActor func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)
	actor    InputReceiver
}

type ActorConn interface {
	Run() error
}

type stateFn func() (stateFn, error)

func (c *serverConn) handleLogin() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	case ET_REQ_LOGIN:
		return c.handleLoginReq, nil
	case ET_REQ_CREATE:
		return c.handleCreateReq, nil

	default:
		log.Println("unexpected encoded type: ", eType)
	}

	return c.handleLogin, nil
}

func (c *serverConn) handleLoginReq() (stateFn, error) {
	var r ReqLogin
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_DOESNT_EXIST, RespActorDoesntExist{
			r.Name, r.Password,
		})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		err := c.EncodeAndSend(ET_RESP_AUTH_FAILED, RespAuthFailed{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	err = c.EncodeAndSend(ET_RESP_LOGIN_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return c.handleConnect(actor), nil
}

func (c *serverConn) handleCreateReq() (stateFn, error) {
	var r ReqCreate
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		err := c.EncodeAndSend(ET_RESP_ACTOR_EXISTS, RespActorExists{r.Name})
		if err != nil {
			return nil, err
		}

		return c.handleLogin, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		//      we should retry contacting the database a
		//      few times
		return nil, err
	}

	err = c.EncodeAndSend(ET_RESP_CREATE_SUCCESS, RespLoginSuccess{actor.Name})
	if err != nil {
		return nil, err
	}

	return c.handleConnect(actor), nil
}

func (c *serverConn) handleConnect(dsactor datastore.Actor) stateFn {
	var handleConnect stateFn

	handleConnect = func() (stateFn, error) {
		eType, err := c.ReadNextType()
		if err != nil {
			return nil, err
		}

		switch eType {
		default:
			// TODO Send a protocol error to the client
			return handleConnect, nil

		case ET_REQ_CONNECT:
		}

		var r ReqConnect
		err = c.Decode(&r)
		if err != nil {
			return nil, err
		}

		actor, entity, initialState := c.connect(dsactor)
		c.actor = actor

		err = c.EncodeAndSend(ET_CONNECTED, entity)
		if err != nil {
			return nil, err
		}

		err = c.EncodeAndSend(ET_WORLD_STATE, <-initialState)
		if err != nil {
			return nil, err
		}

		return c.handleInputReq, nil
	}

	return handleConnect
}

type initialStateWriter struct {
	sendState chan<- rpg2d.WorldState
}

func (c initialStateWriter) WriteWorldState(s rpg2d.WorldState) StateWriter {
	c.sendState <- s

	// TODO Return a conncurent safe StateWriter
	return nil
}

func (c *serverConn) connect(dsactor datastore.Actor) (actor InputReceiver, entity entity.State, initialState <-chan rpg2d.WorldState) {
	initialStateCh := make(chan rpg2d.WorldState)
	actor, entity = c.newActor(dsactor, initialStateWriter{initialStateCh})
	return actor, entity, initialStateCh
}

func (c *serverConn) handleInputReq() (stateFn, error) {
	eType, err := c.ReadNextType()
	if err != nil {
		return nil, err
	}

	switch eType {
	default:
		return c.handleInputReq, fmt.Errorf("unexpected type %v when handling input", eType)

	case ET_REQ_MOVE:
		return c.handleMoveReq, nil
	case ET_REQ_USE:
		return c.handleUseReq, nil
	case ET_REQ_CHAT:
		return c.handleChatReq, nil
	}

	return c.handleInputReq, nil
}

func (c *serverConn) handleMoveReq() (stateFn, error) {
	var r MoveRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitMoveRequest(r)
	return c.handleInputReq, nil
}

func (c *serverConn) handleUseReq() (stateFn, error) {
	var r UseRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitUseRequest(r)
	return c.handleInputReq, nil
}

func (c *serverConn) handleChatReq() (stateFn, error) {
	var r ChatRequest
	err := c.Decode(&r)
	if err != nil {
		return nil, err
	}

	c.actor.SubmitChatRequest(r)
	return c.handleInputReq, nil
}

func (c serverConn) Run() (err error) {
	f := c.handleLogin
	for f != nil && err == nil {
		f, err = f()
	}

	if c.actor != nil {
		c.actor.Close()
	}

	return
}

func (c serverConn) WriteWorldStateDiff(s rpg2d.WorldStateDiff) error {
	return c.EncodeAndSend(ET_WORLD_STATE_DIFF, s)
}

func NewActorGobConn(
	rw io.ReadWriter,
	ds datastore.Datastore,
	newActor func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)) ActorConn {
	return serverConn{
		GobConn:   NewGobConn(rw),
		datastore: ds,
		newActor:  newActor,
	}
}

func newGobWebsocketHandler(
	ds datastore.Datastore,
	newActor func(datastore.Actor, InitialStateWriter) (InputReceiver, entity.State)) websocket.Handler {
	return func(ws *websocket.Conn) {
		ws.PayloadType = websocket.BinaryFrame

		c := NewActorGobConn(ws, ds, newActor)

		// Blocks until the connection is disconnected
		err := c.Run()

		if err != nil {
			log.Printf("packet handler terminated: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package idm

import (
	"strings"
	"testing"
)

func TestScanWhitespace(t *testing.T) {
	var tests = []struct {
		s   string
		tok Token
		lit string
	}{
		{s: ``, tok: Space, lit: "\x00"}, // a byte with the value 0
		{s: ` #`, tok: Space, lit: ` `},
		{s: `  `, tok: Space, lit: "  "},
		{s: "\t", tok: Space, lit: "\t"},
		{s: "\n", tok: Space, lit: "\n"},
		{s: "a", tok: Space, lit: "a"},
		{s: " a", tok: Space, lit: " "},
		{s: "   a", tok: Space, lit: "   "},
		{s: "a ", tok: Space, lit: "a "},
		{s: "a  ", tok: Space, lit: "a  "},
		{s: "a  = b", tok: Space, lit: "a  "},
		{s: "  a  ", tok: Space, lit: "  "},
	}

	for i, tt := range tests {
		s := NewScanner(strings.NewReader(tt.s))

		tok, lit := s.scanWhitespace()
		if tt.tok != tok {
			t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit)
		} else if tt.lit != lit {
			t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit)
		}
	}

}

func TestScanIdentifier(t *testing.T) {
	var tests = []struct {
		s   string
		tok Token
		lit string
	}{
		{s: ``, tok: Identifier, lit: "\x00"}, // a byte with the value 0
		{s: ` #`, tok: Identifier, lit: ` `},
		{s: `  `, tok: Identifier, lit: " "},
		{s: "\t", tok: Identifier, lit: "\t"},
		{s: "\n", tok: Identifier, lit: "\n"},
		{s: "a", tok: Identifier, lit: "a"},
		{s: " a", tok: Identifier, lit: " a"},
		{s: "   a", tok: Identifier, lit: " "},
		{s: "a ", tok: Identifier, lit: "a"},
		{s: "a  ", tok: Identifier, lit: "a"},
		{s: "a  = b", tok: Identifier, lit: "a"},
		{s: "  a  ", tok: Identifier, lit: " "},
		{s: "aaa  ", tok: Identifier, lit: "aaa"},
		{s: " aaa", tok: Identifier, lit: " aaa"},
		{s: "ab123", tok: Identifier, lit: "ab123"},
		{s: "ab_123", tok: Identifier, lit: "ab_123"},
	}

	for i, tt := range tests {
		s := NewScanner(strings.NewReader(tt.s))

		tok, lit := s.scanIdentifier()
		if tt.tok != tok {
			t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit)
		} else if tt.lit != lit {
			t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit)
		}
	}
}

func TestIsWhitespace(t *testing.T) {
	var tests = []struct {
		r        rune
		expected bool
	}{
		{r: ' ', expected: true},
		{r: '\t', expected: true},
		{r: '\n', expected: true},
		{r: 'a', expected: false},
	}
	for i, tt := range tests {
		got := isWhitespace(tt.r)
		if got != tt.expected {
			t.Errorf("%d. %t expected, got %t", i, tt.expected, got)
		}
	}
}

func TestIsLetter(t *testing.T) {
	var tests = []struct {
		r        rune
		expected bool
	}{
		{r: ' ', expected: false},
		{r: '\t', expected: false},
		{r: '0', expected: false},
		{r: 'a', expected: true},
		{r: 'A', expected: true},
	}
	for i, tt := range tests {
		got := isLetter(tt.r)
		if got != tt.expected {
			t.Errorf("%d. %t expected, got %t", i, tt.expected, got)
		}
	}
}

func TestIsDigit(t *testing.T) {
	var tests = []struct {
		r        rune
		expected bool
	}{
		{r: ' ', expected: false},
		{r: '\t', expected: false},
		{r: '0', expected: true},
		{r: 'a', expected: false},
		{r: 'A', expected: false},
	}
	for i, tt := range tests {
		got := isDigit(tt.r)
		if got != tt.expected {
			t.Errorf("%d. %t expected, got %t", i, tt.expected, got)
		}
	}
}
<commit_msg>Add Scan test<commit_after>package idm

import (
	"strings"
	"testing"
)

func TestScan(t *testing.T) {
	var tests = []struct {
		s   string
		tok Token
		lit string
	}{
		{s: ``, tok: EOF},
		{s: `#`, tok: Error, lit: `#`},
		{s: ` `, tok: Space, lit: ` `},
		{s: "\t", tok: Space, lit: "\t"},
		{s: "\n", tok: Space, lit: "\n"},
		{s: `*`, tok: Asterix, lit: `*`},
		{s: `=`, tok: Assign, lit: `=`},
		{s: `a`, tok: Identifier, lit: `a`},
		{s: `a42`, tok: Identifier, lit: `a42`},
		{s: `a_42`, tok: Identifier, lit: `a_42`},
	}
	for i, tt := range tests {
		s := NewScanner(strings.NewReader(tt.s))
		tok, lit := s.Scan()
		if tt.tok != tok {
			t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit)
		} else if tt.lit != lit {
			t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit)
		}
	}
}

func TestScanWhitespace(t *testing.T) {
	var tests = []struct {
		s   string
		tok Token
		lit string
	}{
		{s: ``, tok: Space, lit: "\x00"}, // a byte with the value 0
		{s: ` #`, tok: Space, lit: ` `},
		{s: `  `, tok: Space, lit: "  "},
		{s: "\t", tok: Space, lit: "\t"},
		{s: "\n", tok: Space, lit: "\n"},
		{s: "a", tok: Space, lit: "a"},
		{s: " a", tok: Space, lit: " "},
		{s: "   a", tok: Space, lit: "   "},
		{s: "a ", tok: Space, lit: "a "},
		{s: "a  ", tok: Space, lit: "a  "},
		{s: "a  = b", tok: Space, lit: "a  "},
		{s: "  a  ", tok: Space, lit: "  "},
	}

	for i, tt := range tests {
		s := NewScanner(strings.NewReader(tt.s))

		tok, lit := s.scanWhitespace()
		if tt.tok != tok {
			t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit)
		} else if tt.lit != lit {
			t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit)
		}
	}

}

func TestScanIdentifier(t *testing.T) {
	var tests = []struct {
		s   string
		tok Token
		lit string
	}{
		{s: ``, tok: Identifier, lit: "\x00"}, // a byte with the value 0
		{s: ` #`, tok: Identifier, lit: ` `},
		{s: `  `, tok: Identifier, lit: " "},
		{s: "\t", tok: Identifier, lit: "\t"},
		{s: "\n", tok: Identifier, lit: "\n"},
		{s: "a", tok: Identifier, lit: "a"},
		{s: " a", tok: Identifier, lit: " a"},
		{s: "   a", tok: Identifier, lit: " "},
		{s: "a ", tok: Identifier, lit: "a"},
		{s: "a  ", tok: Identifier, lit: "a"},
		{s: "a  = b", tok: Identifier, lit: "a"},
		{s: "  a  ", tok: Identifier, lit: " "},
		{s: "aaa  ", tok: Identifier, lit: "aaa"},
		{s: " aaa", tok: Identifier, lit: " aaa"},
		{s: "ab123", tok: Identifier, lit: "ab123"},
		{s: "ab_123", tok: Identifier, lit: "ab_123"},
	}

	for i, tt := range tests {
		s := NewScanner(strings.NewReader(tt.s))

		tok, lit := s.scanIdentifier()
		if tt.tok != tok {
			t.Errorf("%d. %q token mismatch: exp=%q got=%q <%q>", i, tt.s, tt.tok, tok, lit)
		} else if tt.lit != lit {
			t.Errorf("%d. %q literal mismatch: exp=%q got=%q", i, tt.s, tt.lit, lit)
		}
	}
}

func TestIsWhitespace(t *testing.T) {
	var tests = []struct {
		r        rune
		expected bool
	}{
		{r: ' ', expected: true},
		{r: '\t', expected: true},
		{r: '\n', expected: true},
		{r: 'a', expected: false},
	}
	for i, tt := range tests {
		got := isWhitespace(tt.r)
		if got != tt.expected {
			t.Errorf("%d. %t expected, got %t", i, tt.expected, got)
		}
	}
}

func TestIsLetter(t *testing.T) {
	var tests = []struct {
		r        rune
		expected bool
	}{
		{r: ' ', expected: false},
		{r: '\t', expected: false},
		{r: '0', expected: false},
		{r: 'a', expected: true},
		{r: 'A', expected: true},
	}
	for i, tt := range tests {
		got := isLetter(tt.r)
		if got != tt.expected {
			t.Errorf("%d. %t expected, got %t", i, tt.expected, got)
		}
	}
}

func TestIsDigit(t *testing.T) {
	var tests = []struct {
		r        rune
		expected bool
	}{
		{r: ' ', expected: false},
		{r: '\t', expected: false},
		{r: '0', expected: true},
		{r: 'a', expected: false},
		{r: 'A', expected: false},
	}
	for i, tt := range tests {
		got := isDigit(tt.r)
		if got != tt.expected {
			t.Errorf("%d. %t expected, got %t", i, tt.expected, got)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
)

const (
	MAXWORDLEN = 1
)

type PSeq struct {
	words    []*Word
	position int
	previous *Phrase
	next     *Phrase
	context  int
}

type WSeq struct {
	characters []*Character
	position   int
	previous   *Word
	next       *Word
	phrase     *Phrase
}

type CSeq struct {
	position int
	previous *Character
	word     *Word
}

type CSeqs []*CSeq

type CStat struct {
	char  *Character
	count int
}

type CStats []*CStat

type Character struct {
	value rune
	seqs  CSeqs
	next  [MAXWORDLEN]CStats
	count int
}

type Characters []*Character

type Word struct {
	chars Characters
	seqs  []*WSeq
}

type Words []*Word

type Phrase struct {
	words []*Word
	seqs  []*PSeq
}

type Phrases []*Phrase

type Current struct {
	value   string
	pChar   *Character
	pWord   *Word
	pPhrase *Phrase
}

var (
	chars   Characters
	words   Words
	phrases Phrases

	cposition = 0
	pChar     *Character
	ipCSeq    int = -1

	wposition = 0
	pWord     *Word
	cWord     *Word
	ipWSeq    int = -1

	pPhrase *Phrase
	cPhrase *Phrase
	ipPSeq  int = -1

	cQueue    Characters = make(Characters, 0, MAXWORDLEN)
	totalChar int        = 0

	debug *bool = flag.Bool("d", false, "Debugging")
)

func main() {

	flag.Parse()
	fmt.Println(*debug)
	args := flag.Args()
	if len(args) < 1 {
		panic("Missing filename")
		os.Exit(1)
	}
	filename := args[0]
	scan(filename)
}

func scan(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	eof := false
	for !eof {
		char, size, err := reader.ReadRune()
		if err != nil {
			if err != io.EOF {
				panic(err)
			}
			eof = true
		}
		if size > 0 {
			_, pChar, err = searchCSeq(char)
		}
	}
	fmt.Println()
	showStats()
}

func (char *Character) addNext(nextChar *Character, position int) {
	found := false
	for _, cstat := range char.next[position] {
		if cstat.char == nextChar {
			cstat.count++
			found = true
			break
		}
	}
	if !found {
		if char.next[position] == nil {
			char.next[position] = make(CStats, 0)
		}
		char.next[position] = append(char.next[position], &CStat{
			char:  nextChar,
			count: 1,
		})
	}
}

func searchCSeq(char rune) (cseq *CSeq, cchar *Character, err error) {
	// search char
	for _, schar := range chars {
		if schar.value == char {
			schar.count++
			//logf("Found char %c\n", char)
			found := false
			for _, cseq := range schar.seqs {
				if cseq.previous == pChar {
					fmt.Printf("\x1b[30m%c\x1b[0m", char)
					found = true
					cseq.previous.addNext(schar, 0)
					break
				}
			}
			if !found {
				fmt.Printf("\x1b[31m%c\x1b[0m", char)
				cseq := CSeq{
					position: cposition,
					previous: pChar,
					word:     nil,
				}
				cseq.previous.addNext(schar, 0)
				schar.seqs = append(schar.seqs, &cseq)
			}
			cposition++
			return cseq, schar, nil
		}
	}
	fmt.Printf("\x1b[32m%c\x1b[0m", char)
	cseq = &CSeq{
		position: cposition,
		previous: pChar,
		word:     nil,
	}
	schar := &Character{
		value: char,
		seqs: CSeqs{
			cseq,
		},
		count: 1,
	}
	if pChar != nil {
		cseq.previous.addNext(schar, 0)
	}
	chars = append(chars, schar)
	logf("Added char %c [%d]\n", char, len(chars))
	cposition++
	return cseq, schar, nil
}

func (char *Character) String() string {
	if char == nil {
		return "ø"
	}
	return fmt.Sprintf("Char %c [%d] Previous: %s Next: %s\n", char.value, len(char.seqs), char.seqs, char.next)
}

func (cseq *CSeq) String() string {
	previous := 'ø'
	if cseq.previous != nil {
		previous = cseq.previous.value
	}
	return fmt.Sprintf("%c", previous)
}

func (word *Word) String() (result string) {
	if word == nil {
		return "ø"
	}
	for _, char := range word.chars {
		result = fmt.Sprintf("%s%c", result, char.value)
	}
	return result
}

func logf(fmt string, v ...interface{}) {
	if *debug {
		log.Printf(fmt, v...)
	}
}

func (cstat *CStat) String() string {
	if cstat == nil {
		return "."
	}
	return fmt.Sprintf("%0X-%c:%d", cstat.char.value, cstat.char.value, cstat.count)
}

// CSeqs sorting
func (cseqs CSeqs) Less(i, j int) bool {
	switch {
	case cseqs[i].previous == nil && cseqs[j].previous != nil:
		return true
	case cseqs[i].previous == nil || cseqs[j].previous == nil:
		return false
	}
	return cseqs[i].previous.value < cseqs[j].previous.value
}
func (cseqs CSeqs) Swap(i, j int) {
	cseqs[i], cseqs[j] = cseqs[j], cseqs[i]
}
func (cseqs CSeqs) Len() int {
	return len(cseqs)
}

// Characters sorting
func (chars Characters) Less(i, j int) bool {
	return chars[i].value < chars[j].value
}
func (chars Characters) Swap(i, j int) {
	chars[i], chars[j] = chars[j], chars[i]
}
func (chars Characters) Len() int {
	return len(chars)
}

// Characters sorting by count
type CharactersByCount Characters

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

// Stats sorting
func (stats CStats) Less(i, j int) bool {
	return stats[i].char.value < stats[j].char.value
}
func (stats CStats) Swap(i, j int) {
	stats[i], stats[j] = stats[j], stats[i]
}
func (stats CStats) Len() int {
	return len(stats)
}

// sequence frequency sorting
type BySeqFrequency Characters

func (chars BySeqFrequency) Len() int {
	return len(chars)
}
func (chars BySeqFrequency) Less(i, j int) bool {
	return len(chars[i].next[0])+len(chars[i].seqs) < len(chars[j].next[0])+len(chars[j].seqs)
}
func (chars BySeqFrequency) Swap(i, j int) {
	chars[i], chars[j] = chars[j], chars[i]
}

func showStats() {
	sort.Sort(chars)
	for _, char := range chars {
		fmt.Printf("%c[%0x]-(%d)", char.value, char.value,char.count)
		sort.Sort(char.seqs)
		fmt.Printf(" -> %s", char.seqs)
		sort.Sort(sort.Reverse(char.next[0]))
		fmt.Printf(" -> %s\n", char.next[0])
	}
	fmt.Println("Sorting by Sequence Frequency")
	sort.Sort(sort.Reverse(BySeqFrequency(chars)))
	for _, char := range chars {
		fmt.Printf("[%0X:%c-%d]\n", char.value, char.value, len(char.next[0])+len(char.seqs))
	}
	fmt.Printf("SPACE is '%c'\n", chars[0].value)
	fmt.Println("Sorting by Appearance Frequency")
	sort.Sort(sort.Reverse(CharactersByCount(chars)))
	for _, char := range chars {
		fmt.Printf("%c[%0x]: %d\n", char.value, char.value, char.count)
	}
}
<commit_msg>Splitting word<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
)

const (
	MAXWORDLEN = 1
)

type PSeq struct {
	words    []*Word
	position int
	previous *Phrase
	next     *Phrase
	context  int
}

type WSeq struct {
	characters []*Character
	position   int
	previous   *Word
	next       *Word
	phrase     *Phrase
}

type CSeq struct {
	position int
	previous *Character
	word     *Word
}

type CSeqs []*CSeq

type CStat struct {
	char  *Character
	count int
}

type CStats []*CStat

type Character struct {
	value rune
	seqs  CSeqs
	next  [MAXWORDLEN]CStats
	count int
}

type Characters []*Character

type Word struct {
	chars Characters
	seqs  []*WSeq
}

type Words []*Word

type Phrase struct {
	words []*Word
	seqs  []*PSeq
}

type Phrases []*Phrase

type Current struct {
	value   string
	pChar   *Character
	pWord   *Word
	pPhrase *Phrase
}

var (
	Chars Characters

	chars   Characters
	words   Words
	phrases Phrases

	cposition = 0
	pChar     *Character
	ipCSeq    int = -1

	wposition = 0
	pWord     *Word
	cWord     *Word
	ipWSeq    int = -1

	pPhrase *Phrase
	cPhrase *Phrase
	ipPSeq  int = -1

	//cQueue    Characters = make(Characters, 0, MAXWORDLEN)
	//totalChar int        = 0
	space *Character

	debug *bool = flag.Bool("d", false, "Debugging")
)

func main() {

	flag.Parse()
	fmt.Println(*debug)
	args := flag.Args()
	if len(args) < 1 {
		panic("Missing filename")
		os.Exit(1)
	}
	filename := args[0]
	scan(filename)
	fmt.Println()
	showStats()
	pass2()
}

func scan(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	eof := false
	for !eof {
		char, size, err := reader.ReadRune()
		if err != nil {
			if err != io.EOF {
				panic(err)
			}
			eof = true
		}
		if size > 0 {
			_, pChar, err = searchCSeq(char)
			Chars = append(Chars, pChar)
		}
	}
}

func (char *Character) addNext(nextChar *Character, position int) {
	found := false
	for _, cstat := range char.next[position] {
		if cstat.char == nextChar {
			cstat.count++
			found = true
			break
		}
	}
	if !found {
		if char.next[position] == nil {
			char.next[position] = make(CStats, 0)
		}
		char.next[position] = append(char.next[position], &CStat{
			char:  nextChar,
			count: 1,
		})
	}
}

func searchCSeq(char rune) (cseq *CSeq, cchar *Character, err error) {
	// search char
	for _, schar := range chars {
		if schar.value == char {
			schar.count++
			//logf("Found char %c\n", char)
			found := false
			for _, cseq := range schar.seqs {
				if cseq.previous == pChar {
					fmt.Printf("\x1b[30m%c\x1b[0m", char)
					found = true
					cseq.previous.addNext(schar, 0)
					break
				}
			}
			if !found {
				fmt.Printf("\x1b[31m%c\x1b[0m", char)
				cseq := CSeq{
					position: cposition,
					previous: pChar,
					word:     nil,
				}
				cseq.previous.addNext(schar, 0)
				schar.seqs = append(schar.seqs, &cseq)
			}
			cposition++
			return cseq, schar, nil
		}
	}
	fmt.Printf("\x1b[32m%c\x1b[0m", char)
	cseq = &CSeq{
		position: cposition,
		previous: pChar,
		word:     nil,
	}
	schar := &Character{
		value: char,
		seqs: CSeqs{
			cseq,
		},
		count: 1,
	}
	if pChar != nil {
		cseq.previous.addNext(schar, 0)
	}
	chars = append(chars, schar)
	logf("Added char %c [%d]\n", char, len(chars))
	cposition++
	return cseq, schar, nil
}

func (char *Character) String() string {
	if char == nil {
		return "ø"
	}
	return fmt.Sprintf("Char %c [%d] Previous: %s Next: %s\n", char.value, len(char.seqs), char.seqs, char.next)
}

func (cseq *CSeq) String() string {
	previous := 'ø'
	if cseq.previous != nil {
		previous = cseq.previous.value
	}
	return fmt.Sprintf("%c", previous)
}

func (word *Word) String() (result string) {
	if word == nil {
		return "ø"
	}
	for _, char := range word.chars {
		result = fmt.Sprintf("%s%c", result, char.value)
	}
	return result
}

func logf(fmt string, v ...interface{}) {
	if *debug {
		log.Printf(fmt, v...)
	}
}

func (cstat *CStat) String() string {
	if cstat == nil {
		return "."
	}
	return fmt.Sprintf("%0X-%c:%d", cstat.char.value, cstat.char.value, cstat.count)
}

// CSeqs sorting
func (cseqs CSeqs) Less(i, j int) bool {
	switch {
	case cseqs[i].previous == nil && cseqs[j].previous != nil:
		return true
	case cseqs[i].previous == nil || cseqs[j].previous == nil:
		return false
	}
	return cseqs[i].previous.value < cseqs[j].previous.value
}
func (cseqs CSeqs) Swap(i, j int) {
	cseqs[i], cseqs[j] = cseqs[j], cseqs[i]
}
func (cseqs CSeqs) Len() int {
	return len(cseqs)
}

// Characters sorting
func (chars Characters) Less(i, j int) bool {
	return chars[i].value < chars[j].value
}
func (chars Characters) Swap(i, j int) {
	chars[i], chars[j] = chars[j], chars[i]
}
func (chars Characters) Len() int {
	return len(chars)
}

// Characters sorting by count
type CharactersByCount Characters

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

// Stats sorting
func (stats CStats) Less(i, j int) bool {
	return stats[i].char.value < stats[j].char.value
}
func (stats CStats) Swap(i, j int) {
	stats[i], stats[j] = stats[j], stats[i]
}
func (stats CStats) Len() int {
	return len(stats)
}

// sequence frequency sorting
type BySeqFrequency Characters

func (chars BySeqFrequency) Len() int {
	return len(chars)
}
func (chars BySeqFrequency) Less(i, j int) bool {
	return len(chars[i].next[0])+len(chars[i].seqs) < len(chars[j].next[0])+len(chars[j].seqs)
}
func (chars BySeqFrequency) Swap(i, j int) {
	chars[i], chars[j] = chars[j], chars[i]
}

func showStats() {
	sort.Sort(chars)
	for _, char := range chars {
		fmt.Printf("%c[%0x]-(%d)", char.value, char.value, char.count)
		sort.Sort(char.seqs)
		fmt.Printf(" -> %s", char.seqs)
		sort.Sort(sort.Reverse(char.next[0]))
		fmt.Printf(" -> %s\n", char.next[0])
	}
	fmt.Println("Sorting by Sequence Frequency")
	sort.Sort(sort.Reverse(BySeqFrequency(chars)))
	for _, char := range chars {
		fmt.Printf("[%0X:%c-%d]\n", char.value, char.value, len(char.next[0])+len(char.seqs))
	}
	space = chars[0]
	fmt.Printf("SPACE is '%c'\n", space.value)
	fmt.Println("Sorting by Appearance Frequency")
	sort.Sort(sort.Reverse(CharactersByCount(chars)))
	for _, char := range chars {
		fmt.Printf("%c[%0x]: %d\n", char.value, char.value, char.count)
	}
}

func pass2() {
	logf("Pass2: words\n")
	//var buffer Characters
	//var pWord Word
	position := 0
	for i, char := range Chars {
		fmt.Printf("%c", char.value)
		if char.value == space.value && position < i {
			word := &Word{
				chars: Chars[position:i],
			}
			position = i + 1
			words = append(words, word)
			logf("Word: [%s]\n", word)
		}
	}
	if position < len(Chars) {
		word := &Word{
			chars: Chars[position:],
		}
		words = append(words, word)
		logf("Word: [%s]\n", word)

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


<commit_before>package sched

import (
	"bytes"
	"log"
	"time"

	"github.com/StackExchange/bosun/conf"
	"github.com/StackExchange/bosun/expr"
)

// Poll dispatches notification checks when needed.
func (s *Schedule) Poll() {
	var timeout time.Duration = time.Hour
	for {
		// Wait for one of these two.
		select {
		case <-time.After(timeout):
		case <-s.nc:
		}
		timeout = s.CheckNotifications()
		s.Save()
	}
}

func (s *Schedule) Notify(st *State, n *conf.Notification) {
	if s.notifications == nil {
		s.notifications = make(map[*conf.Notification][]*State)
	}
	s.notifications[n] = append(s.notifications[n], st)
}

// CheckNotifications processes past notification events. It returns the
// duration until the soonest notification triggers.
func (s *Schedule) CheckNotifications() time.Duration {
	s.Lock()
	defer s.Unlock()
	timeout := time.Hour
	notifications := s.Notifications
	s.Notifications = nil
	for ak, ns := range notifications {
		for name, t := range ns {
			n, present := s.Conf.Notifications[name]
			if !present {
				continue
			}
			remaining := t.Add(n.Timeout).Sub(time.Now())
			if remaining > 0 {
				if remaining < timeout {
					timeout = remaining
				}
				s.AddNotification(ak, n, t)
				continue
			}
			st := s.status[ak]
			if st == nil {
				continue
			}
			s.Notify(st, n)
			if n.Timeout < timeout {
				timeout = n.Timeout
			}
		}
	}
	s.sendNotifications()
	s.notifications = nil
	return timeout
}

func (s *Schedule) sendNotifications() {
	if s.Conf.Quiet {
		return
	}
	for n, states := range s.notifications {
		ustates := make(States)
		for _, st := range states {
			if st.Last().Status == StUnknown {
				ustates[st.AlertKey()] = st
			} else {
				s.notify(st, n)
			}
			if n.Next != nil {
				s.AddNotification(st.AlertKey(), n, time.Now().UTC())
			}
		}
		for name, group := range ustates.GroupSets() {
			s.unotify(name, group, n)
		}
	}
}

func (s *Schedule) notify(st *State, n *conf.Notification) {
	a := s.Conf.Alerts[st.Alert]
	subject := new(bytes.Buffer)
	if err := s.ExecuteSubject(subject, a, st); err != nil {
		log.Println(err)
		subject = bytes.NewBufferString(err.Error())
	}
	body := new(bytes.Buffer)
	attachments, err := s.ExecuteBody(body, a, st, true)
	if err != nil {
		log.Println(err)
		body = bytes.NewBufferString(err.Error())
	}
	n.Notify(subject.Bytes(), body.Bytes(), s.Conf, string(st.AlertKey()), attachments...)
}

func (s *Schedule) unotify(name string, group expr.AlertKeys, n *conf.Notification) {
	subject := new(bytes.Buffer)
	body := new(bytes.Buffer)
	now := time.Now().UTC()
	s.Group[now] = group
	if t := s.Conf.UnknownTemplate; t != nil {
		data := s.unknownData(now, name, group)
		if t.Body != nil {
			if err := t.Body.Execute(body, &data); err != nil {
				log.Println("unknown template error:", err)
			}
		}
		if t.Subject != nil {
			if err := t.Subject.Execute(subject, &data); err != nil {
				log.Println("unknown template error:", err)
			}
		}
	}
	n.Notify(subject.Bytes(), body.Bytes(), s.Conf, name)
}

func (s *Schedule) AddNotification(ak expr.AlertKey, n *conf.Notification, started time.Time) {
	if s.Notifications == nil {
		s.Notifications = make(map[expr.AlertKey]map[string]time.Time)
	}
	if s.Notifications[ak] == nil {
		s.Notifications[ak] = make(map[string]time.Time)
	}
	s.Notifications[ak][n.Name] = started
}
<commit_msg>Only request ak once<commit_after>package sched

import (
	"bytes"
	"log"
	"time"

	"github.com/StackExchange/bosun/conf"
	"github.com/StackExchange/bosun/expr"
)

// Poll dispatches notification checks when needed.
func (s *Schedule) Poll() {
	var timeout time.Duration = time.Hour
	for {
		// Wait for one of these two.
		select {
		case <-time.After(timeout):
		case <-s.nc:
		}
		timeout = s.CheckNotifications()
		s.Save()
	}
}

func (s *Schedule) Notify(st *State, n *conf.Notification) {
	if s.notifications == nil {
		s.notifications = make(map[*conf.Notification][]*State)
	}
	s.notifications[n] = append(s.notifications[n], st)
}

// CheckNotifications processes past notification events. It returns the
// duration until the soonest notification triggers.
func (s *Schedule) CheckNotifications() time.Duration {
	s.Lock()
	defer s.Unlock()
	timeout := time.Hour
	notifications := s.Notifications
	s.Notifications = nil
	for ak, ns := range notifications {
		for name, t := range ns {
			n, present := s.Conf.Notifications[name]
			if !present {
				continue
			}
			remaining := t.Add(n.Timeout).Sub(time.Now())
			if remaining > 0 {
				if remaining < timeout {
					timeout = remaining
				}
				s.AddNotification(ak, n, t)
				continue
			}
			st := s.status[ak]
			if st == nil {
				continue
			}
			s.Notify(st, n)
			if n.Timeout < timeout {
				timeout = n.Timeout
			}
		}
	}
	s.sendNotifications()
	s.notifications = nil
	return timeout
}

func (s *Schedule) sendNotifications() {
	if s.Conf.Quiet {
		return
	}
	for n, states := range s.notifications {
		ustates := make(States)
		for _, st := range states {
			ak := st.AlertKey()
			if st.Last().Status == StUnknown {
				ustates[ak] = st
			} else {
				s.notify(st, n)
			}
			if n.Next != nil {
				s.AddNotification(ak, n, time.Now().UTC())
			}
		}
		for name, group := range ustates.GroupSets() {
			s.unotify(name, group, n)
		}
	}
}

func (s *Schedule) notify(st *State, n *conf.Notification) {
	a := s.Conf.Alerts[st.Alert]
	subject := new(bytes.Buffer)
	if err := s.ExecuteSubject(subject, a, st); err != nil {
		log.Println(err)
		subject = bytes.NewBufferString(err.Error())
	}
	body := new(bytes.Buffer)
	attachments, err := s.ExecuteBody(body, a, st, true)
	if err != nil {
		log.Println(err)
		body = bytes.NewBufferString(err.Error())
	}
	n.Notify(subject.Bytes(), body.Bytes(), s.Conf, string(st.AlertKey()), attachments...)
}

func (s *Schedule) unotify(name string, group expr.AlertKeys, n *conf.Notification) {
	subject := new(bytes.Buffer)
	body := new(bytes.Buffer)
	now := time.Now().UTC()
	s.Group[now] = group
	if t := s.Conf.UnknownTemplate; t != nil {
		data := s.unknownData(now, name, group)
		if t.Body != nil {
			if err := t.Body.Execute(body, &data); err != nil {
				log.Println("unknown template error:", err)
			}
		}
		if t.Subject != nil {
			if err := t.Subject.Execute(subject, &data); err != nil {
				log.Println("unknown template error:", err)
			}
		}
	}
	n.Notify(subject.Bytes(), body.Bytes(), s.Conf, name)
}

func (s *Schedule) AddNotification(ak expr.AlertKey, n *conf.Notification, started time.Time) {
	if s.Notifications == nil {
		s.Notifications = make(map[expr.AlertKey]map[string]time.Time)
	}
	if s.Notifications[ak] == nil {
		s.Notifications[ak] = make(map[string]time.Time)
	}
	s.Notifications[ak][n.Name] = started
}
<|endoftext|>


<commit_before>// +build !windows

// This file contains a simple and incomplete implementation of the terminfo
// database. Information was taken from the ncurses manpages term(5) and
// terminfo(5). Currently, only the string capabilities for special keys and for
// functions without parameters are actually used. Colors are still done with
// ANSI escape sequences. Other special features that are not (yet?) supported
// are reading from ~/.terminfo, the TERMINFO_DIRS variable, Berkeley database
// format and extended capabilities.

package keyboard

import (
    "bytes"
    "encoding/binary"
    "os"
    "errors"
    "strings"
    "io/ioutil"
    "encoding/hex"
)

const (
    ti_header_length = 12
)

var (
    eterm_keys = []string{
        "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
    }
    screen_keys = []string{
        "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC",
    }
    xterm_keys = []string{
        "\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[H", "\x1b[F", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
    }
    rxvt_keys = []string{
        "\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
    }
    linux_keys = []string{
        "\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
    }

    terms = []struct {
        name  string
        keys  []string
    }{
        {"Eterm", eterm_keys},
        {"screen", screen_keys},
        {"xterm", xterm_keys},
        {"xterm-256color", xterm_keys},
        {"rxvt-unicode", rxvt_keys},
        {"rxvt-256color", rxvt_keys},
        {"linux", linux_keys},
    }
)

func load_terminfo() ([]byte, error) {
    var data []byte
    var err error

    term := os.Getenv("TERM")
    if term == "" {
        return nil, errors.New("termbox: TERM not set")
    }

    // The following behaviour follows the one described in terminfo(5) as
    // distributed by ncurses.

    terminfo := os.Getenv("TERMINFO")
    if terminfo != "" {
        // if TERMINFO is set, no other directory should be searched
        return ti_try_path(terminfo)
    }

    // next, consider ~/.terminfo
    home := os.Getenv("HOME")
    if home != "" {
        data, err = ti_try_path(home + "/.terminfo")
        if err == nil {
            return data, nil
        }
    }

    // next, TERMINFO_DIRS
    dirs := os.Getenv("TERMINFO_DIRS")
    if dirs != "" {
        for _, dir := range strings.Split(dirs, ":") {
            if dir == "" {
                // "" -> "/usr/share/terminfo"
                dir = "/usr/share/terminfo"
            }
            data, err = ti_try_path(dir)
            if err == nil {
                return data, nil
            }
        }
    }

    // fall back to /usr/share/terminfo
    return ti_try_path("/usr/share/terminfo")
}

func ti_try_path(path string) (data []byte, err error) {
    // load_terminfo already made sure it is set
    term := os.Getenv("TERM")

    // first try, the typical *nix path
    terminfo := path + "/" + term[0:1] + "/" + term
    data, err = ioutil.ReadFile(terminfo)
    if err == nil {
        return
    }

    // fallback to darwin specific dirs structure
    terminfo = path + "/" + hex.EncodeToString([]byte(term[:1])) + "/" + term
    data, err = ioutil.ReadFile(terminfo)
    return
}

func setup_term_builtin() error {
    name := os.Getenv("TERM")
    if name == "" {
        return errors.New("termbox: TERM environment variable not set")
    }

    for _, t := range terms {
        if t.name == name {
            keys = t.keys
            return nil
        }
    }

    compat_table := []struct {
        partial string
        keys    []string
    }{
        {"xterm", xterm_keys},
        {"rxvt", rxvt_keys},
        {"linux", linux_keys},
        {"Eterm", eterm_keys},
        {"screen", screen_keys},
        // let's assume that 'cygwin' is xterm compatible
        {"cygwin", xterm_keys},
        {"st", xterm_keys},
    }

    // try compatibility variants
    for _, it := range compat_table {
        if strings.Contains(name, it.partial) {
            keys = it.keys
            return nil
        }
    }

    return errors.New("termbox: unsupported terminal")
}

func ti_read_string(rd *bytes.Reader, str_off, table int16) (string, error) {
    var off int16

    _, err := rd.Seek(int64(str_off), 0)
    if err != nil {
        return "", err
    }
    err = binary.Read(rd, binary.LittleEndian, &off)
    if err != nil {
        return "", err
    }
    _, err = rd.Seek(int64(table+off), 0)
    if err != nil {
        return "", err
    }
    var bs []byte
    for {
        b, err := rd.ReadByte()
        if err != nil {
            return "", err
        }
        if b == byte(0x00) {
            break
        }
        bs = append(bs, b)
    }
    return string(bs), nil
}

func setup_term() (err error) {
    var data []byte
    var header [6]int16
    var str_offset, table_offset int16

    data, err = load_terminfo()
    if err != nil {
        return setup_term_builtin()
    }

    rd := bytes.NewReader(data)
    // 0: magic number, 1: size of names section, 2: size of boolean section, 3:
    // size of numbers section (in integers), 4: size of the strings section (in
    // integers), 5: size of the string table

    err = binary.Read(rd, binary.LittleEndian, header[:])
    if err != nil {
        return
    }

    if (header[1]+header[2])%2 != 0 {
        // old quirk to align everything on word boundaries
        header[2] += 1
    }
    str_offset = ti_header_length + header[1] + header[2] + 2*header[3]
    table_offset = str_offset + 2*header[4]

    // "Maps" the special keys constants from termbox.go to the number of the respective
    // string capability in the terminfo file. Taken from (ncurses) term.h.
    ti_keys := []int16{
        66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 70,
        71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 79, 83,
    }

    keys = make([]string, 0xFFFF-key_min)
    for i := range keys {
        keys[i], err = ti_read_string(rd, str_offset+2*ti_keys[i], table_offset)
        if err != nil {
            return
        }
    }
    return nil
}<commit_msg>Refactor terminfo.go<commit_after>// +build !windows
// This file contains a simple and incomplete implementation of the terminfo
// database. Information was taken from the ncurses manpages term(5) and
// terminfo(5). Currently, only the string capabilities for special keys and for
// functions without parameters are actually used. Colors are still done with
// ANSI escape sequences. Other special features that are not (yet?) supported
// are reading from ~/.terminfo, the TERMINFO_DIRS variable, Berkeley database
// format and extended capabilities.

package keyboard

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"io/ioutil"
	"os"
	"strings"
)

const (
	ti_header_length = 12
)

var (
	eterm_keys = []string{
		"\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
	}
	screen_keys = []string{
		"\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1bOA", "\x1bOB", "\x1bOD", "\x1bOC",
	}
	xterm_keys = []string{
		"\x1bOP", "\x1bOQ", "\x1bOR", "\x1bOS", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[H", "\x1b[F", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
	}
	rxvt_keys = []string{
		"\x1b[11~", "\x1b[12~", "\x1b[13~", "\x1b[14~", "\x1b[15~", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[7~", "\x1b[8~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
	}
	linux_keys = []string{
		"\x1b[[A", "\x1b[[B", "\x1b[[C", "\x1b[[D", "\x1b[[E", "\x1b[17~", "\x1b[18~", "\x1b[19~", "\x1b[20~", "\x1b[21~", "\x1b[23~", "\x1b[24~", "\x1b[2~", "\x1b[3~", "\x1b[1~", "\x1b[4~", "\x1b[5~", "\x1b[6~", "\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C",
	}

	terms = []struct {
		name string
		keys []string
	}{
		{"Eterm", eterm_keys},
		{"screen", screen_keys},
		{"xterm", xterm_keys},
		{"xterm-256color", xterm_keys},
		{"rxvt-unicode", rxvt_keys},
		{"rxvt-256color", rxvt_keys},
		{"linux", linux_keys},
	}
)

func load_terminfo() ([]byte, error) {
	var data []byte
	var err error

	term := os.Getenv("TERM")
	if term == "" {
		return nil, errors.New("terminfo: TERM not set")
	}

	// The following behaviour follows the one described in terminfo(5) as
	// distributed by ncurses.

	terminfo := os.Getenv("TERMINFO")
	if terminfo != "" {
		// if TERMINFO is set, no other directory should be searched
		return ti_try_path(terminfo)
	}

	// next, consider ~/.terminfo
	home := os.Getenv("HOME")
	if home != "" {
		data, err = ti_try_path(home + "/.terminfo")
		if err == nil {
			return data, nil
		}
	}

	// next, TERMINFO_DIRS
	dirs := os.Getenv("TERMINFO_DIRS")
	if dirs != "" {
		for _, dir := range strings.Split(dirs, ":") {
			if dir == "" {
				// "" -> "/usr/share/terminfo"
				dir = "/usr/share/terminfo"
			}
			data, err = ti_try_path(dir)
			if err == nil {
				return data, nil
			}
		}
	}

	// fall back to /usr/share/terminfo
	return ti_try_path("/usr/share/terminfo")
}

func ti_try_path(path string) (data []byte, err error) {
	// load_terminfo already made sure it is set
	term := os.Getenv("TERM")

	// first try, the typical *nix path
	terminfo := path + "/" + term[0:1] + "/" + term
	data, err = ioutil.ReadFile(terminfo)
	if err == nil {
		return
	}

	// fallback to darwin specific dirs structure
	terminfo = path + "/" + hex.EncodeToString([]byte(term[:1])) + "/" + term
	data, err = ioutil.ReadFile(terminfo)
	return
}

func setup_term_builtin() error {
	name := os.Getenv("TERM")
	if name == "" {
		return errors.New("termbox: TERM environment variable not set")
	}

	for _, t := range terms {
		if t.name == name {
			keys = t.keys
			return nil
		}
	}

	compat_table := []struct {
		partial string
		keys    []string
	}{
		{"xterm", xterm_keys},
		{"rxvt", rxvt_keys},
		{"linux", linux_keys},
		{"Eterm", eterm_keys},
		{"screen", screen_keys},
		// let's assume that 'cygwin' is xterm compatible
		{"cygwin", xterm_keys},
		{"st", xterm_keys},
	}

	// try compatibility variants
	for _, it := range compat_table {
		if strings.Contains(name, it.partial) {
			keys = it.keys
			return nil
		}
	}

	return errors.New("termbox: unsupported terminal")
}

func setup_term() (err error) {
	var data []byte
	var header [6]int16
	var str_offset, table_offset int16

	data, err = load_terminfo()
	if err != nil {
		return setup_term_builtin()
	}

	rd := bytes.NewReader(data)
	// 0: magic number, 1: size of names section, 2: size of boolean section, 3:
	// size of numbers section (in integers), 4: size of the strings section (in
	// integers), 5: size of the string table

	err = binary.Read(rd, binary.LittleEndian, header[:])
	if err != nil {
		return
	}

	if (header[1]+header[2])%2 != 0 {
		// old quirk to align everything on word boundaries
		header[2] += 1
	}
	str_offset = ti_header_length + header[1] + header[2] + 2*header[3]
	table_offset = str_offset + 2*header[4]

	keys = make([]string, 0xFFFF-key_min)
	for i := range keys {
		keys[i], err = ti_read_string(rd, str_offset+2*ti_keys[i], table_offset)
		if err != nil {
			return
		}
	}
	return nil
}

func ti_read_string(rd *bytes.Reader, str_off, table int16) (string, error) {
	var off int16

	_, err := rd.Seek(int64(str_off), 0)
	if err != nil {
		return "", err
	}
	err = binary.Read(rd, binary.LittleEndian, &off)
	if err != nil {
		return "", err
	}
	_, err = rd.Seek(int64(table+off), 0)
	if err != nil {
		return "", err
	}
	var bs []byte
	for {
		b, err := rd.ReadByte()
		if err != nil {
			return "", err
		}
		if b == byte(0x00) {
			break
		}
		bs = append(bs, b)
	}
	return string(bs), nil
}

// "Maps" special keys constants from termbox.go to the number of the respective
// string capability in the terminfo file. Taken from (ncurses) term.h.
var ti_keys = []int16{
	66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 70,
	71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 79, 83,
}
<|endoftext|>


<commit_before>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 api

import (
	. "atlantis/common"
	. "atlantis/manager/rpc/types"
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"strings"
)

func RequestAppDependency(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	envsString := r.FormValue("Envs")
	envs := strings.Split(envsString, ",")
	arg := ManagerRequestAppDependencyArg{
		ManagerAuthArg: auth,
		App:            vars["Depender"],
		Dependency:     vars["App"],
		Envs:           envs,
	}
	var reply ManagerRequestAppDependencyReply
	err = manager.RequestAppDependency(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status}, err))
}

// ----------------------------------------------------------------------------------------------------------
// Depender App Data Methods
// ----------------------------------------------------------------------------------------------------------

func AddDependerAppData(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	depEnvData := map[string]*DependerEnvData{}
	if r.FormValue("DependerEnvData") != "" {
		err = json.Unmarshal([]byte(r.FormValue("DependerEnvData")), &depEnvData)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	arg := ManagerAddDependerAppDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		DependerAppData: &DependerAppData{
			Name:            vars["Depender"],
			DependerEnvData: depEnvData,
		},
	}
	var reply ManagerAddDependerAppDataReply
	err = manager.AddDependerAppData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func RemoveDependerAppData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerRemoveDependerAppDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
	}
	var reply ManagerRemoveDependerAppDataReply
	err := manager.RemoveDependerAppData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func GetDependerAppData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerGetDependerAppDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
	}
	var reply ManagerGetDependerAppDataReply
	err := manager.GetDependerAppData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{
		"Status":          reply.Status,
		"DependerAppData": reply.DependerAppData,
	}, err))
}

// ----------------------------------------------------------------------------------------------------------
// Depender Env Data Methods
// ----------------------------------------------------------------------------------------------------------

func AddDependerEnvData(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	data := map[string]interface{}{}
	if r.FormValue("Data") != "" {
		err = json.Unmarshal([]byte(r.FormValue("Data")), &data)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	sg := map[string][]uint16{}
	if r.FormValue("SecurityGroup") != "" {
		err = json.Unmarshal([]byte(r.FormValue("SecurityGroup")), &sg)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	arg := ManagerAddDependerEnvDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		DependerEnvData: &DependerEnvData{
			Name:          vars["Env"],
			SecurityGroup: sg,
			DataMap:       data,
		},
	}
	var reply ManagerAddDependerEnvDataReply
	err = manager.AddDependerEnvData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func RemoveDependerEnvData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerRemoveDependerEnvDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Env:            vars["Env"],
	}
	var reply ManagerRemoveDependerEnvDataReply
	err := manager.RemoveDependerEnvData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func GetDependerEnvData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerGetDependerEnvDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Env:            vars["Env"],
	}
	var reply ManagerGetDependerEnvDataReply
	err := manager.GetDependerEnvData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{
		"Status":          reply.Status,
		"DependerEnvData": reply.DependerEnvData,
	}, err))
}

// ----------------------------------------------------------------------------------------------------------
// Depender Env Data For Depender App Methods
// ----------------------------------------------------------------------------------------------------------

func AddDependerEnvDataForDependerApp(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	data := map[string]interface{}{}
	if r.FormValue("Data") != "" {
		err = json.Unmarshal([]byte(r.FormValue("Data")), &data)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	sg := map[string][]uint16{}
	if r.FormValue("SecurityGroup") != "" {
		err = json.Unmarshal([]byte(r.FormValue("SecurityGroup")), &sg)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	arg := ManagerAddDependerEnvDataForDependerAppArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
		DependerEnvData: &DependerEnvData{
			Name:          vars["Env"],
			SecurityGroup: sg,
			DataMap:       data,
		},
	}
	var reply ManagerAddDependerEnvDataForDependerAppReply
	err = manager.AddDependerEnvDataForDependerApp(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func RemoveDependerEnvDataForDependerApp(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerRemoveDependerEnvDataForDependerAppArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
		Env:            vars["Env"],
	}
	var reply ManagerRemoveDependerEnvDataForDependerAppReply
	err := manager.RemoveDependerEnvDataForDependerApp(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func GetDependerEnvDataForDependerApp(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerGetDependerEnvDataForDependerAppArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
		Env:            vars["Env"],
	}
	var reply ManagerGetDependerEnvDataForDependerAppReply
	err := manager.GetDependerEnvDataForDependerApp(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{
		"Status":          reply.Status,
		"DependerEnvData": reply.DependerEnvData,
	}, err))
}
<commit_msg>depender REST API should take group:port for simplicity<commit_after>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 api

import (
	. "atlantis/common"
	. "atlantis/manager/rpc/types"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"strconv"
	"strings"
)

func RequestAppDependency(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	envsString := r.FormValue("Envs")
	envs := strings.Split(envsString, ",")
	arg := ManagerRequestAppDependencyArg{
		ManagerAuthArg: auth,
		App:            vars["Depender"],
		Dependency:     vars["App"],
		Envs:           envs,
	}
	var reply ManagerRequestAppDependencyReply
	err = manager.RequestAppDependency(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status}, err))
}

// ----------------------------------------------------------------------------------------------------------
// Depender App Data Methods
// ----------------------------------------------------------------------------------------------------------

func AddDependerAppData(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	depEnvData := map[string]*DependerEnvData{}
	if r.FormValue("DependerEnvData") != "" {
		err = json.Unmarshal([]byte(r.FormValue("DependerEnvData")), &depEnvData)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	arg := ManagerAddDependerAppDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		DependerAppData: &DependerAppData{
			Name:            vars["Depender"],
			DependerEnvData: depEnvData,
		},
	}
	var reply ManagerAddDependerAppDataReply
	err = manager.AddDependerAppData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func RemoveDependerAppData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerRemoveDependerAppDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
	}
	var reply ManagerRemoveDependerAppDataReply
	err := manager.RemoveDependerAppData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func GetDependerAppData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerGetDependerAppDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
	}
	var reply ManagerGetDependerAppDataReply
	err := manager.GetDependerAppData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{
		"Status":          reply.Status,
		"DependerAppData": reply.DependerAppData,
	}, err))
}

// ----------------------------------------------------------------------------------------------------------
// Depender Env Data Methods
// ----------------------------------------------------------------------------------------------------------

func AddDependerEnvData(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	data := map[string]interface{}{}
	if r.FormValue("Data") != "" {
		err = json.Unmarshal([]byte(r.FormValue("Data")), &data)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	sgRaw := []string{}
	if r.FormValue("SecurityGroup") != "" {
		err = json.Unmarshal([]byte(r.FormValue("SecurityGroup")), &sgRaw)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	// convert []string -> map[string][]uint16
	sg := map[string][]uint16{}
	for _, groupAndPort := range sgRaw {
		parts := strings.SplitN(groupAndPort, ":", 2)
		if len(parts) != 2 {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError},
				errors.New("Invalid Security Group entry: "+groupAndPort)))
			return
		}
		group := parts[0]
		port, err := strconv.ParseUint(parts[1], 10, 16)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError},
				errors.New("Invalid Security Group entry: "+groupAndPort)))
			return
		}
		if existing, exists := sg[group]; !exists {
			sg[group] = []uint16{uint16(port)}
		} else {
			sg[group] = append(existing, uint16(port))
		}
	}
	arg := ManagerAddDependerEnvDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		DependerEnvData: &DependerEnvData{
			Name:          vars["Env"],
			SecurityGroup: sg,
			DataMap:       data,
		},
	}
	var reply ManagerAddDependerEnvDataReply
	err = manager.AddDependerEnvData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func RemoveDependerEnvData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerRemoveDependerEnvDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Env:            vars["Env"],
	}
	var reply ManagerRemoveDependerEnvDataReply
	err := manager.RemoveDependerEnvData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func GetDependerEnvData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerGetDependerEnvDataArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Env:            vars["Env"],
	}
	var reply ManagerGetDependerEnvDataReply
	err := manager.GetDependerEnvData(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{
		"Status":          reply.Status,
		"DependerEnvData": reply.DependerEnvData,
	}, err))
}

// ----------------------------------------------------------------------------------------------------------
// Depender Env Data For Depender App Methods
// ----------------------------------------------------------------------------------------------------------

func AddDependerEnvDataForDependerApp(w http.ResponseWriter, r *http.Request) {
	var err error
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	data := map[string]interface{}{}
	if r.FormValue("Data") != "" {
		err = json.Unmarshal([]byte(r.FormValue("Data")), &data)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	sgRaw := []string{}
	if r.FormValue("SecurityGroup") != "" {
		err = json.Unmarshal([]byte(r.FormValue("SecurityGroup")), &sgRaw)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError}, err))
			return
		}
	}
	// convert []string -> map[string][]uint16
	sg := map[string][]uint16{}
	for _, groupAndPort := range sgRaw {
		parts := strings.SplitN(groupAndPort, ":", 2)
		if len(parts) != 2 {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError},
				errors.New("Invalid Security Group entry: "+groupAndPort)))
			return
		}
		group := parts[0]
		port, err := strconv.ParseUint(parts[1], 10, 16)
		if err != nil {
			fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": StatusError},
				errors.New("Invalid Security Group entry: "+groupAndPort)))
			return
		}
		if existing, exists := sg[group]; !exists {
			sg[group] = []uint16{uint16(port)}
		} else {
			sg[group] = append(existing, uint16(port))
		}
	}
	arg := ManagerAddDependerEnvDataForDependerAppArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
		DependerEnvData: &DependerEnvData{
			Name:          vars["Env"],
			SecurityGroup: sg,
			DataMap:       data,
		},
	}
	var reply ManagerAddDependerEnvDataForDependerAppReply
	err = manager.AddDependerEnvDataForDependerApp(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func RemoveDependerEnvDataForDependerApp(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerRemoveDependerEnvDataForDependerAppArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
		Env:            vars["Env"],
	}
	var reply ManagerRemoveDependerEnvDataForDependerAppReply
	err := manager.RemoveDependerEnvDataForDependerApp(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{"Status": reply.Status, "App": reply.App}, err))
}

func GetDependerEnvDataForDependerApp(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	auth := ManagerAuthArg{r.FormValue("User"), "", r.FormValue("Secret")}
	arg := ManagerGetDependerEnvDataForDependerAppArg{
		ManagerAuthArg: auth,
		App:            vars["App"],
		Depender:       vars["Depender"],
		Env:            vars["Env"],
	}
	var reply ManagerGetDependerEnvDataForDependerAppReply
	err := manager.GetDependerEnvDataForDependerApp(arg, &reply)
	fmt.Fprintf(w, "%s", Output(map[string]interface{}{
		"Status":          reply.Status,
		"DependerEnvData": reply.DependerEnvData,
	}, err))
}
<|endoftext|>