<commit_before>package mains

import (
	"context"
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/yuin/gopher-lua"

	"github.com/zetamatta/nyagos/completion"
)

func complete4getter(L Lua) int {
	key, ok := L.Get(1).(lua.LString)
	if !ok {
		return lerror(L, "nyagos.complete_for[] too few arguments")
	}
	if _, ok := completion.CustomCompletion[string(key)]; ok {
		L.Push(lua.LTrue)
	} else {
		L.Push(lua.LNil)
	}
	return 1
}

type customCompleter struct {
	Func *lua.LFunction
	Name string
}

func (c *customCompleter) String() string {
	return c.Name
}

func (c *customCompleter) Complete(ctx context.Context, args []string) ([]completion.Element, error) {
	LL, ok := ctx.Value(luaKey).(Lua)
	if !ok {
		return nil, errors.New("completion.CustomCompletion: no lua instance")
	}
	tbl := LL.NewTable()
	for i, arg1 := range args {
		LL.SetTable(tbl, lua.LNumber(i+1), lua.LString(arg1))
	}

	defer setContext(LL, getContext(LL))
	setContext(LL, ctx)

	LL.Push(c.Func)
	LL.Push(tbl)
	if err := LL.PCall(1, 1, nil); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	result := LL.Get(-1)
	if rtbl, ok := result.(*lua.LTable); ok {
		r := make([]completion.Element, 0, rtbl.Len())
		var base string
		if len(args) > 0 {
			base = strings.ToUpper(args[len(args)-1])
		}
		rtbl.ForEach(func(_ lua.LValue, val lua.LValue) {
			if _s, ok := val.(lua.LString); ok {
				s := string(_s)
				if strings.HasPrefix(strings.ToUpper(s), base) {
					r = append(r, completion.Element1(s))
				}
			}
		})
		return r, nil
	} else if s, ok := result.(lua.LString); ok {
		list := strings.Split(string(s), "\n")
		r := make([]completion.Element, 0, len(list))
		for _, r1 := range list {
			r = append(r, completion.Element1(string(r1)))
		}
		return r, nil
	} else {
		return nil, errors.New("not a table or string")
	}
}

func complete4setter(L Lua) int {
	key, ok := L.Get(-2).(lua.LString)
	if !ok {
		return lerror(L, "nyagos.complete_for[] too few arguments")
	}
	val := L.Get(-1)
	if val == lua.LNil {
		delete(completion.CustomCompletion, string(key))
		return 0
	}
	f, ok := val.(*lua.LFunction)
	if !ok {
		return lerror(L, "nyagos.complete_for[]= not function")
	}
	completion.CustomCompletion[string(key)] = &customCompleter{
		Func: f,
		Name: string(key),
	}
	return 1
}
<commit_msg>Made `print(nyagos.complete_for["COMMAND"])` work<commit_after>package mains

import (
	"context"
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/yuin/gopher-lua"

	"github.com/zetamatta/nyagos/completion"
)

func complete4getter(L Lua) int {
	key, ok := L.Get(-1).(lua.LString)
	if !ok {
		return lerror(L, "nyagos.complete_for[] too few arguments")
	}
	if p, ok := completion.CustomCompletion[string(key)]; ok {
		L.Push(lua.LString(p.String()))
	} else {
		L.Push(lua.LNil)
	}
	return 1
}

type customCompleter struct {
	Func *lua.LFunction
	Name string
}

func (c *customCompleter) String() string {
	return c.Name
}

func (c *customCompleter) Complete(ctx context.Context, args []string) ([]completion.Element, error) {
	LL, ok := ctx.Value(luaKey).(Lua)
	if !ok {
		return nil, errors.New("completion.CustomCompletion: no lua instance")
	}
	tbl := LL.NewTable()
	for i, arg1 := range args {
		LL.SetTable(tbl, lua.LNumber(i+1), lua.LString(arg1))
	}

	defer setContext(LL, getContext(LL))
	setContext(LL, ctx)

	LL.Push(c.Func)
	LL.Push(tbl)
	if err := LL.PCall(1, 1, nil); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	result := LL.Get(-1)
	if rtbl, ok := result.(*lua.LTable); ok {
		r := make([]completion.Element, 0, rtbl.Len())
		var base string
		if len(args) > 0 {
			base = strings.ToUpper(args[len(args)-1])
		}
		rtbl.ForEach(func(_ lua.LValue, val lua.LValue) {
			if _s, ok := val.(lua.LString); ok {
				s := string(_s)
				if strings.HasPrefix(strings.ToUpper(s), base) {
					r = append(r, completion.Element1(s))
				}
			}
		})
		return r, nil
	} else if s, ok := result.(lua.LString); ok {
		list := strings.Split(string(s), "\n")
		r := make([]completion.Element, 0, len(list))
		for _, r1 := range list {
			r = append(r, completion.Element1(string(r1)))
		}
		return r, nil
	} else {
		return nil, errors.New("not a table or string")
	}
}

func complete4setter(L Lua) int {
	key, ok := L.Get(-2).(lua.LString)
	if !ok {
		return lerror(L, "nyagos.complete_for[] too few arguments")
	}
	val := L.Get(-1)
	if val == lua.LNil {
		delete(completion.CustomCompletion, string(key))
		return 0
	}
	f, ok := val.(*lua.LFunction)
	if !ok {
		return lerror(L, "nyagos.complete_for[]= not function")
	}
	completion.CustomCompletion[string(key)] = &customCompleter{
		Func: f,
		Name: string(key),
	}
	return 1
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mozilla-services/reaper/events"
	"github.com/mozilla-services/reaper/filters"
	"github.com/mozilla-services/reaper/reapable"
	log "github.com/mozilla-services/reaper/reaperlog"
)

var config *AWSConfig

type AWSConfig struct {
	Notifications events.NotificationsConfig
	HTTP          events.HTTPConfig
	Regions       []string
	WhitelistTag  string
	DefaultOwner  string
	DryRun        bool
}

func NewAWSConfig() *AWSConfig {
	return &AWSConfig{}
}

func SetAWSConfig(c *AWSConfig) {
	config = c
}

// convenience function that returns a map of instances in ASGs
func AllASGInstanceIds(as []AutoScalingGroup) map[reapable.Region]map[reapable.ID]bool {
	// maps region to id to bool
	inASG := make(map[reapable.Region]map[reapable.ID]bool)
	for _, region := range config.Regions {
		inASG[reapable.Region(region)] = make(map[reapable.ID]bool)
	}
	for _, a := range as {
		for _, instanceID := range a.Instances {
			// add the instance to the map
			inASG[a.Region][instanceID] = true
		}
	}
	return inASG
}

