<commit_before>package analogueclock

import (
	"math"
	"time"

	"github.com/kasworld/go-sdlgui"
	"github.com/kasworld/htmlcolors"
)

type Clock struct {
	*sdlgui.Control
	bg    htmlcolors.RGBA
	fg    htmlcolors.RGBA
	hhand htmlcolors.RGBA
	mhand htmlcolors.RGBA
	shand htmlcolors.RGBA
	t     time.Time
}

func New(x, y, z int, wx, wy int) *Clock {
	c := Clock{
		sdlgui.NewControl(x, y, z, wx, wy),
		htmlcolors.Black.ToRGBA(),
		htmlcolors.White.ToRGBA(),
		htmlcolors.Green.ToRGBA(),
		htmlcolors.Blue.ToRGBA(),
		htmlcolors.Red.ToRGBA(),
		time.Now(),
	}
	c.BorderSize = 2
	c.BorderType = 1
	return &c
}

func (tc *Clock) SetBG(bg htmlcolors.RGBA) {
	if bg == tc.bg {
		return
	}
	tc.ContentsChanged = true
	tc.bg = bg
}

func (tc *Clock) SetTime(t time.Time) {
	if t == tc.t {
		return
	}
	tc.ContentsChanged = true
	tc.t = t
}

func (tc *Clock) drawFace() {
	tc.Rend.SetDrawColor(tc.fg[0], tc.fg[1], tc.fg[2], tc.fg[3])
	c := tc.GetRect().Center()
	rx := tc.GetRect().SizeVector()[0]/2 - 3
	ry := tc.GetRect().SizeVector()[1]/2 - 3
	rate := float64(ry) / float64(rx)
	for x := -rx; x < rx; x++ {
		theta := math.Acos(float64(x) / float64(rx))
		y := int(float64(x) * math.Tan(theta) * rate)
		if x == 0 {
			y = ry
		}
		tc.Rend.DrawLine(c[0]+x, c[1]-y, c[0]+x, c[1]+y)
	}
}

func (tc *Clock) getFacePos(angle float64, l float64) (int, int) {
	angle -= 90
	xc, yc := tc.W/2, tc.H/2
	x := xc + int(math.Cos(angle/180*math.Pi)*float64(xc)*l)
	y := yc + int(math.Sin(angle/180*math.Pi)*float64(yc)*l)
	return x, y
}

func (tc *Clock) drawHand(co htmlcolors.RGBA, angle float64, l float64) {
	// x1, y1 := tc.W/2, tc.H/2
	x1, y1 := tc.getFacePos(angle, -l/5)
	x2, y2 := tc.getFacePos(angle, l)
	tc.Rend.SetDrawColor(co[0], co[1], co[2], co[3])
	tc.Rend.DrawLine(x1, y1, x2, y2)
}

func (tc *Clock) drawDialLine(co htmlcolors.RGBA, angle float64, s, e float64) {
	x1, y1 := tc.getFacePos(angle, s)
	x2, y2 := tc.getFacePos(angle, e)
	tc.Rend.SetDrawColor(co[0], co[1], co[2], co[3])
	tc.Rend.DrawLine(x1, y1, x2, y2)
}

func (tc *Clock) drawDials() {
	for a := 0.0; a < 360; a += 6 {
		tc.drawDialLine(htmlcolors.Black.ToRGBA(), a, 0.95, 1.0)
	}
	for a := 0.0; a < 360; a += 6 * 5 {
		tc.drawDialLine(htmlcolors.Black.ToRGBA(), a, 0.90, 1.0)
	}
}

func (tc *Clock) time2Angle() (float64, float64, float64) {
	ms := float64(tc.t.Nanosecond()/1000000) / 1000
	s := (float64(tc.t.Second()) + ms)
	m := (float64(tc.t.Minute()) + s/60)
	h := (float64(tc.t.Hour()%12) + m/60)

	sa := s / 60 * 360
	ma := m / 60 * 360
	ha := h / 12 * 360
	return ha, ma, sa
}

func (tc *Clock) DrawSurface() {
	if !tc.ContentsChanged {
		return
	}
	tc.ContentsChanged = false

	tc.Rend.SetDrawColor(tc.bg[0], tc.bg[1], tc.bg[2], tc.bg[3])
	tc.Rend.Clear()

	h, m, s := tc.time2Angle()
	tc.drawFace()
	tc.drawDials()
	tc.drawHand(tc.hhand, h, 0.60)
	tc.drawHand(tc.mhand, m, 0.75)
	tc.drawHand(tc.shand, s, 0.90)

	tc.Rend.Present()
	tc.Win.AddUpdateControl(tc)
}
<commit_msg>add setcolor<commit_after>package analogueclock

import (
	"math"
	"time"

	"github.com/kasworld/go-sdlgui"
	"github.com/kasworld/htmlcolors"
)

const (
	CO_bg = iota
	CO_face
	CO_daial
	CO_hhand
	CO_mhand
	CO_shand
	CO_End
)

type Clock struct {
	*sdlgui.Control
	colors [CO_End]htmlcolors.RGBA
	t      time.Time
}

func New(x, y, z int, wx, wy int) *Clock {
	c := Clock{
		sdlgui.NewControl(x, y, z, wx, wy),
		[CO_End]htmlcolors.RGBA{
			htmlcolors.Black.ToRGBA(),
			htmlcolors.Gray.ToRGBA(),
			htmlcolors.White.ToRGBA(),
			htmlcolors.Green.ToRGBA(),
			htmlcolors.Blue.ToRGBA(),
			htmlcolors.Red.ToRGBA(),
		},
		time.Now(),
	}
	c.BorderType = 0
	return &c
}

func (tc *Clock) SetColor(n int, co htmlcolors.RGBA) {
	if co == tc.colors[n] {
		return
	}
	tc.ContentsChanged = true
	tc.colors[n] = co
}

func (tc *Clock) SetTime(t time.Time) {
	if t == tc.t {
		return
	}
	tc.ContentsChanged = true
	tc.t = t
}

func (tc *Clock) drawFace() {
	tc.Rend.SetDrawColor(tc.colors[CO_face][0], tc.colors[CO_face][1], tc.colors[CO_face][2], tc.colors[CO_face][3])
	c := tc.GetRect().Center()
	rx := tc.GetRect().SizeVector()[0] / 2
	ry := tc.GetRect().SizeVector()[1] / 2
	rate := float64(ry) / float64(rx)
	for x := -rx; x < rx; x++ {
		theta := math.Acos(float64(x) / float64(rx))
		y := int(float64(x) * math.Tan(theta) * rate)
		if x == 0 {
			y = ry
		}
		tc.Rend.DrawLine(c[0]+x, c[1]-y, c[0]+x, c[1]+y)
	}
}

func (tc *Clock) getFacePos(angle float64, l float64) (int, int) {
	angle -= 90
	xc, yc := tc.W/2, tc.H/2
	x := xc + int(math.Cos(angle/180*math.Pi)*float64(xc)*l)
	y := yc + int(math.Sin(angle/180*math.Pi)*float64(yc)*l)
	return x, y
}

func (tc *Clock) drawHand(co htmlcolors.RGBA, angle float64, l float64) {
	// x1, y1 := tc.W/2, tc.H/2
	x1, y1 := tc.getFacePos(angle, -l/5)
	x2, y2 := tc.getFacePos(angle, l)
	tc.Rend.SetDrawColor(co[0], co[1], co[2], co[3])
	tc.Rend.DrawLine(x1, y1, x2, y2)
}

func (tc *Clock) drawDialLine(co htmlcolors.RGBA, angle float64, s, e float64) {
	x1, y1 := tc.getFacePos(angle, s)
	x2, y2 := tc.getFacePos(angle, e)
	tc.Rend.SetDrawColor(co[0], co[1], co[2], co[3])
	tc.Rend.DrawLine(x1, y1, x2, y2)
}

func (tc *Clock) drawDials() {
	for a := 0.0; a < 360; a += 6 {
		tc.drawDialLine(tc.colors[CO_daial], a, 0.95, 0.99)
	}
	for a := 0.0; a < 360; a += 6 * 5 {
		tc.drawDialLine(tc.colors[CO_daial], a, 0.90, 0.99)
	}
}

func (tc *Clock) time2Angle() (float64, float64, float64) {
	ms := float64(tc.t.Nanosecond()/1000000) / 1000
	s := (float64(tc.t.Second()) + ms)
	m := (float64(tc.t.Minute()) + s/60)
	h := (float64(tc.t.Hour()%12) + m/60)

	sa := s / 60 * 360
	ma := m / 60 * 360
	ha := h / 12 * 360
	return ha, ma, sa
}

func (tc *Clock) DrawSurface() {
	if !tc.ContentsChanged {
		return
	}
	tc.ContentsChanged = false

	tc.Rend.SetDrawColor(tc.colors[CO_bg][0], tc.colors[CO_bg][1], tc.colors[CO_bg][2], tc.colors[CO_bg][3])
	tc.Rend.Clear()

	h, m, s := tc.time2Angle()
	tc.drawFace()
	tc.drawDials()
	tc.drawHand(tc.colors[CO_hhand], h, 0.60)
	tc.drawHand(tc.colors[CO_mhand], m, 0.75)
	tc.drawHand(tc.colors[CO_shand], s, 0.90)

	tc.Rend.Present()
	tc.Win.AddUpdateControl(tc)
}
<|endoftext|>


<commit_before>package bot

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"unicode/utf8"

	"github.com/ghodss/yaml"
)

/* conf.go - methods and types for reading and storing json configuration */

var protocolConfig, brainConfig json.RawMessage

type externalPlugin struct {
	Name, Path string // List of names and paths for external plugins; relative paths are searched first in installdir, then localdir
}

// botconf specifies 'bot configuration, and is read from $GOPHER_CONFIGDIR/conf/gopherbot.yaml
type botconf struct {
	AdminContact    string           // Contact info for whomever administers the robot
	Email           string           // From: address when the robot wants to send an email
	MailServer      string           // host:port to contact when sending mail, defaults to 127.0.0.1:25
	Protocol        string           // Name of the connector protocol to use, e.g. "slack"
	ProtocolConfig  json.RawMessage  // Protocol-specific configuration, type for unmarshalling arbitrary config
	Brain           string           // Type of Brain to use
	BrainConfig     json.RawMessage  // Brain-specific configuration, type for unmarshalling arbitrary config
	Name            string           // Name of the 'bot, specify here if the protocol doesn't supply it (slack does)
	DefaultChannels []string         // Channels where plugins are active by default, e.g. [ "general", "random" ]
	IgnoreUsers     []string         // Users the 'bot never talks to - like other bots
	JoinChannels    []string         // Channels the 'bot should join when it logs in (not supported by all protocols)
	ExternalPlugins []externalPlugin // List of non-Go plugins to load
	AdminUsers      []string         // List of users who can access administrative commands
	Alias           string           // One-character alias for commands directed at the 'bot, e.g. ';open the pod bay doors'
	LocalPort       string           // Port number for listening on localhost, for CLI plugins
	LogLevel        string           // Initial log level, can be modified by plugins. One of "trace" "debug" "info" "warn" "error"
}

var config botconf

// getConfigFile loads a config file from localPath. Required indicates whether
// to return an error if the file isn't found.
func getConfigFile(filename string, required bool, c interface{}) error {
	var (
		cf  []byte
		err error
	)

	cf, err = ioutil.ReadFile(b.localPath + "/conf/" + filename)
	if err != nil {
		err = fmt.Errorf("Reading custom configuration for \"%s\": %v", filename, err)
		Log(Debug, err)
		if !required {
			return nil
		} else {
			return err
		}
	} else {
		if err := yaml.Unmarshal(cf, c); err != nil {
			err = fmt.Errorf("Unmarshalling custom \"%s\": %v", filename, err)
			Log(Error, err)
			return err // If a badly-formatted config is loaded, we always return an error
		} else {
			return nil
		}
	}
}

