<commit_before>package ui

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/fabiofalci/sconsify/sconsify"
	"github.com/jroimartin/gocui"
)

type KeyMapping struct {
	key  interface{}
	h    gocui.KeybindingHandler
	view string
}

type Keyboard struct {
	ConfiguredKeys map[string][]string
	UsedFunctions  map[string]bool

	Keys []*KeyMapping
	MultipleKeys []*KeyMapping
}

type KeyEntry struct {
	Key     string
	Command string
}

const (
	PauseTrack string = "PauseTrack"
	ShuffleMode string = "ShuffleMode"
	ShuffleAllMode string = "ShuffleAllMode"
	NextTrack string = "NextTrack"
	ReplayTrack string = "ReplayTrack"
	Search string = "Search"
	Quit string = "Quit"
	QueueTrack string = "QueueTrack"
	RemoveTrackFromPlaylist string = "RemoveTrackFromPlaylist"
	RemoveTrackFromQueue string = "RemoveTrackFromQueue"
	RemoveAllTracksFromQueue string = "RemoveAllTracksFromQueue"
	GoToFirstLine string = "GoToFirstLine"
	GoToLastLine string = "GoToLastLine"
	PlaySelectedTrack string = "PlaySelectedTrack"
)

var multipleKeysBuffer bytes.Buffer
var multipleKeysNumber int
var multipleKeysHandlers map[string]gocui.KeybindingHandler

func (keyboard *Keyboard) defaultValues() {
	if !keyboard.UsedFunctions[PauseTrack] {
		keyboard.addKey("p", PauseTrack)
	}
	if !keyboard.UsedFunctions[ShuffleMode] {
		keyboard.addKey("s", ShuffleMode)
	}
	if !keyboard.UsedFunctions[ShuffleAllMode] {
		keyboard.addKey("S", ShuffleAllMode)
	}
	if !keyboard.UsedFunctions[NextTrack] {
		keyboard.addKey(">", NextTrack)
	}
	if !keyboard.UsedFunctions[ReplayTrack] {
		keyboard.addKey("<", ReplayTrack)
	}
	if !keyboard.UsedFunctions[Search] {
		keyboard.addKey("/", Search)
	}
	if !keyboard.UsedFunctions[Quit] {
		keyboard.addKey("q", Quit)
	}
	if !keyboard.UsedFunctions[QueueTrack] {
		keyboard.addKey("u", QueueTrack)
	}
	if !keyboard.UsedFunctions[RemoveTrackFromPlaylist] {
		keyboard.addKey("d", RemoveTrackFromPlaylist)
	}
	if !keyboard.UsedFunctions[RemoveTrackFromQueue] {
		keyboard.addKey("d", RemoveTrackFromQueue)
	}
	if !keyboard.UsedFunctions[RemoveAllTracksFromQueue] {
		keyboard.addKey("D", RemoveAllTracksFromQueue)
	}
	if !keyboard.UsedFunctions[GoToFirstLine] {
		keyboard.addKey("gg", GoToFirstLine)
	}
	if !keyboard.UsedFunctions[GoToLastLine] {
		keyboard.addKey("G", GoToLastLine)
	}
	if !keyboard.UsedFunctions[PlaySelectedTrack] {
		keyboard.addKey("<space>", PlaySelectedTrack)
		keyboard.addKey("<enter>", PlaySelectedTrack)
	}
}

func (keyboard *Keyboard) loadKeyFunctions() {
	if fileLocation := sconsify.GetKeyFunctionsFileLocation(); fileLocation != "" {
		if b, err := ioutil.ReadFile(fileLocation); err == nil {
			fileContent := make([]KeyEntry, 0)
			if err := json.Unmarshal(b, &fileContent); err == nil {
				for _, keyEntry := range fileContent {
					keyboard.addKey(keyEntry.Key, keyEntry.Command)
				}
			}
		}
	}
}

func (keyboard *Keyboard) addKey(key string, command string) {
	if keyboard.ConfiguredKeys[key] == nil {
		keyboard.ConfiguredKeys[key] = make([]string, 0)
	}
	keyboard.ConfiguredKeys[key] = append(keyboard.ConfiguredKeys[key], command)
	keyboard.UsedFunctions[command] = true
}

func (keyboard *Keyboard) configureKey(handler gocui.KeybindingHandler, command string, view string) {
	for key, commands := range keyboard.ConfiguredKeys {
		for _, c := range commands {
			if c == command {
				keyMapping, isMultiple := createKeyMapping(handler, key, view)
				keyboard.addToKeys(isMultiple, keyMapping)
			}
		}
	}
}

func getFirstRune(value string) rune {
	return getAsRuneArray(value)[0]
}

func getAsRuneArray(value string) []rune {
	return []rune(value)
}

func isMultipleKey(value string) bool {
	return len(getAsRuneArray(value)) > 1
}

func createKeyMapping(handler gocui.KeybindingHandler, key string, view string) (*KeyMapping, bool) {
	switch key {
	case "<enter>":
		return newKeyMapping(gocui.KeyEnter, view, handler), false
	case "<space>":
		return newKeyMapping(gocui.KeySpace, view, handler), false
	}
	if isMultipleKey(key) {
		keyRune := getAsRuneArray(key)
		multipleKeysHandlers[key] = handler
		return newKeyMapping(keyRune[0], view,
			func(g *gocui.Gui, v *gocui.View) error {
				return multipleKeysPressed(g, v, keyRune[0])
			}), true
	}
	return newKeyMapping(getFirstRune(key), view, handler), false
}

func (keyboard *Keyboard) addToKeys(isMultiple bool, keyMapping *KeyMapping) {
	if isMultiple {
		addKeyBinding(&keyboard.MultipleKeys, keyMapping)
	} else {
		addKeyBinding(&keyboard.Keys, keyMapping)
	}
}

func keybindings() error {
	keyboard := &Keyboard{
		ConfiguredKeys: make(map[string][]string),
		UsedFunctions: make(map[string]bool),
		Keys: make([]*KeyMapping, 0),
		MultipleKeys: make([]*KeyMapping, 0)}

	keyboard.loadKeyFunctions()
	keyboard.defaultValues()

	multipleKeysHandlers = make(map[string]gocui.KeybindingHandler)

	for _, view := range []string{VIEW_TRACKS, VIEW_PLAYLISTS, VIEW_QUEUE} {
		keyboard.configureKey(pauseTrackCommand, PauseTrack, view)
		keyboard.configureKey(setShuffleMode, ShuffleMode, view)
		keyboard.configureKey(setShuffleAllMode, ShuffleAllMode, view)
		keyboard.configureKey(nextTrackCommand, NextTrack, view)
		keyboard.configureKey(replayTrackCommand, ReplayTrack, view)
		keyboard.configureKey(enableSearchInputCommand, Search, view)
		keyboard.configureKey(quit, Quit, view)
		addKeyBinding(&keyboard.Keys, newKeyMapping('j', view, cursorDown))
		addKeyBinding(&keyboard.Keys, newKeyMapping('k', view, cursorUp))
	}

	allViews := ""
	keyboard.configureKey(queueTrackCommand, QueueTrack, allViews)
	keyboard.configureKey(removeTrackFromPlaylistsCommand, RemoveTrackFromPlaylist, allViews)
	keyboard.configureKey(removeTrackFromQueueCommand, RemoveTrackFromQueue, allViews)
	keyboard.configureKey(removeAllTracksFromQueueCommand, RemoveAllTracksFromQueue, allViews)
	keyboard.configureKey(goToFirstLineCommand, GoToFirstLine, allViews)
	keyboard.configureKey(goToLastLineCommand, GoToLastLine, allViews)
	keyboard.configureKey(playSelectedTrack, PlaySelectedTrack, allViews)

	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyEnter, VIEW_STATUS, searchCommand))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyHome, allViews, cursorHome))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyEnd, allViews, cursorEnd))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyPgup, allViews, cursorPgup))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyPgdn, allViews, cursorPgdn))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowDown, allViews, cursorDown))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowUp, allViews, cursorUp))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowLeft, VIEW_TRACKS, mainNextViewLeft))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowLeft, VIEW_QUEUE, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowRight, VIEW_PLAYLISTS, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowRight, VIEW_TRACKS, mainNextViewRight))
	addKeyBinding(&keyboard.Keys, newKeyMapping('h', VIEW_TRACKS, mainNextViewLeft))
	addKeyBinding(&keyboard.Keys, newKeyMapping('h', VIEW_QUEUE, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping('l', VIEW_PLAYLISTS, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping('l', VIEW_TRACKS, mainNextViewRight))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyCtrlC, allViews, quit))

	// numbers
	for i := 0; i < 10; i++ {
		numberCopy := i
		addKeyBinding(&keyboard.MultipleKeys, newKeyMapping(rune(i+48), allViews,
			func(g *gocui.Gui, v *gocui.View) error {
				return multipleKeysNumberPressed(numberCopy)
			}))
	}

	for _, key := range keyboard.Keys {
		// it needs to copy the key because closures copy var references and we don't
		// want to execute always the last action
		keyCopy := key
		if err := gui.g.SetKeybinding(key.view, key.key, 0,
			func(g *gocui.Gui, v *gocui.View) error {
				err := keyCopy.h(g, v)
				resetMultipleKeys()
				return err
			}); err != nil {
			return err
		}
	}

	for _, key := range keyboard.MultipleKeys {
		keyCopy := key
		if err := gui.g.SetKeybinding(key.view, key.key, 0,
			func(g *gocui.Gui, v *gocui.View) error {
				return keyCopy.h(g, v)
			}); err != nil {
			return err
		}
	}
	return nil
}

func addKeyBinding(keys *[]*KeyMapping, key *KeyMapping) {
	*keys = append(*keys, key)
}

func newKeyMapping(key interface{}, view string, h gocui.KeybindingHandler) *KeyMapping {
	return &KeyMapping{key: key, h: h, view: view}
}

func resetMultipleKeys() {
	multipleKeysBuffer.Reset()
	multipleKeysNumber = 0
}

func multipleKeysNumberPressed(pressedNumber int) error {
	if multipleKeysNumber == 0 {
		multipleKeysNumber = pressedNumber
	} else {
		multipleKeysNumber = multipleKeysNumber*10 + pressedNumber
	}
	return nil
}

func multipleKeysPressed(g *gocui.Gui, v *gocui.View, pressedKey rune) error {
	multipleKeysBuffer.WriteRune(pressedKey)

	handler := multipleKeysHandlers[multipleKeysBuffer.String()]
	if handler != nil {
		handler(g, v)
		resetMultipleKeys()
	}

	return nil
}

func playSelectedTrack(g *gocui.Gui, v *gocui.View) error {
	player.Play()
	return nil
}

func pauseTrackCommand(g *gocui.Gui, v *gocui.View) error {
	player.Pause()
	return nil
}

func setShuffleMode(g *gocui.Gui, v *gocui.View) error {
	playlists.InvertMode(sconsify.ShuffleMode)
	gui.updateCurrentStatus()
	return nil
}

func setShuffleAllMode(g *gocui.Gui, v *gocui.View) error {
	playlists.InvertMode(sconsify.ShuffleAllMode)
	gui.updateCurrentStatus()
	return nil
}

func nextTrackCommand(g *gocui.Gui, v *gocui.View) error {
	gui.playNext()
	return nil
}

func replayTrackCommand(g *gocui.Gui, v *gocui.View) error {
	gui.replay()
	return nil
}

func queueTrackCommand(g *gocui.Gui, v *gocui.View) error {
	if playlist, trackIndex := gui.getSelectedPlaylistAndTrack(); playlist != nil {
		track := playlist.Track(trackIndex)
		fmt.Fprintf(gui.queueView, "%v\n", track.GetTitle())
		queue.Add(track)
	}
	return nil
}

func removeAllTracksFromQueueCommand(g *gocui.Gui, v *gocui.View) error {
	queue.RemoveAll()
	gui.updateQueueView()
	return gui.enableTracksView()
}

func removeTrackFromQueueCommand(g *gocui.Gui, v *gocui.View) error {
	if index := gui.getQueueSelectedTrackIndex(); index > -1 {
		queue.Remove(index)
		gui.updateQueueView()
	}
	return nil
}

func removeTrackFromPlaylistsCommand(g *gocui.Gui, v *gocui.View) error {
	if playlist := gui.getSelectedPlaylist(); playlist != nil && playlist.IsSearch() {
		playlists.Remove(playlist.Name())
		gui.updatePlaylistsView()
		gui.updateTracksView()
	}
	return nil
}

func enableSearchInputCommand(g *gocui.Gui, v *gocui.View) error {
	gui.clearStatusView()
	gui.statusView.Editable = true
	gui.g.SetCurrentView(VIEW_STATUS)
	return nil
}

func searchCommand(g *gocui.Gui, v *gocui.View) error {
	// after user hit Enter, the typed command is at position -1
	query, _ := gui.statusView.Line(-1)
	query = strings.Trim(query, " ")
	if query != "" {
		events.Search(query)
	}
	gui.enableSideView()
	gui.clearStatusView()
	gui.statusView.Editable = false
	gui.updateCurrentStatus()
	return nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
	consoleUserInterface.Shutdown()
	// TODO wait for shutdown
	// <-events.ShutdownUpdates()
	return gocui.Quit
}
<commit_msg>Fix views to apply some commands<commit_after>package ui

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/fabiofalci/sconsify/sconsify"
	"github.com/jroimartin/gocui"
)

type KeyMapping struct {
	key  interface{}
	h    gocui.KeybindingHandler
	view string
}

type Keyboard struct {
	ConfiguredKeys map[string][]string
	UsedFunctions  map[string]bool

	Keys []*KeyMapping
	MultipleKeys []*KeyMapping
}