// AllAutoScalingGroups describes every AutoScalingGroup in the requested regions
// *AutoScalingGroups are created for every *autoscaling.AutoScalingGroup
// and are passed to a channel
func AllAutoScalingGroups() chan *AutoScalingGroup {
	ch := make(chan *AutoScalingGroup)
	// done keeps track of the number of finished regions
	done := 0

	for _, region := range config.Regions {
		go func(region string) {
			api := autoscaling.New(&aws.Config{Region: region})
			err := api.DescribeAutoScalingGroupsPages(&autoscaling.DescribeAutoScalingGroupsInput{}, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
				for _, asg := range resp.AutoScalingGroups {
					ch <- NewAutoScalingGroup(region, asg)
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					done++
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
			}
		}(region)
	}
	go func() {
		for {
			// if all regions are done, close the channel
			if done == len(config.Regions) {
				close(ch)
				return
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()
	return ch
}

// AllInstances describes every instance in the requested regions
// *Instances are created for each *ec2.Instance
// and are passed to a channel
func AllInstances() chan *Instance {
	ch := make(chan *Instance)
	// done keeps track of the number of finished regions
	done := 0

	for _, region := range config.Regions {
		go func(region string) {
			api := ec2.New(&aws.Config{Region: region})
			// DescribeInstancesPages does autopagination
			err := api.DescribeInstancesPages(&ec2.DescribeInstancesInput{}, func(resp *ec2.DescribeInstancesOutput, lastPage bool) bool {
				for _, res := range resp.Reservations {
					for _, instance := range res.Instances {
						ch <- NewInstance(region, instance)
					}
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					done++
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
			}
		}(region)
	}
	go func() {
		for {
			// if all regions are done, close the channel
			if done == len(config.Regions) {
				close(ch)
				return
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()
	return ch
}

func AllSnapshots() []filters.Filterable {
	regions := config.Regions

	// waitgroup for goroutines
	var wg sync.WaitGroup

	// channel for creating SecurityGroups
	in := make(chan *Snapshot)

	for _, region := range regions {
		wg.Add(1)

		sum := 0

		// goroutine per region to fetch all security groups
		go func(region string) {
			defer wg.Done()
			api := ec2.New(&aws.Config{Region: region})

			// TODO: nextToken paging
			input := &ec2.DescribeSnapshotsInput{}
			resp, err := api.DescribeSnapshots(input)
			if err != nil {
				// TODO: wee
			}

			for _, v := range resp.Snapshots {
				sum += 1
				in <- NewSnapshot(region, v)
			}
		}(region)
	}
	// aggregate
	var snapshots []filters.Filterable
	go func() {
		for s := range in {
			// Reapables[s.Region][s.ID] = s
			snapshots = append(snapshots, s)
		}
	}()

	// synchronous wait for all goroutines in wg to be done
	wg.Wait()

	// done with the channel
	close(in)

	log.Info("Found %d total snapshots.", len(snapshots))
	return snapshots
}
func AllVolumes() Volumes {
	regions := config.Regions

	// waitgroup for goroutines
	var wg sync.WaitGroup

	// channel for creating SecurityGroups
	in := make(chan *Volume)

	for _, region := range regions {
		wg.Add(1)

		sum := 0

		// goroutine per region to fetch all security groups
		go func(region string) {
			defer wg.Done()
			api := ec2.New(&aws.Config{Region: region})

			// TODO: nextToken paging
			input := &ec2.DescribeVolumesInput{}
			resp, err := api.DescribeVolumes(input)
			if err != nil {
				// TODO: wee
			}

			for _, v := range resp.Volumes {
				sum += 1
				in <- NewVolume(region, v)
			}

			log.Info(fmt.Sprintf("Found %d total volumes in %s", sum, region))
		}(region)
	}
	// aggregate
	var volumes Volumes
	go func() {
		for v := range in {
			// Reapables[v.Region][v.ID] = v
			volumes = append(volumes, v)
		}
	}()

	// synchronous wait for all goroutines in wg to be done
	wg.Wait()

	// done with the channel
	close(in)

	log.Info("Found %d total snapshots.", len(volumes))
	return volumes
}
func AllSecurityGroups() SecurityGroups {
	regions := config.Regions

	// waitgroup for goroutines
	var wg sync.WaitGroup

	// channel for creating SecurityGroups
	in := make(chan *SecurityGroup)

	for _, region := range regions {
		wg.Add(1)

		sum := 0

		// goroutine per region to fetch all security groups
		go func(region string) {
			defer wg.Done()
			api := ec2.New(&aws.Config{Region: region})

			// TODO: nextToken paging
			input := &ec2.DescribeSecurityGroupsInput{}
			resp, err := api.DescribeSecurityGroups(input)
			if err != nil {
				// TODO: wee
			}

			for _, sg := range resp.SecurityGroups {
				sum += 1
				in <- NewSecurityGroup(region, sg)
			}

			log.Info(fmt.Sprintf("Found %d total security groups in %s", sum, region))
		}(region)
	}
	// aggregate
	var securityGroups SecurityGroups
	go func() {
		for sg := range in {
			// Reapables[sg.Region][sg.ID] = sg
			securityGroups = append(securityGroups, sg)
		}
	}()

	// synchronous wait for all goroutines in wg to be done
	wg.Wait()

	// done with the channel
	close(in)

	log.Info("Found %d total security groups.", len(securityGroups))
	return securityGroups
}
<commit_msg>using waitgroup (duh)<commit_after>package aws

import (
	"fmt"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/mozilla-services/reaper/events"
	"github.com/mozilla-services/reaper/filters"
	"github.com/mozilla-services/reaper/reapable"
	log "github.com/mozilla-services/reaper/reaperlog"
)

var config *AWSConfig

type AWSConfig struct {
	Notifications events.NotificationsConfig
	HTTP          events.HTTPConfig
	Regions       []string
	WhitelistTag  string
	DefaultOwner  string
	DryRun        bool
}

func NewAWSConfig() *AWSConfig {
	return &AWSConfig{}
}

func SetAWSConfig(c *AWSConfig) {
	config = c
}

// convenience function that returns a map of instances in ASGs
func AllASGInstanceIds(as []AutoScalingGroup) map[reapable.Region]map[reapable.ID]bool {
	// maps region to id to bool
	inASG := make(map[reapable.Region]map[reapable.ID]bool)
	for _, region := range config.Regions {
		inASG[reapable.Region(region)] = make(map[reapable.ID]bool)
	}
	for _, a := range as {
		for _, instanceID := range a.Instances {
			// add the instance to the map
			inASG[a.Region][instanceID] = true
		}
	}
	return inASG
}

// AllAutoScalingGroups describes every AutoScalingGroup in the requested regions
// *AutoScalingGroups are created for every *autoscaling.AutoScalingGroup
// and are passed to a channel
func AllAutoScalingGroups() chan *AutoScalingGroup {
	ch := make(chan *AutoScalingGroup)
	wg := sync.WaitGroup{}

	for _, region := range config.Regions {
		go func(region string) {
			wg.Add(1)
			api := autoscaling.New(&aws.Config{Region: region})
			err := api.DescribeAutoScalingGroupsPages(&autoscaling.DescribeAutoScalingGroupsInput{}, func(resp *autoscaling.DescribeAutoScalingGroupsOutput, lastPage bool) bool {
				for _, asg := range resp.AutoScalingGroups {
					ch <- NewAutoScalingGroup(region, asg)
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					wg.Done()
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
			}
		}(region)
	}
	go func() {
		wg.Wait()
		close(ch)

	}()
	return ch
}

// AllInstances describes every instance in the requested regions
// *Instances are created for each *ec2.Instance
// and are passed to a channel
func AllInstances() chan *Instance {
	ch := make(chan *Instance)
	wg := sync.WaitGroup{}

	for _, region := range config.Regions {
		go func(region string) {
			wg.Add(1)
			api := ec2.New(&aws.Config{Region: region})
			// DescribeInstancesPages does autopagination
			err := api.DescribeInstancesPages(&ec2.DescribeInstancesInput{}, func(resp *ec2.DescribeInstancesOutput, lastPage bool) bool {
				for _, res := range resp.Reservations {
					for _, instance := range res.Instances {
						ch <- NewInstance(region, instance)
					}
				}
				// if we are at the last page, we should not continue
				// the return value of this func is "shouldContinue"
				if lastPage {
					wg.Done()
				}
				return true
			})
			if err != nil {
				// probably should do something here...
				log.Error(err.Error())
			}
		}(region)
	}
	go func() {
		wg.Wait()
		close(ch)
	}()
	return ch
}

func AllSnapshots() []filters.Filterable {
	regions := config.Regions

	// waitgroup for goroutines
	var wg sync.WaitGroup

	// channel for creating SecurityGroups
	in := make(chan *Snapshot)

	for _, region := range regions {
		wg.Add(1)

		sum := 0

		// goroutine per region to fetch all security groups
		go func(region string) {
			defer wg.Done()
			api := ec2.New(&aws.Config{Region: region})

			// TODO: nextToken paging
			input := &ec2.DescribeSnapshotsInput{}
			resp, err := api.DescribeSnapshots(input)
			if err != nil {
				// TODO: wee
			}

			for _, v := range resp.Snapshots {
				sum += 1
				in <- NewSnapshot(region, v)
			}
		}(region)
	}
	// aggregate
	var snapshots []filters.Filterable
	go func() {
		for s := range in {
			// Reapables[s.Region][s.ID] = s
			snapshots = append(snapshots, s)
		}
	}()

	// synchronous wait for all goroutines in wg to be done
	wg.Wait()

	// done with the channel
	close(in)

	log.Info("Found %d total snapshots.", len(snapshots))
	return snapshots
}
func AllVolumes() Volumes {
	regions := config.Regions

	// waitgroup for goroutines
	var wg sync.WaitGroup

	// channel for creating SecurityGroups
	in := make(chan *Volume)

	for _, region := range regions {
		wg.Add(1)

		sum := 0

		// goroutine per region to fetch all security groups
		go func(region string) {
			defer wg.Done()
			api := ec2.New(&aws.Config{Region: region})

			// TODO: nextToken paging
			input := &ec2.DescribeVolumesInput{}
			resp, err := api.DescribeVolumes(input)
			if err != nil {
				// TODO: wee
			}

			for _, v := range resp.Volumes {
				sum += 1
				in <- NewVolume(region, v)
			}

			log.Info(fmt.Sprintf("Found %d total volumes in %s", sum, region))
		}(region)
	}
	// aggregate
	var volumes Volumes
	go func() {
		for v := range in {
			// Reapables[v.Region][v.ID] = v
			volumes = append(volumes, v)
		}
	}()

	// synchronous wait for all goroutines in wg to be done
	wg.Wait()

	// done with the channel
	close(in)

	log.Info("Found %d total snapshots.", len(volumes))
	return volumes
}
func AllSecurityGroups() SecurityGroups {
	regions := config.Regions

	// waitgroup for goroutines
	var wg sync.WaitGroup

	// channel for creating SecurityGroups
	in := make(chan *SecurityGroup)

	for _, region := range regions {
		wg.Add(1)

		sum := 0

		// goroutine per region to fetch all security groups
		go func(region string) {
			defer wg.Done()
			api := ec2.New(&aws.Config{Region: region})

			// TODO: nextToken paging
			input := &ec2.DescribeSecurityGroupsInput{}
			resp, err := api.DescribeSecurityGroups(input)
			if err != nil {
				// TODO: wee
			}

			for _, sg := range resp.SecurityGroups {
				sum += 1
				in <- NewSecurityGroup(region, sg)
			}

			log.Info(fmt.Sprintf("Found %d total security groups in %s", sum, region))
		}(region)
	}
	// aggregate
	var securityGroups SecurityGroups
	go func() {
		for sg := range in {
			// Reapables[sg.Region][sg.ID] = sg
			securityGroups = append(securityGroups, sg)
		}
	}()

	// synchronous wait for all goroutines in wg to be done
	wg.Wait()

	// done with the channel
	close(in)

	log.Info("Found %d total security groups.", len(securityGroups))
	return securityGroups
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"
	"log"

	am "github.com/Ariemeth/quantum-pulse/assets"

	"github.com/go-gl/gl/v4.1-core/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

const windowWidth = 800
const windowHeight = 600

// Engine constitutes the rendering engine which creates and initializes the rendering system.
type Engine struct {
	window       *glfw.Window
	assets       *am.AssetManager
	scenes       map[string]Scene
	currentScene Scene
}

// Init is called to initialize glfw and opengl
func (e *Engine) Init() {
	if err := glfw.Init(); err != nil {
		log.Fatalln("failed to initialize glfw:", err)
	}

	e.window = createWindow()

	initGL()

	e.assets = am.NewAssetManager()
	e.scenes = make(map[string]Scene)

}

// Run starts and runs the main engine loop
func (e *Engine) Run() {
	defer glfw.Terminate()

	previousTime := glfw.GetTime()

	for !e.window.ShouldClose() {
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

		//Update
		time := glfw.GetTime()
		elapsed := time - previousTime
		previousTime = time

		e.currentScene.Update(elapsed)

		// Render
		e.currentScene.Render()

		// Maintenance
		e.window.SwapBuffers()
		glfw.PollEvents()
	}
}

// AddScene adds a scene to the engine.
func (e *Engine) AddScene(scene Scene, name string) {
	e.scenes[name] = scene
}

// LoadScene switches the current scene to the one specificed if it has already been added.
func (e *Engine) LoadScene(name string) {
	scene, status := e.scenes[name]
	if status {
		e.currentScene = scene
	}
}

// LoadSceneFile loads a scene from a file but does not make it the current scene. You
// must still call LoadScene with the scene id to load it as the current scene.  LoadSceneFile
// should not be called before Init is called.
func (e *Engine) LoadSceneFile(fileName string) string {
	scene := NewScene(fileName, e.assets)
	e.AddScene(scene, scene.ID())
	return scene.ID()
}

func createWindow() *glfw.Window {
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 4)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)
	glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
	glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

	window, err := glfw.CreateWindow(windowWidth, windowHeight, "Frame Assault", nil, nil)
	if err != nil {
		panic(err)
	}

	window.MakeContextCurrent()
	window.SetKeyCallback(onKey)
	window.SetMouseButtonCallback(onMouseButton)
	window.SetCloseCallback(onClose)
	window.SetScrollCallback(onScroll)
	window.SetCursorPosCallback(onCursorPos)

	return window
}

func initGL() string {
	// Initialize Glow
	if err := gl.Init(); err != nil {
		panic(err)
	}

	version := gl.GoStr(gl.GetString(gl.VERSION))
	fmt.Println("OpenGL version", version)
	fmt.Println("OpenGl shading version", gl.GoStr(gl.GetString(gl.SHADING_LANGUAGE_VERSION)))
	fmt.Println("OpenGl renderer", gl.GoStr(gl.GetString(gl.RENDERER)))

	// Configure global settings
	gl.Enable(gl.DEPTH_TEST)
	gl.DepthFunc(gl.LESS)
	gl.ClearColor(0.5, 0.5, 0.5, 1.0)

	return version
}

func onKey(window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
	if action != glfw.Press {
		return
	}

	switch glfw.Key(k) {
	case glfw.KeyEscape:
		window.SetShouldClose(true)
	default:
		return
	}
}

func onMouseButton(window *glfw.Window, b glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
	if action != glfw.Press {
		return
	}

	switch glfw.MouseButton(b) {
	case glfw.MouseButtonLeft:
		return
	case glfw.MouseButtonRight:
		return
	default:
		return
	}
}

func onClose(window *glfw.Window) {
	window.SetShouldClose(true)
}

func onScroll(window *glfw.Window, xoff float64, yoff float64) {

}

func onCursorPos(window *glfw.Window, xpos float64, ypos float64) {

}
<commit_msg> Moved LockOsThread to engine The client is no longer required to call LockOSThread.  This is now handled by the init method in the engine utilizing a main func queue to allow functions that need to be run on the main thread to be queued.<commit_after>package engine

import (
	"errors"
	"fmt"
	"log"

	am "github.com/Ariemeth/quantum-pulse/assets"

	"runtime"

	"github.com/go-gl/gl/v4.1-core/gl"
	"github.com/go-gl/glfw/v3.1/glfw"
)

const windowWidth = 800
const windowHeight = 600

var mainQueue = make(chan func())

// Engine constitutes the rendering engine which creates and initializes the rendering system.
type Engine struct {
	window       *glfw.Window
	assets       *am.AssetManager
	scenes       map[string]Scene
	currentScene Scene
}

func init() {
	go processMainFuncs()
}

// processMainFuncs proccess functions that have been added to the mainQueue on the main os thread.
func processMainFuncs() {
	runtime.LockOSThread()

	for {
		f := <-mainQueue
		f()
	}
}

// RunOnMain takes a funcion adds it to a queue that will ensure it is run on the main thread.
func RunOnMain(f func()) {
	done := make(chan bool, 1)
	mainQueue <- func() {
		f()
		done <- true
	}
	<-done
}

// Init is called to initialize glfw and opengl
func (e *Engine) Init() {
	RunOnMain(func() {
		if err := glfw.Init(); err != nil {
			log.Fatalln("failed to initialize glfw:", err)
		}

		e.window = createWindow()

		initGL()

		e.assets = am.NewAssetManager()
		e.scenes = make(map[string]Scene)
	})
}

// Run starts and runs the main engine loop
func (e *Engine) Run() {
	defer RunOnMain(func() { glfw.Terminate() })

	previousTime := glfw.GetTime()

	for !e.window.ShouldClose() {

		//Update
		time := glfw.GetTime()
		elapsed := time - previousTime
		previousTime = time

		e.currentScene.Update(elapsed)

		RunOnMain(func() {
			gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

			// Render
			e.currentScene.Render()

			// Maintenance
			e.window.SwapBuffers()
			glfw.PollEvents()
		})
	}
}

// AddScene adds a scene to the engine.
func (e *Engine) AddScene(scene Scene, name string) {
	e.scenes[name] = scene
}

// LoadScene switches the current scene to the one specificed if it has already been added.
func (e *Engine) LoadScene(name string) {
	scene, status := e.scenes[name]
	if status {
		e.currentScene = scene
	}
}

// LoadSceneFile loads a scene from a file but does not make it the current scene. You
// must still call LoadScene with the scene id to load it as the current scene.  LoadSceneFile
// should not be called before Init is called.
func (e *Engine) LoadSceneFile(fileName string) (string, error) {
	var scene Scene
	RunOnMain(func() { scene = NewScene(fileName, e.assets) })
	if scene != nil {
		e.AddScene(scene, scene.ID())
		return scene.ID(), nil
	}
	return "", errors.New("Unable to load scene file")
}

func createWindow() *glfw.Window {
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 4)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)
	glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
	glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

	window, err := glfw.CreateWindow(windowWidth, windowHeight, "Frame Assault", nil, nil)
	if err != nil {
		panic(err)
	}

	window.MakeContextCurrent()
	window.SetKeyCallback(onKey)
	window.SetMouseButtonCallback(onMouseButton)
	window.SetCloseCallback(onClose)
	window.SetScrollCallback(onScroll)
	window.SetCursorPosCallback(onCursorPos)

	return window
}

func initGL() string {
	// Initialize Glow
	if err := gl.Init(); err != nil {
		panic(err)
	}

	version := gl.GoStr(gl.GetString(gl.VERSION))
	fmt.Println("OpenGL version", version)
	fmt.Println("OpenGl shading version", gl.GoStr(gl.GetString(gl.SHADING_LANGUAGE_VERSION)))
	fmt.Println("OpenGl renderer", gl.GoStr(gl.GetString(gl.RENDERER)))

	// Configure global settings
	gl.Enable(gl.DEPTH_TEST)
	gl.DepthFunc(gl.LESS)
	gl.ClearColor(0.5, 0.5, 0.5, 1.0)

	return version
}

func onKey(window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
	if action != glfw.Press {
		return
	}

	switch glfw.Key(k) {
	case glfw.KeyEscape:
		window.SetShouldClose(true)
	default:
		return
	}
}

func onMouseButton(window *glfw.Window, b glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
	if action != glfw.Press {
		return
	}

	switch glfw.MouseButton(b) {
	case glfw.MouseButtonLeft:
		return
	case glfw.MouseButtonRight:
		return
	default:
		return
	}
}

func onClose(window *glfw.Window) {
	window.SetShouldClose(true)
}

func onScroll(window *glfw.Window, xoff float64, yoff float64) {

}

func onCursorPos(window *glfw.Window, xpos float64, ypos float64) {

}
<|endoftext|>


<commit_before>package dry

import (
	"bytes"
	"fmt"
)

// PanicIfErr panics with a stack trace if any
// of the passed values is a non nil error
func PanicIfErr(values ...interface{}) {
	for _, v := range values {
		if err, ok := v.(error); ok {
			if err != nil {
				panic(fmt.Errorf("Panicking because of error: %s\nAt:\n%s\n", err, StackTrace(2)))
			}
		}
	}
}

// Nop is a dummy function that can be called in source files where
// other debug functions are constantly added and removed.
// That way import "github.com/ungerik/go-quick" won't cause an error when
// no other debug function is currently used.
// Arbitrary objects can be passed as arguments to avoid "declared and not used"
// error messages when commenting code out and in.
// The result is a nil interface{} dummy value.
func Nop(dummiesIn ...interface{}) (dummyOut interface{}) {
	return nil
}

// Error returns r as error, converting it when necessary
func AsError(r interface{}) error {
	if r == nil {
		return nil
	}
	if err, _ := r.(error); err != nil {
		return err
	} else {
		return fmt.Errorf("%v", r)
	}
}

// Returns the first non nil error, or nil
func FirstError(errs ...error) error {
	for _, err := range errs {
		if err != nil {
			return err
		}
	}
	return nil
}

// Returns the last non nil error, or nil
func LastError(errs ...error) error {
	for i := len(errs) - 1; i >= 0; i-- {
		err := errs[i]
		if err != nil {
			return err
		}
	}
	return nil
}

// AsErrorList checks if err is already an ErrorList
// and returns it if this is the case.
// Else an ErrorList with err as element is created.
// Useful if a function potentially returns an ErrorList as error
// and you want to avoid creating nested ErrorLists.
func AsErrorList(err error) ErrorList {
	if list, ok := err.(ErrorList); ok {
		return list
	}
	return ErrorList{err}
}

// ErrorList holds a slice of errors
type ErrorList []error

// Error calls fmt.Println for of every error in the list
// and returns the concernated text.
func (list ErrorList) Error() string {
	if len(list) == 0 {
		return "Empty ErrorList"
	}
	var buf bytes.Buffer
	for _, err := range list {
		fmt.Fprintln(&buf, err)
	}
	return buf.String()
}

func (list ErrorList) First() error {
	if len(list) == 0 {
		return nil
	}
	return list[0]
}

func (list ErrorList) Last() error {
	if len(list) == 0 {
		return nil
	}
	return list[len(list)-1]
}
<commit_msg>NewErrorList, ErrorList.Collect<commit_after>package dry

import (
	"bytes"
	"fmt"
)

// PanicIfErr panics with a stack trace if any
// of the passed values is a non nil error
func PanicIfErr(values ...interface{}) {
	for _, v := range values {
		if err, _ := v.(error); err != nil {
			panic(fmt.Errorf("Panicking because of error: %s\nAt:\n%s\n", err, StackTrace(2)))
		}
	}
}

// Nop is a dummy function that can be called in source files where
// other debug functions are constantly added and removed.
// That way import "github.com/ungerik/go-quick" won't cause an error when
// no other debug function is currently used.
// Arbitrary objects can be passed as arguments to avoid "declared and not used"
// error messages when commenting code out and in.
// The result is a nil interface{} dummy value.
func Nop(dummiesIn ...interface{}) (dummyOut interface{}) {
	return nil
}

// Error returns r as error, converting it when necessary
func AsError(r interface{}) error {
	if r == nil {
		return nil
	}
	if err, _ := r.(error); err != nil {
		return err
	} else {
		return fmt.Errorf("%v", r)
	}
}

// Returns the first non nil error, or nil
func FirstError(errs ...error) error {
	for _, err := range errs {
		if err != nil {
			return err
		}
	}
	return nil
}

// Returns the last non nil error, or nil
func LastError(errs ...error) error {
	for i := len(errs) - 1; i >= 0; i-- {
		err := errs[i]
		if err != nil {
			return err
		}
	}
	return nil
}

// AsErrorList checks if err is already an ErrorList
// and returns it if this is the case.
// Else an ErrorList with err as element is created.
// Useful if a function potentially returns an ErrorList as error
// and you want to avoid creating nested ErrorLists.
func AsErrorList(err error) ErrorList {
	if list, ok := err.(ErrorList); ok {
		return list
	}
	return ErrorList{err}
}

// ErrorList holds a slice of errors
type ErrorList []error

// NewErrorList returns an ErrorList where Collect has been called for args.
// The returned list will be nil if there was no non nil error in args.
// Note that alle methods of ErrorList can be called with a nil ErrorList.
func NewErrorList(args ...interface{}) (list ErrorList) {
	list.Collect(args...)
	return list
}

// Error calls fmt.Println for of every error in the list
// and returns the concernated text.
// Can be called for a nil ErrorList.
func (list ErrorList) Error() string {
	if len(list) == 0 {
		return "Empty ErrorList"
	}
	var buf bytes.Buffer
	for _, err := range list {
		fmt.Fprintln(&buf, err)
	}
	return buf.String()
}

// Err returns the list if it is not empty,
// or nil if it is empty.
// Can be called for a nil ErrorList.
func (list ErrorList) Err() error {
	if len(list) == 0 {
		return nil
	}
	return list
}

// First returns the first error in the list or nil.
// Can be called for a nil ErrorList.
func (list ErrorList) First() error {
	if len(list) == 0 {
		return nil
	}
	return list[0]
}

// Last returns the last error in the list or nil.
// Can be called for a nil ErrorList.
func (list ErrorList) Last() error {
	if len(list) == 0 {
		return nil
	}
	return list[len(list)-1]
}

// Collect adds any non nil errors in args to the list.
func (list *ErrorList) Collect(args ...interface{}) {
	for _, a := range args {
		if err, _ := a.(error); err != nil {
			*list = append(*list, err)
		}
	}
}
<|endoftext|>


<commit_before>package gorethink

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"

	p "github.com/dancannon/gorethink/ql2"
)

var (
	ErrNoConnections    = errors.New("gorethink: no connections were made when creating the session")
	ErrConnectionClosed = errors.New("gorethink: the connection is closed")
)

func printCarrots(t Term, frames []*p.Frame) string {
	var frame *p.Frame
	if len(frames) > 1 {
		frame, frames = frames[0], frames[1:]
	} else if len(frames) == 1 {
		frame, frames = frames[0], []*p.Frame{}
	}

	for i, arg := range t.args {
		if frame.GetPos() == int64(i) {
			t.args[i] = Term{
				termType: p.Term_DATUM,
				data:     printCarrots(arg, frames),
			}
		}
	}

	for k, arg := range t.optArgs {
		if frame.GetOpt() == k {
			t.optArgs[k] = Term{
				termType: p.Term_DATUM,
				data:     printCarrots(arg, frames),
			}
		}
	}

	b := &bytes.Buffer{}
	for _, c := range t.String() {
		if c != '^' {
			b.WriteString(" ")
		} else {
			b.WriteString("^")
		}
	}

	return b.String()
}

// Error constants
var ErrEmptyResult = errors.New("The result does not contain any more rows")

// Connection/Response errors

type rqlResponseError struct {
	response *Response
	term     *Term
}

func (e rqlResponseError) Error() string {
	var err = "An error occurred"
	json.Unmarshal(e.response.Responses[0], &err)

	return fmt.Sprintf("gorethink: %s", err)
}

func (e rqlResponseError) String() string {
	return e.Error()
}

type RqlCompileError struct {
	rqlResponseError
}

type RqlRuntimeError struct {
	rqlResponseError
}

type RqlClientError struct {
	rqlResponseError
}

type RqlDriverError struct {
	message string
}

func (e RqlDriverError) Error() string {
	return fmt.Sprintf("gorethink: %s", e.message)
}

func (e RqlDriverError) String() string {
	return e.Error()
}

type RqlConnectionError struct {
	message string
}

func (e RqlConnectionError) Error() string {
	return fmt.Sprintf("gorethink: %s", e.message)
}

func (e RqlConnectionError) String() string {
	return e.Error()
}
<commit_msg>Fixed rqlResponseError message<commit_after>package gorethink

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"

	p "github.com/dancannon/gorethink/ql2"
)

var (
	ErrNoConnections    = errors.New("gorethink: no connections were made when creating the session")
	ErrConnectionClosed = errors.New("gorethink: the connection is closed")
)

func printCarrots(t Term, frames []*p.Frame) string {
	var frame *p.Frame
	if len(frames) > 1 {
		frame, frames = frames[0], frames[1:]
	} else if len(frames) == 1 {
		frame, frames = frames[0], []*p.Frame{}
	}

	for i, arg := range t.args {
		if frame.GetPos() == int64(i) {
			t.args[i] = Term{
				termType: p.Term_DATUM,
				data:     printCarrots(arg, frames),
			}
		}
	}

	for k, arg := range t.optArgs {
		if frame.GetOpt() == k {
			t.optArgs[k] = Term{
				termType: p.Term_DATUM,
				data:     printCarrots(arg, frames),
			}
		}
	}

	b := &bytes.Buffer{}
	for _, c := range t.String() {
		if c != '^' {
			b.WriteString(" ")
		} else {
			b.WriteString("^")
		}
	}

	return b.String()
}

// Error constants
var ErrEmptyResult = errors.New("The result does not contain any more rows")

// Connection/Response errors

type rqlResponseError struct {
	response *Response
	term     *Term
}

func (e rqlResponseError) Error() string {
	var err = "An error occurred"
	json.Unmarshal(e.response.Responses[0], &err)

	return fmt.Sprintf("gorethink: %s in: \n%s", err, e.term.String())
}

func (e rqlResponseError) String() string {
	return e.Error()
}

type RqlCompileError struct {
	rqlResponseError
}

type RqlRuntimeError struct {
	rqlResponseError
}

type RqlClientError struct {
	rqlResponseError
}

type RqlDriverError struct {
	message string
}

func (e RqlDriverError) Error() string {
	return fmt.Sprintf("gorethink: %s", e.message)
}

func (e RqlDriverError) String() string {
	return e.Error()
}

type RqlConnectionError struct {
	message string
}

func (e RqlConnectionError) Error() string {
	return fmt.Sprintf("gorethink: %s", e.message)
}

func (e RqlConnectionError) String() string {
	return e.Error()
}
<|endoftext|>


<commit_before>package kite

import (
	"fmt"

	"github.com/koding/kite/dnode"
)

// Error is the type of the kite related errors returned from kite package.
type Error struct {
	Type    string `json:"type"`
	Message string `json:"message"`
	Code    string `json:"code"`
}

func (e Error) Error() string {
	return fmt.Sprintf("kite error %s - %s - %s", e.Type, e.Message, e.Code)
}

// createError creates a new kite.Error for the given r variable
func createError(r interface{}) *Error {
	if r == nil {
		return nil
	}

	var kiteErr *Error
	switch err := r.(type) {
	case *Error:
		kiteErr = err
	case *dnode.ArgumentError:
		kiteErr = &Error{
			Type:    "argumentError",
			Message: err.Error(),
		}
	default:
		kiteErr = &Error{
			Type:    "genericError",
			Message: fmt.Sprint(r),
		}
	}

	return kiteErr
}
<commit_msg>kite/errors: return user errors as it is<commit_after>package kite

import (
	"fmt"

	"github.com/koding/kite/dnode"
)

// Error is the type of the kite related errors returned from kite package.
type Error struct {
	Type    string `json:"type"`
	Message string `json:"message"`
	Code    string `json:"code"`
}

func (e Error) Error() string {
	if e.Type == "genericError" {
		return e.Message
	}

	return fmt.Sprintf("kite error %s - %s - %s", e.Type, e.Message, e.Code)
}

// createError creates a new kite.Error for the given r variable
func createError(r interface{}) *Error {
	if r == nil {
		return nil
	}

	var kiteErr *Error
	switch err := r.(type) {
	case *Error:
		kiteErr = err
	case *dnode.ArgumentError:
		kiteErr = &Error{
			Type:    "argumentError",
			Message: err.Error(),
		}
	default:
		kiteErr = &Error{
			Type:    "genericError",
			Message: fmt.Sprint(r),
		}
	}

	return kiteErr
}
<|endoftext|>


<commit_before>package respond

import (
	"fmt"
	"net/http"
)

////// Not Found

type NotFoundError struct {
	ResourceDescription string
}

func (err NotFoundError) Error() string {
	return fmt.Sprintf("resource not found: %s", err.ResourceDescription)
}

func NewNotFoundError(format string, data ...interface{}) *NotFoundError {
	return &NotFoundError{
		ResourceDescription: fmt.Sprintf(format, data...),
	}
}

func (err *NotFoundError) HTTPStatusCode() int {
	return 404
}

////// Not Acceptable

type NotAcceptableError struct {
	Accept string
}

func NewNotAcceptableError(request *http.Request) *NotAcceptableError {
	return &NotAcceptableError{request.Header.Get(`Accept`)}
}

func (err NotAcceptableError) Error() string {
	return fmt.Sprintf("unable to generate content satisfying Accept header: %s", err.Accept)
}

func (err *NotAcceptableError) HTTPStatusCode() int {
	return 406
}

////// Bad Request

type BadRequestError struct {
	Detail string
}

func NewBadRequestError(format string, data ...interface{}) *BadRequestError {
	return &BadRequestError{Detail: fmt.Sprintf(format, data)}
}

func (err BadRequestError) Error() string {
	return err.Detail
}

func (err *BadRequestError) HTTPStatusCode() int {
	return 400
}
<commit_msg>errors Error() received on a pointer<commit_after>package respond

import (
	"fmt"
	"net/http"
)

////// Not Found

type NotFoundError struct {
	ResourceDescription string
}

func (err *NotFoundError) Error() string {
	return fmt.Sprintf("resource not found: %s", err.ResourceDescription)
}

func NewNotFoundError(format string, data ...interface{}) *NotFoundError {
	return &NotFoundError{
		ResourceDescription: fmt.Sprintf(format, data...),
	}
}

func (err *NotFoundError) HTTPStatusCode() int {
	return 404
}

////// Not Acceptable

type NotAcceptableError struct {
	Accept string
}

func NewNotAcceptableError(request *http.Request) *NotAcceptableError {
	return &NotAcceptableError{request.Header.Get(`Accept`)}
}

func (err *NotAcceptableError) Error() string {
	return fmt.Sprintf("unable to generate content satisfying Accept header: %s", err.Accept)
}

func (err *NotAcceptableError) HTTPStatusCode() int {
	return 406
}

////// Bad Request

type BadRequestError struct {
	Detail string
}

func NewBadRequestError(format string, data ...interface{}) *BadRequestError {
	return &BadRequestError{Detail: fmt.Sprintf(format, data)}
}

func (err *BadRequestError) Error() string {
	return err.Detail
}

func (err *BadRequestError) HTTPStatusCode() int {
	return 400
}
<|endoftext|>


<commit_before>package errors

import (
    "fmt"
)

var root *ErrorClass

func New(ec *ErrorClass) *ErrorClass {
    return &ErrorClass{parent: ec}
}

func Wrap(err error) *Error {
    return root.Wrap(err)
}

type ErrorClass struct {
    parent *ErrorClass
}

func (e *ErrorClass) New(format string, args ...interface{}) *Error {
    return e.Wrap(fmt.Errorf(format, args...))
}

func (e *ErrorClass) Wrap(err error) *Error {
    if err == nil {
        return nil
    }
    return &Error{error: err, class: e}
}

func (e *ErrorClass) Is(parent *ErrorClass) bool {
    for check := e; check != nil; check = check.parent {
        if check == parent {
            return true
        }
    }
    return false
}

type Error struct {
    error
    class *ErrorClass
}

func (e *Error) Err() error {
    return e.error
}

func (e *Error) Is(ec *ErrorClass) bool {
    return e.class.Is(ec)
}
<commit_msg>space monkey internal commit export<commit_after>package errors

import (
    "fmt"
)

var root *ErrorClass

func New(ec *ErrorClass) *ErrorClass {
    return &ErrorClass{parent: ec}
}

func Wrap(err error) *Error {
    return root.Wrap(err)
}

type ErrorClass struct {
    parent *ErrorClass
}

func (e *ErrorClass) New(format string, args ...interface{}) *Error {
    return e.Wrap(fmt.Errorf(format, args...))
}

func (e *ErrorClass) Wrap(err error) *Error {
    if err == nil {
        return nil
    }
    return &Error{err: err, class: e}
}

func (e *ErrorClass) Is(parent *ErrorClass) bool {
    for check := e; check != nil; check = check.parent {
        if check == parent {
            return true
        }
    }
    return false
}

type Error struct {
    err   error
    class *ErrorClass
}

func (e *Error) Error() string {
    return e.err.Error()
}

func (e *Error) Err() error {
    return e.err
}

func (e *Error) Is(ec *ErrorClass) bool {
    return e.class.Is(ec)
}
<|endoftext|>


<commit_before>package playingplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"

	"github.com/iopred/bruxism"
)

type playingPlugin struct {
	bruxism.SimplePlugin
	Game string
	URL  string
}

// Name returns the name of the plugin.
func (p *playingPlugin) Name() string {
	return "Playing"
}

// Load will load plugin state from a byte array.
func (p *playingPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Playing Plugin only supports Discord.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	service.(*bruxism.Discord).Session.UpdateStreamingStatus(0, p.Game, p.URL)

	return nil
}

// Save will save plugin state to a byte array.
func (p *playingPlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *playingPlugin) helpFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if detailed {
		return nil
	}

	if !service.IsBotOwner(message) {
		return nil
	}

	return bruxism.CommandHelp(service, "playing", "<game>, <url>", fmt.Sprintf("Set which game %s is playing.", service.UserName()))
}

// Message handler.
func (p *playingPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "playing", message) {
			if !service.IsBotOwner(message) {
				return
			}
			query, _ := bruxism.ParseCommand(service, message)

			split := strings.Split(query, ",")

			p.Game = split[0]
			if len(split) > 1 {
				p.URL = split[1]
			} else {
				p.URL = ""
			}

			service.(*bruxism.Discord).Session.UpdateStreamingStatus(0, p.Game, p.URL)
		}
	}
}

// New will create a new top streamers plugin.
func New() bruxism.Plugin {
	p := &playingPlugin{}
	p.MessageFunc = p.messageFunc
	p.HelpFunc = p.helpFunc
	return p
}
<commit_msg>Trim playing status.<commit_after>package playingplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"

	"github.com/iopred/bruxism"
)

type playingPlugin struct {
	bruxism.SimplePlugin
	Game string
	URL  string
}

// Name returns the name of the plugin.
func (p *playingPlugin) Name() string {
	return "Playing"
}

// Load will load plugin state from a byte array.
func (p *playingPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Playing Plugin only supports Discord.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	service.(*bruxism.Discord).Session.UpdateStreamingStatus(0, p.Game, p.URL)

	return nil
}

// Save will save plugin state to a byte array.
func (p *playingPlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *playingPlugin) helpFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if detailed {
		return nil
	}

	if !service.IsBotOwner(message) {
		return nil
	}

	return bruxism.CommandHelp(service, "playing", "<game>, <url>", fmt.Sprintf("Set which game %s is playing.", service.UserName()))
}

