<commit_before>package main

import (
	"log"

	lua "github.com/yuin/gopher-lua"
	luar "layeh.com/gopher-luar"

	"github.com/zyedidia/micro/internal/action"
	"github.com/zyedidia/micro/internal/buffer"
	"github.com/zyedidia/micro/internal/config"
	"github.com/zyedidia/micro/internal/display"
	ulua "github.com/zyedidia/micro/internal/lua"
	"github.com/zyedidia/micro/internal/screen"
	"github.com/zyedidia/micro/internal/shell"
	"github.com/zyedidia/micro/internal/util"
)

func init() {
	ulua.L = lua.NewState()
	ulua.L.SetGlobal("import", luar.New(ulua.L, LuaImport))
}

func LuaImport(pkg string) *lua.LTable {
	switch pkg {
	case "micro":
		return luaImportMicro()
	case "micro/shell":
		return luaImportMicroShell()
	case "micro/buffer":
		return luaImportMicroBuffer()
	case "micro/config":
		return luaImportMicroConfig()
	case "micro/util":
		return luaImportMicroUtil()
	default:
		return ulua.Import(pkg)
	}
}

func luaImportMicro() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "TermMessage", luar.New(ulua.L, screen.TermMessage))
	ulua.L.SetField(pkg, "TermError", luar.New(ulua.L, screen.TermError))
	ulua.L.SetField(pkg, "InfoBar", luar.New(ulua.L, action.GetInfoBar))
	ulua.L.SetField(pkg, "Log", luar.New(ulua.L, log.Println))
	ulua.L.SetField(pkg, "SetStatusInfoFn", luar.New(ulua.L, display.SetStatusInfoFnLua))

	return pkg
}

func luaImportMicroConfig() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "MakeCommand", luar.New(ulua.L, action.LuaMakeCommand))
	ulua.L.SetField(pkg, "FileComplete", luar.New(ulua.L, buffer.FileComplete))
	ulua.L.SetField(pkg, "HelpComplete", luar.New(ulua.L, action.HelpComplete))
	ulua.L.SetField(pkg, "OptionComplete", luar.New(ulua.L, action.OptionComplete))
	ulua.L.SetField(pkg, "OptionValueComplete", luar.New(ulua.L, action.OptionValueComplete))
	ulua.L.SetField(pkg, "NoComplete", luar.New(ulua.L, nil))
	ulua.L.SetField(pkg, "TryBindKey", luar.New(ulua.L, action.TryBindKey))
	ulua.L.SetField(pkg, "Reload", luar.New(ulua.L, action.ReloadConfig))
	ulua.L.SetField(pkg, "AddRuntimeFileFromMemory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
	ulua.L.SetField(pkg, "AddRuntimeFilesFromDirectory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
	ulua.L.SetField(pkg, "AddRuntimeFile", luar.New(ulua.L, config.PluginAddRuntimeFile))
	ulua.L.SetField(pkg, "ListRuntimeFiles", luar.New(ulua.L, config.PluginListRuntimeFiles))
	ulua.L.SetField(pkg, "ReadRuntimeFile", luar.New(ulua.L, config.PluginReadRuntimeFile))
	ulua.L.SetField(pkg, "RTColorscheme", luar.New(ulua.L, config.RTColorscheme))
	ulua.L.SetField(pkg, "RTSyntax", luar.New(ulua.L, config.RTSyntax))
	ulua.L.SetField(pkg, "RTHelp", luar.New(ulua.L, config.RTHelp))
	ulua.L.SetField(pkg, "RTPlugin", luar.New(ulua.L, config.RTPlugin))
	ulua.L.SetField(pkg, "RegisterCommonOption", luar.New(ulua.L, config.RegisterCommonOption))
	ulua.L.SetField(pkg, "RegisterGlobalOption", luar.New(ulua.L, config.RegisterGlobalOption))

	return pkg
}

func luaImportMicroShell() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "ExecCommand", luar.New(ulua.L, shell.ExecCommand))
	ulua.L.SetField(pkg, "RunCommand", luar.New(ulua.L, shell.RunCommand))
	ulua.L.SetField(pkg, "RunBackgroundShell", luar.New(ulua.L, shell.RunBackgroundShell))
	ulua.L.SetField(pkg, "RunInteractiveShell", luar.New(ulua.L, shell.RunInteractiveShell))
	ulua.L.SetField(pkg, "JobStart", luar.New(ulua.L, shell.JobStart))
	ulua.L.SetField(pkg, "JobSpawn", luar.New(ulua.L, shell.JobSpawn))
	ulua.L.SetField(pkg, "JobStop", luar.New(ulua.L, shell.JobStop))
	ulua.L.SetField(pkg, "JobSend", luar.New(ulua.L, shell.JobSend))
	ulua.L.SetField(pkg, "RunTermEmulator", luar.New(ulua.L, action.RunTermEmulator))
	ulua.L.SetField(pkg, "TermEmuSupported", luar.New(ulua.L, action.TermEmuSupported))

	return pkg
}

func luaImportMicroBuffer() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "NewMessage", luar.New(ulua.L, buffer.NewMessage))
	ulua.L.SetField(pkg, "NewMessageAtLine", luar.New(ulua.L, buffer.NewMessageAtLine))
	ulua.L.SetField(pkg, "MTInfo", luar.New(ulua.L, buffer.MTInfo))
	ulua.L.SetField(pkg, "MTWarning", luar.New(ulua.L, buffer.MTWarning))
	ulua.L.SetField(pkg, "MTError", luar.New(ulua.L, buffer.MTError))
	ulua.L.SetField(pkg, "Loc", luar.New(ulua.L, func(x, y int) buffer.Loc {
		return buffer.Loc{x, y}
	}))
	ulua.L.SetField(pkg, "BTDefault", luar.New(ulua.L, buffer.BTDefault.Kind))
	ulua.L.SetField(pkg, "BTHelp", luar.New(ulua.L, buffer.BTHelp.Kind))
	ulua.L.SetField(pkg, "BTLog", luar.New(ulua.L, buffer.BTLog.Kind))
	ulua.L.SetField(pkg, "BTScratch", luar.New(ulua.L, buffer.BTScratch.Kind))
	ulua.L.SetField(pkg, "BTRaw", luar.New(ulua.L, buffer.BTRaw.Kind))
	ulua.L.SetField(pkg, "BTInfo", luar.New(ulua.L, buffer.BTInfo.Kind))
	ulua.L.SetField(pkg, "NewBufferFromFile", luar.New(ulua.L, func(path string) (*buffer.Buffer, error) {
		return buffer.NewBufferFromFile(path, buffer.BTDefault)
	}))
	ulua.L.SetField(pkg, "ByteOffset", luar.New(ulua.L, buffer.ByteOffset))

	return pkg
}

func luaImportMicroUtil() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "RuneAt", luar.New(ulua.L, util.LuaRuneAt))
	ulua.L.SetField(pkg, "GetLeadingWhitespace", luar.New(ulua.L, util.LuaGetLeadingWhitespace))
	ulua.L.SetField(pkg, "IsWordChar", luar.New(ulua.L, util.LuaIsWordChar))

	return pkg
}
<commit_msg>Add GetGlobalOption access for plugins<commit_after>package main

import (
	"log"

	lua "github.com/yuin/gopher-lua"
	luar "layeh.com/gopher-luar"

	"github.com/zyedidia/micro/internal/action"
	"github.com/zyedidia/micro/internal/buffer"
	"github.com/zyedidia/micro/internal/config"
	"github.com/zyedidia/micro/internal/display"
	ulua "github.com/zyedidia/micro/internal/lua"
	"github.com/zyedidia/micro/internal/screen"
	"github.com/zyedidia/micro/internal/shell"
	"github.com/zyedidia/micro/internal/util"
)

func init() {
	ulua.L = lua.NewState()
	ulua.L.SetGlobal("import", luar.New(ulua.L, LuaImport))
}

func LuaImport(pkg string) *lua.LTable {
	switch pkg {
	case "micro":
		return luaImportMicro()
	case "micro/shell":
		return luaImportMicroShell()
	case "micro/buffer":
		return luaImportMicroBuffer()
	case "micro/config":
		return luaImportMicroConfig()
	case "micro/util":
		return luaImportMicroUtil()
	default:
		return ulua.Import(pkg)
	}
}

func luaImportMicro() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "TermMessage", luar.New(ulua.L, screen.TermMessage))
	ulua.L.SetField(pkg, "TermError", luar.New(ulua.L, screen.TermError))
	ulua.L.SetField(pkg, "InfoBar", luar.New(ulua.L, action.GetInfoBar))
	ulua.L.SetField(pkg, "Log", luar.New(ulua.L, log.Println))
	ulua.L.SetField(pkg, "SetStatusInfoFn", luar.New(ulua.L, display.SetStatusInfoFnLua))

	return pkg
}

func luaImportMicroConfig() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "MakeCommand", luar.New(ulua.L, action.LuaMakeCommand))
	ulua.L.SetField(pkg, "FileComplete", luar.New(ulua.L, buffer.FileComplete))
	ulua.L.SetField(pkg, "HelpComplete", luar.New(ulua.L, action.HelpComplete))
	ulua.L.SetField(pkg, "OptionComplete", luar.New(ulua.L, action.OptionComplete))
	ulua.L.SetField(pkg, "OptionValueComplete", luar.New(ulua.L, action.OptionValueComplete))
	ulua.L.SetField(pkg, "NoComplete", luar.New(ulua.L, nil))
	ulua.L.SetField(pkg, "TryBindKey", luar.New(ulua.L, action.TryBindKey))
	ulua.L.SetField(pkg, "Reload", luar.New(ulua.L, action.ReloadConfig))
	ulua.L.SetField(pkg, "AddRuntimeFileFromMemory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
	ulua.L.SetField(pkg, "AddRuntimeFilesFromDirectory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
	ulua.L.SetField(pkg, "AddRuntimeFile", luar.New(ulua.L, config.PluginAddRuntimeFile))
	ulua.L.SetField(pkg, "ListRuntimeFiles", luar.New(ulua.L, config.PluginListRuntimeFiles))
	ulua.L.SetField(pkg, "ReadRuntimeFile", luar.New(ulua.L, config.PluginReadRuntimeFile))
	ulua.L.SetField(pkg, "RTColorscheme", luar.New(ulua.L, config.RTColorscheme))
	ulua.L.SetField(pkg, "RTSyntax", luar.New(ulua.L, config.RTSyntax))
	ulua.L.SetField(pkg, "RTHelp", luar.New(ulua.L, config.RTHelp))
	ulua.L.SetField(pkg, "RTPlugin", luar.New(ulua.L, config.RTPlugin))
	ulua.L.SetField(pkg, "RegisterCommonOption", luar.New(ulua.L, config.RegisterCommonOption))
	ulua.L.SetField(pkg, "RegisterGlobalOption", luar.New(ulua.L, config.RegisterGlobalOption))
	ulua.L.SetField(pkg, "GetGlobalOption", luar.New(ulua.L, config.GetGlobalOption))

	return pkg
}

func luaImportMicroShell() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "ExecCommand", luar.New(ulua.L, shell.ExecCommand))
	ulua.L.SetField(pkg, "RunCommand", luar.New(ulua.L, shell.RunCommand))
	ulua.L.SetField(pkg, "RunBackgroundShell", luar.New(ulua.L, shell.RunBackgroundShell))
	ulua.L.SetField(pkg, "RunInteractiveShell", luar.New(ulua.L, shell.RunInteractiveShell))
	ulua.L.SetField(pkg, "JobStart", luar.New(ulua.L, shell.JobStart))
	ulua.L.SetField(pkg, "JobSpawn", luar.New(ulua.L, shell.JobSpawn))
	ulua.L.SetField(pkg, "JobStop", luar.New(ulua.L, shell.JobStop))
	ulua.L.SetField(pkg, "JobSend", luar.New(ulua.L, shell.JobSend))
	ulua.L.SetField(pkg, "RunTermEmulator", luar.New(ulua.L, action.RunTermEmulator))
	ulua.L.SetField(pkg, "TermEmuSupported", luar.New(ulua.L, action.TermEmuSupported))

	return pkg
}