type KeyEntry struct {
	Key     string
	Command string
}

const (
	PauseTrack string = "PauseTrack"
	ShuffleMode string = "ShuffleMode"
	ShuffleAllMode string = "ShuffleAllMode"
	NextTrack string = "NextTrack"
	ReplayTrack string = "ReplayTrack"
	Search string = "Search"
	Quit string = "Quit"
	QueueTrack string = "QueueTrack"
	RemoveTrackFromPlaylist string = "RemoveTrackFromPlaylist"
	RemoveTrackFromQueue string = "RemoveTrackFromQueue"
	RemoveAllTracksFromQueue string = "RemoveAllTracksFromQueue"
	GoToFirstLine string = "GoToFirstLine"
	GoToLastLine string = "GoToLastLine"
	PlaySelectedTrack string = "PlaySelectedTrack"
)

var multipleKeysBuffer bytes.Buffer
var multipleKeysNumber int
var multipleKeysHandlers map[string]gocui.KeybindingHandler

func (keyboard *Keyboard) defaultValues() {
	if !keyboard.UsedFunctions[PauseTrack] {
		keyboard.addKey("p", PauseTrack)
	}
	if !keyboard.UsedFunctions[ShuffleMode] {
		keyboard.addKey("s", ShuffleMode)
	}
	if !keyboard.UsedFunctions[ShuffleAllMode] {
		keyboard.addKey("S", ShuffleAllMode)
	}
	if !keyboard.UsedFunctions[NextTrack] {
		keyboard.addKey(">", NextTrack)
	}
	if !keyboard.UsedFunctions[ReplayTrack] {
		keyboard.addKey("<", ReplayTrack)
	}
	if !keyboard.UsedFunctions[Search] {
		keyboard.addKey("/", Search)
	}
	if !keyboard.UsedFunctions[Quit] {
		keyboard.addKey("q", Quit)
	}
	if !keyboard.UsedFunctions[QueueTrack] {
		keyboard.addKey("u", QueueTrack)
	}
	if !keyboard.UsedFunctions[RemoveTrackFromPlaylist] {
		keyboard.addKey("d", RemoveTrackFromPlaylist)
	}
	if !keyboard.UsedFunctions[RemoveTrackFromQueue] {
		keyboard.addKey("d", RemoveTrackFromQueue)
	}
	if !keyboard.UsedFunctions[RemoveAllTracksFromQueue] {
		keyboard.addKey("D", RemoveAllTracksFromQueue)
	}
	if !keyboard.UsedFunctions[GoToFirstLine] {
		keyboard.addKey("gg", GoToFirstLine)
	}
	if !keyboard.UsedFunctions[GoToLastLine] {
		keyboard.addKey("G", GoToLastLine)
	}
	if !keyboard.UsedFunctions[PlaySelectedTrack] {
		keyboard.addKey("<space>", PlaySelectedTrack)
		keyboard.addKey("<enter>", PlaySelectedTrack)
	}
}

func (keyboard *Keyboard) loadKeyFunctions() {
	if fileLocation := sconsify.GetKeyFunctionsFileLocation(); fileLocation != "" {
		if b, err := ioutil.ReadFile(fileLocation); err == nil {
			fileContent := make([]KeyEntry, 0)
			if err := json.Unmarshal(b, &fileContent); err == nil {
				for _, keyEntry := range fileContent {
					keyboard.addKey(keyEntry.Key, keyEntry.Command)
				}
			}
		}
	}
}

func (keyboard *Keyboard) addKey(key string, command string) {
	if keyboard.ConfiguredKeys[key] == nil {
		keyboard.ConfiguredKeys[key] = make([]string, 0)
	}
	keyboard.ConfiguredKeys[key] = append(keyboard.ConfiguredKeys[key], command)
	keyboard.UsedFunctions[command] = true
}

func (keyboard *Keyboard) configureKey(handler gocui.KeybindingHandler, command string, view string) {
	for key, commands := range keyboard.ConfiguredKeys {
		for _, c := range commands {
			if c == command {
				keyMapping, isMultiple := createKeyMapping(handler, key, view)
				keyboard.addToKeys(isMultiple, keyMapping)
			}
		}
	}
}

func getFirstRune(value string) rune {
	return getAsRuneArray(value)[0]
}

func getAsRuneArray(value string) []rune {
	return []rune(value)
}

func isMultipleKey(value string) bool {
	return len(getAsRuneArray(value)) > 1
}

func createKeyMapping(handler gocui.KeybindingHandler, key string, view string) (*KeyMapping, bool) {
	switch key {
	case "<enter>":
		return newKeyMapping(gocui.KeyEnter, view, handler), false
	case "<space>":
		return newKeyMapping(gocui.KeySpace, view, handler), false
	}
	if isMultipleKey(key) {
		keyRune := getAsRuneArray(key)
		multipleKeysHandlers[key] = handler
		return newKeyMapping(keyRune[0], view,
			func(g *gocui.Gui, v *gocui.View) error {
				return multipleKeysPressed(g, v, keyRune[0])
			}), true
	}
	return newKeyMapping(getFirstRune(key), view, handler), false
}

func (keyboard *Keyboard) addToKeys(isMultiple bool, keyMapping *KeyMapping) {
	if isMultiple {
		addKeyBinding(&keyboard.MultipleKeys, keyMapping)
	} else {
		addKeyBinding(&keyboard.Keys, keyMapping)
	}
}

func keybindings() error {
	keyboard := &Keyboard{
		ConfiguredKeys: make(map[string][]string),
		UsedFunctions: make(map[string]bool),
		Keys: make([]*KeyMapping, 0),
		MultipleKeys: make([]*KeyMapping, 0)}

	keyboard.loadKeyFunctions()
	keyboard.defaultValues()

	multipleKeysHandlers = make(map[string]gocui.KeybindingHandler)

	for _, view := range []string{VIEW_TRACKS, VIEW_PLAYLISTS, VIEW_QUEUE} {
		keyboard.configureKey(pauseTrackCommand, PauseTrack, view)
		keyboard.configureKey(setShuffleMode, ShuffleMode, view)
		keyboard.configureKey(setShuffleAllMode, ShuffleAllMode, view)
		keyboard.configureKey(nextTrackCommand, NextTrack, view)
		keyboard.configureKey(replayTrackCommand, ReplayTrack, view)
		keyboard.configureKey(enableSearchInputCommand, Search, view)
		keyboard.configureKey(quit, Quit, view)
		addKeyBinding(&keyboard.Keys, newKeyMapping('j', view, cursorDown))
		addKeyBinding(&keyboard.Keys, newKeyMapping('k', view, cursorUp))
	}

	allViews := ""
	keyboard.configureKey(queueTrackCommand, QueueTrack, VIEW_TRACKS)
	keyboard.configureKey(removeTrackFromPlaylistsCommand, RemoveTrackFromPlaylist, VIEW_PLAYLISTS)
	keyboard.configureKey(removeTrackFromQueueCommand, RemoveTrackFromQueue, VIEW_QUEUE)
	keyboard.configureKey(removeAllTracksFromQueueCommand, RemoveAllTracksFromQueue, VIEW_QUEUE)
	keyboard.configureKey(goToFirstLineCommand, GoToFirstLine, allViews)
	keyboard.configureKey(goToLastLineCommand, GoToLastLine, allViews)
	keyboard.configureKey(playSelectedTrack, PlaySelectedTrack, VIEW_TRACKS)

	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyEnter, VIEW_STATUS, searchCommand))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyHome, allViews, cursorHome))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyEnd, allViews, cursorEnd))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyPgup, allViews, cursorPgup))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyPgdn, allViews, cursorPgdn))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowDown, allViews, cursorDown))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowUp, allViews, cursorUp))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowLeft, VIEW_TRACKS, mainNextViewLeft))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowLeft, VIEW_QUEUE, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowRight, VIEW_PLAYLISTS, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyArrowRight, VIEW_TRACKS, mainNextViewRight))
	addKeyBinding(&keyboard.Keys, newKeyMapping('h', VIEW_TRACKS, mainNextViewLeft))
	addKeyBinding(&keyboard.Keys, newKeyMapping('h', VIEW_QUEUE, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping('l', VIEW_PLAYLISTS, nextView))
	addKeyBinding(&keyboard.Keys, newKeyMapping('l', VIEW_TRACKS, mainNextViewRight))
	addKeyBinding(&keyboard.Keys, newKeyMapping(gocui.KeyCtrlC, allViews, quit))

	// numbers
	for i := 0; i < 10; i++ {
		numberCopy := i
		addKeyBinding(&keyboard.MultipleKeys, newKeyMapping(rune(i+48), allViews,
			func(g *gocui.Gui, v *gocui.View) error {
				return multipleKeysNumberPressed(numberCopy)
			}))
	}

	for _, key := range keyboard.Keys {
		// it needs to copy the key because closures copy var references and we don't
		// want to execute always the last action
		keyCopy := key
		if err := gui.g.SetKeybinding(key.view, key.key, 0,
			func(g *gocui.Gui, v *gocui.View) error {
				err := keyCopy.h(g, v)
				resetMultipleKeys()
				return err
			}); err != nil {
			return err
		}
	}

	for _, key := range keyboard.MultipleKeys {
		keyCopy := key
		if err := gui.g.SetKeybinding(key.view, key.key, 0,
			func(g *gocui.Gui, v *gocui.View) error {
				return keyCopy.h(g, v)
			}); err != nil {
			return err
		}
	}
	return nil
}

func addKeyBinding(keys *[]*KeyMapping, key *KeyMapping) {
	*keys = append(*keys, key)
}

func newKeyMapping(key interface{}, view string, h gocui.KeybindingHandler) *KeyMapping {
	return &KeyMapping{key: key, h: h, view: view}
}

func resetMultipleKeys() {
	multipleKeysBuffer.Reset()
	multipleKeysNumber = 0
}

func multipleKeysNumberPressed(pressedNumber int) error {
	if multipleKeysNumber == 0 {
		multipleKeysNumber = pressedNumber
	} else {
		multipleKeysNumber = multipleKeysNumber*10 + pressedNumber
	}
	return nil
}

func multipleKeysPressed(g *gocui.Gui, v *gocui.View, pressedKey rune) error {
	multipleKeysBuffer.WriteRune(pressedKey)

	handler := multipleKeysHandlers[multipleKeysBuffer.String()]
	if handler != nil {
		handler(g, v)
		resetMultipleKeys()
	}

	return nil
}

func playSelectedTrack(g *gocui.Gui, v *gocui.View) error {
	player.Play()
	return nil
}

func pauseTrackCommand(g *gocui.Gui, v *gocui.View) error {
	player.Pause()
	return nil
}

func setShuffleMode(g *gocui.Gui, v *gocui.View) error {
	playlists.InvertMode(sconsify.ShuffleMode)
	gui.updateCurrentStatus()
	return nil
}

func setShuffleAllMode(g *gocui.Gui, v *gocui.View) error {
	playlists.InvertMode(sconsify.ShuffleAllMode)
	gui.updateCurrentStatus()
	return nil
}

func nextTrackCommand(g *gocui.Gui, v *gocui.View) error {
	gui.playNext()
	return nil
}

func replayTrackCommand(g *gocui.Gui, v *gocui.View) error {
	gui.replay()
	return nil
}

func queueTrackCommand(g *gocui.Gui, v *gocui.View) error {
	if playlist, trackIndex := gui.getSelectedPlaylistAndTrack(); playlist != nil {
		track := playlist.Track(trackIndex)
		fmt.Fprintf(gui.queueView, "%v\n", track.GetTitle())
		queue.Add(track)
	}
	return nil
}

func removeAllTracksFromQueueCommand(g *gocui.Gui, v *gocui.View) error {
	queue.RemoveAll()
	gui.updateQueueView()
	return gui.enableTracksView()
}

func removeTrackFromQueueCommand(g *gocui.Gui, v *gocui.View) error {
	if index := gui.getQueueSelectedTrackIndex(); index > -1 {
		queue.Remove(index)
		gui.updateQueueView()
	}
	return nil
}

func removeTrackFromPlaylistsCommand(g *gocui.Gui, v *gocui.View) error {
	if playlist := gui.getSelectedPlaylist(); playlist != nil && playlist.IsSearch() {
		playlists.Remove(playlist.Name())
		gui.updatePlaylistsView()
		gui.updateTracksView()
	}
	return nil
}

func enableSearchInputCommand(g *gocui.Gui, v *gocui.View) error {
	gui.clearStatusView()
	gui.statusView.Editable = true
	gui.g.SetCurrentView(VIEW_STATUS)
	return nil
}

