<commit_before>package echo

type (
	// Group is a set of sub-routes for a specified route. It can be used for inner
	// routes that share a common middlware or functionality that should be separate
	// from the parent echo instance while still inheriting from it.
	Group struct {
		prefix     string
		middleware []MiddlewareFunc
		echo       *Echo
	}
)

// Use implements `Echo#Use()` for sub-routes within the Group.
func (g *Group) Use(middleware ...MiddlewareFunc) {
	g.middleware = append(g.middleware, middleware...)
	// Allow requests `/prefix & /prefix/*` to reach the group as they might get
	// dropped if router doesn't find a match, making none of the group middleware
	// execute.
	if g.prefix == "" {
		g.Any("/", NotFoundHandler, g.middleware...)
	}
	g.Any("/*", NotFoundHandler, g.middleware...)
}

// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(CONNECT, path, h, m...)
}

// DELETE implements `Echo#DELETE()` for sub-routes within the Group.
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(DELETE, path, h, m...)
}

// GET implements `Echo#GET()` for sub-routes within the Group.
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(GET, path, h, m...)
}

// HEAD implements `Echo#HEAD()` for sub-routes within the Group.
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(HEAD, path, h, m...)
}

// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(OPTIONS, path, h, m...)
}

// PATCH implements `Echo#PATCH()` for sub-routes within the Group.
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(PATCH, path, h, m...)
}

// POST implements `Echo#POST()` for sub-routes within the Group.
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(POST, path, h, m...)
}

// PUT implements `Echo#PUT()` for sub-routes within the Group.
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(PUT, path, h, m...)
}

// TRACE implements `Echo#TRACE()` for sub-routes within the Group.
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(TRACE, path, h, m...)
}

// Any implements `Echo#Any()` for sub-routes within the Group.
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
	for _, m := range methods {
		g.add(m, path, handler, middleware...)
	}
}

// Match implements `Echo#Match()` for sub-routes within the Group.
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
	for _, m := range methods {
		g.add(m, path, handler, middleware...)
	}
}

// Group creates a new sub-group with prefix and optional sub-group-level middleware.
func (g *Group) Group(prefix string, middleware ...MiddlewareFunc) *Group {
	m := []MiddlewareFunc{}
	m = append(m, g.middleware...)
	m = append(m, middleware...)
	return g.echo.Group(g.prefix+prefix, m...)
}

// Static implements `Echo#Static()` for sub-routes within the Group.
func (g *Group) Static(prefix, root string) {
	g.echo.Static(g.prefix+prefix, root)
}

// File implements `Echo#File()` for sub-routes within the Group.
func (g *Group) File(path, file string) {
	g.echo.File(g.prefix+path, file)
}

func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
	// Combine into a new slice to avoid accidentally passing the same slice for
	// multiple routes, which would lead to later add() calls overwriting the
	// middleware from earlier calls.
	m := []MiddlewareFunc{}
	m = append(m, g.middleware...)
	m = append(m, middleware...)
	g.echo.add(method, g.prefix+path, handler, m...)
}
<commit_msg>fixed default routes for group<commit_after>package echo

type (
	// Group is a set of sub-routes for a specified route. It can be used for inner
	// routes that share a common middlware or functionality that should be separate
	// from the parent echo instance while still inheriting from it.
	Group struct {
		prefix     string
		middleware []MiddlewareFunc
		echo       *Echo
	}
)

// Use implements `Echo#Use()` for sub-routes within the Group.
func (g *Group) Use(middleware ...MiddlewareFunc) {
	g.middleware = append(g.middleware, middleware...)
	// Allow requests `/prefix & /prefix/*` to reach the group as they might get
	// dropped if router doesn't find a match, making none of the group middleware
	// execute.
	paths := []string{"/*"}
	if g.prefix == "" {
		paths = append(paths, "/")
	} else {
		paths = append(paths, "")
	}
	for _, p := range paths {
		g.Any(p, NotFoundHandler, g.middleware...)
	}
}

// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(CONNECT, path, h, m...)
}

// DELETE implements `Echo#DELETE()` for sub-routes within the Group.
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(DELETE, path, h, m...)
}

// GET implements `Echo#GET()` for sub-routes within the Group.
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(GET, path, h, m...)
}

// HEAD implements `Echo#HEAD()` for sub-routes within the Group.
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(HEAD, path, h, m...)
}

// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(OPTIONS, path, h, m...)
}

// PATCH implements `Echo#PATCH()` for sub-routes within the Group.
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(PATCH, path, h, m...)
}

// POST implements `Echo#POST()` for sub-routes within the Group.
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(POST, path, h, m...)
}

// PUT implements `Echo#PUT()` for sub-routes within the Group.
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(PUT, path, h, m...)
}

// TRACE implements `Echo#TRACE()` for sub-routes within the Group.
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) {
	g.add(TRACE, path, h, m...)
}

// Any implements `Echo#Any()` for sub-routes within the Group.
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
	for _, m := range methods {
		g.add(m, path, handler, middleware...)
	}
}

// Match implements `Echo#Match()` for sub-routes within the Group.
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
	for _, m := range methods {
		g.add(m, path, handler, middleware...)
	}
}

// Group creates a new sub-group with prefix and optional sub-group-level middleware.
func (g *Group) Group(prefix string, middleware ...MiddlewareFunc) *Group {
	m := []MiddlewareFunc{}
	m = append(m, g.middleware...)
	m = append(m, middleware...)
	return g.echo.Group(g.prefix+prefix, m...)
}

// Static implements `Echo#Static()` for sub-routes within the Group.
func (g *Group) Static(prefix, root string) {
	g.echo.Static(g.prefix+prefix, root)
}

// File implements `Echo#File()` for sub-routes within the Group.
func (g *Group) File(path, file string) {
	g.echo.File(g.prefix+path, file)
}

func (g *Group) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
	// Combine into a new slice to avoid accidentally passing the same slice for
	// multiple routes, which would lead to later add() calls overwriting the
	// middleware from earlier calls.
	m := []MiddlewareFunc{}
	m = append(m, g.middleware...)
	m = append(m, middleware...)
	g.echo.add(method, g.prefix+path, handler, m...)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/Zac-Garby/pluto/bytecode"
	"github.com/Zac-Garby/pluto/compiler"
	"github.com/Zac-Garby/pluto/parser"
	"github.com/Zac-Garby/pluto/vm"
)

func main() {
	compiler := compiler.New()

	p := parser.New(`

a = 5
a

`)
	program := p.Parse()

	if len(p.Errors) > 0 {
		p.PrintErrors()
		os.Exit(1)
	}

	compiler.CompileProgram(program)

	code, err := bytecode.Read(compiler.Bytes)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	store := vm.NewStore()
	store.Names = compiler.Names

	machine := vm.New()
	machine.Run(code, store, compiler.Constants)

	if machine.Error != nil {
		fmt.Println(machine.Error)
		return
	}

	val := machine.ExtractValue()

	fmt.Println(">>", val)
}
<commit_msg>Only print output if it's non-nil<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/Zac-Garby/pluto/bytecode"
	"github.com/Zac-Garby/pluto/compiler"
	"github.com/Zac-Garby/pluto/parser"
	"github.com/Zac-Garby/pluto/vm"
)