// Message handler.
func (p *playingPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "playing", message) {
			if !service.IsBotOwner(message) {
				return
			}
			query, _ := bruxism.ParseCommand(service, message)

			split := strings.Split(query, ",")

			p.Game = strings.Trim(split[0])
			if len(split) > 1 {
				p.URL = strings.Trim(split[1], " ")
			} else {
				p.URL = ""
			}

			service.(*bruxism.Discord).Session.UpdateStreamingStatus(0, p.Game, p.URL)
		}
	}
}

// New will create a new top streamers plugin.
func New() bruxism.Plugin {
	p := &playingPlugin{}
	p.MessageFunc = p.messageFunc
	p.HelpFunc = p.helpFunc
	return p
}
<|endoftext|>


<commit_before>package chatroom

import (
	"container/list"
	"time"
)

type Event struct {
	Type      string // "join", "leave", or "message"
	User      string
	Timestamp int    // Unix timestamp (secs)
	Text      string // What the user said (if Type == "say")
}

type Subscription struct {
	Archive []Event      // All the events from the archive.
	New     <-chan Event // New events coming in.
}

// Owner of a subscription must cancel it when they stop listening to events.
func (s Subscription) Cancel() {
	unsubscribe <- s.New // Unsubscribe the channel.
	drain(s.New)         // Drain it, just in case there was a pending publish.
}