func logStrToLevel(l string) LogLevel {
	switch l {
	case "trace":
		return Trace
	case "debug":
		return Debug
	case "info":
		return Info
	case "warn":
		return Warn
	default:
		return Error
	}
}

// loadConfig loads the 'bot's json configuration files. An error on first load
// results in log.fatal, but later Loads just log the error.
func loadConfig() error {
	var loglevel LogLevel
	var newconfig botconf
	pluginsOk := true

	// Load default config from const defaultConfig, then overlay
	// with yaml from <localdir>/conf/gopherbot.yaml
	if err := yaml.Unmarshal([]byte(defaultConfig), &newconfig); err != nil {
		return fmt.Errorf("Unmarshalling robot default newconfig: %v", err)
	}
	if err := getConfigFile("gopherbot.yaml", true, &newconfig); err != nil {
		return fmt.Errorf("Loading newconfiguration file: %v", err)
	}

	loglevel = logStrToLevel(newconfig.LogLevel)
	setLogLevel(loglevel)

	b.lock.Lock()

	if newconfig.AdminContact != "" {
		b.adminContact = newconfig.AdminContact
	}
	if newconfig.Email != "" {
		b.email = newconfig.Email
	}
	if newconfig.MailServer != "" {
		b.mailserver = newconfig.MailServer
	}
	if newconfig.Alias != "" {
		alias, _ := utf8.DecodeRuneInString(newconfig.Alias)
		if !strings.ContainsRune(string(aliases+escape_aliases), alias) {
			return fmt.Errorf("Invalid alias specified, ignoring. Must be one of: %s%s", escape_aliases, aliases)
		} else {
			b.alias = alias
		}
	}
	if newconfig.LocalPort != "" {
		b.port = "127.0.0.1:" + newconfig.LocalPort
		err := os.Setenv("GOPHER_HTTP_POST", "http://"+b.port)
		if err != nil {
			Log(Error, fmt.Errorf("Error exporting GOPHER_HTTP_PORT: %q", err))
		}
	}
	if newconfig.Name != "" {
		b.name = newconfig.Name
	}

	if newconfig.Brain != "" {
		b.brainProvider = newconfig.Brain
	}

	if newconfig.Protocol != "" {
		b.protocol = newconfig.Protocol
	} else {
		return fmt.Errorf("Protocol not specified in gopherbot.json")
	}

	if newconfig.BrainConfig != nil {
		brainConfig = newconfig.BrainConfig
	}
	if newconfig.ProtocolConfig != nil {
		protocolConfig = newconfig.ProtocolConfig
	}

	if newconfig.AdminUsers != nil {
		b.adminUsers = newconfig.AdminUsers
	}
	if newconfig.DefaultChannels != nil {
		b.plugChannels = newconfig.DefaultChannels
	}
	if newconfig.ExternalPlugins != nil {
		for i, ep := range newconfig.ExternalPlugins {
			if len(ep.Name) == 0 || len(ep.Path) == 0 {
				pluginsOk = false
				Log(Error, fmt.Errorf("Reading external plugins, zero-length Name or Path for plugin #%d, not reloading plugins", i))
			}
		}
		if pluginsOk {
			b.externalPlugins = newconfig.ExternalPlugins
		}
	}
	if newconfig.IgnoreUsers != nil {
		b.ignoreUsers = newconfig.IgnoreUsers
	}
	if newconfig.JoinChannels != nil {
		b.joinChannels = newconfig.JoinChannels
	}

	// loadPluginConfig does it's own locking
	b.lock.Unlock()

	botLock.Lock()
	config = newconfig
	botLock.Unlock()

	updateRegexes()
	if pluginsOk {
		loadPluginConfig()
	} else {
		return fmt.Errorf("Error reading external plugin config")
	}

	return nil
}
<commit_msg>Make logStrToLevel a little more robust with a ToLower comparison<commit_after>package bot

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"unicode/utf8"

	"github.com/ghodss/yaml"
)

/* conf.go - methods and types for reading and storing json configuration */

var protocolConfig, brainConfig json.RawMessage

type externalPlugin struct {
	Name, Path string // List of names and paths for external plugins; relative paths are searched first in installdir, then localdir
}

// botconf specifies 'bot configuration, and is read from $GOPHER_CONFIGDIR/conf/gopherbot.yaml
type botconf struct {
	AdminContact    string           // Contact info for whomever administers the robot
	Email           string           // From: address when the robot wants to send an email
	MailServer      string           // host:port to contact when sending mail, defaults to 127.0.0.1:25
	Protocol        string           // Name of the connector protocol to use, e.g. "slack"
	ProtocolConfig  json.RawMessage  // Protocol-specific configuration, type for unmarshalling arbitrary config
	Brain           string           // Type of Brain to use
	BrainConfig     json.RawMessage  // Brain-specific configuration, type for unmarshalling arbitrary config
	Name            string           // Name of the 'bot, specify here if the protocol doesn't supply it (slack does)
	DefaultChannels []string         // Channels where plugins are active by default, e.g. [ "general", "random" ]
	IgnoreUsers     []string         // Users the 'bot never talks to - like other bots
	JoinChannels    []string         // Channels the 'bot should join when it logs in (not supported by all protocols)
	ExternalPlugins []externalPlugin // List of non-Go plugins to load
	AdminUsers      []string         // List of users who can access administrative commands
	Alias           string           // One-character alias for commands directed at the 'bot, e.g. ';open the pod bay doors'
	LocalPort       string           // Port number for listening on localhost, for CLI plugins
	LogLevel        string           // Initial log level, can be modified by plugins. One of "trace" "debug" "info" "warn" "error"
}

var config botconf

// getConfigFile loads a config file from localPath. Required indicates whether
// to return an error if the file isn't found.
func getConfigFile(filename string, required bool, c interface{}) error {
	var (
		cf  []byte
		err error
	)

	cf, err = ioutil.ReadFile(b.localPath + "/conf/" + filename)
	if err != nil {
		err = fmt.Errorf("Reading custom configuration for \"%s\": %v", filename, err)
		Log(Debug, err)
		if !required {
			return nil
		} else {
			return err
		}
	} else {
		if err := yaml.Unmarshal(cf, c); err != nil {
			err = fmt.Errorf("Unmarshalling custom \"%s\": %v", filename, err)
			Log(Error, err)
			return err // If a badly-formatted config is loaded, we always return an error
		} else {
			return nil
		}
	}
}

func logStrToLevel(l string) LogLevel {
	switch strings.ToLower(l) {
	case "trace":
		return Trace
	case "debug":
		return Debug
	case "info":
		return Info
	case "warn":
		return Warn
	default:
		return Error
	}
}

// loadConfig loads the 'bot's json configuration files. An error on first load
// results in log.fatal, but later Loads just log the error.
func loadConfig() error {
	var loglevel LogLevel
	var newconfig botconf
	pluginsOk := true

	// Load default config from const defaultConfig, then overlay
	// with yaml from <localdir>/conf/gopherbot.yaml
	if err := yaml.Unmarshal([]byte(defaultConfig), &newconfig); err != nil {
		return fmt.Errorf("Unmarshalling robot default newconfig: %v", err)
	}
	if err := getConfigFile("gopherbot.yaml", true, &newconfig); err != nil {
		return fmt.Errorf("Loading newconfiguration file: %v", err)
	}

	loglevel = logStrToLevel(newconfig.LogLevel)
	setLogLevel(loglevel)

	b.lock.Lock()

	if newconfig.AdminContact != "" {
		b.adminContact = newconfig.AdminContact
	}
	if newconfig.Email != "" {
		b.email = newconfig.Email
	}
	if newconfig.MailServer != "" {
		b.mailserver = newconfig.MailServer
	}
	if newconfig.Alias != "" {
		alias, _ := utf8.DecodeRuneInString(newconfig.Alias)
		if !strings.ContainsRune(string(aliases+escape_aliases), alias) {
			return fmt.Errorf("Invalid alias specified, ignoring. Must be one of: %s%s", escape_aliases, aliases)
		} else {
			b.alias = alias
		}
	}
	if newconfig.LocalPort != "" {
		b.port = "127.0.0.1:" + newconfig.LocalPort
		err := os.Setenv("GOPHER_HTTP_POST", "http://"+b.port)
		if err != nil {
			Log(Error, fmt.Errorf("Error exporting GOPHER_HTTP_PORT: %q", err))
		}
	}
	if newconfig.Name != "" {
		b.name = newconfig.Name
	}

	if newconfig.Brain != "" {
		b.brainProvider = newconfig.Brain
	}

	if newconfig.Protocol != "" {
		b.protocol = newconfig.Protocol
	} else {
		return fmt.Errorf("Protocol not specified in gopherbot.json")
	}

	if newconfig.BrainConfig != nil {
		brainConfig = newconfig.BrainConfig
	}
	if newconfig.ProtocolConfig != nil {
		protocolConfig = newconfig.ProtocolConfig
	}

	if newconfig.AdminUsers != nil {
		b.adminUsers = newconfig.AdminUsers
	}
	if newconfig.DefaultChannels != nil {
		b.plugChannels = newconfig.DefaultChannels
	}
	if newconfig.ExternalPlugins != nil {
		for i, ep := range newconfig.ExternalPlugins {
			if len(ep.Name) == 0 || len(ep.Path) == 0 {
				pluginsOk = false
				Log(Error, fmt.Errorf("Reading external plugins, zero-length Name or Path for plugin #%d, not reloading plugins", i))
			}
		}
		if pluginsOk {
			b.externalPlugins = newconfig.ExternalPlugins
		}
	}
	if newconfig.IgnoreUsers != nil {
		b.ignoreUsers = newconfig.IgnoreUsers
	}
	if newconfig.JoinChannels != nil {
		b.joinChannels = newconfig.JoinChannels
	}

	// loadPluginConfig does it's own locking
	b.lock.Unlock()

	botLock.Lock()
	config = newconfig
	botLock.Unlock()

	updateRegexes()
	if pluginsOk {
		loadPluginConfig()
	} else {
		return fmt.Errorf("Error reading external plugin config")
	}

	return nil
}
<|endoftext|>


<commit_before>package bearer

import (
	"bytes"
	"net/http"
	"net/url"
	"testing"

	"github.com/takama/router"
)