func main() {
	compiler := compiler.New()

	p := parser.New(`

a = 5
a

`)
	program := p.Parse()

	if len(p.Errors) > 0 {
		p.PrintErrors()
		os.Exit(1)
	}

	compiler.CompileProgram(program)

	code, err := bytecode.Read(compiler.Bytes)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	store := vm.NewStore()
	store.Names = compiler.Names

	machine := vm.New()
	machine.Run(code, store, compiler.Constants)

	if machine.Error != nil {
		fmt.Println(machine.Error)
		return
	}

	val := machine.ExtractValue()

	if val != nil {
		fmt.Println(">>", val)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/nickpeirson/gearadmin"
	"github.com/nsf/termbox-go"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

type display struct {
	statusLines   gearadmin.StatusLines
	fieldWidths   fieldWidths
	position      int
	height        int
	headerHeight  int
	footerHeight  int
	numberOfRows  int
	width         int
	initialised   bool
	sortField     rune
	sortAscending bool
	redraw        chan bool
}

type fieldWidths struct {
	name    int
	queued  int
	running int
	workers int
	total   int
}

var VERSION = "0.2.0"
var pollInterval = 1 * time.Second
var quit = make(chan bool)
var statusDisplay = display{}
var columnNames = gearadmin.StatusLine{
	Name:    "Job name",
	Queued:  "Queued",
	Running: "Running",
	Workers: "Workers",
}

func fieldWidthsFactory(status gearadmin.StatusLines) (widths fieldWidths) {
	widths = fieldWidths{
		len(columnNames.Name),
		len(columnNames.Queued),
		len(columnNames.Running),
		len(columnNames.Workers),
		0,
	}
	for _, statusLine := range status {
		widths.name = max(len(statusLine.Name)+1, widths.name)
		widths.queued = max(len(statusLine.Queued)+1, widths.queued)
		widths.running = max(len(statusLine.Running)+1, widths.running)
		widths.workers = max(len(statusLine.Workers), widths.workers)
	}
	widths.total = widths.name + widths.queued + widths.running + widths.workers + 3
	return
}

var doLogging bool
var showAll bool
var gearmanHost string
var gearmanPort string
var initialSortIndex string
var queueNameInclude string
var queueNameExclude string

func init() {
	logDefault := false
	logUsage := "Log debug to /tmp/gearman_gtop.log"
	flag.BoolVar(&doLogging, "log", logDefault, logUsage)
	flag.BoolVar(&doLogging, "l", logDefault, logUsage+" (shorthand)")
	allDefault := false
	allUsage := "Show all queues, even if the have no workers or jobs"
	flag.BoolVar(&showAll, "all", allDefault, allUsage)
	flag.BoolVar(&showAll, "a", allDefault, allUsage+" (shorthand)")
	hostDefault := "localhost"
	hostUsage := "Gearmand host to connect to"
	flag.StringVar(&gearmanHost, "host", hostDefault, hostUsage)
	flag.StringVar(&gearmanHost, "h", hostDefault, hostUsage+" (shorthand)")
	portDefault := "4730"
	portUsage := "Gearmand port to connect to"
	flag.StringVar(&gearmanPort, "port", portDefault, portUsage)
	flag.StringVar(&gearmanPort, "p", portDefault, portUsage+" (shorthand)")
	flag.StringVar(&initialSortIndex, "sort", "1", "Index of the column to sort by")
	flag.StringVar(&queueNameInclude, "filterInclude", "", "Include queues containing this string. Can provide multiple separated by commas.")
	flag.StringVar(&queueNameExclude, "filterExclude", "", "Exclude queues containing this string. Can provide multiple separated by commas.")
	statusDisplay.redraw = make(chan bool, 5)
}

func main() {
	flag.Parse()
	if doLogging {
		defer (initLogging()).Close()
	} else {
		log.SetOutput(ioutil.Discard)
	}
	statusDisplay.sortEvent(rune(initialSortIndex[0]))

	err := termbox.Init()
	if err != nil {
		fatal(err.Error())
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)
	log.Println("Termbox initialised")

	statusDisplay.resize(termbox.Size())

	go statusDisplay.updateLines()
	go handleEvents()
	go statusDisplay.draw()
	<-quit
	log.Println("Exiting")
	return
}

func handleEvents() {
	for {
		event := termbox.PollEvent()
		log.Println("Recieved event: ", event)
		switch event.Type {
		case termbox.EventKey:
			switch event.Ch {
			case 'q':
				quit <- true
			case '1', '2', '3', '4':
				statusDisplay.sortEvent(event.Ch)
			default:
				switch event.Key {
				case termbox.KeyCtrlC:
					quit <- true
				case termbox.KeyArrowUp:
					statusDisplay.scrollOutput(-1)
				case termbox.KeyArrowDown:
					statusDisplay.scrollOutput(+1)
				}
			}
		case termbox.EventResize:
			log.Println("Redrawing for resize")
			statusDisplay.resize(event.Width, event.Height)
		}
	}
}

func (d *display) updateLines() {
	log.Println("Connecting to gearman")
	gearadminClient := gearadmin.New(gearmanHost, gearmanPort)
	defer gearadminClient.Close()
	responseFilter := statusFilter(initialiseFilters())
	for {
		log.Println("Getting status")
		start := time.Now()
		statusLines, err := gearadminClient.StatusFiltered(responseFilter)
		if err != nil {
			fatal("Couldn't get gearman status from " + gearmanHost + ":" + gearmanPort + " (Error: " + err.Error() + ")")
			return
		}
		d.statusLines = statusLines
		d.sortLines()
		d.fieldWidths = fieldWidthsFactory(statusLines)
		d.redraw <- true
		duration := time.Since(start)
		time.Sleep(pollInterval - duration)
	}
}

func (d *display) scrollOutput(direction int) {
	log.Println("Scrolling")
	scrolledToTop := d.position == 0
	scrolledToBottom := len(d.statusLines) - d.position == d.numberOfRows
	if (direction < 0 && !scrolledToTop) || (direction > 0 && !scrolledToBottom) {
		log.Println("Moving")
		d.position += direction
		d.redraw <- true
	}
}

func (d *display) draw() {
	for {
		<-d.redraw
		lines := d.statusLines

		widths := d.fieldWidths
		widths.name += d.width - widths.total

		termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
		if len(lines) > 0 {
			log.Print("First line: ", lines[0])
			log.Print("Last line: ", lines[len(lines)-1])
		} else {
			log.Print("No lines")
		}
		d.headerHeight = drawHeader(widths)
		d.footerHeight = drawFooter(lines, d.position, d.height, d.width)
		d.numberOfRows = d.height-d.headerHeight-d.footerHeight
		printY := d.headerHeight
		printLines := lines[d.position:]
		if len(printLines) > d.numberOfRows {
			printLines = printLines[:d.numberOfRows]
		}
		for _, line := range printLines {
			drawLine(printY, widths, line, false)
			printY++
		}

		termbox.Flush()
	}
}

func drawHeader(widths fieldWidths) int {
	drawLine(0, widths, columnNames, true)
	return 1
}

func drawLine(y int, widths fieldWidths, line gearadmin.StatusLine, bold bool) {
	x := 0
	if len(line.Name) > widths.name {
		line.Name = line.Name[:widths.name]
	}
	x = drawField(x, y, widths.name, line.Name, bold)
	x = drawField(x, y, widths.queued, line.Queued, bold)
	x = drawField(x, y, widths.running, line.Running, bold)
	x = drawField(x, y, widths.workers, line.Workers, bold)
}

func drawField(x, y, fieldWidth int, value string, bold bool) int {
	intValue, ok := strconv.Atoi(value)
	if ok == nil {
		value = fmt.Sprintf("%"+strconv.Itoa(fieldWidth)+"d", intValue) + " "
	}
	fg := termbox.ColorDefault
	if bold {
		fg |= termbox.AttrBold
	}
	print_tb(x, y, fg, termbox.ColorDefault, value)
	return x + fieldWidth + 1
}

func drawFooter(sl gearadmin.StatusLines, position, y, width int) (footerHeight int) {
	footerHeight = 1
	displayedLines := y + position - 1
	totalLines := len(sl)
	progress := fmt.Sprintf("%d/%d", min(displayedLines, totalLines), totalLines)
	print_tb(width-len(progress), y - footerHeight, termbox.ColorDefault, termbox.ColorDefault, progress)
	return
}

func statusFilter(includeTerms, excludeTerms []string) gearadmin.StatusLineFilter {
	return func(line gearadmin.StatusLine) bool {
		if !showAll && line.Queued == "0" &&
			line.Running == "0" && line.Workers == "0" {
			return false
		}
		if len(includeTerms) == 0 && len(excludeTerms) == 0 {
			return true
		}
		name := strings.ToLower(line.Name)
		for _, excludeTerm := range excludeTerms {
			if strings.Contains(name, excludeTerm) {
				return false
			}
		}
		for _, includeTerm := range includeTerms {
			if strings.Contains(name, includeTerm) {
				return true
			}
		}
		return len(includeTerms) == 0
	}
}

func initialiseFilters() (include, exclude []string) {
	if len(queueNameInclude) > 0 {
		queueNameInclude = strings.ToLower(queueNameInclude)
		include = strings.Split(queueNameInclude, ",")
	}
	if len(queueNameExclude) > 0 {
		queueNameExclude = strings.ToLower(queueNameExclude)
		exclude = strings.Split(queueNameExclude, ",")
	}
	log.Printf("Including: %d %v", len(include), include)
	log.Printf("Excluding: %d %v", len(exclude), exclude)
	return
}

var sortFields = map[rune]gearadmin.By{
	'1': gearadmin.ByName,
	'2': gearadmin.ByQueued,
	'3': gearadmin.ByRunning,
	'4': gearadmin.ByWorkers,
}

func (d *display) sortLines() {
	d.statusLines.Sort(sortFields[d.sortField], d.sortAscending)
}

func (d *display) sortEvent(index rune) {
	log.Println("Handling sort event")
	if d.sortField == index {
		d.sortAscending = !d.sortAscending
	} else if index == '1' {
		d.sortAscending = true
	} else {
		d.sortAscending = false
	}
	d.sortField = index
	d.sortLines()
	log.Printf("%#v\n", d.redraw)
	d.redraw <- true
}

func (d *display) resize(width, height int) {
	log.Println("Display resized")
	d.height = height
	d.width = width
	d.redraw <- true
}

func initLogging() *os.File {
	f, err := os.OpenFile("/tmp/gearman_gtop.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	log.SetOutput(f)
	log.Println("Logging initialised")
	return f
}

func print_tb(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x++
	}
}

func fatal(msg string) {
	termbox.Close()
	log.Println("Exiting: ", msg)
	fmt.Println(msg)
	os.Exit(2)
}

func max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}
<commit_msg>Go fmt<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/nickpeirson/gearadmin"
	"github.com/nsf/termbox-go"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

type display struct {
	statusLines   gearadmin.StatusLines
	fieldWidths   fieldWidths
	position      int
	height        int
	headerHeight  int
	footerHeight  int
	numberOfRows  int
	width         int
	initialised   bool
	sortField     rune
	sortAscending bool
	redraw        chan bool
}

type fieldWidths struct {
	name    int
	queued  int
	running int
	workers int
	total   int
}

var VERSION = "0.2.0"
var pollInterval = 1 * time.Second
var quit = make(chan bool)
var statusDisplay = display{}
var columnNames = gearadmin.StatusLine{
	Name:    "Job name",
	Queued:  "Queued",
	Running: "Running",
	Workers: "Workers",
}

func fieldWidthsFactory(status gearadmin.StatusLines) (widths fieldWidths) {
	widths = fieldWidths{
		len(columnNames.Name),
		len(columnNames.Queued),
		len(columnNames.Running),
		len(columnNames.Workers),
		0,
	}
	for _, statusLine := range status {
		widths.name = max(len(statusLine.Name)+1, widths.name)
		widths.queued = max(len(statusLine.Queued)+1, widths.queued)
		widths.running = max(len(statusLine.Running)+1, widths.running)
		widths.workers = max(len(statusLine.Workers), widths.workers)
	}
	widths.total = widths.name + widths.queued + widths.running + widths.workers + 3
	return
}

var doLogging bool
var showAll bool
var gearmanHost string
var gearmanPort string
var initialSortIndex string
var queueNameInclude string
var queueNameExclude string

func init() {
	logDefault := false
	logUsage := "Log debug to /tmp/gearman_gtop.log"
	flag.BoolVar(&doLogging, "log", logDefault, logUsage)
	flag.BoolVar(&doLogging, "l", logDefault, logUsage+" (shorthand)")
	allDefault := false
	allUsage := "Show all queues, even if the have no workers or jobs"
	flag.BoolVar(&showAll, "all", allDefault, allUsage)
	flag.BoolVar(&showAll, "a", allDefault, allUsage+" (shorthand)")
	hostDefault := "localhost"
	hostUsage := "Gearmand host to connect to"
	flag.StringVar(&gearmanHost, "host", hostDefault, hostUsage)
	flag.StringVar(&gearmanHost, "h", hostDefault, hostUsage+" (shorthand)")
	portDefault := "4730"
	portUsage := "Gearmand port to connect to"
	flag.StringVar(&gearmanPort, "port", portDefault, portUsage)
	flag.StringVar(&gearmanPort, "p", portDefault, portUsage+" (shorthand)")
	flag.StringVar(&initialSortIndex, "sort", "1", "Index of the column to sort by")
	flag.StringVar(&queueNameInclude, "filterInclude", "", "Include queues containing this string. Can provide multiple separated by commas.")
	flag.StringVar(&queueNameExclude, "filterExclude", "", "Exclude queues containing this string. Can provide multiple separated by commas.")
	statusDisplay.redraw = make(chan bool, 5)
}

func main() {
	flag.Parse()
	if doLogging {
		defer (initLogging()).Close()
	} else {
		log.SetOutput(ioutil.Discard)
	}
	statusDisplay.sortEvent(rune(initialSortIndex[0]))

	err := termbox.Init()
	if err != nil {
		fatal(err.Error())
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)
	log.Println("Termbox initialised")

	statusDisplay.resize(termbox.Size())

	go statusDisplay.updateLines()
	go handleEvents()
	go statusDisplay.draw()
	<-quit
	log.Println("Exiting")
	return
}

func handleEvents() {
	for {
		event := termbox.PollEvent()
		log.Println("Recieved event: ", event)
		switch event.Type {
		case termbox.EventKey:
			switch event.Ch {
			case 'q':
				quit <- true
			case '1', '2', '3', '4':
				statusDisplay.sortEvent(event.Ch)
			default:
				switch event.Key {
				case termbox.KeyCtrlC:
					quit <- true
				case termbox.KeyArrowUp:
					statusDisplay.scrollOutput(-1)
				case termbox.KeyArrowDown:
					statusDisplay.scrollOutput(+1)
				}
			}
		case termbox.EventResize:
			log.Println("Redrawing for resize")
			statusDisplay.resize(event.Width, event.Height)
		}
	}
}

func (d *display) updateLines() {
	log.Println("Connecting to gearman")
	gearadminClient := gearadmin.New(gearmanHost, gearmanPort)
	defer gearadminClient.Close()
	responseFilter := statusFilter(initialiseFilters())
	for {
		log.Println("Getting status")
		start := time.Now()
		statusLines, err := gearadminClient.StatusFiltered(responseFilter)
		if err != nil {
			fatal("Couldn't get gearman status from " + gearmanHost + ":" + gearmanPort + " (Error: " + err.Error() + ")")
			return
		}
		d.statusLines = statusLines
		d.sortLines()
		d.fieldWidths = fieldWidthsFactory(statusLines)
		d.redraw <- true
		duration := time.Since(start)
		time.Sleep(pollInterval - duration)
	}
}

func (d *display) scrollOutput(direction int) {
	log.Println("Scrolling")
	scrolledToTop := d.position == 0
	scrolledToBottom := len(d.statusLines)-d.position == d.numberOfRows
	if (direction < 0 && !scrolledToTop) || (direction > 0 && !scrolledToBottom) {
		log.Println("Moving")
		d.position += direction
		d.redraw <- true
	}
}

func (d *display) draw() {
	for {
		<-d.redraw
		lines := d.statusLines

		widths := d.fieldWidths
		widths.name += d.width - widths.total

		termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
		if len(lines) > 0 {
			log.Print("First line: ", lines[0])
			log.Print("Last line: ", lines[len(lines)-1])
		} else {
			log.Print("No lines")
		}
		d.headerHeight = drawHeader(widths)
		d.footerHeight = drawFooter(lines, d.position, d.height, d.width)
		d.numberOfRows = d.height - d.headerHeight - d.footerHeight
		printY := d.headerHeight
		printLines := lines[d.position:]
		if len(printLines) > d.numberOfRows {
			printLines = printLines[:d.numberOfRows]
		}
		for _, line := range printLines {
			drawLine(printY, widths, line, false)
			printY++
		}

		termbox.Flush()
	}
}

func drawHeader(widths fieldWidths) int {
	drawLine(0, widths, columnNames, true)
	return 1
}

func drawLine(y int, widths fieldWidths, line gearadmin.StatusLine, bold bool) {
	x := 0
	if len(line.Name) > widths.name {
		line.Name = line.Name[:widths.name]
	}
	x = drawField(x, y, widths.name, line.Name, bold)
	x = drawField(x, y, widths.queued, line.Queued, bold)
	x = drawField(x, y, widths.running, line.Running, bold)
	x = drawField(x, y, widths.workers, line.Workers, bold)
}

func drawField(x, y, fieldWidth int, value string, bold bool) int {
	intValue, ok := strconv.Atoi(value)
	if ok == nil {
		value = fmt.Sprintf("%"+strconv.Itoa(fieldWidth)+"d", intValue) + " "
	}
	fg := termbox.ColorDefault
	if bold {
		fg |= termbox.AttrBold
	}
	print_tb(x, y, fg, termbox.ColorDefault, value)
	return x + fieldWidth + 1
}

func drawFooter(sl gearadmin.StatusLines, position, y, width int) (footerHeight int) {
	footerHeight = 1
	displayedLines := y + position - 1
	totalLines := len(sl)
	progress := fmt.Sprintf("%d/%d", min(displayedLines, totalLines), totalLines)
	print_tb(width-len(progress), y-footerHeight, termbox.ColorDefault, termbox.ColorDefault, progress)
	return
}

func statusFilter(includeTerms, excludeTerms []string) gearadmin.StatusLineFilter {
	return func(line gearadmin.StatusLine) bool {
		if !showAll && line.Queued == "0" &&
			line.Running == "0" && line.Workers == "0" {
			return false
		}
		if len(includeTerms) == 0 && len(excludeTerms) == 0 {
			return true
		}
		name := strings.ToLower(line.Name)
		for _, excludeTerm := range excludeTerms {
			if strings.Contains(name, excludeTerm) {
				return false
			}
		}
		for _, includeTerm := range includeTerms {
			if strings.Contains(name, includeTerm) {
				return true
			}
		}
		return len(includeTerms) == 0
	}
}

func initialiseFilters() (include, exclude []string) {
	if len(queueNameInclude) > 0 {
		queueNameInclude = strings.ToLower(queueNameInclude)
		include = strings.Split(queueNameInclude, ",")
	}
	if len(queueNameExclude) > 0 {
		queueNameExclude = strings.ToLower(queueNameExclude)
		exclude = strings.Split(queueNameExclude, ",")
	}
	log.Printf("Including: %d %v", len(include), include)
	log.Printf("Excluding: %d %v", len(exclude), exclude)
	return
}

var sortFields = map[rune]gearadmin.By{
	'1': gearadmin.ByName,
	'2': gearadmin.ByQueued,
	'3': gearadmin.ByRunning,
	'4': gearadmin.ByWorkers,
}

func (d *display) sortLines() {
	d.statusLines.Sort(sortFields[d.sortField], d.sortAscending)
}

func (d *display) sortEvent(index rune) {
	log.Println("Handling sort event")
	if d.sortField == index {
		d.sortAscending = !d.sortAscending
	} else if index == '1' {
		d.sortAscending = true
	} else {
		d.sortAscending = false
	}
	d.sortField = index
	d.sortLines()
	log.Printf("%#v\n", d.redraw)
	d.redraw <- true
}

func (d *display) resize(width, height int) {
	log.Println("Display resized")
	d.height = height
	d.width = width
	d.redraw <- true
}

func initLogging() *os.File {
	f, err := os.OpenFile("/tmp/gearman_gtop.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	log.SetOutput(f)
	log.Println("Logging initialised")
	return f
}

func print_tb(x, y int, fg, bg termbox.Attribute, msg string) {
	for _, c := range msg {
		termbox.SetCell(x, y, c, fg, bg)
		x++
	}
}

func fatal(msg string) {
	termbox.Close()
	log.Println("Exiting: ", msg)
	fmt.Println(msg)
	os.Exit(2)
}

func max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2018 Tigera, 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 commands

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"text/tabwriter"
	"text/template"

	log "github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/runtime"

	"github.com/projectcalico/calicoctl/calicoctl/resourcemgr"
	"github.com/projectcalico/go-json/json"
	"github.com/projectcalico/go-yaml-wrapper"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
	"github.com/projectcalico/libcalico-go/lib/options"
)

type resourcePrinter interface {
	print(client client.Interface, resources []runtime.Object) error
}

// resourcePrinterJSON implements the resourcePrinter interface and is used to display
// a slice of resources in JSON format.
type resourcePrinterJSON struct{}

func (r resourcePrinterJSON) print(client client.Interface, resources []runtime.Object) error {
	// If the results contain a single entry then extract the only value.
	var rs interface{}
	if len(resources) == 1 {
		rs = resources[0]
	} else {
		rs = resources
	}
	if output, err := json.MarshalIndent(rs, "", "  "); err != nil {
		return err
	} else {
		fmt.Printf("%s\n", string(output))
	}
	return nil
}

// resourcePrinterYAML implements the resourcePrinter interface and is used to display
// a slice of resources in YAML format.
type resourcePrinterYAML struct{}

func (r resourcePrinterYAML) print(client client.Interface, resources []runtime.Object) error {
	// If the results contain a single entry then extract the only value.
	var rs interface{}
	if len(resources) == 1 {
		rs = resources[0]
	} else {
		rs = resources
	}
	if output, err := yaml.Marshal(rs); err != nil {
		return err
	} else {
		fmt.Printf("%s", string(output))
	}
	return nil
}

// resourcePrinterTable implements the resourcePrinter interface and is used to display
// a slice of resources in ps table format.
type resourcePrinterTable struct {
	// The headings to display in the table.  If this is nil, the default headings for the
	// resource are used instead (in which case the `wide` boolean below is used to specify
	// whether wide or narrow format is required.
	headings []string

	// Wide format.  When headings have not been explicitly specified, this is used to
	// determine whether to the resource-specific default wide or narrow headings.
	wide bool

	// Namespace included. When a resource being printed is namespaced, this is used
	// to determine if the namespace column should be printed or not.
	printNamespace bool
}

func (r resourcePrinterTable) print(client client.Interface, resources []runtime.Object) error {
	log.Infof("Output in table format (wide=%v)", r.wide)
	for _, resource := range resources {
		// Get the resource manager for the resource type.
		rm := resourcemgr.GetResourceManager(resource)

		// If no headings have been specified then we must be using the default
		// headings for that resource type.
		headings := r.headings
		if r.headings == nil {
			headings = rm.GetTableDefaultHeadings(r.wide)
		}

		// Look up the template string for the specific resource type.
		tpls, err := rm.GetTableTemplate(headings, r.printNamespace)
		if err != nil {
			return err
		}
		log.WithField("template", tpls).Debug("Got resource template")

		// Convert the template string into a template - we need to include the join
		// function.
		fns := template.FuncMap{
			"join":            join,
			"joinAndTruncate": joinAndTruncate,
			"config":          config(client),
		}
		tmpl, err := template.New("get").Funcs(fns).Parse(tpls)
		if err != nil {
			panic(err)
		}

		// Use a tabwriter to write out the template - this provides better formatting.
		writer := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
		err = tmpl.Execute(writer, resource)
		if err != nil {
			panic(err)
		}
		writer.Flush()

		// Templates for ps format are internally defined and therefore we should not
		// hit errors writing the table formats.
		if err != nil {
			panic(err)
		}

		// Leave a gap after each table.
		fmt.Printf("\n")
	}
	return nil
}

// resourcePrinterTemplateFile implements the resourcePrinter interface and is used to display
// a slice of resources using a user-defined go-lang template specified in a file.
type resourcePrinterTemplateFile struct {
	templateFile string
}

func (r resourcePrinterTemplateFile) print(client client.Interface, resources []runtime.Object) error {
	template, err := ioutil.ReadFile(r.templateFile)
	if err != nil {
		return err
	}
	rp := resourcePrinterTemplate{template: string(template)}
	return rp.print(client, resources)
}

// resourcePrinterTemplate implements the resourcePrinter interface and is used to display
// a slice of resources using a user-defined go-lang template string.
type resourcePrinterTemplate struct {
	template string
}

func (r resourcePrinterTemplate) print(client client.Interface, resources []runtime.Object) error {
	// We include a join function in the template as it's useful for multi
	// value columns.
	fns := template.FuncMap{
		"join":            join,
		"joinAndTruncate": joinAndTruncate,
		"config":          config(client),
	}
	tmpl, err := template.New("get").Funcs(fns).Parse(r.template)
	if err != nil {
		return err
	}

	err = tmpl.Execute(os.Stdout, resources)
	return err
}

// join is similar to strings.Join() but takes an arbitrary slice of interfaces and converts
// each to its string representation and joins them together with the provided separator
// string.
func join(items interface{}, separator string) string {
	return joinAndTruncate(items, separator, 0)
}

// joinAndTruncate is similar to strings.Join() but takes an arbitrary slice of interfaces and converts
// each to its string representation, joins them together with the provided separator
// string and (if maxLen is >0) truncates the output at the given maximum length.
func joinAndTruncate(items interface{}, separator string, maxLen int) string {
	if reflect.TypeOf(items).Kind() != reflect.Slice {
		// Input wasn't a slice, convert it to one so we can take advantage of shared
		// buffer/truncation logic...
		items = []interface{}{items}
	}

	slice := reflect.ValueOf(items)
	buf := new(bytes.Buffer)
	for i := 0; i < slice.Len(); i++ {
		if i > 0 {
			buf.WriteString(separator)
		}
		fmt.Fprint(buf, slice.Index(i).Interface())
		if maxLen > 0 && buf.Len() > maxLen {
			// Break out early so that we don't have to stringify a long list, only to then throw it away.
			const truncationSuffix = "..."
			buf.Truncate(maxLen - len(truncationSuffix))
			buf.WriteString(truncationSuffix)
			break
		}
	}
	return buf.String()
}

// config returns a function that returns the current global named config
// value.
func config(client client.Interface) func(string) string {
	var asValue string
	return func(name string) string {
		switch strings.ToLower(name) {
		case "asnumber":
			if asValue == "" {
				if bgpConfig, err := client.BGPConfigurations().Get(context.Background(), "default", options.GetOptions{}); err != nil {
					asValue = "64512"
				} else {
					asValue = bgpConfig.Spec.ASNumber.String()
				}
			}
			return asValue
		}
		panic("unhandled config type")
	}
}
<commit_msg>Added comments<commit_after>// Copyright (c) 2016-2018 Tigera, 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 commands

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"text/tabwriter"
	"text/template"

	log "github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/runtime"

	"github.com/projectcalico/calicoctl/calicoctl/resourcemgr"
	"github.com/projectcalico/go-json/json"
	"github.com/projectcalico/go-yaml-wrapper"
	client "github.com/projectcalico/libcalico-go/lib/clientv3"
	"github.com/projectcalico/libcalico-go/lib/options"
)

