<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are beling developed and are very
// experimental at this point.  They will most likley change so please use the
// low level functions if that's a problem.

// Package discordgo provides Discord binding for Go
package discordgo

import (
	"fmt"
	"reflect"
)

// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.11.0-alpha"

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
	}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}

	// Variables used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

			// If third string exists, it must be an auth token.
			if len(v) > 2 {
				s.Token = v[2]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			//		case Config:
			// TODO: Parse configuration

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord, if a token was specified
	// Discord will verify it for free, or log the user in if it is
	// invalid.
	if pass == "" {
		s.Token = auth
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			return
		}
	}

	// The Session is now able to have RestAPI methods called on it.
	// It is recommended that you now call Open() so that events will trigger.

	return
}

// validateHandler takes an event handler func, and returns the type of event.
// eg.
//     Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
//     will return the reflect.Type of *discordgo.MessageCreate
func (s *Session) validateHandler(handler interface{}) reflect.Type {
	handlerType := reflect.TypeOf(handler)

	if handlerType.NumIn() != 2 {
		panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
	}

	if handlerType.In(0) != reflect.TypeOf(s) {
		panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
	}

	eventType := handlerType.In(1)

	// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
	if eventType.Kind() == reflect.Interface {
		eventType = nil
	}

	return eventType
}

// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the interface fires.
// eventToInterface in events.go has a list of all the Discord WSAPI events
// and their respective interface.
// eg:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
//     })
//
// or:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
//     })
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {
	s.initialize()

	eventType := s.validateHandler(handler)

	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	h := reflect.ValueOf(handler)

	handlers := s.handlers[eventType]
	if handlers == nil {
		handlers = []reflect.Value{}
	}
	s.handlers[eventType] = append(handlers, h)

	// This must be done as we need a consistent reference to the
	// reflected value, otherwise a RemoveHandler method would have
	// been nice.
	return func() {
		s.handlersMu.Lock()
		defer s.handlersMu.Unlock()

		handlers := s.handlers[eventType]
		for i, v := range handlers {
			if h == v {
				s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
				return
			}
		}
	}
}

// handle calls any handlers that match the event type and any handlers of
// interface{}.
func (s *Session) handle(event interface{}) {
	s.handlersMu.RLock()
	defer s.handlersMu.RUnlock()

	if s.handlers == nil {
		return
	}

	handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}

	if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
		for _, handler := range handlers {
			handler.Call(handlerParameters)
		}
	}

	if handlers, ok := s.handlers[nil]; ok {
		for _, handler := range handlers {
			handler.Call(handlerParameters)
		}
	}
}

// initialize adds all internal handlers and state tracking handlers.
func (s *Session) initialize() {
	s.handlersMu.Lock()
	if s.handlers != nil {
		s.handlersMu.Unlock()
		return
	}

	s.handlers = map[interface{}][]reflect.Value{}
	s.handlersMu.Unlock()

	s.AddHandler(s.onEvent)
	s.AddHandler(s.onReady)
	s.AddHandler(s.onVoiceServerUpdate)
	s.AddHandler(s.onVoiceStateUpdate)
	s.AddHandler(s.State.onInterface)
}

// onEvent handles events that are unhandled or errored while unmarshalling
func (s *Session) onEvent(se *Session, e *Event) {
	printEvent(e)
}

// onReady handles the ready event.
func (s *Session) onReady(se *Session, r *Ready) {
	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}
<commit_msg>Bump to v0.11.0<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains high level helper functions and easy entry points for the
// entire discordgo package.  These functions are beling developed and are very
// experimental at this point.  They will most likley change so please use the
// low level functions if that's a problem.

// Package discordgo provides Discord binding for Go
package discordgo

import (
	"fmt"
	"reflect"
)

// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
const VERSION = "0.11.0"

// New creates a new Discord session and will automate some startup
// tasks if given enough information to do so.  Currently you can pass zero
// arguments and it will return an empty Discord session.
// There are 3 ways to call New:
//     With a single auth token - All requests will use the token blindly,
//         no verification of the token will be done and requests may fail.
//     With an email and password - Discord will sign in with the provided
//         credentials.
//     With an email, password and auth token - Discord will verify the auth
//         token, if it is invalid it will sign in with the provided
//         credentials. This is the Discord recommended way to sign in.
func New(args ...interface{}) (s *Session, err error) {

	// Create an empty Session interface.
	s = &Session{
		State:                  NewState(),
		StateEnabled:           true,
		Compress:               true,
		ShouldReconnectOnError: true,
	}

	// If no arguments are passed return the empty Session interface.
	// Later I will add default values, if appropriate.
	if args == nil {
		return
	}

	// Variables used below when parsing func arguments
	var auth, pass string

	// Parse passed arguments
	for _, arg := range args {

		switch v := arg.(type) {

		case []string:
			if len(v) > 3 {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			// First string is either token or username
			if len(v) > 0 {
				auth = v[0]
			}

			// If second string exists, it must be a password.
			if len(v) > 1 {
				pass = v[1]
			}

			// If third string exists, it must be an auth token.
			if len(v) > 2 {
				s.Token = v[2]
			}

		case string:
			// First string must be either auth token or username.
			// Second string must be a password.
			// Only 2 input strings are supported.

			if auth == "" {
				auth = v
			} else if pass == "" {
				pass = v
			} else if s.Token == "" {
				s.Token = v
			} else {
				err = fmt.Errorf("Too many string parameters provided.")
				return
			}

			//		case Config:
			// TODO: Parse configuration

		default:
			err = fmt.Errorf("Unsupported parameter type provided.")
			return
		}
	}

	// If only one string was provided, assume it is an auth token.
	// Otherwise get auth token from Discord, if a token was specified
	// Discord will verify it for free, or log the user in if it is
	// invalid.
	if pass == "" {
		s.Token = auth
	} else {
		err = s.Login(auth, pass)
		if err != nil || s.Token == "" {
			err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
			return
		}
	}

	// The Session is now able to have RestAPI methods called on it.
	// It is recommended that you now call Open() so that events will trigger.

	return
}

// validateHandler takes an event handler func, and returns the type of event.
// eg.
//     Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
//     will return the reflect.Type of *discordgo.MessageCreate
func (s *Session) validateHandler(handler interface{}) reflect.Type {
	handlerType := reflect.TypeOf(handler)

	if handlerType.NumIn() != 2 {
		panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
	}

	if handlerType.In(0) != reflect.TypeOf(s) {
		panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
	}

	eventType := handlerType.In(1)

	// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
	if eventType.Kind() == reflect.Interface {
		eventType = nil
	}

	return eventType
}

// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the interface fires.
// eventToInterface in events.go has a list of all the Discord WSAPI events
// and their respective interface.
// eg:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
//     })
//
// or:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
//     })
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {
	s.initialize()

	eventType := s.validateHandler(handler)

	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	h := reflect.ValueOf(handler)

	handlers := s.handlers[eventType]
	if handlers == nil {
		handlers = []reflect.Value{}
	}
	s.handlers[eventType] = append(handlers, h)

	// This must be done as we need a consistent reference to the
	// reflected value, otherwise a RemoveHandler method would have
	// been nice.
	return func() {
		s.handlersMu.Lock()
		defer s.handlersMu.Unlock()

		handlers := s.handlers[eventType]
		for i, v := range handlers {
			if h == v {
				s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
				return
			}
		}
	}
}

// handle calls any handlers that match the event type and any handlers of
// interface{}.
func (s *Session) handle(event interface{}) {
	s.handlersMu.RLock()
	defer s.handlersMu.RUnlock()

	if s.handlers == nil {
		return
	}

	handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}

	if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
		for _, handler := range handlers {
			handler.Call(handlerParameters)
		}
	}

	if handlers, ok := s.handlers[nil]; ok {
		for _, handler := range handlers {
			handler.Call(handlerParameters)
		}
	}
}

// initialize adds all internal handlers and state tracking handlers.
func (s *Session) initialize() {
	s.handlersMu.Lock()
	if s.handlers != nil {
		s.handlersMu.Unlock()
		return
	}

	s.handlers = map[interface{}][]reflect.Value{}
	s.handlersMu.Unlock()

	s.AddHandler(s.onEvent)
	s.AddHandler(s.onReady)
	s.AddHandler(s.onVoiceServerUpdate)
	s.AddHandler(s.onVoiceStateUpdate)
	s.AddHandler(s.State.onInterface)
}

// onEvent handles events that are unhandled or errored while unmarshalling
func (s *Session) onEvent(se *Session, e *Event) {
	printEvent(e)
}

// onReady handles the ready event.
func (s *Session) onReady(se *Session, r *Ready) {
	go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
}
<|endoftext|>


<commit_before>package vm

type Display struct {
}

func (dm *Display) XorPixel(x int, y int) bool {
	return true
}
<commit_msg>Adding Display methods. Look ma, no tests<commit_after>package vm

const BLANK = "_"

type Display struct {
	canvas [][]string
	pixels [][]string

	// Display dimensions
	width  int
	height int

	xScale int
	yScale int
}

func NewDisplay() *Display {
	return &Display{canvas: [][]string{}, width: 64, height: 32}
}

func (display *Display) Clear() {
	for i := 0; i < display.width; i++ {
		for j := 0; j < display.height; j++ {
			display.canvas[i][j] = BLANK
		}
	}
}

func (display *Display) XorPixel(x int, y int) bool {
	// Wrap around vertically
	if x > display.width {
		x -= display.width
	} else if x < 0 {
		x += display.width
	}

	// Wrap around horizontally
	if y > display.height {
		y -= display.height
	} else if y < 0 {
		y += display.height
	}

	// Set the pixel state
	active := display.canvas[x][y] == BLANK
	if active {
		// Javascript was: var active = this.canvas[x][y] ^= 1
		display.canvas[x][y] = "*"
	} else {
		display.canvas[x][y] = " "
	}
	return active
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"regexp"

	"github.com/jangler/edit"
	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/sdl_ttf"
)

const padPx = 2 // number of pixels used to pad UI elements

var (
	// colors for use with FillRect
	bgColor       uint32 = 0xffffffff
	fgColor       uint32 = 0xff2f2f2f
	statusBgColor uint32 = 0xffe2e2e2

	// colors for use with ttf functions
	bgColorSDL       = sdl.Color{0xff, 0xff, 0xff, 0xff}
	fgColorSDL       = sdl.Color{0x2f, 0x2f, 0x2f, 0xff}
	statusBgColorSDL = sdl.Color{0xe2, 0xe2, 0xe2, 0xff}
	commentColor     = sdl.Color{0x3f, 0x5a, 0x8d, 0xff}
	keywordColor     = sdl.Color{0x3a, 0x63, 0x41, 0xff}
	literalColor     = sdl.Color{0x8e, 0x4a, 0x43, 0xff}
)

var fontWidth int

// Pane is a buffer with associated metadata.
type Pane struct {
	*edit.Buffer
	Title      string
	TabWidth   int
	Cols, Rows int
}