func TestFromHeader(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// positive case
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer "+token)

	res, err := fromHeader(r)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative case - without token at all
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}

	res, err = fromHeader(r)
	if err == nil {
		t.Errorf("Result of fromHeader is wrong: expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative case - wrong token
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer 123"+token)

	res, err = fromHeader(r)
	if err != nil {
		t.Error(err)
	}

	if res == token {
		t.Error("Token must be wrong, got right")
	}
}

func TestFromBody(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// positive case
	c := new(router.Control)
	data := url.Values{}
	data.Set("access_token", token)
	r, err := http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err := fromBody(r)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, wrong content type
	c = new(router.Control)
	data = url.Values{}
	data.Set("access_token", token)
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/json")
	c.Request = r

	res, err = fromBody(r)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative test, no token at all
	c = new(router.Control)
	data = url.Values{}
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err = fromBody(r)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}

func TestFromQueryString(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// positive case
	c := new(router.Control)
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	param := router.Param{
		Key:   "access_token",
		Value: token,
	}
	c.Set(param)

	res, err := fromQueryString(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, no token at all
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	res, err = fromQueryString(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}

func TestGetBearerToken(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// - Test Set #1. From Header. -
	// positive case
	c := new(router.Control)
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer "+token)
	c.Request = r

	res, err := getBearerToken(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative case - without token at all
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Errorf("Result of fromHeader is wrong: expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative case - wrong token
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer 123"+token)
	c.Request = r

	res, err = getBearerToken(c)
	if err != nil {
		t.Error(err)
	}

	if res == token {
		t.Error("Token must be wrong, got right")
	}

	// - Test Set #2. From Body. -
	// positive case
	c = new(router.Control)
	data := url.Values{}
	data.Set("access_token", token)
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err = getBearerToken(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, wrong content type
	c = new(router.Control)
	data = url.Values{}
	data.Set("access_token", token)
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/json")
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative test, no token at all
	c = new(router.Control)
	data = url.Values{}
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// - Test Set #3. From QueryString. -
	// positive case
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	param := router.Param{
		Key:   "access_token",
		Value: token,
	}
	c.Set(param)

	res, err = getBearerToken(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, no token at all
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}
<commit_msg>Make tests simplier<commit_after>package bearer

import (
	"bytes"
	"net/http"
	"net/url"
	"testing"

	"github.com/takama/router"
)

func TestFromHeader(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// positive case
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer "+token)

	res, err := fromHeader(r)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative case - without token at all
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}

	res, err = fromHeader(r)
	if err == nil {
		t.Errorf("Result of fromHeader is wrong: expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative case - wrong token
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer 123"+token)

	res, err = fromHeader(r)
	if err != nil {
		t.Error(err)
	}

	if res == token {
		t.Error("Token must be wrong, got right")
	}
}

func TestFromBody(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// positive case
	c := new(router.Control)
	data := url.Values{}
	data.Set("access_token", token)
	r, err := http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err := fromBody(r)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, wrong content type
	c = new(router.Control)
	data = url.Values{}
	data.Set("access_token", token)
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/json")
	c.Request = r

	res, err = fromBody(r)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative test, no token at all
	c = new(router.Control)
	data = url.Values{}
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err = fromBody(r)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}

func TestFromQueryString(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// positive case
	c := new(router.Control)
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	param := router.Param{
		Key:   "access_token",
		Value: token,
	}
	c.Set(param)

	res, err := fromQueryString(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, no token at all
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	res, err = fromQueryString(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}

func TestGetBearerTokenFromHeader(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// - Test Set #1. From Header. -
	// positive case
	c := new(router.Control)
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer "+token)
	c.Request = r

	res, err := getBearerToken(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative case - without token at all
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Errorf("Result of fromHeader is wrong: expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative case - wrong token
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Authorization", "Bearer 123"+token)
	c.Request = r

	res, err = getBearerToken(c)
	if err != nil {
		t.Error(err)
	}

	if res == token {
		t.Error("Token must be wrong, got right")
	}
}

func TestGetBearerTokenFromBody(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// - Test Set #2. From Body. -
	// positive case
	c := new(router.Control)
	data := url.Values{}
	data.Set("access_token", token)
	r, err := http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err := getBearerToken(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, wrong content type
	c = new(router.Control)
	data = url.Values{}
	data.Set("access_token", token)
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/json")
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}

	// negative test, no token at all
	c = new(router.Control)
	data = url.Values{}
	r, err = http.NewRequest("POST", "test", bytes.NewBufferString(data.Encode()))
	if err != nil {
		t.Error(err)
	}
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}

func TestGetBearerTokenFromQueryString(t *testing.T) {
	token := "mF_9.B5f-4.1JqM"

	// - Test Set #3. From QueryString. -
	// positive case
	c := new(router.Control)
	r, err := http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	param := router.Param{
		Key:   "access_token",
		Value: token,
	}
	c.Set(param)

	res, err := getBearerToken(c)
	if err != nil {
		t.Error(err)
	}
	if res != token {
		t.Errorf("Token %s is wrong, expected: %s", res, token)
	}

	// negative test, no token at all
	c = new(router.Control)
	r, err = http.NewRequest("GET", "test", nil)
	if err != nil {
		t.Error(err)
	}
	c.Request = r

	res, err = getBearerToken(c)
	if err == nil {
		t.Error("Expected error, got nil")
	}
	if len(res) != 0 {
		t.Errorf("Expected empty token, got %s", res)
	}
}
<|endoftext|>


<commit_before>package volumeapi

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
)

const (
	DefaultDockerRootDirectory    = "/var/lib/docker/volumes"
	defaultContentTypeV1          = "appplication/vnd.docker.plugins.v1+json"
	defaultImplementationManifest = `{"Implements": ["VolumeDriver"]}`

	activatePath    = "/Plugin.Activate"
	createPath      = "/VolumeDriver.Create"
	remotePath      = "/VolumeDriver.Remove"
	hostVirtualPath = "/VolumeDriver.Path"
	mountPath       = "/VolumeDriver.Mount"
	unmountPath     = "/VolumeDriver.Unmount"
)

type VolumeRequest struct {
	Name string
}

type VolumeResponse struct {
	Mountpoint string
	Err        error
}

type VolumeDriver interface {
	Create(VolumeRequest) VolumeResponse
	Remove(VolumeRequest) VolumeResponse
	Path(VolumeRequest) VolumeResponse
	Mount(VolumeRequest) VolumeResponse
	Unmount(VolumeRequest) VolumeResponse
}

type VolumeHandler struct {
	handler VolumeDriver
	mux     *http.ServeMux
}

type actionHandler func(VolumeRequest) VolumeResponse

func NewVolumeHandler(handler VolumeDriver) *VolumeHandler {
	h := &VolumeHandler{handler, http.NewServeMux()}
	h.initMux()
	return h
}

func (h *VolumeHandler) initMux() {
	h.mux.HandleFunc(activatePath, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", defaultContentTypeV1)
		fmt.Fprintln(w, defaultImplementationManifest)
	})

	h.handle(createPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Create(req)
	})

	h.handle(remotePath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Remove(req)
	})

	h.handle(hostVirtualPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Path(req)
	})

	h.handle(mountPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Mount(req)
	})

	h.handle(unmountPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Unmount(req)
	})
}

func (h *VolumeHandler) handle(name string, actionCall actionHandler) {
	h.mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
		req, err := decodeRequest(w, r)
		if err != nil {
			return
		}

		res := actionCall(req)

		encodeResponse(w, res)
	})
}

func (h *VolumeHandler) ListenAndServe(proto, addr, group string) error {
	server := http.Server{
		Addr:    addr,
		Handler: h.mux,
	}

	start := make(chan struct{})

	var l net.Listener
	var err error
	switch proto {
	case "tcp":
		l, err = newTcpSocket(addr, nil, start)
	case "unix":
		l, err = newUnixSocket(addr, group, start)
	}
	if err != nil {
		return err
	}

	close(start)
	return server.Serve(l)
}

func decodeRequest(w http.ResponseWriter, r *http.Request) (req VolumeRequest, err error) {
	if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
	return
}

func encodeResponse(w http.ResponseWriter, res VolumeResponse) {
	w.Header().Set("Content-Type", defaultContentTypeV1)
	if res.Err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
	json.NewEncoder(w).Encode(res)
}
<commit_msg>Make the response error a string.<commit_after>package volumeapi

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
)

const (
	DefaultDockerRootDirectory    = "/var/lib/docker/volumes"
	defaultContentTypeV1          = "appplication/vnd.docker.plugins.v1+json"
	defaultImplementationManifest = `{"Implements": ["VolumeDriver"]}`

	activatePath    = "/Plugin.Activate"
	createPath      = "/VolumeDriver.Create"
	remotePath      = "/VolumeDriver.Remove"
	hostVirtualPath = "/VolumeDriver.Path"
	mountPath       = "/VolumeDriver.Mount"
	unmountPath     = "/VolumeDriver.Unmount"
)

type VolumeRequest struct {
	Name string
}

type VolumeResponse struct {
	Mountpoint string
	Err        string
}

type VolumeDriver interface {
	Create(VolumeRequest) VolumeResponse
	Remove(VolumeRequest) VolumeResponse
	Path(VolumeRequest) VolumeResponse
	Mount(VolumeRequest) VolumeResponse
	Unmount(VolumeRequest) VolumeResponse
}

type VolumeHandler struct {
	handler VolumeDriver
	mux     *http.ServeMux
}

type actionHandler func(VolumeRequest) VolumeResponse

func NewVolumeHandler(handler VolumeDriver) *VolumeHandler {
	h := &VolumeHandler{handler, http.NewServeMux()}
	h.initMux()
	return h
}

func (h *VolumeHandler) initMux() {
	h.mux.HandleFunc(activatePath, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", defaultContentTypeV1)
		fmt.Fprintln(w, defaultImplementationManifest)
	})

	h.handle(createPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Create(req)
	})

	h.handle(remotePath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Remove(req)
	})

	h.handle(hostVirtualPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Path(req)
	})

	h.handle(mountPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Mount(req)
	})

	h.handle(unmountPath, func(req VolumeRequest) VolumeResponse {
		return h.handler.Unmount(req)
	})
}

func (h *VolumeHandler) handle(name string, actionCall actionHandler) {
	h.mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
		req, err := decodeRequest(w, r)
		if err != nil {
			return
		}

		res := actionCall(req)

		encodeResponse(w, res)
	})
}

func (h *VolumeHandler) ListenAndServe(proto, addr, group string) error {
	server := http.Server{
		Addr:    addr,
		Handler: h.mux,
	}

	start := make(chan struct{})

	var l net.Listener
	var err error
	switch proto {
	case "tcp":
		l, err = newTcpSocket(addr, nil, start)
	case "unix":
		l, err = newUnixSocket(addr, group, start)
	}
	if err != nil {
		return err
	}

	close(start)
	return server.Serve(l)
}

func decodeRequest(w http.ResponseWriter, r *http.Request) (req VolumeRequest, err error) {
	if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
	return
}

func encodeResponse(w http.ResponseWriter, res VolumeResponse) {
	w.Header().Set("Content-Type", defaultContentTypeV1)
	if res.Err != "" {
		w.WriteHeader(http.StatusInternalServerError)
	}
	json.NewEncoder(w).Encode(res)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin"
)

//Mime types for different files
var MimeTypes = map[string]string{
	".css":  "text/css",
	".js":   "application/javascript",
	".icon": "image-x-icon",
}

//Error struct
type Error struct {
	Message string `json:"error"`
}

//NewError creates new Error struct from go's error
func NewError(err error) Error {
	return Error{err.Error()}
}

func assetContentType(name string) string {
	mime := MimeTypes[filepath.Ext(name)]

	if mime != "" {
		return mime
	}

	return "text/plain"
}

//APIHome load home page
func APIHome(c *gin.Context) {
	data, err := Asset("static/index.html")

	if err != nil {
		c.String(400, err.Error())
		return
	}

	c.Data(200, "text/html; charset=utf-8", data)
}

//APIConnect will connect to our mysql database
func APIConnect(c *gin.Context) {
	url := c.Request.FormValue("url")

	if url == "" {
		c.JSON(400, Error{"Url parameter is required"})
		return
	}

	client, err := NewClientFromURL(url)
	if err != nil {
		c.JSON(400, Error{err.Error()})
		return
	}

	err = client.Test()
	if err != nil {
		c.JSON(400, Error{err.Error()})
		return
	}

	info, err := client.Info()

	if err == nil {
		if dbClient != nil {
			dbClient.db.Close()
		}

		dbClient = client
	}

	c.JSON(200, info.Format()[0])
}

func APIClose(c *gin.Context) {
	err := dbClient.Close()

	if err != nil {
		c.JSON(400, NewError(err))
	}

	c.Writer.WriteHeader(204)
}

//APIGetDatabases will get you all databases in system
func APIGetDatabases(c *gin.Context) {
	names, err := dbClient.Databases()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, names)
}