func luaImportMicroBuffer() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "NewMessage", luar.New(ulua.L, buffer.NewMessage))
	ulua.L.SetField(pkg, "NewMessageAtLine", luar.New(ulua.L, buffer.NewMessageAtLine))
	ulua.L.SetField(pkg, "MTInfo", luar.New(ulua.L, buffer.MTInfo))
	ulua.L.SetField(pkg, "MTWarning", luar.New(ulua.L, buffer.MTWarning))
	ulua.L.SetField(pkg, "MTError", luar.New(ulua.L, buffer.MTError))
	ulua.L.SetField(pkg, "Loc", luar.New(ulua.L, func(x, y int) buffer.Loc {
		return buffer.Loc{x, y}
	}))
	ulua.L.SetField(pkg, "BTDefault", luar.New(ulua.L, buffer.BTDefault.Kind))
	ulua.L.SetField(pkg, "BTHelp", luar.New(ulua.L, buffer.BTHelp.Kind))
	ulua.L.SetField(pkg, "BTLog", luar.New(ulua.L, buffer.BTLog.Kind))
	ulua.L.SetField(pkg, "BTScratch", luar.New(ulua.L, buffer.BTScratch.Kind))
	ulua.L.SetField(pkg, "BTRaw", luar.New(ulua.L, buffer.BTRaw.Kind))
	ulua.L.SetField(pkg, "BTInfo", luar.New(ulua.L, buffer.BTInfo.Kind))
	ulua.L.SetField(pkg, "NewBufferFromFile", luar.New(ulua.L, func(path string) (*buffer.Buffer, error) {
		return buffer.NewBufferFromFile(path, buffer.BTDefault)
	}))
	ulua.L.SetField(pkg, "ByteOffset", luar.New(ulua.L, buffer.ByteOffset))

	return pkg
}

func luaImportMicroUtil() *lua.LTable {
	pkg := ulua.L.NewTable()

	ulua.L.SetField(pkg, "RuneAt", luar.New(ulua.L, util.LuaRuneAt))
	ulua.L.SetField(pkg, "GetLeadingWhitespace", luar.New(ulua.L, util.LuaGetLeadingWhitespace))
	ulua.L.SetField(pkg, "IsWordChar", luar.New(ulua.L, util.LuaIsWordChar))

	return pkg
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path"

	"github.com/weppos/pslint"
)

func usage() {
	bin := path.Base(os.Args[0])
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", bin)
	fmt.Fprintf(os.Stderr, "\t%s [flags] # check from stdin\n", bin)
	fmt.Fprintf(os.Stderr, "\t%s [flags] --file path # check the content of file \n", bin)
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func init() {
	flag.Usage = usage
	flag.Parse()
}

func main() {
	switch flag.NArg() {
	case 0:
		fmt.Println(pslint.Hello())
	default:
		flag.Usage()
		os.Exit(2)
	}
}
<commit_msg>Quick way to read from pipe<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
	"io/ioutil"

	//"github.com/weppos/pslint"
)

func usage() {
	bin := path.Base(os.Args[0])
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", bin)
	fmt.Fprintf(os.Stderr, "\t%s [flags] # check from stdin\n", bin)
	fmt.Fprintf(os.Stderr, "\t%s [flags] --file path # check the content of file \n", bin)
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

func init() {
	flag.Usage = usage
	flag.Parse()
}

func main() {
	switch flag.NArg() {
	case 0:
		fi, _ := os.Stdin.Stat()
		if fi.Mode() & os.ModeNamedPipe == 0 {
			flag.Usage()
			os.Exit(2)
		} else {
			bytes, _ := ioutil.ReadAll(os.Stdin)
			fmt.Print(string(bytes))
		}
	default:
		flag.Usage()
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/lmas/ss13_se"
)

func main() {
	// TODO: load config from a toml file
	conf := ss13_se.Conf{
		WebAddr:       ":8000",
		ReadTimeout:   30 * time.Second,
		WriteTimeout:  30 * time.Second,
		ScrapeTimeout: 10 * time.Minute,
		Storage: &ss13_se.StorageSqlite{
			Path: "./tmp/servers.db",
		},
	}
	app, err := ss13_se.New(conf)
	if err != nil {
		panic(err)
	}

	err = app.Run()
	if err != nil {
		panic(err)
	}
}
<commit_msg>Adds cmd flags to change addr/path<commit_after>package main

import (
	"flag"
	"time"

	"github.com/lmas/ss13_se"
)

var (
	flagAddr = flag.String("addr", ":8000", "Adress and port to run the web server on")
	flagPath = flag.String("path", "servers.db", "File path to database")
)

func main() {
	flag.Parse()

	// TODO: load config from a toml file
	conf := ss13_se.Conf{
		WebAddr:       *flagAddr,
		ReadTimeout:   30 * time.Second,
		WriteTimeout:  30 * time.Second,
		ScrapeTimeout: 10 * time.Minute,
		Storage: &ss13_se.StorageSqlite{
			Path: *flagPath,
		},
	}
	app, err := ss13_se.New(conf)
	if err != nil {
		panic(err)
	}

	err = app.Run()
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package jsonpatch

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
)

const (
	eRaw = iota
	eDoc = iota
	eAry = iota
)

type lazyNode struct {
	raw   *json.RawMessage
	doc   partialDoc
	ary   partialArray
	which int
}

type operation map[string]*json.RawMessage
type Patch []operation

type partialDoc map[string]*lazyNode
type partialArray []*lazyNode

type container interface {
	get(key string) (*lazyNode, error)
	set(key string, val *lazyNode) error
	remove(key string) error
}

func newLazyNode(raw *json.RawMessage) *lazyNode {
	return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw}
}

func (n *lazyNode) MarshalJSON() ([]byte, error) {
	switch n.which {
	case eRaw:
		return *n.raw, nil
	case eDoc:
		return json.Marshal(n.doc)
	case eAry:
		return json.Marshal(n.ary)
	default:
		return nil, fmt.Errorf("Unknown type")
	}
}

func (n *lazyNode) UnmarshalJSON(data []byte) error {
	dest := make(json.RawMessage, len(data))
	copy(dest, data)
	n.raw = &dest
	n.which = eRaw
	return nil
}

func (n *lazyNode) intoDoc() (*partialDoc, error) {
	if n.which == eDoc {
		return &n.doc, nil
	}

	err := json.Unmarshal(*n.raw, &n.doc)

	if err != nil {
		return nil, err
	}

	n.which = eDoc
	return &n.doc, nil
}

func (n *lazyNode) intoAry() (*partialArray, error) {
	if n.which == eAry {
		return &n.ary, nil
	}

	err := json.Unmarshal(*n.raw, &n.ary)

	if err != nil {
		return nil, err
	}

	n.which = eAry
	return &n.ary, nil
}

func (n *lazyNode) compact() []byte {
	buf := new(bytes.Buffer)

	err := json.Compact(buf, *n.raw)

	if err != nil {
		return *n.raw
	}

	return buf.Bytes()
}

func (n *lazyNode) tryDoc() bool {
	err := json.Unmarshal(*n.raw, &n.doc)

	if err != nil {
		return false
	}

	n.which = eDoc
	return true
}

func (n *lazyNode) tryAry() bool {
	err := json.Unmarshal(*n.raw, &n.ary)

	if err != nil {
		return false
	}

	n.which = eAry
	return true
}

func (n *lazyNode) equal(o *lazyNode) bool {
	if n.which == eRaw {
		if !n.tryDoc() && !n.tryAry() {
			if o.which != eRaw {
				return false
			}

			return bytes.Equal(n.compact(), o.compact())
		}
	}

	if n.which == eDoc {
		if o.which == eRaw {
			if !o.tryDoc() {
				return false
			}
		}

		if o.which != eDoc {
			return false
		}

		for k, v := range n.doc {
			ov, ok := o.doc[k]

			if !ok {
				return false
			}

			if v == nil && ov == nil {
				continue
			}

			if !v.equal(ov) {
				return false
			}
		}

		return true
	}

	if o.which != eAry && !o.tryAry() {
		return false
	}

	if len(n.ary) != len(o.ary) {
		return false
	}

	for idx, val := range n.ary {
		if !val.equal(o.ary[idx]) {
			return false
		}
	}

	return true
}

func (o operation) kind() string {
	if obj, ok := o["op"]; ok {
		var op string

		err := json.Unmarshal(*obj, &op)

		if err != nil {
			return "unknown"
		}

		return op
	}

	return "unknown"
}

func (o operation) path() string {
	if obj, ok := o["path"]; ok {
		var op string

		err := json.Unmarshal(*obj, &op)

		if err != nil {
			return "unknown"
		}

		return op
	}

	return "unknown"
}

func (o operation) from() string {
	if obj, ok := o["from"]; ok {
		var op string

		err := json.Unmarshal(*obj, &op)

		if err != nil {
			return "unknown"
		}

		return op
	}

	return "unknown"
}

func (o operation) value() *lazyNode {
	if obj, ok := o["value"]; ok {
		return newLazyNode(obj)
	}

	return nil
}

func isArray(buf []byte) bool {
	for _, c := range buf {
		switch c {
		case ' ':
		case '\n':
		case '\t':
			continue
		case '[':
			return true
		default:
			break
		}
	}

	return false
}

func findObject(doc *partialDoc, path string) (container, string) {
	split := strings.Split(path, "/")

	parts := split[1 : len(split)-1]

	key := split[len(split)-1]

	var err error

	for idx, part := range parts {
		next, ok := (*doc)[part]
		if !ok {
			return nil, ""
		}

		if isArray(*next.raw) {
			if idx == len(parts)-1 {
				ary, err := next.intoAry()

				if err != nil {
					return nil, ""
				}

				return ary, key
			} else {
				return nil, ""
			}
		} else {
			doc, err = next.intoDoc()

			if err != nil {
				return nil, ""
			}
		}
	}

	return doc, key
}

func (d *partialDoc) set(key string, val *lazyNode) error {
	(*d)[key] = val
	return nil
}

func (d *partialDoc) get(key string) (*lazyNode, error) {
	return (*d)[key], nil
}

func (d *partialDoc) remove(key string) error {
	delete(*d, key)
	return nil
}

func (d *partialArray) set(key string, val *lazyNode) error {
	if key == "-" {
		*d = append(*d, val)
		return nil
	}

	idx, err := strconv.Atoi(key)

	if err != nil {
		return err
	}

	ary := make([]*lazyNode, len(*d)+1)

	cur := *d

	copy(ary[0:idx], cur[0:idx])
	ary[idx] = val
	copy(ary[idx+1:], cur[idx:])

	*d = ary
	return nil
}

func (d *partialArray) get(key string) (*lazyNode, error) {
	idx, err := strconv.Atoi(key)

	if err != nil {
		return nil, err
	}

	return (*d)[idx], nil
}

func (d *partialArray) remove(key string) error {
	idx, err := strconv.Atoi(key)

	if err != nil {
		return err
	}

	cur := *d

	ary := make([]*lazyNode, len(cur)-1)

	copy(ary[0:idx], cur[0:idx])
	copy(ary[idx:], cur[idx+1:])

	*d = ary
	return nil

}

func (p Patch) add(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	if con == nil {
		return fmt.Errorf("Missing container: %s", path)
	}

	con.set(key, op.value())

	return nil
}

func (p Patch) remove(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	return con.remove(key)
}

func (p Patch) replace(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	con.set(key, op.value())

	return nil
}

func (p Patch) move(doc *partialDoc, op operation) error {
	from := op.from()

	con, key := findObject(doc, from)

	val, err := con.get(key)

	if err != nil {
		return err
	}

	con.remove(key)

	path := op.path()

	con, key = findObject(doc, path)

	con.set(key, val)

	return nil
}

var eTestFailed = fmt.Errorf("Testing value failed")

func (p Patch) test(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	val, err := con.get(key)

	if err != nil {
		return err
	}

	if val.equal(op.value()) {
		return nil
	}

	return eTestFailed
}

// Indicate if 2 JSON documents have the same structural equality
func Equal(a, b []byte) bool {
	ra := make(json.RawMessage, len(a))
	copy(ra, a)
	la := newLazyNode(&ra)

	rb := make(json.RawMessage, len(b))
	copy(rb, b)
	lb := newLazyNode(&rb)

	return la.equal(lb)
}

// Given a JSON document `doc`, treat it like a document
// conforming to RFC6902 and decode it.
func DecodePatch(buf []byte) (Patch, error) {
	var p Patch

	err := json.Unmarshal(buf, &p)

	if err != nil {
		return nil, err
	}

	return p, nil
}

// Mutate a JSON document according to the patch and return
// the new document
func (p Patch) Apply(doc []byte) ([]byte, error) {
	pd := new(partialDoc)

	err := json.Unmarshal(doc, pd)

	if err != nil {
		return nil, err
	}

	err = nil

	for _, op := range p {
		switch op.kind() {
		case "add":
			err = p.add(pd, op)
		case "remove":
			err = p.remove(pd, op)
		case "replace":
			err = p.replace(pd, op)
		case "move":
			err = p.move(pd, op)
		case "test":
			err = p.test(pd, op)
		default:
			err = fmt.Errorf("Unexpected kind: %s", op.kind())
		}

		if err != nil {
			return nil, err
		}
	}

	return json.Marshal(pd)
}
<commit_msg>Fix bugs with array indexing, array detection<commit_after>package jsonpatch

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
)

const (
	eRaw = iota
	eDoc = iota
	eAry = iota
)

type lazyNode struct {
	raw   *json.RawMessage
	doc   partialDoc
	ary   partialArray
	which int
}