type resourcePrinter interface {
	print(client client.Interface, resources []runtime.Object) error
}

// resourcePrinterJSON implements the resourcePrinter interface and is used to display
// a slice of resources in JSON format.
type resourcePrinterJSON struct{}

func (r resourcePrinterJSON) print(client client.Interface, resources []runtime.Object) error {
	// If the results contain a single entry then extract the only value.
	var rs interface{}
	if len(resources) == 1 {
		rs = resources[0]
	} else {
		rs = resources
	}
	if output, err := json.MarshalIndent(rs, "", "  "); err != nil {
		return err
	} else {
		fmt.Printf("%s\n", string(output))
	}
	return nil
}

// resourcePrinterYAML implements the resourcePrinter interface and is used to display
// a slice of resources in YAML format.
type resourcePrinterYAML struct{}

func (r resourcePrinterYAML) print(client client.Interface, resources []runtime.Object) error {
	// If the results contain a single entry then extract the only value.
	var rs interface{}
	if len(resources) == 1 {
		rs = resources[0]
	} else {
		rs = resources
	}
	if output, err := yaml.Marshal(rs); err != nil {
		return err
	} else {
		fmt.Printf("%s", string(output))
	}
	return nil
}

// resourcePrinterTable implements the resourcePrinter interface and is used to display
// a slice of resources in ps table format.
type resourcePrinterTable struct {
	// The headings to display in the table.  If this is nil, the default headings for the
	// resource are used instead (in which case the `wide` boolean below is used to specify
	// whether wide or narrow format is required.
	headings []string

	// Wide format.  When headings have not been explicitly specified, this is used to
	// determine whether to the resource-specific default wide or narrow headings.
	wide bool

	// Namespace included. When a resource being printed is namespaced, this is used
	// to determine if the namespace column should be printed or not.
	printNamespace bool
}