func searchCommand(g *gocui.Gui, v *gocui.View) error {
	// after user hit Enter, the typed command is at position -1
	query, _ := gui.statusView.Line(-1)
	query = strings.Trim(query, " ")
	if query != "" {
		events.Search(query)
	}
	gui.enableSideView()
	gui.clearStatusView()
	gui.statusView.Editable = false
	gui.updateCurrentStatus()
	return nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
	consoleUserInterface.Shutdown()
	// TODO wait for shutdown
	// <-events.ShutdownUpdates()
	return gocui.Quit
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package ui

import (
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"

	"github.com/bogem/nehm/track"
)

// TracksMenu gets tracks from GetTracks function, show these tracks in menu
// and returns selected.
//
// TracksMenu finishes when user pushes 'd' button.
type TracksMenu struct {
	GetTracks func(offset uint) ([]track.Track, error)
	Limit     uint
	Offset    uint

	selected          []track.Track
	selectionFinished bool
}

// Show gets tracks from GetTracks function, show these tracks,
// adds selected to TracksMenu.selected and returns them.
func (tm TracksMenu) Show() []track.Track {
	Println("Getting information about tracks")
	tracks, err := tm.GetTracks(tm.Offset)
	if err != nil {
		handleError(err)
		Term("", nil)
	}
	oldOffset := tm.Offset

	if len(tracks) == 0 {
		Term("there are no tracks to show", nil)
	}

	for !tm.selectionFinished {
		if oldOffset != tm.Offset {
			oldOffset = tm.Offset
			tracks, err = tm.GetTracks(tm.Offset)
			if err != nil {
				handleError(err)
				if tm.Offset >= tm.Limit {
					Println("Downloading previous page")
					Sleep() // pause the goroutine so user can read the errors
					tm.Offset -= tm.Limit
					continue
				} else {
					Term("", nil)
				}
			}
		}

		trackItems := tm.formTrackItems(tracks)
		clearScreen()
		tm.showMenu(trackItems)
	}
	return tm.selected
}

func handleError(err error) {
	switch {
	case strings.Contains(err.Error(), "403"):
		Error("you're not allowed to see these tracks", nil)
	case strings.Contains(err.Error(), "404"):
		Error("there are no tracks", nil)
	default:
		Error("", err)
	}
}

var trackItems []MenuItem

func (tm *TracksMenu) formTrackItems(tracks []track.Track) []MenuItem {
	if trackItems == nil {
		trackItems = make([]MenuItem, 0, tm.Limit)
	}
	trackItems = trackItems[:0]

	for i, t := range tracks {
		desc := t.Fullname() + " (" + t.Duration() + ")"

		var trackItem MenuItem
		if contains(tm.selected, t) {
			trackItem = MenuItem{
				Index: GreenString("A"),
				Desc:  desc,
			}
		} else {
			t := t // https://golang.org/doc/faq#closures_and_goroutines
			trackItem = MenuItem{
				Index: strconv.Itoa(i + 1),
				Desc:  desc,
				Run:   func() { tm.selected = append(tm.selected, t) },
			}
		}
		trackItems = append(trackItems, trackItem)
	}
	return trackItems
}

func contains(s []track.Track, t track.Track) bool {
	for _, v := range s {
		if v.ID() == t.ID() {
			return true
		}
	}
	return false
}

// clearPath is used for holding the path to clear binary,
// so exec.Command() don't have to always look the path to command.
var clearPath string

func clearScreen() {
	var err error
	if runtime.GOOS == "windows" {
		clearPath, err = exec.LookPath("cls")
	} else {
		clearPath, err = exec.LookPath("clear")
	}
	if err != nil { // if there is no clear command, just do not clear the screen
		return
	}
	cmd := exec.Command(clearPath)
	cmd.Stdout = os.Stdout
	cmd.Run()
}

var (
	controlItems []MenuItem
	menu         Menu
)

func (tm *TracksMenu) showMenu(trackItems []MenuItem) {
	if controlItems == nil {
		controlItems = tm.controlItems()
	}
	menu.Clear()
	menu.AddItems(trackItems...)
	menu.AddNewline()
	menu.AddItems(controlItems...)
	menu.Show()
}

func (tm *TracksMenu) controlItems() []MenuItem {
	return []MenuItem{
		MenuItem{
			Index: "d",
			Desc:  GreenString("Download tracks"),
			Run:   func() { tm.selectionFinished = true },
		},

		MenuItem{
			Index: "n",
			Desc:  "Next page",
			Run:   func() { tm.Offset += tm.Limit },
		},

		MenuItem{
			Index: "p",
			Desc:  "Prev page",
			Run: func() {
				if tm.Offset >= tm.Limit {
					tm.Offset -= tm.Limit
				} else {
					tm.Offset = 0
				}
			},
		},
	}
}
<commit_msg>Use map for TracksMenu.selected<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package ui

import (
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"

	"github.com/bogem/nehm/track"
)

// TracksMenu gets tracks from GetTracks function, show these tracks in menu
// and returns selected.
//
// TracksMenu finishes when user pushes 'd' button.
type TracksMenu struct {
	GetTracks func(offset uint) ([]track.Track, error)
	Limit     uint
	Offset    uint

	selected          map[float64]bool
	selectionFinished bool
}

// Show gets tracks from GetTracks function, show these tracks,
// adds selected to TracksMenu.selected and returns them.
func (tm TracksMenu) Show() []track.Track {
	Println("Getting information about tracks")
	tracks, err := tm.GetTracks(tm.Offset)
	if err != nil {
		handleError(err)
		Term("", nil)
	}
	oldOffset := tm.Offset

	if len(tracks) == 0 {
		Term("there are no tracks to show", nil)
	}

	tm.selected = make(map[float64]track.Track)
	for !tm.selectionFinished {
		if oldOffset != tm.Offset {
			oldOffset = tm.Offset
			tracks, err = tm.GetTracks(tm.Offset)
			if err != nil {
				handleError(err)
				if tm.Offset >= tm.Limit {
					Println("Downloading previous page")
					Sleep() // pause the goroutine so user can read the errors
					tm.Offset -= tm.Limit
					continue
				} else {
					Term("", nil)
				}
			}
		}

		trackItems := tm.formTrackItems(tracks)
		clearScreen()
		tm.showMenu(trackItems)
	}

	selected := make([]track.Track, 0, len(tm.selected))
	for _, t := range tm.selected {
		selected = append(selected, t)
	}
	return selected
}

func handleError(err error) {
	switch {
	case strings.Contains(err.Error(), "403"):
		Error("you're not allowed to see these tracks", nil)
	case strings.Contains(err.Error(), "404"):
		Error("there are no tracks", nil)
	default:
		Error("", err)
	}
}

var trackItems []MenuItem

func (tm *TracksMenu) formTrackItems(tracks []track.Track) []MenuItem {
	if trackItems == nil {
		trackItems = make([]MenuItem, 0, tm.Limit)
	}
	trackItems = trackItems[:0]

	for i, t := range tracks {
		desc := t.Fullname() + " (" + t.Duration() + ")"

		var trackItem MenuItem
		if contains := tm.selected[t.ID()]; contains {
			trackItem = MenuItem{
				Index: GreenString("A"),
				Desc:  desc,
			}
		} else {
			t := t // https://golang.org/doc/faq#closures_and_goroutines
			trackItem = MenuItem{
				Index: strconv.Itoa(i + 1),
				Desc:  desc,
				Run:   func() { tm.selected[t.ID()] = true },
			}
		}
		trackItems = append(trackItems, trackItem)
	}
	return trackItems
}

func contains(s []track.Track, t track.Track) bool {
	for _, v := range s {
		if v.ID() == t.ID() {
			return true
		}
	}
	return false
}

// clearPath is used for holding the path to clear binary,
// so exec.Command() don't have to always look the path to command.
var clearPath string

func clearScreen() {
	var err error
	if runtime.GOOS == "windows" {
		clearPath, err = exec.LookPath("cls")
	} else {
		clearPath, err = exec.LookPath("clear")
	}
	if err != nil { // if there is no clear command, just do not clear the screen
		return
	}
	cmd := exec.Command(clearPath)
	cmd.Stdout = os.Stdout
	cmd.Run()
}

var (
	controlItems []MenuItem
	menu         Menu
)

func (tm *TracksMenu) showMenu(trackItems []MenuItem) {
	if controlItems == nil {
		controlItems = tm.controlItems()
	}
	menu.Clear()
	menu.AddItems(trackItems...)
	menu.AddNewline()
	menu.AddItems(controlItems...)
	menu.Show()
}

func (tm *TracksMenu) controlItems() []MenuItem {
	return []MenuItem{
		MenuItem{
			Index: "d",
			Desc:  GreenString("Download tracks"),
			Run:   func() { tm.selectionFinished = true },
		},

		MenuItem{
			Index: "n",
			Desc:  "Next page",
			Run:   func() { tm.Offset += tm.Limit },
		},

		MenuItem{
			Index: "p",
			Desc:  "Prev page",
			Run: func() {
				if tm.Offset >= tm.Limit {
					tm.Offset -= tm.Limit
				} else {
					tm.Offset = 0
				}
			},
		},
	}
}
<|endoftext|>


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

// Package endpoints provides constants for using OAuth2 to access various services.
package endpoints

import (
	"strings"

	"golang.org/x/oauth2"
)

// Amazon is the endpoint for Amazon.
var Amazon = oauth2.Endpoint{
	AuthURL:  "https://www.amazon.com/ap/oa",
	TokenURL: "https://api.amazon.com/auth/o2/token",
}

// Bitbucket is the endpoint for Bitbucket.
var Bitbucket = oauth2.Endpoint{
	AuthURL:  "https://bitbucket.org/site/oauth2/authorize",
	TokenURL: "https://bitbucket.org/site/oauth2/access_token",
}

// Cern is the endpoint for CERN.
var Cern = oauth2.Endpoint{
	AuthURL:  "https://oauth.web.cern.ch/OAuth/Authorize",
	TokenURL: "https://oauth.web.cern.ch/OAuth/Token",
}

// Facebook is the endpoint for Facebook.
var Facebook = oauth2.Endpoint{
	AuthURL:  "https://www.facebook.com/v3.2/dialog/oauth",
	TokenURL: "https://graph.facebook.com/v3.2/oauth/access_token",
}

// Foursquare is the endpoint for Foursquare.
var Foursquare = oauth2.Endpoint{
	AuthURL:  "https://foursquare.com/oauth2/authorize",
	TokenURL: "https://foursquare.com/oauth2/access_token",
}

// Fitbit is the endpoint for Fitbit.
var Fitbit = oauth2.Endpoint{
	AuthURL:  "https://www.fitbit.com/oauth2/authorize",
	TokenURL: "https://api.fitbit.com/oauth2/token",
}

// GitHub is the endpoint for Github.
var GitHub = oauth2.Endpoint{
	AuthURL:  "https://github.com/login/oauth/authorize",
	TokenURL: "https://github.com/login/oauth/access_token",
}

// GitLab is the endpoint for GitLab.
var GitLab = oauth2.Endpoint{
	AuthURL:  "https://gitlab.com/oauth/authorize",
	TokenURL: "https://gitlab.com/oauth/token",
}

// Google is the endpoint for Google.
var Google = oauth2.Endpoint{
	AuthURL:  "https://accounts.google.com/o/oauth2/auth",
	TokenURL: "https://oauth2.googleapis.com/token",
}

// Heroku is the endpoint for Heroku.
var Heroku = oauth2.Endpoint{
	AuthURL:  "https://id.heroku.com/oauth/authorize",
	TokenURL: "https://id.heroku.com/oauth/token",
}

// HipChat is the endpoint for HipChat.
var HipChat = oauth2.Endpoint{
	AuthURL:  "https://www.hipchat.com/users/authorize",
	TokenURL: "https://api.hipchat.com/v2/oauth/token",
}

// Instagram is the endpoint for Instagram.
var Instagram = oauth2.Endpoint{
	AuthURL:  "https://api.instagram.com/oauth/authorize",
	TokenURL: "https://api.instagram.com/oauth/access_token",
}

// KaKao is the endpoint for KaKao.
var KaKao = oauth2.Endpoint{
	AuthURL:  "https://kauth.kakao.com/oauth/authorize",
	TokenURL: "https://kauth.kakao.com/oauth/token",
}

// LinkedIn is the endpoint for LinkedIn.
var LinkedIn = oauth2.Endpoint{
	AuthURL:  "https://www.linkedin.com/oauth/v2/authorization",
	TokenURL: "https://www.linkedin.com/oauth/v2/accessToken",
}

// Mailchimp is the endpoint for Mailchimp.
var Mailchimp = oauth2.Endpoint{
	AuthURL:  "https://login.mailchimp.com/oauth2/authorize",
	TokenURL: "https://login.mailchimp.com/oauth2/token",
}

// Mailru is the endpoint for Mail.Ru.
var Mailru = oauth2.Endpoint{
	AuthURL:  "https://o2.mail.ru/login",
	TokenURL: "https://o2.mail.ru/token",
}

// MediaMath is the endpoint for MediaMath.
var MediaMath = oauth2.Endpoint{
	AuthURL:  "https://api.mediamath.com/oauth2/v1.0/authorize",
	TokenURL: "https://api.mediamath.com/oauth2/v1.0/token",
}

// MediaMathSandbox is the endpoint for MediaMath Sandbox.
var MediaMathSandbox = oauth2.Endpoint{
	AuthURL:  "https://t1sandbox.mediamath.com/oauth2/v1.0/authorize",
	TokenURL: "https://t1sandbox.mediamath.com/oauth2/v1.0/token",
}

// Microsoft is the endpoint for Microsoft.
var Microsoft = oauth2.Endpoint{
	AuthURL:  "https://login.live.com/oauth20_authorize.srf",
	TokenURL: "https://login.live.com/oauth20_token.srf",
}

// NokiaHealth is the endpoint for Nokia Health.
var NokiaHealth = oauth2.Endpoint{
	AuthURL:  "https://account.health.nokia.com/oauth2_user/authorize2",
	TokenURL: "https://account.health.nokia.com/oauth2/token",
}

// Odnoklassniki is the endpoint for Odnoklassniki.
var Odnoklassniki = oauth2.Endpoint{
	AuthURL:  "https://www.odnoklassniki.ru/oauth/authorize",
	TokenURL: "https://api.odnoklassniki.ru/oauth/token.do",
}

// PayPal is the endpoint for PayPal.
var PayPal = oauth2.Endpoint{
	AuthURL:  "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
	TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice",
}

// PayPalSandbox is the endpoint for PayPal Sandbox.
var PayPalSandbox = oauth2.Endpoint{
	AuthURL:  "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
	TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice",
}

// Slack is the endpoint for Slack.
var Slack = oauth2.Endpoint{
	AuthURL:  "https://slack.com/oauth/authorize",
	TokenURL: "https://slack.com/api/oauth.access",
}

// Spotify is the endpoint for Spotify.
var Spotify = oauth2.Endpoint{
	AuthURL:  "https://accounts.spotify.com/authorize",
	TokenURL: "https://accounts.spotify.com/api/token",
}

// StackOverflow is the endpoint for Stack Overflow.
var StackOverflow = oauth2.Endpoint{
	AuthURL:  "https://stackoverflow.com/oauth",
	TokenURL: "https://stackoverflow.com/oauth/access_token",
}

// Strava is the endpoint for Strava.
var Strava = oauth2.Endpoint{
	AuthURL:  "https://www.strava.com/oauth/authorize",
	TokenURL: "https://www.strava.com/oauth/token",
}

// Twitch is the endpoint for Twitch.
var Twitch = oauth2.Endpoint{
	AuthURL:  "https://id.twitch.tv/oauth2/authorize",
	TokenURL: "https://id.twitch.tv/oauth2/token",
}

// Uber is the endpoint for Uber.
var Uber = oauth2.Endpoint{
	AuthURL:  "https://login.uber.com/oauth/v2/authorize",
	TokenURL: "https://login.uber.com/oauth/v2/token",
}

// Vk is the endpoint for Vk.
var Vk = oauth2.Endpoint{
	AuthURL:  "https://oauth.vk.com/authorize",
	TokenURL: "https://oauth.vk.com/access_token",
}

// Yahoo is the endpoint for Yahoo.
var Yahoo = oauth2.Endpoint{
	AuthURL:  "https://api.login.yahoo.com/oauth2/request_auth",
	TokenURL: "https://api.login.yahoo.com/oauth2/get_token",
}

// Yandex is the endpoint for Yandex.
var Yandex = oauth2.Endpoint{
	AuthURL:  "https://oauth.yandex.com/authorize",
	TokenURL: "https://oauth.yandex.com/token",
}

// Zoom is the endpoint for Zoom.
var Zoom = oauth2.Endpoint{
	AuthURL:  "https://zoom.us/oauth/authorize",
	TokenURL: "https://zoom.us/oauth/token",
}

// AzureAD returns a new oauth2.Endpoint for the given tenant at Azure Active Directory.
// If tenant is empty, it uses the tenant called `common`.
//
// For more information see:
// https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints
func AzureAD(tenant string) oauth2.Endpoint {
	if tenant == "" {
		tenant = "common"
	}
	return oauth2.Endpoint{
		AuthURL:  "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/authorize",
		TokenURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token",
	}
}

// HipChatServer returns a new oauth2.Endpoint for a HipChat Server instance
// running on the given domain or host.
func HipChatServer(host string) oauth2.Endpoint {
	return oauth2.Endpoint{
		AuthURL:  "https://" + host + "/users/authorize",
		TokenURL: "https://" + host + "/v2/oauth/token",
	}
}

// AWSCognito returns a new oauth2.Endpoint for the supplied AWS Cognito domain which is
// linked to your Cognito User Pool.
//
// Example domain: https://testing.auth.us-east-1.amazoncognito.com
//
// For more information see:
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain.html
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html
func AWSCognito(domain string) oauth2.Endpoint {
	domain = strings.TrimRight(domain, "/")
	return oauth2.Endpoint{
		AuthURL:  domain + "/oauth2/authorize",
		TokenURL: domain + "/oauth2/token",
	}
}
<commit_msg>endpoints: add Battlenet endpoints<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package endpoints provides constants for using OAuth2 to access various services.
package endpoints

import (
	"strings"

	"golang.org/x/oauth2"
)

// Amazon is the endpoint for Amazon.
var Amazon = oauth2.Endpoint{
	AuthURL:  "https://www.amazon.com/ap/oa",
	TokenURL: "https://api.amazon.com/auth/o2/token",
}

// Battlenet is the endpoint for Battlenet.
var Battlenet = oauth2.Endpoint{
	AuthURL: "https://battle.net/oauth/authorize",
	TokenURL: "https://battle.net/oauth/token",
}

// Bitbucket is the endpoint for Bitbucket.
var Bitbucket = oauth2.Endpoint{
	AuthURL:  "https://bitbucket.org/site/oauth2/authorize",
	TokenURL: "https://bitbucket.org/site/oauth2/access_token",
}

// Cern is the endpoint for CERN.
var Cern = oauth2.Endpoint{
	AuthURL:  "https://oauth.web.cern.ch/OAuth/Authorize",
	TokenURL: "https://oauth.web.cern.ch/OAuth/Token",
}

// Facebook is the endpoint for Facebook.
var Facebook = oauth2.Endpoint{
	AuthURL:  "https://www.facebook.com/v3.2/dialog/oauth",
	TokenURL: "https://graph.facebook.com/v3.2/oauth/access_token",
}

// Foursquare is the endpoint for Foursquare.
var Foursquare = oauth2.Endpoint{
	AuthURL:  "https://foursquare.com/oauth2/authorize",
	TokenURL: "https://foursquare.com/oauth2/access_token",
}

// Fitbit is the endpoint for Fitbit.
var Fitbit = oauth2.Endpoint{
	AuthURL:  "https://www.fitbit.com/oauth2/authorize",
	TokenURL: "https://api.fitbit.com/oauth2/token",
}

// GitHub is the endpoint for Github.
var GitHub = oauth2.Endpoint{
	AuthURL:  "https://github.com/login/oauth/authorize",
	TokenURL: "https://github.com/login/oauth/access_token",
}

// GitLab is the endpoint for GitLab.
var GitLab = oauth2.Endpoint{
	AuthURL:  "https://gitlab.com/oauth/authorize",
	TokenURL: "https://gitlab.com/oauth/token",
}

// Google is the endpoint for Google.
var Google = oauth2.Endpoint{
	AuthURL:  "https://accounts.google.com/o/oauth2/auth",
	TokenURL: "https://oauth2.googleapis.com/token",
}

// Heroku is the endpoint for Heroku.
var Heroku = oauth2.Endpoint{
	AuthURL:  "https://id.heroku.com/oauth/authorize",
	TokenURL: "https://id.heroku.com/oauth/token",
}

// HipChat is the endpoint for HipChat.
var HipChat = oauth2.Endpoint{
	AuthURL:  "https://www.hipchat.com/users/authorize",
	TokenURL: "https://api.hipchat.com/v2/oauth/token",
}

// Instagram is the endpoint for Instagram.
var Instagram = oauth2.Endpoint{
	AuthURL:  "https://api.instagram.com/oauth/authorize",
	TokenURL: "https://api.instagram.com/oauth/access_token",
}

// KaKao is the endpoint for KaKao.
var KaKao = oauth2.Endpoint{
	AuthURL:  "https://kauth.kakao.com/oauth/authorize",
	TokenURL: "https://kauth.kakao.com/oauth/token",
}

// LinkedIn is the endpoint for LinkedIn.
var LinkedIn = oauth2.Endpoint{
	AuthURL:  "https://www.linkedin.com/oauth/v2/authorization",
	TokenURL: "https://www.linkedin.com/oauth/v2/accessToken",
}

// Mailchimp is the endpoint for Mailchimp.
var Mailchimp = oauth2.Endpoint{
	AuthURL:  "https://login.mailchimp.com/oauth2/authorize",
	TokenURL: "https://login.mailchimp.com/oauth2/token",
}

// Mailru is the endpoint for Mail.Ru.
var Mailru = oauth2.Endpoint{
	AuthURL:  "https://o2.mail.ru/login",
	TokenURL: "https://o2.mail.ru/token",
}

// MediaMath is the endpoint for MediaMath.
var MediaMath = oauth2.Endpoint{
	AuthURL:  "https://api.mediamath.com/oauth2/v1.0/authorize",
	TokenURL: "https://api.mediamath.com/oauth2/v1.0/token",
}

// MediaMathSandbox is the endpoint for MediaMath Sandbox.
var MediaMathSandbox = oauth2.Endpoint{
	AuthURL:  "https://t1sandbox.mediamath.com/oauth2/v1.0/authorize",
	TokenURL: "https://t1sandbox.mediamath.com/oauth2/v1.0/token",
}

// Microsoft is the endpoint for Microsoft.
var Microsoft = oauth2.Endpoint{
	AuthURL:  "https://login.live.com/oauth20_authorize.srf",
	TokenURL: "https://login.live.com/oauth20_token.srf",
}

// NokiaHealth is the endpoint for Nokia Health.
var NokiaHealth = oauth2.Endpoint{
	AuthURL:  "https://account.health.nokia.com/oauth2_user/authorize2",
	TokenURL: "https://account.health.nokia.com/oauth2/token",
}

// Odnoklassniki is the endpoint for Odnoklassniki.
var Odnoklassniki = oauth2.Endpoint{
	AuthURL:  "https://www.odnoklassniki.ru/oauth/authorize",
	TokenURL: "https://api.odnoklassniki.ru/oauth/token.do",
}

// PayPal is the endpoint for PayPal.
var PayPal = oauth2.Endpoint{
	AuthURL:  "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
	TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice",
}

// PayPalSandbox is the endpoint for PayPal Sandbox.
var PayPalSandbox = oauth2.Endpoint{
	AuthURL:  "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
	TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice",
}

// Slack is the endpoint for Slack.
var Slack = oauth2.Endpoint{
	AuthURL:  "https://slack.com/oauth/authorize",
	TokenURL: "https://slack.com/api/oauth.access",
}

// Spotify is the endpoint for Spotify.
var Spotify = oauth2.Endpoint{
	AuthURL:  "https://accounts.spotify.com/authorize",
	TokenURL: "https://accounts.spotify.com/api/token",
}

// StackOverflow is the endpoint for Stack Overflow.
var StackOverflow = oauth2.Endpoint{
	AuthURL:  "https://stackoverflow.com/oauth",
	TokenURL: "https://stackoverflow.com/oauth/access_token",
}

// Strava is the endpoint for Strava.
var Strava = oauth2.Endpoint{
	AuthURL:  "https://www.strava.com/oauth/authorize",
	TokenURL: "https://www.strava.com/oauth/token",
}

// Twitch is the endpoint for Twitch.
var Twitch = oauth2.Endpoint{
	AuthURL:  "https://id.twitch.tv/oauth2/authorize",
	TokenURL: "https://id.twitch.tv/oauth2/token",
}

// Uber is the endpoint for Uber.
var Uber = oauth2.Endpoint{
	AuthURL:  "https://login.uber.com/oauth/v2/authorize",
	TokenURL: "https://login.uber.com/oauth/v2/token",
}

// Vk is the endpoint for Vk.
var Vk = oauth2.Endpoint{
	AuthURL:  "https://oauth.vk.com/authorize",
	TokenURL: "https://oauth.vk.com/access_token",
}

// Yahoo is the endpoint for Yahoo.
var Yahoo = oauth2.Endpoint{
	AuthURL:  "https://api.login.yahoo.com/oauth2/request_auth",
	TokenURL: "https://api.login.yahoo.com/oauth2/get_token",
}

// Yandex is the endpoint for Yandex.
var Yandex = oauth2.Endpoint{
	AuthURL:  "https://oauth.yandex.com/authorize",
	TokenURL: "https://oauth.yandex.com/token",
}

// Zoom is the endpoint for Zoom.
var Zoom = oauth2.Endpoint{
	AuthURL:  "https://zoom.us/oauth/authorize",
	TokenURL: "https://zoom.us/oauth/token",
}

// AzureAD returns a new oauth2.Endpoint for the given tenant at Azure Active Directory.
// If tenant is empty, it uses the tenant called `common`.
//
// For more information see:
// https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints
func AzureAD(tenant string) oauth2.Endpoint {
	if tenant == "" {
		tenant = "common"
	}
	return oauth2.Endpoint{
		AuthURL:  "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/authorize",
		TokenURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token",
	}
}

// HipChatServer returns a new oauth2.Endpoint for a HipChat Server instance
// running on the given domain or host.
func HipChatServer(host string) oauth2.Endpoint {
	return oauth2.Endpoint{
		AuthURL:  "https://" + host + "/users/authorize",
		TokenURL: "https://" + host + "/v2/oauth/token",
	}
}

// AWSCognito returns a new oauth2.Endpoint for the supplied AWS Cognito domain which is
// linked to your Cognito User Pool.
//
// Example domain: https://testing.auth.us-east-1.amazoncognito.com
//
// For more information see:
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain.html
// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html
func AWSCognito(domain string) oauth2.Endpoint {
	domain = strings.TrimRight(domain, "/")
	return oauth2.Endpoint{
		AuthURL:  domain + "/oauth2/authorize",
		TokenURL: domain + "/oauth2/token",
	}
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"net/http"
	"time"

	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/services"
	"github.com/ovh/cds/engine/api/sessionstore"
	"github.com/ovh/cds/engine/api/token"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

func (api *API) postServiceRegisterHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		srv := &sdk.Service{}
		if err := UnmarshalBody(r, srv); err != nil {
			return sdk.WrapError(err, "postServiceRegisterHandler")
		}

		// Load token
		t, errL := token.LoadToken(api.mustDB(), srv.Token)
		if errL != nil {
			return sdk.WrapError(sdk.ErrUnauthorized, "postServiceRegisterHandler> Cannot register service: %v", errL)
		}

		//Service must be with a sharedinfra group token
		if t.GroupID != group.SharedInfraGroup.ID {
			return sdk.WrapError(sdk.ErrUnauthorized, "postServiceRegisterHandler> Cannot register service")
		}

		//Insert or update the service
		repo := services.NewRepository(api.mustDB, api.Cache)
		if err := repo.Begin(); err != nil {
			return sdk.WrapError(err, "postServiceRegisterHandler")
		}

		//Try to find the service, and keep; else generate a new one
		oldSrv, errOldSrv := repo.FindByName(srv.Name)
		if oldSrv != nil {
			srv.Hash = oldSrv.Hash
		} else if errOldSrv == sdk.ErrNotFound {
			//Generate a hash
			hash, errsession := sessionstore.NewSessionKey()
			if errsession != nil {
				return sdk.WrapError(errsession, "postServiceRegisterHandler> Unable to create session")
			}
			srv.Hash = string(hash)
		} else {
			return sdk.WrapError(errOldSrv, "postServiceRegisterHandler")
		}

		srv.LastHeartbeat = time.Now()
		srv.Token = ""

		defer repo.Rollback()

		if oldSrv != nil {
			if err := repo.Update(srv); err != nil {
				return sdk.WrapError(err, "postServiceRegisterHandler> Unable to update service %s", srv.Name)
			}
		} else {
			if err := repo.Insert(srv); err != nil {
				return sdk.WrapError(err, "postServiceRegisterHandler> Unable to insert service %s", srv.Name)
			}
		}

		if err := repo.Commit(); err != nil {
			return sdk.WrapError(err, "postServiceRegisterHandler")
		}

		return WriteJSON(w, srv, http.StatusOK)
	}
}