// See ensurees that the mark with ID id is visible on the pane's screen.
func (p Pane) See(id int) {
	index := p.IndexFromMark(id)
	_, row := p.CoordsFromIndex(index)
	if row < -p.Rows {
		p.Scroll(row - p.Rows/2)
	} else if row < 0 {
		p.Scroll(row)
	} else if row >= p.Rows*2 {
		p.Scroll(row + 1 - p.Rows/2)
	} else if row >= p.Rows {
		p.Scroll(row + 1 - p.Rows)
	}
}

// createWindow returns a new SDL window of appropriate size given font, and
// titled title.
func createWindow(title string, font *ttf.Font) *sdl.Window {
	width := fontWidth*80 + padPx*2
	height := font.Height()*27 + padPx*6
	win, err := sdl.CreateWindow(title, sdl.WINDOWPOS_UNDEFINED,
		sdl.WINDOWPOS_UNDEFINED, width, height, sdl.WINDOW_RESIZABLE)
	if err != nil {
		log.Fatal(err)
	}
	return win
}

// drawBuffer draws the displayed contents of pane to dst using font.
func drawBuffer(pane *Pane, font *ttf.Font, dst *sdl.Surface, focused bool) {
	x, y := padPx, padPx
	mark := pane.IndexFromMark(insertMark)
	col, row := pane.CoordsFromIndex(mark)
	sel := pane.IndexFromMark(selMark)
	selStart, selEnd := sel, mark
	if mark.Less(sel) {
		selStart, selEnd = mark, sel
	}
	startCol, startRow := pane.CoordsFromIndex(selStart)
	endCol, endRow := pane.CoordsFromIndex(selEnd)
	for i, line := range pane.DisplayLines() {
		c := 0
		for e := line.Front(); e != nil; e = e.Next() {
			text := e.Value.(edit.Fragment).Text
			fg := fgColorSDL
			switch e.Value.(edit.Fragment).Tag {
			case commentId:
				fg = commentColor
			case keywordId:
				fg = keywordColor
			case literalId:
				fg = literalColor
			}
			if i >= startRow && i <= endRow {
				var pre, mid, post string
				if i == startRow && c < startCol {
					if startCol-c < len(text) {
						pre = text[:startCol-c]
					} else {
						pre = text
					}
				}
				if i == endRow && c+len(text) > endCol {
					if c < endCol {
						post = text[endCol-c:]
					} else {
						post = text
					}
				}
				mid = text[len(pre) : len(text)-len(post)]
				x = drawString(font, pre, fg, bgColorSDL, dst, x, y)
				x = drawString(font, mid, fg, statusBgColorSDL, dst, x, y)
				x = drawString(font, post, fg, bgColorSDL, dst, x, y)
				c += len(text)
			} else {
				x = drawString(font, text, fg, bgColorSDL, dst, x, y)
			}
		}
		if focused && i == row {
			dst.FillRect(&sdl.Rect{int32(padPx + fontWidth*col), int32(y),
				1, int32(font.Height())}, fgColor)
		}
		y += font.Height()
		x = padPx
	}
}

// drawString draws s to dst at (x, y) using font, and returns x plus the width
// of the text in pixels.
func drawString(font *ttf.Font, s string, fg, bg sdl.Color, dst *sdl.Surface,
	x, y int) int {
	if s != "" {
		surf, err := font.RenderUTF8_Shaded(s, fg, bg)
		if err != nil {
			panic(err)
		}
		defer surf.Free()
		err = surf.Blit(&sdl.Rect{0, 0, surf.W, surf.H}, dst,
			&sdl.Rect{int32(x), int32(y), 0, 0})
		if err != nil {
			log.Fatal(err)
		}
		x += int(surf.W)
	}
	return x
}

// drawStatusLine draws s at the bottom of dst using font.
func drawStatusLine(dst *sdl.Surface, font *ttf.Font, s string,
	input *edit.Buffer, pane *Pane, focused bool) {
	// draw background
	bgRect := sdl.Rect{
		0,
		dst.H - int32(font.Height()) - padPx*2,
		dst.W,
		int32(font.Height()) + padPx*2,
	}
	dst.FillRect(&bgRect, statusBgColor)

	// draw status text
	x, y := padPx, int(dst.H)-font.Height()-padPx
	x = drawString(font, s, fgColorSDL, statusBgColorSDL, dst, x, y)

	if focused {
		// draw input text and cursor
		drawString(font, input.Get(edit.Index{1, 0}, input.End()), fgColorSDL,
			statusBgColorSDL, dst, x, y)
		index := input.IndexFromMark(insertMark)
		dst.FillRect(&sdl.Rect{int32(x + fontWidth*index.Char), int32(y),
			1, int32(font.Height())}, fgColor)
	} else {
		// draw cursor pos
		index := pane.IndexFromMark(insertMark)
		line := pane.Get(edit.Index{index.Line, 0}, index)
		col := 0
		for _, ch := range line {
			if ch == '\t' {
				col += pane.TabWidth - col%pane.TabWidth
			} else {
				col++
			}
		}
		cursorPos := fmt.Sprintf("%d,%d", index.Line, index.Char)
		if col != index.Char {
			cursorPos += fmt.Sprintf("-%d", col)
		}
		drawString(font, cursorPos, fgColorSDL, statusBgColorSDL, dst,
			int(dst.W)-padPx-fontWidth*17, int(dst.H)-font.Height()-padPx)

		// draw scroll percent
		f := pane.ScrollFraction()
		scrollStr := fmt.Sprintf("%d%%", int(f*100))
		if f < 0 {
			scrollStr = "All"
		}
		drawString(font, scrollStr, fgColorSDL, statusBgColorSDL, dst,
			int(dst.W)-padPx-fontWidth*4, int(dst.H)-font.Height()-padPx)
	}
}

// paneSpace returns the number of vertical pixels available to each pane,
// sized equally out of n panes.
func paneSpace(height, n int, font *ttf.Font) int {
	return (height - font.Height() - padPx*2) / n
}

// bufSize returns the number of rows and columns available to each pane,
// sized equally out of n panes.
func bufSize(width, height, n int, font *ttf.Font) (cols, rows int) {
	cols = (width - padPx*2) / fontWidth
	rows = paneSpace(height, n, font) / font.Height()
	return
}

// RenderContext contains information needed to update the display.
type RenderContext struct {
	Pane   *Pane
	Input  *edit.Buffer
	Focus  *edit.Buffer
	Status string
	Font   *ttf.Font
	Window *sdl.Window
	Regexp *regexp.Regexp
}

// render updates the display.
func render(rc *RenderContext) {
	surf, err := rc.Window.GetSurface()
	if err != nil {
		log.Fatal(err)
	}
	surf.FillRect(&sdl.Rect{0, 0, surf.W, surf.H}, bgColor)
	paneFocused := rc.Focus == rc.Pane.Buffer
	defer func() {
		if err := recover(); err != nil {
			log.Print(err)
			rc.Font = getFont()
			render(rc)
		}
	}()
	drawBuffer(rc.Pane, rc.Font, surf, paneFocused)
	drawStatusLine(surf, rc.Font, rc.Status, rc.Input, rc.Pane, !paneFocused)
	rc.Window.UpdateSurface()
}
<commit_msg>Don't display cursor, scroll along with status message<commit_after>package main

import (
	"fmt"
	"log"
	"regexp"

	"github.com/jangler/edit"
	"github.com/veandco/go-sdl2/sdl"
	"github.com/veandco/go-sdl2/sdl_ttf"
)

const padPx = 2 // number of pixels used to pad UI elements

var (
	// colors for use with FillRect
	bgColor       uint32 = 0xffffffff
	fgColor       uint32 = 0xff2f2f2f
	statusBgColor uint32 = 0xffe2e2e2

	// colors for use with ttf functions
	bgColorSDL       = sdl.Color{0xff, 0xff, 0xff, 0xff}
	fgColorSDL       = sdl.Color{0x2f, 0x2f, 0x2f, 0xff}
	statusBgColorSDL = sdl.Color{0xe2, 0xe2, 0xe2, 0xff}
	commentColor     = sdl.Color{0x3f, 0x5a, 0x8d, 0xff}
	keywordColor     = sdl.Color{0x3a, 0x63, 0x41, 0xff}
	literalColor     = sdl.Color{0x8e, 0x4a, 0x43, 0xff}
)

var fontWidth int

// Pane is a buffer with associated metadata.
type Pane struct {
	*edit.Buffer
	Title      string
	TabWidth   int
	Cols, Rows int
}

// See ensurees that the mark with ID id is visible on the pane's screen.
func (p Pane) See(id int) {
	index := p.IndexFromMark(id)
	_, row := p.CoordsFromIndex(index)
	if row < -p.Rows {
		p.Scroll(row - p.Rows/2)
	} else if row < 0 {
		p.Scroll(row)
	} else if row >= p.Rows*2 {
		p.Scroll(row + 1 - p.Rows/2)
	} else if row >= p.Rows {
		p.Scroll(row + 1 - p.Rows)
	}
}

// createWindow returns a new SDL window of appropriate size given font, and
// titled title.
func createWindow(title string, font *ttf.Font) *sdl.Window {
	width := fontWidth*80 + padPx*2
	height := font.Height()*27 + padPx*6
	win, err := sdl.CreateWindow(title, sdl.WINDOWPOS_UNDEFINED,
		sdl.WINDOWPOS_UNDEFINED, width, height, sdl.WINDOW_RESIZABLE)
	if err != nil {
		log.Fatal(err)
	}
	return win
}

// drawBuffer draws the displayed contents of pane to dst using font.
func drawBuffer(pane *Pane, font *ttf.Font, dst *sdl.Surface, focused bool) {
	x, y := padPx, padPx
	mark := pane.IndexFromMark(insertMark)
	col, row := pane.CoordsFromIndex(mark)
	sel := pane.IndexFromMark(selMark)
	selStart, selEnd := sel, mark
	if mark.Less(sel) {
		selStart, selEnd = mark, sel
	}
	startCol, startRow := pane.CoordsFromIndex(selStart)
	endCol, endRow := pane.CoordsFromIndex(selEnd)
	for i, line := range pane.DisplayLines() {
		c := 0
		for e := line.Front(); e != nil; e = e.Next() {
			text := e.Value.(edit.Fragment).Text
			fg := fgColorSDL
			switch e.Value.(edit.Fragment).Tag {
			case commentId:
				fg = commentColor
			case keywordId:
				fg = keywordColor
			case literalId:
				fg = literalColor
			}
			if i >= startRow && i <= endRow {
				var pre, mid, post string
				if i == startRow && c < startCol {
					if startCol-c < len(text) {
						pre = text[:startCol-c]
					} else {
						pre = text
					}
				}
				if i == endRow && c+len(text) > endCol {
					if c < endCol {
						post = text[endCol-c:]
					} else {
						post = text
					}
				}
				mid = text[len(pre) : len(text)-len(post)]
				x = drawString(font, pre, fg, bgColorSDL, dst, x, y)
				x = drawString(font, mid, fg, statusBgColorSDL, dst, x, y)
				x = drawString(font, post, fg, bgColorSDL, dst, x, y)
				c += len(text)
			} else {
				x = drawString(font, text, fg, bgColorSDL, dst, x, y)
			}
		}
		if focused && i == row {
			dst.FillRect(&sdl.Rect{int32(padPx + fontWidth*col), int32(y),
				1, int32(font.Height())}, fgColor)
		}
		y += font.Height()
		x = padPx
	}
}