func (r resourcePrinterTable) print(client client.Interface, resources []runtime.Object) error {
	log.Infof("Output in table format (wide=%v)", r.wide)
	for _, resource := range resources {
		// Get the resource manager for the resource type.
		rm := resourcemgr.GetResourceManager(resource)

		// If no headings have been specified then we must be using the default
		// headings for that resource type.
		headings := r.headings
		if r.headings == nil {
			headings = rm.GetTableDefaultHeadings(r.wide)
		}

		// Look up the template string for the specific resource type.
		tpls, err := rm.GetTableTemplate(headings, r.printNamespace)
		if err != nil {
			return err
		}
		log.WithField("template", tpls).Debug("Got resource template")

		// Convert the template string into a template - we need to include the join
		// function.
		fns := template.FuncMap{
			"join":            join,
			"joinAndTruncate": joinAndTruncate,
			"config":          config(client),
		}
		tmpl, err := template.New("get").Funcs(fns).Parse(tpls)
		if err != nil {
			panic(err)
		}

		// Use a tabwriter to write out the template - this provides better formatting.
		writer := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
		err = tmpl.Execute(writer, resource)
		if err != nil {
			panic(err)
		}
		writer.Flush()

		// Templates for ps format are internally defined and therefore we should not
		// hit errors writing the table formats.
		if err != nil {
			panic(err)
		}

		// Leave a gap after each table.
		fmt.Printf("\n")
	}
	return nil
}

// resourcePrinterTemplateFile implements the resourcePrinter interface and is used to display
// a slice of resources using a user-defined go-lang template specified in a file.
type resourcePrinterTemplateFile struct {
	templateFile string
}

func (r resourcePrinterTemplateFile) print(client client.Interface, resources []runtime.Object) error {
	template, err := ioutil.ReadFile(r.templateFile)
	if err != nil {
		return err
	}
	rp := resourcePrinterTemplate{template: string(template)}
	return rp.print(client, resources)
}

// resourcePrinterTemplate implements the resourcePrinter interface and is used to display
// a slice of resources using a user-defined go-lang template string.
type resourcePrinterTemplate struct {
	template string
}

func (r resourcePrinterTemplate) print(client client.Interface, resources []runtime.Object) error {
	// We include a join function in the template as it's useful for multi
	// value columns.
	fns := template.FuncMap{
		"join":            join,
		"joinAndTruncate": joinAndTruncate,
		"config":          config(client),
	}
	tmpl, err := template.New("get").Funcs(fns).Parse(r.template)
	if err != nil {
		return err
	}

	err = tmpl.Execute(os.Stdout, resources)
	return err
}

// join is similar to strings.Join() but takes an arbitrary slice of interfaces and converts
// each to its string representation and joins them together with the provided separator
// string.
func join(items interface{}, separator string) string {
	return joinAndTruncate(items, separator, 0)
}

// joinAndTruncate is similar to strings.Join() but takes an arbitrary slice of interfaces and converts
// each to its string representation, joins them together with the provided separator
// string and (if maxLen is >0) truncates the output at the given maximum length.
func joinAndTruncate(items interface{}, separator string, maxLen int) string {
	if reflect.TypeOf(items).Kind() != reflect.Slice {
		// Input wasn't a slice, convert it to one so we can take advantage of shared
		// buffer/truncation logic...
		items = []interface{}{items}
	}

	slice := reflect.ValueOf(items)
	buf := new(bytes.Buffer)
	for i := 0; i < slice.Len(); i++ {
		if i > 0 {
			buf.WriteString(separator)
		}
		fmt.Fprint(buf, slice.Index(i).Interface())
		if maxLen > 0 && buf.Len() > maxLen {
			// Break out early so that we don't have to stringify a long list, only to then throw it away.
			const truncationSuffix = "..."
			buf.Truncate(maxLen - len(truncationSuffix))
			buf.WriteString(truncationSuffix)
			break
		}
	}
	return buf.String()
}

// config returns a function that returns the current global named config
// value.
func config(client client.Interface) func(string) string {
	var asValue string
	return func(name string) string {
		switch strings.ToLower(name) {
		case "asnumber":
			if asValue == "" {
				if bgpConfig, err := client.BGPConfigurations().Get(context.Background(), "default", options.GetOptions{}); err != nil {
					// Use the default ASNumber of 64512 when there is none configured.
					asValue = "64512"
				} else {
					asValue = bgpConfig.Spec.ASNumber.String()
				}
			}
			return asValue
		}
		panic("unhandled config type")
	}
}
<|endoftext|>


<commit_before>package odb

import "io"

// Blob represents a Git object of type "blob".
type Blob struct {
	// Size is the total uncompressed size of the blob's contents.
	Size int64
	// Contents is a reader that yields the uncompressed blob contents. It
	// may only be read once. It may or may not implement io.ReadSeeker.
	Contents io.Reader

	// closeFn is a function that is called to free any resources held by
	// the Blob.  In particular, this will close a file, if the Blob is
	// being read from a file on disk.
	closeFn func() error
}

var _ Object = (*Blob)(nil)

// Type implements Object.ObjectType by returning the correct object type for
// Blobs, BlobObjectType.
func (b *Blob) Type() ObjectType { return BlobObjectType }

// Decode implements Object.Decode and decodes the uncompressed blob contents
// being read. It returns the number of bytes that it consumed off of the
// stream, which is always zero.
//
// If any error(s) was(were) encountered while reading the blob, that error will
// be returned.
func (b *Blob) Decode(r io.Reader, size int64) (n int, err error) {
	b.Size = size
	b.Contents = io.LimitReader(r, size)

	b.closeFn = func() error {
		if closer, ok := r.(io.Closer); ok {
			return closer.Close()
		}
		return nil
	}

	return 0, nil
}

// Encode encodes the blob's contents to the given io.Writer, "w". If there was
// any error copying the blob's contents, that error will be returned.
//
// Otherwise, the number of bytes written will be returned.
func (b *Blob) Encode(to io.Writer) (n int, err error) {
	nn, err := io.Copy(to, b.Contents)
	if err != nil {
		return 0, err
	}
	return int(nn), err
}

// Closes closes any resources held by the open Blob, or returns nil if there
// were no errors.
func (b *Blob) Close() error {
	if b.closeFn == nil {
		return nil
	}
	return b.closeFn()
}
<commit_msg>git/odb: simplify Blob.Encode<commit_after>package odb

import "io"

// Blob represents a Git object of type "blob".
type Blob struct {
	// Size is the total uncompressed size of the blob's contents.
	Size int64
	// Contents is a reader that yields the uncompressed blob contents. It
	// may only be read once. It may or may not implement io.ReadSeeker.
	Contents io.Reader

	// closeFn is a function that is called to free any resources held by
	// the Blob.  In particular, this will close a file, if the Blob is
	// being read from a file on disk.
	closeFn func() error
}

var _ Object = (*Blob)(nil)

// Type implements Object.ObjectType by returning the correct object type for
// Blobs, BlobObjectType.
func (b *Blob) Type() ObjectType { return BlobObjectType }

// Decode implements Object.Decode and decodes the uncompressed blob contents
// being read. It returns the number of bytes that it consumed off of the
// stream, which is always zero.
//
// If any error(s) was(were) encountered while reading the blob, that error will
// be returned.
func (b *Blob) Decode(r io.Reader, size int64) (n int, err error) {
	b.Size = size
	b.Contents = io.LimitReader(r, size)

	b.closeFn = func() error {
		if closer, ok := r.(io.Closer); ok {
			return closer.Close()
		}
		return nil
	}

	return 0, nil
}

// Encode encodes the blob's contents to the given io.Writer, "w". If there was
// any error copying the blob's contents, that error will be returned.
//
// Otherwise, the number of bytes written will be returned.
func (b *Blob) Encode(to io.Writer) (n int, err error) {
	nn, err := io.Copy(to, b.Contents)

	return int(nn), err
}

// Closes closes any resources held by the open Blob, or returns nil if there
// were no errors.
func (b *Blob) Close() error {
	if b.closeFn == nil {
		return nil
	}
	return b.closeFn()
}
<|endoftext|>


<commit_before>// Copyright 2018 The casbin Authors. 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 effector

import "errors"

// DefaultEffector is default effector for Casbin.
type DefaultEffector struct {
}

// NewDefaultEffector is the constructor for DefaultEffector.
func NewDefaultEffector() *DefaultEffector {
	e := DefaultEffector{}
	return &e
}