type operation map[string]*json.RawMessage
type Patch []operation

type partialDoc map[string]*lazyNode
type partialArray []*lazyNode

type container interface {
	get(key string) (*lazyNode, error)
	set(key string, val *lazyNode) error
	remove(key string) error
}

func newLazyNode(raw *json.RawMessage) *lazyNode {
	return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw}
}

func (n *lazyNode) MarshalJSON() ([]byte, error) {
	switch n.which {
	case eRaw:
		return *n.raw, nil
	case eDoc:
		return json.Marshal(n.doc)
	case eAry:
		return json.Marshal(n.ary)
	default:
		return nil, fmt.Errorf("Unknown type")
	}
}

func (n *lazyNode) UnmarshalJSON(data []byte) error {
	dest := make(json.RawMessage, len(data))
	copy(dest, data)
	n.raw = &dest
	n.which = eRaw
	return nil
}

func (n *lazyNode) intoDoc() (*partialDoc, error) {
	if n.which == eDoc {
		return &n.doc, nil
	}

	err := json.Unmarshal(*n.raw, &n.doc)

	if err != nil {
		return nil, err
	}

	n.which = eDoc

	return &n.doc, nil
}

func (n *lazyNode) intoAry() (*partialArray, error) {
	if n.which == eAry {
		return &n.ary, nil
	}

	err := json.Unmarshal(*n.raw, &n.ary)

	if err != nil {
		return nil, err
	}

	n.which = eAry
	return &n.ary, nil
}

func (n *lazyNode) compact() []byte {
	buf := new(bytes.Buffer)

	err := json.Compact(buf, *n.raw)

	if err != nil {
		return *n.raw
	}

	return buf.Bytes()
}

func (n *lazyNode) tryDoc() bool {
	err := json.Unmarshal(*n.raw, &n.doc)

	if err != nil {
		return false
	}

	n.which = eDoc
	return true
}

func (n *lazyNode) tryAry() bool {
	err := json.Unmarshal(*n.raw, &n.ary)

	if err != nil {
		return false
	}

	n.which = eAry
	return true
}

func (n *lazyNode) equal(o *lazyNode) bool {
	if n.which == eRaw {
		if !n.tryDoc() && !n.tryAry() {
			if o.which != eRaw {
				return false
			}

			return bytes.Equal(n.compact(), o.compact())
		}
	}

	if n.which == eDoc {
		if o.which == eRaw {
			if !o.tryDoc() {
				return false
			}
		}

		if o.which != eDoc {
			return false
		}

		for k, v := range n.doc {
			ov, ok := o.doc[k]

			if !ok {
				return false
			}

			if v == nil && ov == nil {
				continue
			}

			if !v.equal(ov) {
				return false
			}
		}

		return true
	}

	if o.which != eAry && !o.tryAry() {
		return false
	}

	if len(n.ary) != len(o.ary) {
		return false
	}

	for idx, val := range n.ary {
		if !val.equal(o.ary[idx]) {
			return false
		}
	}

	return true
}

func (o operation) kind() string {
	if obj, ok := o["op"]; ok {
		var op string

		err := json.Unmarshal(*obj, &op)

		if err != nil {
			return "unknown"
		}

		return op
	}

	return "unknown"
}

func (o operation) path() string {
	if obj, ok := o["path"]; ok {
		var op string

		err := json.Unmarshal(*obj, &op)

		if err != nil {
			return "unknown"
		}

		return op
	}

	return "unknown"
}

func (o operation) from() string {
	if obj, ok := o["from"]; ok {
		var op string

		err := json.Unmarshal(*obj, &op)

		if err != nil {
			return "unknown"
		}

		return op
	}

	return "unknown"
}

func (o operation) value() *lazyNode {
	if obj, ok := o["value"]; ok {
		return newLazyNode(obj)
	}

	return nil
}

func isArray(buf []byte) bool {
Loop:
	for _, c := range buf {
		switch c {
		case ' ':
		case '\n':
		case '\t':
			continue
		case '[':
			return true
		default:
			break Loop
		}
	}

	return false
}

func findObject(arg1 *partialDoc, path string) (container, string) {
	doc := container(arg1)

	split := strings.Split(path, "/")

	parts := split[1 : len(split)-1]

	key := split[len(split)-1]

	var err error

	for _, part := range parts {

		next, ok := doc.get(part)

		if ok != nil {
			return nil, ""
		}

		if isArray(*next.raw) {
			{
				doc, err = next.intoAry()

				if err != nil {
					return nil, ""
				}
			}
		} else {
			doc, err = next.intoDoc()

			if err != nil {
				return nil, ""
			}
		}
	}

	return doc, key
}

func (d *partialDoc) set(key string, val *lazyNode) error {
	(*d)[key] = val
	return nil
}

func (d *partialDoc) get(key string) (*lazyNode, error) {
	return (*d)[key], nil
}

func (d *partialDoc) remove(key string) error {
	delete(*d, key)
	return nil
}

func (d *partialArray) set(key string, val *lazyNode) error {
	if key == "-" {
		*d = append(*d, val)
		return nil
	}

	idx, err := strconv.Atoi(key)

	if err != nil {
		return err
	}

	ary := make([]*lazyNode, len(*d)+1)

	cur := *d

	copy(ary[0:idx], cur[0:idx])
	ary[idx] = val
	copy(ary[idx+1:], cur[idx:])

	*d = ary
	return nil
}

func (d *partialArray) get(key string) (*lazyNode, error) {
	idx, err := strconv.Atoi(key)

	if err != nil {
		return nil, err
	}

	return (*d)[idx], nil
}

func (d *partialArray) remove(key string) error {
	idx, err := strconv.Atoi(key)

	if err != nil {
		return err
	}

	cur := *d

	ary := make([]*lazyNode, len(cur)-1)

	copy(ary[0:idx], cur[0:idx])
	copy(ary[idx:], cur[idx+1:])

	*d = ary
	return nil

}

func (p Patch) add(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	if con == nil {
		return fmt.Errorf("Missing container: %s", path)
	}

	con.set(key, op.value())

	return nil
}

func (p Patch) remove(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	return con.remove(key)
}

func (p Patch) replace(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	con.set(key, op.value())

	return nil
}

func (p Patch) move(doc *partialDoc, op operation) error {
	from := op.from()

	con, key := findObject(doc, from)

	val, err := con.get(key)

	if err != nil {
		return err
	}

	con.remove(key)

	path := op.path()

	con, key = findObject(doc, path)

	con.set(key, val)

	return nil
}

var eTestFailed = fmt.Errorf("Testing value failed")

func (p Patch) test(doc *partialDoc, op operation) error {
	path := op.path()

	con, key := findObject(doc, path)

	val, err := con.get(key)

	if err != nil {
		return err
	}

	if val.equal(op.value()) {
		return nil
	}

	return eTestFailed
}

// Indicate if 2 JSON documents have the same structural equality
func Equal(a, b []byte) bool {
	ra := make(json.RawMessage, len(a))
	copy(ra, a)
	la := newLazyNode(&ra)

	rb := make(json.RawMessage, len(b))
	copy(rb, b)
	lb := newLazyNode(&rb)

	return la.equal(lb)
}

// Given a JSON document `doc`, treat it like a document
// conforming to RFC6902 and decode it.
func DecodePatch(buf []byte) (Patch, error) {
	var p Patch

	err := json.Unmarshal(buf, &p)

	if err != nil {
		return nil, err
	}

	return p, nil
}

// Mutate a JSON document according to the patch and return
// the new document
func (p Patch) Apply(doc []byte) ([]byte, error) {
	pd := new(partialDoc)

	err := json.Unmarshal(doc, pd)

	if err != nil {
		return nil, err
	}

	err = nil

	for _, op := range p {
		switch op.kind() {
		case "add":
			err = p.add(pd, op)
		case "remove":
			err = p.remove(pd, op)
		case "replace":
			err = p.replace(pd, op)
		case "move":
			err = p.move(pd, op)
		case "test":
			err = p.test(pd, op)
		default:
			err = fmt.Errorf("Unexpected kind: %s", op.kind())
		}

		if err != nil {
			return nil, err
		}
	}

	return json.Marshal(pd)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/antage/eventsource"
	"github.com/bwmarrin/discordgo"
	"github.com/gorilla/handlers"
	"github.com/gorilla/sessions"
	"golang.org/x/oauth2"
	redis "gopkg.in/redis.v3"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"time"
)

var (
	// Permission Constants
	READ_MESSAGES = 1024
	SEND_MESSAGES = 2048
	CONNECT       = 1048576
	SPEAK         = 2097152

	// Redis client (for stats)
	rcli *redis.Client

	// Oauth2 Config
	oauthConf *oauth2.Config

	// Used for storing session information in a cookie
	store *sessions.CookieStore

	// Used for pushing live stat updates to the client
	es eventsource.EventSource

	// Source of the HTML page (cached in memory for performance)
	htmlIndexPage string

	// Base URL of the discord API
	apiBaseUrl = "https://discordapp.com/api"
)

// Represents a JSON struct of stats that are updated every second and pushed to the client
type CountUpdate struct {
	Total          string `json:"total"`
	UniqueUsers    string `json:"unique_users"`
	UniqueGuilds   string `json:"unique_guilds"`
	UniqueChannels string `json:"unique_channels"`
	SecretCount    string `json:"secret_count"`
}

func (c *CountUpdate) ToJSON() []byte {
	data, _ := json.Marshal(c)
	return data
}

func NewCountUpdate() *CountUpdate {
	var (
		totalCmd  *redis.StringCmd
		usersCmd  *redis.IntCmd
		guildsCmd *redis.IntCmd
		chansCmd  *redis.IntCmd
		secretCmd *redis.StringCmd
	)

	// Make a pipelined request to redis for all the counter values
	errors, err := rcli.Pipelined(func(pipe *redis.Pipeline) error {
		totalCmd = pipe.Get("airhorn:a:total")
		usersCmd = pipe.SCard("airhorn:a:users")
		guildsCmd = pipe.SCard("airhorn:a:guilds")
		chansCmd = pipe.SCard("airhorn:a:channels")
		secretCmd = pipe.Get("airhorn:a:sound:truck")
		return nil
	})

	// Generally this is not a huge deal, lets try to continue on
	if err != nil {
		log.WithFields(log.Fields{
			"error":  err,
			"errors": errors,
		}).Warning("Failed to get a count update from redis")
	}

	secretCount := secretCmd.Val()
	if secretCount == "" {
		secretCount = "0"
	}

	return &CountUpdate{
		Total:          totalCmd.Val(),
		UniqueUsers:    strconv.FormatInt(usersCmd.Val(), 10),
		UniqueGuilds:   strconv.FormatInt(guildsCmd.Val(), 10),
		UniqueChannels: strconv.FormatInt(chansCmd.Val(), 10),
		SecretCount:    secretCmd.Val(),
	}
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// Return a random character sequence of n length
func randSeq(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

// Returns the current session or aborts the request
func getSessionOrAbort(w http.ResponseWriter, r *http.Request) *sessions.Session {
	session, err := store.Get(r, "session")

	if session == nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("Failed to get session")
		http.Error(w, "Invalid or corrupted session", http.StatusInternalServerError)
		return nil
	}

	return session
}

// Renders the index page
func handleIndex(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(htmlIndexPage))
}

// Redirects to the oauth2
func handleLogin(w http.ResponseWriter, r *http.Request) {
	session := getSessionOrAbort(w, r)
	if session == nil {
		return
	}

	// Create a random state
	session.Values["state"] = randSeq(32)
	session.Save(r, w)

	// OR the permissions we want
	perms := READ_MESSAGES | SEND_MESSAGES | CONNECT | SPEAK

	// Return a redirect to the ouath provider
	url := oauthConf.AuthCodeURL(session.Values["state"].(string), oauth2.AccessTypeOnline)
	http.Redirect(w, r, url+fmt.Sprintf("&permissions=%v", perms), http.StatusTemporaryRedirect)
}

