<commit_before>package ledger

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

type Entry struct {
	Date        string // "Y-m-d"
	Description string
	Change      int // in cents
}

func isValidCurrency(currency string) bool {
	return currency == "USD" || currency == "EUR"
}

func isValidLocale(locale string) bool {
	return locale == "en-US" || locale == "nl-NL"
}

func isValidDate(entries []Entry) bool {
	for _, entry := range entries {
		if len(entry.Date) != 10 {
			return false
		}
		_, d2, _, d4, _ := entry.Date[0:4], entry.Date[4], entry.Date[5:7], entry.Date[7], entry.Date[8:10]
		if d2 != '-' || d4 != '-' {
			return false
		}
	}
	return true
}

func formatDescription(description string) string {
	if len(description) > 25 {
		return description[:22] + "..."
	}
	return description + strings.Repeat(" ", 25-len(description))
}
func formatDate(locale string, date string) string {
	year, month, day := date[0:4], date[5:7], date[8:10]

	if locale == "nl-NL" {
		return fmt.Sprintf("%v-%v-%v", day, month, year)
	} else if locale == "en-US" {
		return fmt.Sprintf("%v/%v/%v", month, day, year)
	}
	return ""
}

func header(locale string) (output string) {
	if locale == "nl-NL" {
		return "Datum" +
			strings.Repeat(" ", 10-len("Datum")) +
			" | " +
			"Omschrijving" +
			strings.Repeat(" ", 25-len("Omschrijving")) +
			" | " + "Verandering" + "\n"
	} else if locale == "en-US" {
		return "Date" +
			strings.Repeat(" ", 10-len("Date")) +
			" | " +
			"Description" +
			strings.Repeat(" ", 25-len("Description")) +
			" | " + "Change" + "\n"
	}
	return ""
}

func FormatLedger(currency string, locale string, entries []Entry) (output string, e error) {
	if !isValidCurrency(currency) {
		return "", errors.New("invalid currency")
	}
	if !isValidLocale(locale) {
		return "", errors.New("invalid locale")
	}
	if !isValidDate(entries) {
		return "", errors.New("invalid date")
	}

	entriesCopy := append([]Entry{}, entries...)

	m1 := map[bool]int{true: 0, false: 1}
	m2 := map[bool]int{true: -1, false: 1}
	es := entriesCopy
	for len(es) > 1 {
		first, rest := es[0], es[1:]
		success := false
		for !success {
			success = true
			for i, e := range rest {
				if (m1[e.Date == first.Date]*m2[e.Date < first.Date]*4 +
					m1[e.Description == first.Description]*m2[e.Description < first.Description]*2 +
					m1[e.Change == first.Change]*m2[e.Change < first.Change]*1) < 0 {
					es[0], es[i+1] = es[i+1], es[0]
					success = false
				}
			}
		}
		es = es[1:]
	}

	output += header(locale)
	co := make(chan struct {
		i int
		s string
		e error
	})
	for i, et := range entriesCopy {
		go func(i int, entry Entry) {
			de := formatDescription(entry.Description)
			d := formatDate(locale, entry.Date)
			negative := false
			cents := entry.Change
			if cents < 0 {
				cents = cents * -1
				negative = true
			}
			var a string
			if locale == "nl-NL" {
				if currency == "EUR" {
					a += "€"
				} else if currency == "USD" {
					a += "$"
				} else {
					co <- struct {
						i int
						s string
						e error
					}{e: errors.New("")}
				}
				a += " "
				centsStr := strconv.Itoa(cents)
				switch len(centsStr) {
				case 1:
					centsStr = "00" + centsStr
				case 2:
					centsStr = "0" + centsStr
				}
				rest := centsStr[:len(centsStr)-2]
				var parts []string
				for len(rest) > 3 {
					parts = append(parts, rest[len(rest)-3:])
					rest = rest[:len(rest)-3]
				}
				if len(rest) > 0 {
					parts = append(parts, rest)
				}
				for i := len(parts) - 1; i >= 0; i-- {
					a += parts[i] + "."
				}
				a = a[:len(a)-1]
				a += ","
				a += centsStr[len(centsStr)-2:]
				if negative {
					a += "-"
				} else {
					a += " "
				}
			} else if locale == "en-US" {
				if negative {
					a += "("
				}
				if currency == "EUR" {
					a += "€"
				} else if currency == "USD" {
					a += "$"
				} else {
					co <- struct {
						i int
						s string
						e error
					}{e: errors.New("")}
				}
				centsStr := strconv.Itoa(cents)
				switch len(centsStr) {
				case 1:
					centsStr = "00" + centsStr
				case 2:
					centsStr = "0" + centsStr
				}
				rest := centsStr[:len(centsStr)-2]
				var parts []string
				for len(rest) > 3 {
					parts = append(parts, rest[len(rest)-3:])
					rest = rest[:len(rest)-3]
				}
				if len(rest) > 0 {
					parts = append(parts, rest)
				}
				for i := len(parts) - 1; i >= 0; i-- {
					a += parts[i] + ","
				}
				a = a[:len(a)-1]
				a += "."
				a += centsStr[len(centsStr)-2:]
				if negative {
					a += ")"
				} else {
					a += " "
				}
			} else {
				co <- struct {
					i int
					s string
					e error
				}{e: errors.New("")}
			}
			var al int
			for range a {
				al++
			}
			co <- struct {
				i int
				s string
				e error
			}{i: i, s: d + strings.Repeat(" ", 10-len(d)) + " | " + de + " | " +
				strings.Repeat(" ", 13-al) + a + "\n"}
		}(i, et)
	}
	temp := make([]string, len(entriesCopy))
	for range entriesCopy {
		v := <-co
		if v.e != nil {
			return "", v.e
		}
		temp[v.i] = v.s
	}
	for i := 0; i < len(entriesCopy); i++ {
		output += temp[i]
	}
	return output, nil
}
<commit_msg>Panic if invalid locale<commit_after>package ledger

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

type Entry struct {
	Date        string // "Y-m-d"
	Description string
	Change      int // in cents
}

func isValidCurrency(currency string) bool {
	return currency == "USD" || currency == "EUR"
}

func isValidLocale(locale string) bool {
	return locale == "en-US" || locale == "nl-NL"
}

func isValidDate(entries []Entry) bool {
	for _, entry := range entries {
		if len(entry.Date) != 10 {
			return false
		}
		_, d2, _, d4, _ := entry.Date[0:4], entry.Date[4], entry.Date[5:7], entry.Date[7], entry.Date[8:10]
		if d2 != '-' || d4 != '-' {
			return false
		}
	}
	return true
}

func formatDescription(description string) string {
	if len(description) > 25 {
		return description[:22] + "..."
	}
	return description + strings.Repeat(" ", 25-len(description))
}
func formatDate(locale string, date string) string {
	year, month, day := date[0:4], date[5:7], date[8:10]

	if locale == "nl-NL" {
		return fmt.Sprintf("%v-%v-%v", day, month, year)
	} else if locale == "en-US" {
		return fmt.Sprintf("%v/%v/%v", month, day, year)
	}
	panic("invalid locale")
}

func header(locale string) (output string) {
	if locale == "nl-NL" {
		return "Datum" +
			strings.Repeat(" ", 10-len("Datum")) +
			" | " +
			"Omschrijving" +
			strings.Repeat(" ", 25-len("Omschrijving")) +
			" | " + "Verandering" + "\n"
	} else if locale == "en-US" {
		return "Date" +
			strings.Repeat(" ", 10-len("Date")) +
			" | " +
			"Description" +
			strings.Repeat(" ", 25-len("Description")) +
			" | " + "Change" + "\n"
	}
	panic("invalid locale")
}

func FormatLedger(currency string, locale string, entries []Entry) (output string, e error) {
	if !isValidCurrency(currency) {
		return "", errors.New("invalid currency")
	}
	if !isValidLocale(locale) {
		return "", errors.New("invalid locale")
	}
	if !isValidDate(entries) {
		return "", errors.New("invalid date")
	}

	entriesCopy := append([]Entry{}, entries...)

	m1 := map[bool]int{true: 0, false: 1}
	m2 := map[bool]int{true: -1, false: 1}
	es := entriesCopy
	for len(es) > 1 {
		first, rest := es[0], es[1:]
		success := false
		for !success {
			success = true
			for i, e := range rest {
				if (m1[e.Date == first.Date]*m2[e.Date < first.Date]*4 +
					m1[e.Description == first.Description]*m2[e.Description < first.Description]*2 +
					m1[e.Change == first.Change]*m2[e.Change < first.Change]*1) < 0 {
					es[0], es[i+1] = es[i+1], es[0]
					success = false
				}
			}
		}
		es = es[1:]
	}

	output += header(locale)
	co := make(chan struct {
		i int
		s string
		e error
	})
	for i, et := range entriesCopy {
		go func(i int, entry Entry) {
			de := formatDescription(entry.Description)
			d := formatDate(locale, entry.Date)
			negative := false
			cents := entry.Change
			if cents < 0 {
				cents = cents * -1
				negative = true
			}
			var a string
			if locale == "nl-NL" {
				if currency == "EUR" {
					a += "€"
				} else if currency == "USD" {
					a += "$"
				} else {
					co <- struct {
						i int
						s string
						e error
					}{e: errors.New("")}
				}
				a += " "
				centsStr := strconv.Itoa(cents)
				switch len(centsStr) {
				case 1:
					centsStr = "00" + centsStr
				case 2:
					centsStr = "0" + centsStr
				}
				rest := centsStr[:len(centsStr)-2]
				var parts []string
				for len(rest) > 3 {
					parts = append(parts, rest[len(rest)-3:])
					rest = rest[:len(rest)-3]
				}
				if len(rest) > 0 {
					parts = append(parts, rest)
				}
				for i := len(parts) - 1; i >= 0; i-- {
					a += parts[i] + "."
				}
				a = a[:len(a)-1]
				a += ","
				a += centsStr[len(centsStr)-2:]
				if negative {
					a += "-"
				} else {
					a += " "
				}
			} else if locale == "en-US" {
				if negative {
					a += "("
				}
				if currency == "EUR" {
					a += "€"
				} else if currency == "USD" {
					a += "$"
				} else {
					co <- struct {
						i int
						s string
						e error
					}{e: errors.New("")}
				}
				centsStr := strconv.Itoa(cents)
				switch len(centsStr) {
				case 1:
					centsStr = "00" + centsStr
				case 2:
					centsStr = "0" + centsStr
				}
				rest := centsStr[:len(centsStr)-2]
				var parts []string
				for len(rest) > 3 {
					parts = append(parts, rest[len(rest)-3:])
					rest = rest[:len(rest)-3]
				}
				if len(rest) > 0 {
					parts = append(parts, rest)
				}
				for i := len(parts) - 1; i >= 0; i-- {
					a += parts[i] + ","
				}
				a = a[:len(a)-1]
				a += "."
				a += centsStr[len(centsStr)-2:]
				if negative {
					a += ")"
				} else {
					a += " "
				}
			} else {
				co <- struct {
					i int
					s string
					e error
				}{e: errors.New("")}
			}
			var al int
			for range a {
				al++
			}
			co <- struct {
				i int
				s string
				e error
			}{i: i, s: d + strings.Repeat(" ", 10-len(d)) + " | " + de + " | " +
				strings.Repeat(" ", 13-al) + a + "\n"}
		}(i, et)
	}
	temp := make([]string, len(entriesCopy))
	for range entriesCopy {
		v := <-co
		if v.e != nil {
			return "", v.e
		}
		temp[v.i] = v.s
	}
	for i := 0; i < len(entriesCopy); i++ {
		output += temp[i]
	}
	return output, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package types

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/token"

	"code.google.com/p/go.tools/go/exact"
)