// MergeEffects merges all matching results collected by the enforcer into a single decision.
func (e *DefaultEffector) MergeEffects(expr string, effects []Effect, matches []float64, policyIndex int, policyLength int) (Effect, int, error) {
	result := Indeterminate
	explainIndex := -1

	// short-circuit some effects in the middle
	if expr != "priority(p_eft) || deny" {
		if policyIndex < policyLength-1 {
			// choose not to short-circuit
			return result, explainIndex, nil
		}
	}

	// merge all effects at last
	if expr == "some(where (p_eft == allow))" {
		result = Indeterminate
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft == Allow {
				result = Allow
				explainIndex = i
				break
			}
		}
	} else if expr == "!some(where (p_eft == deny))" {
		// if no deny rules are matched, then allow
		result = Allow
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft == Deny {
				result = Deny
				explainIndex = i
				break
			}
		}
	} else if expr == "some(where (p_eft == allow)) && !some(where (p_eft == deny))" {
		result = Indeterminate
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft == Allow {
				result = Allow
			} else if eft == Deny {
				result = Deny
				explainIndex = i
				break
			}
		}
	} else if expr == "priority(p_eft) || deny" {
		result = Indeterminate
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft != Indeterminate {
				if eft == Allow {
					result = Allow
				} else {
					result = Deny
				}
				explainIndex = i
				break
			}
		}
	} else {
		return Deny, -1, errors.New("unsupported effect")
	}

	return result, explainIndex, nil
}
<commit_msg>fix: Set hit rule to first matched allow rule for allow-and-deny effect.<commit_after>// Copyright 2018 The casbin Authors. 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 effector

import "errors"

// DefaultEffector is default effector for Casbin.
type DefaultEffector struct {
}

// NewDefaultEffector is the constructor for DefaultEffector.
func NewDefaultEffector() *DefaultEffector {
	e := DefaultEffector{}
	return &e
}

// MergeEffects merges all matching results collected by the enforcer into a single decision.
func (e *DefaultEffector) MergeEffects(expr string, effects []Effect, matches []float64, policyIndex int, policyLength int) (Effect, int, error) {
	result := Indeterminate
	explainIndex := -1

	// short-circuit some effects in the middle
	if expr != "priority(p_eft) || deny" {
		if policyIndex < policyLength-1 {
			// choose not to short-circuit
			return result, explainIndex, nil
		}
	}

	// merge all effects at last
	if expr == "some(where (p_eft == allow))" {
		result = Indeterminate
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft == Allow {
				result = Allow
				explainIndex = i
				break
			}
		}
	} else if expr == "!some(where (p_eft == deny))" {
		// if no deny rules are matched, then allow
		result = Allow
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft == Deny {
				result = Deny
				explainIndex = i
				break
			}
		}
	} else if expr == "some(where (p_eft == allow)) && !some(where (p_eft == deny))" {
		result = Indeterminate
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft == Allow {
				// set hit rule to first matched allow rule, maybe overridden by the deny part
				if result == Indeterminate {
					explainIndex = i
				}
				result = Allow
			} else if eft == Deny {
				result = Deny
				// set hit rule to the (first) matched deny rule
				explainIndex = i
				break
			}
		}
	} else if expr == "priority(p_eft) || deny" {
		result = Indeterminate
		for i, eft := range effects {
			if matches[i] == 0 {
				continue
			}

			if eft != Indeterminate {
				if eft == Allow {
					result = Allow
				} else {
					result = Deny
				}
				explainIndex = i
				break
			}
		}
	} else {
		return Deny, -1, errors.New("unsupported effect")
	}

	return result, explainIndex, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-github 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 github

import "fmt"

// RepositoriesService handles communication with the repository related
// methods of the GitHub API.
//
// GitHub API docs: http://developer.github.com/v3/repos/
type RepositoriesService struct {
	client *Client
}

// Repository represents a GitHub repository.
type Repository struct {
	ID              *int       `json:"id,omitempty"`
	Owner           *User      `json:"owner,omitempty"`
	Name            *string    `json:"name,omitempty"`
	Description     *string    `json:"description,omitempty"`
	Homepage        *string    `json:"homepage,omitempty"`
	DefaultBranch   *string    `json:"default_branch,omitempty"`
	MasterBranch    *string    `json:"master_branch,omitempty"`
	CreatedAt       *Timestamp `json:"created_at,omitempty"`
	PushedAt        *Timestamp `json:"pushed_at,omitempty"`
	UpdatedAt       *Timestamp `json:"updated_at,omitempty"`
	URL             *string    `json:"url,omitempty"`
	HTMLURL         *string    `json:"html_url,omitempty"`
	CloneURL        *string    `json:"clone_url,omitempty"`
	GitURL          *string    `json:"git_url,omitempty"`
	SSHURL          *string    `json:"ssh_url,omitempty"`
	SVNURL          *string    `json:"svn_url,omitempty"`
	Language        *string    `json:"language,omitempty"`
	Fork            *bool      `json:"fork"`
	ForksCount      *int       `json:"forks_count,omitempty"`
	WatchersCount   *int       `json:"watchers_count,omitempty"`
	OpenIssuesCount *int       `json:"open_issues_count,omitempty"`
	Size            *int       `json:"size,omitempty"`

	// Additional mutable fields when creating and editing a repository
	Private   *bool `json:"private"`
	HasIssues *bool `json:"has_issues"`
	HasWiki   *bool `json:"has_wiki"`
}

func (r Repository) String() string {
	return Stringify(r)
}

// RepositoryListOptions specifies the optional parameters to the
// RepositoriesService.List method.
type RepositoryListOptions struct {
	// Type of repositories to list.  Possible values are: all, owner, public,
	// private, member.  Default is "all".
	Type string `url:"type,omitempty"`

	// How to sort the repository list.  Possible values are: created, updated,
	// pushed, full_name.  Default is "full_name".
	Sort string `url:"sort,omitempty"`

	// Direction in which to sort repositories.  Possible values are: asc, desc.
	// Default is "asc" when sort is "full_name", otherwise default is "desc".
	Direction string `url:"direction,omitempty"`

	ListOptions
}

// List the repositories for a user.  Passing the empty string will list
// repositories for the authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/repos/#list-user-repositories
func (s *RepositoriesService) List(user string, opt *RepositoryListOptions) ([]Repository, *Response, error) {
	var u string
	if user != "" {
		u = fmt.Sprintf("users/%v/repos", user)
	} else {
		u = "user/repos"
	}
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repos := new([]Repository)
	resp, err := s.client.Do(req, repos)
	if err != nil {
		return nil, resp, err
	}

	return *repos, resp, err
}

// RepositoryListByOrgOptions specifies the optional parameters to the
// RepositoriesService.ListByOrg method.
type RepositoryListByOrgOptions struct {
	// Type of repositories to list.  Possible values are: all, public, private,
	// forks, sources, member.  Default is "all".
	Type string `url:"type,omitempty"`

	ListOptions
}

// ListByOrg lists the repositories for an organization.
//
// GitHub API docs: http://developer.github.com/v3/repos/#list-organization-repositories
func (s *RepositoriesService) ListByOrg(org string, opt *RepositoryListByOrgOptions) ([]Repository, *Response, error) {
	u := fmt.Sprintf("orgs/%v/repos", org)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repos := new([]Repository)
	resp, err := s.client.Do(req, repos)
	if err != nil {
		return nil, resp, err
	}

	return *repos, resp, err
}

// RepositoryListAllOptions specifies the optional parameters to the
// RepositoriesService.ListAll method.
type RepositoryListAllOptions struct {
	// ID of the last repository seen
	Since int `url:"since,omitempty"`

	ListOptions
}