func (api *API) serviceAPIHeartbeat(c context.Context) {
	tick := time.NewTicker(30 * time.Second).C

	repo := services.NewRepository(api.mustDB, api.Cache)

	hash, errsession := sessionstore.NewSessionKey()
	if errsession != nil {
		log.Error("serviceAPIHeartbeat> Unable to create session:%v", errsession)
		return
	}

	// first call
	api.serviceAPIHeartbeatUpdate(c, repo, hash)

	for {
		select {
		case <-c.Done():
			if c.Err() != nil {
				log.Error("Exiting serviceAPIHeartbeat: %v", c.Err())
				return
			}
		case <-tick:
			api.serviceAPIHeartbeatUpdate(c, repo, hash)
		}
	}
}

func (api *API) serviceAPIHeartbeatUpdate(c context.Context, repo *services.Repository, hash sessionstore.SessionKey) {
	if err := repo.Begin(); err != nil {
		log.Error("serviceAPIHeartbeat> error on repo.Begin:%v", err)
		return
	}

	srv := &sdk.Service{
		Name:             event.GetCDSName(),
		MonitoringStatus: api.Status(),
		Hash:             string(hash),
		LastHeartbeat:    time.Now(),
		Type:             services.TypeAPI,
	}

	//Try to find the service, and keep; else generate a new one
	oldSrv, errOldSrv := repo.FindByName(srv.Name)
	if errOldSrv != nil && errOldSrv != sdk.ErrNotFound {
		log.Error("serviceAPIHeartbeat> Unable to find by name:%v", errOldSrv)
		repo.Rollback()
		return
	}

	if oldSrv != nil {
		if err := repo.Update(srv); err != nil {
			log.Error("serviceAPIHeartbeat> Unable to update service %s: %v", srv.Name, err)
			repo.Rollback()
			return
		}
	} else {
		if err := repo.Insert(srv); err != nil {
			log.Error("serviceAPIHeartbeat> Unable to insert service %s: %v", srv.Name, err)
			repo.Rollback()
			return
		}
	}

	if err := repo.Commit(); err != nil {
		log.Error("serviceAPIHeartbeat> error on repo.Commit: %v", err)
		repo.Rollback()
		return
	}
}
<commit_msg>fix (hatchery): register user's hatchery as service (#2583)<commit_after>package api