// drawString draws s to dst at (x, y) using font, and returns x plus the width
// of the text in pixels.
func drawString(font *ttf.Font, s string, fg, bg sdl.Color, dst *sdl.Surface,
	x, y int) int {
	if s != "" {
		surf, err := font.RenderUTF8_Shaded(s, fg, bg)
		if err != nil {
			panic(err)
		}
		defer surf.Free()
		err = surf.Blit(&sdl.Rect{0, 0, surf.W, surf.H}, dst,
			&sdl.Rect{int32(x), int32(y), 0, 0})
		if err != nil {
			log.Fatal(err)
		}
		x += int(surf.W)
	}
	return x
}

// drawStatusLine draws s at the bottom of dst using font.
func drawStatusLine(dst *sdl.Surface, font *ttf.Font, s string,
	input *edit.Buffer, pane *Pane, focused bool) {
	// draw background
	bgRect := sdl.Rect{
		0,
		dst.H - int32(font.Height()) - padPx*2,
		dst.W,
		int32(font.Height()) + padPx*2,
	}
	dst.FillRect(&bgRect, statusBgColor)

	// draw status text
	x, y := padPx, int(dst.H)-font.Height()-padPx
	x = drawString(font, s, fgColorSDL, statusBgColorSDL, dst, x, y)

	if focused {
		// draw input text and cursor
		drawString(font, input.Get(edit.Index{1, 0}, input.End()), fgColorSDL,
			statusBgColorSDL, dst, x, y)
		index := input.IndexFromMark(insertMark)
		dst.FillRect(&sdl.Rect{int32(x + fontWidth*index.Char), int32(y),
			1, int32(font.Height())}, fgColor)
	} else if s == pane.Title {
		// draw cursor pos
		index := pane.IndexFromMark(insertMark)
		line := pane.Get(edit.Index{index.Line, 0}, index)
		col := 0
		for _, ch := range line {
			if ch == '\t' {
				col += pane.TabWidth - col%pane.TabWidth
			} else {
				col++
			}
		}
		cursorPos := fmt.Sprintf("%d,%d", index.Line, index.Char)
		if col != index.Char {
			cursorPos += fmt.Sprintf("-%d", col)
		}
		drawString(font, cursorPos, fgColorSDL, statusBgColorSDL, dst,
			int(dst.W)-padPx-fontWidth*17, int(dst.H)-font.Height()-padPx)

		// draw scroll percent
		f := pane.ScrollFraction()
		scrollStr := fmt.Sprintf("%d%%", int(f*100))
		if f < 0 {
			scrollStr = "All"
		}
		drawString(font, scrollStr, fgColorSDL, statusBgColorSDL, dst,
			int(dst.W)-padPx-fontWidth*4, int(dst.H)-font.Height()-padPx)
	}
}

// paneSpace returns the number of vertical pixels available to each pane,
// sized equally out of n panes.
func paneSpace(height, n int, font *ttf.Font) int {
	return (height - font.Height() - padPx*2) / n
}

// bufSize returns the number of rows and columns available to each pane,
// sized equally out of n panes.
func bufSize(width, height, n int, font *ttf.Font) (cols, rows int) {
	cols = (width - padPx*2) / fontWidth
	rows = paneSpace(height, n, font) / font.Height()
	return
}

// RenderContext contains information needed to update the display.
type RenderContext struct {
	Pane   *Pane
	Input  *edit.Buffer
	Focus  *edit.Buffer
	Status string
	Font   *ttf.Font
	Window *sdl.Window
	Regexp *regexp.Regexp
}

// render updates the display.
func render(rc *RenderContext) {
	surf, err := rc.Window.GetSurface()
	if err != nil {
		log.Fatal(err)
	}
	surf.FillRect(&sdl.Rect{0, 0, surf.W, surf.H}, bgColor)
	paneFocused := rc.Focus == rc.Pane.Buffer
	defer func() {
		if err := recover(); err != nil {
			log.Print(err)
			rc.Font = getFont()
			render(rc)
		}
	}()
	drawBuffer(rc.Pane, rc.Font, surf, paneFocused)
	drawStatusLine(surf, rc.Font, rc.Status, rc.Input, rc.Pane, !paneFocused)
	rc.Window.UpdateSurface()
}
<|endoftext|>


<commit_before>package dns

import (
	"errors"
	"net"
	"strings"
)

const spfPrefix = "v=spf1 "
const spfPrefixTrim = "v=spf1"

func checkSPFVersion(spf []string) bool {
	if len(spf) == 0 {
		return false
	}

	first := spf[0]

	if len(first) >= len(spfPrefix) && strings.HasPrefix(first, spfPrefix) {
		return true
	}

	if len(first) == len(spfPrefixTrim) && first == spfPrefixTrim {
		return true
	}

	return false
}

// LookupSPF retireves SPF query from domain in question.
// If also carries out initial validation on whether the TXT record is an SPF record (by comparing the string to a 'v=spf1' value)
// In the future function should properly handle all known DNS related errors as well as recurively query for SPF records
// TODO(zaccone): Handle typical DNS errors and recusive calls
func LookupSPF(domain string) ([]string, error) {
	var spfRecords []string
	var err error
	if spfRecords, err = net.LookupTXT(domain); err != nil {
		//TODO(zaccone): Handle DNS errors
		return nil, err
	}

	if checkSPFVersion(spfRecords) == false {
		return nil, errors.New("invalid SPF record")
	}

	return spfRecords, nil
}
<commit_msg>Improve error log after incorrect SPF version.<commit_after>package dns

import (
	"errors"
	"net"
	"strings"
)

const spfPrefix = "v=spf1 "
const spfPrefixTrim = "v=spf1"

func checkSPFVersion(spf []string) bool {
	if len(spf) == 0 {
		return false
	}

	first := spf[0]

	if len(first) >= len(spfPrefix) && strings.HasPrefix(first, spfPrefix) {
		return true
	}

	if len(first) == len(spfPrefixTrim) && first == spfPrefixTrim {
		return true
	}

	return false
}

// LookupSPF retireves SPF query from domain in question.
// If also carries out initial validation on whether the TXT record is an SPF record (by comparing the string to a 'v=spf1' value)
// In the future function should properly handle all known DNS related errors as well as recurively query for SPF records
// TODO(zaccone): Handle typical DNS errors and recusive calls
func LookupSPF(domain string) ([]string, error) {
	var spfRecords []string
	var err error
	if spfRecords, err = net.LookupTXT(domain); err != nil {
		/*
			Note(zaccone): We need to handle DNS related errors in the upper layer, as depending on error type a SPF related
			result/exception will be raised.
		*/
		return nil, err
	}

	if checkSPFVersion(spfRecords) == false {
		return nil, errors.New(strings.Join([]string{"Invalid SPF record: ", strings.Join(spfRecords, " ")}, " "))
	}

	return spfRecords, nil
}
<|endoftext|>


<commit_before>// Package wkbcommon contains code common to WKB and EWKB encoding.
package wkbcommon

import (
	"encoding/binary"
	"io"
	"math"
)

func readFloat(buf []byte, byteOrder binary.ByteOrder) float64 {
	u := byteOrder.Uint64(buf)
	return math.Float64frombits(u)
}

func ReadUInt32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
	var buf [4]byte
	if _, err := io.ReadFull(r, buf[:]); err != nil {
		return 0, err
	}
	return byteOrder.Uint32(buf[:]), nil
}

func ReadFloatArray(r io.Reader, byteOrder binary.ByteOrder, array []float64) error {
	buf := make([]byte, 8*len(array))
	if _, err := io.ReadFull(r, buf); err != nil {
		return err
	}
	// Convert to an array of floats
	for i := range array {
		array[i] = readFloat(buf[8*i:], byteOrder)
	}
	return nil
}

func ReadByte(r io.Reader) (byte, error) {
	var buf [1]byte
	if _, err := r.Read(buf[:]); err != nil {
		return 0, err
	}
	return buf[0], nil
}

func writeFloat(buf []byte, byteOrder binary.ByteOrder, value float64) {
	u := math.Float64bits(value)
	byteOrder.PutUint64(buf, u)
}

func WriteFloatArray(w io.Writer, byteOrder binary.ByteOrder, array []float64) error {
	buf := make([]byte, 8*len(array))
	for i, f := range array {
		writeFloat(buf[8*i:], byteOrder, f)
	}
	_, err := w.Write(buf)
	return err
}

func WriteUInt32(w io.Writer, byteOrder binary.ByteOrder, value uint32) error {
	var buf [4]byte
	byteOrder.PutUint32(buf[:], value)
	_, err := w.Write(buf[:])
	return err
}

func WriteByte(w io.Writer, value byte) error {
	var buf [1]byte
	buf[0] = value
	_, err := w.Write(buf[:])
	return err
}
<commit_msg>Add documentation to satisfy golint<commit_after>// Package wkbcommon contains code common to WKB and EWKB encoding.
package wkbcommon

import (
	"encoding/binary"
	"io"
	"math"
)

func readFloat(buf []byte, byteOrder binary.ByteOrder) float64 {
	u := byteOrder.Uint64(buf)
	return math.Float64frombits(u)
}

// ReadUInt32 reads a uint32 from r.
func ReadUInt32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
	var buf [4]byte
	if _, err := io.ReadFull(r, buf[:]); err != nil {
		return 0, err
	}
	return byteOrder.Uint32(buf[:]), nil
}

// ReadFloatArray reads a []float64 from r.
func ReadFloatArray(r io.Reader, byteOrder binary.ByteOrder, array []float64) error {
	buf := make([]byte, 8*len(array))
	if _, err := io.ReadFull(r, buf); err != nil {
		return err
	}
	// Convert to an array of floats
	for i := range array {
		array[i] = readFloat(buf[8*i:], byteOrder)
	}
	return nil
}

// ReadByte reads a byte from r.
func ReadByte(r io.Reader) (byte, error) {
	var buf [1]byte
	if _, err := r.Read(buf[:]); err != nil {
		return 0, err
	}
	return buf[0], nil
}

func writeFloat(buf []byte, byteOrder binary.ByteOrder, value float64) {
	u := math.Float64bits(value)
	byteOrder.PutUint64(buf, u)
}

// WriteFloatArray writes a []float64 to w.
func WriteFloatArray(w io.Writer, byteOrder binary.ByteOrder, array []float64) error {
	buf := make([]byte, 8*len(array))
	for i, f := range array {
		writeFloat(buf[8*i:], byteOrder, f)
	}
	_, err := w.Write(buf)
	return err
}