// TODO(gri) Document factory, accessor methods, and fields. General clean-up.

// An Object describes a named language entity such as a package,
// constant, type, variable, function (incl. methods), or label.
// All objects implement the Object interface.
//
type Object interface {
	Parent() *Scope   // scope in which this object is declared
	Pos() token.Pos   // position of object identifier in declaration
	Pkg() *Package    // nil for objects in the Universe scope and labels
	Name() string     // package local object name
	Type() Type       // object type
	IsExported() bool // reports whether the name starts with a capital letter
	Id() string       // object id (see Id below)

	// String returns a human-readable string of the object.
	String() string

	// isUsed reports whether the object was marked as 'used'.
	isUsed() bool

	// setParent sets the parent scope of the object.
	setParent(*Scope)

	// sameId reports whether obj.Id() and Id(pkg, name) are the same.
	sameId(pkg *Package, name string) bool
}

// Id returns name if it is exported, otherwise it
// returns the name qualified with the package path.
func Id(pkg *Package, name string) string {
	if ast.IsExported(name) {
		return name
	}
	// unexported names need the package path for differentiation
	path := ""
	// TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
	// if pkg == nil {
	// 	panic("nil package in lookup of unexported name")
	// }
	if pkg != nil {
		path = pkg.path
		if path == "" {
			path = "?"
		}
	}
	return path + "." + name
}

// An object implements the common parts of an Object.
type object struct {
	parent *Scope
	pos    token.Pos
	pkg    *Package
	name   string
	typ    Type
	used   bool
}

func (obj *object) Parent() *Scope   { return obj.parent }
func (obj *object) Pos() token.Pos   { return obj.pos }
func (obj *object) Pkg() *Package    { return obj.pkg }
func (obj *object) Name() string     { return obj.name }
func (obj *object) Type() Type       { return obj.typ }
func (obj *object) IsExported() bool { return ast.IsExported(obj.name) }
func (obj *object) Id() string       { return Id(obj.pkg, obj.name) }
func (obj *object) String() string   { panic("abstract") }

func (obj *object) isUsed() bool { return obj.used }

func (obj *object) toString(kind string, typ Type) string {
	var buf bytes.Buffer

	buf.WriteString(kind)
	buf.WriteByte(' ')
	// For package-level objects, package-qualify the name.
	if obj.pkg != nil && obj.pkg.scope.Lookup(obj.name) == obj {
		buf.WriteString(obj.pkg.name)
		buf.WriteByte('.')
	}
	buf.WriteString(obj.name)
	if typ != nil {
		buf.WriteByte(' ')
		writeType(&buf, typ)
	}

	return buf.String()
}

func (obj *object) setParent(parent *Scope) { obj.parent = parent }

func (obj *object) sameId(pkg *Package, name string) bool {
	// spec:
	// "Two identifiers are different if they are spelled differently,
	// or if they appear in different packages and are not exported.
	// Otherwise, they are the same."
	if name != obj.name {
		return false
	}
	// obj.Name == name
	if obj.IsExported() {
		return true
	}
	// not exported, so packages must be the same (pkg == nil for
	// fields in Universe scope; this can only happen for types
	// introduced via Eval)
	if pkg == nil || obj.pkg == nil {
		return pkg == obj.pkg
	}
	// pkg != nil && obj.pkg != nil
	return pkg.path == obj.pkg.path
}

// A PkgName represents an imported Go package.
type PkgName struct {
	object
}

func NewPkgName(pos token.Pos, pkg *Package, name string) *PkgName {
	return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], false}}
}

func (obj *PkgName) String() string { return obj.toString("package", nil) }

// A Const represents a declared constant.
type Const struct {
	object
	val exact.Value

	visited bool // for initialization cycle detection
}

func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const {
	return &Const{object: object{nil, pos, pkg, name, typ, false}, val: val}
}

func (obj *Const) String() string   { return obj.toString("const", obj.typ) }
func (obj *Const) Val() exact.Value { return obj.val }

// A TypeName represents a declared type.
type TypeName struct {
	object
}

func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
	return &TypeName{object{nil, pos, pkg, name, typ, false}}
}

func (obj *TypeName) String() string { return obj.toString("type", obj.typ.Underlying()) }

// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
	object

	anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
	visited   bool // for initialization cycle detection
}

func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, false}}
}

func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, true}} // parameters are always 'used'
}

func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, false}, anonymous: anonymous}
}

func (obj *Var) Anonymous() bool { return obj.anonymous }
func (obj *Var) String() string  { return obj.toString("var", obj.typ) }

// A Func represents a declared function, concrete method, or abstract
// (interface) method.  Its Type() is always a *Signature.
// An abstract method may belong to many interfaces due to embedding.
type Func struct {
	object
}

func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
	// don't store a nil signature
	var typ Type
	if sig != nil {
		typ = sig
	}
	return &Func{object{nil, pos, pkg, name, typ, false}}
}

// FullName returns the package- or receiver-type-qualified name of
// function or method obj.
func (obj *Func) FullName() string {
	var buf bytes.Buffer
	obj.fullname(&buf)
	return buf.String()
}

func (obj *Func) fullname(buf *bytes.Buffer) {
	if obj.typ != nil {
		sig := obj.typ.(*Signature)
		if recv := sig.Recv(); recv != nil {
			buf.WriteByte('(')
			if _, ok := recv.Type().(*Interface); ok {
				// gcimporter creates abstract methods of
				// named interfaces using the interface type
				// (not the named type) as the receiver.
				// Don't print it in full.
				buf.WriteString("interface")
			} else {
				writeType(buf, recv.Type())
			}
			buf.WriteByte(')')
			buf.WriteByte('.')
		} else if obj.pkg != nil {
			buf.WriteString(obj.pkg.name)
			buf.WriteByte('.')
		}
	}
	buf.WriteString(obj.name)
}

func (obj *Func) String() string {
	var buf bytes.Buffer
	buf.WriteString("func ")
	obj.fullname(&buf)
	if obj.typ != nil {
		writeSignature(&buf, obj.typ.(*Signature))
	}
	return buf.String()
}

// A Label represents a declared label.
type Label struct {
	object
}

func NewLabel(pos token.Pos, name string) *Label {
	return &Label{object{pos: pos, name: name, typ: Typ[Invalid]}}
}

func (obj *Label) String() string { return fmt.Sprintf("label %s", obj.Name()) }

// A Builtin represents a built-in function.
// Builtins don't have a valid type.
type Builtin struct {
	object

	id builtinId
}

func newBuiltin(id builtinId) *Builtin {
	return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
}

// Nil represents the predeclared value nil.
type Nil struct {
	object
}

func (*Nil) String() string { return "nil" }
<commit_msg>go.tools/go/types: add Var.IsField() accessor, and print "field" not "var".<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package types

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/token"

	"code.google.com/p/go.tools/go/exact"
)

// TODO(gri) Document factory, accessor methods, and fields. General clean-up.

// An Object describes a named language entity such as a package,
// constant, type, variable, function (incl. methods), or label.
// All objects implement the Object interface.
//
type Object interface {
	Parent() *Scope   // scope in which this object is declared
	Pos() token.Pos   // position of object identifier in declaration
	Pkg() *Package    // nil for objects in the Universe scope and labels
	Name() string     // package local object name
	Type() Type       // object type
	IsExported() bool // reports whether the name starts with a capital letter
	Id() string       // object id (see Id below)

	// String returns a human-readable string of the object.
	String() string

	// isUsed reports whether the object was marked as 'used'.
	isUsed() bool

	// setParent sets the parent scope of the object.
	setParent(*Scope)

	// sameId reports whether obj.Id() and Id(pkg, name) are the same.
	sameId(pkg *Package, name string) bool
}

// Id returns name if it is exported, otherwise it
// returns the name qualified with the package path.
func Id(pkg *Package, name string) string {
	if ast.IsExported(name) {
		return name
	}
	// unexported names need the package path for differentiation
	path := ""
	// TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
	// if pkg == nil {
	// 	panic("nil package in lookup of unexported name")
	// }
	if pkg != nil {
		path = pkg.path
		if path == "" {
			path = "?"
		}
	}
	return path + "." + name
}

// An object implements the common parts of an Object.
type object struct {
	parent *Scope
	pos    token.Pos
	pkg    *Package
	name   string
	typ    Type
	used   bool
}

func (obj *object) Parent() *Scope   { return obj.parent }
func (obj *object) Pos() token.Pos   { return obj.pos }
func (obj *object) Pkg() *Package    { return obj.pkg }
func (obj *object) Name() string     { return obj.name }
func (obj *object) Type() Type       { return obj.typ }
func (obj *object) IsExported() bool { return ast.IsExported(obj.name) }
func (obj *object) Id() string       { return Id(obj.pkg, obj.name) }
func (obj *object) String() string   { panic("abstract") }

func (obj *object) isUsed() bool { return obj.used }

func (obj *object) toString(kind string, typ Type) string {
	var buf bytes.Buffer

	buf.WriteString(kind)
	buf.WriteByte(' ')
	// For package-level objects, package-qualify the name.
	if obj.pkg != nil && obj.pkg.scope.Lookup(obj.name) == obj {
		buf.WriteString(obj.pkg.name)
		buf.WriteByte('.')
	}
	buf.WriteString(obj.name)
	if typ != nil {
		buf.WriteByte(' ')
		writeType(&buf, typ)
	}

	return buf.String()
}

func (obj *object) setParent(parent *Scope) { obj.parent = parent }

func (obj *object) sameId(pkg *Package, name string) bool {
	// spec:
	// "Two identifiers are different if they are spelled differently,
	// or if they appear in different packages and are not exported.
	// Otherwise, they are the same."
	if name != obj.name {
		return false
	}
	// obj.Name == name
	if obj.IsExported() {
		return true
	}
	// not exported, so packages must be the same (pkg == nil for
	// fields in Universe scope; this can only happen for types
	// introduced via Eval)
	if pkg == nil || obj.pkg == nil {
		return pkg == obj.pkg
	}
	// pkg != nil && obj.pkg != nil
	return pkg.path == obj.pkg.path
}

// A PkgName represents an imported Go package.
type PkgName struct {
	object
}

func NewPkgName(pos token.Pos, pkg *Package, name string) *PkgName {
	return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], false}}
}

func (obj *PkgName) String() string { return obj.toString("package", nil) }

// A Const represents a declared constant.
type Const struct {
	object
	val exact.Value

	visited bool // for initialization cycle detection
}

func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val exact.Value) *Const {
	return &Const{object: object{nil, pos, pkg, name, typ, false}, val: val}
}

func (obj *Const) String() string   { return obj.toString("const", obj.typ) }
func (obj *Const) Val() exact.Value { return obj.val }

// A TypeName represents a declared type.
type TypeName struct {
	object
}

func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
	return &TypeName{object{nil, pos, pkg, name, typ, false}}
}

func (obj *TypeName) String() string { return obj.toString("type", obj.typ.Underlying()) }

// A Variable represents a declared variable (including function parameters and results, and struct fields).
type Var struct {
	object

	anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
	visited   bool // for initialization cycle detection
	isField   bool // var is struct field
}

func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, false}}
}

func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, true}} // parameters are always 'used'
}