import (
	"context"
	"net/http"
	"time"

	"github.com/ovh/cds/engine/api/event"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/services"
	"github.com/ovh/cds/engine/api/sessionstore"
	"github.com/ovh/cds/engine/api/token"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

func (api *API) postServiceRegisterHandler() Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		srv := &sdk.Service{}
		if err := UnmarshalBody(r, srv); err != nil {
			return sdk.WrapError(err, "postServiceRegisterHandler")
		}

		// Load token
		t, errL := token.LoadToken(api.mustDB(), srv.Token)
		if errL != nil {
			return sdk.WrapError(sdk.ErrUnauthorized, "postServiceRegisterHandler> Cannot register service: %v", errL)
		}

		//Service must be with a sharedinfra group token
		// except for hatchery: users can start hatchery with their group
		if t.GroupID != group.SharedInfraGroup.ID && srv.Type != services.TypeHatchery {
			return sdk.WrapError(sdk.ErrUnauthorized, "postServiceRegisterHandler> Cannot register service for group %d with service %s", t.GroupID, srv.Type)
		}

		//Insert or update the service
		repo := services.NewRepository(api.mustDB, api.Cache)
		if err := repo.Begin(); err != nil {
			return sdk.WrapError(err, "postServiceRegisterHandler")
		}

		//Try to find the service, and keep; else generate a new one
		oldSrv, errOldSrv := repo.FindByName(srv.Name)
		if oldSrv != nil {
			srv.Hash = oldSrv.Hash
		} else if errOldSrv == sdk.ErrNotFound {
			//Generate a hash
			hash, errsession := sessionstore.NewSessionKey()
			if errsession != nil {
				return sdk.WrapError(errsession, "postServiceRegisterHandler> Unable to create session")
			}
			srv.Hash = string(hash)
		} else {
			return sdk.WrapError(errOldSrv, "postServiceRegisterHandler")
		}

		srv.LastHeartbeat = time.Now()
		srv.Token = ""

		defer repo.Rollback()

		if oldSrv != nil {
			if err := repo.Update(srv); err != nil {
				return sdk.WrapError(err, "postServiceRegisterHandler> Unable to update service %s", srv.Name)
			}
		} else {
			if err := repo.Insert(srv); err != nil {
				return sdk.WrapError(err, "postServiceRegisterHandler> Unable to insert service %s", srv.Name)
			}
		}

		if err := repo.Commit(); err != nil {
			return sdk.WrapError(err, "postServiceRegisterHandler")
		}

		return WriteJSON(w, srv, http.StatusOK)
	}
}

func (api *API) serviceAPIHeartbeat(c context.Context) {
	tick := time.NewTicker(30 * time.Second).C

	repo := services.NewRepository(api.mustDB, api.Cache)

	hash, errsession := sessionstore.NewSessionKey()
	if errsession != nil {
		log.Error("serviceAPIHeartbeat> Unable to create session:%v", errsession)
		return
	}

	// first call
	api.serviceAPIHeartbeatUpdate(c, repo, hash)

	for {
		select {
		case <-c.Done():
			if c.Err() != nil {
				log.Error("Exiting serviceAPIHeartbeat: %v", c.Err())
				return
			}
		case <-tick:
			api.serviceAPIHeartbeatUpdate(c, repo, hash)
		}
	}
}

func (api *API) serviceAPIHeartbeatUpdate(c context.Context, repo *services.Repository, hash sessionstore.SessionKey) {
	if err := repo.Begin(); err != nil {
		log.Error("serviceAPIHeartbeat> error on repo.Begin:%v", err)
		return
	}

	srv := &sdk.Service{
		Name:             event.GetCDSName(),
		MonitoringStatus: api.Status(),
		Hash:             string(hash),
		LastHeartbeat:    time.Now(),
		Type:             services.TypeAPI,
	}

	//Try to find the service, and keep; else generate a new one
	oldSrv, errOldSrv := repo.FindByName(srv.Name)
	if errOldSrv != nil && errOldSrv != sdk.ErrNotFound {
		log.Error("serviceAPIHeartbeat> Unable to find by name:%v", errOldSrv)
		repo.Rollback()
		return
	}

	if oldSrv != nil {
		if err := repo.Update(srv); err != nil {
			log.Error("serviceAPIHeartbeat> Unable to update service %s: %v", srv.Name, err)
			repo.Rollback()
			return
		}
	} else {
		if err := repo.Insert(srv); err != nil {
			log.Error("serviceAPIHeartbeat> Unable to insert service %s: %v", srv.Name, err)
			repo.Rollback()
			return
		}
	}

	if err := repo.Commit(); err != nil {
		log.Error("serviceAPIHeartbeat> error on repo.Commit: %v", err)
		repo.Rollback()
		return
	}
}
<|endoftext|>


<commit_before>// Package rp provides an implementation of the txsub.ResultProvider interface
// backed using the SQL databases used by both stellar core and horizon
package rp

import (
	"bytes"
	"encoding/base64"
	"github.com/jmoiron/sqlx"
	"github.com/stellar/go-stellar-base/xdr"
	"github.com/stellar/horizon/db"
	cqs "github.com/stellar/horizon/db/queries/core"
	hqs "github.com/stellar/horizon/db/queries/history"
	"github.com/stellar/horizon/db/records/core"
	"github.com/stellar/horizon/db/records/history"
	"github.com/stellar/horizon/txsub"
	"golang.org/x/net/context"
)

// ResultProvider provides transactio submission results by querying the
// connected horizon and stellar core databases.
type ResultProvider struct {
	Core    *sqlx.DB
	History *sqlx.DB
}

// ResultByHash implements txsub.ResultProvider
func (rp *ResultProvider) ResultByHash(ctx context.Context, hash string) txsub.Result {

	// query history database
	var hr history.Transaction
	hq := &hqs.TransactionByHash{
		SqlQuery: db.SqlQuery{DB: rp.History},
		Hash:     hash,
	}

	err := db.Get(ctx, hq, &hr)
	if err == nil {
		return txResultFromHistory(hr)
	}

	if err != db.ErrNoResults {
		return txsub.Result{Err: err}
	}

	// query core database
	var cr core.Transaction
	cq := &cqs.TransactionByHash{
		SqlQuery: db.SqlQuery{DB: rp.Core},
		Hash:     hash,
	}

	err = db.Get(ctx, cq, &cr)
	if err == nil {
		return txResultFromCore(cr)
	}

	if err != db.ErrNoResults {
		return txsub.Result{Err: err}
	}

	// if no result was found in either db, return ErrNoResults
	return txsub.Result{Err: txsub.ErrNoResults}
}

func txResultFromHistory(tx history.Transaction) txsub.Result {
	return txsub.Result{
		Hash:           tx.TransactionHash,
		LedgerSequence: tx.LedgerSequence,
		EnvelopeXDR:    tx.TxEnvelope,
		ResultXDR:      tx.TxResult,
		ResultMetaXDR:  tx.TxMeta,
	}
}

func txResultFromCore(tx core.Transaction) txsub.Result {
	//decode the result xdr, extract TransactionResult
	var trp xdr.TransactionResultPair
	err := xdr.SafeUnmarshalBase64(tx.ResultXDR, &trp)

	if err != nil {
		return txsub.Result{Err: err}
	}

	tr := trp.Result

	// re-encode result to base64
	var raw bytes.Buffer
	_, err = xdr.Marshal(&raw, tr)

	if err != nil {
		return txsub.Result{Err: err}
	}

	trx := base64.StdEncoding.EncodeToString(raw.Bytes())

	// if result is success, send a normal resposne
	if tr.Result.Code == xdr.TransactionResultCodeTxSuccess {
		return txsub.Result{
			Hash:           tx.TransactionHash,
			LedgerSequence: tx.LedgerSequence,
			EnvelopeXDR:    tx.EnvelopeXDR,
			ResultXDR:      trx,
			ResultMetaXDR:  tx.ResultMetaXDR,
		}
	}

	// if failed, produce a FailedTransactionError
	return txsub.Result{
		Err: &txsub.FailedTransactionError{
			ResultXDR: trx,
		},
		Hash:           tx.TransactionHash,
		LedgerSequence: tx.LedgerSequence,
		EnvelopeXDR:    tx.EnvelopeXDR,
		ResultXDR:      trx,
		ResultMetaXDR:  tx.ResultMetaXDR,
	}
}
<commit_msg>Fix result provider<commit_after>// Package rp provides an implementation of the txsub.ResultProvider interface
// backed using the SQL databases used by both stellar core and horizon
package rp

import (
	"bytes"
	"encoding/base64"
	"github.com/jmoiron/sqlx"
	"github.com/stellar/go-stellar-base/xdr"
	"github.com/stellar/horizon/db"
	cqs "github.com/stellar/horizon/db/queries/core"
	hqs "github.com/stellar/horizon/db/queries/history"
	"github.com/stellar/horizon/db/records/core"
	"github.com/stellar/horizon/db/records/history"
	"github.com/stellar/horizon/txsub"
	"golang.org/x/net/context"
)

// ResultProvider provides transactio submission results by querying the
// connected horizon and stellar core databases.
type ResultProvider struct {
	Core    *sqlx.DB
	History *sqlx.DB
}

// ResultByHash implements txsub.ResultProvider
func (rp *ResultProvider) ResultByHash(ctx context.Context, hash string) txsub.Result {

	// query history database
	var hr history.Transaction
	hq := &hqs.TransactionByHash{
		SqlQuery: db.SqlQuery{DB: rp.History},
		Hash:     hash,
	}

	err := db.Get(ctx, hq, &hr)
	if err == nil {
		return txResultFromHistory(hr)
	}

	if err != db.ErrNoResults {
		return txsub.Result{Err: err}
	}

	// query core database
	var cr core.Transaction
	cq := &cqs.TransactionByHash{
		DB:   db.SqlQuery{DB: rp.Core},
		Hash: hash,
	}

	err = db.Get(ctx, cq, &cr)
	if err == nil {
		return txResultFromCore(cr)
	}

	if err != db.ErrNoResults {
		return txsub.Result{Err: err}
	}

	// if no result was found in either db, return ErrNoResults
	return txsub.Result{Err: txsub.ErrNoResults}
}

func txResultFromHistory(tx history.Transaction) txsub.Result {
	return txsub.Result{
		Hash:           tx.TransactionHash,
		LedgerSequence: tx.LedgerSequence,
		EnvelopeXDR:    tx.TxEnvelope,
		ResultXDR:      tx.TxResult,
		ResultMetaXDR:  tx.TxMeta,
	}
}