//APIGetDatabaseTables will give the tables of a database
func APIGetDatabaseTables(c *gin.Context) {
	res, err := dbClient.DatabaseTables(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetDatabaseViews will give the views of a database
func APIGetDatabaseViews(c *gin.Context) {
	res, err := dbClient.DatabaseViews(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetDatabaseProcedures will give the stored procedures of a database
func APIGetDatabaseProcedures(c *gin.Context) {
	res, err := dbClient.DatabaseProcedures(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetDatabaseFunctions will give the functions of a database
func APIGetDatabaseFunctions(c *gin.Context) {
	res, err := dbClient.DatabaseFunctions(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APISetDefaultDatabase will set the database as default db for connection
func APISetDefaultDatabase(c *gin.Context) {
	dbName := c.Params.ByName("database")
	query := fmt.Sprintf("use %s;", dbName)

	APIHandleQuery(query, c)
}

//APIRunQuery will run the user's sql query
func APIRunQuery(c *gin.Context) {
	query := strings.TrimSpace(c.Request.FormValue("query"))

	if query == "" {
		c.JSON(400, errors.New("Query parameter is missing"))
		return
	}

	APIHandleQuery(query, c)
}

//APIExplainQuery will run explain on the sql query and return the output
func APIExplainQuery(c *gin.Context) {
	query := strings.TrimSpace(c.Request.FormValue("query"))

	if query == "" {
		c.JSON(400, errors.New("Query parameter is missing"))
		return
	}

	APIHandleQuery(fmt.Sprintf("EXPLAIN %s", query), c)
}

//APIGetTables will fetch the tables in our
func APIGetTables(c *gin.Context) {
	names, err := dbClient.Tables()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, names)
}

//APIGetTable get a single table
func APIGetTable(c *gin.Context) {
	res, err := dbClient.Table(c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetTableInfo returns info about table like row_count, data size etc.
func APIGetTableInfo(c *gin.Context) {
	res, err := dbClient.TableInfo(c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res.Format()[0])
}

//APIHistory will return query history of current dbClient
func APIHistory(c *gin.Context) {
	c.JSON(200, dbClient.history)
}

//APIInfo returns information about the current db connecction
func APIInfo(c *gin.Context) {
	if dbClient == nil {
		c.JSON(400, Error{"Not connected"})
		return
	}

	res, err := dbClient.Info()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res.Format()[0])
}

//APITableIndexes returns the indexs of a table
func APITableIndexes(c *gin.Context) {
	res, err := dbClient.TableIndexes(c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIProcedureParameters returns the parameters of a procedure
func APIProcedureParameters(c *gin.Context) {
	res, err := dbClient.ProcedureParameters(c.Params.ByName("procedure"), c.Request.FormValue("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetCollationCharSet returns the character sets and collation available in
//database
func APIGetCollationCharSet(c *gin.Context) {
	res, err := dbClient.DatabaseCollationCharSet()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIAlterDatabase alter database to change charset & collation
func APIAlterDatabase(c *gin.Context) {
	res, err := dbClient.AlterDatabase(c.Params.ByName("database"),
		c.Request.FormValue("charset"), c.Request.FormValue("collation"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(201, res)
}

//APIDropDatabase drops the given database from the system
func APIDropDatabase(c *gin.Context) {
	_, err := dbClient.DropDatabase(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(204)
}

//APIDropTable will drop the table from this database
func APIDropTable(c *gin.Context) {
	_, err := dbClient.DropTable(c.Params.ByName("database"), c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(204)
}

//APITruncateTable truncates the table
func APITruncateTable(c *gin.Context) {
	_, err := dbClient.TruncateTable(c.Params.ByName("database"), c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(204)
}

//APIProcedureDefinition get definition of a procedure
func APIProcedureDefinition(c *gin.Context) {
	res, err := dbClient.ProcedureDefinition("procedure", c.Params.ByName("database"), c.Params.ByName("procedure"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIFunctionDefinition get definition of a function
func APIFunctionDefinition(c *gin.Context) {
	res, err := dbClient.ProcedureDefinition("function", c.Params.ByName("database"), c.Params.ByName("function"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APICreateProcedure creates/edits a stored procedure
func APICreateProcedure(c *gin.Context) {
	dbName := c.Params.ByName("database")
	procName := c.Params.ByName("procedure")
	procDef := c.Request.FormValue("definition")

	_, err := dbClient.ProcedureCreate("PROCEDURE", dbName, procName, procDef)

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(200)
}

//APICreateFunction creates/edits a function
func APICreateFunction(c *gin.Context) {
	dbName := c.Params.ByName("database")
	procName := c.Params.ByName("function")
	procDef := c.Request.FormValue("definition")

	_, err := dbClient.ProcedureCreate("FUNCTION", dbName, procName, procDef)

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(200)
}

//APIViewDefinition gets the definition of a view
func APIViewDefinition(c *gin.Context) {
	res, err := dbClient.ViewDefinition(c.Params.ByName("database"), c.Params.ByName("view"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIHandleQuery handles thq query and return the resultset as JSON
func APIHandleQuery(query string, c *gin.Context) {
	result, err := dbClient.Query(query)

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	q := c.Request.URL.Query()

	if len(q["format"]) > 0 {
		if q["format"][0] == "csv" {
			c.Data(200, "text/csv", result.CSV())
			return
		}
	}

	c.JSON(200, result)
}

//APIServeAsset serves the static assets
func APIServeAsset(c *gin.Context) {
	file := fmt.Sprintf(
		"static/%s/%s",
		c.Params.ByName("type"),
		c.Params.ByName("name"),
	)

	data, err := Asset(file)

	if err != nil {
		c.String(400, err.Error())
		return
	}

	if len(data) == 0 {
		c.String(404, "Asset is empty")
		return
	}

	c.Data(200, assetContentType(file), data)
}
<commit_msg>Add mime type for .svg<commit_after>package main

import (
	"errors"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/gin-gonic/gin"
)

//Mime types for different files
var MimeTypes = map[string]string{
	".css":  "text/css",
	".js":   "application/javascript",
	".icon": "image-x-icon",
	".svg":  "image/svg+xml",
}

//Error struct
type Error struct {
	Message string `json:"error"`
}

//NewError creates new Error struct from go's error
func NewError(err error) Error {
	return Error{err.Error()}
}

func assetContentType(name string) string {
	mime := MimeTypes[filepath.Ext(name)]

	if mime != "" {
		return mime
	}

	return "text/plain"
}

//APIHome load home page
func APIHome(c *gin.Context) {
	data, err := Asset("static/index.html")

	if err != nil {
		c.String(400, err.Error())
		return
	}

	c.Data(200, "text/html; charset=utf-8", data)
}

//APIConnect will connect to our mysql database
func APIConnect(c *gin.Context) {
	url := c.Request.FormValue("url")

	if url == "" {
		c.JSON(400, Error{"Url parameter is required"})
		return
	}

	client, err := NewClientFromURL(url)
	if err != nil {
		c.JSON(400, Error{err.Error()})
		return
	}

	err = client.Test()
	if err != nil {
		c.JSON(400, Error{err.Error()})
		return
	}

	info, err := client.Info()

	if err == nil {
		if dbClient != nil {
			dbClient.db.Close()
		}

		dbClient = client
	}

	c.JSON(200, info.Format()[0])
}

func APIClose(c *gin.Context) {
	err := dbClient.Close()

	if err != nil {
		c.JSON(400, NewError(err))
	}

	c.Writer.WriteHeader(204)
}

//APIGetDatabases will get you all databases in system
func APIGetDatabases(c *gin.Context) {
	names, err := dbClient.Databases()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, names)
}

//APIGetDatabaseTables will give the tables of a database
func APIGetDatabaseTables(c *gin.Context) {
	res, err := dbClient.DatabaseTables(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetDatabaseViews will give the views of a database
func APIGetDatabaseViews(c *gin.Context) {
	res, err := dbClient.DatabaseViews(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetDatabaseProcedures will give the stored procedures of a database
func APIGetDatabaseProcedures(c *gin.Context) {
	res, err := dbClient.DatabaseProcedures(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetDatabaseFunctions will give the functions of a database
func APIGetDatabaseFunctions(c *gin.Context) {
	res, err := dbClient.DatabaseFunctions(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APISetDefaultDatabase will set the database as default db for connection
func APISetDefaultDatabase(c *gin.Context) {
	dbName := c.Params.ByName("database")
	query := fmt.Sprintf("use %s;", dbName)

	APIHandleQuery(query, c)
}

//APIRunQuery will run the user's sql query
func APIRunQuery(c *gin.Context) {
	query := strings.TrimSpace(c.Request.FormValue("query"))

	if query == "" {
		c.JSON(400, errors.New("Query parameter is missing"))
		return
	}

	APIHandleQuery(query, c)
}

//APIExplainQuery will run explain on the sql query and return the output
func APIExplainQuery(c *gin.Context) {
	query := strings.TrimSpace(c.Request.FormValue("query"))

	if query == "" {
		c.JSON(400, errors.New("Query parameter is missing"))
		return
	}

	APIHandleQuery(fmt.Sprintf("EXPLAIN %s", query), c)
}

//APIGetTables will fetch the tables in our
func APIGetTables(c *gin.Context) {
	names, err := dbClient.Tables()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, names)
}

//APIGetTable get a single table
func APIGetTable(c *gin.Context) {
	res, err := dbClient.Table(c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetTableInfo returns info about table like row_count, data size etc.
func APIGetTableInfo(c *gin.Context) {
	res, err := dbClient.TableInfo(c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res.Format()[0])
}

//APIHistory will return query history of current dbClient
func APIHistory(c *gin.Context) {
	c.JSON(200, dbClient.history)
}

//APIInfo returns information about the current db connecction
func APIInfo(c *gin.Context) {
	if dbClient == nil {
		c.JSON(400, Error{"Not connected"})
		return
	}

	res, err := dbClient.Info()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res.Format()[0])
}

//APITableIndexes returns the indexs of a table
func APITableIndexes(c *gin.Context) {
	res, err := dbClient.TableIndexes(c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIProcedureParameters returns the parameters of a procedure
func APIProcedureParameters(c *gin.Context) {
	res, err := dbClient.ProcedureParameters(c.Params.ByName("procedure"), c.Request.FormValue("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIGetCollationCharSet returns the character sets and collation available in
//database
func APIGetCollationCharSet(c *gin.Context) {
	res, err := dbClient.DatabaseCollationCharSet()

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIAlterDatabase alter database to change charset & collation
func APIAlterDatabase(c *gin.Context) {
	res, err := dbClient.AlterDatabase(c.Params.ByName("database"),
		c.Request.FormValue("charset"), c.Request.FormValue("collation"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(201, res)
}

//APIDropDatabase drops the given database from the system
func APIDropDatabase(c *gin.Context) {
	_, err := dbClient.DropDatabase(c.Params.ByName("database"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(204)
}

//APIDropTable will drop the table from this database
func APIDropTable(c *gin.Context) {
	_, err := dbClient.DropTable(c.Params.ByName("database"), c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(204)
}

//APITruncateTable truncates the table
func APITruncateTable(c *gin.Context) {
	_, err := dbClient.TruncateTable(c.Params.ByName("database"), c.Params.ByName("table"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(204)
}

//APIProcedureDefinition get definition of a procedure
func APIProcedureDefinition(c *gin.Context) {
	res, err := dbClient.ProcedureDefinition("procedure", c.Params.ByName("database"), c.Params.ByName("procedure"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIFunctionDefinition get definition of a function
func APIFunctionDefinition(c *gin.Context) {
	res, err := dbClient.ProcedureDefinition("function", c.Params.ByName("database"), c.Params.ByName("function"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APICreateProcedure creates/edits a stored procedure
func APICreateProcedure(c *gin.Context) {
	dbName := c.Params.ByName("database")
	procName := c.Params.ByName("procedure")
	procDef := c.Request.FormValue("definition")

	_, err := dbClient.ProcedureCreate("PROCEDURE", dbName, procName, procDef)

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(200)
}

//APICreateFunction creates/edits a function
func APICreateFunction(c *gin.Context) {
	dbName := c.Params.ByName("database")
	procName := c.Params.ByName("function")
	procDef := c.Request.FormValue("definition")

	_, err := dbClient.ProcedureCreate("FUNCTION", dbName, procName, procDef)

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.Writer.WriteHeader(200)
}

//APIViewDefinition gets the definition of a view
func APIViewDefinition(c *gin.Context) {
	res, err := dbClient.ViewDefinition(c.Params.ByName("database"), c.Params.ByName("view"))

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	c.JSON(200, res)
}

//APIHandleQuery handles thq query and return the resultset as JSON
func APIHandleQuery(query string, c *gin.Context) {
	result, err := dbClient.Query(query)

	if err != nil {
		c.JSON(400, NewError(err))
		return
	}

	q := c.Request.URL.Query()

	if len(q["format"]) > 0 {
		if q["format"][0] == "csv" {
			c.Data(200, "text/csv", result.CSV())
			return
		}
	}

	c.JSON(200, result)
}

//APIServeAsset serves the static assets
func APIServeAsset(c *gin.Context) {
	file := fmt.Sprintf(
		"static/%s/%s",
		c.Params.ByName("type"),
		c.Params.ByName("name"),
	)

	data, err := Asset(file)

	if err != nil {
		c.String(400, err.Error())
		return
	}

	if len(data) == 0 {
		c.String(404, "Asset is empty")
		return
	}

	c.Data(200, assetContentType(file), data)
}
<|endoftext|>


<commit_before>package tnt

import (
	"bytes"
	"fmt"
)

var packedInt0 = PackInt(0)
var packedInt1 = PackInt(1)

func packLittle(value uint, bytes int) []byte {
	b := value
	result := make([]byte, bytes)
	for i := 0; i < bytes; i++ {
		result[i] = uint8(b & 0xFF)
		b >>= 8
	}
	return result
}

func packBig(value int, bytes int) []byte {
	b := value
	result := make([]byte, bytes)
	for i := bytes - 1; i >= 0; i-- {
		result[i] = uint8(b & 0xFF)
		b >>= 8
	}
	return result
}

func PackB(value uint8) []byte {
	return []byte{value}
}

// PackInt is alias for PackL
func PackInt(value uint32) []byte {
	return packLittle(uint(value), 4)
}

// PackLong is alias for PackQ
func PackLong(value uint64) []byte {
	return packLittle(uint(value), 8)
}

// PackIntBase128 is port from python pack_int_base128
func PackIntBase128(value uint32) []byte {
	if value < (1 << 7) {
		return []byte{
			uint8(value),
		}
	}

	if value < (1 << 14) {
		return []byte{
			uint8((value>>7)&0xff | 0x80),
			uint8(value & 0x7F),
		}
	}

	if value < (1 << 21) {
		return []byte{
			uint8((value>>14)&0xff | 0x80),
			uint8((value>>7)&0xff | 0x80),
			uint8(value & 0x7F),
		}
	}

	if value < (1 << 28) {
		return []byte{
			uint8((value>>21)&0xff | 0x80),
			uint8((value>>14)&0xff | 0x80),
			uint8((value>>7)&0xff | 0x80),
			uint8(value & 0x7F),
		}
	}

	return []byte{
		uint8((value>>28)&0xff | 0x80),
		uint8((value>>21)&0xff | 0x80),
		uint8((value>>14)&0xff | 0x80),
		uint8((value>>7)&0xff | 0x80),
		uint8(value & 0x7F),
	}
}

func packFieldStr(value []byte) []byte {
	valueLenPacked := PackIntBase128(uint32(len(value)))

	var buffer bytes.Buffer
	buffer.Write(valueLenPacked)
	buffer.Write([]byte(value))

	return buffer.Bytes()
}

func packFieldInt(value uint32) []byte {
	var buffer bytes.Buffer
	buffer.Write(PackB(4))
	buffer.Write(PackInt(value))

	return buffer.Bytes()
}

func packTuple(value Tuple) []byte {
	var buffer bytes.Buffer

	fields := len(value)

	buffer.Write(PackInt(uint32(fields)))

	for i := 0; i < fields; i++ {
		buffer.Write(packFieldStr(value[i]))
	}

	return buffer.Bytes()
}

func interfaceToUint32(t interface{}) (uint32, error) {
	switch t := t.(type) {
	default:
		return 0, fmt.Errorf("unexpected type %T\n", t) // %T prints whatever type t has
	case int:
		return uint32(t), nil
	case int64:
		return uint32(t), nil
	case uint:
		return uint32(t), nil
	case uint64:
		return uint32(t), nil
	case int32:
		return uint32(t), nil
	case uint32:
		return uint32(t), nil
	}
}

func (q *Select) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	limit := q.Limit
	if limit == 0 {
		limit = 0xffffffff
	}

	if q.Space != nil {
		i, err := interfaceToUint32(q.Space)
		if err != nil {
			return nil, err
		}
		bodyBuffer.Write(PackInt(uint32(i)))
	} else {
		bodyBuffer.Write(PackInt(defaultSpace))
	}
	bodyBuffer.Write(PackInt(q.Index))
	bodyBuffer.Write(PackInt(q.Offset))
	bodyBuffer.Write(PackInt(limit))

	if q.Value != nil {
		bodyBuffer.Write(PackInt(1))
		bodyBuffer.Write(packTuple(Tuple{q.Value}))
	} else if q.Values != nil {
		cnt := len(q.Values)
		bodyBuffer.Write(PackInt(uint32(cnt)))
		for i := 0; i < cnt; i++ {
			bodyBuffer.Write(packTuple(Tuple{q.Values[i]}))
		}
	} else if q.Tuples != nil {
		cnt := len(q.Tuples)
		bodyBuffer.Write(PackInt(uint32(cnt)))
		for i := 0; i < cnt; i++ {
			bodyBuffer.Write(packTuple(q.Tuples[i]))
		}
	} else {
		bodyBuffer.Write(packedInt0)
	}

	buffer.Write(PackInt(requestTypeSelect))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil
}

func (q *Insert) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	if q.Space != nil {
		i, err := interfaceToUint32(q.Space)
		if err != nil {
			return nil, err
		}
		bodyBuffer.Write(PackInt(uint32(i)))
	} else {
		bodyBuffer.Write(PackInt(defaultSpace))
	}

	if q.ReturnTuple {
		bodyBuffer.Write(packedInt1)
	} else {
		bodyBuffer.Write(packedInt0)
	}
	bodyBuffer.Write(packTuple(q.Tuple))

	buffer.Write(PackInt(requestTypeInsert))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil

}

func (q *Delete) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	if q.Space != nil {
		i, err := interfaceToUint32(q.Space)
		if err != nil {
			return nil, err
		}
		bodyBuffer.Write(PackInt(uint32(i)))
	} else {
		bodyBuffer.Write(PackInt(defaultSpace))
	}

	if q.ReturnTuple {
		bodyBuffer.Write(packedInt1)
	} else {
		bodyBuffer.Write(packedInt0)
	}

	if q.Value != nil {
		bodyBuffer.Write(packTuple(Tuple{q.Value}))
	} else if q.Values != nil {
		cnt := len(q.Values)
		for i := 0; i < cnt; i++ {
			bodyBuffer.Write(packTuple(Tuple{q.Values[i]}))
		}
	}

	buffer.Write(PackInt(requestTypeDelete))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil

}

func (q *Call) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	if q.ReturnTuple {
		bodyBuffer.Write(packedInt1)
	} else {
		bodyBuffer.Write(packedInt0)
	}
	bodyBuffer.Write(packFieldStr(q.Name))
	bodyBuffer.Write(packTuple(q.Tuple))

	buffer.Write(PackInt(requestTypeCall))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil

}
<commit_msg>add PackDouble to helper/tnt<commit_after>package tnt

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

var packedInt0 = PackInt(0)
var packedInt1 = PackInt(1)

func packLittle(value uint, bytes int) []byte {
	b := value
	result := make([]byte, bytes)
	for i := 0; i < bytes; i++ {
		result[i] = uint8(b & 0xFF)
		b >>= 8
	}
	return result
}

func packBig(value int, bytes int) []byte {
	b := value
	result := make([]byte, bytes)
	for i := bytes - 1; i >= 0; i-- {
		result[i] = uint8(b & 0xFF)
		b >>= 8
	}
	return result
}

func PackB(value uint8) []byte {
	return []byte{value}
}

// PackInt is alias for PackL
func PackInt(value uint32) []byte {
	return packLittle(uint(value), 4)
}

// PackLong is alias for PackQ
func PackLong(value uint64) []byte {
	return packLittle(uint(value), 8)
}

func PackDouble(value float64) []byte {
	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.LittleEndian, value)
	return buffer.Bytes()
}

// PackIntBase128 is port from python pack_int_base128
func PackIntBase128(value uint32) []byte {
	if value < (1 << 7) {
		return []byte{
			uint8(value),
		}
	}

	if value < (1 << 14) {
		return []byte{
			uint8((value>>7)&0xff | 0x80),
			uint8(value & 0x7F),
		}
	}

	if value < (1 << 21) {
		return []byte{
			uint8((value>>14)&0xff | 0x80),
			uint8((value>>7)&0xff | 0x80),
			uint8(value & 0x7F),
		}
	}

	if value < (1 << 28) {
		return []byte{
			uint8((value>>21)&0xff | 0x80),
			uint8((value>>14)&0xff | 0x80),
			uint8((value>>7)&0xff | 0x80),
			uint8(value & 0x7F),
		}
	}

	return []byte{
		uint8((value>>28)&0xff | 0x80),
		uint8((value>>21)&0xff | 0x80),
		uint8((value>>14)&0xff | 0x80),
		uint8((value>>7)&0xff | 0x80),
		uint8(value & 0x7F),
	}
}

func packFieldStr(value []byte) []byte {
	valueLenPacked := PackIntBase128(uint32(len(value)))

	var buffer bytes.Buffer
	buffer.Write(valueLenPacked)
	buffer.Write([]byte(value))

	return buffer.Bytes()
}

func packFieldInt(value uint32) []byte {
	var buffer bytes.Buffer
	buffer.Write(PackB(4))
	buffer.Write(PackInt(value))

	return buffer.Bytes()
}

func packTuple(value Tuple) []byte {
	var buffer bytes.Buffer

	fields := len(value)

	buffer.Write(PackInt(uint32(fields)))

	for i := 0; i < fields; i++ {
		buffer.Write(packFieldStr(value[i]))
	}

	return buffer.Bytes()
}

func interfaceToUint32(t interface{}) (uint32, error) {
	switch t := t.(type) {
	default:
		return 0, fmt.Errorf("unexpected type %T\n", t) // %T prints whatever type t has
	case int:
		return uint32(t), nil
	case int64:
		return uint32(t), nil
	case uint:
		return uint32(t), nil
	case uint64:
		return uint32(t), nil
	case int32:
		return uint32(t), nil
	case uint32:
		return uint32(t), nil
	}
}

func (q *Select) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	limit := q.Limit
	if limit == 0 {
		limit = 0xffffffff
	}

	if q.Space != nil {
		i, err := interfaceToUint32(q.Space)
		if err != nil {
			return nil, err
		}
		bodyBuffer.Write(PackInt(uint32(i)))
	} else {
		bodyBuffer.Write(PackInt(defaultSpace))
	}
	bodyBuffer.Write(PackInt(q.Index))
	bodyBuffer.Write(PackInt(q.Offset))
	bodyBuffer.Write(PackInt(limit))

	if q.Value != nil {
		bodyBuffer.Write(PackInt(1))
		bodyBuffer.Write(packTuple(Tuple{q.Value}))
	} else if q.Values != nil {
		cnt := len(q.Values)
		bodyBuffer.Write(PackInt(uint32(cnt)))
		for i := 0; i < cnt; i++ {
			bodyBuffer.Write(packTuple(Tuple{q.Values[i]}))
		}
	} else if q.Tuples != nil {
		cnt := len(q.Tuples)
		bodyBuffer.Write(PackInt(uint32(cnt)))
		for i := 0; i < cnt; i++ {
			bodyBuffer.Write(packTuple(q.Tuples[i]))
		}
	} else {
		bodyBuffer.Write(packedInt0)
	}

	buffer.Write(PackInt(requestTypeSelect))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil
}

func (q *Insert) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	if q.Space != nil {
		i, err := interfaceToUint32(q.Space)
		if err != nil {
			return nil, err
		}
		bodyBuffer.Write(PackInt(uint32(i)))
	} else {
		bodyBuffer.Write(PackInt(defaultSpace))
	}

	if q.ReturnTuple {
		bodyBuffer.Write(packedInt1)
	} else {
		bodyBuffer.Write(packedInt0)
	}
	bodyBuffer.Write(packTuple(q.Tuple))

	buffer.Write(PackInt(requestTypeInsert))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil

}

func (q *Delete) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	if q.Space != nil {
		i, err := interfaceToUint32(q.Space)
		if err != nil {
			return nil, err
		}
		bodyBuffer.Write(PackInt(uint32(i)))
	} else {
		bodyBuffer.Write(PackInt(defaultSpace))
	}

	if q.ReturnTuple {
		bodyBuffer.Write(packedInt1)
	} else {
		bodyBuffer.Write(packedInt0)
	}

	if q.Value != nil {
		bodyBuffer.Write(packTuple(Tuple{q.Value}))
	} else if q.Values != nil {
		cnt := len(q.Values)
		for i := 0; i < cnt; i++ {
			bodyBuffer.Write(packTuple(Tuple{q.Values[i]}))
		}
	}

	buffer.Write(PackInt(requestTypeDelete))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil

}

func (q *Call) Pack(requestID uint32, defaultSpace uint32) ([]byte, error) {
	var bodyBuffer bytes.Buffer
	var buffer bytes.Buffer

	if q.ReturnTuple {
		bodyBuffer.Write(packedInt1)
	} else {
		bodyBuffer.Write(packedInt0)
	}
	bodyBuffer.Write(packFieldStr(q.Name))
	bodyBuffer.Write(packTuple(q.Tuple))

	buffer.Write(PackInt(requestTypeCall))
	buffer.Write(PackInt(uint32(bodyBuffer.Len())))
	buffer.Write(PackInt(requestID))
	buffer.Write(bodyBuffer.Bytes())

	return buffer.Bytes(), nil

}
<|endoftext|>


<commit_before>package tui

import (
	"image"
	"testing"
)

var drawBoxTests = []struct {
	test  string
	size  image.Point
	setup func() *Box
	want  string
}{
	{
		test: "Empty horizontal box",
		setup: func() *Box {
			b := NewHBox()
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│........│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Horizontal box containing one widget",
		setup: func() *Box {
			b := NewHBox(NewLabel("test"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│test....│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Horizontal box containing multiple widgets",
		setup: func() *Box {
			b := NewHBox(NewLabel("test"), NewLabel("foo"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│testfoo.│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Empty vertical box",
		setup: func() *Box {
			b := NewVBox()
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│........│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Vertical box containing one widget",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│test....│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Vertical box containing multiple widgets",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"), NewLabel("foo"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│test....│
│........│
│foo.....│
└────────┘
`,
	},
	{
		test: "Horizontally centered box",
		size: image.Point{32, 5},
		setup: func() *Box {
			nested := NewVBox(NewLabel("test"))
			nested.SetBorder(true)

			b := NewHBox(NewSpacer(), nested, NewSpacer())
			b.SetBorder(true)
			return b
		},
		want: `
┌──────────────────────────────┐
│............┌────┐............│
│............│test│............│
│............└────┘............│
└──────────────────────────────┘
`,
	},
	{
		test: "Two columns",
		size: image.Point{32, 10},
		setup: func() *Box {
			first := NewVBox(NewLabel("test"))
			first.SetBorder(true)

			second := NewVBox(NewLabel("test"))
			second.SetBorder(true)

			third := NewHBox(first, second)
			third.SetBorder(true)

			return third
		},
		want: `
┌──────────────────────────────┐
│┌─────────────┐┌─────────────┐│
││test.........││test.........││
││.............││.............││
││.............││.............││
││.............││.............││
││.............││.............││
││.............││.............││
│└─────────────┘└─────────────┘│
└──────────────────────────────┘
`,
	},
	{
		test: "Two rows with two columns",
		size: image.Point{32, 22},
		setup: func() *Box {
			col0 := NewVBox(NewLabel("test"))
			col0.SetBorder(true)
			col1 := NewVBox(NewLabel("test"))
			col1.SetBorder(true)

			row0 := NewHBox(col0, col1)
			row0.SetBorder(true)

			col2 := NewVBox(NewLabel("test"))
			col2.SetBorder(true)
			col3 := NewVBox(NewLabel("test"))
			col3.SetBorder(true)

			row1 := NewHBox(col2, col3)
			row1.SetBorder(true)

			b := NewVBox(row0, row1)
			b.SetBorder(true)

			return b
		},
		want: `
┌──────────────────────────────┐
│┌────────────────────────────┐│
││┌────────────┐┌────────────┐││
│││test........││test........│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
││└────────────┘└────────────┘││
│└────────────────────────────┘│
│┌────────────────────────────┐│
││┌────────────┐┌────────────┐││
│││test........││test........│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
││└────────────┘└────────────┘││
│└────────────────────────────┘│
└──────────────────────────────┘
`,
	},
	{
		test: "Maximum/Preferred/Preferred",
		size: image.Point{32, 24},
		setup: func() *Box {
			edit0 := NewLabel("")
			edit0.SetText("test\ntesting\nfoo\nbar")
			row0 := NewVBox(edit0)
			row0.SetSizePolicy(Preferred, Maximum)
			row0.SetBorder(true)

			edit1 := NewLabel("")
			edit1.SetText("test\ntesting\nfoo\nbar")
			row1 := NewVBox(edit1)
			row1.SetSizePolicy(Preferred, Preferred)
			row1.SetBorder(true)

			edit2 := NewLabel("")
			edit2.SetText("foo")
			row2 := NewVBox(edit2)
			row2.SetSizePolicy(Preferred, Preferred)
			row2.SetBorder(true)

			b := NewVBox(row0, row1, row2)
			b.SetBorder(true)

			return b
		},
		want: `
┌──────────────────────────────┐
│┌────────────────────────────┐│
││test........................││
││testing.....................││
││foo.........................││
││bar.........................││
│└────────────────────────────┘│
│┌────────────────────────────┐│
││test........................││
││testing.....................││
││foo.........................││
││bar.........................││
││............................││
││............................││
│└────────────────────────────┘│
│┌────────────────────────────┐│
││foo.........................││
││............................││
││............................││
││............................││
││............................││
││............................││
│└────────────────────────────┘│
└──────────────────────────────┘
`,
	},
	{
		test: "Box with title",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"))
			b.SetTitle("Title")
			b.SetBorder(true)
			return b
		},
		want: `
┌Title───┐
│test....│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Box with very long title",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"))
			b.SetTitle("Very long title")
			b.SetBorder(true)
			return b
		},
		want: `
┌Very lon┐
│test....│
│........│
│........│
└────────┘
`,
	},
}

func TestBox_Draw(t *testing.T) {
	for _, tt := range drawBoxTests {
		tt := tt
		t.Run(tt.test, func(t *testing.T) {
			var surface *TestSurface
			if tt.size.X == 0 && tt.size.Y == 0 {
				surface = NewTestSurface(10, 5)
			} else {
				surface = NewTestSurface(tt.size.X, tt.size.Y)
			}

			painter := NewPainter(surface, NewTheme())
			painter.Repaint(tt.setup())

			if surface.String() != tt.want {
				t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), tt.want)
			}
		})
	}
}

func TestBox_IsFocused(t *testing.T) {
	btn := NewButton("Test box focus")
	box := NewVBox(btn)
	want := false
	if box.IsFocused() != want {
		t.Errorf("got = \n%t\n\nwant = \n%t", box.IsFocused(), want)
	}
	btn.SetFocused(true)
	want = true
	if box.IsFocused() != want {
		t.Errorf("got = \n%t\n\nwant = \n%t", box.IsFocused(), want)
	}
}

var insertWidgetTests = []struct {
	test  string
	size  image.Point
	setup func() *Box
	index int
	want  string
}{
	{
		test:  "Insert at beginning of box",
		index: 0,
		want: `
┌──────────────────┐
│Insertion.........│
│..................│
│Test 0............│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`,
	},
	{
		test:  "Insert in the middle",
		index: 1,
		want: `
┌──────────────────┐
│Test 0............│
│..................│
│Insertion.........│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`,
	},
	{
		test:  "Slice index out of range",
		index: 5,
		want: `
┌──────────────────┐
│Test 0............│
│..................│
│..................│
│Test 1............│
│..................│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`,
	},
	{
		test:  "Append widget",
		index: 3,
		want: `
┌──────────────────┐
│Test 0............│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
│Insertion.........│
│..................│
└──────────────────┘
`,
	},
}

func TestBox_Insert(t *testing.T) {
	for _, tt := range insertWidgetTests {
		tt := tt
		t.Run(tt.test, func(t *testing.T) {
			surface := NewTestSurface(20, 10)
			painter := NewPainter(surface, NewTheme())

			label0 := NewLabel("Test 0")
			label1 := NewLabel("Test 1")
			label2 := NewLabel("Test 2")

			b := NewVBox(label0, label1, label2)

			insertLabel := NewLabel("Insertion")
			b.Insert(tt.index, insertLabel)

			b.SetBorder(true)

			painter.Repaint(b)

			if surface.String() != tt.want {
				t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), tt.want)
			}
		})
	}
}

func TestBox_Prepend(t *testing.T) {
	want := `
┌──────────────────┐
│Prepend...........│
│..................│
│Test 0............│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`
	surface := NewTestSurface(20, 10)
	painter := NewPainter(surface, NewTheme())

	label0 := NewLabel("Test 0")
	label1 := NewLabel("Test 1")
	label2 := NewLabel("Test 2")

	b := NewVBox(label0, label1, label2)

	label := NewLabel("Prepend")
	b.Prepend(label)

	b.SetBorder(true)

	painter.Repaint(b)

	if surface.String() != want {
		t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), want)
	}
}

func TestBox_Remove(t *testing.T) {
	want := `
┌──────────────────┐
│Test 0............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`
	surface := NewTestSurface(20, 6)
	painter := NewPainter(surface, NewTheme())

	label0 := NewLabel("Test 0")
	label1 := NewLabel("Test 1")
	label2 := NewLabel("Test 2")

	b := NewVBox(label0, label1, label2)

	b.Remove(1)
	b.Remove(10)

	b.SetBorder(true)

	painter.Repaint(b)

	if surface.String() != want {
		t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), want)
	}
}
<commit_msg>Add test for box styling<commit_after>package tui

import (
	"image"
	"testing"
)

var drawBoxTests = []struct {
	test  string
	size  image.Point
	setup func() *Box
	want  string
}{
	{
		test: "Empty horizontal box",
		setup: func() *Box {
			b := NewHBox()
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│........│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Horizontal box containing one widget",
		setup: func() *Box {
			b := NewHBox(NewLabel("test"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│test....│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Horizontal box containing multiple widgets",
		setup: func() *Box {
			b := NewHBox(NewLabel("test"), NewLabel("foo"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│testfoo.│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Empty vertical box",
		setup: func() *Box {
			b := NewVBox()
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│........│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Vertical box containing one widget",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│test....│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Vertical box containing multiple widgets",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"), NewLabel("foo"))
			b.SetBorder(true)
			return b
		},
		want: `
┌────────┐
│test....│
│........│
│foo.....│
└────────┘
`,
	},
	{
		test: "Horizontally centered box",
		size: image.Point{32, 5},
		setup: func() *Box {
			nested := NewVBox(NewLabel("test"))
			nested.SetBorder(true)

			b := NewHBox(NewSpacer(), nested, NewSpacer())
			b.SetBorder(true)
			return b
		},
		want: `
┌──────────────────────────────┐
│............┌────┐............│
│............│test│............│
│............└────┘............│
└──────────────────────────────┘
`,
	},
	{
		test: "Two columns",
		size: image.Point{32, 10},
		setup: func() *Box {
			first := NewVBox(NewLabel("test"))
			first.SetBorder(true)

			second := NewVBox(NewLabel("test"))
			second.SetBorder(true)

			third := NewHBox(first, second)
			third.SetBorder(true)

			return third
		},
		want: `
┌──────────────────────────────┐
│┌─────────────┐┌─────────────┐│
││test.........││test.........││
││.............││.............││
││.............││.............││
││.............││.............││
││.............││.............││
││.............││.............││
│└─────────────┘└─────────────┘│
└──────────────────────────────┘
`,
	},
	{
		test: "Two rows with two columns",
		size: image.Point{32, 22},
		setup: func() *Box {
			col0 := NewVBox(NewLabel("test"))
			col0.SetBorder(true)
			col1 := NewVBox(NewLabel("test"))
			col1.SetBorder(true)

			row0 := NewHBox(col0, col1)
			row0.SetBorder(true)

			col2 := NewVBox(NewLabel("test"))
			col2.SetBorder(true)
			col3 := NewVBox(NewLabel("test"))
			col3.SetBorder(true)

			row1 := NewHBox(col2, col3)
			row1.SetBorder(true)

			b := NewVBox(row0, row1)
			b.SetBorder(true)

			return b
		},
		want: `
┌──────────────────────────────┐
│┌────────────────────────────┐│
││┌────────────┐┌────────────┐││
│││test........││test........│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
││└────────────┘└────────────┘││
│└────────────────────────────┘│
│┌────────────────────────────┐│
││┌────────────┐┌────────────┐││
│││test........││test........│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
│││............││............│││
││└────────────┘└────────────┘││
│└────────────────────────────┘│
└──────────────────────────────┘
`,
	},
	{
		test: "Maximum/Preferred/Preferred",
		size: image.Point{32, 24},
		setup: func() *Box {
			edit0 := NewLabel("")
			edit0.SetText("test\ntesting\nfoo\nbar")
			row0 := NewVBox(edit0)
			row0.SetSizePolicy(Preferred, Maximum)
			row0.SetBorder(true)

			edit1 := NewLabel("")
			edit1.SetText("test\ntesting\nfoo\nbar")
			row1 := NewVBox(edit1)
			row1.SetSizePolicy(Preferred, Preferred)
			row1.SetBorder(true)

			edit2 := NewLabel("")
			edit2.SetText("foo")
			row2 := NewVBox(edit2)
			row2.SetSizePolicy(Preferred, Preferred)
			row2.SetBorder(true)

			b := NewVBox(row0, row1, row2)
			b.SetBorder(true)

			return b
		},
		want: `
┌──────────────────────────────┐
│┌────────────────────────────┐│
││test........................││
││testing.....................││
││foo.........................││
││bar.........................││
│└────────────────────────────┘│
│┌────────────────────────────┐│
││test........................││
││testing.....................││
││foo.........................││
││bar.........................││
││............................││
││............................││
│└────────────────────────────┘│
│┌────────────────────────────┐│
││foo.........................││
││............................││
││............................││
││............................││
││............................││
││............................││
│└────────────────────────────┘│
└──────────────────────────────┘
`,
	},
	{
		test: "Box with title",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"))
			b.SetTitle("Title")
			b.SetBorder(true)
			return b
		},
		want: `
┌Title───┐
│test....│
│........│
│........│
└────────┘
`,
	},
	{
		test: "Box with very long title",
		setup: func() *Box {
			b := NewVBox(NewLabel("test"))
			b.SetTitle("Very long title")
			b.SetBorder(true)
			return b
		},
		want: `
┌Very lon┐
│test....│
│........│
│........│
└────────┘
`,
	},
}

func TestBox_Draw(t *testing.T) {
	for _, tt := range drawBoxTests {
		tt := tt
		t.Run(tt.test, func(t *testing.T) {
			var surface *TestSurface
			if tt.size.X == 0 && tt.size.Y == 0 {
				surface = NewTestSurface(10, 5)
			} else {
				surface = NewTestSurface(tt.size.X, tt.size.Y)
			}

			painter := NewPainter(surface, NewTheme())
			painter.Repaint(tt.setup())

			if surface.String() != tt.want {
				t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), tt.want)
			}
		})
	}
}

var styleBoxTests = []struct {
	test  string
	setup func() *Box
	theme func() *Theme
	wantColors  string
	wantDecorations string
}{
	{
		test: "Red horizontal box",
		setup: func() *Box {
			b := NewHBox()
			return b
		},
		theme: func() *Theme{
			t := NewTheme()
			t.SetStyle("box", Style{
				Fg: Color(3),
			})
			return t
		},
		wantColors: `
3333333333
3333333333
3333333333
3333333333
3333333333
`,
	},
}

func TestBox_Style(t *testing.T) {
	for _, tt := range styleBoxTests {
		tt := tt
		t.Run(tt.test, func(t *testing.T) {
			surface := NewTestSurface(10, 5)
			painter := NewPainter(surface, tt.theme())
			painter.Repaint(tt.setup())

			if tt.wantColors != "" && surface.FgColors() != tt.wantColors {
				t.Errorf("wrong colors: got = \n%s\n\nwant = \n%s", surface.FgColors(), tt.wantColors)
			}
			if tt.wantDecorations != "" && surface.Decorations() != tt.wantDecorations {
				t.Errorf("wrong decorations: got = \n%s\n\nwant = \n%s", surface.Decorations(), tt.wantDecorations)
			}
		})
	}
}

func TestBox_IsFocused(t *testing.T) {
	btn := NewButton("Test box focus")
	box := NewVBox(btn)
	want := false
	if box.IsFocused() != want {
		t.Errorf("got = \n%t\n\nwant = \n%t", box.IsFocused(), want)
	}
	btn.SetFocused(true)
	want = true
	if box.IsFocused() != want {
		t.Errorf("got = \n%t\n\nwant = \n%t", box.IsFocused(), want)
	}
}

var insertWidgetTests = []struct {
	test  string
	size  image.Point
	setup func() *Box
	index int
	want  string
}{
	{
		test:  "Insert at beginning of box",
		index: 0,
		want: `
┌──────────────────┐
│Insertion.........│
│..................│
│Test 0............│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`,
	},
	{
		test:  "Insert in the middle",
		index: 1,
		want: `
┌──────────────────┐
│Test 0............│
│..................│
│Insertion.........│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`,
	},
	{
		test:  "Slice index out of range",
		index: 5,
		want: `
┌──────────────────┐
│Test 0............│
│..................│
│..................│
│Test 1............│
│..................│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`,
	},
	{
		test:  "Append widget",
		index: 3,
		want: `
┌──────────────────┐
│Test 0............│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
│Insertion.........│
│..................│
└──────────────────┘
`,
	},
}

func TestBox_Insert(t *testing.T) {
	for _, tt := range insertWidgetTests {
		tt := tt
		t.Run(tt.test, func(t *testing.T) {
			surface := NewTestSurface(20, 10)
			painter := NewPainter(surface, NewTheme())

			label0 := NewLabel("Test 0")
			label1 := NewLabel("Test 1")
			label2 := NewLabel("Test 2")

			b := NewVBox(label0, label1, label2)

			insertLabel := NewLabel("Insertion")
			b.Insert(tt.index, insertLabel)

			b.SetBorder(true)

			painter.Repaint(b)

			if surface.String() != tt.want {
				t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), tt.want)
			}
		})
	}
}

func TestBox_Prepend(t *testing.T) {
	want := `
┌──────────────────┐
│Prepend...........│
│..................│
│Test 0............│
│..................│
│Test 1............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`
	surface := NewTestSurface(20, 10)
	painter := NewPainter(surface, NewTheme())

	label0 := NewLabel("Test 0")
	label1 := NewLabel("Test 1")
	label2 := NewLabel("Test 2")

	b := NewVBox(label0, label1, label2)

	label := NewLabel("Prepend")
	b.Prepend(label)

	b.SetBorder(true)

	painter.Repaint(b)

	if surface.String() != want {
		t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), want)
	}
}

func TestBox_Remove(t *testing.T) {
	want := `
┌──────────────────┐
│Test 0............│
│..................│
│Test 2............│
│..................│
└──────────────────┘
`
	surface := NewTestSurface(20, 6)
	painter := NewPainter(surface, NewTheme())

	label0 := NewLabel("Test 0")
	label1 := NewLabel("Test 1")
	label2 := NewLabel("Test 2")

	b := NewVBox(label0, label1, label2)

	b.Remove(1)
	b.Remove(10)

	b.SetBorder(true)

	painter.Repaint(b)

	if surface.String() != want {
		t.Errorf("got = \n%s\n\nwant = \n%s", surface.String(), want)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"

	"github.com/nsf/termbox-go"

	"github.com/prattmic/lesser/lineio"
)

type size struct {
	x int
	y int
}

type Event int

const (
	EventQuit Event = iota
)

type Lesser struct {
	src    lineio.LineReader
	events chan Event
	size   size
}

func (l *Lesser) listenEvents() {
	for {
		e := termbox.PollEvent()
		switch e.Type {
		case termbox.EventKey:
			switch e.Ch {
			case 'q':
				l.events <- EventQuit
			}
		}
	}
}

func (l *Lesser) fillScreen() error {
	for y := 0; y < l.size.y; y++ {
		buf := make([]byte, l.size.x)

		_, err := l.src.ReadLine(buf, y+1)
		// EOF just means the line was shorter than the display.
		if err != nil && err != io.EOF {
			return err
		}

		for i, c := range buf {
			termbox.SetCell(i, y, rune(c), 0, 0)
		}
	}

	termbox.Flush()

	return nil
}

func (l *Lesser) Run() {
	go l.listenEvents()

	err := l.fillScreen()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to fill screen: %v\n", err)
		return
	}

	select {
	case e := <-l.events:
		switch e {
		case EventQuit:
			return
		}
	}
}

func NewLesser(f *os.File) Lesser {
	x, y := termbox.Size()

	return Lesser{
		src:    lineio.NewLineReader(f),
		events: make(chan Event, 1),
		size:   size{x: x, y: y},
	}
}
<commit_msg>Scrolling support<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"sync"

	"github.com/nsf/termbox-go"

	"github.com/prattmic/lesser/lineio"
)

type size struct {
	x int
	y int
}

type Event int

const (
	// EventQuit requests an application exit.
	EventQuit Event = iota

	// EventRefresh requests a display refresh.
	EventRefresh
)

type Lesser struct {
	// src is the source file being displayed.
	src    lineio.LineReader

	// events is used to notify the main goroutine of events.
	events chan Event

	// mu locks the fields below.
	mu     sync.Mutex

	// size is the size of the display.
	size   size

	// line is the line number of the first line of the display.
	line   int
}

// scrollUp moves the display up (i.e., decrements the first line number).
// You cannot scroll beyond the beginning of the file.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollUp() {
	l.mu.Lock()
	defer l.mu.Unlock()

	if l.line > 1 {
		l.line--
	}
}

// scrollDown moves the display down (i.e., increments the first line number).
// FIXME(prattmic): Nothing prevents scrolling beyond the end of the file.
// refreshScreen must be called for the display to actually update.
func (l *Lesser) scrollDown() {
	l.mu.Lock()
	defer l.mu.Unlock()

	l.line++
}

func (l *Lesser) listenEvents() {
	for {
		e := termbox.PollEvent()
		switch e.Type {
		case termbox.EventKey:
			switch e.Ch {
			case 'q':
				l.events <- EventQuit
			case 'j':
				l.scrollDown()
				l.events <- EventRefresh
			case 'k':
				l.scrollUp()
				l.events <- EventRefresh
			}
		}
	}
}

func (l *Lesser) refreshScreen() error {
	l.mu.Lock()
	defer l.mu.Unlock()

	for y := 0; y < l.size.y; y++ {
		buf := make([]byte, l.size.x)

		_, err := l.src.ReadLine(buf, l.line+y)
		// EOF just means the line was shorter than the display.
		if err != nil && err != io.EOF {
			return err
		}

		for i, c := range buf {
			termbox.SetCell(i, y, rune(c), 0, 0)
		}
	}

	termbox.Flush()

	return nil
}

func (l *Lesser) Run() {
	go l.listenEvents()

	err := l.refreshScreen()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to refresh screen: %v\n", err)
		return
	}

	for {
		e := <-l.events

		switch e {
		case EventQuit:
			return
		case EventRefresh:
			err = l.refreshScreen()
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to refresh screen: %v\n", err)
				return
			}
		}
	}
}

func NewLesser(f *os.File) Lesser {
	x, y := termbox.Size()

	return Lesser{
		src:    lineio.NewLineReader(f),
		size:   size{x: x, y: y},
		line:   1,
		events: make(chan Event, 1),
	}
}
<|endoftext|>


<commit_before>package meep

import (
	"fmt"
	"os"
	"strings"
	"testing"
)

func TestReacharound(t *testing.T) {
	type Woop struct {
		AutodescribingError
		Wonk string
	}
	var err error
	err = &Woop{Wonk: "Bonk"}
	err = New(err)
	woop := err.(*Woop)
	if woop.Wonk != "Bonk" {
		t.Errorf("Bonk somehow became %q", woop.Wonk)
	}
	if woop.AutodescribingError.self == nil {
		t.Errorf("No impact")
	}
	if woop.AutodescribingError.self != err {
		t.Errorf("Drat")
	}
}

func TestAutodescribeSimple(t *testing.T) {
	type Woop struct {
		AutodescribingError
		Wonk string
	}
	err := New(&Woop{Wonk: "Bonk"})
	expect := `Error[meep.Woop]: Wonk="Bonk";`
	if expect != err.Error() {
		t.Errorf("expected %q, got %q", expect, err.Error())
	}
}

func TestAutodescribePlusCause(t *testing.T) {
	type Woop struct {
		AutodescribingError
		CauseableError
		Wonk string
	}
	err := New(&Woop{
		Wonk:           "Bonk",
		CauseableError: CauseableError{fmt.Errorf("lecause")},
	})
	expect := `Error[meep.Woop]: Wonk="Bonk";` + "\n"
	expect += "\t" + `Caused by: lecause` + "\n"
	actual := err.Error()
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}
	t.Logf("this is what a very basic error with a nested cause looks like:\n>>>\n%s\n<<<\n", actual)
}

func TestAutodescribePlusTraceableCause(t *testing.T) {
	type Woop struct {
		AutodescribingError
		CauseableError
		Wonk string
	}
	type Boop struct {
		TraceableError
		AutodescribingError
	}
	err := New(&Woop{
		Wonk: "Bonk",
		CauseableError: CauseableError{
			New(&Boop{}),
		},
	})
	expect := `Error[meep.Woop]: Wonk="Bonk";` + "\n"
	expect += "\t" + `Caused by: Error[meep.Boop]: ` + "\n" // trailing space questionable
	expect += "\t\t" + `Stack trace:` + "\n"
	expect += "\t\t\t" + `·> /autodescriber_test.go:74: meep.TestAutodescribePlusTraceableCause` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/testing/testing.go:447: testing.tRunner` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit` + "\n"
	var cwd, _ = os.Getwd()
	actual := err.Error()
	actual = strings.Replace(actual, cwd, "", -1) // strip the local build path
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}
	t.Logf("this is what errors with causes that have stacktraces look like :D\n>>>\n%s\n<<<\n", actual)
}

func TestAutodescribePlusTraceableCauseDoubleTrouble(t *testing.T) {
	type Woop struct {
		AutodescribingError
		CauseableError
		Wonk string
	}
	type Boop struct {
		AutodescribingError
		CauseableError
		TraceableError
	}
	err := New(&Woop{
		Wonk: "Bonk",
		CauseableError: CauseableError{
			New(&Boop{
				CauseableError: CauseableError{
					New(&Boop{}),
				},
			}),
		},
	})
	expect := `Error[meep.Woop]: Wonk="Bonk";` + "\n"
	expect += "\t" + `Caused by: Error[meep.Boop]: ` + "\n"   // trailing space questionable
	expect += "\t\t" + `Caused by: Error[meep.Boop]: ` + "\n" // trailing space questionable
	expect += "\t\t\t" + `Stack trace:` + "\n"
	expect += "\t\t\t\t" + `·> /autodescriber_test.go:108: meep.TestAutodescribePlusTraceableCauseDoubleTrouble` + "\n"
	expect += "\t\t\t\t" + `·> /usr/local/go/src/testing/testing.go:447: testing.tRunner` + "\n"
	expect += "\t\t\t\t" + `·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit` + "\n"
	expect += "\t\t" + `Stack trace:` + "\n"
	expect += "\t\t\t" + `·> /autodescriber_test.go:110: meep.TestAutodescribePlusTraceableCauseDoubleTrouble` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/testing/testing.go:447: testing.tRunner` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit` + "\n"
	var cwd, _ = os.Getwd()
	actual := err.Error()
	actual = strings.Replace(actual, cwd, "", -1) // strip the local build path
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}

	actual = strings.Replace(actual, "\t", "\\t\t", -1)
	actual = strings.Replace(actual, "\n", "\\n\n", -1)
	t.Logf("this is what errors with causes that have stacktraces look like :D\n>>>\n%s\n<<<\n", actual)
}
<commit_msg>Add test of many fields.<commit_after>package meep

import (
	"fmt"
	"os"
	"strings"
	"testing"
)

func TestReacharound(t *testing.T) {
	type Woop struct {
		AutodescribingError
		Wonk string
	}
	var err error
	err = &Woop{Wonk: "Bonk"}
	err = New(err)
	woop := err.(*Woop)
	if woop.Wonk != "Bonk" {
		t.Errorf("Bonk somehow became %q", woop.Wonk)
	}
	if woop.AutodescribingError.self == nil {
		t.Errorf("No impact")
	}
	if woop.AutodescribingError.self != err {
		t.Errorf("Drat")
	}
}

func TestAutodescribeSimple(t *testing.T) {
	type Woop struct {
		AutodescribingError
		Wonk string
	}
	err := New(&Woop{Wonk: "Bonk"})
	expect := `Error[meep.Woop]: Wonk="Bonk";`
	if expect != err.Error() {
		t.Errorf("expected %q, got %q", expect, err.Error())
	}
}

func TestAutodescribePlusCause(t *testing.T) {
	type Woop struct {
		AutodescribingError
		CauseableError
		Wonk string
	}
	err := New(&Woop{
		Wonk:           "Bonk",
		CauseableError: CauseableError{fmt.Errorf("lecause")},
	})
	expect := `Error[meep.Woop]: Wonk="Bonk";` + "\n"
	expect += "\t" + `Caused by: lecause` + "\n"
	actual := err.Error()
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}
	t.Logf("this is what a very basic error with a nested cause looks like:\n>>>\n%s\n<<<\n", actual)
}

func TestAutodescribePlusTraceableCause(t *testing.T) {
	type Woop struct {
		AutodescribingError
		CauseableError
		Wonk string
	}
	type Boop struct {
		TraceableError
		AutodescribingError
	}
	err := New(&Woop{
		Wonk: "Bonk",
		CauseableError: CauseableError{
			New(&Boop{}),
		},
	})
	expect := `Error[meep.Woop]: Wonk="Bonk";` + "\n"
	expect += "\t" + `Caused by: Error[meep.Boop]: ` + "\n" // trailing space questionable
	expect += "\t\t" + `Stack trace:` + "\n"
	expect += "\t\t\t" + `·> /autodescriber_test.go:74: meep.TestAutodescribePlusTraceableCause` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/testing/testing.go:447: testing.tRunner` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit` + "\n"
	var cwd, _ = os.Getwd()
	actual := err.Error()
	actual = strings.Replace(actual, cwd, "", -1) // strip the local build path
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}
	t.Logf("this is what errors with causes that have stacktraces look like :D\n>>>\n%s\n<<<\n", actual)
}

func TestAutodescribePlusTraceableCauseDoubleTrouble(t *testing.T) {
	type Woop struct {
		AutodescribingError
		CauseableError
		Wonk string
	}
	type Boop struct {
		AutodescribingError
		CauseableError
		TraceableError
	}
	err := New(&Woop{
		Wonk: "Bonk",
		CauseableError: CauseableError{
			New(&Boop{
				CauseableError: CauseableError{
					New(&Boop{}),
				},
			}),
		},
	})
	expect := `Error[meep.Woop]: Wonk="Bonk";` + "\n"
	expect += "\t" + `Caused by: Error[meep.Boop]: ` + "\n"   // trailing space questionable
	expect += "\t\t" + `Caused by: Error[meep.Boop]: ` + "\n" // trailing space questionable
	expect += "\t\t\t" + `Stack trace:` + "\n"
	expect += "\t\t\t\t" + `·> /autodescriber_test.go:108: meep.TestAutodescribePlusTraceableCauseDoubleTrouble` + "\n"
	expect += "\t\t\t\t" + `·> /usr/local/go/src/testing/testing.go:447: testing.tRunner` + "\n"
	expect += "\t\t\t\t" + `·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit` + "\n"
	expect += "\t\t" + `Stack trace:` + "\n"
	expect += "\t\t\t" + `·> /autodescriber_test.go:110: meep.TestAutodescribePlusTraceableCauseDoubleTrouble` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/testing/testing.go:447: testing.tRunner` + "\n"
	expect += "\t\t\t" + `·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit` + "\n"
	var cwd, _ = os.Getwd()
	actual := err.Error()
	actual = strings.Replace(actual, cwd, "", -1) // strip the local build path
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}

	actual = strings.Replace(actual, "\t", "\\t\t", -1)
	actual = strings.Replace(actual, "\n", "\\n\n", -1)
	t.Logf("this is what errors with causes that have stacktraces look like :D\n>>>\n%s\n<<<\n", actual)
}

func TestAutodescribeManyFields(t *testing.T) {
	type ErrBananaPancakes struct {
		AutodescribingError
		Alpha string
		Beta  int
		Gamma interface{}
		Delta string
	}
	err := New(&ErrBananaPancakes{
		Alpha: "unO",
		Beta:  1,
		Gamma: struct{}{},
		Delta: "ca\ttorce",
	})
	expect := `Error[meep.ErrBananaPancakes]: Alpha="unO";Beta=1;Gamma=struct {}{};Delta="ca\ttorce";`
	actual := err.Error()
	if expect != actual {
		t.Errorf("mismatch:\n  expected %q\n       got %q", expect, actual)
	}
}
<|endoftext|>