func handleCallback(w http.ResponseWriter, r *http.Request) {
	session := getSessionOrAbort(w, r)
	if session == nil {
		return
	}

	// Check the state string is correct
	state := r.FormValue("state")
	if state != session.Values["state"] {
		log.WithFields(log.Fields{
			"expected": session.Values["state"],
			"received": state,
		}).Error("Invalid OAuth state")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	errorMsg := r.FormValue("error")
	if errorMsg != "" {
		log.WithFields(log.Fields{
			"error": errorMsg,
		}).Error("Received OAuth error from provider")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	token, err := oauthConf.Exchange(oauth2.NoContext, r.FormValue("code"))
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"token": token,
		}).Error("Failed to exchange token with provider")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	body, _ := json.Marshal(map[interface{}]interface{}{})
	req, err := http.NewRequest("GET", apiBaseUrl+"/users/@me", bytes.NewBuffer(body))
	if err != nil {
		log.WithFields(log.Fields{
			"body":  body,
			"req":   req,
			"error": err,
		}).Error("Failed to create @me request")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	req.Header.Set("Authorization", token.Type()+" "+token.AccessToken)
	client := &http.Client{Timeout: (20 * time.Second)}
	resp, err := client.Do(req)
	if err != nil {
		log.WithFields(log.Fields{
			"error":  err,
			"client": client,
			"resp":   resp,
		}).Error("Failed to request @me data")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"body":  resp.Body,
		}).Error("Failed to read data from HTTP response")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	user := discordgo.User{}
	err = json.Unmarshal(respBody, &user)
	if err != nil {
		log.WithFields(log.Fields{
			"data":  respBody,
			"error": err,
		}).Error("Failed to parse JSON payload from HTTP response")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	// Finally write some information to the session store
	session.Values["token"] = token.AccessToken
	session.Values["username"] = user.Username
	session.Values["tag"] = user.Discriminator
	delete(session.Values, "state")
	session.Save(r, w)

	// And redirect the user back to the dashboard
	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}

func handleMe(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session")

	body, err := json.Marshal(map[string]interface{}{
		"username": session.Values["username"],
		"tag":      session.Values["tag"],
	})

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(body)
}

func server() {
	server := http.NewServeMux()
	server.Handle("/", http.FileServer(http.Dir("static/dist")))
	server.HandleFunc("/me", handleMe)
	server.HandleFunc("/login", handleLogin)
	server.HandleFunc("/callback", handleCallback)

	// Only add this route if we have stats to push (e.g. redis connection)
	if es != nil {
		server.Handle("/events", es)
	}

	port := os.Getenv("PORT")
	if port == "" {
		port = "14000"
	}

	log.WithFields(log.Fields{
		"port": port,
	}).Info("Starting HTTP Server")

	// If the requests log doesnt exist, make it
	if _, err := os.Stat("requests.log"); os.IsNotExist(err) {
		ioutil.WriteFile("requests.log", []byte{}, 0600)
	}

	// Open the log file in append mode
	logFile, err := os.OpenFile("requests.log", os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Error("Failed to open requests log file")
		return
	}
	defer logFile.Close()

	// Actually start the server
	loggedRouter := handlers.LoggingHandler(logFile, server)
	http.ListenAndServe(":"+port, loggedRouter)
}

func broadcastLoop() {
	var id int = 0
	for {
		time.Sleep(time.Second * 1)

		es.SendEventMessage(string(NewCountUpdate().ToJSON()), "message", strconv.Itoa(id))
		id += 1
	}
}

func connectToRedis(connStr string) (err error) {
	log.WithFields(log.Fields{
		"host": connStr,
	}).Info("Connecting to redis")

	// Open the connection
	rcli = redis.NewClient(&redis.Options{Addr: connStr, DB: 0})

	// Attempt to ping it
	_, err = rcli.Ping().Result()

	if err != nil {
		log.WithFields(log.Fields{
			"host":  connStr,
			"error": err,
		}).Error("Failed to connect to redis")
		fmt.Printf("Failed to connect to redis: %s\n", err)
		return err
	}

	return nil
}

func main() {
	var (
		ClientID     = flag.String("i", "", "OAuth2 Client ID")
		ClientSecret = flag.String("s", "", "OAtuh2 Client Secret")
		Redis        = flag.String("r", "", "Redis Connection String")
		err          error
	)
	flag.Parse()

	if *Redis != "" {
		// First, open a redis connection we use for stats
		if connectToRedis(*Redis) != nil {
			return
		}

		// Now start the eventsource loop for client-side stat update
		es = eventsource.New(nil, nil)
		defer es.Close()
		go broadcastLoop()
	}

	// Load the HTML static page
	data, err := ioutil.ReadFile("templates/index.html")
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("Failed to open index.html")
		return
	}
	htmlIndexPage = string(data)

	// Create a cookie store
	store = sessions.NewCookieStore([]byte(*ClientSecret))

	// Setup the OAuth2 Configuration
	endpoint := oauth2.Endpoint{
		AuthURL:  apiBaseUrl + "/oauth2/authorize",
		TokenURL: apiBaseUrl + "/oauth2/token",
	}

	oauthConf = &oauth2.Config{
		ClientID:     *ClientID,
		ClientSecret: *ClientSecret,
		Scopes:       []string{"bot", "identify"},
		Endpoint:     endpoint,
		RedirectURL:  "https://airhornbot.com/callback",
	}

	server()
}
<commit_msg>Remove unused function<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/antage/eventsource"
	"github.com/bwmarrin/discordgo"
	"github.com/gorilla/handlers"
	"github.com/gorilla/sessions"
	"golang.org/x/oauth2"
	redis "gopkg.in/redis.v3"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"time"
)

var (
	// Permission Constants
	READ_MESSAGES = 1024
	SEND_MESSAGES = 2048
	CONNECT       = 1048576
	SPEAK         = 2097152

	// Redis client (for stats)
	rcli *redis.Client

	// Oauth2 Config
	oauthConf *oauth2.Config

	// Used for storing session information in a cookie
	store *sessions.CookieStore

	// Used for pushing live stat updates to the client
	es eventsource.EventSource

	// Source of the HTML page (cached in memory for performance)
	htmlIndexPage string

	// Base URL of the discord API
	apiBaseUrl = "https://discordapp.com/api"
)

// Represents a JSON struct of stats that are updated every second and pushed to the client
type CountUpdate struct {
	Total          string `json:"total"`
	UniqueUsers    string `json:"unique_users"`
	UniqueGuilds   string `json:"unique_guilds"`
	UniqueChannels string `json:"unique_channels"`
	SecretCount    string `json:"secret_count"`
}

func (c *CountUpdate) ToJSON() []byte {
	data, _ := json.Marshal(c)
	return data
}

func NewCountUpdate() *CountUpdate {
	var (
		totalCmd  *redis.StringCmd
		usersCmd  *redis.IntCmd
		guildsCmd *redis.IntCmd
		chansCmd  *redis.IntCmd
		secretCmd *redis.StringCmd
	)

	// Make a pipelined request to redis for all the counter values
	errors, err := rcli.Pipelined(func(pipe *redis.Pipeline) error {
		totalCmd = pipe.Get("airhorn:a:total")
		usersCmd = pipe.SCard("airhorn:a:users")
		guildsCmd = pipe.SCard("airhorn:a:guilds")
		chansCmd = pipe.SCard("airhorn:a:channels")
		secretCmd = pipe.Get("airhorn:a:sound:truck")
		return nil
	})

	// Generally this is not a huge deal, lets try to continue on
	if err != nil {
		log.WithFields(log.Fields{
			"error":  err,
			"errors": errors,
		}).Warning("Failed to get a count update from redis")
	}

	secretCount := secretCmd.Val()
	if secretCount == "" {
		secretCount = "0"
	}

	return &CountUpdate{
		Total:          totalCmd.Val(),
		UniqueUsers:    strconv.FormatInt(usersCmd.Val(), 10),
		UniqueGuilds:   strconv.FormatInt(guildsCmd.Val(), 10),
		UniqueChannels: strconv.FormatInt(chansCmd.Val(), 10),
		SecretCount:    secretCmd.Val(),
	}
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

// Return a random character sequence of n length
func randSeq(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

// Returns the current session or aborts the request
func getSessionOrAbort(w http.ResponseWriter, r *http.Request) *sessions.Session {
	session, err := store.Get(r, "session")

	if session == nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("Failed to get session")
		http.Error(w, "Invalid or corrupted session", http.StatusInternalServerError)
		return nil
	}

	return session
}

// Redirects to the oauth2
func handleLogin(w http.ResponseWriter, r *http.Request) {
	session := getSessionOrAbort(w, r)
	if session == nil {
		return
	}

	// Create a random state
	session.Values["state"] = randSeq(32)
	session.Save(r, w)

	// OR the permissions we want
	perms := READ_MESSAGES | SEND_MESSAGES | CONNECT | SPEAK

	// Return a redirect to the ouath provider
	url := oauthConf.AuthCodeURL(session.Values["state"].(string), oauth2.AccessTypeOnline)
	http.Redirect(w, r, url+fmt.Sprintf("&permissions=%v", perms), http.StatusTemporaryRedirect)
}

func handleCallback(w http.ResponseWriter, r *http.Request) {
	session := getSessionOrAbort(w, r)
	if session == nil {
		return
	}

	// Check the state string is correct
	state := r.FormValue("state")
	if state != session.Values["state"] {
		log.WithFields(log.Fields{
			"expected": session.Values["state"],
			"received": state,
		}).Error("Invalid OAuth state")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	errorMsg := r.FormValue("error")
	if errorMsg != "" {
		log.WithFields(log.Fields{
			"error": errorMsg,
		}).Error("Received OAuth error from provider")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	token, err := oauthConf.Exchange(oauth2.NoContext, r.FormValue("code"))
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"token": token,
		}).Error("Failed to exchange token with provider")
		http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
		return
	}

	body, _ := json.Marshal(map[interface{}]interface{}{})
	req, err := http.NewRequest("GET", apiBaseUrl+"/users/@me", bytes.NewBuffer(body))
	if err != nil {
		log.WithFields(log.Fields{
			"body":  body,
			"req":   req,
			"error": err,
		}).Error("Failed to create @me request")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	req.Header.Set("Authorization", token.Type()+" "+token.AccessToken)
	client := &http.Client{Timeout: (20 * time.Second)}
	resp, err := client.Do(req)
	if err != nil {
		log.WithFields(log.Fields{
			"error":  err,
			"client": client,
			"resp":   resp,
		}).Error("Failed to request @me data")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"body":  resp.Body,
		}).Error("Failed to read data from HTTP response")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	user := discordgo.User{}
	err = json.Unmarshal(respBody, &user)
	if err != nil {
		log.WithFields(log.Fields{
			"data":  respBody,
			"error": err,
		}).Error("Failed to parse JSON payload from HTTP response")
		http.Error(w, "Failed to retrieve user profile", http.StatusInternalServerError)
		return
	}

	// Finally write some information to the session store
	session.Values["token"] = token.AccessToken
	session.Values["username"] = user.Username
	session.Values["tag"] = user.Discriminator
	delete(session.Values, "state")
	session.Save(r, w)

	// And redirect the user back to the dashboard
	http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}

func handleMe(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session")

	body, err := json.Marshal(map[string]interface{}{
		"username": session.Values["username"],
		"tag":      session.Values["tag"],
	})

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(body)
}

func server() {
	server := http.NewServeMux()
	server.Handle("/", http.FileServer(http.Dir("static/dist")))
	server.HandleFunc("/me", handleMe)
	server.HandleFunc("/login", handleLogin)
	server.HandleFunc("/callback", handleCallback)

	// Only add this route if we have stats to push (e.g. redis connection)
	if es != nil {
		server.Handle("/events", es)
	}

	port := os.Getenv("PORT")
	if port == "" {
		port = "14000"
	}

	log.WithFields(log.Fields{
		"port": port,
	}).Info("Starting HTTP Server")

	// If the requests log doesnt exist, make it
	if _, err := os.Stat("requests.log"); os.IsNotExist(err) {
		ioutil.WriteFile("requests.log", []byte{}, 0600)
	}

	// Open the log file in append mode
	logFile, err := os.OpenFile("requests.log", os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Error("Failed to open requests log file")
		return
	}
	defer logFile.Close()

	// Actually start the server
	loggedRouter := handlers.LoggingHandler(logFile, server)
	http.ListenAndServe(":"+port, loggedRouter)
}

func broadcastLoop() {
	var id int = 0
	for {
		time.Sleep(time.Second * 1)

		es.SendEventMessage(string(NewCountUpdate().ToJSON()), "message", strconv.Itoa(id))
		id += 1
	}
}

func connectToRedis(connStr string) (err error) {
	log.WithFields(log.Fields{
		"host": connStr,
	}).Info("Connecting to redis")

	// Open the connection
	rcli = redis.NewClient(&redis.Options{Addr: connStr, DB: 0})

	// Attempt to ping it
	_, err = rcli.Ping().Result()

	if err != nil {
		log.WithFields(log.Fields{
			"host":  connStr,
			"error": err,
		}).Error("Failed to connect to redis")
		fmt.Printf("Failed to connect to redis: %s\n", err)
		return err
	}

	return nil
}