// ListAll lists all GitHub repositories in the order that they were created.
//
// GitHub API docs: http://developer.github.com/v3/repos/#list-all-public-repositories
func (s *RepositoriesService) ListAll(opt *RepositoryListAllOptions) ([]Repository, *Response, error) {
	u, err := addOptions("repositories", opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repos := new([]Repository)
	resp, err := s.client.Do(req, repos)
	if err != nil {
		return nil, resp, err
	}

	return *repos, resp, err
}

// Create a new repository.  If an organization is specified, the new
// repository will be created under that org.  If the empty string is
// specified, it will be created for the authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/repos/#create
func (s *RepositoriesService) Create(org string, repo *Repository) (*Repository, *Response, error) {
	var u string
	if org != "" {
		u = fmt.Sprintf("orgs/%v/repos", org)
	} else {
		u = "user/repos"
	}

	req, err := s.client.NewRequest("POST", u, repo)
	if err != nil {
		return nil, nil, err
	}

	r := new(Repository)
	resp, err := s.client.Do(req, r)
	if err != nil {
		return nil, resp, err
	}

	return r, resp, err
}

// Get fetches a repository.
//
// GitHub API docs: http://developer.github.com/v3/repos/#get
func (s *RepositoriesService) Get(owner, repo string) (*Repository, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repository := new(Repository)
	resp, err := s.client.Do(req, repository)
	if err != nil {
		return nil, resp, err
	}

	return repository, resp, err
}

// Edit updates a repository.
//
// GitHub API docs: http://developer.github.com/v3/repos/#edit
func (s *RepositoriesService) Edit(owner, repo string, repository *Repository) (*Repository, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v", owner, repo)
	req, err := s.client.NewRequest("PATCH", u, repository)
	if err != nil {
		return nil, nil, err
	}

	r := new(Repository)
	resp, err := s.client.Do(req, r)
	if err != nil {
		return nil, resp, err
	}

	return r, resp, err
}

// ListLanguages lists languages for the specified repository. The returned map
// specifies the languages and the number of bytes of code written in that
// language. For example:
//
//     {
//       "C": 78769,
//       "Python": 7769
//     }
//
// GitHub API Docs: http://developer.github.com/v3/repos/#list-languages
func (s *RepositoriesService) ListLanguages(owner string, repository string) (map[string]int, *Response, error) {
	u := fmt.Sprintf("/repos/%v/%v/languages", owner, repository)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	languages := make(map[string]int)
	resp, err := s.client.Do(req, &languages)
	if err != nil {
		return nil, resp, err
	}

	return languages, resp, err
}
<commit_msg>Add MirrorURL field to Repository<commit_after>// Copyright 2013 The go-github 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 github

import "fmt"

// RepositoriesService handles communication with the repository related
// methods of the GitHub API.
//
// GitHub API docs: http://developer.github.com/v3/repos/
type RepositoriesService struct {
	client *Client
}

// Repository represents a GitHub repository.
type Repository struct {
	ID              *int       `json:"id,omitempty"`
	Owner           *User      `json:"owner,omitempty"`
	Name            *string    `json:"name,omitempty"`
	Description     *string    `json:"description,omitempty"`
	Homepage        *string    `json:"homepage,omitempty"`
	DefaultBranch   *string    `json:"default_branch,omitempty"`
	MasterBranch    *string    `json:"master_branch,omitempty"`
	CreatedAt       *Timestamp `json:"created_at,omitempty"`
	PushedAt        *Timestamp `json:"pushed_at,omitempty"`
	UpdatedAt       *Timestamp `json:"updated_at,omitempty"`
	URL             *string    `json:"url,omitempty"`
	HTMLURL         *string    `json:"html_url,omitempty"`
	CloneURL        *string    `json:"clone_url,omitempty"`
	GitURL          *string    `json:"git_url,omitempty"`
	MirrorURL       *string    `json:"mirror_url,omitempty"`
	SSHURL          *string    `json:"ssh_url,omitempty"`
	SVNURL          *string    `json:"svn_url,omitempty"`
	Language        *string    `json:"language,omitempty"`
	Fork            *bool      `json:"fork"`
	ForksCount      *int       `json:"forks_count,omitempty"`
	WatchersCount   *int       `json:"watchers_count,omitempty"`
	OpenIssuesCount *int       `json:"open_issues_count,omitempty"`
	Size            *int       `json:"size,omitempty"`

	// Additional mutable fields when creating and editing a repository
	Private   *bool `json:"private"`
	HasIssues *bool `json:"has_issues"`
	HasWiki   *bool `json:"has_wiki"`
}

func (r Repository) String() string {
	return Stringify(r)
}

// RepositoryListOptions specifies the optional parameters to the
// RepositoriesService.List method.
type RepositoryListOptions struct {
	// Type of repositories to list.  Possible values are: all, owner, public,
	// private, member.  Default is "all".
	Type string `url:"type,omitempty"`

	// How to sort the repository list.  Possible values are: created, updated,
	// pushed, full_name.  Default is "full_name".
	Sort string `url:"sort,omitempty"`

	// Direction in which to sort repositories.  Possible values are: asc, desc.
	// Default is "asc" when sort is "full_name", otherwise default is "desc".
	Direction string `url:"direction,omitempty"`

	ListOptions
}

// List the repositories for a user.  Passing the empty string will list
// repositories for the authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/repos/#list-user-repositories
func (s *RepositoriesService) List(user string, opt *RepositoryListOptions) ([]Repository, *Response, error) {
	var u string
	if user != "" {
		u = fmt.Sprintf("users/%v/repos", user)
	} else {
		u = "user/repos"
	}
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repos := new([]Repository)
	resp, err := s.client.Do(req, repos)
	if err != nil {
		return nil, resp, err
	}

	return *repos, resp, err
}

// RepositoryListByOrgOptions specifies the optional parameters to the
// RepositoriesService.ListByOrg method.
type RepositoryListByOrgOptions struct {
	// Type of repositories to list.  Possible values are: all, public, private,
	// forks, sources, member.  Default is "all".
	Type string `url:"type,omitempty"`

	ListOptions
}

// ListByOrg lists the repositories for an organization.
//
// GitHub API docs: http://developer.github.com/v3/repos/#list-organization-repositories
func (s *RepositoriesService) ListByOrg(org string, opt *RepositoryListByOrgOptions) ([]Repository, *Response, error) {
	u := fmt.Sprintf("orgs/%v/repos", org)
	u, err := addOptions(u, opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repos := new([]Repository)
	resp, err := s.client.Do(req, repos)
	if err != nil {
		return nil, resp, err
	}

	return *repos, resp, err
}

// RepositoryListAllOptions specifies the optional parameters to the
// RepositoriesService.ListAll method.
type RepositoryListAllOptions struct {
	// ID of the last repository seen
	Since int `url:"since,omitempty"`

	ListOptions
}

// ListAll lists all GitHub repositories in the order that they were created.
//
// GitHub API docs: http://developer.github.com/v3/repos/#list-all-public-repositories
func (s *RepositoriesService) ListAll(opt *RepositoryListAllOptions) ([]Repository, *Response, error) {
	u, err := addOptions("repositories", opt)
	if err != nil {
		return nil, nil, err
	}

	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repos := new([]Repository)
	resp, err := s.client.Do(req, repos)
	if err != nil {
		return nil, resp, err
	}

	return *repos, resp, err
}

// Create a new repository.  If an organization is specified, the new
// repository will be created under that org.  If the empty string is
// specified, it will be created for the authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/repos/#create
func (s *RepositoriesService) Create(org string, repo *Repository) (*Repository, *Response, error) {
	var u string
	if org != "" {
		u = fmt.Sprintf("orgs/%v/repos", org)
	} else {
		u = "user/repos"
	}

	req, err := s.client.NewRequest("POST", u, repo)
	if err != nil {
		return nil, nil, err
	}

	r := new(Repository)
	resp, err := s.client.Do(req, r)
	if err != nil {
		return nil, resp, err
	}

	return r, resp, err
}

// Get fetches a repository.
//
// GitHub API docs: http://developer.github.com/v3/repos/#get
func (s *RepositoriesService) Get(owner, repo string) (*Repository, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v", owner, repo)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	repository := new(Repository)
	resp, err := s.client.Do(req, repository)
	if err != nil {
		return nil, resp, err
	}

	return repository, resp, err
}

// Edit updates a repository.
//
// GitHub API docs: http://developer.github.com/v3/repos/#edit
func (s *RepositoriesService) Edit(owner, repo string, repository *Repository) (*Repository, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v", owner, repo)
	req, err := s.client.NewRequest("PATCH", u, repository)
	if err != nil {
		return nil, nil, err
	}

	r := new(Repository)
	resp, err := s.client.Do(req, r)
	if err != nil {
		return nil, resp, err
	}

	return r, resp, err
}

// ListLanguages lists languages for the specified repository. The returned map
// specifies the languages and the number of bytes of code written in that
// language. For example:
//
//     {
//       "C": 78769,
//       "Python": 7769
//     }
//
// GitHub API Docs: http://developer.github.com/v3/repos/#list-languages
func (s *RepositoriesService) ListLanguages(owner string, repository string) (map[string]int, *Response, error) {
	u := fmt.Sprintf("/repos/%v/%v/languages", owner, repository)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

	languages := make(map[string]int)
	resp, err := s.client.Do(req, &languages)
	if err != nil {
		return nil, resp, err
	}

	return languages, resp, err
}
<|endoftext|>


<commit_before>package glog

import (
	"runtime"
)

var (
	messageChan  = make(chan Event, 10)
	backendChans []chan<- Event
)

type data struct {
	d interface{}
}

// Data tags an item to be ignored by glog when logging but to pass it
// to any registered backends. An example would be:
//
//   var req *http.Request
//   ...
//   glog.Error("failed to complete process", glog.Data(req))
func Data(arg interface{}) interface{} {
	return data{arg}
}

// An Event contains a logged event's severity (INFO, WARN, ERROR, FATAL),
// a format string (if Infof, Warnf, Errorf or Fatalf were used) and a slice
// of everything else passed to the log call.
type Event struct {
	Severity   string
	Message    []byte
	Data       []interface{}
	StackTrace []uintptr // inner to outer
}

// NewEvent creates a glog.Event from the logged event's severity,
// format string (if Infof, Warnf, Errorf or Fatalf were called) and
// any other arguments passed to the log call.
// NewEvent separates out any items tagged by Data() and stores them
// in Event.Data.
func NewEvent(s severity, message []byte, dataArgs []interface{}, extraDepth int) Event {
	var stackTrace []uintptr

	if s >= errorLog {
		callers := make([]uintptr, 20)
		written := runtime.Callers(4+extraDepth, callers)
		stackTrace = callers[:written]
	}

	return Event{
		Severity:   severityName[s],
		Message:    message,
		Data:       dataArgs,
		StackTrace: stackTrace,
	}
}

// filterData splits out any items tagged by Data() and returns two slices:
// the first with only argments meant for the log call and the second with
// only arguments meant to passed to any registered backends.
func filterData(args []interface{}) ([]interface{}, []interface{}) {
	var (
		realArgs []interface{}
		dataArgs []interface{}
	)

	for _, arg := range args {
		if argd, ok := arg.(data); ok {
			dataArgs = append(dataArgs, argd.d)
		} else {
			realArgs = append(realArgs, arg)
		}
	}
	return realArgs, dataArgs
}

// RegisterBackend returns a channel on which Event's will be passed
// when they are logged.
//
// The caller is responsible for any necessary synchronization such
// that the call to this function "happens before" any events to be
// logged to this channel or other calls to RegisterBackend().
func RegisterBackend() <-chan Event {
	if len(backendChans) == 0 {
		go broadcastEvents()
	}

	c := make(chan Event, 100)
	backendChans = append(backendChans, c)
	return c
}

// eventForBackends creates and writes a glog.Event to the message channel
// if and only if we have registered backends.
func eventForBackends(e Event) {
	if len(backendChans) > 0 {
		select {
		case messageChan <- e:
		default:
		}
	}
}

func broadcastEvents() {
	for e := range messageChan {
		for _, c := range backendChans {
			select {
			case c <- e:
			default:
			}
		}
	}
}
<commit_msg>glog_backend: Propagate error types passed directly to glog as an explicit ErrorArg<commit_after>package glog

import (
	"runtime"
)

var (
	messageChan  = make(chan Event, 10)
	backendChans []chan<- Event
)

type data struct {
	d interface{}
}

// Data tags an item to be ignored by glog when logging but to pass it
// to any registered backends. An example would be:
//
//   var req *http.Request
//   ...
//   glog.Error("failed to complete process", glog.Data(req))
func Data(arg interface{}) interface{} {
	return data{arg}
}

// An Event contains a logged event's severity (INFO, WARN, ERROR, FATAL),
// a format string (if Infof, Warnf, Errorf or Fatalf were used) and a slice
// of everything else passed to the log call.
type Event struct {
	Severity   string
	Message    []byte
	Data       []interface{}
	StackTrace []uintptr // inner to outer
}

// NewEvent creates a glog.Event from the logged event's severity,
// format string (if Infof, Warnf, Errorf or Fatalf were called) and
// any other arguments passed to the log call.
// NewEvent separates out any items tagged by Data() and stores them
// in Event.Data.
func NewEvent(s severity, message []byte, dataArgs []interface{}, extraDepth int) Event {
	var stackTrace []uintptr

	if s >= errorLog {
		callers := make([]uintptr, 20)
		written := runtime.Callers(4+extraDepth, callers)
		stackTrace = callers[:written]
	}

	return Event{
		Severity:   severityName[s],
		Message:    message,
		Data:       dataArgs,
		StackTrace: stackTrace,
	}
}

// filterData splits out any items tagged by Data() and returns two slices:
// the first with only argments meant for the log call and the second with
// only arguments meant to passed to any registered backends.
func filterData(args []interface{}) ([]interface{}, []interface{}) {
	var (
		realArgs []interface{}
		dataArgs []interface{}
	)

	for _, arg := range args {
		if argd, ok := arg.(data); ok {
			dataArgs = append(dataArgs, argd.d)
		} else {
			realArgs = append(realArgs, arg)
			// PATCH(jwoglom): Propagate an error type passed directly to
			// glog as an implicit glog.ErrorArg
			if errarg, ok := arg.(error); ok {
				dataArgs = append(dataArgs, ErrorArg{errarg})
			}
		}
	}
	return realArgs, dataArgs
}

// RegisterBackend returns a channel on which Event's will be passed
// when they are logged.
//
// The caller is responsible for any necessary synchronization such
// that the call to this function "happens before" any events to be
// logged to this channel or other calls to RegisterBackend().
func RegisterBackend() <-chan Event {
	if len(backendChans) == 0 {
		go broadcastEvents()
	}

	c := make(chan Event, 100)
	backendChans = append(backendChans, c)
	return c
}

// eventForBackends creates and writes a glog.Event to the message channel
// if and only if we have registered backends.
func eventForBackends(e Event) {
	if len(backendChans) > 0 {
		select {
		case messageChan <- e:
		default:
		}
	}
}

func broadcastEvents() {
	for e := range messageChan {
		for _, c := range backendChans {
			select {
			case c <- e:
			default:
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"io/ioutil"
	"github.com/emersion/go-kdeconnect/crypto"
	"github.com/emersion/go-kdeconnect/engine"
	"github.com/emersion/go-kdeconnect/plugin"
	"github.com/emersion/go-kdeconnect/network"
	"github.com/godbus/dbus"
	"github.com/esiqveland/notify"
	"github.com/emersion/go-mpris"
)

func getPrivateKey() (priv *crypto.PrivateKey, err error) {
	configHomeDir := os.Getenv("XDG_CONFIG_HOME")
	if configHomeDir == "" {
		homeDir := os.Getenv("HOME")
		if homeDir == "" {
			return
		}
		configHomeDir = homeDir+"/.config"
	}

	configDir := configHomeDir+"/gnomeconnect"
	err = os.MkdirAll(configDir, 0755)
	if err != nil {
		return
	}

	privateKeyFile := configDir+"/private.pem"
	raw, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		priv, err = crypto.GeneratePrivateKey()
		if err != nil {
			return
		}

		raw, err = priv.Marshal()
		if err != nil {
			return
		}

		err = ioutil.WriteFile(privateKeyFile, raw, 0644)
		return
	}

	priv, err = crypto.UnmarshalPrivateKey(raw)
	return
}

func getDeviceIcon(device *network.Device) string {
	switch device.Type {
	case "phone":
		return "phone"
	case "tablet":
		return "pda" // TODO: find something better
	case "desktop":
		return "computer"
	default:
		return ""
	}
}

func newNotification() notify.Notification {
	return notify.Notification{
		AppName: "GNOMEConnect",
	}
}

func main() {
	config := engine.DefaultConfig()

 	priv, err := getPrivateKey()
	if priv == nil {
		log.Fatal("Could not get private key:", err)
	}
	if err != nil {
		log.Println("Warning: error while getting private key:", err)
	}
	config.PrivateKey = priv

	battery := plugin.NewBattery()
	ping := plugin.NewPing()
	notification := plugin.NewNotification()
	mprisPlugin := plugin.NewMpris()

	conn, err := dbus.SessionBus()
	if err != nil {
		panic(err)
	}

	notifier := notify.New(conn)

	go (func() {
		for {
			select {
			case event := <-ping.Incoming:
				log.Println("Ping:", event.Device.Name)

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Ping from "+event.Device.Name
				notifier.SendNotification(n)
			case event := <-battery.Incoming:
				log.Println("Battery:", event.Device.Name, event.BatteryBody)

				if event.ThresholdEvent == plugin.BatteryThresholdEventLow {
					n := newNotification()
					n.AppIcon = "battery-caution"
					n.Summary = event.Device.Name+" has low battery"
					notifier.SendNotification(n)
				}
				// TODO: remove notification when charging
			case event := <-notification.Incoming:
				log.Println("Notification:", event.Device.Name, event.NotificationBody)

				if event.IsCancel {
					// TODO: dismiss notification
					break
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Notification from "+event.AppName+" on "+event.Device.Name
				n.Body = event.Ticker
				notifier.SendNotification(n)

				// TODO: wait for notification dismiss and send message to remote
			case event := <-mprisPlugin.Incoming:
				log.Println("Mpris:", event.Device.Name, event.MprisBody)

				if event.RequestPlayerList {
					names, err := mpris.List(conn)
					if err != nil {
						log.Println("Warning: cannot list available MPRIS players", err)
						break
					}

					mprisPlugin.SendPlayerList(event.Device, names)
				}

				if event.Player != "" {
					player := mpris.New(conn, event.Player)

					event.RequestNowPlaying = true
					switch event.Action {
					case "Next":
						player.Next()
					case "Previous":
						player.Previous()
					case "Pause":
						player.Pause()
					case "PlayPause":
						player.PlayPause()
					case "Stop":
						player.Stop()
					case "Play":
						player.Play()
					default:
						event.RequestNowPlaying = false
					}

					if event.SetVolume != 0 {
						player.SetVolume(float64(event.SetVolume) / 100)
						event.RequestVolume = true
					}

					if event.RequestNowPlaying || event.RequestVolume {
						reply := &plugin.MprisBody{}
						if event.RequestNowPlaying {
							metadata := player.GetMetadata()
							reply.NowPlaying = metadata["xesam:title"].String()
							reply.IsPlaying = (player.GetPlaybackStatus() == "Playing")
							reply.Length = float64(metadata["mpris:length"].Value().(int64)) / 1000
							reply.Pos = float64(player.GetPosition()) / 1000
						}
						if event.RequestVolume {
							reply.Volume = int(player.GetVolume() * 100)
						}
						event.Device.Send(plugin.MprisType, reply)
					}
				}
			}
		}
	})()

	hdlr := plugin.NewHandler()
	hdlr.Register(battery)
	hdlr.Register(ping)
	hdlr.Register(notification)
	hdlr.Register(mprisPlugin)

	e := engine.New(hdlr, config)

	go (func() {
		devices := map[string]*network.Device{}
		notifications := map[string]int{}

		defer (func() {
			// Close all notifications
			for _, id := range notifications {
				notifier.CloseNotification(id)
			}
		})()

		for {
			select {
			case device := <-e.Joins:
				if device.Id == "" {
					continue
				}

				devices[device.Id] = device

				n := newNotification()
				n.AppIcon = getDeviceIcon(device)
				n.Summary = device.Name
				n.Body = "Device connected"
				n.Hints = map[string]dbus.Variant{
					"resident": dbus.MakeVariant(true),
					"category": dbus.MakeVariant("device.added"),
				}
				id, _ := notifier.SendNotification(n)

				notifications[device.Id] = int(id)
			case device := <-e.Leaves:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
				if _, ok := devices[device.Id]; ok {
					delete(devices, device.Id)
				}
			}
		}
	})()

	e.Listen()
}
<commit_msg>Adds actions to notifications<commit_after>package main

import (
	"log"
	"os"
	"io/ioutil"
	"github.com/emersion/go-kdeconnect/crypto"
	"github.com/emersion/go-kdeconnect/engine"
	"github.com/emersion/go-kdeconnect/plugin"
	"github.com/emersion/go-kdeconnect/network"
	"github.com/godbus/dbus"
	"github.com/esiqveland/notify"
	"github.com/emersion/go-mpris"
)

func getPrivateKey() (priv *crypto.PrivateKey, err error) {
	configHomeDir := os.Getenv("XDG_CONFIG_HOME")
	if configHomeDir == "" {
		homeDir := os.Getenv("HOME")
		if homeDir == "" {
			return
		}
		configHomeDir = homeDir+"/.config"
	}

	configDir := configHomeDir+"/gnomeconnect"
	err = os.MkdirAll(configDir, 0755)
	if err != nil {
		return
	}

	privateKeyFile := configDir+"/private.pem"
	raw, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		priv, err = crypto.GeneratePrivateKey()
		if err != nil {
			return
		}

		raw, err = priv.Marshal()
		if err != nil {
			return
		}

		err = ioutil.WriteFile(privateKeyFile, raw, 0644)
		return
	}

	priv, err = crypto.UnmarshalPrivateKey(raw)
	return
}

func getDeviceIcon(device *network.Device) string {
	switch device.Type {
	case "phone":
		return "phone"
	case "tablet":
		return "pda" // TODO: find something better
	case "desktop":
		return "computer"
	default:
		return ""
	}
}

func newNotification() notify.Notification {
	return notify.Notification{
		AppName: "GNOMEConnect",
	}
}

func main() {
	config := engine.DefaultConfig()

 	priv, err := getPrivateKey()
	if priv == nil {
		log.Fatal("Could not get private key:", err)
	}
	if err != nil {
		log.Println("Warning: error while getting private key:", err)
	}
	config.PrivateKey = priv

	battery := plugin.NewBattery()
	ping := plugin.NewPing()
	notification := plugin.NewNotification()
	mprisPlugin := plugin.NewMpris()

	conn, err := dbus.SessionBus()
	if err != nil {
		panic(err)
	}

	notifier, err := notify.New(conn)
	if err != nil {
		panic(err)
	}

	go (func() {
		for {
			select {
			case event := <-ping.Incoming:
				log.Println("Ping:", event.Device.Name)

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Ping from "+event.Device.Name
				notifier.SendNotification(n)
			case event := <-battery.Incoming:
				log.Println("Battery:", event.Device.Name, event.BatteryBody)

				if event.ThresholdEvent == plugin.BatteryThresholdEventLow {
					n := newNotification()
					n.AppIcon = "battery-caution"
					n.Summary = event.Device.Name+" has low battery"
					notifier.SendNotification(n)
				}
				// TODO: remove notification when charging
			case event := <-notification.Incoming:
				log.Println("Notification:", event.Device.Name, event.NotificationBody)

				if event.IsCancel {
					// TODO: dismiss notification
					break
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Notification from "+event.AppName+" on "+event.Device.Name
				n.Body = event.Ticker
				notifier.SendNotification(n)

				// TODO: wait for notification dismiss and send message to remote
			case event := <-mprisPlugin.Incoming:
				log.Println("Mpris:", event.Device.Name, event.MprisBody)

				if event.RequestPlayerList {
					names, err := mpris.List(conn)
					if err != nil {
						log.Println("Warning: cannot list available MPRIS players", err)
						break
					}

					mprisPlugin.SendPlayerList(event.Device, names)
				}

				if event.Player != "" {
					player := mpris.New(conn, event.Player)

					event.RequestNowPlaying = true
					switch event.Action {
					case "Next":
						player.Next()
					case "Previous":
						player.Previous()
					case "Pause":
						player.Pause()
					case "PlayPause":
						player.PlayPause()
					case "Stop":
						player.Stop()
					case "Play":
						player.Play()
					default:
						event.RequestNowPlaying = false
					}

					if event.SetVolume != 0 {
						player.SetVolume(float64(event.SetVolume) / 100)
						event.RequestVolume = true
					}

					if event.RequestNowPlaying || event.RequestVolume {
						reply := &plugin.MprisBody{}
						if event.RequestNowPlaying {
							metadata := player.GetMetadata()
							reply.NowPlaying = metadata["xesam:title"].String()
							reply.IsPlaying = (player.GetPlaybackStatus() == "Playing")
							reply.Length = float64(metadata["mpris:length"].Value().(int64)) / 1000
							reply.Pos = float64(player.GetPosition()) / 1000
						}
						if event.RequestVolume {
							reply.Volume = int(player.GetVolume() * 100)
						}
						event.Device.Send(plugin.MprisType, reply)
					}
				}
			}
		}
	})()

	hdlr := plugin.NewHandler()
	hdlr.Register(battery)
	hdlr.Register(ping)
	hdlr.Register(notification)
	hdlr.Register(mprisPlugin)

	e := engine.New(hdlr, config)

	go (func() {
		devices := map[string]*network.Device{}
		notifications := map[string]int{}

		closed := notifier.NotificationClosed()
		actions := notifier.ActionInvoked()

		defer (func() {
			// Close all notifications
			for _, id := range notifications {
				notifier.CloseNotification(id)
			}
		})()

		getDeviceFromNotification := func (notificationId int) *network.Device {
			for deviceId, id := range notifications {
				if id == notificationId {
					if device, ok := devices[deviceId]; ok {
						return device
					} else {
						return nil
					}
				}
			}
			return nil
		}

		for {
			select {
			case device := <-e.Joins:
				if device.Id == "" {
					continue
				}

				devices[device.Id] = device

				n := newNotification()
				n.AppIcon = getDeviceIcon(device)
				n.Summary = device.Name
				n.Body = "New device available"
				n.Hints = map[string]dbus.Variant{
					"category": dbus.MakeVariant("device"),
				}
				n.Actions = []string{"pair", "Pair device"}
				id, _ := notifier.SendNotification(n)

				notifications[device.Id] = int(id)
			case device := <-e.RequestsPairing:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(device)
				n.Summary = device.Name
				n.Body = "New pair request"
				n.Hints = map[string]dbus.Variant{
					"category": dbus.MakeVariant("device"),
				}
				n.Actions = []string{"pair", "Accept", "unpair", "Reject"}
				id, _ := notifier.SendNotification(n)

				notifications[device.Id] = int(id)
			case device := <-e.Paired:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(device)
				n.Summary = device.Name
				n.Body = "Device connected"
				n.Hints = map[string]dbus.Variant{
					"resident": dbus.MakeVariant(true),
					"category": dbus.MakeVariant("device.added"),
				}
				id, _ := notifier.SendNotification(n)

				notifications[device.Id] = int(id)
			case device := <-e.Unpaired:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
			case device := <-e.Leaves:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
				if _, ok := devices[device.Id]; ok {
					delete(devices, device.Id)
				}
			case signal := <-actions:
				device := getDeviceFromNotification(int(signal.Id))
				if device == nil {
					continue
				}

				log.Println(device.Name, signal.ActionKey)

				switch signal.ActionKey {
				case "pair":
					e.PairDevice(device)
				case "unpair":
					e.UnpairDevice(device)
				}
			case signal := <-closed:
				device := getDeviceFromNotification(int(signal.Id))
				if device == nil {
					continue
				}

				log.Println(device.Name, signal.Reason)

				switch signal.Reason {
				case notify.ReasonDismissedByUser:
					e.UnpairDevice(device)
				}
			}
		}
	})()

	e.Listen()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

func eval(r io.Reader, i io.Reader, w io.Writer, e io.Writer) {
	prog, err := ioutil.ReadAll(r) // .bf "file" read into a byte array
	if err != nil {
		fmt.Fprintf(e, "%v\n", err)
		os.Exit(1)
	}

	input := bufio.NewReader(i) // buffered reader for `,` requests

	var (
		fpos uint   = 0                  // file position
		dpos uint   = 0                  // data position
		dpth uint   = 1                  // scope depth - for `[` and `]`
		size uint   = 30000              // size of data card
		data []byte = make([]byte, size) // data card with `size` items
	)

	for fpos < uint(len(prog)) {
		switch prog[fpos] {
		case '+': // increment at current position
			data[dpos]++
		case '-': // decrement at current position
			data[dpos]--
		case '>': // move to next position
			if dpos == size-1 {
				dpos = 0
			} else {
				dpos++
			}
		case '<': // move to previous position
			if dpos == 0 {
				dpos = size - 1
			} else {
				dpos--
			}
		case '.': // output value of current position
			fmt.Fprintf(w, "%c", data[dpos])
		case ',': // read value into current position
			if data[dpos], err = input.ReadByte(); err != nil {
				os.Exit(0)
			}
		case '[': // if current position is false, skip to ]
			if data[dpos] == 0 {
				for { // skip forward until as same scope depth
					fpos++
					if prog[fpos] == '[' {
						dpth++
					} else if prog[fpos] == ']' {
						dpth--
					}
					if dpth == 0 {
						break
					}
				}
				dpth = 1 // reset scope depth
			}
		case ']': // if at current position true, return to [
			if data[dpos] != 0 {
				for { // move back until at same scope depth
					fpos--
					if prog[fpos] == ']' {
						dpth++
					} else if prog[fpos] == '[' {
						dpth--
					}
					if dpth == 0 {
						break
					}
				}
			}
			dpth = 1 // reset scope depth
		}
		fpos++
	}
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s [file.bf]\n", os.Args[0])
		os.Exit(1)
	}
	r, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	eval(r, os.Stdin, os.Stdout, os.Stderr)
}
<commit_msg>Improved go interpreter<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

func eval(r io.Reader, i io.Reader, w io.Writer) error {
	prog, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}
	input := bufio.NewReader(i) // buffered reader for `,` requests

	var (
		fpos uint   = 0                  // file position
		dpos uint   = 0                  // data position
		dpth uint   = 1                  // scope depth - for `[` and `]`
		size uint   = 30000              // size of data card
		plen uint   = uint(len(prog))    // programme length
		data []byte = make([]byte, size) // data card with `size` items
	)

	for fpos < plen {
		switch prog[fpos] {
		case '+': // increment at current position
			data[dpos]++
		case '-': // decrement at current position
			data[dpos]--
		case '>': // move to next position
			if dpos == size-1 {
				dpos = 0
			} else {
				dpos++
			}
		case '<': // move to previous position
			if dpos == 0 {
				dpos = size - 1
			} else {
				dpos--
			}
		case '.': // output value of current position
			fmt.Fprintf(w, "%c", data[dpos])
		case ',': // read value into current position
			if data[dpos], err = input.ReadByte(); err != nil {
				os.Exit(0)
			}
		case '[': // if current position is false, skip to ]
			if data[dpos] == 0 {
				for { // skip forward until as same scope depth
					fpos++
					if prog[fpos] == '[' {
						dpth++
					} else if prog[fpos] == ']' {
						dpth--
					}
					if dpth == 0 {
						break
					}
				}
				dpth = 1 // reset scope depth
			}
		case ']': // if at current position true, return to [
			if data[dpos] != 0 {
				for { // move back until at same scope depth
					fpos--
					if prog[fpos] == ']' {
						dpth++
					} else if prog[fpos] == '[' {
						dpth--
					}
					if dpth == 0 {
						break
					}
				}
			}
			dpth = 1 // reset scope depth
		}
		fpos++
	}
	return nil
}

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage: %s [file.bf]\n", os.Args[0])
		os.Exit(3)
	}

	r, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(2)
	}

	err = eval(r, os.Stdin, os.Stdout)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package gobreak

import (
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestDo(t *testing.T) {
	err := Do("test", func() error {
		return nil
	}, nil)
	assert.Nil(t, err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, nil)
	assert.Equal(t, errors.New("failed"), err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, func(error) error {
		return nil
	})
	assert.Nil(t, err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, func(error) error {
		return errors.New("fallback")
	})
	assert.Equal(t, errors.New("fallback"), err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, nil)
	assert.Equal(t, errors.New("circuit breaker 'test' is open"), err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, nil)
	assert.Equal(t, errors.New("circuit breaker 'test' is open"), err)
}

func TestDoDelay(t *testing.T) {
	err := Do("delay", func() error {
		time.Sleep(1 * time.Second)
		return nil
	}, nil)
	assert.Nil(t, err)
}

func BenchmarkNormal(b *testing.B) {
	for i := 0; i < b.N; i++ {
		func() error {
			return nil
		}()
	}
}

func BenchmarkDo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Do("test", func() error {
			return nil
		}, nil)
	}
}

func BenchmarkDoFail(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Do("test", func() error {
			return errors.New("fail")
		}, nil)
	}
}
<commit_msg>fix test case<commit_after>package gobreak