func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var {
	return &Var{object: object{nil, pos, pkg, name, typ, false}, anonymous: anonymous, isField: true}
}

func (obj *Var) Anonymous() bool { return obj.anonymous }
func (obj *Var) String() string {
	kind := "var"
	if obj.isField {
		kind = "field"
	}
	return obj.toString(kind, obj.typ)
}
func (obj *Var) IsField() bool { return obj.isField }

// A Func represents a declared function, concrete method, or abstract
// (interface) method.  Its Type() is always a *Signature.
// An abstract method may belong to many interfaces due to embedding.
type Func struct {
	object
}

func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
	// don't store a nil signature
	var typ Type
	if sig != nil {
		typ = sig
	}
	return &Func{object{nil, pos, pkg, name, typ, false}}
}

// FullName returns the package- or receiver-type-qualified name of
// function or method obj.
func (obj *Func) FullName() string {
	var buf bytes.Buffer
	obj.fullname(&buf)
	return buf.String()
}

func (obj *Func) fullname(buf *bytes.Buffer) {
	if obj.typ != nil {
		sig := obj.typ.(*Signature)
		if recv := sig.Recv(); recv != nil {
			buf.WriteByte('(')
			if _, ok := recv.Type().(*Interface); ok {
				// gcimporter creates abstract methods of
				// named interfaces using the interface type
				// (not the named type) as the receiver.
				// Don't print it in full.
				buf.WriteString("interface")
			} else {
				writeType(buf, recv.Type())
			}
			buf.WriteByte(')')
			buf.WriteByte('.')
		} else if obj.pkg != nil {
			buf.WriteString(obj.pkg.name)
			buf.WriteByte('.')
		}
	}
	buf.WriteString(obj.name)
}

func (obj *Func) String() string {
	var buf bytes.Buffer
	buf.WriteString("func ")
	obj.fullname(&buf)
	if obj.typ != nil {
		writeSignature(&buf, obj.typ.(*Signature))
	}
	return buf.String()
}

// A Label represents a declared label.
type Label struct {
	object
}

func NewLabel(pos token.Pos, name string) *Label {
	return &Label{object{pos: pos, name: name, typ: Typ[Invalid]}}
}

func (obj *Label) String() string { return fmt.Sprintf("label %s", obj.Name()) }

// A Builtin represents a built-in function.
// Builtins don't have a valid type.
type Builtin struct {
	object

	id builtinId
}

func newBuiltin(id builtinId) *Builtin {
	return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
}

// Nil represents the predeclared value nil.
type Nil struct {
	object
}

func (*Nil) String() string { return "nil" }
<|endoftext|>


<commit_before>package redisq

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/vivace-io/evelib/crest"
)

// RecieverChan is a channel that may recive a payload containing information
// pertaining to a killmail.
type RecieverChan chan Payload

// RecieverFunc is a function that may called and passed a new Payload model as
// the parameter.
type RecieverFunc func(Payload)

// Payload is the json encoded response containing the killmail that is returned
// as well as some meta information.
type Payload struct {
	KillID int `json:"killID"`
	Zkb    struct {
		LocationID int     `json:"locationID"`
		Hash       string  `json:"hash"`
		TotalValue float32 `json:"totalValue"`
		Points     int     `json:"points"`
		Href       string  `json:"href"`
	}
	Killmail crest.Killmail `json:"killmail"`
}

type response struct {
	Payload *Payload `json:"package"`
}

// Client to listen to ZKillboards RedisQ service for new Killmails recieved
// from EVE Online.
type Client struct {
	locker    *sync.RWMutex
	rchan     []RecieverChan
	rfunc     []RecieverFunc
	addr      string
	userAgent string
	running   bool
	errchan   chan error
}

// NewClient takes an option parameter, which may be nil, and configures and
// returns a new client. Returns error for invalid configuration or any
// connection problems.
func NewClient(opts *Options) (client *Client, err error) {
	if opts == nil {
		opts = DefaultOptions()
	}
	client = &Client{
		locker: new(sync.RWMutex),
		addr:   opts.Addr,
	}
	if client.addr == "" {
		err = errors.New("address for RedisQ endpoint was not set")
		client = nil
		return
	}
	return
}

// AddChan adds a channel to the client which will be sent all future Killmails
// that are recieved.
func (client *Client) AddChan(reciever RecieverChan) {
	client.locker.Lock()
	defer client.locker.Unlock()
	client.rchan = append(client.rchan, reciever)
}

// AddChan adds a function to the client which will be called andpassed all
// future killmails that are recieved.
func (client *Client) AddFunc(reciever RecieverFunc) {
	client.locker.Lock()
	defer client.locker.Unlock()
	client.rfunc = append(client.rfunc, reciever)
}

// SetErrorChannel accepts a channel of type error to which errors encountered
// are sent by the client.
func (client *Client) SetErrorChannel(errchan chan error) {
	client.locker.Lock()
	defer client.locker.Unlock()
	client.errchan = errchan
}

// Listen starts recieving and distributing killmails to added recievers.
func (client *Client) Listen() {
	if client.running {
		return
	}
	client.running = true
	go func() {
		var err error
		var resp response
		for client.running {
			resp, err = client.fetch()
			if err != nil {
				client.sendError(err)
				// Usually if an error is encountered, it's a connection issue. So
				// ease back and wait for a minute when client happens and retry after.
				time.Sleep(1 * time.Minute)
			} else {
				if resp.Payload != nil {
					client.send(*resp.Payload)
				}
			}
		}
	}()
	return
}

// Close the client's connection to RedisQ.
func (client *Client) Close() {
	client.running = false
}

// fetch a killmail from RedisQ.
func (client *Client) fetch() (resp response, err error) {
	webc := &http.Client{}
	request, err := http.NewRequest("GET", client.addr, nil)
	if err != nil {
		panic(err)
	}
	request.Header.Add("User-Agent", client.userAgent)

	rawresp, err := webc.Do(request)
	if err != nil {
		return
	}
	defer rawresp.Body.Close()

	body, err := ioutil.ReadAll(rawresp.Body)
	if err != nil {
		return
	}
	err = json.Unmarshal(body, &resp)
	return
}

// send a killmail to all reciever channels/functions on seperate goroutines
// for each.
func (client *Client) send(payload Payload) {
	client.locker.RLock()
	defer client.locker.RUnlock()

	for _, c := range client.rchan {
		go func() {
			c <- payload
		}()
	}
	for _, r := range client.rfunc {
		go r(payload)
	}
}

// sendError to error channel, if it exists.
func (client *Client) sendError(err error) {
	if client.errchan == nil {
		return
	}
	go func() {
		client.errchan <- err
	}()
}
<commit_msg>redisq updates crest.Killmail with missing information, such as the value and location ID<commit_after>package redisq

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"sync"
	"time"

	"github.com/vivace-io/evelib/crest"
)

// RecieverChan is a channel that may recive a payload containing information
// pertaining to a killmail.
type RecieverChan chan Payload

// RecieverFunc is a function that may called and passed a new Payload model as
// the parameter.
type RecieverFunc func(Payload)

// Payload is the json encoded response containing the killmail that is returned
// as well as some meta information.
type Payload struct {
	KillID int `json:"killID"`
	Zkb    struct {
		LocationID int     `json:"locationID"`
		Hash       string  `json:"hash"`
		TotalValue float32 `json:"totalValue"`
		Points     int     `json:"points"`
		Href       string  `json:"href"`
	}
	Killmail crest.Killmail `json:"killmail"`
}

type response struct {
	Payload *Payload `json:"package"`
}

// Client to listen to ZKillboards RedisQ service for new Killmails recieved
// from EVE Online.
type Client struct {
	locker    *sync.RWMutex
	rchan     []RecieverChan
	rfunc     []RecieverFunc
	addr      string
	userAgent string
	running   bool
	errchan   chan error
}

// NewClient takes an option parameter, which may be nil, and configures and
// returns a new client. Returns error for invalid configuration or any
// connection problems.
func NewClient(opts *Options) (client *Client, err error) {
	if opts == nil {
		opts = DefaultOptions()
	}
	client = &Client{
		locker: new(sync.RWMutex),
		addr:   opts.Addr,
	}
	if client.addr == "" {
		err = errors.New("address for RedisQ endpoint was not set")
		client = nil
		return
	}
	return
}

// AddChan adds a channel to the client which will be sent all future Killmails
// that are recieved.
func (client *Client) AddChan(reciever RecieverChan) {
	client.locker.Lock()
	defer client.locker.Unlock()
	client.rchan = append(client.rchan, reciever)
}

// AddChan adds a function to the client which will be called andpassed all
// future killmails that are recieved.
func (client *Client) AddFunc(reciever RecieverFunc) {
	client.locker.Lock()
	defer client.locker.Unlock()
	client.rfunc = append(client.rfunc, reciever)
}

// SetErrorChannel accepts a channel of type error to which errors encountered
// are sent by the client.
func (client *Client) SetErrorChannel(errchan chan error) {
	client.locker.Lock()
	defer client.locker.Unlock()
	client.errchan = errchan
}

// Listen starts recieving and distributing killmails to added recievers.
func (client *Client) Listen() {
	if client.running {
		return
	}
	client.running = true
	go func() {
		var err error
		var resp response
		for client.running {
			resp, err = client.fetch()
			if err != nil {
				client.sendError(err)
				// Usually if an error is encountered, it's a connection issue. So
				// ease back and wait for a minute when client happens and retry after.
				time.Sleep(1 * time.Minute)
			} else {
				if resp.Payload != nil {
					client.send(*resp.Payload)
				}
			}
		}
	}()
	return
}

// Close the client's connection to RedisQ.
func (client *Client) Close() {
	client.running = false
}

// fetch a killmail from RedisQ.
func (client *Client) fetch() (resp response, err error) {
	webc := &http.Client{}
	request, err := http.NewRequest("GET", client.addr, nil)
	if err != nil {
		panic(err)
	}
	request.Header.Add("User-Agent", client.userAgent)

	rawresp, err := webc.Do(request)
	if err != nil {
		return
	}
	defer rawresp.Body.Close()

	body, err := ioutil.ReadAll(rawresp.Body)
	if err != nil {
		return
	}
	err = json.Unmarshal(body, &resp)
	return
}

// send a killmail to all reciever channels/functions on seperate goroutines
// for each.
func (client *Client) send(payload Payload) {
	client.locker.RLock()
	defer client.locker.RUnlock()
	payload.Killmail.Value = payload.Zkb.TotalValue
	payload.Killmail.LocationID = payload.Zkb.LocationID
	payload.Killmail.KillHash = payload.Zkb.Hash
	for _, c := range client.rchan {
		go func() {
			c <- payload
		}()
	}
	for _, r := range client.rfunc {
		go r(payload)
	}
}

// sendError to error channel, if it exists.
func (client *Client) sendError(err error) {
	if client.errchan == nil {
		return
	}
	go func() {
		client.errchan <- err
	}()
}
<|endoftext|>


<commit_before>package redlot

import (
	"strconv"

	"github.com/syndtr/goleveldb/leveldb/util"
)

func encodeHashKey(name, key []byte) (buf []byte) {
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	buf = append(buf, key...)
	return
}

func decodeHashKey(b []byte) (name, key []byte) {
	nameLen := bytesToUint32(b[1:5])
	name = b[5 : 5+nameLen]
	key = b[5+nameLen:]
	return
}

func encodeHsizeKey(name []byte) (buf []byte) {
	buf = append(buf, typeHSIZE)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	return
}