func main() {
	var (
		ClientID     = flag.String("i", "", "OAuth2 Client ID")
		ClientSecret = flag.String("s", "", "OAtuh2 Client Secret")
		Redis        = flag.String("r", "", "Redis Connection String")
		err          error
	)
	flag.Parse()

	if *Redis != "" {
		// First, open a redis connection we use for stats
		if connectToRedis(*Redis) != nil {
			return
		}

		// Now start the eventsource loop for client-side stat update
		es = eventsource.New(nil, nil)
		defer es.Close()
		go broadcastLoop()
	}

	// Load the HTML static page
	data, err := ioutil.ReadFile("templates/index.html")
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("Failed to open index.html")
		return
	}
	htmlIndexPage = string(data)

	// Create a cookie store
	store = sessions.NewCookieStore([]byte(*ClientSecret))

	// Setup the OAuth2 Configuration
	endpoint := oauth2.Endpoint{
		AuthURL:  apiBaseUrl + "/oauth2/authorize",
		TokenURL: apiBaseUrl + "/oauth2/token",
	}

	oauthConf = &oauth2.Config{
		ClientID:     *ClientID,
		ClientSecret: *ClientSecret,
		Scopes:       []string{"bot", "identify"},
		Endpoint:     endpoint,
		RedirectURL:  "https://airhornbot.com/callback",
	}

	server()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/wader/ydls/ydls"
)

var gitCommit = "dev"

var versionFlag = flag.Bool("version", false, "Print version ("+gitCommit+")")
var debugFlag = flag.Bool("debug", false, "Debug output")
var configFlag = flag.String("config", "ydls.json", "Config file")

type progressWriter struct {
	fn    func(bytes uint64)
	bytes uint64
}

func (pw *progressWriter) Write(p []byte) (n int, err error) {
	pw.bytes += uint64(len(p))
	pw.fn(pw.bytes)
	return len(p), nil
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s [options] URL [format]:\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	if *versionFlag {
		fmt.Println(gitCommit)
		os.Exit(0)
	}
	if os.Getenv("DEBUG") != "" {
		*debugFlag = true
	}
}

func absRootPath(root string, path string) (string, error) {
	abs, err := filepath.Abs(filepath.Join(root, path))
	if err != nil {
		return "", err
	}
	if !strings.HasPrefix(abs, filepath.Clean(root+string(filepath.Separator))) {
		return "", fmt.Errorf("%s is outside root path %s", abs, root)
	}

	return abs, nil
}

func fatalIfErrorf(err error, format string, a ...interface{}) {
	if err != nil {
		a = append(a, err)
		log.Fatalf(format+": %v", a...)
	}
}

func main() {
	y, err := ydls.NewFromFile(*configFlag)
	if err != nil {
		log.Fatalf("failed to read config: %s", err)
	}
	var debugLog *log.Logger
	if *debugFlag {
		debugLog = log.New(os.Stdout, "DEBUG: ", log.Ltime)
	}

	url := flag.Arg(0)
	if url == "" {
		log.Fatalf("no URL specified")
	}

	var downloadOptions ydls.DownloadOptions
	if flag.NArg() == 1 {
		downloadOptions = ydls.DownloadOptions{URL: url}
	} else {
		downloadOptions, err = y.ParseDownloadOptions(url, flag.Arg(1), flag.Args()[2:])
		fatalIfErrorf(err, "format and options")
	}

	ctx, cancelFn := context.WithCancel(context.Background())
	defer cancelFn()

	dr, err := y.Download(ctx, downloadOptions, debugLog)
	fatalIfErrorf(err, "download failed")
	defer dr.Media.Close()
	defer dr.Wait()

	wd, err := os.Getwd()
	fatalIfErrorf(err, "getwd")

	path, err := absRootPath(wd, dr.Filename)
	fatalIfErrorf(err, "write path")

	mediaFile, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	fatalIfErrorf(err, "failed to open file")
	defer mediaFile.Close()

	pw := &progressWriter{fn: func(bytes uint64) {
		fmt.Printf("\r%s %.2fMB", dr.Filename, float64(bytes)/(1024*1024))
	}}
	mw := io.MultiWriter(mediaFile, pw)

	io.Copy(mw, dr.Media)
	fmt.Print("\n")
}
<commit_msg>Add codec argument help<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/wader/ydls/ydls"
)

var gitCommit = "dev"

var versionFlag = flag.Bool("version", false, "Print version ("+gitCommit+")")
var debugFlag = flag.Bool("debug", false, "Debug output")
var configFlag = flag.String("config", "ydls.json", "Config file")

type progressWriter struct {
	fn    func(bytes uint64)
	bytes uint64
}

func (pw *progressWriter) Write(p []byte) (n int, err error) {
	pw.bytes += uint64(len(p))
	pw.fn(pw.bytes)
	return len(p), nil
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s [options] URL [format] [codec] [codec]:\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	if *versionFlag {
		fmt.Println(gitCommit)
		os.Exit(0)
	}
	if os.Getenv("DEBUG") != "" {
		*debugFlag = true
	}
}

func absRootPath(root string, path string) (string, error) {
	abs, err := filepath.Abs(filepath.Join(root, path))
	if err != nil {
		return "", err
	}
	if !strings.HasPrefix(abs, filepath.Clean(root+string(filepath.Separator))) {
		return "", fmt.Errorf("%s is outside root path %s", abs, root)
	}

	return abs, nil
}

func fatalIfErrorf(err error, format string, a ...interface{}) {
	if err != nil {
		a = append(a, err)
		log.Fatalf(format+": %v", a...)
	}
}

func main() {
	y, err := ydls.NewFromFile(*configFlag)
	if err != nil {
		log.Fatalf("failed to read config: %s", err)
	}
	var debugLog *log.Logger
	if *debugFlag {
		debugLog = log.New(os.Stdout, "DEBUG: ", log.Ltime)
	}

	url := flag.Arg(0)
	if url == "" {
		log.Fatalf("no URL specified")
	}

	var downloadOptions ydls.DownloadOptions
	if flag.NArg() == 1 {
		downloadOptions = ydls.DownloadOptions{URL: url}
	} else {
		downloadOptions, err = y.ParseDownloadOptions(url, flag.Arg(1), flag.Args()[2:])
		fatalIfErrorf(err, "format and options")
	}

	ctx, cancelFn := context.WithCancel(context.Background())
	defer cancelFn()

	dr, err := y.Download(ctx, downloadOptions, debugLog)
	fatalIfErrorf(err, "download failed")
	defer dr.Media.Close()
	defer dr.Wait()

	wd, err := os.Getwd()
	fatalIfErrorf(err, "getwd")

	path, err := absRootPath(wd, dr.Filename)
	fatalIfErrorf(err, "write path")

	mediaFile, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	fatalIfErrorf(err, "failed to open file")
	defer mediaFile.Close()

	pw := &progressWriter{fn: func(bytes uint64) {
		fmt.Printf("\r%s %.2fMB", dr.Filename, float64(bytes)/(1024*1024))
	}}
	mw := io.MultiWriter(mediaFile, pw)

	io.Copy(mw, dr.Media)
	fmt.Print("\n")
}
<|endoftext|>


<commit_before>/*
Copyright 2015 ENDOH takanao.
<https://github.com/MiCHiLU/go-lru-cache-stats>

Copyright 2012 Google Inc.
<https://github.com/golang/groupcache>

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.
*/

// peers.go defines how processes find and communicate with their peers.

package lru

import (
	pb "github.com/golang/groupcache/groupcachepb"
)

// Context is an opaque value passed through calls to the
// ProtoGetter. It may be nil if your ProtoGetter implementation does
// not require a context.
type Context interface{}

// ProtoGetter is the interface that must be implemented by a peer.
type ProtoGetter interface {
	Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error
}

// NoPeers is an implementation of PeerPicker that never finds a peer.
type NoPeers struct{}
<commit_msg>remove NoPeers<commit_after>/*
Copyright 2015 ENDOH takanao.
<https://github.com/MiCHiLU/go-lru-cache-stats>

Copyright 2012 Google Inc.
<https://github.com/golang/groupcache>

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.
*/

// peers.go defines how processes find and communicate with their peers.

package lru

import (
	pb "github.com/golang/groupcache/groupcachepb"
)

// Context is an opaque value passed through calls to the
// ProtoGetter. It may be nil if your ProtoGetter implementation does
// not require a context.
type Context interface{}

// ProtoGetter is the interface that must be implemented by a peer.
type ProtoGetter interface {
	Get(context Context, in *pb.GetRequest, out *pb.GetResponse) error
}
<|endoftext|>


<commit_before>package memory

import (
	"context"
	"time"

	"github.com/google/uuid"
	"github.com/micro/go-micro/v2/debug/trace"
	"github.com/micro/go-micro/v2/util/ring"
)

type Tracer struct {
	opts trace.Options

	// ring buffer of traces
	buffer *ring.Buffer
}

func (t *Tracer) Read(opts ...trace.ReadOption) ([]*trace.Span, error) {
	var options trace.ReadOptions
	for _, o := range opts {
		o(&options)
	}

	sp := t.buffer.Get(t.buffer.Size())

	var spans []*trace.Span

	for _, span := range sp {
		val := span.Value.(*trace.Span)
		// skip if trace id is specified and doesn't match
		if len(options.Trace) > 0 && val.Trace != options.Trace {
			continue
		}
		spans = append(spans, val)
	}

	return spans, nil
}

func (t *Tracer) Start(ctx context.Context, name string) (context.Context, *trace.Span) {
	span := &trace.Span{
		Name:     name,
		Trace:    uuid.New().String(),
		Id:       uuid.New().String(),
		Started:  time.Now(),
		Metadata: make(map[string]string),
	}

	// return span if no context
	if ctx == nil {
		return trace.ToContext(context.Background(), span.Trace, span.Id), span
	}
	traceID, parentSpanID, ok := trace.FromContext(ctx)
	// If the trace can not be found in the header,
	// that means this is where the trace is created.
	if !ok {
		return trace.ToContext(ctx, span.Trace, span.Id), span
	}

	// set trace id
	span.Trace = traceID
	// set parent
	span.Parent = parentSpanID

	// return the span
	return trace.ToContext(ctx, span.Trace, span.Id), span
}

func (t *Tracer) Finish(s *trace.Span) error {
	// set finished time
	s.Duration = time.Since(s.Started)
	// save the span
	t.buffer.Put(s)

	return nil
}