func newEvent(typ, user, msg string) Event {
	return Event{typ, user, int(time.Now().Unix()), msg}
}

func Subscribe() Subscription {
	resp := make(chan Subscription)
	subscribe <- resp
	return <-resp
}

func Join(user string) {
	publish <- newEvent("join", user, "")
}

func Say(user, message string) {
	publish <- newEvent("message", user, message)
}

func Leave(user string) {
	publish <- newEvent("leave", user, "")
}

const archiveSize = 10

var (
	// Send a channel here to get room events back.  It will send the entire
	// archive initially, and then new messages as they come in.
	subscribe = make(chan (chan<- Subscription), 10)
	// Send a channel here to unsubscribe.
	unsubscribe = make(chan (<-chan Event), 10)
	// Send events here to publish them.
	publish = make(chan Event, 10)
)

// This function loops forever, handling the chat room pubsub
func chatroom() {
	archive := list.New()
	subscribers := list.New()

	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription{events, subscriber}

		case event := <-publish:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				ch.Value.(chan Event) <- event
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			archive.PushBack(event)

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
					break
				}
			}
		}
	}
}

func init() {
	go chatroom()
}

// Helpers

// Drains a given channel of any messages.
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}
<commit_msg>Fix typo in chatroom.go comment<commit_after>package chatroom