// WriteUInt32 writes a uint32 to w.
func WriteUInt32(w io.Writer, byteOrder binary.ByteOrder, value uint32) error {
	var buf [4]byte
	byteOrder.PutUint32(buf[:], value)
	_, err := w.Write(buf[:])
	return err
}

// WriteByte wrties a byte to w.
func WriteByte(w io.Writer, value byte) error {
	var buf [1]byte
	buf[0] = value
	_, err := w.Write(buf[:])
	return err
}
<|endoftext|>


<commit_before>package jsonrpc

import (
	"context"
	"fmt"
	"net/http"

	"github.com/intel-go/fastjson"
)

// Handler links a method of JSON-RPC request.
type Handler interface {
	ServeJSONRPC(c context.Context, params *fastjson.RawMessage) (result interface{}, err *Error)
}

// ServeHTTP provides basic JSON-RPC handling.
func (mr *MethodRepository) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	rs, batch, err := ParseRequest(r)
	if err != nil {
		err := SendResponse(w, []*Response{
			{
				Version: Version,
				Error:   err,
			},
		}, false)
		if err != nil {
			fmt.Fprint(w, "Failed to encode error objects")
			w.WriteHeader(http.StatusInternalServerError)
		}
		return
	}

	resp := make([]*Response, len(rs))
	for i := range rs {
		resp[i] = mr.InvokeMethod(r.Context(), rs[i])
	}

	if err := SendResponse(w, resp, batch); err != nil {
		fmt.Fprint(w, "Failed to encode result objects")
		w.WriteHeader(http.StatusInternalServerError)
	}
}

// InvokeMethod invokes JSON-RPC method.
func (mr *MethodRepository) InvokeMethod(c context.Context, r *Request) *Response {
	var h Handler
	res := NewResponse(r)
	h, res.Error = mr.TakeMethod(r)
	if res.Error != nil {
		return res
	}
	wrappedContext := WithRequestID(c, r.ID)
	wrappedContext = WithMethodName(wrappedContext, r.Method)
	res.Result, res.Error = h.ServeJSONRPC(WithRequestID(c, r.ID), r.Params)
	if res.Error != nil {
		res.Result = nil
	}
	return res
}
<commit_msg>Fix ineffectual assignment bug<commit_after>package jsonrpc

import (
	"context"
	"fmt"
	"net/http"

	"github.com/intel-go/fastjson"
)

// Handler links a method of JSON-RPC request.
type Handler interface {
	ServeJSONRPC(c context.Context, params *fastjson.RawMessage) (result interface{}, err *Error)
}

// ServeHTTP provides basic JSON-RPC handling.
func (mr *MethodRepository) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	rs, batch, err := ParseRequest(r)
	if err != nil {
		err := SendResponse(w, []*Response{
			{
				Version: Version,
				Error:   err,
			},
		}, false)
		if err != nil {
			fmt.Fprint(w, "Failed to encode error objects")
			w.WriteHeader(http.StatusInternalServerError)
		}
		return
	}

	resp := make([]*Response, len(rs))
	for i := range rs {
		resp[i] = mr.InvokeMethod(r.Context(), rs[i])
	}

	if err := SendResponse(w, resp, batch); err != nil {
		fmt.Fprint(w, "Failed to encode result objects")
		w.WriteHeader(http.StatusInternalServerError)
	}
}

// InvokeMethod invokes JSON-RPC method.
func (mr *MethodRepository) InvokeMethod(c context.Context, r *Request) *Response {
	var h Handler
	res := NewResponse(r)
	h, res.Error = mr.TakeMethod(r)
	if res.Error != nil {
		return res
	}

	wrappedContext := WithRequestID(c, r.ID)
	wrappedContext = WithMethodName(wrappedContext, r.Method)
	res.Result, res.Error = h.ServeJSONRPC(wrappedContext, r.Params)
	if res.Error != nil {
		res.Result = nil
	}
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"image"
	"image/jpeg"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type ErrorResponse struct {
	Msg string `json:"error"`
}

type Uploadable struct {
	Data   io.Reader
	Key    string
	length int64
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string, width int, height int) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x-%dx%d", hash.Sum(nil), width, height)
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]
	// Set a hard limit in MB on files
	var limit int64 = 5
	if r.ContentLength > limit<<20 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("The file size limit is %dMB", limit),
		})
		return
	} else if r.ContentLength == 0 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("File must have size greater than 0"),
		})
		return
	}

	mime := r.Header.Get("Content-Type")

	data, err := processFile(r.Body, mime, bucket)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	r.Body.Close()

	err = storage.PutReader(bucket, data.Key, data.Data,
		data.length, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		if secure {
			uri.Scheme = "https"
		} else {
			uri.Scheme = "http"
		}
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, data.Key)

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

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}

func processFile(src io.Reader, mime string, bucket string) (*Uploadable, error) {
	if mime == "image/jpeg" || mime == "image/jpg" {
		image, format, err := fetch.GetRotatedImage(src)
		if err != nil {
			return nil, err
		}
		if format != "jpeg" {
			return nil, errors.New("You sent a bad JPEG file.")
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data := new(bytes.Buffer)
		length := int64(data.Len())
		err = jpeg.Encode(data, image, nil)
		if err != nil {
			return nil, err
		}

		upload := Uploadable{data, key, length}
		return &upload, nil

	} else {
		raw, err := ioutil.ReadAll(src)
		if err != nil {
			return nil, err
		}

		data := bytes.NewReader(raw)
		length := int64(data.Len())
		image, _, err := image.Decode(data)
		if err != nil {
			return nil, err
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data.Seek(0, 0)

		upload := Uploadable{data, key, length}
		return &upload, nil
	}
}
<commit_msg>Gotta make Length exported<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"image"
	"image/jpeg"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type ErrorResponse struct {
	Msg string `json:"error"`
}

type Uploadable struct {
	Data   io.Reader
	Key    string
	Length int64
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string, width int, height int) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x-%dx%d", hash.Sum(nil), width, height)
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]
	// Set a hard limit in MB on files
	var limit int64 = 5
	if r.ContentLength > limit<<20 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("The file size limit is %dMB", limit),
		})
		return
	} else if r.ContentLength == 0 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(ErrorResponse{
			Msg: fmt.Sprintf("File must have size greater than 0"),
		})
		return
	}

	mime := r.Header.Get("Content-Type")

	data, err := processFile(r.Body, mime, bucket)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	r.Body.Close()

	err = storage.PutReader(bucket, data.Key, data.Data,
		data.Length, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		if secure {
			uri.Scheme = "https"
		} else {
			uri.Scheme = "http"
		}
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, data.Key)

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

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}