func txResultFromCore(tx core.Transaction) txsub.Result {
	//decode the result xdr, extract TransactionResult
	var trp xdr.TransactionResultPair
	err := xdr.SafeUnmarshalBase64(tx.ResultXDR, &trp)

	if err != nil {
		return txsub.Result{Err: err}
	}

	tr := trp.Result

	// re-encode result to base64
	var raw bytes.Buffer
	_, err = xdr.Marshal(&raw, tr)

	if err != nil {
		return txsub.Result{Err: err}
	}

	trx := base64.StdEncoding.EncodeToString(raw.Bytes())

	// if result is success, send a normal resposne
	if tr.Result.Code == xdr.TransactionResultCodeTxSuccess {
		return txsub.Result{
			Hash:           tx.TransactionHash,
			LedgerSequence: tx.LedgerSequence,
			EnvelopeXDR:    tx.EnvelopeXDR,
			ResultXDR:      trx,
			ResultMetaXDR:  tx.ResultMetaXDR,
		}
	}

	// if failed, produce a FailedTransactionError
	return txsub.Result{
		Err: &txsub.FailedTransactionError{
			ResultXDR: trx,
		},
		Hash:           tx.TransactionHash,
		LedgerSequence: tx.LedgerSequence,
		EnvelopeXDR:    tx.EnvelopeXDR,
		ResultXDR:      trx,
		ResultMetaXDR:  tx.ResultMetaXDR,
	}
}
<|endoftext|>


<commit_before>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
import "C"
import (
	"unsafe"
)

// MediaTrackType represents the type of a media track.
type MediaTrackType int

// Media track types.
const (
	MediaTrackUnknown MediaTrackType = iota - 1
	MediaTrackAudio
	MediaTrackVideo
	MediaTrackText
)

// MediaAudioTrack contains information specific to audio media tracks.
type MediaAudioTrack struct {
	Channels uint // number of audio channels.
	Rate     uint // audio sample rate.
}

// MediaVideoTrack contains information specific to video media tracks.
type MediaVideoTrack struct {
	Width  uint // video width.
	Height uint // video height.

	// Aspect ratio information.
	AspectRatioNum uint // aspect ratio numerator.
	AspectRatioDen uint // aspect ratio denominator.

	// Frame rate information.
	FrameRateNum uint // frame rate numerator.
	FrameRateDen uint // frame rate denominator.
}

// MediaSubtitleTrack contains information specific to subtitle media tracks.
type MediaSubtitleTrack struct {
	Encoding string // character encoding of the subtitle.
}

// MediaTrack contains information regarding a media track.
type MediaTrack struct {
	ID      int            // Media track identifier.
	Type    MediaTrackType // Media track type.
	BitRate uint           // Media track bit rate.

	// libVLC representation of the four-character code of the codec used by
	// the media track.
	Codec uint

	// The original four-character code of the codec used by the media track,
	// extracted from the container.
	OriginalCodec uint

	// Codec profile (real audio flavor, MPEG audio layer, H264 profile, etc.).
	// NOTE: Codec specific.
	Profile int

	// Stream restriction level (resolution, bitrate, codec features, etc.).
	// NOTE: Codec specific.
	Level int

	Language    string // Media track language name.
	Description string // Description of the media track.

	// Type specific information.
	Audio    *MediaAudioTrack
	Video    *MediaVideoTrack
	Subtitle *MediaSubtitleTrack
}

func (mt *MediaTrack) assertInit() error {
	if mt == nil {
		return ErrMediaTrackNotInitialized
	}

	return nil
}

func parseMediaTrack(cTrack *C.libvlc_media_track_t) (*MediaTrack, error) {
	if cTrack == nil {
		return nil, ErrMediaTrackNotInitialized
	}

	mt := &MediaTrack{
		ID:            int(cTrack.i_id),
		Type:          MediaTrackType(cTrack.i_type),
		BitRate:       uint(cTrack.i_bitrate),
		Codec:         uint(cTrack.i_codec),
		OriginalCodec: uint(cTrack.i_original_fourcc),
		Profile:       int(cTrack.i_profile),
		Level:         int(cTrack.i_level),
		Language:      C.GoString(cTrack.psz_language),
		Description:   C.GoString(cTrack.psz_description),
	}

	switch mt.Type {
	case MediaTrackAudio:
		audio := *(**C.libvlc_audio_track_t)(unsafe.Pointer(&cTrack.anon0[0]))
		if audio == nil {
			break
		}

		mt.Audio = &MediaAudioTrack{
			Channels: uint(audio.i_channels),
			Rate:     uint(audio.i_rate),
		}
	case MediaTrackVideo:
		video := *(**C.libvlc_video_track_t)(unsafe.Pointer(&cTrack.anon0[0]))
		if video == nil {
			break
		}

		mt.Video = &MediaVideoTrack{
			Width:          uint(video.i_width),
			Height:         uint(video.i_height),
			AspectRatioNum: uint(video.i_sar_num),
			AspectRatioDen: uint(video.i_sar_den),
			FrameRateNum:   uint(video.i_frame_rate_num),
			FrameRateDen:   uint(video.i_frame_rate_den),
		}
	case MediaTrackText:
		subtitle := *(**C.libvlc_subtitle_track_t)(unsafe.Pointer(&cTrack.anon0[0]))
		if subtitle == nil {
			break
		}

		mt.Subtitle = &MediaSubtitleTrack{
			Encoding: C.GoString(subtitle.psz_encoding),
		}
	}

	return mt, nil
}
<commit_msg>Add MediaTrackDescriptor struct in v2<commit_after>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
import "C"
import (
	"unsafe"
)

// MediaTrackType represents the type of a media track.
type MediaTrackType int

// Media track types.
const (
	MediaTrackUnknown MediaTrackType = iota - 1
	MediaTrackAudio
	MediaTrackVideo
	MediaTrackText
)

// MediaTrackDescriptor contains information about a media track.
type MediaTrackDescriptor struct {
	ID          int    // Media track identifier.
	Description string // Description of the media track.
}

// MediaAudioTrack contains information specific to audio media tracks.
type MediaAudioTrack struct {
	Channels uint // number of audio channels.
	Rate     uint // audio sample rate.
}

// MediaVideoTrack contains information specific to video media tracks.
type MediaVideoTrack struct {
	Width  uint // video width.
	Height uint // video height.

	// Aspect ratio information.
	AspectRatioNum uint // aspect ratio numerator.
	AspectRatioDen uint // aspect ratio denominator.

	// Frame rate information.
	FrameRateNum uint // frame rate numerator.
	FrameRateDen uint // frame rate denominator.
}

// MediaSubtitleTrack contains information specific to subtitle media tracks.
type MediaSubtitleTrack struct {
	Encoding string // character encoding of the subtitle.
}

// MediaTrack contains information regarding a media track.
type MediaTrack struct {
	ID      int            // Media track identifier.
	Type    MediaTrackType // Media track type.
	BitRate uint           // Media track bit rate.

	// libVLC representation of the four-character code of the codec used by
	// the media track.
	Codec uint

	// The original four-character code of the codec used by the media track,
	// extracted from the container.
	OriginalCodec uint

	// Codec profile (real audio flavor, MPEG audio layer, H264 profile, etc.).
	// NOTE: Codec specific.
	Profile int

	// Stream restriction level (resolution, bitrate, codec features, etc.).
	// NOTE: Codec specific.
	Level int

	Language    string // Media track language name.
	Description string // Description of the media track.

	// Type specific information.
	Audio    *MediaAudioTrack
	Video    *MediaVideoTrack
	Subtitle *MediaSubtitleTrack
}

func (mt *MediaTrack) assertInit() error {
	if mt == nil {
		return ErrMediaTrackNotInitialized
	}

	return nil
}

func parseMediaTrack(cTrack *C.libvlc_media_track_t) (*MediaTrack, error) {
	if cTrack == nil {
		return nil, ErrMediaTrackNotInitialized
	}

	mt := &MediaTrack{
		ID:            int(cTrack.i_id),
		Type:          MediaTrackType(cTrack.i_type),
		BitRate:       uint(cTrack.i_bitrate),
		Codec:         uint(cTrack.i_codec),
		OriginalCodec: uint(cTrack.i_original_fourcc),
		Profile:       int(cTrack.i_profile),
		Level:         int(cTrack.i_level),
		Language:      C.GoString(cTrack.psz_language),
		Description:   C.GoString(cTrack.psz_description),
	}

	switch mt.Type {
	case MediaTrackAudio:
		audio := *(**C.libvlc_audio_track_t)(unsafe.Pointer(&cTrack.anon0[0]))
		if audio == nil {
			break
		}

		mt.Audio = &MediaAudioTrack{
			Channels: uint(audio.i_channels),
			Rate:     uint(audio.i_rate),
		}
	case MediaTrackVideo:
		video := *(**C.libvlc_video_track_t)(unsafe.Pointer(&cTrack.anon0[0]))
		if video == nil {
			break
		}

		mt.Video = &MediaVideoTrack{
			Width:          uint(video.i_width),
			Height:         uint(video.i_height),
			AspectRatioNum: uint(video.i_sar_num),
			AspectRatioDen: uint(video.i_sar_den),
			FrameRateNum:   uint(video.i_frame_rate_num),
			FrameRateDen:   uint(video.i_frame_rate_den),
		}
	case MediaTrackText:
		subtitle := *(**C.libvlc_subtitle_track_t)(unsafe.Pointer(&cTrack.anon0[0]))
		if subtitle == nil {
			break
		}

		mt.Subtitle = &MediaSubtitleTrack{
			Encoding: C.GoString(subtitle.psz_encoding),
		}
	}

	return mt, nil
}

func parseMediaTrackDescriptorList(cDescriptors *C.libvlc_track_description_t) ([]*MediaTrackDescriptor, error) {
	if cDescriptors == nil {
		return nil, nil
	}

	var descriptors []*MediaTrackDescriptor
	for n := cDescriptors; n != nil; n = (*C.libvlc_track_description_t)(n.p_next) {
		descriptors = append(descriptors, &MediaTrackDescriptor{
			ID:          int(n.i_id),
			Description: C.GoString(n.psz_name),
		})
	}

	C.libvlc_track_description_list_release(cDescriptors)
	return descriptors, getError()
}
<|endoftext|>


<commit_before>package rest

import (
	"encoding/json"
	"fmt"
	"path"

	"github.com/bitfinexcom/bitfinex-api-go/v2"
)

// OrderService manages data flow for the Order API endpoint
type OrderService struct {
	requestFactory
	Synchronous
}

type CancelOrderMultiArgs struct {
	ID  []int           `json:"id,omitempty"`
	GID []int           `json:"gid,omitempty"`
	CID [][]interface{} `json:"cid,omitempty"`
	All int             `json:"all,omitempty"`
}

type OrderMultiArgs struct {
	Ops [][]interface{} `json:"ops"`
}

// Retrieves all of the active orders
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) All() (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders("")
}

// Retrieves all of the active orders with for the given symbol
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetBySymbol(symbol string) (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders(symbol)
}

// Retrieve an active order by the given ID
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetByOrderId(orderID int64) (o *bitfinex.Order, err error) {
	os, err := s.All()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves all past orders
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) AllHistory() (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders("")
}

// Retrieves all past orders with the given symbol
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryBySymbol(symbol string) (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders(symbol)
}

// Retrieve a single order in history with the given id
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryByOrderId(orderID int64) (o *bitfinex.Order, err error) {
	os, err := s.AllHistory()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves the trades generated by an order
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) OrderTrades(symbol string, orderID int64) (*bitfinex.TradeExecutionUpdateSnapshot, error) {
	key := fmt.Sprintf("%s:%d", symbol, orderID)
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("order", key, "trades"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewTradeExecutionUpdateSnapshotFromRaw(raw)
}

func (s *OrderService) getActiveOrders(symbol string) (*bitfinex.OrderSnapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := bitfinex.NewOrderSnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &bitfinex.OrderSnapshot{}, nil
	}
	return os, nil
}

func (s *OrderService) getHistoricalOrders(symbol string) (*bitfinex.OrderSnapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol, "hist"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := bitfinex.NewOrderSnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &bitfinex.OrderSnapshot{}, nil
	}
	return os, nil
}