import (
	"container/list"
	"time"
)

type Event struct {
	Type      string // "join", "leave", or "message"
	User      string
	Timestamp int    // Unix timestamp (secs)
	Text      string // What the user said (if Type == "message")
}

type Subscription struct {
	Archive []Event      // All the events from the archive.
	New     <-chan Event // New events coming in.
}

// Owner of a subscription must cancel it when they stop listening to events.
func (s Subscription) Cancel() {
	unsubscribe <- s.New // Unsubscribe the channel.
	drain(s.New)         // Drain it, just in case there was a pending publish.
}

func newEvent(typ, user, msg string) Event {
	return Event{typ, user, int(time.Now().Unix()), msg}
}

func Subscribe() Subscription {
	resp := make(chan Subscription)
	subscribe <- resp
	return <-resp
}

func Join(user string) {
	publish <- newEvent("join", user, "")
}

func Say(user, message string) {
	publish <- newEvent("message", user, message)
}

func Leave(user string) {
	publish <- newEvent("leave", user, "")
}

const archiveSize = 10

var (
	// Send a channel here to get room events back.  It will send the entire
	// archive initially, and then new messages as they come in.
	subscribe = make(chan (chan<- Subscription), 10)
	// Send a channel here to unsubscribe.
	unsubscribe = make(chan (<-chan Event), 10)
	// Send events here to publish them.
	publish = make(chan Event, 10)
)

// This function loops forever, handling the chat room pubsub
func chatroom() {
	archive := list.New()
	subscribers := list.New()

	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription{events, subscriber}

		case event := <-publish:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				ch.Value.(chan Event) <- event
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			archive.PushBack(event)

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
					break
				}
			}
		}
	}
}

func init() {
	go chatroom()
}

// Helpers

// Drains a given channel of any messages.
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}
<|endoftext|>


<commit_before>package auth

import (
	"encoding/base64"
	"errors"
	"net/http"
	"strings"

	"code.google.com/p/go.crypto/bcrypt"
)

type Handler struct {
	Handler        http.Handler
	Username       string
	HashedPassword string
}

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	header := r.Header.Get("Authorization")
	username, password, err := ExtractUsernameAndPassword(header)
	if err != nil {
		h.unauthorized(w)
		return
	}

	if h.correctCredentials(username, password) {
		h.Handler.ServeHTTP(w, r)
	} else {
		h.unauthorized(w)
	}
}

func (h Handler) correctCredentials(username string, password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(h.HashedPassword), []byte(password))
	return h.Username == username && err == nil
}

func (h Handler) unauthorized(w http.ResponseWriter) {
	w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
	w.WriteHeader(http.StatusUnauthorized)
}

var ErrUnparsableHeader = errors.New("cannot parse 'Authorization' header")

func ExtractUsernameAndPassword(authorizationHeader string) (string, string, error) {
	if !strings.HasPrefix(authorizationHeader, "Basic ") {
		return "", "", ErrUnparsableHeader
	}

	substring := authorizationHeader[6:]
	decodedSubstring, err := base64.StdEncoding.DecodeString(substring)
	if err != nil {
		return "", "", ErrUnparsableHeader
	}

	parts := strings.Split(string(decodedSubstring), ":")
	if len(parts) != 2 {
		return "", "", ErrUnparsableHeader
	}

	return parts[0], parts[1], nil
}
<commit_msg>improve variable names<commit_after>package auth

import (
	"encoding/base64"
	"errors"
	"net/http"
	"strings"

	"code.google.com/p/go.crypto/bcrypt"
)

type Handler struct {
	Handler        http.Handler
	Username       string
	HashedPassword string
}

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	header := r.Header.Get("Authorization")
	username, password, err := ExtractUsernameAndPassword(header)
	if err != nil {
		h.unauthorized(w)
		return
	}

	if h.correctCredentials(username, password) {
		h.Handler.ServeHTTP(w, r)
	} else {
		h.unauthorized(w)
	}
}

func (h Handler) correctCredentials(username string, password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(h.HashedPassword), []byte(password))
	return h.Username == username && err == nil
}

func (h Handler) unauthorized(w http.ResponseWriter) {
	w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
	w.WriteHeader(http.StatusUnauthorized)
}

var ErrUnparsableHeader = errors.New("cannot parse 'Authorization' header")

func ExtractUsernameAndPassword(authorizationHeader string) (string, string, error) {
	if !strings.HasPrefix(authorizationHeader, "Basic ") {
		return "", "", ErrUnparsableHeader
	}

	encodedCredentials := authorizationHeader[6:]
	credentials, err := base64.StdEncoding.DecodeString(encodedCredentials)
	if err != nil {
		return "", "", ErrUnparsableHeader
	}

	parts := strings.Split(string(credentials), ":")
	if len(parts) != 2 {
		return "", "", ErrUnparsableHeader
	}

	return parts[0], parts[1], nil
}
<|endoftext|>


<commit_before>package parser_test

import (
	"archive/zip"
	"errors"
	"log"
	"net"
	"reflect"
	"strconv"
	"strings"
	"testing"

	"github.com/m-lab/annotation-service/loader"
	"github.com/m-lab/annotation-service/parser"
)