func NewTracer(opts ...trace.Option) trace.Tracer {
	var options trace.Options
	for _, o := range opts {
		o(&options)
	}

	return &Tracer{
		opts: options,
		// the last 256 requests
		buffer: ring.New(256),
	}
}
<commit_msg>fix prealloc in trace (#1558)<commit_after>package memory

import (
	"context"
	"time"

	"github.com/google/uuid"
	"github.com/micro/go-micro/v2/debug/trace"
	"github.com/micro/go-micro/v2/util/ring"
)

type Tracer struct {
	opts trace.Options

	// ring buffer of traces
	buffer *ring.Buffer
}

func (t *Tracer) Read(opts ...trace.ReadOption) ([]*trace.Span, error) {
	var options trace.ReadOptions
	for _, o := range opts {
		o(&options)
	}

	sp := t.buffer.Get(t.buffer.Size())

	spans := make([]*trace.Span, 0, len(sp))

	for _, span := range sp {
		val := span.Value.(*trace.Span)
		// skip if trace id is specified and doesn't match
		if len(options.Trace) > 0 && val.Trace != options.Trace {
			continue
		}
		spans = append(spans, val)
	}

	return spans, nil
}

func (t *Tracer) Start(ctx context.Context, name string) (context.Context, *trace.Span) {
	span := &trace.Span{
		Name:     name,
		Trace:    uuid.New().String(),
		Id:       uuid.New().String(),
		Started:  time.Now(),
		Metadata: make(map[string]string),
	}

	// return span if no context
	if ctx == nil {
		return trace.ToContext(context.Background(), span.Trace, span.Id), span
	}
	traceID, parentSpanID, ok := trace.FromContext(ctx)
	// If the trace can not be found in the header,
	// that means this is where the trace is created.
	if !ok {
		return trace.ToContext(ctx, span.Trace, span.Id), span
	}

	// set trace id
	span.Trace = traceID
	// set parent
	span.Parent = parentSpanID

	// return the span
	return trace.ToContext(ctx, span.Trace, span.Id), span
}

func (t *Tracer) Finish(s *trace.Span) error {
	// set finished time
	s.Duration = time.Since(s.Started)
	// save the span
	t.buffer.Put(s)

	return nil
}

func NewTracer(opts ...trace.Option) trace.Tracer {
	var options trace.Options
	for _, o := range opts {
		o(&options)
	}

	return &Tracer{
		opts: options,
		// the last 256 requests
		buffer: ring.New(256),
	}
}
<|endoftext|>


<commit_before>/*
 *    Copyright (C) 2015 Stefan Luecke
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors: Stefan Luecke <glaxx@glaxx.net>
 */

package database

import (
	log "github.com/Sirupsen/logrus"
	dmp "github.com/sergi/go-diff/diffmatchpatch"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"strconv"
	"time"
)

type ItemDBProvider struct {
	c     *mgo.Collection
	ch    *mgo.Collection
	idgen *idgenerator
}

func (p *ItemDBProvider) GetItemById(id uint64) (Item, error) {
	res := Item{}
	err := p.c.Find(bson.M{"eid": id}).One(&res)
	return res, err
}

func (p *ItemDBProvider) GetItemLog(id uint64) ([]ItemHistory, error) {
	res := make([]ItemHistory, 0)
	err := p.ch.Find(bson.M{"item.eid": id}).All(&res)
	for i := 0; i != len(res); i++ {
		res[i].Timestamp = res[i].ID.Time()
	}
	return res, err
}

func (p *ItemDBProvider) CreateItem(itm *Item) (uint64, error) {
	itm.EID = p.idgen.GenerateID()
	log.WithFields(log.Fields{"ID": itm.EID}).Debug("Generated ID")
	err := p.c.Insert(itm)
	return itm.EID, err
}

func (p *ItemDBProvider) ListItem() ([]Item, error) {
	itm := make([]Item, 0)
	err := p.c.Find(nil).All(&itm)
	return itm, err
}

func (p *ItemDBProvider) UpdateItem(itm *Item, ih *ItemHistory) error {
	err := p.ch.Insert(ih)
	if err != nil {
		return err
	}
	return p.c.Update(bson.M{"eid": itm.EID}, itm)
}

func (p *ItemDBProvider) CheckItemExistance(itm *Item) bool {
	temp := Item{}
	err := p.c.Find(bson.M{"eid": itm.EID}).One(&temp)
	if err != nil {
		if err.Error() == "not found" {
			return false
		} else {
			log.WithFields(log.Fields{"Err": err}).
				Debug("TODO: check more error codes here")
			return true
		}
	}
	return true
}

func (p *ItemDBProvider) DeleteItem(itm *Item, ih *ItemHistory) error {
	err := p.ch.Insert(ih)
	if err != nil {
		return err
	}
	return p.c.Remove(bson.M{"eid": itm.EID})
}

type Item struct {
	ID          bson.ObjectId `bson:"_id,omitempty" json:"-"`
	EID         uint64        `json:"Id"`
	Name        string        `bson:",omitempty"`
	Description string        `bson:",omitempty"`
	Contains    []uint64      `bson:",omitempty"`
	Owner       string        `bson:",omitempty"`
	Maintainer  string        `bson:",omitempty"`
	Usage       string        `bson:",omitempty"`
	Discard     string        `bson:",omitempty"`
}

type ItemHistory struct {
	ID        bson.ObjectId `bson:"_id,omitempty" json:"-"`
	Timestamp time.Time     `bson:"-" json:",omitempty"`
	User      string
	Item      map[string]interface{}
}

func (i *Item) NewItemHistory(it *Item, user string) *ItemHistory {
	res := new(ItemHistory)
	res.Item = make(map[string]interface{})
	res.Item["eid"] = i.EID
	res.User = user

	if it == nil {
		res.Item["deleted"] = true
		return res
	}
	//res.Item["_id"] = i.ID
	if i.Name != it.Name {
		res.Item["name"] = it.Name
	}
	if i.Description != it.Description {
		d := dmp.New()
		d.DiffTimeout = 200 * time.Millisecond
		res.Item["description"] = d.DiffMain(i.Description, it.Description, true)
	}
	condiff := uint64Diff(i.Contains, it.Contains)
	if len(condiff) > 0 {
		res.Item["contains"] = condiff
	}
	if i.Owner != it.Owner {
		res.Item["owner"] = it.Owner
	}
	if i.Maintainer != it.Maintainer {
		res.Item["maintainer"] = it.Maintainer
	}
	if i.Usage != it.Usage {
		res.Item["usage"] = it.Usage
	}
	if i.Discard != it.Discard {
		res.Item["discard"] = it.Discard
	}
	return res
}

func uint64Diff(u1, u2 []uint64) map[string]dmp.Operation {
	// mgo.bson does only support strings as keys
	res := make(map[string]dmp.Operation)
	for i := 0; i != len(u1); i++ {
		if !uint64Contains(u2, u1[i]) {
			res[strconv.FormatUint(u1[i], 10)] = dmp.DiffDelete
		}
	}
	for i := 0; i != len(u2); i++ {
		if !uint64Contains(u1, u2[i]) {
			res[strconv.FormatUint(u2[i], 10)] = dmp.DiffInsert
		}
	}
	return res
}

func uint64Contains(sl []uint64, u uint64) bool {
	for i := 0; i != len(sl); i++ {
		if sl[i] == u {
			return true
		}
	}
	return false
}
<commit_msg>Added functions to generate/stop a ItemDBProvider<commit_after>/*
 *    Copyright (C) 2015 Stefan Luecke
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as published
 *    by the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    Authors: Stefan Luecke <glaxx@glaxx.net>
 */

package database

import (
	log "github.com/Sirupsen/logrus"
	dmp "github.com/sergi/go-diff/diffmatchpatch"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"strconv"
	"time"
)

type ItemDBProvider struct {
	c     *mgo.Collection
	ch    *mgo.Collection
	idgen *idgenerator
}

func NewItemDBProvider(s *mgo.Session, dbname string) *ItemDBProvider {
	res := new(ItemDBProvider)
	res.c = s.DB(dbname).C("item")
	res.ch = s.DB(dbname).C("item_history")
	res.idgen = NewIDGenerator(s.DB(dbname).C("counters"))
	return res
}

func (p *ItemDBProvider) Stop() {
	p.idgen.StopIDGenerator()
}

func (p *ItemDBProvider) GetItemById(id uint64) (Item, error) {
	res := Item{}
	err := p.c.Find(bson.M{"eid": id}).One(&res)
	return res, err
}

func (p *ItemDBProvider) GetItemLog(id uint64) ([]ItemHistory, error) {
	res := make([]ItemHistory, 0)
	err := p.ch.Find(bson.M{"item.eid": id}).All(&res)
	for i := 0; i != len(res); i++ {
		res[i].Timestamp = res[i].ID.Time()
	}
	return res, err
}

func (p *ItemDBProvider) CreateItem(itm *Item) (uint64, error) {
	itm.EID = p.idgen.GenerateID()
	log.WithFields(log.Fields{"ID": itm.EID}).Debug("Generated ID")
	err := p.c.Insert(itm)
	return itm.EID, err
}

func (p *ItemDBProvider) ListItem() ([]Item, error) {
	itm := make([]Item, 0)
	err := p.c.Find(nil).All(&itm)
	return itm, err
}

func (p *ItemDBProvider) UpdateItem(itm *Item, ih *ItemHistory) error {
	err := p.ch.Insert(ih)
	if err != nil {
		return err
	}
	return p.c.Update(bson.M{"eid": itm.EID}, itm)
}

func (p *ItemDBProvider) CheckItemExistance(itm *Item) bool {
	temp := Item{}
	err := p.c.Find(bson.M{"eid": itm.EID}).One(&temp)
	if err != nil {
		if err.Error() == "not found" {
			return false
		} else {
			log.WithFields(log.Fields{"Err": err}).
				Debug("TODO: check more error codes here")
			return true
		}
	}
	return true
}

func (p *ItemDBProvider) DeleteItem(itm *Item, ih *ItemHistory) error {
	err := p.ch.Insert(ih)
	if err != nil {
		return err
	}
	return p.c.Remove(bson.M{"eid": itm.EID})
}

type Item struct {
	ID          bson.ObjectId `bson:"_id,omitempty" json:"-"`
	EID         uint64        `json:"Id"`
	Name        string        `bson:",omitempty"`
	Description string        `bson:",omitempty"`
	Contains    []uint64      `bson:",omitempty"`
	Owner       string        `bson:",omitempty"`
	Maintainer  string        `bson:",omitempty"`
	Usage       string        `bson:",omitempty"`
	Discard     string        `bson:",omitempty"`
}

type ItemHistory struct {
	ID        bson.ObjectId `bson:"_id,omitempty" json:"-"`
	Timestamp time.Time     `bson:"-" json:",omitempty"`
	User      string
	Item      map[string]interface{}
}

func (i *Item) NewItemHistory(it *Item, user string) *ItemHistory {
	res := new(ItemHistory)
	res.Item = make(map[string]interface{})
	res.Item["eid"] = i.EID
	res.User = user

	if it == nil {
		res.Item["deleted"] = true
		return res
	}
	//res.Item["_id"] = i.ID
	if i.Name != it.Name {
		res.Item["name"] = it.Name
	}
	if i.Description != it.Description {
		d := dmp.New()
		d.DiffTimeout = 200 * time.Millisecond
		res.Item["description"] = d.DiffMain(i.Description, it.Description, true)
	}
	condiff := uint64Diff(i.Contains, it.Contains)
	if len(condiff) > 0 {
		res.Item["contains"] = condiff
	}
	if i.Owner != it.Owner {
		res.Item["owner"] = it.Owner
	}
	if i.Maintainer != it.Maintainer {
		res.Item["maintainer"] = it.Maintainer
	}
	if i.Usage != it.Usage {
		res.Item["usage"] = it.Usage
	}
	if i.Discard != it.Discard {
		res.Item["discard"] = it.Discard
	}
	return res
}

func uint64Diff(u1, u2 []uint64) map[string]dmp.Operation {
	// mgo.bson does only support strings as keys
	res := make(map[string]dmp.Operation)
	for i := 0; i != len(u1); i++ {
		if !uint64Contains(u2, u1[i]) {
			res[strconv.FormatUint(u1[i], 10)] = dmp.DiffDelete
		}
	}
	for i := 0; i != len(u2); i++ {
		if !uint64Contains(u1, u2[i]) {
			res[strconv.FormatUint(u2[i], 10)] = dmp.DiffInsert
		}
	}
	return res
}

func uint64Contains(sl []uint64, u uint64) bool {
	for i := 0; i != len(sl); i++ {
		if sl[i] == u {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

/*
WARNING: Until issue #67 is done, this is a copy from HW2.
*/

import (
	"fmt"
	"math"
	"math/rand"
	"time"

	"github.com/ChristopherRabotin/smd"
	"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat/distmv"
)

const (
	r2d = 180 / math.Pi
	d2r = 1 / r2d
)

// Station defines a ground station.
type Station struct {
	name              string
	R, V              []float64 // position and velocity in ECEF
	latΦ, longθ       float64   // these are stored in radians!
	altitude          float64
	ρNoise, ρDotNoise *distmv.Normal // Station noise
}

// PerformMeasurement returns whether the SC is visible, and if so, the measurement.
func (s Station) PerformMeasurement(θgst float64, state smd.MissionState) (bool, Measurement) {
	// The station vectors are in ECEF, so let's convert the state to ECEF.
	//rECEF := smd.ECI2ECEF(state.Orbit.R(), θgst)
	//vECEF := smd.ECI2ECEF(state.Orbit.V(), θgst)
	// Compute visibility for each station.

	ρECI, ρ, el, _ := s.RangeElAz(state.Orbit.R(), θgst)
	//XXX: Compute regardless of visibility for now..

	vDiffECI := make([]float64, 3)
	stationVECI := smd.ECI2ECEF(s.V, θgst)
	vECI := state.Orbit.V()
	for i := 0; i < 3; i++ {
		vDiffECI[i] = (vECI[i] - stationVECI[i]) / ρ
	}
	// SC is visible.
	ρDot := mat64.Dot(mat64.NewVector(3, ρECI), mat64.NewVector(3, vDiffECI))
	ρNoisy := ρ + s.ρNoise.Rand(nil)[0]
	ρDotNoisy := ρDot + s.ρDotNoise.Rand(nil)[0]
	// Add this to the list of measurements
	return el >= 10, Measurement{ρNoisy, ρDotNoisy, ρ, ρDot, θgst, state, s}
}

// RangeElAz returns the range (in the SEZ frame), elevation and azimuth (in degrees) of a given R vector in ECEF.
func (s Station) RangeElAz(rECI []float64, θgst float64) (ρECI []float64, ρ, el, az float64) {
	// Compute ECI position of the station
	stationRECI := smd.ECEF2ECI(s.R, θgst)
	ρECI = make([]float64, 3)
	for i := 0; i < 3; i++ {
		ρECI[i] = rECI[i] - stationRECI[i]
	}
	ρ = norm(ρECI)
	rSEZ := smd.MxV33(smd.R3(s.longθ), ρECI)
	rSEZ = smd.MxV33(smd.R2(math.Pi/2-s.latΦ), rSEZ)
	el = math.Asin(rSEZ[2]/ρ) * r2d
	az = (2*math.Pi + math.Atan2(rSEZ[1], -rSEZ[0])) * r2d
	return
}

// NewStation returns a new station. Angles in degrees.
func NewStation(name string, altitude, latΦ, longθ, σρ, σρDot float64) Station {
	R := smd.GEO2ECEF(altitude, latΦ*d2r, longθ*d2r)
	V := cross([]float64{0, 0, smd.EarthRotationRate}, R)
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	ρNoise, ok := distmv.NewNormal([]float64{0}, mat64.NewSymDense(1, []float64{σρ}), seed)
	if !ok {
		panic("NOK in Gaussian")
	}
	ρDotNoise, ok := distmv.NewNormal([]float64{0}, mat64.NewSymDense(1, []float64{σρDot}), seed)
	if !ok {
		panic("NOK in Gaussian")
	}
	return Station{name, R, V, latΦ * d2r, longθ * d2r, altitude, ρNoise, ρDotNoise}
}

// Measurement stores a measurement of a station.
type Measurement struct {
	ρ, ρDot         float64 // Store the range and range rate
	trueρ, trueρDot float64 // Store the true range and range rate
	θgst            float64
	State           smd.MissionState
	Station         Station
}

// StateVector returns the state vector as a mat64.Vector
func (m Measurement) StateVector() *mat64.Vector {
	return mat64.NewVector(2, []float64{m.ρ, m.ρDot})
}

// HTilde returns the H tilde matrix for this given measurement.
func (m Measurement) HTilde(state smd.MissionState, θgst, θdot float64) *mat64.Dense {
	withRotation := true
	theta := θgst
	thetadot := θdot
	xS := m.Station.R[0]
	yS := m.Station.R[1]
	zS := m.Station.R[2]
	xSDot := m.Station.V[0]
	ySDot := m.Station.V[1]
	zSDot := m.Station.V[2]
	R := state.Orbit.R()
	V := state.Orbit.V()
	x := R[0]
	y := R[1]
	z := R[2]
	xDot := V[0]
	yDot := V[1]
	zDot := V[2]
	rho := m.ρ
	rho2 := math.Pow(m.ρ, 2)
	rhodot := m.ρDot

	H := mat64.NewDense(2, 6, nil)
	if withRotation {
		//drho partials
		drhoDx := (x - xS*math.Cos(theta) + yS*math.Sin(theta)) / rho
		drhoDy := (y - yS*math.Cos(theta) - xS*math.Sin(theta)) / rho
		drhoDz := (z - zS*math.Cos(theta)) / rho

		//drhodot partials
		drhodotDx := (xDot+xS*thetadot*math.Sin(theta)+yS*thetadot*math.Cos(theta))/rho - rhodot*(x-xS*math.Cos(theta)+yS*math.Sin(theta))/rho2
		drhodotDy := (yDot+yS*thetadot*math.Sin(theta)-xS*thetadot*math.Cos(theta))/rho - rhodot*(y-yS*math.Cos(theta)-xS*math.Sin(theta))/rho2
		drhodotDz := zDot/rho - rhodot*(z-zS)/rho2
		drhodotDxdot := (x - xS*math.Cos(theta) - yS*math.Sin(theta)) / rho
		drhodotDydot := (y - yS*math.Cos(theta) - xS*math.Sin(theta)) / rho
		drhodotDzdot := (z - zS) / rho
		// \partial \rho / \partial {x,y,z}
		H.Set(0, 0, drhoDx)
		H.Set(0, 1, drhoDy)
		H.Set(0, 2, drhoDz)
		// \partial \dot\rho / \partial {x,y,z}
		H.Set(1, 0, drhodotDx)
		H.Set(1, 1, drhodotDy)
		H.Set(1, 2, drhodotDz)
		H.Set(1, 3, drhodotDxdot)
		H.Set(1, 4, drhodotDydot)
		H.Set(1, 5, drhodotDzdot)
	} else {
		// \partial \rho / \partial {x,y,z}
		H.Set(0, 0, (x-xS)/m.ρ)
		H.Set(0, 1, (y-yS)/m.ρ)
		H.Set(0, 2, (z-zS)/m.ρ)
		// \partial \dot\rho / \partial {x,y,z}
		H.Set(1, 0, (xDot-xSDot)/m.ρ+(m.ρDot/math.Pow(m.ρ, 2))*(x-xS))
		H.Set(1, 1, (yDot-ySDot)/m.ρ+(m.ρDot/math.Pow(m.ρ, 2))*(y-yS))
		H.Set(1, 2, (zDot-zSDot)/m.ρ+(m.ρDot/math.Pow(m.ρ, 2))*(z-zS))
		H.Set(1, 3, (x-xS)/m.ρ)
		H.Set(1, 4, (y-yS)/m.ρ)
		H.Set(1, 5, (z-zS)/m.ρ)
	}
	return H
}

// CSV returns the data as CSV (does *not* include the new line)
func (m Measurement) CSV() string {
	return fmt.Sprintf("%f,%f,%f,%f,", m.trueρ, m.trueρDot, m.ρ, m.ρDot)
}

func (m Measurement) String() string {
	return fmt.Sprintf("%s@%s", m.Station.name, m.State.DT)
}

// Unshamefully copied from smd/math.go
func cross(a, b []float64) []float64 {
	return []float64{a[1]*b[2] - a[2]*b[1],
		a[2]*b[0] - a[0]*b[2],
		a[0]*b[1] - a[1]*b[0]} // Cross product R x V.
}

// norm returns the norm of a given vector which is supposed to be 3x1.
func norm(v []float64) float64 {
	return math.Sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
}
<commit_msg>Reverted changes.<commit_after>package main

/*
WARNING: Until issue #67 is done, this is a copy from HW2.
*/

import (
	"fmt"
	"math"
	"math/rand"
	"time"

	"github.com/ChristopherRabotin/smd"
	"github.com/gonum/matrix/mat64"
	"github.com/gonum/stat/distmv"
)

const (
	r2d = 180 / math.Pi
	d2r = 1 / r2d
)

// Station defines a ground station.
type Station struct {
	name              string
	R, V              []float64 // position and velocity in ECEF
	latΦ, longθ       float64   // these are stored in radians!
	altitude          float64
	ρNoise, ρDotNoise *distmv.Normal // Station noise
}

// PerformMeasurement returns whether the SC is visible, and if so, the measurement.
func (s Station) PerformMeasurement(θgst float64, state smd.MissionState) (bool, Measurement) {
	// The station vectors are in ECEF, so let's convert the state to ECEF.
	rECEF := smd.ECI2ECEF(state.Orbit.R(), θgst)
	vECEF := smd.ECI2ECEF(state.Orbit.V(), θgst)
	// Compute visibility for each station.

	ρECEF, ρ, el, _ := s.RangeElAz(rECEF)
	//XXX: Compute regardless of visibility for now..

	vDiffECEF := make([]float64, 3)
	for i := 0; i < 3; i++ {
		vDiffECEF[i] = (vECEF[i] - s.V[i]) / ρ
	}
	// SC is visible.
	ρDot := mat64.Dot(mat64.NewVector(3, ρECEF), mat64.NewVector(3, vDiffECEF))
	ρNoisy := ρ + s.ρNoise.Rand(nil)[0]
	ρDotNoisy := ρDot + s.ρDotNoise.Rand(nil)[0]
	// Add this to the list of measurements
	return el >= 10, Measurement{ρNoisy, ρDotNoisy, ρ, ρDot, θgst, state, s}

}

// RangeElAz returns the range (in the SEZ frame), elevation and azimuth (in degrees) of a given R vector in ECEF.
func (s Station) RangeElAz(rECEF []float64) (ρECEF []float64, ρ, el, az float64) {
	ρECEF = make([]float64, 3)
	for i := 0; i < 3; i++ {
		ρECEF[i] = rECEF[i] - s.R[i]
	}
	ρ = norm(ρECEF)
	rSEZ := smd.MxV33(smd.R3(s.longθ), ρECEF)
	rSEZ = smd.MxV33(smd.R2(math.Pi/2-s.latΦ), rSEZ)
	el = math.Asin(rSEZ[2]/ρ) * r2d
	az = (2*math.Pi + math.Atan2(rSEZ[1], -rSEZ[0])) * r2d
	return
}

// NewStation returns a new station. Angles in degrees.
func NewStation(name string, altitude, latΦ, longθ, σρ, σρDot float64) Station {
	R := smd.GEO2ECEF(altitude, latΦ*d2r, longθ*d2r)
	V := cross([]float64{0, 0, smd.EarthRotationRate}, R)
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	ρNoise, ok := distmv.NewNormal([]float64{0}, mat64.NewSymDense(1, []float64{σρ}), seed)
	if !ok {
		panic("NOK in Gaussian")
	}
	ρDotNoise, ok := distmv.NewNormal([]float64{0}, mat64.NewSymDense(1, []float64{σρDot}), seed)
	if !ok {
		panic("NOK in Gaussian")
	}
	return Station{name, R, V, latΦ * d2r, longθ * d2r, altitude, ρNoise, ρDotNoise}
}

// Measurement stores a measurement of a station.
type Measurement struct {
	ρ, ρDot         float64 // Store the range and range rate
	trueρ, trueρDot float64 // Store the true range and range rate
	θgst            float64
	State           smd.MissionState
	Station         Station
}

// StateVector returns the state vector as a mat64.Vector
func (m Measurement) StateVector() *mat64.Vector {
	return mat64.NewVector(2, []float64{m.ρ, m.ρDot})
}

// HTilde returns the H tilde matrix for this given measurement.
func (m Measurement) HTilde(state smd.MissionState, θgst, θdot float64) *mat64.Dense {
	withRotation := true
	theta := θgst
	thetadot := θdot
	xS := m.Station.R[0]
	yS := m.Station.R[1]
	zS := m.Station.R[2]
	xSDot := m.Station.V[0]
	ySDot := m.Station.V[1]
	zSDot := m.Station.V[2]
	R := state.Orbit.R()
	V := state.Orbit.V()
	x := R[0]
	y := R[1]
	z := R[2]
	xDot := V[0]
	yDot := V[1]
	zDot := V[2]
	rho := m.ρ
	rho2 := math.Pow(m.ρ, 2)
	rhodot := m.ρDot

	H := mat64.NewDense(2, 6, nil)
	if withRotation {
		//drho partials
		drhoDx := (x - xS*math.Cos(theta) + yS*math.Sin(theta)) / rho
		drhoDy := (y - yS*math.Cos(theta) - xS*math.Sin(theta)) / rho
		drhoDz := (z - zS*math.Cos(theta)) / rho

		//drhodot partials
		drhodotDx := (xDot+xS*thetadot*math.Sin(theta)+yS*thetadot*math.Cos(theta))/rho - rhodot*(x-xS*math.Cos(theta)+yS*math.Sin(theta))/rho2
		drhodotDy := (yDot+yS*thetadot*math.Sin(theta)-xS*thetadot*math.Cos(theta))/rho - rhodot*(y-yS*math.Cos(theta)-xS*math.Sin(theta))/rho2
		drhodotDz := zDot/rho - rhodot*(z-zS)/rho2
		drhodotDxdot := (x - xS*math.Cos(theta) - yS*math.Sin(theta)) / rho
		drhodotDydot := (y - yS*math.Cos(theta) - xS*math.Sin(theta)) / rho
		drhodotDzdot := (z - zS) / rho
		// \partial \rho / \partial {x,y,z}
		H.Set(0, 0, drhoDx)
		H.Set(0, 1, drhoDy)
		H.Set(0, 2, drhoDz)
		// \partial \dot\rho / \partial {x,y,z}
		H.Set(1, 0, drhodotDx)
		H.Set(1, 1, drhodotDy)
		H.Set(1, 2, drhodotDz)
		H.Set(1, 3, drhodotDxdot)
		H.Set(1, 4, drhodotDydot)
		H.Set(1, 5, drhodotDzdot)
	} else {
		// \partial \rho / \partial {x,y,z}
		H.Set(0, 0, (x-xS)/m.ρ)
		H.Set(0, 1, (y-yS)/m.ρ)
		H.Set(0, 2, (z-zS)/m.ρ)
		// \partial \dot\rho / \partial {x,y,z}
		H.Set(1, 0, (xDot-xSDot)/m.ρ+(m.ρDot/math.Pow(m.ρ, 2))*(x-xS))
		H.Set(1, 1, (yDot-ySDot)/m.ρ+(m.ρDot/math.Pow(m.ρ, 2))*(y-yS))
		H.Set(1, 2, (zDot-zSDot)/m.ρ+(m.ρDot/math.Pow(m.ρ, 2))*(z-zS))
		H.Set(1, 3, (x-xS)/m.ρ)
		H.Set(1, 4, (y-yS)/m.ρ)
		H.Set(1, 5, (z-zS)/m.ρ)
	}
	return H
}

// CSV returns the data as CSV (does *not* include the new line)
func (m Measurement) CSV() string {
	return fmt.Sprintf("%f,%f,%f,%f,", m.trueρ, m.trueρDot, m.ρ, m.ρDot)
}

func (m Measurement) String() string {
	return fmt.Sprintf("%s@%s", m.Station.name, m.State.DT)
}

// Unshamefully copied from smd/math.go
func cross(a, b []float64) []float64 {
	return []float64{a[1]*b[2] - a[2]*b[1],
		a[2]*b[0] - a[0]*b[2],
		a[0]*b[1] - a[1]*b[0]} // Cross product R x V.
}

// norm returns the norm of a given vector which is supposed to be 3x1.
func norm(v []float64) float64 {
	return math.Sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
}
<|endoftext|>


<commit_before>package exchangerates

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/OpenBazaar/openbazaar-go/bitcoin/exchange"
	"golang.org/x/net/proxy"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
	"reflect"
)

type ExchangeRateProvider struct {
	fetchUrl        string
	cache           map[string]float64
	client          *http.Client
	decoder         ExchangeRateDecoder
	bitcoinProvider *exchange.BitcoinPriceFetcher
}

type ExchangeRateDecoder interface {
	decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error)
}

type KrakenDecoder struct{}
type BitfinexDecoder struct{}
type BittrexDecoder struct{}
type PoloniexDecoder struct{}

type BitcoinCashPriceFetcher struct {
	sync.Mutex
	cache     map[string]float64
	providers []*ExchangeRateProvider
}

func NewBitcoinCashPriceFetcher(dialer proxy.Dialer) *BitcoinCashPriceFetcher {
	bp := exchange.NewBitcoinPriceFetcher(dialer)
	b := BitcoinCashPriceFetcher{
		cache: make(map[string]float64),
	}
	dial := net.Dial
	if dialer != nil {
		dial = dialer.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	client := &http.Client{Transport: tbTransport, Timeout: time.Minute}

	b.providers = []*ExchangeRateProvider{
		{"https://bittrex.com/api/v1.1/public/getticker?market=btc-bcc", b.cache, client, BittrexDecoder{}, bp},
		{"https://api.bitfinex.com/v1/pubticker/bchbtc", b.cache, client, BitfinexDecoder{}, bp},
		{"https://poloniex.com/public?command=returnTicker", b.cache, client, PoloniexDecoder{}, bp},
		{"https://api.kraken.com/0/public/Ticker?pair=BCHXBT", b.cache, client, KrakenDecoder{}, bp},
	}
	go b.run()
	return &b
}

func (b *BitcoinCashPriceFetcher) GetExchangeRate(currencyCode string) (float64, error) {
	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinCashPriceFetcher) GetLatestRate(currencyCode string) (float64, error) {
	b.fetchCurrentRates()
	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinCashPriceFetcher) GetAllRates(cacheOK bool) (map[string]float64, error) {
	if !cacheOK {
		err := b.fetchCurrentRates()
		if err != nil {
			return nil, err
		}
	}
	b.Lock()
	defer b.Unlock()
	return b.cache, nil
}

func (b *BitcoinCashPriceFetcher) UnitsPerCoin() int {
	return exchange.SatoshiPerBTC
}

func (b *BitcoinCashPriceFetcher) fetchCurrentRates() error {
	b.Lock()
	defer b.Unlock()
	for _, provider := range b.providers {
		err := provider.fetch()
		if err == nil {
			return nil
		}
		fmt.Println(err)
	}
	return errors.New("All exchange rate API queries failed")
}

func (b *BitcoinCashPriceFetcher) run() {
	b.fetchCurrentRates()
	ticker := time.NewTicker(time.Minute * 15)
	for range ticker.C {
		b.fetchCurrentRates()
	}
}

func (provider *ExchangeRateProvider) fetch() (err error) {
	if len(provider.fetchUrl) == 0 {
		err = errors.New("Provider has no fetchUrl")
		return err
	}
	resp, err := provider.client.Get(provider.fetchUrl)
	if err != nil {
		return err
	}
	decoder := json.NewDecoder(resp.Body)
	var dataMap interface{}
	err = decoder.Decode(&dataMap)
	if err != nil {
		return err
	}
	return provider.decoder.decode(dataMap, provider.cache, provider.bitcoinProvider)
}

func (b KrakenDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	obj, ok := dat.(map[string]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	result, ok := obj["result"]
	if !ok {
		return errors.New("KrakenDecoder: field `result` not found")
	}
	resultMap, ok := result.(map[string]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	pair, ok := resultMap["BCHXBT"]
	if !ok {
		return errors.New("KrakenDecoder: field `BCHXBT` not found")
	}
	pairMap, ok := pair.(map[string]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	c, ok := pairMap["c"]
	if !ok {
		return errors.New("KrakenDecoder: field `c` not found")
	}
	cList, ok := c.([]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	rateStr, ok := cList[0].(string)
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	price, err := strconv.ParseFloat(rateStr, 64)
	if err != nil {
		return err
	}
	rate := price

	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}

func (b BitfinexDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	obj, ok := dat.(map[string]interface{})
	if !ok {
		return errors.New("BitfinexDecoder type assertion failure")
	}
	r, ok := obj["last_price"]
	if !ok {
		return errors.New("BitfinexDecoder: field `last_price` not found")
	}
	rateStr, ok := r.(string)
	if !ok {
		return errors.New("BitfinexDecoder type assertion failure")
	}
	price, err := strconv.ParseFloat(rateStr, 64)
	if err != nil {
		return err
	}
	rate := price

	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}

func (b BittrexDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	obj, ok := dat.(map[string]interface{})
	if !ok {
		return errors.New("BittrexDecoder type assertion failure")
	}
	result, ok := obj["result"]
	if !ok {
		return errors.New("BittrexDecoder: field `result` not found")
	}
	resultMap, ok := result.(map[string]interface{})
	if !ok {
		return errors.New("BittrexDecoder type assertion failure")
	}
	exRate, ok := resultMap["Last"]
	if !ok {
		return errors.New("BittrexDecoder: field `Last` not found")
	}
	rate, ok := exRate.(float64)
	if !ok {
		return errors.New("BittrexDecoder type assertion failure")
	}

	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}

func (b PoloniexDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	data := dat.(map[string]interface{})
	var rate float64
	for k, v := range data {
		if k == "BTC_BCH" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
			}
			s, ok := val["last"].(string)
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (string) field")
			}
			price, err := strconv.ParseFloat(s, 64)
			if err != nil {
				return err
			}
			rate = price
		}
	}
	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}<commit_msg>Add ticker.openbazaar.org to exchange rate providers<commit_after>package exchangerates

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/OpenBazaar/openbazaar-go/bitcoin/exchange"
	"golang.org/x/net/proxy"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
	"reflect"
)

type ExchangeRateProvider struct {
	fetchUrl        string
	cache           map[string]float64
	client          *http.Client
	decoder         ExchangeRateDecoder
	bitcoinProvider *exchange.BitcoinPriceFetcher
}

type ExchangeRateDecoder interface {
	decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error)
}

type OpenBazaarDecoder struct{}
type KrakenDecoder struct{}
type BitfinexDecoder struct{}
type BittrexDecoder struct{}
type PoloniexDecoder struct{}

type BitcoinCashPriceFetcher struct {
	sync.Mutex
	cache     map[string]float64
	providers []*ExchangeRateProvider
}

func NewBitcoinCashPriceFetcher(dialer proxy.Dialer) *BitcoinCashPriceFetcher {
	bp := exchange.NewBitcoinPriceFetcher(dialer)
	b := BitcoinCashPriceFetcher{
		cache: make(map[string]float64),
	}
	dial := net.Dial
	if dialer != nil {
		dial = dialer.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	client := &http.Client{Transport: tbTransport, Timeout: time.Minute}

	b.providers = []*ExchangeRateProvider{
		{"https://ticker.openbazaar.org/api", b.cache, client, OpenBazaarDecoder{}, nil},
		{"https://bittrex.com/api/v1.1/public/getticker?market=btc-bcc", b.cache, client, BittrexDecoder{}, bp},
		{"https://api.bitfinex.com/v1/pubticker/bchbtc", b.cache, client, BitfinexDecoder{}, bp},
		{"https://poloniex.com/public?command=returnTicker", b.cache, client, PoloniexDecoder{}, bp},
		{"https://api.kraken.com/0/public/Ticker?pair=BCHXBT", b.cache, client, KrakenDecoder{}, bp},
	}
	go b.run()
	return &b
}

func (b *BitcoinCashPriceFetcher) GetExchangeRate(currencyCode string) (float64, error) {
	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinCashPriceFetcher) GetLatestRate(currencyCode string) (float64, error) {
	b.fetchCurrentRates()
	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinCashPriceFetcher) GetAllRates(cacheOK bool) (map[string]float64, error) {
	if !cacheOK {
		err := b.fetchCurrentRates()
		if err != nil {
			return nil, err
		}
	}
	b.Lock()
	defer b.Unlock()
	return b.cache, nil
}

func (b *BitcoinCashPriceFetcher) UnitsPerCoin() int {
	return exchange.SatoshiPerBTC
}

func (b *BitcoinCashPriceFetcher) fetchCurrentRates() error {
	b.Lock()
	defer b.Unlock()
	for _, provider := range b.providers {
		err := provider.fetch()
		if err == nil {
			return nil
		}
		fmt.Println(err)
	}
	return errors.New("All exchange rate API queries failed")
}

func (b *BitcoinCashPriceFetcher) run() {
	b.fetchCurrentRates()
	ticker := time.NewTicker(time.Minute * 15)
	for range ticker.C {
		b.fetchCurrentRates()
	}
}

func (provider *ExchangeRateProvider) fetch() (err error) {
	if len(provider.fetchUrl) == 0 {
		err = errors.New("Provider has no fetchUrl")
		return err
	}
	resp, err := provider.client.Get(provider.fetchUrl)
	if err != nil {
		return err
	}
	decoder := json.NewDecoder(resp.Body)
	var dataMap interface{}
	err = decoder.Decode(&dataMap)
	if err != nil {
		return err
	}
	return provider.decoder.decode(dataMap, provider.cache, provider.bitcoinProvider)
}

func (b OpenBazaarDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	data := dat.(map[string]interface{})

	bch, ok := data["BCH"]
	if !ok {
		return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'BCH' field")
	}
	val, ok := bch.(map[string]interface{})
	if !ok {
		return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
	}
	bchRate, ok := val["last"].(float64)
	if !ok {
		return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field")
	}
	for k, v := range data {
		if k != "timestamp" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
			}
			price, ok := val["last"].(float64)
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field")
			}
			cache[k] = price*(1/bchRate)
		}
	}
	return nil
}