func processFile(src io.Reader, mime string, bucket string) (*Uploadable, error) {
	if mime == "image/jpeg" || mime == "image/jpg" {
		image, format, err := fetch.GetRotatedImage(src)
		if err != nil {
			return nil, err
		}
		if format != "jpeg" {
			return nil, errors.New("You sent a bad JPEG file.")
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data := new(bytes.Buffer)
		length := int64(data.Len())
		err = jpeg.Encode(data, image, nil)
		if err != nil {
			return nil, err
		}

		upload := Uploadable{data, key, length}
		return &upload, nil

	} else {
		raw, err := ioutil.ReadAll(src)
		if err != nil {
			return nil, err
		}

		data := bytes.NewReader(raw)
		length := int64(data.Len())
		image, _, err := image.Decode(data)
		if err != nil {
			return nil, err
		}

		width := image.Bounds().Size().X
		height := image.Bounds().Size().Y
		key := fileKey(bucket, width, height)

		data.Seek(0, 0)

		upload := Uploadable{data, key, length}
		return &upload, nil
	}
}
<|endoftext|>


<commit_before>package shoauth

import "net/http"

type shopifyOauthHandler struct {
	successHandler http.Handler
	failureHandler http.Handler
	config         *ShopifyConfig
	ShopifyPersistence
}

// NewShopifyOauthHandler returns the middleware handler that handles Shopify
// oauth requests and responses.  It will call successHandler.ServeHTTP on a
// successful installation or verification, and will call
// failureHandler.ServeHTTP on an unsuccessful installation or verification.
// The user must pass a shopifyPersistence-satisfying struct and any functions
// they wish to operate on the default config object.
func NewShopifyOauthHandler(successHandler http.Handler, failureHandler http.Handler, persistence ShopifyPersistence, configOptions ...func(*ShopifyConfig)) *shopifyOauthHandler {
	// Set some sensible defaults.
	config := &ShopifyConfig{
		RedirectURI: "",
		HelpURI:     "/help",
		Webhooks:    make(map[string]string),
	}

	// Apply the custom config functions passed.
	for _, f := range configOptions {
		f(config)
	}

	return &shopifyOauthHandler{
		successHandler:     successHandler,
		failureHandler:     failureHandler,
		ShopifyPersistence: persistence,
		config:             config,
	}
}

func (s *shopifyOauthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// If the user has authenticated via the initial app Callback, the app
	// should have registered a valid session for the user.  As long as that
	// session is active, we do not need to validate requests from said user.
	// The help page is also static and unsigned - we can just display it.
	if s.HasValidSession(r) || r.URL.Path == s.config.HelpURI {
		s.successHandler.ServeHTTP(w, r)
		return
	}

	if len(r.FormValue("shop")) == 0 {
		s.failureHandler.ServeHTTP(w, r)
		return
	}

	// If this shop has not installed our app, and we do not have a code
	// parameter, redirect them to the install page
	if !s.InstallationExists(r.FormValue("shop")) && len(r.FormValue("code")) == 0 {
		// Construct our scopes parameter
		scopeParameter := ""
		if len(s.config.Scopes) > 0 {
			scopeParameter = "&scope="
			for i, scope := range s.config.Scopes {
				scopeParameter += scope
				if (i + 1) < len(s.config.Scopes) {
					scopeParameter += ","
				}
			}
		}
		redirectURL := "https://" + r.FormValue("shop") + "/admin/oauth/authorize?client_id=" + s.config.ClientID + scopeParameter
		if len(s.config.RedirectURI) > 0 {
			redirectURL += "&redirect_uri=" + s.config.RedirectURI
		}
		http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
		return
	}

	// If this shop has not installed our app, and we do have a code parameter,
	// attempt an installation.
	if !s.InstallationExists(r.FormValue("shop")) {
		// We perform the installation - if it fails, call the app's
		// failure handler.  Otherwise, we open up the app.  If it's embedded,
		// we do this within the admin interface.  Otherwise, just call the app
		// handler.
		if err := s.performInstallation(r.FormValue("shop"), r.FormValue("code")); err != nil {
			s.failureHandler.ServeHTTP(w, r)
		} else {
			if s.config.IsEmbedded {
				http.Redirect(w, r, "https://"+r.FormValue("shop")+"/admin/apps/"+s.config.ClientID, http.StatusMovedPermanently)
			} else {
				s.successHandler.ServeHTTP(w, r)
			}
		}
		// If this is not an installation request, we must validate that it has
		// actually come from shopify according to their predefined rules.
	} else {
		if err := validateRequest(r, s.config.SharedSecret); err != nil {
			s.failureHandler.ServeHTTP(w, r)
		} else {
			s.successHandler.ServeHTTP(w, r)
		}
	}
}
<commit_msg>Cleaning up scope parameter creation.<commit_after>package shoauth

import (
	"net/http"
	"strings"
)

type shopifyOauthHandler struct {
	successHandler http.Handler
	failureHandler http.Handler
	config         *ShopifyConfig
	ShopifyPersistence
}

// NewShopifyOauthHandler returns the middleware handler that handles Shopify
// oauth requests and responses.  It will call successHandler.ServeHTTP on a
// successful installation or verification, and will call
// failureHandler.ServeHTTP on an unsuccessful installation or verification.
// The user must pass a shopifyPersistence-satisfying struct and any functions
// they wish to operate on the default config object.
func NewShopifyOauthHandler(successHandler http.Handler, failureHandler http.Handler, persistence ShopifyPersistence, configOptions ...func(*ShopifyConfig)) *shopifyOauthHandler {
	// Set some sensible defaults.
	config := &ShopifyConfig{
		RedirectURI: "",
		HelpURI:     "/help",
		Webhooks:    make(map[string]string),
	}

	// Apply the custom config functions passed.
	for _, f := range configOptions {
		f(config)
	}

	return &shopifyOauthHandler{
		successHandler:     successHandler,
		failureHandler:     failureHandler,
		ShopifyPersistence: persistence,
		config:             config,
	}
}

func (s *shopifyOauthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	// If the user has authenticated via the initial app Callback, the app
	// should have registered a valid session for the user.  As long as that
	// session is active, we do not need to validate requests from said user.
	// The help page is also static and unsigned - we can just display it.
	if s.HasValidSession(r) || r.URL.Path == s.config.HelpURI {
		s.successHandler.ServeHTTP(w, r)
		return
	}

	if len(r.FormValue("shop")) == 0 {
		s.failureHandler.ServeHTTP(w, r)
		return
	}

	// If this shop has not installed our app, and we do not have a code
	// parameter, redirect them to the install page
	if !s.InstallationExists(r.FormValue("shop")) && len(r.FormValue("code")) == 0 {
		// Construct our scopes parameter
		scopeParameter := ""
		if len(s.config.Scopes) > 0 {
			scopeParameter = "&scope=" + strings.Join(s.config.Scopes, ",")
		}
		redirectURL := "https://" + r.FormValue("shop") + "/admin/oauth/authorize?client_id=" + s.config.ClientID + scopeParameter
		if len(s.config.RedirectURI) > 0 {
			redirectURL += "&redirect_uri=" + s.config.RedirectURI
		}
		http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
		return
	}

	// If this shop has not installed our app, and we do have a code parameter,
	// attempt an installation.
	if !s.InstallationExists(r.FormValue("shop")) {
		// We perform the installation - if it fails, call the app's
		// failure handler.  Otherwise, we open up the app.  If it's embedded,
		// we do this within the admin interface.  Otherwise, just call the app
		// handler.
		if err := s.performInstallation(r.FormValue("shop"), r.FormValue("code")); err != nil {
			s.failureHandler.ServeHTTP(w, r)
		} else {
			if s.config.IsEmbedded {
				http.Redirect(w, r, "https://"+r.FormValue("shop")+"/admin/apps/"+s.config.ClientID, http.StatusMovedPermanently)
			} else {
				s.successHandler.ServeHTTP(w, r)
			}
		}
		// If this is not an installation request, we must validate that it has
		// actually come from shopify according to their predefined rules.
	} else {
		if err := validateRequest(r, s.config.SharedSecret); err != nil {
			s.failureHandler.ServeHTTP(w, r)
		} else {
			s.successHandler.ServeHTTP(w, r)
		}
	}
}
<|endoftext|>


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

package fit

import (
	"fmt"
	"math"
	"strings"

	"github.com/gonum/matrix/mat64"
)

// LinearLeastSquares computes the least squares fit for the function
//
//   f(x) = Β₀terms₀(x) + Β₁terms₁(x) + ...
//
// to the data (xs[i], ys[i]). It returns the parameters Β₀, Β₁, ...
// that minimize the sum of the squares of the residuals of f:
//
//   ∑ (ys[i] - f(xs[i]))²
//
// If weights is non-nil, it is used to weight these residuals:
//
//   ∑ weights[i] × (ys[i] - f(xs[i]))²
//
// The function f is specified by one Go function for each linear
// term. For efficiency, the Go function is vectorized: it will be
// passed a slice of x values in xs and must fill the slice termOut
// with the value of the term for each value in xs.
//
// Note that this is called a "linear" least squares fit because the
// fitted function is linear in the computed parameters. The function
// need not be linear in x.
func LinearLeastSquares(xs, ys, weights []float64, terms ...func(xs, termOut []float64)) (params []float64) {
	// The optimal parameters are found by solving for Β̂ in the
	// "normal equations":
	//
	//    (𝐗ᵀ𝐖𝐗)Β̂ = 𝐗ᵀ𝐖𝐲
	//
	// where 𝐖 is a diagonal weight matrix (or the identity matrix
	// for the unweighted case).

	// TODO: Consider using orthogonal decomposition.

	// TODO: Consider providing a multidimensional version of
	// this.

	if len(xs) != len(ys) {
		panic("len(xs) != len(ys)")
	}
	if weights != nil && len(xs) != len(weights) {
		panic("len(xs) != len(weights")
	}

	// Construct 𝐗ᵀ. This is the more convenient representation
	// for efficiently calling the term functions.
	xTVals := make([]float64, len(terms)*len(xs))
	for i, term := range terms {
		term(xs, xTVals[i*len(xs):i*len(xs)+len(xs)])
	}
	XT := mat64.NewDense(len(terms), len(xs), xTVals)
	X := XT.T()

	// Construct 𝐗ᵀ𝐖.
	var XTW *mat64.Dense
	if weights == nil {
		// 𝐖 is the identity matrix.
		XTW = XT
	} else {
		// Since 𝐖 is a diagonal matrix, we do this directly.
		XTW = mat64.DenseCopyOf(XT)
		WDiag := mat64.NewVector(len(weights), weights)
		for row := 0; row < len(terms); row++ {
			rowView := XTW.RowView(row)
			rowView.MulElemVec(rowView, WDiag)
		}
	}

	// Construct 𝐲.
	y := mat64.NewVector(len(ys), ys)

	// Compute Β̂.
	lhs := mat64.NewDense(len(terms), len(terms), nil)
	lhs.Mul(XTW, X)

	rhs := mat64.NewVector(len(terms), nil)
	rhs.MulVec(XTW, y)

	BVals := make([]float64, len(terms))
	B := mat64.NewVector(len(terms), BVals)
	B.SolveVec(lhs, rhs)
	return BVals
}

// PolynomialRegressionResult is the resulting polynomial from a
// PolynomialRegression.
//
// TODO: Should this just be a least squares regression result? We
// have the terms functions, so we can construct F, though it won't be
// very efficient.
type PolynomialRegressionResult struct {
	// Coefficients is the coefficients of the fitted polynomial.
	// Coefficients[i] is the coefficient of the x^i term.
	Coefficients []float64

	// F evaluates the fitted polynomial at x.
	F func(x float64) float64
}

func (r PolynomialRegressionResult) String() string {
	var terms []string
	for pow, factor := range r.Coefficients {
		switch {
		case factor == 0:
			continue
		case pow == 0:
			terms = append(terms, fmt.Sprintf("%v", factor))
		case pow == 1:
			terms = append(terms, fmt.Sprintf("%vx", factor))
		default:
			terms = append(terms, fmt.Sprintf("%vx^%d", factor, pow))
		}
	}
	if len(terms) == 0 {
		return "0"
	}
	return strings.Join(terms, "+")
}

// PolynomialRegression performs a least squares regression with a
// polynomial of the given degree. If weights is non-nil, it is used
// to weight the residuals.
func PolynomialRegression(xs, ys, weights []float64, degree int) PolynomialRegressionResult {
	terms := make([]func(xs, termOut []float64), degree+1)
	terms[0] = func(xs, termsOut []float64) {
		for i := range termsOut {
			termsOut[i] = 1
		}
	}
	if degree >= 1 {
		terms[1] = func(xs, termOut []float64) {
			copy(termOut, xs)
		}
	}
	if degree >= 2 {
		terms[2] = func(xs, termOut []float64) {
			for i, x := range xs {
				termOut[i] = x * x
			}
		}
	}
	for d := 3; d < len(terms); d++ {
		terms[d] = func(xs, termOut []float64) {
			for i, x := range xs {
				termOut[i] = math.Pow(x, float64(d+1))
			}
		}
	}

	coeffs := LinearLeastSquares(xs, ys, weights, terms...)
	f := func(x float64) float64 {
		y := coeffs[0]
		xp := x
		for _, c := range coeffs[1:] {
			y += xp * c
			xp *= x
		}
		return y
	}
	return PolynomialRegressionResult{coeffs, f}
}
<commit_msg>fit: fix typo in panic message<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fit

import (
	"fmt"
	"math"
	"strings"

	"github.com/gonum/matrix/mat64"
)

// LinearLeastSquares computes the least squares fit for the function
//
//   f(x) = Β₀terms₀(x) + Β₁terms₁(x) + ...
//
// to the data (xs[i], ys[i]). It returns the parameters Β₀, Β₁, ...
// that minimize the sum of the squares of the residuals of f:
//
//   ∑ (ys[i] - f(xs[i]))²
//
// If weights is non-nil, it is used to weight these residuals:
//
//   ∑ weights[i] × (ys[i] - f(xs[i]))²
//
// The function f is specified by one Go function for each linear
// term. For efficiency, the Go function is vectorized: it will be
// passed a slice of x values in xs and must fill the slice termOut
// with the value of the term for each value in xs.
//
// Note that this is called a "linear" least squares fit because the
// fitted function is linear in the computed parameters. The function
// need not be linear in x.
func LinearLeastSquares(xs, ys, weights []float64, terms ...func(xs, termOut []float64)) (params []float64) {
	// The optimal parameters are found by solving for Β̂ in the
	// "normal equations":
	//
	//    (𝐗ᵀ𝐖𝐗)Β̂ = 𝐗ᵀ𝐖𝐲
	//
	// where 𝐖 is a diagonal weight matrix (or the identity matrix
	// for the unweighted case).

	// TODO: Consider using orthogonal decomposition.

	// TODO: Consider providing a multidimensional version of
	// this.

	if len(xs) != len(ys) {
		panic("len(xs) != len(ys)")
	}
	if weights != nil && len(xs) != len(weights) {
		panic("len(xs) != len(weights)")
	}

	// Construct 𝐗ᵀ. This is the more convenient representation
	// for efficiently calling the term functions.
	xTVals := make([]float64, len(terms)*len(xs))
	for i, term := range terms {
		term(xs, xTVals[i*len(xs):i*len(xs)+len(xs)])
	}
	XT := mat64.NewDense(len(terms), len(xs), xTVals)
	X := XT.T()

	// Construct 𝐗ᵀ𝐖.
	var XTW *mat64.Dense
	if weights == nil {
		// 𝐖 is the identity matrix.
		XTW = XT
	} else {
		// Since 𝐖 is a diagonal matrix, we do this directly.
		XTW = mat64.DenseCopyOf(XT)
		WDiag := mat64.NewVector(len(weights), weights)
		for row := 0; row < len(terms); row++ {
			rowView := XTW.RowView(row)
			rowView.MulElemVec(rowView, WDiag)
		}
	}

	// Construct 𝐲.
	y := mat64.NewVector(len(ys), ys)

	// Compute Β̂.
	lhs := mat64.NewDense(len(terms), len(terms), nil)
	lhs.Mul(XTW, X)

	rhs := mat64.NewVector(len(terms), nil)
	rhs.MulVec(XTW, y)

	BVals := make([]float64, len(terms))
	B := mat64.NewVector(len(terms), BVals)
	B.SolveVec(lhs, rhs)
	return BVals
}

// PolynomialRegressionResult is the resulting polynomial from a
// PolynomialRegression.
//
// TODO: Should this just be a least squares regression result? We
// have the terms functions, so we can construct F, though it won't be
// very efficient.
type PolynomialRegressionResult struct {
	// Coefficients is the coefficients of the fitted polynomial.
	// Coefficients[i] is the coefficient of the x^i term.
	Coefficients []float64

	// F evaluates the fitted polynomial at x.
	F func(x float64) float64
}

func (r PolynomialRegressionResult) String() string {
	var terms []string
	for pow, factor := range r.Coefficients {
		switch {
		case factor == 0:
			continue
		case pow == 0:
			terms = append(terms, fmt.Sprintf("%v", factor))
		case pow == 1:
			terms = append(terms, fmt.Sprintf("%vx", factor))
		default:
			terms = append(terms, fmt.Sprintf("%vx^%d", factor, pow))
		}
	}
	if len(terms) == 0 {
		return "0"
	}
	return strings.Join(terms, "+")
}

// PolynomialRegression performs a least squares regression with a
// polynomial of the given degree. If weights is non-nil, it is used
// to weight the residuals.
func PolynomialRegression(xs, ys, weights []float64, degree int) PolynomialRegressionResult {
	terms := make([]func(xs, termOut []float64), degree+1)
	terms[0] = func(xs, termsOut []float64) {
		for i := range termsOut {
			termsOut[i] = 1
		}
	}
	if degree >= 1 {
		terms[1] = func(xs, termOut []float64) {
			copy(termOut, xs)
		}
	}
	if degree >= 2 {
		terms[2] = func(xs, termOut []float64) {
			for i, x := range xs {
				termOut[i] = x * x
			}
		}
	}
	for d := 3; d < len(terms); d++ {
		terms[d] = func(xs, termOut []float64) {
			for i, x := range xs {
				termOut[i] = math.Pow(x, float64(d+1))
			}
		}
	}

	coeffs := LinearLeastSquares(xs, ys, weights, terms...)
	f := func(x float64) float64 {
		y := coeffs[0]
		xp := x
		for _, c := range coeffs[1:] {
			y += xp * c
			xp *= x
		}
		return y
	}
	return PolynomialRegressionResult{coeffs, f}
}
<|endoftext|>


<commit_before>package static

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/volatile/core"
)