func TestIPGLite1(t *testing.T) {
	var ipv4 []parser.IPNode
	var ipv4Expected = []parser.IPNode{
		parser.IPNode{
			net.ParseIP("1.0.0.0"),
			net.ParseIP("1.0.0.255"),
			0,
			"",
			0,
			0,
		},
		parser.IPNode{
			net.ParseIP("1.0.1.0"),
			net.ParseIP("1.0.3.255"),
			4,
			"",
			0,
			0,
		},
		parser.IPNode{
			net.ParseIP("1.0.4.0"),
			net.ParseIP("1.0.7.255"),
			4,
			"",
			0,
			0,
		},
	}
	locationIdMap := map[int]int{
		609013: 0,
		104084: 4,
		17:     4,
	}
	reader, err := zip.OpenReader("testdata/GeoLiteLatest.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}

	rcIPv4, err := loader.FindFile("GeoLiteCity-Blocks.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rcIPv4.Close()
	ipv4, err = parser.CreateIPList(rcIPv4, locationIdMap, "geolatest")
	if err != nil {
		t.Errorf("Failed to create ipv4")
	}
	err = compareIPLists(ipv4Expected, ipv4)
	if err != nil {
		t.Errorf("Lists are not equal")
	}
}
func TestIPLisGLite2(t *testing.T) {
	var ipv4, ipv6 []parser.IPNode
	var ipv6Expected = []parser.IPNode{
		parser.IPNode{
			parser.RangeCIDR("600:8801:9400:5a1:948b:ab15:dde3:61a3/128", "low"),
			parser.RangeCIDR("600:8801:9400:5a1:948b:ab15:dde3:61a3/128", "high"),
			4,
			"91941",
			32.7596,
			-116.994,
		},
		parser.IPNode{
			parser.RangeCIDR("2001:5::/32", "low"),
			parser.RangeCIDR("2001:5::/32", "high"),
			4,
			"",
			47,
			8,
		},
		parser.IPNode{
			parser.RangeCIDR("2001:200::/40", "low"),
			parser.RangeCIDR("2001:200::/40", "high"),
			4,
			"",
			36,
			138,
		},
	}
	var ipv4Expected = []parser.IPNode{
		parser.IPNode{
			parser.RangeCIDR("1.0.0.0/24", "low"),
			parser.RangeCIDR("1.0.0.0/24", "high"),
			0,
			"3095",
			-37.7,
			145.1833,
		},
		parser.IPNode{
			parser.RangeCIDR("1.0.1.0/24", "low"),
			parser.RangeCIDR("1.0.1.0/24", "high"),
			4,
			"",
			26.0614,
			119.3061,
		},
		parser.IPNode{
			parser.RangeCIDR("1.0.2.0/23", "low"),
			parser.RangeCIDR("1.0.2.0/23", "high"),
			4,
			"",
			26.0614,
			119.3061,
		},
	}
	locationIdMap := map[int]int{
		2151718: 0,
		1810821: 4,
		5363990: 4,
		6255148: 4,
		1861060: 4,
	}
	reader, err := zip.OpenReader("testdata/GeoLite2City.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}

	rcIPv4, err := loader.FindFile("GeoLite2-City-Blocks-IPv4.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rcIPv4.Close()
	ipv4, err = parser.CreateIPList(rcIPv4, locationIdMap, "geolite2")
	if err != nil {
		t.Errorf("Failed to create ipv4")
	}
	err = compareIPLists(ipv4Expected, ipv4)
	if err != nil {
		t.Errorf("Lists are not equal")
	}

	rcIPv6, err := loader.FindFile("GeoLite2-City-Blocks-IPv6.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rcIPv6.Close()
	ipv6, err = parser.CreateIPList(rcIPv6, locationIdMap, "geolite2")
	if err != nil {
		log.Println(err)
		t.Errorf("Failed to create ipv6")
	}
	err = compareIPLists(ipv6Expected, ipv6)
	if err != nil {
		t.Errorf("Lists are not equal")
	}
}

func TestLocationListGLite2(t *testing.T) {
	var locationList []parser.LocationNode
	var idMap map[int]int
	var LocList = []parser.LocationNode{
		parser.LocationNode{
			32909,
			"AS",
			"IR",
			"Iran",
			0,
			"Shahre Jadide Andisheh",
		},
		parser.LocationNode{
			49518,
			"AF",
			"RW",
			"Rwanda",
			0,
			"",
		},
		parser.LocationNode{
			51537,
			"AF",
			"SO",
			"Somalia",
			0,
			"",
		},
	}
	LocIdMap := map[int]int{
		51537: 2,
		49518: 1,
		32909: 0,
	}

	reader, err := zip.OpenReader("testdata/GeoLite2City.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}

	rc, err := loader.FindFile("GeoLite2-City-Locations-en.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rc.Close()
	locationList, idMap, err = parser.CreateLocationList(rc)
	if err != nil {
		t.Errorf("Failed to CreateLocationList")
	}
	if locationList == nil || idMap == nil {
		t.Errorf("Failed to create LocationList and mapID")
	}

	err = compareLocLists(locationList, LocList)
	if err != nil {
		t.Errorf("Location lists are not equal")
	}

	eq := reflect.DeepEqual(LocIdMap, idMap)
	if !eq {
		t.Errorf("Location maps are not equal")
	}
}

func TestCorruptData(t *testing.T) {
	reader, err := zip.OpenReader("testdata/GeoLite2CityCORRUPT.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}
	r := &(reader.Reader)
	for _, f := range r.File {
		if len(f.Name) >= len("GeoLite2-City-Locations-en.csv") && f.Name[len(f.Name)-len("GeoLite2-City-Locations-en.csv"):] == "GeoLite2-City-Locations-en.csv" {
			rc, err := f.Open()
			if err != nil {
				t.Errorf("Failed to open GeoLite2-City-Locations-en.csv")
			}
			defer rc.Close()
			_, _, err = parser.CreateLocationList(rc)
			if err.Error() != "Corrupted Data: wrong number of columns" {
				if err == nil {
					t.Errorf("Error inconsistent:\ngot: nil\nwanted: Corrupted Data: wrong number of columns")
				}
				if err != nil {
					output := strings.Join([]string{"Error inconsistent:\ngot: ", err.Error(), "\nwanted: Corrupted Data: wrong number of columns"}, "")
					t.Errorf(output)
				}

			}
		}
	}
}

func floatToString(num float64) string {
	return strconv.FormatFloat(num, 'f', 6, 64)
}

func compareIPLists(listComp, list []parser.IPNode) error {
	for index, element := range list {
		if !((element.IPAddressLow).Equal(listComp[index].IPAddressLow)) {
			output := strings.Join([]string{"IPAddress Low inconsistent\ngot:", element.IPAddressLow.String(), " \nwanted:", listComp[index].IPAddressLow.String()}, "")
			log.Println(output)
			return errors.New(output)
		}
		if !((element.IPAddressHigh).Equal(listComp[index].IPAddressHigh)) {
			output := strings.Join([]string{"IPAddressHigh inconsistent\ngot:", element.IPAddressHigh.String(), " \nwanted:", listComp[index].IPAddressHigh.String()}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.LocationIndex != listComp[index].LocationIndex {
			output := strings.Join([]string{"LocationIndex inconsistent\ngot:", strconv.Itoa(element.LocationIndex), " \nwanted:", strconv.Itoa(listComp[index].LocationIndex)}, "")
			log.Println(output)
			log.Println(element)
			return errors.New(output)
		}
		if element.PostalCode != listComp[index].PostalCode {
			output := strings.Join([]string{"PostalCode inconsistent\ngot:", element.PostalCode, " \nwanted:", listComp[index].PostalCode}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.Latitude != listComp[index].Latitude {
			output := strings.Join([]string{"Latitude inconsistent\ngot:", floatToString(element.Latitude), " \nwanted:", floatToString(listComp[index].Latitude)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.Longitude != listComp[index].Longitude {
			output := strings.Join([]string{"Longitude inconsistent\ngot:", floatToString(element.Longitude), " \nwanted:", floatToString(listComp[index].Longitude)}, "")
			log.Println(output)
			return errors.New(output)
		}
	}
	return nil
}

func compareLocLists(list, listComp []parser.LocationNode) error {
	for index, element := range list {
		if element.GeonameID != listComp[index].GeonameID {
			output := strings.Join([]string{"GeonameID inconsistent\ngot:", strconv.Itoa(element.GeonameID), " \nwanted:", strconv.Itoa(listComp[index].GeonameID)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.ContinentCode != listComp[index].ContinentCode {
			output := strings.Join([]string{"Continent code inconsistent\ngot:", element.ContinentCode, " \nwanted:", listComp[index].ContinentCode}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.CountryCode != listComp[index].CountryCode {
			output := strings.Join([]string{"Country code inconsistent\ngot:", element.CountryCode, " \nwanted:", listComp[index].CountryCode}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.CountryName != listComp[index].CountryName {
			output := strings.Join([]string{"Country name inconsistent\ngot:", element.CountryName, " \nwanted:", listComp[index].CountryName}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.MetroCode != listComp[index].MetroCode {
			output := strings.Join([]string{"Longitude inconsistent\ngot:", strconv.FormatInt(element.MetroCode, 16), " \nwanted:", strconv.FormatInt(listComp[index].MetroCode, 16)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.CityName != listComp[index].CityName {
			output := strings.Join([]string{"Longitude inconsistent\ngot:", element.CityName, " \nwanted:", listComp[index].CityName}, "")
			log.Println(output)
			return errors.New(output)
		}
	}
	return nil
}
<commit_msg>delete debug statement<commit_after>package parser_test

import (
	"archive/zip"
	"errors"
	"log"
	"net"
	"reflect"
	"strconv"
	"strings"
	"testing"

	"github.com/m-lab/annotation-service/loader"
	"github.com/m-lab/annotation-service/parser"
)

func TestIPGLite1(t *testing.T) {
	var ipv4 []parser.IPNode
	var ipv4Expected = []parser.IPNode{
		parser.IPNode{
			net.ParseIP("1.0.0.0"),
			net.ParseIP("1.0.0.255"),
			0,
			"",
			0,
			0,
		},
		parser.IPNode{
			net.ParseIP("1.0.1.0"),
			net.ParseIP("1.0.3.255"),
			4,
			"",
			0,
			0,
		},
		parser.IPNode{
			net.ParseIP("1.0.4.0"),
			net.ParseIP("1.0.7.255"),
			4,
			"",
			0,
			0,
		},
	}
	locationIdMap := map[int]int{
		609013: 0,
		104084: 4,
		17:     4,
	}
	reader, err := zip.OpenReader("testdata/GeoLiteLatest.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}

	rcIPv4, err := loader.FindFile("GeoLiteCity-Blocks.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rcIPv4.Close()
	ipv4, err = parser.CreateIPList(rcIPv4, locationIdMap, "geolatest")
	if err != nil {
		t.Errorf("Failed to create ipv4")
	}
	err = compareIPLists(ipv4Expected, ipv4)
	if err != nil {
		t.Errorf("Lists are not equal")
	}
}
func TestIPLisGLite2(t *testing.T) {
	var ipv4, ipv6 []parser.IPNode
	var ipv6Expected = []parser.IPNode{
		parser.IPNode{
			parser.RangeCIDR("600:8801:9400:5a1:948b:ab15:dde3:61a3/128", "low"),
			parser.RangeCIDR("600:8801:9400:5a1:948b:ab15:dde3:61a3/128", "high"),
			4,
			"91941",
			32.7596,
			-116.994,
		},
		parser.IPNode{
			parser.RangeCIDR("2001:5::/32", "low"),
			parser.RangeCIDR("2001:5::/32", "high"),
			4,
			"",
			47,
			8,
		},
		parser.IPNode{
			parser.RangeCIDR("2001:200::/40", "low"),
			parser.RangeCIDR("2001:200::/40", "high"),
			4,
			"",
			36,
			138,
		},
	}
	var ipv4Expected = []parser.IPNode{
		parser.IPNode{
			parser.RangeCIDR("1.0.0.0/24", "low"),
			parser.RangeCIDR("1.0.0.0/24", "high"),
			0,
			"3095",
			-37.7,
			145.1833,
		},
		parser.IPNode{
			parser.RangeCIDR("1.0.1.0/24", "low"),
			parser.RangeCIDR("1.0.1.0/24", "high"),
			4,
			"",
			26.0614,
			119.3061,
		},
		parser.IPNode{
			parser.RangeCIDR("1.0.2.0/23", "low"),
			parser.RangeCIDR("1.0.2.0/23", "high"),
			4,
			"",
			26.0614,
			119.3061,
		},
	}
	locationIdMap := map[int]int{
		2151718: 0,
		1810821: 4,
		5363990: 4,
		6255148: 4,
		1861060: 4,
	}
	reader, err := zip.OpenReader("testdata/GeoLite2City.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}

	rcIPv4, err := loader.FindFile("GeoLite2-City-Blocks-IPv4.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rcIPv4.Close()
	ipv4, err = parser.CreateIPList(rcIPv4, locationIdMap, "geolite2")
	if err != nil {
		t.Errorf("Failed to create ipv4")
	}
	err = compareIPLists(ipv4Expected, ipv4)
	if err != nil {
		t.Errorf("Lists are not equal")
	}

	rcIPv6, err := loader.FindFile("GeoLite2-City-Blocks-IPv6.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rcIPv6.Close()
	ipv6, err = parser.CreateIPList(rcIPv6, locationIdMap, "geolite2")
	if err != nil {
		log.Println(err)
		t.Errorf("Failed to create ipv6")
	}
	err = compareIPLists(ipv6Expected, ipv6)
	if err != nil {
		t.Errorf("Lists are not equal")
	}
}

func TestLocationListGLite2(t *testing.T) {
	var locationList []parser.LocationNode
	var idMap map[int]int
	var LocList = []parser.LocationNode{
		parser.LocationNode{
			32909,
			"AS",
			"IR",
			"Iran",
			0,
			"Shahre Jadide Andisheh",
		},
		parser.LocationNode{
			49518,
			"AF",
			"RW",
			"Rwanda",
			0,
			"",
		},
		parser.LocationNode{
			51537,
			"AF",
			"SO",
			"Somalia",
			0,
			"",
		},
	}
	LocIdMap := map[int]int{
		51537: 2,
		49518: 1,
		32909: 0,
	}

	reader, err := zip.OpenReader("testdata/GeoLite2City.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}

	rc, err := loader.FindFile("GeoLite2-City-Locations-en.csv", &reader.Reader)
	if err != nil {
		t.Errorf("Failed to create io.ReaderCloser")
	}
	defer rc.Close()
	locationList, idMap, err = parser.CreateLocationList(rc)
	if err != nil {
		t.Errorf("Failed to CreateLocationList")
	}
	if locationList == nil || idMap == nil {
		t.Errorf("Failed to create LocationList and mapID")
	}

	err = compareLocLists(locationList, LocList)
	if err != nil {
		t.Errorf("Location lists are not equal")
	}

	eq := reflect.DeepEqual(LocIdMap, idMap)
	if !eq {
		t.Errorf("Location maps are not equal")
	}
}

func TestCorruptData(t *testing.T) {
	reader, err := zip.OpenReader("testdata/GeoLite2CityCORRUPT.zip")
	if err != nil {
		t.Errorf("Error opening zip file")
	}
	r := &(reader.Reader)
	for _, f := range r.File {
		if len(f.Name) >= len("GeoLite2-City-Locations-en.csv") && f.Name[len(f.Name)-len("GeoLite2-City-Locations-en.csv"):] == "GeoLite2-City-Locations-en.csv" {
			rc, err := f.Open()
			if err != nil {
				t.Errorf("Failed to open GeoLite2-City-Locations-en.csv")
			}
			defer rc.Close()
			_, _, err = parser.CreateLocationList(rc)
			if err.Error() != "Corrupted Data: wrong number of columns" {
				if err == nil {
					t.Errorf("Error inconsistent:\ngot: nil\nwanted: Corrupted Data: wrong number of columns")
				}
				if err != nil {
					output := strings.Join([]string{"Error inconsistent:\ngot: ", err.Error(), "\nwanted: Corrupted Data: wrong number of columns"}, "")
					t.Errorf(output)
				}

			}
		}
	}
}

func floatToString(num float64) string {
	return strconv.FormatFloat(num, 'f', 6, 64)
}

func compareIPLists(listComp, list []parser.IPNode) error {
	for index, element := range list {
		if !((element.IPAddressLow).Equal(listComp[index].IPAddressLow)) {
			output := strings.Join([]string{"IPAddress Low inconsistent\ngot:", element.IPAddressLow.String(), " \nwanted:", listComp[index].IPAddressLow.String()}, "")
			log.Println(output)
			return errors.New(output)
		}
		if !((element.IPAddressHigh).Equal(listComp[index].IPAddressHigh)) {
			output := strings.Join([]string{"IPAddressHigh inconsistent\ngot:", element.IPAddressHigh.String(), " \nwanted:", listComp[index].IPAddressHigh.String()}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.LocationIndex != listComp[index].LocationIndex {
			output := strings.Join([]string{"LocationIndex inconsistent\ngot:", strconv.Itoa(element.LocationIndex), " \nwanted:", strconv.Itoa(listComp[index].LocationIndex)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.PostalCode != listComp[index].PostalCode {
			output := strings.Join([]string{"PostalCode inconsistent\ngot:", element.PostalCode, " \nwanted:", listComp[index].PostalCode}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.Latitude != listComp[index].Latitude {
			output := strings.Join([]string{"Latitude inconsistent\ngot:", floatToString(element.Latitude), " \nwanted:", floatToString(listComp[index].Latitude)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.Longitude != listComp[index].Longitude {
			output := strings.Join([]string{"Longitude inconsistent\ngot:", floatToString(element.Longitude), " \nwanted:", floatToString(listComp[index].Longitude)}, "")
			log.Println(output)
			return errors.New(output)
		}
	}
	return nil
}

func compareLocLists(list, listComp []parser.LocationNode) error {
	for index, element := range list {
		if element.GeonameID != listComp[index].GeonameID {
			output := strings.Join([]string{"GeonameID inconsistent\ngot:", strconv.Itoa(element.GeonameID), " \nwanted:", strconv.Itoa(listComp[index].GeonameID)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.ContinentCode != listComp[index].ContinentCode {
			output := strings.Join([]string{"Continent code inconsistent\ngot:", element.ContinentCode, " \nwanted:", listComp[index].ContinentCode}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.CountryCode != listComp[index].CountryCode {
			output := strings.Join([]string{"Country code inconsistent\ngot:", element.CountryCode, " \nwanted:", listComp[index].CountryCode}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.CountryName != listComp[index].CountryName {
			output := strings.Join([]string{"Country name inconsistent\ngot:", element.CountryName, " \nwanted:", listComp[index].CountryName}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.MetroCode != listComp[index].MetroCode {
			output := strings.Join([]string{"Longitude inconsistent\ngot:", strconv.FormatInt(element.MetroCode, 16), " \nwanted:", strconv.FormatInt(listComp[index].MetroCode, 16)}, "")
			log.Println(output)
			return errors.New(output)
		}
		if element.CityName != listComp[index].CityName {
			output := strings.Join([]string{"Longitude inconsistent\ngot:", element.CityName, " \nwanted:", listComp[index].CityName}, "")
			log.Println(output)
			return errors.New(output)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"testing"

	"github.com/Tamrin007/monkey/ast"
	"github.com/Tamrin007/monkey/lexer"
)

func TestLetStatements(t *testing.T) {
	input := `
let x = 5;
let y = 10;
let foobar = 838383;
	`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParserErrors(t, p)
	if program == nil {
		t.Fatalf("ParseProgram() returned nil")
	}
	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	tests := []struct {
		expectedIdentifier string
	}{
		{"x"},
		{"y"},
		{"foobar"},
	}

	for i, tt := range tests {
		stmt := program.Statements[i]
		if !testLetStatement(t, stmt, tt.expectedIdentifier) {
			return
		}
	}
}

func checkParserErrors(t *testing.T, p *Parser) {
	errors := p.Errors()
	if len(errors) == 0 {
		return
	}

	t.Errorf("Parser has %d errors", len(errors))
	for _, msg := range errors {
		t.Errorf("parser error: %q", msg)
	}
	t.FailNow()
}

func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
	if s.TokenLiteral() != "let" {
		t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
		return false
	}

	letStmt, ok := s.(*ast.LetStatement)
	if !ok {
		t.Errorf("s not *ast.LetStatement. got=%T", s)
		return false
	}

	if letStmt.Name.TokenLiteral() != name {
		t.Errorf("s.Name not '%s'. got=%s", name, letStmt.Name)
		return false
	}

	return true
}

func TestReturnStatement(t *testing.T) {
	input := `
return 5;
return 10;
return 993322;
`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParserErrors(t, p)

	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	for _, stmt := range program.Statements {
		returnStmt, ok := stmt.(*ast.ReturnStatement)
		if !ok {
			t.Errorf("stmt not *ast.returnStatement. got=%T", stmt)
			continue
		}
		if returnStmt.TokenLiteral() != "return" {
			t.Errorf("returnStmt.TokenLiteral not 'return', got %q", returnStmt.TokenLiteral())
		}
	}
}

func TestIdentifierExpression(t *testing.T) {
	input := "foobar;"

	l := lexer.New(input)
	p := New(l)
	program := p.ParseProgram()
	checkParserErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program has not enough statements. got=%d", len(program.Statements))
	}
	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	ident, ok := stmt.Expression.(*ast.Identifier)
	if !ok {
		t.Fatalf("exp not *ast.Identifier. got=%T", stmt.Expression)
	}
	if ident.Value != "foobar" {
		t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
	}
	if ident.TokenLiteral() != "foobar" {
		t.Errorf("ident.TokenLiteral not %s. got=%s", "foobar", ident.TokenLiteral())
	}
}

func TestIntegerLiteralExpression(t *testing.T) {
	input := "5;"

	l := lexer.New(input)
	p := New(l)
	program := p.ParseProgram()
	checkParserErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program has not enough statements. got=%d", len(program.Statements))
	}
	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	literal, ok := stmt.Expression.(*ast.IntegerLiteral)
	if !ok {
		t.Fatalf("exp not *ast.IntegerLiteral. got=%T", stmt.Expression)
	}
	if literal.Value != 5 {
		t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
	}
	if literal.TokenLiteral() != "5" {
		t.Errorf("literal.TokenLiteral not %s. got=%s", "5", literal.TokenLiteral())
	}
}

func TestParsingPrefixExpressions(t *testing.T) {
	prefixTests := []struct {
		input        string
		operator     string
		integerValue int64
	}{
		{"!5;", "!", 5},
		{"-15;", "-", 15},
	}

	for _, tt := range prefixTests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program has not enough statements. got=%d", len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.PrefixExpression)
		if !ok {
			t.Fatalf("stmt is not ast.PrefixExpression. got=%T", stmt.Expression)
		}
		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}
		if !testIntegerLiteral(t, exp.Right, tt.integerValue) {
			return
		}
	}
}

func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
	integ, ok := il.(*ast.IntegerLiteral)
	if !ok {
		t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
		return false
	}
	if integ.Value != value {
		t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
		return false
	}

	if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
		t.Errorf("integ.TokenLiteral not %d. got=%s", value, integ.TokenLiteral())
		return false
	}

	return true
}

func TestParsingInfixExpressions(t *testing.T) {
	infixTests := []struct {
		input      string
		leftValue  int64
		operator   string
		rightValue int64
	}{
		{"5 + 5", 5, "+", 5},
		{"5 - 5", 5, "-", 5},
		{"5 * 5", 5, "*", 5},
		{"5 / 5", 5, "/", 5},
		{"5 > 5", 5, ">", 5},
		{"5 < 5", 5, "<", 5},
		{"5 == 5", 5, "==", 5},
		{"5 != 5", 5, "!=", 5},
	}

	for _, tt := range infixTests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program.Statements does not contain %d statements. got=%d\n", 1, len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.InfixExpression)
		if !ok {
			t.Fatalf("exp is not ast.InfixExpression. got=%T", stmt.Expression)
		}

		if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
			return
		}

		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}

		if !testIntegerLiteral(t, exp.Right, tt.rightValue) {
			return
		}
	}
}

func TestOperatorPrecedenceParsing(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{
			"-a * b",
			"((-a) * b)",
		},
		{
			"!-a",
			"(!(-a))",
		},
		{
			"a + b + c",
			"((a + b) + c)",
		},
		{
			"a + b - c",
			"((a + b) - c)",
		},
		{
			"a * b * c",
			"((a * b) * c)",
		},
		{
			"a * b / c",
			"((a * b) / c)",
		},
		{
			"a + b / c",
			"(a + (b / c))",
		},
		{
			"a + b * c + d / e - f",
			"(((a + (b * c)) + (d / e)) - f)",
		},
		{
			"3 + 4; -5 * 5",
			"(3 + 4)((-5) * 5)",
		},
		{
			"5 > 4 == 3 < 4",
			"((5 > 4) == (3 < 4))",
		},
		{
			"5 < 4 != 3 > 4",
			"((5 < 4) != (3 > 4))",
		},
		{
			"3 + 4 * 5 == 3 * 1 + 4 * 5",
			"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
		},
		{
			"3 + 4 * 5 == 3 * 1 + 4 * 5",
			"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
		},
	}

	for _, tt := range tests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		actual := program.String()
		if actual != tt.expected {
			t.Errorf("expected=%q, got=%q", tt.expected, actual)
		}
	}
}

func testIdentifier(t *testing.T, exp ast.Expression, value string) bool {
	ident, ok := exp.(*ast.Identifier)
	if !ok {
		t.Errorf("exp not *ast.Identifier. got=%T", exp)
		return false
	}

	if ident.Value != value {
		t.Errorf("ident.Value not %s. got=%s", value, ident.Value)
		return false
	}

	if ident.TokenLiteral() != value {
		t.Errorf("ident.TokenLiteral not %s. got=%s", value, ident.TokenLiteral())
		return false
	}

	return true
}

func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) bool {
	switch v := expected.(type) {
	case int:
		return testIntegerLiteral(t, exp, int64(v))
	case int64:
		return testIntegerLiteral(t, exp, v)
	case string:
		return testIdentifier(t, exp, v)
	}
	t.Errorf("type of exp not handled. got=%T", exp)
	return false
}

func testInfixExpression(t *testing.T, exp ast.Expression, left interface{}, operator string, right interface{}) bool {
	opExp, ok := exp.(*ast.InfixExpression)
	if !ok {
		t.Errorf("exp is not ast.OperatorExpression. got=%T(%s)", exp, exp)
		return false
	}

	if !testLiteralExpression(t, opExp.Left, left) {
		return false
	}

	if opExp.Operator != operator {
		t.Errorf("exp.Operator is not '%s'. got=%q", operator, opExp.Operator)
		return false
	}

	if !testLiteralExpression(t, opExp.Right, right) {
		return false
	}

	return true
}
<commit_msg>add tests for boolean expression<commit_after>package parser

import (
	"fmt"
	"testing"

	"github.com/Tamrin007/monkey/ast"
	"github.com/Tamrin007/monkey/lexer"
)

func TestLetStatements(t *testing.T) {
	input := `
let x = 5;
let y = 10;
let foobar = 838383;
	`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParserErrors(t, p)
	if program == nil {
		t.Fatalf("ParseProgram() returned nil")
	}
	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	tests := []struct {
		expectedIdentifier string
	}{
		{"x"},
		{"y"},
		{"foobar"},
	}

	for i, tt := range tests {
		stmt := program.Statements[i]
		if !testLetStatement(t, stmt, tt.expectedIdentifier) {
			return
		}
	}
}

func checkParserErrors(t *testing.T, p *Parser) {
	errors := p.Errors()
	if len(errors) == 0 {
		return
	}

	t.Errorf("Parser has %d errors", len(errors))
	for _, msg := range errors {
		t.Errorf("parser error: %q", msg)
	}
	t.FailNow()
}

func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
	if s.TokenLiteral() != "let" {
		t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
		return false
	}

	letStmt, ok := s.(*ast.LetStatement)
	if !ok {
		t.Errorf("s not *ast.LetStatement. got=%T", s)
		return false
	}

	if letStmt.Name.TokenLiteral() != name {
		t.Errorf("s.Name not '%s'. got=%s", name, letStmt.Name)
		return false
	}

	return true
}

func TestReturnStatement(t *testing.T) {
	input := `
return 5;
return 10;
return 993322;
`

	l := lexer.New(input)
	p := New(l)

	program := p.ParseProgram()
	checkParserErrors(t, p)

	if len(program.Statements) != 3 {
		t.Fatalf("program.Statements does not contain 3 statements. got=%d", len(program.Statements))
	}

	for _, stmt := range program.Statements {
		returnStmt, ok := stmt.(*ast.ReturnStatement)
		if !ok {
			t.Errorf("stmt not *ast.returnStatement. got=%T", stmt)
			continue
		}
		if returnStmt.TokenLiteral() != "return" {
			t.Errorf("returnStmt.TokenLiteral not 'return', got %q", returnStmt.TokenLiteral())
		}
	}
}

func TestIdentifierExpression(t *testing.T) {
	input := "foobar;"

	l := lexer.New(input)
	p := New(l)
	program := p.ParseProgram()
	checkParserErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program has not enough statements. got=%d", len(program.Statements))
	}
	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	ident, ok := stmt.Expression.(*ast.Identifier)
	if !ok {
		t.Fatalf("exp not *ast.Identifier. got=%T", stmt.Expression)
	}
	if ident.Value != "foobar" {
		t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
	}
	if ident.TokenLiteral() != "foobar" {
		t.Errorf("ident.TokenLiteral not %s. got=%s", "foobar", ident.TokenLiteral())
	}
}

func TestIntegerLiteralExpression(t *testing.T) {
	input := "5;"

	l := lexer.New(input)
	p := New(l)
	program := p.ParseProgram()
	checkParserErrors(t, p)

	if len(program.Statements) != 1 {
		t.Fatalf("program has not enough statements. got=%d", len(program.Statements))
	}
	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
	if !ok {
		t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
	}

	literal, ok := stmt.Expression.(*ast.IntegerLiteral)
	if !ok {
		t.Fatalf("exp not *ast.IntegerLiteral. got=%T", stmt.Expression)
	}
	if literal.Value != 5 {
		t.Errorf("literal.Value not %d. got=%d", 5, literal.Value)
	}
	if literal.TokenLiteral() != "5" {
		t.Errorf("literal.TokenLiteral not %s. got=%s", "5", literal.TokenLiteral())
	}
}

func TestParsingPrefixExpressions(t *testing.T) {
	prefixTests := []struct {
		input        string
		operator     string
		integerValue int64
	}{
		{"!5;", "!", 5},
		{"-15;", "-", 15},
	}

	for _, tt := range prefixTests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program has not enough statements. got=%d", len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.PrefixExpression)
		if !ok {
			t.Fatalf("stmt is not ast.PrefixExpression. got=%T", stmt.Expression)
		}
		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}
		if !testIntegerLiteral(t, exp.Right, tt.integerValue) {
			return
		}
	}
}

func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
	integ, ok := il.(*ast.IntegerLiteral)
	if !ok {
		t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
		return false
	}
	if integ.Value != value {
		t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
		return false
	}

	if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
		t.Errorf("integ.TokenLiteral not %d. got=%s", value, integ.TokenLiteral())
		return false
	}

	return true
}

func TestParsingInfixExpressions(t *testing.T) {
	infixTests := []struct {
		input      string
		leftValue  int64
		operator   string
		rightValue int64
	}{
		{"5 + 5", 5, "+", 5},
		{"5 - 5", 5, "-", 5},
		{"5 * 5", 5, "*", 5},
		{"5 / 5", 5, "/", 5},
		{"5 > 5", 5, ">", 5},
		{"5 < 5", 5, "<", 5},
		{"5 == 5", 5, "==", 5},
		{"5 != 5", 5, "!=", 5},
	}

	for _, tt := range infixTests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program.Statements does not contain %d statements. got=%d\n", 1, len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", program.Statements[0])
		}

		exp, ok := stmt.Expression.(*ast.InfixExpression)
		if !ok {
			t.Fatalf("exp is not ast.InfixExpression. got=%T", stmt.Expression)
		}

		if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
			return
		}

		if exp.Operator != tt.operator {
			t.Fatalf("exp.Operator is not '%s'. got=%s", tt.operator, exp.Operator)
		}

		if !testIntegerLiteral(t, exp.Right, tt.rightValue) {
			return
		}
	}
}

func TestOperatorPrecedenceParsing(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{
			"-a * b",
			"((-a) * b)",
		},
		{
			"!-a",
			"(!(-a))",
		},
		{
			"a + b + c",
			"((a + b) + c)",
		},
		{
			"a + b - c",
			"((a + b) - c)",
		},
		{
			"a * b * c",
			"((a * b) * c)",
		},
		{
			"a * b / c",
			"((a * b) / c)",
		},
		{
			"a + b / c",
			"(a + (b / c))",
		},
		{
			"a + b * c + d / e - f",
			"(((a + (b * c)) + (d / e)) - f)",
		},
		{
			"3 + 4; -5 * 5",
			"(3 + 4)((-5) * 5)",
		},
		{
			"5 > 4 == 3 < 4",
			"((5 > 4) == (3 < 4))",
		},
		{
			"5 < 4 != 3 > 4",
			"((5 < 4) != (3 > 4))",
		},
		{
			"3 + 4 * 5 == 3 * 1 + 4 * 5",
			"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
		},
		{
			"3 + 4 * 5 == 3 * 1 + 4 * 5",
			"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
		},
	}

	for _, tt := range tests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		actual := program.String()
		if actual != tt.expected {
			t.Errorf("expected=%q, got=%q", tt.expected, actual)
		}
	}
}

func testIdentifier(t *testing.T, exp ast.Expression, value string) bool {
	ident, ok := exp.(*ast.Identifier)
	if !ok {
		t.Errorf("exp not *ast.Identifier. got=%T", exp)
		return false
	}

	if ident.Value != value {
		t.Errorf("ident.Value not %s. got=%s", value, ident.Value)
		return false
	}

	if ident.TokenLiteral() != value {
		t.Errorf("ident.TokenLiteral not %s. got=%s", value, ident.TokenLiteral())
		return false
	}

	return true
}

func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) bool {
	switch v := expected.(type) {
	case int:
		return testIntegerLiteral(t, exp, int64(v))
	case int64:
		return testIntegerLiteral(t, exp, v)
	case string:
		return testIdentifier(t, exp, v)
	}
	t.Errorf("type of exp not handled. got=%T", exp)
	return false
}

func testInfixExpression(t *testing.T, exp ast.Expression, left interface{}, operator string, right interface{}) bool {
	opExp, ok := exp.(*ast.InfixExpression)
	if !ok {
		t.Errorf("exp is not ast.OperatorExpression. got=%T(%s)", exp, exp)
		return false
	}

	if !testLiteralExpression(t, opExp.Left, left) {
		return false
	}

	if opExp.Operator != operator {
		t.Errorf("exp.Operator is not '%s'. got=%q", operator, opExp.Operator)
		return false
	}

	if !testLiteralExpression(t, opExp.Right, right) {
		return false
	}

	return true
}

func TestBooleanExpression(t *testing.T) {
	tests := []struct {
		input           string
		expectedBoolean bool
	}{
		{"true;", true},
		{"false;", false},
	}

	for _, tt := range tests {
		l := lexer.New(tt.input)
		p := New(l)
		program := p.ParseProgram()
		checkParserErrors(t, p)

		if len(program.Statements) != 1 {
			t.Fatalf("program has not enough statements. got=%d",
				len(program.Statements))
		}

		stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
		if !ok {
			t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
				program.Statements[0])
		}

		boolean, ok := stmt.Expression.(*ast.Boolean)
		if !ok {
			t.Fatalf("exp not *ast.Boolean. got=%T", stmt.Expression)
		}
		if boolean.Value != tt.expectedBoolean {
			t.Errorf("boolean.Value not %t. got=%t", tt.expectedBoolean,
				boolean.Value)
		}
	}
}
<|endoftext|>