func (b KrakenDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	obj, ok := dat.(map[string]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	result, ok := obj["result"]
	if !ok {
		return errors.New("KrakenDecoder: field `result` not found")
	}
	resultMap, ok := result.(map[string]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	pair, ok := resultMap["BCHXBT"]
	if !ok {
		return errors.New("KrakenDecoder: field `BCHXBT` not found")
	}
	pairMap, ok := pair.(map[string]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	c, ok := pairMap["c"]
	if !ok {
		return errors.New("KrakenDecoder: field `c` not found")
	}
	cList, ok := c.([]interface{})
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	rateStr, ok := cList[0].(string)
	if !ok {
		return errors.New("KrackenDecoder type assertion failure")
	}
	price, err := strconv.ParseFloat(rateStr, 64)
	if err != nil {
		return err
	}
	rate := price

	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}

func (b BitfinexDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	obj, ok := dat.(map[string]interface{})
	if !ok {
		return errors.New("BitfinexDecoder type assertion failure")
	}
	r, ok := obj["last_price"]
	if !ok {
		return errors.New("BitfinexDecoder: field `last_price` not found")
	}
	rateStr, ok := r.(string)
	if !ok {
		return errors.New("BitfinexDecoder type assertion failure")
	}
	price, err := strconv.ParseFloat(rateStr, 64)
	if err != nil {
		return err
	}
	rate := price

	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}

func (b BittrexDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	obj, ok := dat.(map[string]interface{})
	if !ok {
		return errors.New("BittrexDecoder type assertion failure")
	}
	result, ok := obj["result"]
	if !ok {
		return errors.New("BittrexDecoder: field `result` not found")
	}
	resultMap, ok := result.(map[string]interface{})
	if !ok {
		return errors.New("BittrexDecoder type assertion failure")
	}
	exRate, ok := resultMap["Last"]
	if !ok {
		return errors.New("BittrexDecoder: field `Last` not found")
	}
	rate, ok := exRate.(float64)
	if !ok {
		return errors.New("BittrexDecoder type assertion failure")
	}

	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}

func (b PoloniexDecoder) decode(dat interface{}, cache map[string]float64, bp *exchange.BitcoinPriceFetcher) (err error) {
	rates, err := bp.GetAllRates(false)
	if err != nil {
		return err
	}
	data := dat.(map[string]interface{})
	var rate float64
	for k, v := range data {
		if k == "BTC_BCH" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
			}
			s, ok := val["last"].(string)
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (string) field")
			}
			price, err := strconv.ParseFloat(s, 64)
			if err != nil {
				return err
			}
			rate = price
		}
	}
	if rate == 0 {
		return errors.New("Bitcoin-BitcoinCash price data not available")
	}
	for k, v := range rates {
		cache[k] = v * rate
	}
	return nil
}<|endoftext|>