const (
	// DefaultMaxAge provides a default caching value of 1 hour.
	DefaultMaxAge = 1 * time.Hour

	assetsDir = "static"
)

var (
	fs = http.FileServer(http.Dir(assetsDir))
)

// Use tells the core to use this handler.
func Use(maxAge time.Duration) {
	maxAgeString := fmt.Sprintf("%.f", maxAge.Seconds())
	core.Use(func(c *core.Context) {
		if strings.HasPrefix(c.Request.URL.Path, "/") {
			if core.Production {
				c.ResponseWriter.Header().Set("Cache-Control", "public, max-age="+maxAgeString)
			}
			http.StripPrefix("/"+assetsDir, fs).ServeHTTP(c.ResponseWriter, c.Request)
		} else {
			c.Next()
		}
	})
}
<commit_msg>Fix static path detection<commit_after>package static

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/volatile/core"
)

const (
	// DefaultMaxAge provides a default caching value of 1 hour.
	DefaultMaxAge = 1 * time.Hour

	assetsDir = "static"
)

var (
	fs = http.FileServer(http.Dir(assetsDir))
)

// Use tells the core to use this handler.
func Use(maxAge time.Duration) {
	maxAgeString := fmt.Sprintf("%.f", maxAge.Seconds())
	core.Use(func(c *core.Context) {
		if strings.HasPrefix(c.Request.URL.Path, "/"+assetsDir) {
			if core.Production {
				c.ResponseWriter.Header().Set("Cache-Control", "public, max-age="+maxAgeString)
			}
			http.StripPrefix("/"+assetsDir, fs).ServeHTTP(c.ResponseWriter, c.Request)
		} else {
			c.Next()
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package tail implements "tail -F" functionality following rotated logs
package tail

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/golang/glog"
	"golang.org/x/exp/inotify"
)

type Tail struct {
	reader     *bufio.Reader
	readerErr  error
	readerLock sync.RWMutex
	filename   string
	file       *os.File
	stop       chan bool
	watcher    *inotify.Watcher
}

const (
	defaultRetryInterval = 100 * time.Millisecond
	maxRetryInterval     = 30 * time.Second
)

// NewTail starts opens the given file and watches it for deletion/rotation
func NewTail(filename string) (*Tail, error) {
	t, err := newTail(filename)
	if err != nil {
		return nil, err
	}
	go t.watchLoop()
	return t, nil
}

// newTail creates a Tail object.
func newTail(filename string) (*Tail, error) {
	t := &Tail{
		filename: filename,
	}
	var err error
	t.stop = make(chan bool)
	t.watcher, err = inotify.NewWatcher()
	if err != nil {
		return nil, fmt.Errorf("inotify init failed on %s: %v", t.filename, err)
	}
	// Initialize readerErr as io.EOF, so that the reader can work properly
	// during initialization.
	t.readerErr = io.EOF
	return t, nil
}

// Read implements the io.Reader interface for Tail
func (t *Tail) Read(p []byte) (int, error) {
	t.readerLock.RLock()
	defer t.readerLock.RUnlock()
	if t.readerErr != nil {
		return 0, t.readerErr
	}
	return t.reader.Read(p)
}

var _ io.ReadCloser = &Tail{}

// Close stops watching and closes the file
func (t *Tail) Close() error {
	close(t.stop)
	return nil
}

func (t *Tail) attemptOpen() error {
	t.readerLock.Lock()
	defer t.readerLock.Unlock()
	t.readerErr = nil
	attempt := 0
	for interval := defaultRetryInterval; ; interval *= 2 {
		attempt++
		glog.V(4).Infof("Opening %s (attempt %d)", t.filename, attempt)
		var err error
		t.file, err = os.Open(t.filename)
		if err == nil {
			// TODO: not interested in old events?
			// t.file.Seek(0, os.SEEK_END)
			t.reader = bufio.NewReader(t.file)
			return nil
		}
		if interval >= maxRetryInterval {
			break
		}
		select {
		case <-time.After(interval):
		case <-t.stop:
			t.readerErr = io.EOF
			return fmt.Errorf("watch was cancelled")
		}
	}
	err := fmt.Errorf("can't open log file %s", t.filename)
	t.readerErr = err
	return err
}

func (t *Tail) watchLoop() {
	for {
		err := t.watchFile()
		if err != nil {
			glog.Errorf("Tail failed on %s: %v", t.filename, err)
			break
		}
	}
}

func (t *Tail) watchFile() error {
	err := t.attemptOpen()
	if err != nil {
		return err
	}
	defer t.file.Close()

	watchDir := filepath.Dir(t.filename)
	err = t.watcher.AddWatch(watchDir, inotify.IN_MOVED_FROM|inotify.IN_DELETE)
	if err != nil {
		return fmt.Errorf("Failed to add watch to directory %s: %v", watchDir, err)
	}
	defer t.watcher.RemoveWatch(watchDir)

	for {
		select {
		case event := <-t.watcher.Event:
			eventPath := filepath.Clean(event.Name) // Directory events have an extra '/'
			if eventPath == t.filename {
				glog.V(4).Infof("Log file %s moved/deleted", t.filename)
				return nil
			}
		case <-t.stop:
			return fmt.Errorf("watch was cancelled")
		}
	}
}
<commit_msg>add error log for open log file<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package tail implements "tail -F" functionality following rotated logs
package tail

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/golang/glog"
	"golang.org/x/exp/inotify"
)

type Tail struct {
	reader     *bufio.Reader
	readerErr  error
	readerLock sync.RWMutex
	filename   string
	file       *os.File
	stop       chan bool
	watcher    *inotify.Watcher
}

const (
	defaultRetryInterval = 100 * time.Millisecond
	maxRetryInterval     = 30 * time.Second
)

// NewTail starts opens the given file and watches it for deletion/rotation
func NewTail(filename string) (*Tail, error) {
	t, err := newTail(filename)
	if err != nil {
		return nil, err
	}
	go t.watchLoop()
	return t, nil
}

// newTail creates a Tail object.
func newTail(filename string) (*Tail, error) {
	t := &Tail{
		filename: filename,
	}
	var err error
	t.stop = make(chan bool)
	t.watcher, err = inotify.NewWatcher()
	if err != nil {
		return nil, fmt.Errorf("inotify init failed on %s: %v", t.filename, err)
	}
	// Initialize readerErr as io.EOF, so that the reader can work properly
	// during initialization.
	t.readerErr = io.EOF
	return t, nil
}

// Read implements the io.Reader interface for Tail
func (t *Tail) Read(p []byte) (int, error) {
	t.readerLock.RLock()
	defer t.readerLock.RUnlock()
	if t.readerErr != nil {
		return 0, t.readerErr
	}
	return t.reader.Read(p)
}

var _ io.ReadCloser = &Tail{}

// Close stops watching and closes the file
func (t *Tail) Close() error {
	close(t.stop)
	return nil
}

func (t *Tail) attemptOpen() error {
	t.readerLock.Lock()
	defer t.readerLock.Unlock()
	t.readerErr = nil
	attempt := 0
	var lastErr error
	for interval := defaultRetryInterval; ; interval *= 2 {
		attempt++
		glog.V(4).Infof("Opening %s (attempt %d)", t.filename, attempt)
		var err error
		t.file, err = os.Open(t.filename)
		if err == nil {
			// TODO: not interested in old events?
			// t.file.Seek(0, os.SEEK_END)
			t.reader = bufio.NewReader(t.file)
			return nil
		}
		lastErr = err
		glog.V(4).Infof("open log file %s error: %v", t.filename, err)

		if interval >= maxRetryInterval {
			break
		}
		select {
		case <-time.After(interval):
		case <-t.stop:
			t.readerErr = io.EOF
			return fmt.Errorf("watch was cancelled")
		}
	}
	err := fmt.Errorf("can't open log file %s: %v", t.filename, lastErr)
	t.readerErr = err
	return err
}

func (t *Tail) watchLoop() {
	for {
		err := t.watchFile()
		if err != nil {
			glog.Errorf("Tail failed on %s: %v", t.filename, err)
			break
		}
	}
}

func (t *Tail) watchFile() error {
	err := t.attemptOpen()
	if err != nil {
		return err
	}
	defer t.file.Close()

	watchDir := filepath.Dir(t.filename)
	err = t.watcher.AddWatch(watchDir, inotify.IN_MOVED_FROM|inotify.IN_DELETE)
	if err != nil {
		return fmt.Errorf("Failed to add watch to directory %s: %v", watchDir, err)
	}
	defer t.watcher.RemoveWatch(watchDir)

	for {
		select {
		case event := <-t.watcher.Event:
			eventPath := filepath.Clean(event.Name) // Directory events have an extra '/'
			if eventPath == t.filename {
				glog.V(4).Infof("Log file %s moved/deleted", t.filename)
				return nil
			}
		case <-t.stop:
			return fmt.Errorf("watch was cancelled")
		}
	}
}
<|endoftext|>


<commit_before>// +build windows

package enforcer

import (
	"errors"
	"sync"
	"time"

	"github.com/scjalliance/resourceful/guardian"
	"github.com/scjalliance/resourceful/policy"
)

// Service is a resourceful process monitoring service. It watches the local
// set of processes and enforces resourceful policies.
type Service struct {
	client              *guardian.Client
	enforcementInterval time.Duration // Process polling interval
	policyInterval      time.Duration // Configuration polling interval
	hostname            string
	passive             bool // Don't kill processes if true
	logger              Logger

	polMutex sync.RWMutex
	policies policy.Set

	managedMutex sync.RWMutex
	managed      map[UniqueID]*ManagedProcess
	skipped      map[UniqueID]struct{}

	opMutex  sync.Mutex
	shutdown chan<- struct{} // Close to signal shutdown
	stopped  <-chan struct{} // Closed when shutdown completed
}

// New returns a new policy monitor service with the given client.
func New(client *guardian.Client, enforcementInterval, policyInterval time.Duration, hostname string, passive bool, logger Logger) *Service {
	return &Service{
		client:              client,
		enforcementInterval: enforcementInterval,
		policyInterval:      policyInterval,
		hostname:            hostname,
		passive:             passive,
		logger:              logger,
		managed:             make(map[UniqueID]*ManagedProcess, 8),
		skipped:             make(map[UniqueID]struct{}),
	}
}

// Start starts the service if it isn't running.
func (s *Service) Start() error {
	s.opMutex.Lock()
	defer s.opMutex.Unlock()

	if s.shutdown != nil {
		return errors.New("the policy monitor service is already running")
	}

	shutdown := make(chan struct{})
	s.shutdown = shutdown

	stopped := make(chan struct{})
	s.stopped = stopped

	go s.run(shutdown, stopped)

	return nil
}

// Stop stops the service if it's running.
func (s *Service) Stop() {
	s.opMutex.Lock()
	defer s.opMutex.Unlock()

	if s.shutdown == nil {
		return
	}

	close(s.shutdown)
	s.shutdown = nil

	<-s.stopped
	s.stopped = nil
}

// Policies returns the most recently retrieved set of policies.
func (s *Service) Policies() policy.Set {
	s.polMutex.RLock()
	defer s.polMutex.RUnlock()
	return s.policies
}

// UpdatePolicies causes the service to update its policies.
func (s *Service) UpdatePolicies() {
	response, err := s.client.Policies()
	if err != nil {
		s.log("Failed to retrieve policies: %v", err.Error())
		return
	}

	s.polMutex.Lock()
	additions, deletions := s.policies.Diff(response.Policies)
	s.policies = response.Policies
	s.polMutex.Unlock()

	for _, pol := range additions {
		s.log("POL: ADD %s: %s", pol.Hash().String(), pol.String())
	}
	for _, pol := range deletions {
		s.log("POL: REM %s: %s", pol.Hash().String(), pol.String())
	}
}

func (s *Service) manage(p Process) {
	s.log("Starting management of %s", Subject(s.hostname, p))

	id := p.UniqueID()

	s.managedMutex.Lock()
	defer s.managedMutex.Unlock()

	if _, exists := s.managed[id]; exists {
		// Already managed
		return
	}

	mp, err := Manage(s.client, s.hostname, p, s.passive)
	if err != nil {
		s.log("Unable to manage process %s: %v", id, err)
	}
	s.managed[id] = mp
}

func (s *Service) run(shutdown <-chan struct{}, stopped chan<- struct{}) {
	defer close(stopped)

	var wg sync.WaitGroup
	wg.Add(2)

	// Perform enforcement on an interval
	go func() {
		defer wg.Done()

		enforceTimer := time.NewTicker(s.enforcementInterval)
		defer enforceTimer.Stop()

		for {
			select {
			case <-shutdown:
				return
			case <-enforceTimer.C:
				if err := s.Enforce(); err != nil {
					s.log("Enforcement failed: %s", err)
				}
			}
		}
	}()

	// Update policies on an interval
	go func() {
		defer wg.Done()

		// Attempt initial retrieval of policies
		s.UpdatePolicies()

		policyTimer := time.NewTicker(s.policyInterval)
		defer policyTimer.Stop()

		for {
			select {
			case <-shutdown:
				return
			case <-policyTimer.C:
				s.UpdatePolicies()
			}
		}
	}()

	// Wait for both goroutines to shutdown
	wg.Wait()

	// Stop all process management
	s.managedMutex.Lock()
	defer s.managedMutex.Unlock()
	for id, mp := range s.managed {
		mp.Stop()
		delete(s.managed, id)
		s.log("Stopped management of %s", Subject(s.hostname, mp.proc))
	}
}

// Enforce causes the service to enforce the current policy set.
func (s *Service) Enforce() error {
	policies := s.Policies()

	procs, err := Scan(policies)
	if err != nil {
		return err
	}

	scanned := make(map[UniqueID]struct{}, len(procs))

	s.managedMutex.Lock()
	defer s.managedMutex.Unlock()

	var pending []Process
	for _, proc := range procs {
		id := proc.UniqueID()
		scanned[id] = struct{}{} // Record the ID in the map of scanned procs

		// Don't manage blacklisted processes
		if Blacklisted(proc) {
			if mp, exists := s.managed[id]; exists {
				// Remove from managed and add to skipped
				mp.Stop()
				delete(s.managed, id)
				s.skipped[id] = struct{}{}
				s.log("Stopped management of blacklisted process: %s", Subject(s.hostname, proc))
			} else if _, exists := s.skipped[id]; !exists {
				// Add to skipped
				s.log("Skipped management of blacklisted process: %s", Subject(s.hostname, proc))
				s.skipped[id] = struct{}{}
			}
			continue
		} else {
			if _, exists := s.skipped[id]; exists {
				// Remove from skipped
				delete(s.skipped, id)
			}
		}

		// Don't re-process processes that are already managed
		if _, exists := s.managed[id]; exists {
			continue
		}

		// If it matches a policy add it to the pending slice
		subject := Subject(s.hostname, proc)
		env := Env(s.hostname, proc)
		matches := policies.Match(subject.Resource, subject.Consumer, env)
		if len(matches) > 0 {
			pending = append(pending, proc)
		}
	}

	// Bookkeeping for dead processes
	for id := range s.managed {
		if _, exists := scanned[id]; !exists {
			proc := s.managed[id].proc
			s.managed[id].Stop() // If the process died this is redundant, but if it no longer needs a lease this cleans up the manager
			delete(s.managed, id)
			s.log("Stopped management of %s", Subject(s.hostname, proc))
		}
	}

	for id := range s.skipped {
		if _, exists := scanned[id]; !exists {
			delete(s.skipped, id)
		}
	}

	// Exit early if nothing is pending
	if len(pending) == 0 {
		return nil
	}

	// Begin management of newly discovered processes
	s.log("Enforcement found %d new processes", len(pending))

	for _, proc := range pending {
		id := proc.UniqueID()
		mp, err := Manage(s.client, s.hostname, proc, s.passive)
		if err != nil {
			s.log("Unable to manage process %s: %v", id, err)
			continue
		}
		s.managed[id] = mp

		s.log("Started management of %s", Subject(s.hostname, proc))
	}

	return nil
}

// TODO: Accept an event ID or event interface?
func (s *Service) log(format string, v ...interface{}) {
	// TODO: Try casting s.logger to a different interface so that we can log event IDs?
	if s.logger != nil {
		s.logger.Printf(format, v...)
	}
}
<commit_msg>enforcer: Removed leftover Service.manage() method<commit_after>// +build windows

package enforcer

import (
	"errors"
	"sync"
	"time"

	"github.com/scjalliance/resourceful/guardian"
	"github.com/scjalliance/resourceful/policy"
)

// Service is a resourceful process monitoring service. It watches the local
// set of processes and enforces resourceful policies.
type Service struct {
	client              *guardian.Client
	enforcementInterval time.Duration // Process polling interval
	policyInterval      time.Duration // Configuration polling interval
	hostname            string
	passive             bool // Don't kill processes if true
	logger              Logger

	polMutex sync.RWMutex
	policies policy.Set

	managedMutex sync.RWMutex
	managed      map[UniqueID]*ManagedProcess
	skipped      map[UniqueID]struct{}

	opMutex  sync.Mutex
	shutdown chan<- struct{} // Close to signal shutdown
	stopped  <-chan struct{} // Closed when shutdown completed
}

// New returns a new policy monitor service with the given client.
func New(client *guardian.Client, enforcementInterval, policyInterval time.Duration, hostname string, passive bool, logger Logger) *Service {
	return &Service{
		client:              client,
		enforcementInterval: enforcementInterval,
		policyInterval:      policyInterval,
		hostname:            hostname,
		passive:             passive,
		logger:              logger,
		managed:             make(map[UniqueID]*ManagedProcess, 8),
		skipped:             make(map[UniqueID]struct{}),
	}
}

// Start starts the service if it isn't running.
func (s *Service) Start() error {
	s.opMutex.Lock()
	defer s.opMutex.Unlock()

	if s.shutdown != nil {
		return errors.New("the policy monitor service is already running")
	}

	shutdown := make(chan struct{})
	s.shutdown = shutdown

	stopped := make(chan struct{})
	s.stopped = stopped

	go s.run(shutdown, stopped)

	return nil
}

// Stop stops the service if it's running.
func (s *Service) Stop() {
	s.opMutex.Lock()
	defer s.opMutex.Unlock()

	if s.shutdown == nil {
		return
	}

	close(s.shutdown)
	s.shutdown = nil

	<-s.stopped
	s.stopped = nil
}

// Policies returns the most recently retrieved set of policies.
func (s *Service) Policies() policy.Set {
	s.polMutex.RLock()
	defer s.polMutex.RUnlock()
	return s.policies
}

// UpdatePolicies causes the service to update its policies.
func (s *Service) UpdatePolicies() {
	response, err := s.client.Policies()
	if err != nil {
		s.log("Failed to retrieve policies: %v", err.Error())
		return
	}

	s.polMutex.Lock()
	additions, deletions := s.policies.Diff(response.Policies)
	s.policies = response.Policies
	s.polMutex.Unlock()

	for _, pol := range additions {
		s.log("POL: ADD %s: %s", pol.Hash().String(), pol.String())
	}
	for _, pol := range deletions {
		s.log("POL: REM %s: %s", pol.Hash().String(), pol.String())
	}
}

func (s *Service) run(shutdown <-chan struct{}, stopped chan<- struct{}) {
	defer close(stopped)

	var wg sync.WaitGroup
	wg.Add(2)

	// Perform enforcement on an interval
	go func() {
		defer wg.Done()

		enforceTimer := time.NewTicker(s.enforcementInterval)
		defer enforceTimer.Stop()

		for {
			select {
			case <-shutdown:
				return
			case <-enforceTimer.C:
				if err := s.Enforce(); err != nil {
					s.log("Enforcement failed: %s", err)
				}
			}
		}
	}()

	// Update policies on an interval
	go func() {
		defer wg.Done()

		// Attempt initial retrieval of policies
		s.UpdatePolicies()

		policyTimer := time.NewTicker(s.policyInterval)
		defer policyTimer.Stop()

		for {
			select {
			case <-shutdown:
				return
			case <-policyTimer.C:
				s.UpdatePolicies()
			}
		}
	}()

	// Wait for both goroutines to shutdown
	wg.Wait()

	// Stop all process management
	s.managedMutex.Lock()
	defer s.managedMutex.Unlock()
	for id, mp := range s.managed {
		mp.Stop()
		delete(s.managed, id)
		s.log("Stopped management of %s", Subject(s.hostname, mp.proc))
	}
}

// Enforce causes the service to enforce the current policy set.
func (s *Service) Enforce() error {
	policies := s.Policies()

	procs, err := Scan(policies)
	if err != nil {
		return err
	}

	scanned := make(map[UniqueID]struct{}, len(procs))

	s.managedMutex.Lock()
	defer s.managedMutex.Unlock()

	var pending []Process
	for _, proc := range procs {
		id := proc.UniqueID()
		scanned[id] = struct{}{} // Record the ID in the map of scanned procs

		// Don't manage blacklisted processes
		if Blacklisted(proc) {
			if mp, exists := s.managed[id]; exists {
				// Remove from managed and add to skipped
				mp.Stop()
				delete(s.managed, id)
				s.skipped[id] = struct{}{}
				s.log("Stopped management of blacklisted process: %s", Subject(s.hostname, proc))
			} else if _, exists := s.skipped[id]; !exists {
				// Add to skipped
				s.log("Skipped management of blacklisted process: %s", Subject(s.hostname, proc))
				s.skipped[id] = struct{}{}
			}
			continue
		} else {
			if _, exists := s.skipped[id]; exists {
				// Remove from skipped
				delete(s.skipped, id)
			}
		}

		// Don't re-process processes that are already managed
		if _, exists := s.managed[id]; exists {
			continue
		}

		// If it matches a policy add it to the pending slice
		subject := Subject(s.hostname, proc)
		env := Env(s.hostname, proc)
		matches := policies.Match(subject.Resource, subject.Consumer, env)
		if len(matches) > 0 {
			pending = append(pending, proc)
		}
	}

	// Bookkeeping for dead processes
	for id := range s.managed {
		if _, exists := scanned[id]; !exists {
			proc := s.managed[id].proc
			s.managed[id].Stop() // If the process died this is redundant, but if it no longer needs a lease this cleans up the manager
			delete(s.managed, id)
			s.log("Stopped management of %s", Subject(s.hostname, proc))
		}
	}

	for id := range s.skipped {
		if _, exists := scanned[id]; !exists {
			delete(s.skipped, id)
		}
	}

	// Exit early if nothing is pending
	if len(pending) == 0 {
		return nil
	}

	// Begin management of newly discovered processes
	s.log("Enforcement found %d new processes", len(pending))

	for _, proc := range pending {
		id := proc.UniqueID()
		mp, err := Manage(s.client, s.hostname, proc, s.passive)
		if err != nil {
			s.log("Unable to manage process %s: %v", id, err)
			continue
		}
		s.managed[id] = mp

		s.log("Started management of %s", Subject(s.hostname, proc))
	}

	return nil
}

// TODO: Accept an event ID or event interface?
func (s *Service) log(format string, v ...interface{}) {
	// TODO: Try casting s.logger to a different interface so that we can log event IDs?
	if s.logger != nil {
		s.logger.Printf(format, v...)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/paked/engi"
	"log"
)

var (
	bot   engi.Drawable
	batch *engi.Batch
	World *GameWorld
)

type GameWorld struct {
	bot   engi.Drawable
	batch *engi.Batch
	font  *engi.Font
	engi.World
}

func (game *GameWorld) Preload() {
	engi.Files.Add("bot", "data/icon.png")
	engi.Files.Add("font", "data/font.png")
	game.batch = engi.NewBatch(engi.Width(), engi.Height())
	log.Println("Preloaded")
}

func (game *GameWorld) Setup() {
	engi.SetBg(0x2d3739)

	game.AddSystem(&RenderSystem{})

	entity := engi.NewEntity([]string{"RenderSystem"})
	component := NewRenderComponent(engi.Files.Image("bot"), engi.Point{0, 0}, engi.Point{10, 10})
	entity.AddComponent(component)
	game.AddEntity(entity)

	entityTwo := engi.NewEntity([]string{"RenderSystem"})
	componentTwo := NewRenderComponent(engi.Files.Image("font"), engi.Point{100, 100}, engi.Point{1, 1})

	entityTwo.AddComponent(componentTwo)
	game.AddEntity(entityTwo)
	log.Println("Setup")
}

type RenderSystem struct {
	*engi.System
}

func (rs *RenderSystem) New() {
	rs.System = &engi.System{}
}

func (rs RenderSystem) Pre() {
	engi.Gl.Clear(engi.Gl.COLOR_BUFFER_BIT)
	World.batch.Begin()
}

func (rs RenderSystem) Post() {
	World.batch.End()
}

func (rs RenderSystem) Update(entity *engi.Entity, dt float32) {
	component, ok := entity.GetComponent("RenderComponent").(RenderComponent)
	if ok {
		switch component.Display.(type) {
		case engi.Drawable:
			drawable := component.Display.(engi.Drawable)
			// World.batch.Draw(drawable, 512, 320, 0.5, 0.5, 10, 10, 0, 0xffffff, 1)
			World.batch.Draw(drawable, component.Position.X, component.Position.Y, 0, 0, component.Scale.X, component.Scale.Y, 0, 0xffffff, 1)
		case engi.Font:
			font := component.Display.(engi.Font)
			font.Print(batch, "Hello", 0, 0, 0x000)
		}
	}
}

func (rs RenderSystem) Name() string {
	return "RenderSystem"
}

func (rs RenderSystem) Priority() int {
	return 1
}

type RenderComponent struct {
	Display  interface{}
	Position engi.Point
	Scale    engi.Point
}

func NewRenderComponent(display interface{}, position, scale engi.Point) RenderComponent {
	return RenderComponent{Display: display, Position: position, Scale: scale}
}

func (rc RenderComponent) Name() string {
	return "RenderComponent"
}

func main() {
	World = &GameWorld{}
	engi.Open("Hello", 1024, 640, false, World)
}
<commit_msg>Font support<commit_after>package main

import (
	"github.com/paked/engi"
	"log"
)

var World *GameWorld

type GameWorld struct {
	bot   engi.Drawable
	batch *engi.Batch
	font  *engi.Font
	engi.World
}

func (game *GameWorld) Preload() {
	engi.Files.Add("bot", "data/icon.png")
	engi.Files.Add("font", "data/font.png")
	game.batch = engi.NewBatch(engi.Width(), engi.Height())
	log.Println("Preloaded")
}

func (game *GameWorld) Setup() {
	engi.SetBg(0x2d3739)

	game.AddSystem(&RenderSystem{})

	entity := engi.NewEntity([]string{"RenderSystem"})
	component := NewRenderComponent(engi.Files.Image("bot"), engi.Point{0, 0}, engi.Point{10, 10}, "bot")
	entity.AddComponent(component)
	game.AddEntity(entity)

	entityTwo := engi.NewEntity([]string{"RenderSystem"})
	componentTwo := NewRenderComponent(engi.NewGridFont(engi.Files.Image("font"), 20, 20), engi.Point{200, 400}, engi.Point{1, 1}, "YOLO MATE WASSUP")

	entityTwo.AddComponent(componentTwo)
	game.AddEntity(entityTwo)
	log.Println("Setup")
}

type RenderSystem struct {
	*engi.System
}

func (rs *RenderSystem) New() {
	rs.System = &engi.System{}
}

func (rs RenderSystem) Pre() {
	engi.Gl.Clear(engi.Gl.COLOR_BUFFER_BIT)
	World.batch.Begin()
}

func (rs RenderSystem) Post() {
	World.batch.End()
}

func (rs RenderSystem) Update(entity *engi.Entity, dt float32) {
	component, ok := entity.GetComponent("RenderComponent").(RenderComponent)
	if ok {
		switch component.Display.(type) {
		case engi.Drawable:
			drawable := component.Display.(engi.Drawable)
			World.batch.Draw(drawable, component.Position.X, component.Position.Y, 0, 0, component.Scale.X, component.Scale.Y, 0, 0xffffff, 1)
		case *engi.Font:
			font := component.Display.(*engi.Font)
			font.Print(World.batch, component.Label, component.Position.X, component.Position.Y, 0xffffff)
		}
	}
}

func (rs RenderSystem) Name() string {
	return "RenderSystem"
}

func (rs RenderSystem) Priority() int {
	return 1
}

type RenderComponent struct {
	Display  interface{}
	Position engi.Point
	Scale    engi.Point
	Label    string
}

func NewRenderComponent(display interface{}, position, scale engi.Point, label string) RenderComponent {
	return RenderComponent{Display: display, Position: position, Scale: scale, Label: label}
}

func (rc RenderComponent) Name() string {
	return "RenderComponent"
}

func main() {
	World = &GameWorld{}
	engi.Open("Hello", 1024, 640, false, World)
}
<|endoftext|>