func decodeHsizeKey(b []byte) (key []byte) {
	return b[5:]
}

func hashSizeIncr(name []byte, incr int) {
	hsize := encodeHsizeKey(name)

	var size uint32
	if b, err := db.Get(hsize, nil); err == nil {
		size = bytesToUint32(b)
	}

	if incr > 0 {
		size += uint32(incr)
	} else {
		size = size - uint32(0-incr)
	}
	db.Put(hsize, uint32ToBytes(size), nil)
}

// Hset will set a hashmap value by the key.
// Args: name string, key string, value any
func Hset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	hashSizeIncr(args[0], 1)
	err = db.Put(encodeHashKey(args[0], args[1]), args[2], nil)
	if err != nil {
		hashSizeIncr(args[0], -1)
		return nil, err
	}

	return
}

// Hset will return a hashmap value by the key.
// Args: name string, key string
func Hget(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}
	var b []byte
	b, err = db.Get(encodeHashKey(args[0], args[1]), nil)
	return string(b), err
}

// Hdel will delete a hashmap value by the key.
// Args: name string, key string
func Hdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

func hincr(name, key []byte, increment int) (r int64, err error) {
	hash := encodeHashKey(name, key)
	v, _ := db.Get(hash, nil)
	var number int
	var exists bool
	if len(v) != 0 {
		number, err = strconv.Atoi(string(v))
		exists = true
		if err != nil {
			return -1, errNotInt
		}
	}
	number += increment
	r = int64(number)
	err = db.Put(hash, []byte(strconv.Itoa(number)), nil)
	if err == nil && !exists {
		hashSizeIncr(name, 1)
	}
	return
}

// Hincr will incr a hashmap value by the key.
// Args: name string, key string
func Hincr(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return hincr(args[0], args[1], 1)
}

// Hincrby will incr number a hashmap value by the key.
// Args: name string, key string, value int
func Hincrby(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}
	i, e := strconv.Atoi(string(args[2]))
	if e != nil {
		return -1, errNotInt
	}

	return hincr(args[0], args[1], i)
}

// Hexists will check the hashmap key is exists.
// Args: name string, key string
func Hexists(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// Hsize will return the hashmap size.
// Args: name string
func Hsize(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	var size int64
	if b, e := db.Get(encodeHsizeKey(args[0]), nil); e != nil {
		size = -1
	} else {
		size = int64(bytesToUint32(b))
	}

	return size, nil
}

// Hlist will list all hashmap in the range.
// Args: start string, end string, limit int
func Hlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hrlist will reverse list all hashmap in the range.
// Args: start string, end string, limit int
func Hrlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

func hscan(args [][]byte, kv, reverse bool) (r []string, err error) {
	if _, err = db.Get(encodeHsizeKey(args[0]), nil); err != nil {
		return
	}

	if len(args[1]) != 0 && string(args[1]) >= string(args[2]) {
		return []string{""}, nil
	}

	var ks, ke []byte
	if len(args[1]) == 0 {
		ks = append(ks, typeHASH)
		ks = append(ks, uint32ToBytes(uint32(len(args[0])))...)
		ks = append(ks, args[0]...)
	} else {
		ks = encodeHashKey(args[0], args[1])
	}

	if len(args[2]) == 0 {
		ke = append(ks, []byte{0xff}...)
	} else {
		ke = encodeHashKey(args[0], args[2])
	}

	limit, _ := strconv.Atoi(string(args[3]))

	iter := db.NewIterator(&util.Range{Start: ks, Limit: ke}, nil)
	var iters func() bool
	if reverse {
		iter.Seek(ke)
		iters = iter.Prev
	} else {
		iters = iter.Next
	}
	for iters() {
		_, key := decodeHashKey(iter.Key())
		r = append(r, string(key))
		if kv {
			r = append(r, string(iter.Value()))
		}
		limit--
		if limit <= 0 {
			break
		}
	}

	iter.Release()
	err = iter.Error()

	return
}

// Hkeys will list the hashmap keys in the range.
// Args: name string, start string, end string, limit int
func Hkeys(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	r, err = hscan(args, false, false)

	return
}

// Hrkeys will reverse list the hashmap keys in the range.
// Args: name string, start string, end string, limit int
func Hrkeys(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	r, err = hscan(args, false, true)

	return
}

// Hgetall will list all keys/value in the hashmap.
// Args: name string
func Hgetall(args [][]byte) (r []string, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	if _, err = db.Get(encodeHsizeKey(args[0]), nil); err != nil {
		return
	}

	var buf []byte
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(args[0])))...)
	buf = append(buf, args[0]...)
	ke := append(buf, []byte{0xff}...)

	iter := db.NewIterator(&util.Range{Start: buf, Limit: ke}, nil)
	for iter.Next() {
		_, key := decodeHashKey(iter.Key())
		r = append(r, string(key))
		r = append(r, string(iter.Value()))
	}
	iter.Release()
	err = iter.Error()
	return
}

// Hscan will list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hrscan will reverse list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hrscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	return
}

// Hclear will remove all value in the hashmap.
// Args: name string
func Hclear(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	return
}

// MultiHget will return multi hashmap value by keys.
func MultiHget(args [][]byte) (r []string, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// MultiHset will set multi hashmap value by keys.
func MultiHset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 && len(args)%2 == 0 {
		return nil, errNosArgs
	}

	return
}

// MultiHdel will delete multi hashmap value by keys.
func MultiHdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}
<commit_msg>Implement Hscan and Hrscan methods.<commit_after>package redlot

import (
	"strconv"

	"github.com/syndtr/goleveldb/leveldb/util"
)

func encodeHashKey(name, key []byte) (buf []byte) {
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	buf = append(buf, key...)
	return
}

func decodeHashKey(b []byte) (name, key []byte) {
	nameLen := bytesToUint32(b[1:5])
	name = b[5 : 5+nameLen]
	key = b[5+nameLen:]
	return
}

func encodeHsizeKey(name []byte) (buf []byte) {
	buf = append(buf, typeHSIZE)
	buf = append(buf, uint32ToBytes(uint32(len(name)))...)
	buf = append(buf, name...)
	return
}

func decodeHsizeKey(b []byte) (key []byte) {
	return b[5:]
}

func hashSizeIncr(name []byte, incr int) {
	hsize := encodeHsizeKey(name)

	var size uint32
	if b, err := db.Get(hsize, nil); err == nil {
		size = bytesToUint32(b)
	}

	if incr > 0 {
		size += uint32(incr)
	} else {
		size = size - uint32(0-incr)
	}
	db.Put(hsize, uint32ToBytes(size), nil)
}

// Hset will set a hashmap value by the key.
// Args: name string, key string, value any
func Hset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	hashSizeIncr(args[0], 1)
	err = db.Put(encodeHashKey(args[0], args[1]), args[2], nil)
	if err != nil {
		hashSizeIncr(args[0], -1)
		return nil, err
	}

	return
}

// Hset will return a hashmap value by the key.
// Args: name string, key string
func Hget(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}
	var b []byte
	b, err = db.Get(encodeHashKey(args[0], args[1]), nil)
	return string(b), err
}

// Hdel will delete a hashmap value by the key.
// Args: name string, key string
func Hdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

func hincr(name, key []byte, increment int) (r int64, err error) {
	hash := encodeHashKey(name, key)
	v, _ := db.Get(hash, nil)
	var number int
	var exists bool
	if len(v) != 0 {
		number, err = strconv.Atoi(string(v))
		exists = true
		if err != nil {
			return -1, errNotInt
		}
	}
	number += increment
	r = int64(number)
	err = db.Put(hash, []byte(strconv.Itoa(number)), nil)
	if err == nil && !exists {
		hashSizeIncr(name, 1)
	}
	return
}

// Hincr will incr a hashmap value by the key.
// Args: name string, key string
func Hincr(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return hincr(args[0], args[1], 1)
}

// Hincrby will incr number a hashmap value by the key.
// Args: name string, key string, value int
func Hincrby(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}
	i, e := strconv.Atoi(string(args[2]))
	if e != nil {
		return -1, errNotInt
	}

	return hincr(args[0], args[1], i)
}

// Hexists will check the hashmap key is exists.
// Args: name string, key string
func Hexists(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// Hsize will return the hashmap size.
// Args: name string
func Hsize(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	var size int64
	if b, e := db.Get(encodeHsizeKey(args[0]), nil); e != nil {
		size = -1
	} else {
		size = int64(bytesToUint32(b))
	}

	return size, nil
}

// Hlist will list all hashmap in the range.
// Args: start string, end string, limit int
func Hlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

// Hrlist will reverse list all hashmap in the range.
// Args: start string, end string, limit int
func Hrlist(args [][]byte) (r []string, err error) {
	if len(args) < 3 {
		return nil, errNosArgs
	}

	return
}

func hscan(args [][]byte, kv, reverse bool) (r []string, err error) {
	if _, err = db.Get(encodeHsizeKey(args[0]), nil); err != nil {
		return
	}

	if len(args[1]) != 0 && string(args[1]) >= string(args[2]) {
		return []string{""}, nil
	}

	var ks, ke []byte
	if len(args[1]) == 0 {
		ks = append(ks, typeHASH)
		ks = append(ks, uint32ToBytes(uint32(len(args[0])))...)
		ks = append(ks, args[0]...)
	} else {
		ks = encodeHashKey(args[0], args[1])
	}

	if len(args[2]) == 0 {
		ke = append(ks, []byte{0xff}...)
	} else {
		ke = encodeHashKey(args[0], args[2])
	}

	limit, _ := strconv.Atoi(string(args[3]))

	iter := db.NewIterator(&util.Range{Start: ks, Limit: ke}, nil)
	var iters func() bool
	if reverse {
		iter.Seek(ke)
		iters = iter.Prev
	} else {
		iters = iter.Next
	}
	for iters() {
		_, key := decodeHashKey(iter.Key())
		r = append(r, string(key))
		if kv {
			r = append(r, string(iter.Value()))
		}
		limit--
		if limit <= 0 {
			break
		}
	}

	iter.Release()
	err = iter.Error()

	return
}

// Hkeys will list the hashmap keys in the range.
// Args: name string, start string, end string, limit int
func Hkeys(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	r, err = hscan(args, false, false)

	return
}

// Hrkeys will reverse list the hashmap keys in the range.
// Args: name string, start string, end string, limit int
func Hrkeys(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	r, err = hscan(args, false, true)

	return
}

// Hgetall will list all keys/value in the hashmap.
// Args: name string
func Hgetall(args [][]byte) (r []string, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	if _, err = db.Get(encodeHsizeKey(args[0]), nil); err != nil {
		return
	}

	var buf []byte
	buf = append(buf, typeHASH)
	buf = append(buf, uint32ToBytes(uint32(len(args[0])))...)
	buf = append(buf, args[0]...)
	ke := append(buf, []byte{0xff}...)

	iter := db.NewIterator(&util.Range{Start: buf, Limit: ke}, nil)
	for iter.Next() {
		_, key := decodeHashKey(iter.Key())
		r = append(r, string(key))
		r = append(r, string(iter.Value()))
	}
	iter.Release()
	err = iter.Error()
	return
}

// Hscan will list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	r, err = hscan(args, true, false)

	return
}

// Hrscan will reverse list keys/value of the hashmap in the range.
// Args: name string, start string, end string, limit int
func Hrscan(args [][]byte) (r []string, err error) {
	if len(args) < 4 {
		return nil, errNosArgs
	}

	r, err = hscan(args, true, true)

	return
}