import (
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestDo(t *testing.T) {
	err := Do("test", func() error {
		return nil
	}, nil)
	assert.Nil(t, err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, nil)
	assert.Equal(t, errors.New("failed"), err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, func(error) error {
		return nil
	})
	assert.Nil(t, err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, func(error) error {
		return errors.New("fallback")
	})
	assert.Equal(t, errors.New("fallback"), err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, nil)
	assert.Equal(t, errors.New("circuit breaker is open"), err)

	err = Do("test", func() error {
		return errors.New("failed")
	}, nil)
	assert.Equal(t, errors.New("circuit breaker is open"), err)
}

func TestDoDelay(t *testing.T) {
	err := Do("delay", func() error {
		time.Sleep(1 * time.Second)
		return nil
	}, nil)
	assert.Nil(t, err)
}

func BenchmarkNormal(b *testing.B) {
	for i := 0; i < b.N; i++ {
		func() error {
			return nil
		}()
	}
}

func BenchmarkDo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Do("test", func() error {
			return nil
		}, nil)
	}
}

func BenchmarkDoFail(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Do("test", func() error {
			return errors.New("fail")
		}, nil)
	}
}
<|endoftext|>


<commit_before>// Package rating defines types and methods for setting/getting ratings for paths and
// persisting this data.
package rating

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"sync"

	"tchaik.com/index"
)