// Submit a request to create a new order
// see https://docs.bitfinex.com/reference#submit-order for more info
func (s *OrderService) SubmitOrder(order *bitfinex.OrderNewRequest) (*bitfinex.Notification, error) {
	bytes, err := order.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "submit"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to update an order with the given id with the given changes
// see https://docs.bitfinex.com/reference#order-update for more info
func (s *OrderService) SubmitUpdateOrder(order *bitfinex.OrderUpdateRequest) (*bitfinex.Notification, error) {
	bytes, err := order.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "update"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to cancel an order with the given Id
// see https://docs.bitfinex.com/reference#cancel-order for more info
func (s *OrderService) SubmitCancelOrder(oc *bitfinex.OrderCancelRequest) error {
	bytes, err := oc.ToJSON()
	if err != nil {
		return err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "cancel"), bytes)
	if err != nil {
		return err
	}
	_, err = s.Request(req)
	if err != nil {
		return err
	}
	return nil
}

// CancelOrderMulti cancels multiple orders simultaneously. Orders can be canceled based on the Order ID,
// the combination of Client Order ID and Client Order Date, or the Group Order ID. Alternatively, the body
// param 'all' can be used with a value of 1 to cancel all orders.
// see https://docs.bitfinex.com/reference#rest-auth-order-cancel-multi for more info
func (s *OrderService) CancelOrderMulti(args CancelOrderMultiArgs) ([]interface{}, error) {
	bytes, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "cancel", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	return s.Request(req)
}

// CancelOrders cancels multiple orders simultaneously. Accepts a slice of order ID's to be canceled.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) CancelOrders(ids []int) ([]interface{}, error) {
	pld := OrderMultiArgs{
		Ops: [][]interface{}{
			{
				"oc_multi",
				map[string][]int{"id": ids},
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	return s.Request(req)
}
<commit_msg>changing arguments signature for easier understanding of api<commit_after>package rest

import (
	"encoding/json"
	"fmt"
	"path"

	"github.com/bitfinexcom/bitfinex-api-go/v2"
)

// OrderService manages data flow for the Order API endpoint
type OrderService struct {
	requestFactory
	Synchronous
}

type OrderIDs []int
type GroupOrderIDs []int
type ClientOrderIDs [][]interface{}

type CancelOrderMultiArgs struct {
	OrderIDs       OrderIDs       `json:"id,omitempty"`
	GroupOrderIDs  GroupOrderIDs  `json:"gid,omitempty"`
	ClientOrderIDs ClientOrderIDs `json:"cid,omitempty"`
	All            int            `json:"all,omitempty"`
}

type OrderMultiArgs struct {
	Ops [][]interface{} `json:"ops"`
}

// Retrieves all of the active orders
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) All() (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders("")
}

// Retrieves all of the active orders with for the given symbol
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetBySymbol(symbol string) (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getActiveOrders(symbol)
}

// Retrieve an active order by the given ID
// See https://docs.bitfinex.com/reference#rest-auth-orders for more info
func (s *OrderService) GetByOrderId(orderID int64) (o *bitfinex.Order, err error) {
	os, err := s.All()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves all past orders
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) AllHistory() (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders("")
}

// Retrieves all past orders with the given symbol
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryBySymbol(symbol string) (*bitfinex.OrderSnapshot, error) {
	// use no symbol, this will get all orders
	return s.getHistoricalOrders(symbol)
}

// Retrieve a single order in history with the given id
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) GetHistoryByOrderId(orderID int64) (o *bitfinex.Order, err error) {
	os, err := s.AllHistory()
	if err != nil {
		return nil, err
	}
	for _, order := range os.Snapshot {
		if order.ID == orderID {
			return order, nil
		}
	}
	return nil, bitfinex.ErrNotFound
}

// Retrieves the trades generated by an order
// See https://docs.bitfinex.com/reference#orders-history for more info
func (s *OrderService) OrderTrades(symbol string, orderID int64) (*bitfinex.TradeExecutionUpdateSnapshot, error) {
	key := fmt.Sprintf("%s:%d", symbol, orderID)
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("order", key, "trades"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewTradeExecutionUpdateSnapshotFromRaw(raw)
}

func (s *OrderService) getActiveOrders(symbol string) (*bitfinex.OrderSnapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := bitfinex.NewOrderSnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &bitfinex.OrderSnapshot{}, nil
	}
	return os, nil
}

func (s *OrderService) getHistoricalOrders(symbol string) (*bitfinex.OrderSnapshot, error) {
	req, err := s.requestFactory.NewAuthenticatedRequest(bitfinex.PermissionRead, path.Join("orders", symbol, "hist"))
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	os, err := bitfinex.NewOrderSnapshotFromRaw(raw)
	if err != nil {
		return nil, err
	}
	if os == nil {
		return &bitfinex.OrderSnapshot{}, nil
	}
	return os, nil
}

// Submit a request to create a new order
// see https://docs.bitfinex.com/reference#submit-order for more info
func (s *OrderService) SubmitOrder(order *bitfinex.OrderNewRequest) (*bitfinex.Notification, error) {
	bytes, err := order.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "submit"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to update an order with the given id with the given changes
// see https://docs.bitfinex.com/reference#order-update for more info
func (s *OrderService) SubmitUpdateOrder(order *bitfinex.OrderUpdateRequest) (*bitfinex.Notification, error) {
	bytes, err := order.ToJSON()
	if err != nil {
		return nil, err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "update"), bytes)
	if err != nil {
		return nil, err
	}
	raw, err := s.Request(req)
	if err != nil {
		return nil, err
	}
	return bitfinex.NewNotificationFromRaw(raw)
}

// Submit a request to cancel an order with the given Id
// see https://docs.bitfinex.com/reference#cancel-order for more info
func (s *OrderService) SubmitCancelOrder(oc *bitfinex.OrderCancelRequest) error {
	bytes, err := oc.ToJSON()
	if err != nil {
		return err
	}
	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(bitfinex.PermissionWrite, path.Join("order", "cancel"), bytes)
	if err != nil {
		return err
	}
	_, err = s.Request(req)
	if err != nil {
		return err
	}
	return nil
}

// CancelOrderMulti cancels multiple orders simultaneously. Orders can be canceled based on the Order ID,
// the combination of Client Order ID and Client Order Date, or the Group Order ID. Alternatively, the body
// param 'all' can be used with a value of 1 to cancel all orders.
// see https://docs.bitfinex.com/reference#rest-auth-order-cancel-multi for more info
func (s *OrderService) CancelOrderMulti(args CancelOrderMultiArgs) ([]interface{}, error) {
	bytes, err := json.Marshal(args)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "cancel", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	return s.Request(req)
}

// CancelOrders cancels multiple orders simultaneously. Accepts a slice of order ID's to be canceled.
// see https://docs.bitfinex.com/reference#rest-auth-order-multi for more info
func (s *OrderService) CancelOrders(ids OrderIDs) ([]interface{}, error) {
	pld := OrderMultiArgs{
		Ops: [][]interface{}{
			{
				"oc_multi",
				map[string][]int{"id": ids},
			},
		},
	}

	bytes, err := json.Marshal(pld)
	if err != nil {
		return nil, err
	}

	req, err := s.requestFactory.NewAuthenticatedRequestWithBytes(
		bitfinex.PermissionWrite,
		path.Join("order", "multi"),
		bytes,
	)
	if err != nil {
		return nil, err
	}

	return s.Request(req)
}
<|endoftext|>


<commit_before>package vals

import "testing"

var hcase = hash{
	"a": "v1",
	"b": "v2",
	"c": "v3",
}

func TestHashType(t *testing.T) {
	if got := hcase.Type(); got != "hash" {
		t.Errorf("expected %q, got %q", "hash", got)
	}
}

func TestHashHDel(t *testing.T) {
	// copy the hcase
	hdelCase := make(hash)
	for k, v := range hcase {
		hdelCase[k] = v
	}
	cases := []struct {
		h      Hash
		fields []string
		exp    int64
	}{
		0: {hdelCase, []string{"z", "x", "y"}, 0},
		1: {hdelCase, []string{"z", "a", "y"}, 1},
		2: {hdelCase, []string{"a", "a", "a"}, 0},
		3: {hdelCase, []string{"b", "c", "d"}, 2},
	}
	for i, c := range cases {
		got := c.h.HDel(c.fields...)
		if c.exp != got {
			t.Errorf("%d: expected %d, got %d", i, c.exp, got)
		}
	}
}

func TestHashExists(t *testing.T) {
	cases := []struct {
		h     Hash
		field string
		exp   bool
	}{
		0: {hcase, "y", false},
		1: {hcase, "a", true},
		2: {hcase, "b", true},
		3: {hcase, "c", true},
		4: {hcase, "d", false},
	}
	for i, c := range cases {
		got := c.h.HExists(c.field)
		if c.exp != got {
			t.Errorf("%d: expected %t, got %t", i, c.exp, got)
		}
	}
}
<commit_msg>fix errorf<commit_after>package vals

import (
	"reflect"
	"testing"
)

var hcase = hash{
	"a": "v1",
	"b": "v2",
	"c": "v3",
}

var hempty = hash{}

func clone(h hash) hash {
	h2 := hash{}
	for k, v := range h {
		h2[k] = v
	}
	return h2
}

func TestHashType(t *testing.T) {
	if got := hcase.Type(); got != "hash" {
		t.Errorf("expected %q, got %q", "hash", got)
	}
}

func TestHashHDel(t *testing.T) {
	// copy the hcase
	hdelCase := clone(hcase)
	cases := []struct {
		h      Hash
		fields []string
		exp    int64
	}{
		0: {hdelCase, []string{"z", "x", "y"}, 0},
		1: {hdelCase, []string{"z", "a", "y"}, 1},
		2: {hdelCase, []string{"a", "a", "a"}, 0},
		3: {hdelCase, []string{"b", "c", "d"}, 2},
		4: {hempty, []string{"b", "c", "d"}, 0},
	}
	for i, c := range cases {
		got := c.h.HDel(c.fields...)
		if c.exp != got {
			t.Errorf("%d: expected %d, got %d", i, c.exp, got)
		}
	}
}

func TestHashExists(t *testing.T) {
	cases := []struct {
		h     Hash
		field string
		exp   bool
	}{
		0: {hcase, "y", false},
		1: {hcase, "a", true},
		2: {hcase, "b", true},
		3: {hcase, "c", true},
		4: {hcase, "d", false},
		5: {hempty, "a", false},
	}
	for i, c := range cases {
		got := c.h.HExists(c.field)
		if c.exp != got {
			t.Errorf("%d: expected %t, got %t", i, c.exp, got)
		}
	}
}

func TestHashGet(t *testing.T) {
	cases := []struct {
		h     Hash
		field string
		exp   string
		ok    bool
	}{
		0: {hcase, "a", "v1", true},
		1: {hcase, "z", "", false},
		2: {hcase, "b", "v2", true},
		3: {hcase, "c", "v3", true},
		4: {hempty, "a", "", false},
	}
	for i, c := range cases {
		got, ok := c.h.HGet(c.field)
		if got != c.exp {
			t.Errorf("%d: expected %q, got %q", i, c.exp, got)
		}
		if ok != c.ok {
			t.Errorf("%d: expected %t, got %t", i, c.ok, ok)
		}
	}
}

func TestHashGetAll(t *testing.T) {
	cases := []struct {
		h   Hash
		exp []string
	}{
		0: {hcase, []string{"a", "v1", "b", "v2", "c", "v3"}},
		1: {hempty, []string{}},
	}
	for i, c := range cases {
		got := c.h.HGetAll()
		if !reflect.DeepEqual(got, c.exp) {
			t.Errorf("%d: expected %v, got %v", i, c.exp, got)
		}
	}
}

func TestHashHKeys(t *testing.T) {
	cases := []struct {
		h   Hash
		exp []string
	}{
		0: {hcase, []string{"a", "b", "c"}},
		1: {hempty, []string{}},
	}
	for i, c := range cases {
		got := c.h.HKeys()
		if !reflect.DeepEqual(got, c.exp) {
			t.Errorf("%d: expected %v, got %v", i, c.exp, got)
		}
	}
}

func TestHashHLen(t *testing.T) {
	cases := []struct {
		h   Hash
		exp int64
	}{
		0: {hcase, 3},
		1: {hempty, 0},
	}
	for i, c := range cases {
		got := c.h.HLen()
		if got != c.exp {
			t.Errorf("%d: expected %d, got %d", i, c.exp, got)
		}
	}
}

func TestHashHMGet(t *testing.T) {
	cases := []struct {
		h      Hash
		fields []string
		exp    []interface{}
	}{
		0: {hcase, []string{}, []interface{}{}},
		1: {hcase, []string{"a", "a", "a"}, []interface{}{"v1", "v1", "v1"}},
		2: {hcase, []string{"a", "b", "c"}, []interface{}{"v1", "v2", "v3"}},
		3: {hcase, []string{"a", "z", "c", "e"}, []interface{}{"v1", nil, "v3", nil}},
		4: {hempty, []string{"a", "b", "c"}, []interface{}{nil, nil, nil}},
	}
	for i, c := range cases {
		got := c.h.HMGet(c.fields...)
		if !reflect.DeepEqual(got, c.exp) {
			t.Errorf("%d: expected %v, got %v", i, c.exp, got)
		}
	}
}

func TestHashHMSet(t *testing.T) {
	hmset := clone(hcase)
	hmempty := clone(hempty)
	cases := []struct {
		h      Hash
		fields []string
		ln     int64
	}{
		0: {hmset, []string{"a", "v4", "d", "v5"}, 4},
		1: {hmset, []string{"e", "v6"}, 5},
		2: {hmempty, []string{"a", "v1", "b", "v2", "c", "v3", "d", "v4", "a", "v5"}, 4},
	}
	for i, c := range cases {
		c.h.HMSet(c.fields...)
		if ln := c.h.HLen(); ln != c.ln {
			t.Errorf("%d: expected length of %d, got %d", i, c.ln, ln)
		}
	}
}

func TestHashHSet(t *testing.T) {
	hset := clone(hcase)
	hempty := clone(hempty)
	cases := []struct {
		h     Hash
		field string
		val   string
		exp   bool
		ln    int64
	}{
		0: {hset, "a", "v0", false, 3},
		1: {hset, "d", "v4", true, 4},
		2: {hempty, "a", "v1", true, 1},
	}
	for i, c := range cases {
		got := c.h.HSet(c.field, c.val)
		if got != c.exp {
			t.Errorf("%d: expected %t, got %t", i, c.exp, got)
		}
		if ln := c.h.HLen(); ln != c.ln {
			t.Errorf("%d: expected length of %d, got %d", i, c.ln, ln)
		}
		if val, _ := c.h.HGet(c.field); val != c.val {
			t.Errorf("%d: expected field to be %q, got %q", i, c.val, val)
		}
	}
}

func TestHashHSetNx(t *testing.T) {
	hset := clone(hcase)
	hempty := clone(hempty)
	cases := []struct {
		h     Hash
		field string
		val   string
		exp   bool
		ln    int64
	}{
		0: {hset, "a", "v0", false, 3},
		1: {hset, "d", "v4", true, 4},
		2: {hempty, "a", "v1", true, 1},
	}
	for i, c := range cases {
		ori, _ := c.h.HGet(c.field)
		got := c.h.HSetNx(c.field, c.val)
		if got != c.exp {
			t.Errorf("%d: expected %t, got %t", i, c.exp, got)
		}
		if ln := c.h.HLen(); ln != c.ln {
			t.Errorf("%d: expected length of %d, got %d", i, c.ln, ln)
		}
		new, _ := c.h.HGet(c.field)
		if c.exp && c.val != new {
			t.Errorf("%d: expected field to be %q, got %q", i, c.val, new)
		}
		if !c.exp && new != ori {
			t.Errorf("%d: expected field to be %q, got %q", i, ori, new)
		}
	}
}

func TestHashHVals(t *testing.T) {
	cases := []struct {
		h   Hash
		exp []string
	}{
		0: {hcase, []string{"v1", "v2", "v3"}},
		1: {hempty, []string{}},
	}
	for i, c := range cases {
		got := c.h.HVals()
		if !reflect.DeepEqual(got, c.exp) {
			t.Errorf("%d: expected %v, got %v", i, c.exp, got)
		}
	}
}
<|endoftext|>


<commit_before>package events

import (
	"container/list"
	"reflect"
	"sync"
)

type listenerType map[string]*list.List
type eventHandler struct {
	raw  interface{}
	fn   reflect.Value
	args []reflect.Type
	once bool
}

type EventEmitter struct {
	sync.RWMutex
	listeners listenerType
}

func NewEventEmitter() (ee *EventEmitter) {
	ee = new(EventEmitter)
	ee.listeners = make(listenerType)
	return
}

func (ee *EventEmitter) InitEventEmitter() {
	ee.listeners = make(listenerType)
}

func getEventHandler(fn interface{}, once bool) (handler *eventHandler) {
	fnValue := reflect.ValueOf(fn)
	fnType := reflect.TypeOf(fn)
	if fnType.Kind() != reflect.Func {
		return nil
	}

	handler = new(eventHandler)
	handler.once = once
	handler.fn = fnValue
	handler.args = make([]reflect.Type, fnType.NumIn())

	for i := range handler.args {
		handler.args[i] = fnType.In(i)
	}

	return handler
}

func (ee *EventEmitter) addListener(event string, listener interface{}, once bool) {
	el := getEventHandler(listener, once)
	if el == nil {
		return
	}
	ee.Lock()
	defer ee.Unlock()
	ls, found := ee.listeners[event]
	if !found || ls == nil {
		ls = list.New()
		ee.listeners[event] = ls
	}
	ls.PushBack(el)
}

func (ee *EventEmitter) On(event string, listener interface{}) {
	ee.addListener(event, listener, false)
}

func (ee *EventEmitter) Once(event string, listener interface{}) {
	ee.addListener(event, listener, true)
}

func tryCall(el *eventHandler, args []interface{}) {
	if len(args) == len(el.args) {
		callArgs := make([]reflect.Value, len(args))
		for i, arg := range args {
			callArgs[i] = reflect.ValueOf(arg)
			if !reflect.TypeOf(arg).AssignableTo(el.args[i]) {
				return
			}
		}
		go el.fn.Call(callArgs)
	}
}

func (ee *EventEmitter) Emit(event string, args ...interface{}) {
	ee.Lock()
	defer ee.Unlock()
	ls, found := ee.listeners[event]
	if found {
		for l := ls.Front(); l != nil; {
			next := l.Next()
			eh := l.Value.(*eventHandler)
			if eh.once {
				ls.Remove(l)
			}
			tryCall(eh, args)
			l = next
		}
	}
}

func (ee *EventEmitter) RemoveListener(event string, listener interface{}) {
	var e *list.Element
	ee.Lock()
	defer func() {
		recover()
		ee.Unlock()
	}()
	ptr := reflect.ValueOf(listener).Pointer()
	ls, found := ee.listeners[event]
	if found {
		for e = ls.Front(); e != nil; e = e.Next() {
			eh := e.Value.(*eventHandler)
			if eh.fn.Pointer() == ptr {
				ls.Remove(e)
				return
			}
		}
	}
}

func (ee *EventEmitter) RemoveAllListeners(evs ...string) {
	ee.Lock()
	defer ee.Unlock()
	for _, ev := range evs {
		delete(ee.listeners, ev)
	}
}
<commit_msg>No raw is needed in eventHandler<commit_after>package events

import (
	"container/list"
	"reflect"
	"sync"
)

type listenerType map[string]*list.List
type eventHandler struct {
	fn   reflect.Value
	args []reflect.Type
	once bool
}

type EventEmitter struct {
	sync.RWMutex
	listeners listenerType
}

func NewEventEmitter() (ee *EventEmitter) {
	ee = new(EventEmitter)
	ee.listeners = make(listenerType)
	return
}

func (ee *EventEmitter) InitEventEmitter() {
	ee.listeners = make(listenerType)
}

func getEventHandler(fn interface{}, once bool) (handler *eventHandler) {
	fnValue := reflect.ValueOf(fn)
	fnType := reflect.TypeOf(fn)
	if fnType.Kind() != reflect.Func {
		return nil
	}

	handler = new(eventHandler)
	handler.once = once
	handler.fn = fnValue
	handler.args = make([]reflect.Type, fnType.NumIn())

	for i := range handler.args {
		handler.args[i] = fnType.In(i)
	}

	return handler
}

func (ee *EventEmitter) addListener(event string, listener interface{}, once bool) {
	el := getEventHandler(listener, once)
	if el == nil {
		return
	}
	ee.Lock()
	defer ee.Unlock()
	ls, found := ee.listeners[event]
	if !found || ls == nil {
		ls = list.New()
		ee.listeners[event] = ls
	}
	ls.PushBack(el)
}

func (ee *EventEmitter) On(event string, listener interface{}) {
	ee.addListener(event, listener, false)
}

func (ee *EventEmitter) Once(event string, listener interface{}) {
	ee.addListener(event, listener, true)
}

func tryCall(el *eventHandler, args []interface{}) {
	if len(args) == len(el.args) {
		callArgs := make([]reflect.Value, len(args))
		for i, arg := range args {
			callArgs[i] = reflect.ValueOf(arg)
			if !reflect.TypeOf(arg).AssignableTo(el.args[i]) {
				return
			}
		}
		go el.fn.Call(callArgs)
	}
}

func (ee *EventEmitter) Emit(event string, args ...interface{}) {
	ee.Lock()
	defer ee.Unlock()
	ls, found := ee.listeners[event]
	if found {
		for l := ls.Front(); l != nil; {
			next := l.Next()
			eh := l.Value.(*eventHandler)
			if eh.once {
				ls.Remove(l)
			}
			tryCall(eh, args)
			l = next
		}
	}
}

func (ee *EventEmitter) RemoveListener(event string, listener interface{}) {
	var e *list.Element
	ee.Lock()
	defer func() {
		recover()
		ee.Unlock()
	}()
	ptr := reflect.ValueOf(listener).Pointer()
	ls, found := ee.listeners[event]
	if found {
		for e = ls.Front(); e != nil; e = e.Next() {
			eh := e.Value.(*eventHandler)
			if eh.fn.Pointer() == ptr {
				ls.Remove(e)
				return
			}
		}
	}
}

func (ee *EventEmitter) RemoveAllListeners(evs ...string) {
	ee.Lock()
	defer ee.Unlock()
	for _, ev := range evs {
		delete(ee.listeners, ev)
	}
}
<|endoftext|>


<commit_before>package container_test

import (
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/gozk/zookeeper"
	"launchpad.net/juju-core/juju/charm"
	"launchpad.net/juju-core/juju/container"
	"launchpad.net/juju-core/juju/state"
	"launchpad.net/juju-core/juju/testing"
	"net/url"
	"os"
	"path/filepath"
	stdtesting "testing"
)

type suite struct {
	state *state.State
}

var zkServer *zookeeper.Server

var _ = Suite(&suite{})

func Test(t *stdtesting.T) {
	zkServer = testing.StartZkServer()
	defer zkServer.Destroy()
	TestingT(t)
}

func (s *suite) SetUpSuite(c *C) {
	addr, err := zkServer.Addr()
	c.Assert(err, IsNil)
	s.state, err = state.Initialize(&state.Info{
		Addrs: []string{addr},
	})
	c.Assert(err, IsNil)
}

func (s *suite) TestDeploy(c *C) {
	// create a unit to deploy
	dummyCharm := testing.Charms.Dir("dummy")
	u := fmt.Sprintf("local:series/%s-%d", dummyCharm.Meta().Name, dummyCharm.Revision())
	curl := charm.MustParseURL(u)
	bundleURL, err := url.Parse("http://bundle.url")
	c.Assert(err, IsNil)
	dummy, err := s.state.AddCharm(dummyCharm, curl, bundleURL, "dummy-sha256")
	c.Assert(err, IsNil)
	service, err := s.state.AddService("dummy", dummy)
	c.Assert(err, IsNil)
	unit, err := service.AddUnit()
	c.Assert(err, IsNil)

	oldInitDir, oldJujuDir := *container.InitDir, *container.JujuDir
	defer func() {
		*container.InitDir, *container.JujuDir = oldInitDir, oldJujuDir
	}()
	*container.InitDir, *container.JujuDir = c.MkDir(), c.MkDir()

	unitName := "juju-agent-dummy-0"
	upstartScript := filepath.Join(*container.InitDir, unitName+".conf")

	unitDir := filepath.Join(*container.JujuDir, "units", "dummy-0")

	cont := container.Simple
	err = cont.Deploy(unit)
	c.Assert(err, ErrorMatches, `(.|\n)+Unknown job(.|\n)+`)

	data, err := ioutil.ReadFile(upstartScript)
	c.Assert(err, IsNil)
	c.Assert(string(data), Matches, `(.|\n)+unit --unit-name(.|\n)+`)

	// We can't check that the unit directory is created, because
	// it is removed when the call to Deploy fails, but
	// we can check that it is removed.

	err = os.MkdirAll(filepath.Join(unitDir, "foo"), 0777)
	c.Assert(err, IsNil)

	err = cont.Destroy(unit)
	c.Assert(err, IsNil)

	_, err = os.Stat(unitDir)
	c.Assert(err, NotNil)

	_, err = os.Stat(upstartScript)
	c.Assert(err, NotNil)
}
<commit_msg>container: fix test<commit_after>package container_test

import (
	"fmt"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/gozk/zookeeper"
	"launchpad.net/juju-core/juju/charm"
	"launchpad.net/juju-core/juju/container"
	"launchpad.net/juju-core/juju/state"
	"launchpad.net/juju-core/juju/testing"
	"net/url"
	"os"
	"path/filepath"
	stdtesting "testing"
)

type suite struct {
	state *state.State
}

var zkServer *zookeeper.Server

var _ = Suite(&suite{})

func Test(t *stdtesting.T) {
	zkServer = testing.StartZkServer()
	defer zkServer.Destroy()
	TestingT(t)
}

func (s *suite) SetUpSuite(c *C) {
	addr, err := zkServer.Addr()
	c.Assert(err, IsNil)
	s.state, err = state.Initialize(&state.Info{
		Addrs: []string{addr},
	})
	c.Assert(err, IsNil)
}

func (s *suite) TestDeploy(c *C) {
	// make sure there's a jujud "executable" in the path.
	binDir := c.MkDir()
	exe := filepath.Join(binDir, "jujud")
	defer os.Setenv("PATH", os.Getenv("PATH"))
	os.Setenv("PATH", binDir)
	err := ioutil.WriteFile(exe, nil, 0777)
	c.Assert(err, IsNil)

	// create a unit to deploy
	dummyCharm := testing.Charms.Dir("dummy")
	u := fmt.Sprintf("local:series/%s-%d", dummyCharm.Meta().Name, dummyCharm.Revision())
	curl := charm.MustParseURL(u)
	bundleURL, err := url.Parse("http://bundle.url")
	c.Assert(err, IsNil)
	dummy, err := s.state.AddCharm(dummyCharm, curl, bundleURL, "dummy-sha256")
	c.Assert(err, IsNil)
	service, err := s.state.AddService("dummy", dummy)
	c.Assert(err, IsNil)
	unit, err := service.AddUnit()
	c.Assert(err, IsNil)

	oldInitDir, oldJujuDir := *container.InitDir, *container.JujuDir
	defer func() {
		*container.InitDir, *container.JujuDir = oldInitDir, oldJujuDir
	}()
	*container.InitDir, *container.JujuDir = c.MkDir(), c.MkDir()

	unitName := "juju-agent-dummy-0"
	upstartScript := filepath.Join(*container.InitDir, unitName+".conf")

	unitDir := filepath.Join(*container.JujuDir, "units", "dummy-0")

	cont := container.Simple
	err = cont.Deploy(unit)
	c.Assert(err, ErrorMatches, `(.|\n)+Unknown job(.|\n)+`)

	data, err := ioutil.ReadFile(upstartScript)
	c.Assert(err, IsNil)
	c.Assert(string(data), Matches, `(.|\n)+`+regexp.QuotaMeta(exe)+` unit --unit-name(.|\n)+`)

	// We can't check that the unit directory is created, because
	// it is removed when the call to Deploy fails, but
	// we can check that it is removed.

	err = os.MkdirAll(filepath.Join(unitDir, "foo"), 0777)
	c.Assert(err, IsNil)

	err = cont.Destroy(unit)
	c.Assert(err, IsNil)

	_, err = os.Stat(unitDir)
	c.Assert(err, NotNil)

	_, err = os.Stat(upstartScript)
	c.Assert(err, NotNil)
}
<|endoftext|>