// Hclear will remove all value in the hashmap.
// Args: name string
func Hclear(args [][]byte) (r interface{}, err error) {
	if len(args) < 1 {
		return nil, errNosArgs
	}

	return
}

// MultiHget will return multi hashmap value by keys.
func MultiHget(args [][]byte) (r []string, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}

// MultiHset will set multi hashmap value by keys.
func MultiHset(args [][]byte) (r interface{}, err error) {
	if len(args) < 3 && len(args)%2 == 0 {
		return nil, errNosArgs
	}

	return
}

// MultiHdel will delete multi hashmap value by keys.
func MultiHdel(args [][]byte) (r interface{}, err error) {
	if len(args) < 2 {
		return nil, errNosArgs
	}

	return
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.

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

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

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

package guest

import (
	"context"
	"flag"
	"fmt"
	"io"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/vim25/types"
)

type ps struct {
	*flags.OutputFlag
	*GuestFlag

	every bool
	exit  bool
	wait  bool

	pids pidSelector
	uids uidSelector
}

type pidSelector []int64

func (s *pidSelector) String() string {
	return fmt.Sprint(*s)
}

func (s *pidSelector) Set(value string) error {
	v, err := strconv.ParseInt(value, 0, 64)
	if err != nil {
		return err
	}
	*s = append(*s, v)
	return nil
}

type uidSelector map[string]bool

func (s uidSelector) String() string {
	return ""
}

func (s uidSelector) Set(value string) error {
	s[value] = true
	return nil
}

func init() {
	cli.Register("guest.ps", &ps{})
}

func (cmd *ps) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
	cmd.OutputFlag.Register(ctx, f)

	cmd.GuestFlag, ctx = newGuestFlag(ctx)
	cmd.GuestFlag.Register(ctx, f)

	cmd.uids = make(map[string]bool)
	f.BoolVar(&cmd.every, "e", false, "Select all processes")
	f.BoolVar(&cmd.exit, "x", false, "Output exit time and code")
	f.BoolVar(&cmd.wait, "X", false, "Wait for process to exit")
	f.Var(&cmd.pids, "p", "Select by process ID")
	f.Var(&cmd.uids, "U", "Select by process UID")
}

func (cmd *ps) Process(ctx context.Context) error {
	if err := cmd.OutputFlag.Process(ctx); err != nil {
		return err
	}
	if err := cmd.GuestFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *ps) Description() string {
	return `List processes in VM.

By default, unless the '-e', '-p' or '-U' flag is specified, only processes owned
by the '-l' flag user are displayed.

The '-x' and '-X' flags only apply to processes started by vmware-tools,
such as those started with the govc guest.start command.

Examples:
  govc guest.ps -vm $name
  govc guest.ps -vm $name -e
  govc guest.ps -vm $name -p 12345
  govc guest.ps -vm $name -U root`
}

func running(procs []types.GuestProcessInfo) bool {
	for _, p := range procs {
		if p.EndTime == nil {
			return true
		}
	}
	return false
}

func (cmd *ps) list(ctx context.Context) ([]types.GuestProcessInfo, error) {
	m, err := cmd.ProcessManager()
	if err != nil {
		return nil, err
	}

	auth := cmd.Auth()

	for {
		procs, err := m.ListProcesses(ctx, auth, cmd.pids)
		if err != nil {
			return nil, err
		}

		if cmd.wait && running(procs) {
			<-time.After(time.Millisecond * 250)
			continue
		}

		return procs, nil
	}
}

func (cmd *ps) Run(ctx context.Context, f *flag.FlagSet) error {
	procs, err := cmd.list(ctx)
	if err != nil {
		return err
	}

	r := &psResult{cmd, procs}

	return cmd.WriteResult(r)
}

type psResult struct {
	cmd         *ps
	ProcessInfo []types.GuestProcessInfo
}

func (r *psResult) Write(w io.Writer) error {
	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)

	fmt.Fprintf(tw, "%s\t%s\t%s", "UID", "PID", "STIME")
	if r.cmd.exit {
		fmt.Fprintf(tw, "\t%s\t%s", "XTIME", "XCODE")
	}
	fmt.Fprint(tw, "\tCMD\n")

	if len(r.cmd.pids) != 0 {
		r.cmd.every = true
	}

	if !r.cmd.every && len(r.cmd.uids) == 0 {
		r.cmd.uids[r.cmd.auth.Username] = true
	}

	for _, p := range r.ProcessInfo {
		if r.cmd.every || r.cmd.uids[p.Owner] {
			fmt.Fprintf(tw, "%s\t%d\t%s", p.Owner, p.Pid, p.StartTime.Format("15:04"))
			if r.cmd.exit {
				etime := "-"
				ecode := "-"
				if p.EndTime != nil {
					etime = p.EndTime.Format("15:04")
					ecode = strconv.Itoa(int(p.ExitCode))
				}
				fmt.Fprintf(tw, "\t%s\t%s", etime, ecode)
			}
			fmt.Fprintf(tw, "\t%s\n", strings.TrimSpace(p.CmdLine))
		}
	}

	return tw.Flush()
}
<commit_msg>govc: increase guest.ps -X poll interval<commit_after>/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.

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

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

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

package guest

import (
	"context"
	"flag"
	"fmt"
	"io"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/vim25/types"
)

type ps struct {
	*flags.OutputFlag
	*GuestFlag

	every bool
	exit  bool
	wait  bool

	pids pidSelector
	uids uidSelector
}

type pidSelector []int64

func (s *pidSelector) String() string {
	return fmt.Sprint(*s)
}

func (s *pidSelector) Set(value string) error {
	v, err := strconv.ParseInt(value, 0, 64)
	if err != nil {
		return err
	}
	*s = append(*s, v)
	return nil
}

type uidSelector map[string]bool

func (s uidSelector) String() string {
	return ""
}

func (s uidSelector) Set(value string) error {
	s[value] = true
	return nil
}

func init() {
	cli.Register("guest.ps", &ps{})
}

func (cmd *ps) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
	cmd.OutputFlag.Register(ctx, f)

	cmd.GuestFlag, ctx = newGuestFlag(ctx)
	cmd.GuestFlag.Register(ctx, f)

	cmd.uids = make(map[string]bool)
	f.BoolVar(&cmd.every, "e", false, "Select all processes")
	f.BoolVar(&cmd.exit, "x", false, "Output exit time and code")
	f.BoolVar(&cmd.wait, "X", false, "Wait for process to exit")
	f.Var(&cmd.pids, "p", "Select by process ID")
	f.Var(&cmd.uids, "U", "Select by process UID")
}

func (cmd *ps) Process(ctx context.Context) error {
	if err := cmd.OutputFlag.Process(ctx); err != nil {
		return err
	}
	if err := cmd.GuestFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *ps) Description() string {
	return `List processes in VM.

By default, unless the '-e', '-p' or '-U' flag is specified, only processes owned
by the '-l' flag user are displayed.

The '-x' and '-X' flags only apply to processes started by vmware-tools,
such as those started with the govc guest.start command.

Examples:
  govc guest.ps -vm $name
  govc guest.ps -vm $name -e
  govc guest.ps -vm $name -p 12345
  govc guest.ps -vm $name -U root`
}

func running(procs []types.GuestProcessInfo) bool {
	for _, p := range procs {
		if p.EndTime == nil {
			return true
		}
	}
	return false
}

func (cmd *ps) list(ctx context.Context) ([]types.GuestProcessInfo, error) {
	m, err := cmd.ProcessManager()
	if err != nil {
		return nil, err
	}

	auth := cmd.Auth()

	for {
		procs, err := m.ListProcesses(ctx, auth, cmd.pids)
		if err != nil {
			return nil, err
		}

		if cmd.wait && running(procs) {
			<-time.After(time.Second)
			continue
		}

		return procs, nil
	}
}

func (cmd *ps) Run(ctx context.Context, f *flag.FlagSet) error {
	procs, err := cmd.list(ctx)
	if err != nil {
		return err
	}

	r := &psResult{cmd, procs}

	return cmd.WriteResult(r)
}

type psResult struct {
	cmd         *ps
	ProcessInfo []types.GuestProcessInfo
}

func (r *psResult) Write(w io.Writer) error {
	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)

	fmt.Fprintf(tw, "%s\t%s\t%s", "UID", "PID", "STIME")
	if r.cmd.exit {
		fmt.Fprintf(tw, "\t%s\t%s", "XTIME", "XCODE")
	}
	fmt.Fprint(tw, "\tCMD\n")

	if len(r.cmd.pids) != 0 {
		r.cmd.every = true
	}

	if !r.cmd.every && len(r.cmd.uids) == 0 {
		r.cmd.uids[r.cmd.auth.Username] = true
	}

	for _, p := range r.ProcessInfo {
		if r.cmd.every || r.cmd.uids[p.Owner] {
			fmt.Fprintf(tw, "%s\t%d\t%s", p.Owner, p.Pid, p.StartTime.Format("15:04"))
			if r.cmd.exit {
				etime := "-"
				ecode := "-"
				if p.EndTime != nil {
					etime = p.EndTime.Format("15:04")
					ecode = strconv.Itoa(int(p.ExitCode))
				}
				fmt.Fprintf(tw, "\t%s\t%s", etime, ecode)
			}
			fmt.Fprintf(tw, "\t%s\n", strings.TrimSpace(p.CmdLine))
		}
	}

	return tw.Flush()
}
<|endoftext|>


<commit_before>//
// go-sonos
// ========
//
// Copyright (c) 2012, Ian T. Richards <ianr@panix.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package sonos

import (
	"encoding/xml"
	"github.com/ianr0bkny/go-sonos/upnp"
	_ "log"
)

type AlarmClock struct {
	Svc *upnp.Service
}