// Value is a type which represents a rating value.
type Value uint

// None is the Value used to mark a path as having no rating.
const None Value = 0

// IsValid returns true if the Value is valid.
func (v Value) IsValid() bool {
	return 0 <= v && v <= 5
}

// Store is an interface which defines methods necessary for setting and getting ratings for
// index paths.
type Store interface {
	// Set the rating for the path.
	Set(index.Path, Value) error
	// Get the rating for the path.
	Get(index.Path) Value
}

// NewStore creates a basic implementation of a ratings store, using the given path as the
// source of data. Note: we do not enforce any locking on the underlying file, which is read
// once to initialise the store, and then overwritten after each call to Set.
func NewStore(path string) (Store, error) {
	f, err := os.Open(path)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		f, err = os.Create(path)
		if err != nil {
			return nil, err
		}
	}
	defer f.Close()

	m := make(map[string]Value)
	dec := json.NewDecoder(f)
	err = dec.Decode(&m)
	if err != nil && err != io.EOF {
		return nil, err
	}

	return &basicStore{
		m:    m,
		path: path,
	}, nil
}

type basicStore struct {
	sync.RWMutex

	m    map[string]Value
	path string
}

func (s *basicStore) persist() error {
	f, err := os.Create(s.path)
	if err != nil {
		return err
	}
	defer f.Close()

	b, err := json.Marshal(s.m)
	if err != nil {
		return err
	}

	_, err = f.Write(b)
	return err
}

// Set implements Store.
func (s *basicStore) Set(p index.Path, v Value) error {
	s.Lock()
	defer s.Unlock()

	s.m[fmt.Sprintf("%v", p)] = v
	return s.persist()
}

// Get implements Store.
func (s *basicStore) Get(p index.Path) Value {
	s.RLock()
	defer s.RUnlock()

	return s.m[fmt.Sprintf("%v", p)]
}
<commit_msg>Use PersistStore in index/rating.<commit_after>// Package rating defines types and methods for setting/getting ratings for paths and
// persisting this data.
package rating

import (
	"fmt"
	"sync"

	"tchaik.com/index"
)

// Value is a type which represents a rating value.
type Value uint

// None is the Value used to mark a path as having no rating.
const None Value = 0

// IsValid returns true if the Value is valid.
func (v Value) IsValid() bool {
	return 0 <= v && v <= 5
}

// Store is an interface which defines methods necessary for setting and getting ratings for
// index paths.
type Store interface {
	// Set the rating for the path.
	Set(index.Path, Value) error
	// Get the rating for the path.
	Get(index.Path) Value
}

// NewStore creates a basic implementation of a ratings store, using the given path as the
// source of data. Note: we do not enforce any locking on the underlying file, which is read
// once to initialise the store, and then overwritten after each call to Set.
func NewStore(path string) (Store, error) {
	m := make(map[string]Value)
	s, err := index.NewPersistStore(path, &m)
	if err != nil {
		return nil, err
	}

	return &basicStore{
		m:     m,
		store: s,
	}, nil
}

type basicStore struct {
	sync.RWMutex

	m     map[string]Value
	store index.PersistStore
}

// Set implements Store.
func (s *basicStore) Set(p index.Path, v Value) error {
	s.Lock()
	defer s.Unlock()

	s.m[fmt.Sprintf("%v", p)] = v
	return s.store.Persist(&s.m)
}

// Get implements Store.
func (s *basicStore) Get(p index.Path) Value {
	s.RLock()
	defer s.RUnlock()

	return s.m[fmt.Sprintf("%v", p)]
}
<|endoftext|>