func (this *AlarmClock) SetFormat(desiredTimeFormat, desiredDateFormat string) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"DesiredTimeFormat", desiredTimeFormat},
		{"DesiredDateFormat", desiredDateFormat},
	}
	response := upnp.Call(this.Svc, "SetFormat", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetFormat() (currentTimeFormat, currentDateFormat string, err error) {
	type Response struct {
		XMLName           xml.Name
		CurrentTimeFormat string
		CurrentDateFormat string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetFormat")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	currentTimeFormat = doc.CurrentTimeFormat
	currentDateFormat = doc.CurrentDateFormat
	err = doc.Error()
	return
}

func (this *AlarmClock) SetTimeZone(index int32, autoAdjustDst bool) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"Index", index},
		{"AutoAdjustDst", autoAdjustDst},
	}
	response := upnp.Call(this.Svc, "SetTimeZone", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeZone() (index int32, autoAdjustDst bool, err error) {
	type Response struct {
		XMLName       xml.Name
		Index         int32
		AutoAdjustDst bool
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeZone")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	index = doc.Index
	autoAdjustDst = doc.AutoAdjustDst
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeZoneAndRule() (index int32, autoAdjustDst bool, timeZone string, err error) {
	type Response struct {
		XMLName       xml.Name
		Index         int32
		AutoAdjustDst bool
		TimeZone      string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeZoneAndRule")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	index = doc.Index
	autoAdjustDst = doc.AutoAdjustDst
	timeZone = doc.TimeZone
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeZoneRule(index int32) (timeZone string, err error) {
	type Response struct {
		XMLName       xml.Name
		TimeZone      string
		upnp.ErrorResponse
	}
	args := []upnp.Arg {
		{"Index", index},
	}
	response := upnp.Call(this.Svc, "GetTimeZoneRule", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	timeZone = doc.TimeZone
	err = doc.Error()
	return
}

func (this *AlarmClock) SetTimeServer(desiredTimeServer string) (err error) {
	type Response struct {
		XMLName       xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg {
		{"DesiredTimeServer", desiredTimeServer},
	}
	response := upnp.Call(this.Svc, "SetTimeServer", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeServer() (currentTimeServer string, err error) {
	type Response struct {
		XMLName       xml.Name
		CurrentTimeServer string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeServer")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	currentTimeServer = doc.CurrentTimeServer
	err = doc.Error()
	return
}
<commit_msg>AlarmClock<commit_after>//
// go-sonos
// ========
//
// Copyright (c) 2012, Ian T. Richards <ianr@panix.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package sonos

import (
	"encoding/xml"
	"github.com/ianr0bkny/go-sonos/upnp"
	_ "log"
)

type AlarmClock struct {
	Svc *upnp.Service
}

func (this *AlarmClock) SetFormat(desiredTimeFormat, desiredDateFormat string) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"DesiredTimeFormat", desiredTimeFormat},
		{"DesiredDateFormat", desiredDateFormat},
	}
	response := upnp.Call(this.Svc, "SetFormat", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetFormat() (currentTimeFormat, currentDateFormat string, err error) {
	type Response struct {
		XMLName           xml.Name
		CurrentTimeFormat string
		CurrentDateFormat string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetFormat")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	currentTimeFormat = doc.CurrentTimeFormat
	currentDateFormat = doc.CurrentDateFormat
	err = doc.Error()
	return
}

func (this *AlarmClock) SetTimeZone(index int32, autoAdjustDst bool) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"Index", index},
		{"AutoAdjustDst", autoAdjustDst},
	}
	response := upnp.Call(this.Svc, "SetTimeZone", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeZone() (index int32, autoAdjustDst bool, err error) {
	type Response struct {
		XMLName       xml.Name
		Index         int32
		AutoAdjustDst bool
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeZone")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	index = doc.Index
	autoAdjustDst = doc.AutoAdjustDst
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeZoneAndRule() (index int32, autoAdjustDst bool, timeZone string, err error) {
	type Response struct {
		XMLName       xml.Name
		Index         int32
		AutoAdjustDst bool
		TimeZone      string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeZoneAndRule")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	index = doc.Index
	autoAdjustDst = doc.AutoAdjustDst
	timeZone = doc.TimeZone
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeZoneRule(index int32) (timeZone string, err error) {
	type Response struct {
		XMLName  xml.Name
		TimeZone string
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"Index", index},
	}
	response := upnp.Call(this.Svc, "GetTimeZoneRule", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	timeZone = doc.TimeZone
	err = doc.Error()
	return
}

func (this *AlarmClock) SetTimeServer(desiredTimeServer string) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"DesiredTimeServer", desiredTimeServer},
	}
	response := upnp.Call(this.Svc, "SetTimeServer", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetTimeServer() (currentTimeServer string, err error) {
	type Response struct {
		XMLName           xml.Name
		CurrentTimeServer string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeServer")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	currentTimeServer = doc.CurrentTimeServer
	err = doc.Error()
	return
}

func (this *AlarmClock) SetTimeNow(desiredTime, timeZoneForDesiredTime string) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"DesiredTime", desiredTime},
		{"TimeZoneForDesiredTime", timeZoneForDesiredTime},
	}
	response := upnp.Call(this.Svc, "SetTimeNow", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetHouseholdTimeAtStamp(timeStamp string) (householdUTCTime string, err error) {
	type Response struct {
		XMLName          xml.Name
		HouseholdUTCTime string
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"TimeStamp", timeStamp},
	}
	response := upnp.Call(this.Svc, "GetHouseholdTimeAtStamp", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	householdUTCTime = doc.HouseholdUTCTime
	err = doc.Error()
	return
}

type GetTimeNowResponse struct {
	CurrentUTCTime        string
	CurrentLocalTime      string
	CurrentTimeZone       string
	CurrentTimeGeneration uint32
}

func (this *AlarmClock) GetTimeNow() (getTimeNowResponse *GetTimeNowResponse, err error) {
	type Response struct {
		XMLName xml.Name
		GetTimeNowResponse
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetTimeNow")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	getTimeNowResponse = &doc.GetTimeNowResponse
	err = doc.Error()
	return
}

const (
	Recurrence_ONCE     = "ONCE"
	Recurrence_WEEKDAYS = "WEEKDAYS"
	Recurrence_WEEKENDS = "WEEKENDS"
	Recurrence_DAILY    = "DAILY"
)

const (
	AlarmPlayMode_NORMAL           = "NORMAL"
	AlarmPlayMode_REPEAT_ALL       = "REPEAT_ALL"
	AlarmPlayMode_SHUFFLE_NOREPEAT = "SHUFFLE_NOREPEAT"
	AlarmPlayMode_SHUFFLE          = "SHUFFLE"
)

type CreateAlarmRequest struct {
	StartLocalTime     string
	Duration           string
	Recurrence         string
	Enabled            bool
	RoomUUID           string
	ProgramURI         string
	ProgramMetaData    string
	PlayMode           string
	Volume             uint16
	IncludeLinkedZones bool
}

func (this *AlarmClock) CreateAlarm(req *CreateAlarmRequest) (assignedId uint32, err error) {
	type Response struct {
		XMLName    xml.Name
		AssignedID uint32
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"StartLocalTime", req.StartLocalTime},
		{"Duration", req.Duration},
		{"Recurrence", req.Recurrence},
		{"Enabled", req.Enabled},
		{"RoomUUID", req.RoomUUID},
		{"ProgramURI", req.ProgramURI},
		{"ProgramMetaData", req.ProgramMetaData},
		{"PlayMode", req.PlayMode},
		{"Volume", req.Volume},
		{"IncludeLinkedZones", req.IncludeLinkedZones},
	}
	response := upnp.Call(this.Svc, "CreateAlarm", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	assignedId = doc.AssignedID
	err = doc.Error()
	return
}

type UpdateAlarmRequest CreateAlarmRequest

func (this *AlarmClock) UpdateAlarm(id uint32, req *UpdateAlarmRequest) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"ID", id},
		{"StartLocalTime", req.StartLocalTime},
		{"Duration", req.Duration},
		{"Recurrence", req.Recurrence},
		{"Enabled", req.Enabled},
		{"RoomUUID", req.RoomUUID},
		{"ProgramURI", req.ProgramURI},
		{"ProgramMetaData", req.ProgramMetaData},
		{"PlayMode", req.PlayMode},
		{"Volume", req.Volume},
		{"IncludeLinkedZones", req.IncludeLinkedZones},
	}
	response := upnp.Call(this.Svc, "UpdateAlarm", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) DestroyAlarm(id uint32) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"ID", id},
	}
	response := upnp.Call(this.Svc, "DestroyAlarm", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) ListAlarms() (currentAlarmList, currentAlarmListVersion string, err error) {
	type Response struct {
		XMLName                 xml.Name
		CurrentAlarmList        string
		CurrentAlarmListVersion string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "ListAlarms")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	currentAlarmList = doc.CurrentAlarmList
	currentAlarmListVersion = doc.CurrentAlarmListVersion
	err = doc.Error()
	return
}

func (this *AlarmClock) SetDailyIndexRefreshTime(desiredDailyIndexRefreshTime string) (err error) {
	type Response struct {
		XMLName xml.Name
		upnp.ErrorResponse
	}
	args := []upnp.Arg{
		{"DesiredDailyIndexRefreshTime", desiredDailyIndexRefreshTime},
	}
	response := upnp.Call(this.Svc, "SetDailyIndexRefreshTime", args)
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	err = doc.Error()
	return
}

func (this *AlarmClock) GetDailyIndexRefreshTime() (currentDailyIndexRefreshTime string, err error) {
	type Response struct {
		XMLName                      xml.Name
		CurrentDailyIndexRefreshTime string
		upnp.ErrorResponse
	}
	response := upnp.CallVa(this.Svc, "GetDailyIndexRefreshTime")
	doc := Response{}
	xml.Unmarshal([]byte(response), &doc)
	currentDailyIndexRefreshTime = doc.CurrentDailyIndexRefreshTime
	err = doc.Error()
	return
}
<|endoftext|>


<commit_before>package credential

import (
	"errors"
	"fmt"
	"sync"

	"koding/kites/kloud/stack"
	"koding/klientctl/ctlcli"
	"koding/klientctl/kloud"
)

// DefaultClient
var DefaultClient = &Client{}

// ListOptions
type ListOptions struct {
	Team     string
	Provider string
}

// CreateOptions
type CreateOptions struct {
	Team     string
	Provider string
	Title    string
	Data     []byte
}

// Valid implements the stack.Validator interface.
func (opts *CreateOptions) Valid() error {
	if opts == nil {
		return errors.New("credential: arguments are missing")
	}

	if len(opts.Data) == 0 {
		return errors.New("credential: data is missing")
	}

	return nil
}

// Client
type Client struct {
	Kloud *kloud.Client

	once   sync.Once // for c.init()
	cached stack.Credentials
	used   map[string]string
}

// List
func (c *Client) List(opts *ListOptions) (stack.Credentials, error) {
	c.init()

	var req *stack.CredentialListRequest
	var resp stack.CredentialListResponse

	if opts != nil {
		req = &stack.CredentialListRequest{
			Team:     opts.Team,
			Provider: opts.Provider,
		}
	}

	if err := c.kloud().Tell("credential.list", req, &resp); err != nil {
		return nil, err
	}

	c.cache(resp.Credentials)

	return resp.Credentials, nil
}

// Create
func (c *Client) Create(opts *CreateOptions) (*stack.CredentialItem, error) {
	c.init()

	if err := opts.Valid(); err != nil {
		return nil, err
	}

	req := &stack.CredentialAddRequest{
		Provider: opts.Provider,
		Team:     opts.Team,
		Title:    opts.Title,
		Data:     opts.Data,
	}

	var resp stack.CredentialAddResponse

	if err := c.kloud().Tell("credential.add", req, &resp); err != nil {
		return nil, err
	}

	return &stack.CredentialItem{
		Title:      resp.Title,
		Team:       req.Team,
		Identifier: resp.Identifier,
	}, nil
}

// Use
func (c *Client) Use(identifier string) error {
	c.init()

	provider, err := c.Provider(identifier)
	if err != nil {
		return err
	}

	c.used[provider] = identifier

	return nil
}

// User
func (c *Client) Used() map[string]string {
	c.init()

	return c.used
}

// Provider
func (c *Client) Provider(identifier string) (provider string, err error) {
	c.init()

	if cred, ok := c.cached.Find(identifier); ok {
		return cred.Provider, nil
	}

	creds, err := c.List(nil)
	if err != nil {
		return "", err
	}

	c.cache(creds)

	if cred, ok := c.cached.Find(identifier); ok {
		return cred.Provider, nil
	}

	return "", fmt.Errorf("credential: %q does not exist or is not shared with the user", identifier)
}

// Close
func (c *Client) Close() (err error) {
	if len(c.cached) != 0 {
		err = nonil(
			c.kloud().Cache().SetValue("credentials", c.cached),
			c.kloud().Cache().SetValue("credentials.used", c.used),
		)
	}

	return err
}

func (c *Client) cache(credentials stack.Credentials) {
	c.init()

	for provider, creds := range credentials {
		if len(creds) == 0 {
			continue
		}

		if identifier, ok := c.used[provider]; !ok || identifier == "" {
			c.used[provider] = creds[0].Identifier
		}

		if _, ok := c.cached[provider]; !ok {
			c.cached[provider] = creds
			continue
		}

	merge:
		for _, cred := range creds {
			for _, cached := range c.cached[provider] {
				if cached.Identifier == cred.Identifier {
					continue merge
				}
			}

			c.cached[provider] = append(c.cached[provider], cred)
		}
	}
}

func (c *Client) init() {
	c.once.Do(c.readCache)
}

func (c *Client) readCache() {
	c.cached = make(stack.Credentials)
	c.used = make(map[string]string)

	// Ignoring read error, if it's non-nil then empty cache is going to
	// be used instead.
	_ = c.kloud().Cache().GetValue("credentials", &c.cached)
	_ = c.kloud().Cache().GetValue("credentials.used", &c.used)

	// Flush cache on exit.
	ctlcli.CloseOnExit(c)
}

func (c *Client) kloud() *kloud.Client {
	if c.Kloud != nil {
		return c.Kloud
	}

	return kloud.DefaultClient
}

func nonil(err ...error) error {
	for _, e := range err {
		if e != nil {
			return e
		}
	}

	return nil
}

func List(opts *ListOptions) (stack.Credentials, error)         { return DefaultClient.List(opts) }
func Create(opts *CreateOptions) (*stack.CredentialItem, error) { return DefaultClient.Create(opts) }
func Use(identifier string) error                               { return DefaultClient.Use(identifier) }
func Used() map[string]string                                   { return DefaultClient.Used() }
func Provider(identifier string) (string, error)                { return DefaultClient.Provider(identifier) }
<commit_msg>kd/kloud/credential: simplify (*Client).cache()<commit_after>package credential

import (
	"errors"
	"fmt"
	"sync"

	"koding/kites/kloud/stack"
	"koding/klientctl/ctlcli"
	"koding/klientctl/kloud"
)

// DefaultClient
var DefaultClient = &Client{}

// ListOptions
type ListOptions struct {
	Team     string
	Provider string
}

// CreateOptions
type CreateOptions struct {
	Team     string
	Provider string
	Title    string
	Data     []byte
}

// Valid implements the stack.Validator interface.
func (opts *CreateOptions) Valid() error {
	if opts == nil {
		return errors.New("credential: arguments are missing")
	}

	if len(opts.Data) == 0 {
		return errors.New("credential: data is missing")
	}

	return nil
}

// Client
type Client struct {
	Kloud *kloud.Client

	once   sync.Once // for c.init()
	cached stack.Credentials
	used   map[string]string
}

// List
func (c *Client) List(opts *ListOptions) (stack.Credentials, error) {
	c.init()

	var req *stack.CredentialListRequest
	var resp stack.CredentialListResponse

	if opts != nil {
		req = &stack.CredentialListRequest{
			Team:     opts.Team,
			Provider: opts.Provider,
		}
	}

	if err := c.kloud().Tell("credential.list", req, &resp); err != nil {
		return nil, err
	}

	c.cache(resp.Credentials)

	return resp.Credentials, nil
}

// Create
func (c *Client) Create(opts *CreateOptions) (*stack.CredentialItem, error) {
	c.init()

	if err := opts.Valid(); err != nil {
		return nil, err
	}

	req := &stack.CredentialAddRequest{
		Provider: opts.Provider,
		Team:     opts.Team,
		Title:    opts.Title,
		Data:     opts.Data,
	}

	var resp stack.CredentialAddResponse

	if err := c.kloud().Tell("credential.add", req, &resp); err != nil {
		return nil, err
	}

	return &stack.CredentialItem{
		Title:      resp.Title,
		Team:       req.Team,
		Identifier: resp.Identifier,
	}, nil
}

// Use
func (c *Client) Use(identifier string) error {
	c.init()

	provider, err := c.Provider(identifier)
	if err != nil {
		return err
	}

	c.used[provider] = identifier

	return nil
}

// User
func (c *Client) Used() map[string]string {
	c.init()

	return c.used
}

// Provider
func (c *Client) Provider(identifier string) (provider string, err error) {
	c.init()

	if cred, ok := c.cached.Find(identifier); ok {
		return cred.Provider, nil
	}

	creds, err := c.List(nil)
	if err != nil {
		return "", err
	}

	c.cache(creds)

	if cred, ok := c.cached.Find(identifier); ok {
		return cred.Provider, nil
	}

	return "", fmt.Errorf("credential: %q does not exist or is not shared with the user", identifier)
}

// Close
func (c *Client) Close() (err error) {
	if len(c.cached) != 0 {
		err = nonil(
			c.kloud().Cache().SetValue("credentials", c.cached),
			c.kloud().Cache().SetValue("credentials.used", c.used),
		)
	}

	return err
}

func (c *Client) cache(credentials stack.Credentials) {
	c.init()

	for provider, creds := range credentials {
		if len(creds) == 0 {
			continue
		}

		if identifier, ok := c.used[provider]; !ok || identifier == "" {
			c.used[provider] = creds[0].Identifier
		}

		if _, ok := c.cached[provider]; !ok {
			c.cached[provider] = creds
			continue
		}

		for _, cred := range creds {
			if _, ok := c.cached.Find(cred.Identifier); ok {
				continue
			}

			c.cached[provider] = append(c.cached[provider], cred)
		}
	}
}

func (c *Client) init() {
	c.once.Do(c.readCache)
}

func (c *Client) readCache() {
	c.cached = make(stack.Credentials)
	c.used = make(map[string]string)

	// Ignoring read error, if it's non-nil then empty cache is going to
	// be used instead.
	_ = c.kloud().Cache().GetValue("credentials", &c.cached)
	_ = c.kloud().Cache().GetValue("credentials.used", &c.used)

	// Flush cache on exit.
	ctlcli.CloseOnExit(c)
}

func (c *Client) kloud() *kloud.Client {
	if c.Kloud != nil {
		return c.Kloud
	}

	return kloud.DefaultClient
}

func nonil(err ...error) error {
	for _, e := range err {
		if e != nil {
			return e
		}
	}

	return nil
}

func List(opts *ListOptions) (stack.Credentials, error)         { return DefaultClient.List(opts) }
func Create(opts *CreateOptions) (*stack.CredentialItem, error) { return DefaultClient.Create(opts) }
func Use(identifier string) error                               { return DefaultClient.Use(identifier) }
func Used() map[string]string                                   { return DefaultClient.Used() }
func Provider(identifier string) (string, error)                { return DefaultClient.Provider(identifier) }
<|endoftext|>


<commit_before>package realtime

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"strconv"
	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"

	"github.com/streadway/amqp"
)

type Action func(*RealtimeWorkerController, []byte) error

type RealtimeWorkerController struct {
	routes  map[string]Action
	log     logging.Logger
	rmqConn *amqp.Connection
}

func (r *RealtimeWorkerController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	r.log.Error("an error occured deleting realtime event", err)
	delivery.Ack(false)
	return false
}

func NewRealtimeWorkerController(rmq *rabbitmq.RabbitMQ, log logging.Logger) (*RealtimeWorkerController, error) {
	rmqConn, err := rmq.Connect("NewRealtimeWorkerController")
	if err != nil {
		return nil, err
	}

	ffc := &RealtimeWorkerController{
		log:     log,
		rmqConn: rmqConn.Conn(),
	}

	routes := map[string]Action{
		"api.channel_message_created": (*RealtimeWorkerController).MessageSaved,
		"api.channel_message_updated": (*RealtimeWorkerController).MessageUpdated,
		"api.channel_message_deleted": (*RealtimeWorkerController).MessageDeleted,

		"api.interaction_created": (*RealtimeWorkerController).InteractionSaved,
		"api.interaction_deleted": (*RealtimeWorkerController).InteractionDeleted,

		"api.message_reply_created": (*RealtimeWorkerController).MessageReplySaved,
		"api.message_reply_deleted": (*RealtimeWorkerController).MessageReplyDeleted,

		"api.channel_message_list_created": (*RealtimeWorkerController).MessageListSaved,
		"api.channel_message_list_updated": (*RealtimeWorkerController).MessageListUpdated,
		"api.channel_message_list_deleted": (*RealtimeWorkerController).MessageListDeleted,

		"api.channel_participant_created": (*RealtimeWorkerController).ChannelParticipantAdded,
		"api.channel_participant_deleted": (*RealtimeWorkerController).ChannelParticipantRemoved,
	}

	ffc.routes = routes

	return ffc, nil
}

func (f *RealtimeWorkerController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

func mapMessageToChannelMessage(data []byte) (*models.ChannelMessage, error) {
	cm := models.NewChannelMessage()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func mapMessageToChannelMessageList(data []byte) (*models.ChannelMessageList, error) {
	cm := models.NewChannelMessageList()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func mapMessageToInteraction(data []byte) (*models.Interaction, error) {
	i := models.NewInteraction()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

func mapMessageToMessageReply(data []byte) (*models.MessageReply, error) {
	i := models.NewMessageReply()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

// no operation for message save for now
func (f *RealtimeWorkerController) MessageSaved(data []byte) error {
	return nil
}

// no operation for message delete for now
// channel_message_delete will handle message deletions from the
func (f *RealtimeWorkerController) MessageDeleted(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageUpdated(data []byte) error {
	cm, err := mapMessageToChannelMessage(data)
	if err != nil {
		return err
	}

	// this is here for sending
	// old account id in message updated event
	container, err := cm.BuildEmptyMessageContainer()
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(cm.GetId(), container, "updateInstance")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) ChannelParticipantAdded(data []byte) error {
	return f.handleChannelParticipantEvent("AddedToChannel", data)
}

func (f *RealtimeWorkerController) ChannelParticipantRemoved(data []byte) error {
	return f.handleChannelParticipantEvent("RemovedFromChannel", data)
}

func (f *RealtimeWorkerController) handleChannelParticipantEvent(eventName string, data []byte) error {
	cp := models.NewChannelParticipant()
	if err := json.Unmarshal(data, cp); err != nil {
		return err
	}

	c := models.NewChannel()
	if err := c.ById(cp.ChannelId); err != nil {
		return err
	}

	return f.sendNotification(cp.AccountId, eventName, c)
}

func (f *RealtimeWorkerController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent("InteractionAdded", data)
}

func (f *RealtimeWorkerController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent("InteractionRemoved", data)
}

func (f *RealtimeWorkerController) handleInteractionEvent(eventName string, data []byte) error {
	i, err := mapMessageToInteraction(data)
	if err != nil {
		return err
	}

	count, err := i.Count(i.TypeConstant)
	if err != nil {
		return err
	}

	oldId, err := models.AccountOldIdById(i.AccountId)
	if err != nil {
		return err
	}

	res := map[string]interface{}{
		"messageId":    i.MessageId,
		"accountId":    i.AccountId,
		"accountOldId": oldId,
		"typeConstant": i.TypeConstant,
		"count":        count,
	}

	err = f.sendInstanceEvent(i.MessageId, res, eventName)
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplySaved(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	reply := models.NewChannelMessage()
	if err := reply.ById(i.ReplyId); err != nil {
		return err
	}

	cmc, err := reply.BuildEmptyMessageContainer()
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, cmc, "ReplyAdded")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplyDeleted(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, i, "ReplyRemoved")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

// send message to the channel
func (f *RealtimeWorkerController) MessageListSaved(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageAdded")
	if err != nil {
		return err
	}

	return nil
}

// no operation for channel_message_list_updated event
func (f *RealtimeWorkerController) MessageListUpdated(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageListDeleted(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageRemoved")
	if err != nil {
		return err
	}
	return nil
}

func (f *RealtimeWorkerController) sendInstanceEvent(instanceId int64, message interface{}, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	routingKey := "oid." + strconv.FormatInt(instanceId, 10) + ".event." + eventName

	updateMessage, err := json.Marshal(message)
	if err != nil {
		return err
	}

	updateArr := make([]string, 1)
	if eventName == "updateInstance" {
		updateArr[0] = fmt.Sprintf("{\"$set\":%s}", string(updateMessage))
	} else {
		updateArr[0] = string(updateMessage)
	}

	msg, err := json.Marshal(updateArr)
	if err != nil {
		return err
	}

	return channel.Publish(
		"updateInstances", // exchange name
		routingKey,        // routing key
		false,             // mandatory
		false,             // immediate
		amqp.Publishing{Body: msg}, // message
	)
}

func (f *RealtimeWorkerController) sendChannelEvent(cml *models.ChannelMessageList, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	secretNames, err := fetchSecretNames(cml.ChannelId)
	if err != nil {
		return err
	}

	// if we dont have any secret names, just return
	if len(secretNames) < 1 {
		f.log.Info("Channel %d doest have any secret name", cml.ChannelId)
		return nil
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(cml.MessageId); err != nil {
		return err
	}

	byteMessage, err := json.Marshal(cm)
	if err != nil {
		return err
	}

	for _, secretName := range secretNames {
		routingKey := "socialapi.channelsecret." + secretName + "." + eventName

		if err := channel.Publish(
			"broker",   // exchange name
			routingKey, // routing key
			false,      // mandatory
			false,      // immediate
			amqp.Publishing{Body: byteMessage}, // message
		); err != nil {
			return err
		}
	}
	return nil
}

func fetchSecretNames(channelId int64) ([]string, error) {
	names := make([]string, 0)
	c, err := fetchChannel(channelId)
	if err != nil {
		return names, err
	}

	name := fmt.Sprintf(
		"socialapi-group-%s-type-%s-name-%s",
		c.GroupName,
		c.TypeConstant,
		c.Name,
	)

	names, err = modelhelper.FetchFlattenedSecretName(name)
	return names, nil
}

func fetchChannel(channelId int64) (*models.Channel, error) {
	c := models.NewChannel()
	if err := c.ById(channelId); err != nil {
		return nil, err
	}
	return c, nil
}

func (f *RealtimeWorkerController) sendNotification(accountId int64, eventName string, data interface{}) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	oldAccount, err := modelhelper.GetAccountBySocialApiId(accountId)
	if err != nil {
		return err
	}

	notification := map[string]interface{}{
		"event":    eventName,
		"contents": data,
	}

	byteNotification, err := json.Marshal(notification)
	if err != nil {
		return err
	}

	return channel.Publish(
		"notification",
		oldAccount.Profile.Nickname, // this is routing key
		false,
		false,
		amqp.Publishing{Body: byteNotification},
	)
}
<commit_msg>Social: remove mapping of messages from realtime worker<commit_after>package realtime

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"strconv"
	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"

	"github.com/streadway/amqp"
)

type Action func(*RealtimeWorkerController, []byte) error

type RealtimeWorkerController struct {
	routes  map[string]Action
	log     logging.Logger
	rmqConn *amqp.Connection
}

func (r *RealtimeWorkerController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	r.log.Error("an error occured deleting realtime event", err)
	delivery.Ack(false)
	return false
}

func NewRealtimeWorkerController(rmq *rabbitmq.RabbitMQ, log logging.Logger) (*RealtimeWorkerController, error) {
	rmqConn, err := rmq.Connect("NewRealtimeWorkerController")
	if err != nil {
		return nil, err
	}

	ffc := &RealtimeWorkerController{
		log:     log,
		rmqConn: rmqConn.Conn(),
	}

	routes := map[string]Action{
		"api.channel_message_created": (*RealtimeWorkerController).MessageSaved,
		"api.channel_message_updated": (*RealtimeWorkerController).MessageUpdated,
		"api.channel_message_deleted": (*RealtimeWorkerController).MessageDeleted,

		"api.interaction_created": (*RealtimeWorkerController).InteractionSaved,
		"api.interaction_deleted": (*RealtimeWorkerController).InteractionDeleted,

		"api.message_reply_created": (*RealtimeWorkerController).MessageReplySaved,
		"api.message_reply_deleted": (*RealtimeWorkerController).MessageReplyDeleted,

		"api.channel_message_list_created": (*RealtimeWorkerController).MessageListSaved,
		"api.channel_message_list_updated": (*RealtimeWorkerController).MessageListUpdated,
		"api.channel_message_list_deleted": (*RealtimeWorkerController).MessageListDeleted,

		"api.channel_participant_created": (*RealtimeWorkerController).ChannelParticipantAdded,
		"api.channel_participant_deleted": (*RealtimeWorkerController).ChannelParticipantRemoved,
	}

	ffc.routes = routes

	return ffc, nil
}

func (f *RealtimeWorkerController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(f, data)
}

// no operation for message save for now
func (f *RealtimeWorkerController) MessageSaved(data []byte) error {
	return nil
}

// no operation for message delete for now
// channel_message_delete will handle message deletions from the
func (f *RealtimeWorkerController) MessageDeleted(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageUpdated(data []byte) error {
	cm, err := mapMessageToChannelMessage(data)
	if err != nil {
		return err
	}

	// this is here for sending
	// old account id in message updated event
	container, err := cm.BuildEmptyMessageContainer()
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(cm.GetId(), container, "updateInstance")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) ChannelParticipantAdded(data []byte) error {
	return f.handleChannelParticipantEvent("AddedToChannel", data)
}

func (f *RealtimeWorkerController) ChannelParticipantRemoved(data []byte) error {
	return f.handleChannelParticipantEvent("RemovedFromChannel", data)
}

func (f *RealtimeWorkerController) handleChannelParticipantEvent(eventName string, data []byte) error {
	cp := models.NewChannelParticipant()
	if err := json.Unmarshal(data, cp); err != nil {
		return err
	}

	c := models.NewChannel()
	if err := c.ById(cp.ChannelId); err != nil {
		return err
	}

	return f.sendNotification(cp.AccountId, eventName, c)
}

func (f *RealtimeWorkerController) InteractionSaved(data []byte) error {
	return f.handleInteractionEvent("InteractionAdded", data)
}

func (f *RealtimeWorkerController) InteractionDeleted(data []byte) error {
	return f.handleInteractionEvent("InteractionRemoved", data)
}

func (f *RealtimeWorkerController) handleInteractionEvent(eventName string, data []byte) error {
	i, err := mapMessageToInteraction(data)
	if err != nil {
		return err
	}

	count, err := i.Count(i.TypeConstant)
	if err != nil {
		return err
	}

	oldId, err := models.AccountOldIdById(i.AccountId)
	if err != nil {
		return err
	}

	res := map[string]interface{}{
		"messageId":    i.MessageId,
		"accountId":    i.AccountId,
		"accountOldId": oldId,
		"typeConstant": i.TypeConstant,
		"count":        count,
	}

	err = f.sendInstanceEvent(i.MessageId, res, eventName)
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplySaved(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	reply := models.NewChannelMessage()
	if err := reply.ById(i.ReplyId); err != nil {
		return err
	}

	cmc, err := reply.BuildEmptyMessageContainer()
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, cmc, "ReplyAdded")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

func (f *RealtimeWorkerController) MessageReplyDeleted(data []byte) error {
	i, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	err = f.sendInstanceEvent(i.MessageId, i, "ReplyRemoved")
	if err != nil {
		fmt.Println(err)
		return err
	}

	return nil
}

// send message to the channel
func (f *RealtimeWorkerController) MessageListSaved(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageAdded")
	if err != nil {
		return err
	}

	return nil
}

// no operation for channel_message_list_updated event
func (f *RealtimeWorkerController) MessageListUpdated(data []byte) error {
	return nil
}

func (f *RealtimeWorkerController) MessageListDeleted(data []byte) error {
	cml, err := mapMessageToChannelMessageList(data)
	if err != nil {
		return err
	}

	err = f.sendChannelEvent(cml, "MessageRemoved")
	if err != nil {
		return err
	}
	return nil
}

func (f *RealtimeWorkerController) sendInstanceEvent(instanceId int64, message interface{}, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	routingKey := "oid." + strconv.FormatInt(instanceId, 10) + ".event." + eventName

	updateMessage, err := json.Marshal(message)
	if err != nil {
		return err
	}

	updateArr := make([]string, 1)
	if eventName == "updateInstance" {
		updateArr[0] = fmt.Sprintf("{\"$set\":%s}", string(updateMessage))
	} else {
		updateArr[0] = string(updateMessage)
	}

	msg, err := json.Marshal(updateArr)
	if err != nil {
		return err
	}

	return channel.Publish(
		"updateInstances", // exchange name
		routingKey,        // routing key
		false,             // mandatory
		false,             // immediate
		amqp.Publishing{Body: msg}, // message
	)
}

func (f *RealtimeWorkerController) sendChannelEvent(cml *models.ChannelMessageList, eventName string) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	secretNames, err := fetchSecretNames(cml.ChannelId)
	if err != nil {
		return err
	}

	// if we dont have any secret names, just return
	if len(secretNames) < 1 {
		f.log.Info("Channel %d doest have any secret name", cml.ChannelId)
		return nil
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(cml.MessageId); err != nil {
		return err
	}

	byteMessage, err := json.Marshal(cm)
	if err != nil {
		return err
	}

	for _, secretName := range secretNames {
		routingKey := "socialapi.channelsecret." + secretName + "." + eventName

		if err := channel.Publish(
			"broker",   // exchange name
			routingKey, // routing key
			false,      // mandatory
			false,      // immediate
			amqp.Publishing{Body: byteMessage}, // message
		); err != nil {
			return err
		}
	}
	return nil
}

func fetchSecretNames(channelId int64) ([]string, error) {
	names := make([]string, 0)
	c, err := fetchChannel(channelId)
	if err != nil {
		return names, err
	}

	name := fmt.Sprintf(
		"socialapi-group-%s-type-%s-name-%s",
		c.GroupName,
		c.TypeConstant,
		c.Name,
	)

	names, err = modelhelper.FetchFlattenedSecretName(name)
	return names, nil
}

func fetchChannel(channelId int64) (*models.Channel, error) {
	c := models.NewChannel()
	if err := c.ById(channelId); err != nil {
		return nil, err
	}
	return c, nil
}

func (f *RealtimeWorkerController) sendNotification(accountId int64, eventName string, data interface{}) error {
	channel, err := f.rmqConn.Channel()
	if err != nil {
		return err
	}
	defer channel.Close()

	oldAccount, err := modelhelper.GetAccountBySocialApiId(accountId)
	if err != nil {
		return err
	}

	notification := map[string]interface{}{
		"event":    eventName,
		"contents": data,
	}

	byteNotification, err := json.Marshal(notification)
	if err != nil {
		return err
	}

	return channel.Publish(
		"notification",
		oldAccount.Profile.Nickname, // this is routing key
		false,
		false,
		amqp.Publishing{Body: byteNotification},
	)
}
<|endoftext|>