<commit_before>package boardgame

import (
	"errors"
)

//GameDelegate is the place that various parts of the game lifecycle can be
//modified to support this particular game.
type GameDelegate interface {

	//Name is a string that defines the type of game this is. The name should
	//be unique and compact. Good examples are "tictactoe", "blackjack". Once
	//configured, names should never change over the lifetime of the gametype,
	//since it will be persisted in storage. Subclasses should override this.
	Name() string

	//DisplayName is a string that defines the type of game this is in a way
	//appropriate for humans. The name should be unique but human readable. It
	//is purely for human consumption, and may change over time with no
	//adverse effects. Good examples are "Tic Tac Toe", "Blackjack".
	//Subclasses should override this.
	DisplayName() string

	//DistributeComponentToStarterStack is called during set up to establish
	//the Deck/Stack invariant that every component in the chest is placed in
	//precisely one Stack. Game will call this on each component in the Chest
	//in order. This is where the logic goes to make sure each Component goes
	//into its correct starter stack. You must return a non-nil Stack for each
	//call, after which the given Component will be inserted into
	//NextSlotIndex of that stack. If that is not the ordering you desire, you
	//can fix it up in FinishSetUp by using SwapComponents. If any errors are
	//returned, any nil Stacks are returned, or any returned stacks don't have
	//space for another component, game.SetUp will fail. State and Component
	//are only provided for reference; do not modify them.
	DistributeComponentToStarterStack(state State, c *Component) (Stack, error)

	//BeginSetup is a chance to modify the initial state object *before* the
	//components are distributed to it. This is a good place to configure
	//state that will be necessary for you to make the right decisions in
	//DistributeComponentToStarterStack.
	BeginSetUp(state MutableState)

	//FinishSetUp is called during game.SetUp, *after* components have been
	//distributed to their StarterStack. This is the last chance to modify the
	//state before the game's initial state is considered final. For example,
	//if you have a card game this is where you'd make sure the starter draw
	//stacks are shuffled.
	FinishSetUp(state MutableState)

	//CheckGameFinished should return true if the game is finished, and who
	//the winners are. Called after every move is applied.
	CheckGameFinished(state State) (finished bool, winners []int)

	//ProposeFixUpMove is called after a move has been applied. It may return
	//a FixUp move, which will be applied before any other moves are applied.
	//If it returns nil, we may take the next move off of the queue. FixUp
	//moves are useful for things like shuffling a discard deck back into a
	//draw deck, or other moves that are necessary to get the GameState back
	//into reasonable shape.
	ProposeFixUpMove(state State) Move

	//DefaultNumPlayers returns the number of users that this game defaults to.
	//For example, for tictactoe, it will be 2. If 0 is provided to
	//game.SetUp(), we wil use this value insteadp.
	DefaultNumPlayers() int

	//LegalNumPlayers will be consulted when a new game is created. It should
	//return true if the given number of players is legal, and false
	//otherwise. If this returns false, the game's SetUp will fail. Game.SetUp
	//will automatically reject a numPlayers that does not result in at least
	//one player existing.
	LegalNumPlayers(numPlayers int) bool

	//EmptyGameState and EmptyPlayerState are called to get an instantiation
	//of the concrete game/player structs that your package defines. This is
	//used both to create the initial state, but also to inflate states from
	//the database. These methods should always return the underlying same
	//type of struct when called. This means that if different players have
	//very different roles in a game, there might be many properties that are
	//not in use for any given player. The simple properties (ints, bools,
	//strings) should all be their zero-value. Importantly, all Stacks should
	//be non- nil, because an initialized struct contains information about
	//things like MaxSize, Size, and a reference to the deck they are
	//affiliated with. Game methods that use these will fail if the State
	//objects return have uninitialized stacks. Since these two methods are
	//always required and always specific to each game type,
	//DefaultGameDelegate does not implement them, as an extra reminder that
	//you must implement them yourself.
	EmptyGameState() MutableGameState
	//EmptyPlayerState is similar to EmptyGameState, but playerIndex is the
	//value that this PlayerState must return when its PlayerIndex() is
	//called.
	EmptyPlayerState(playerIndex int) MutablePlayerState

	//EmptyDynamicComponentValues returns an empty DynamicComponentValues for
	//the given deck. If nil is returned, then the components in that deck
	//don't have any dynamic component state. This method must always return
	//the same underlying type of struct for the same deck.
	EmptyDynamicComponentValues(deck *Deck) ComponentValues

	//EmptyComputed{Global,Player}PropertyCollection should return a struct
	//that implements PropertyReadSetter. Computed properties will be stored
	//in the objects that are returned. This allows users of the framework to
	//do a single cast of the underlying object and then access the properties
	//in a type-checked way after that. If you return nil, we will use a
	//generic, flexible PropertyReadSetter instead.
	EmptyComputedGlobalPropertyCollection() ComputedPropertyCollection
	EmptyComputedPlayerPropertyCollection() ComputedPropertyCollection

	//StateSanitizationPolicy returns the policy for sanitizing states in this
	//game. The policy should not change over time. See StatePolicy for more
	//on how sanitization policies are calculated and applied.
	StateSanitizationPolicy() *StatePolicy

	//ComputedPropertiesConfig returns a pointer to the config for how
	//computed properties for this game should be constructed.
	ComputedPropertiesConfig() *ComputedPropertiesConfig

	//Diagram should return a basic debug rendering of state in multi-line
	//ascii art. Useful for debugging. State.Diagram() will reach out to this
	//method.
	Diagram(s State) string

	//SetManager configures which manager this delegate is in use with. A
	//given delegate can only be used by a single manager at a time.
	SetManager(manager *GameManager)

	//Manager returns the Manager that was set on this delegate.
	Manager() *GameManager
}

//DefaultGameDelegate is a struct that implements stubs for all of
//GameDelegate's methods. This makes it easy to override just one or two
//methods by creating your own struct that anonymously embeds this one. You
//almost certainly want to override StartingState.
type DefaultGameDelegate struct {
	manager *GameManager
}

func (d *DefaultGameDelegate) Diagram(state State) string {
	return "This should be overriden to render a reasonable state here"
}

func (d *DefaultGameDelegate) Name() string {
	return "default"
}

func (d *DefaultGameDelegate) DisplayName() string {
	return "Default Game"
}

func (d *DefaultGameDelegate) Manager() *GameManager {
	return d.manager
}

func (d *DefaultGameDelegate) SetManager(manager *GameManager) {
	d.manager = manager
}

func (d *DefaultGameDelegate) EmptyDynamicComponentValues(deck *Deck) ComponentValues {
	return nil
}

func (d *DefaultGameDelegate) EmptyComputedGlobalPropertyCollection() ComputedPropertyCollection {
	return nil
}

func (d *DefaultGameDelegate) EmptyComputedPlayerPropertyCollection() ComputedPropertyCollection {
	return nil
}

//The Default ProposeFixUpMove runs through all moves in FixUpMoves, in order,
//and returns the first one that is legal at the current state. In many cases,
//this behavior should be suficient and need not be overwritten. Be extra sure
//that your FixUpMoves have a conservative Legal function, otherwise you could
//get a panic from applying too many FixUp moves.
func (d *DefaultGameDelegate) ProposeFixUpMove(state State) Move {
	for _, move := range d.Manager().FixUpMoves() {
		move.DefaultsForState(state)
		if err := move.Legal(state); err == nil {
			//Found it!
			return move
		}
	}
	//No moves apply now.
	return nil
}

func (d *DefaultGameDelegate) DistributeComponentToStarterStack(state State, c *Component) (Stack, error) {
	//The stub returns an error, because if this is called that means there
	//was a component in the deck. And if we didn't store it in a stack, then
	//we are in violation of the invariant.
	return nil, errors.New("DistributeComponentToStarterStack was called, but the component was not stored in a stack")
}

func (d *DefaultGameDelegate) StateSanitizationPolicy() *StatePolicy {
	return nil
}

func (d *DefaultGameDelegate) ComputedPropertiesConfig() *ComputedPropertiesConfig {
	return nil
}

func (d *DefaultGameDelegate) BeginSetUp(state MutableState) {
	//Don't need to do anything by default
}

func (d *DefaultGameDelegate) FinishSetUp(state MutableState) {
	//Don't need to do anything by default
}

func (d *DefaultGameDelegate) CheckGameFinished(state State) (finished bool, winners []int) {
	return false, nil
}

func (d *DefaultGameDelegate) DefaultNumPlayers() int {
	return 2
}

func (d *DefaultGameDelegate) LegalNumPlayers(numPlayers int) bool {
	if numPlayers > 0 && numPlayers <= 10 {
		return true
	}
	return false
}
<commit_msg>EmptyDynamicComponentValues takes a string, no longer a pointer to deck.<commit_after>package boardgame

import (
	"errors"
)

//GameDelegate is the place that various parts of the game lifecycle can be
//modified to support this particular game.
type GameDelegate interface {

	//Name is a string that defines the type of game this is. The name should
	//be unique and compact. Good examples are "tictactoe", "blackjack". Once
	//configured, names should never change over the lifetime of the gametype,
	//since it will be persisted in storage. Subclasses should override this.
	Name() string

	//DisplayName is a string that defines the type of game this is in a way
	//appropriate for humans. The name should be unique but human readable. It
	//is purely for human consumption, and may change over time with no
	//adverse effects. Good examples are "Tic Tac Toe", "Blackjack".
	//Subclasses should override this.
	DisplayName() string

	//DistributeComponentToStarterStack is called during set up to establish
	//the Deck/Stack invariant that every component in the chest is placed in
	//precisely one Stack. Game will call this on each component in the Chest
	//in order. This is where the logic goes to make sure each Component goes
	//into its correct starter stack. You must return a non-nil Stack for each
	//call, after which the given Component will be inserted into
	//NextSlotIndex of that stack. If that is not the ordering you desire, you
	//can fix it up in FinishSetUp by using SwapComponents. If any errors are
	//returned, any nil Stacks are returned, or any returned stacks don't have
	//space for another component, game.SetUp will fail. State and Component
	//are only provided for reference; do not modify them.
	DistributeComponentToStarterStack(state State, c *Component) (Stack, error)

	//BeginSetup is a chance to modify the initial state object *before* the
	//components are distributed to it. This is a good place to configure
	//state that will be necessary for you to make the right decisions in
	//DistributeComponentToStarterStack.
	BeginSetUp(state MutableState)

	//FinishSetUp is called during game.SetUp, *after* components have been
	//distributed to their StarterStack. This is the last chance to modify the
	//state before the game's initial state is considered final. For example,
	//if you have a card game this is where you'd make sure the starter draw
	//stacks are shuffled.
	FinishSetUp(state MutableState)

	//CheckGameFinished should return true if the game is finished, and who
	//the winners are. Called after every move is applied.
	CheckGameFinished(state State) (finished bool, winners []int)

	//ProposeFixUpMove is called after a move has been applied. It may return
	//a FixUp move, which will be applied before any other moves are applied.
	//If it returns nil, we may take the next move off of the queue. FixUp
	//moves are useful for things like shuffling a discard deck back into a
	//draw deck, or other moves that are necessary to get the GameState back
	//into reasonable shape.
	ProposeFixUpMove(state State) Move

	//DefaultNumPlayers returns the number of users that this game defaults to.
	//For example, for tictactoe, it will be 2. If 0 is provided to
	//game.SetUp(), we wil use this value insteadp.
	DefaultNumPlayers() int

	//LegalNumPlayers will be consulted when a new game is created. It should
	//return true if the given number of players is legal, and false
	//otherwise. If this returns false, the game's SetUp will fail. Game.SetUp
	//will automatically reject a numPlayers that does not result in at least
	//one player existing.
	LegalNumPlayers(numPlayers int) bool

	//EmptyGameState and EmptyPlayerState are called to get an instantiation
	//of the concrete game/player structs that your package defines. This is
	//used both to create the initial state, but also to inflate states from
	//the database. These methods should always return the underlying same
	//type of struct when called. This means that if different players have
	//very different roles in a game, there might be many properties that are
	//not in use for any given player. The simple properties (ints, bools,
	//strings) should all be their zero-value. Importantly, all Stacks should
	//be non- nil, because an initialized struct contains information about
	//things like MaxSize, Size, and a reference to the deck they are
	//affiliated with. Game methods that use these will fail if the State
	//objects return have uninitialized stacks. Since these two methods are
	//always required and always specific to each game type,
	//DefaultGameDelegate does not implement them, as an extra reminder that
	//you must implement them yourself.
	EmptyGameState() MutableGameState
	//EmptyPlayerState is similar to EmptyGameState, but playerIndex is the
	//value that this PlayerState must return when its PlayerIndex() is
	//called.
	EmptyPlayerState(playerIndex int) MutablePlayerState

	//EmptyDynamicComponentValues returns an empty DynamicComponentValues for
	//the given deck. If nil is returned, then the components in that deck
	//don't have any dynamic component state. This method must always return
	//the same underlying type of struct for the same deck.
	EmptyDynamicComponentValues(deckName string) ComponentValues

	//EmptyComputed{Global,Player}PropertyCollection should return a struct
	//that implements PropertyReadSetter. Computed properties will be stored
	//in the objects that are returned. This allows users of the framework to
	//do a single cast of the underlying object and then access the properties
	//in a type-checked way after that. If you return nil, we will use a
	//generic, flexible PropertyReadSetter instead.
	EmptyComputedGlobalPropertyCollection() ComputedPropertyCollection
	EmptyComputedPlayerPropertyCollection() ComputedPropertyCollection

	//StateSanitizationPolicy returns the policy for sanitizing states in this
	//game. The policy should not change over time. See StatePolicy for more
	//on how sanitization policies are calculated and applied.
	StateSanitizationPolicy() *StatePolicy

	//ComputedPropertiesConfig returns a pointer to the config for how
	//computed properties for this game should be constructed.
	ComputedPropertiesConfig() *ComputedPropertiesConfig

	//Diagram should return a basic debug rendering of state in multi-line
	//ascii art. Useful for debugging. State.Diagram() will reach out to this
	//method.
	Diagram(s State) string

	//SetManager configures which manager this delegate is in use with. A
	//given delegate can only be used by a single manager at a time.
	SetManager(manager *GameManager)

	//Manager returns the Manager that was set on this delegate.
	Manager() *GameManager
}

//DefaultGameDelegate is a struct that implements stubs for all of
//GameDelegate's methods. This makes it easy to override just one or two
//methods by creating your own struct that anonymously embeds this one. You
//almost certainly want to override StartingState.
type DefaultGameDelegate struct {
	manager *GameManager
}

func (d *DefaultGameDelegate) Diagram(state State) string {
	return "This should be overriden to render a reasonable state here"
}

func (d *DefaultGameDelegate) Name() string {
	return "default"
}

func (d *DefaultGameDelegate) DisplayName() string {
	return "Default Game"
}

func (d *DefaultGameDelegate) Manager() *GameManager {
	return d.manager
}

func (d *DefaultGameDelegate) SetManager(manager *GameManager) {
	d.manager = manager
}

func (d *DefaultGameDelegate) EmptyDynamicComponentValues(deckName string) ComponentValues {
	return nil
}

func (d *DefaultGameDelegate) EmptyComputedGlobalPropertyCollection() ComputedPropertyCollection {
	return nil
}

func (d *DefaultGameDelegate) EmptyComputedPlayerPropertyCollection() ComputedPropertyCollection {
	return nil
}

//The Default ProposeFixUpMove runs through all moves in FixUpMoves, in order,
//and returns the first one that is legal at the current state. In many cases,
//this behavior should be suficient and need not be overwritten. Be extra sure
//that your FixUpMoves have a conservative Legal function, otherwise you could
//get a panic from applying too many FixUp moves.
func (d *DefaultGameDelegate) ProposeFixUpMove(state State) Move {
	for _, move := range d.Manager().FixUpMoves() {
		move.DefaultsForState(state)
		if err := move.Legal(state); err == nil {
			//Found it!
			return move
		}
	}
	//No moves apply now.
	return nil
}

func (d *DefaultGameDelegate) DistributeComponentToStarterStack(state State, c *Component) (Stack, error) {
	//The stub returns an error, because if this is called that means there
	//was a component in the deck. And if we didn't store it in a stack, then
	//we are in violation of the invariant.
	return nil, errors.New("DistributeComponentToStarterStack was called, but the component was not stored in a stack")
}

func (d *DefaultGameDelegate) StateSanitizationPolicy() *StatePolicy {
	return nil
}

func (d *DefaultGameDelegate) ComputedPropertiesConfig() *ComputedPropertiesConfig {
	return nil
}

func (d *DefaultGameDelegate) BeginSetUp(state MutableState) {
	//Don't need to do anything by default
}

func (d *DefaultGameDelegate) FinishSetUp(state MutableState) {
	//Don't need to do anything by default
}

func (d *DefaultGameDelegate) CheckGameFinished(state State) (finished bool, winners []int) {
	return false, nil
}

func (d *DefaultGameDelegate) DefaultNumPlayers() int {
	return 2
}

func (d *DefaultGameDelegate) LegalNumPlayers(numPlayers int) bool {
	if numPlayers > 0 && numPlayers <= 10 {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"bufio"
	"image"
	"image/color"
	"image/png"
	"log"
	"math"
	"os"

	"code.google.com/p/draw2d/draw2d"

	"github.com/paulsmith/gogeos/geos"
)

const (
	WIDTH  = 512
	HEIGHT = 512
	PAD    = 20
)

func main() {
	ex1()
	ex2()
	ex3()
	ex4()
	ex5()
	ex6()
	ex7()
	ex8()
	ex9()
}

func ex1() {
	example("example1.png", geos.Must(geos.FromWKT("LINESTRING (0 0, 10 10, 20 20)")))
}

var (
	a = geos.Must(geos.FromWKT("POLYGON ((0 3, 2 3, 3 1, 1 0, 0 1.5, 0 3))"))
	b = geos.Must(geos.FromWKT("POLYGON ((1 2, 1.5 4, 3.5 4, 4.5 2.5, 1 2))"))
)

func ex2() {
	a := geos.Must(geos.FromWKT("LINEARRING (0 3, 2 3, 3 1, 1 0, 0 1.5, 0 3)"))
	b := geos.Must(geos.FromWKT("LINEARRING (1 2, 1.5 4, 3.5 4, 4.5 2.5, 1 2)"))
	example("example2-a-b.png", a, b)
}

func ex3() {
	example("example3-intersection.png", a, b, geos.Must(a.Intersection(b)))
}

func ex4() {
	example("example4-union.png", a, b, geos.Must(a.Union(b)))
}

func ex5() {
	example("example5-difference.png", a, b, geos.Must(a.Difference(b)))
}

func ex6() {
	example("example6-difference.png", a, b, geos.Must(b.Difference(a)))
}

func ex7() {
	example("example7-symdifference.png", geos.Must(a.SymDifference(b)))
}

func ex8() {
	l := geos.Must(geos.FromWKT("LINESTRING (0 2, 6 2, 3 4, 4.5 8, 1 8, 3 0)"))
	b := geos.Must(l.Buffer(0.5))
	example("example8-buffer.png", b, l)
}

func ex9() {
	wkt := []string{
		"LINESTRING (2 10, 3 10, 4 9, 5 8)",
		"LINESTRING (5 8, 6 6, 6 5)",
		"LINESTRING (6 5, 9 6, 10 7, 11 9)",
		"LINESTRING (11 9, 11 10, 10 11)",
		"LINESTRING (6 5, 3 4, 2 3)",
		"LINESTRING (2 3, 1.5 2.5, 1 1, 1 0)",
		"LINESTRING (1 0, 0 0, 0 -1, 1 -2)",
		"LINESTRING (1 -2, 2 -2, 3 -1, 3 0, 1 0)",
		"LINESTRING (6 5, 6 3, 6.5 2.5)",
		"LINESTRING (6.5 2.5, 7.5 2, 8.5 1.5)",
		"LINESTRING (8.5 1.5, 9 0.5, 10 0.5, 11 1.5)",
	}
	var linestrings []*geos.Geometry
	for i := range wkt {
		linestrings = append(linestrings, geos.Must(geos.FromWKT(wkt[i])))
	}
	example("example9-unmerged-linestrings.png", linestrings...)
	merged := geos.Must(geos.Must(geos.NewCollection(geos.MULTILINESTRING, linestrings...)).LineMerge())
	example("example9-merged-linestrings.png", merged)
}

func example(filename string, geoms ...*geos.Geometry) {
	m, ctxt := newContext(WIDTH, HEIGHT)
	ctxt.Clear()
	drawGeoms(m, ctxt, geoms...)
	saveImageToFile(m, filename)
}

func test() {
	g1 := geos.Must(geos.FromWKT("LINESTRING(0 0, 3 3, 3 2, 4 2, 7 5)"))
	g2 := geos.Must(geos.FromWKT("LINESTRING(0 10, 1 0, 0 10, 10 10)"))
	g3 := geos.Must(geos.FromWKT("POINT(5 5)"))
	g4 := geos.Must(g3.Buffer(1))
	g5 := geos.Must(geos.FromWKT("MULTIPOINT(5 6, 6 6, 3 8, 7 8)"))
	example("test.png", g1, g2, g4, g3, g5)
}

var (
	blue    = color.RGBA{0x4C, 0x94, 0xFF, 0xFF}
	orange  = color.RGBA{0xFF, 0xB8, 0x4C, 0xFF}
	magenta = color.RGBA{0xFF, 0x4C, 0xEE, 0xFF}
	green   = color.RGBA{0x4C, 0xFF, 0x5E, 0xFF}
	purple  = color.RGBA{0x8B, 0x4C, 0xFF, 0xFF}
	ygreen  = color.RGBA{0xC1, 0xFF, 0x4C, 0xFF}
	red     = color.RGBA{0xFF, 0x4C, 0x67, 0xFF}
	cyan    = color.RGBA{0x4C, 0xFF, 0xE5, 0xFF}
)

var colors = []color.Color{
	blue,
	orange,
	magenta,
	green,
	purple,
	ygreen,
	red,
	cyan,
}

func drawGeoms(m image.Image, ctxt draw2d.GraphicContext, geoms ...*geos.Geometry) {
	// get envelope and calculate scale fn
	var env Envelope
	if len(geoms) > 1 {
		coll, err := geos.NewCollection(geos.GEOMETRYCOLLECTION, geoms...)
		if err != nil {
			log.Fatal(err)
		}
		union, err := coll.UnaryUnion()
		if err != nil {
			log.Fatal(err)
		}
		env = envelope(union)
	} else {
		env = envelope(geoms[0])
	}
	scale := func(x, y float64) (float64, float64) {
		x = env.Px(x)*(WIDTH-2*PAD) + PAD
		y = HEIGHT - (env.Py(y)*(HEIGHT-2*PAD) + PAD)
		return x, y
	}

	var draw func(geoms ...*geos.Geometry)

	draw = func(geoms ...*geos.Geometry) {
		for i, g := range geoms {
			// pick color
			c := colors[i%len(colors)]
			// switch type
			_type, err := g.Type()
			if err != nil {
				log.Fatal(err)
			}
			switch _type {
			case geos.POINT:
				drawPoint(ctxt, g, c, 4.0, scale)
			case geos.LINESTRING, geos.LINEARRING:
				drawLine(ctxt, g, c, 4.0, scale)
			case geos.POLYGON:
				drawPolygon(ctxt, g, c, darken(c), 3.0, scale)
			case geos.MULTIPOINT, geos.MULTILINESTRING, geos.MULTIPOLYGON, geos.GEOMETRYCOLLECTION:
				n, err := g.NGeometry()
				if err != nil {
					log.Fatal(err)
				}
				var subgeoms []*geos.Geometry
				for i := 0; i < n; i++ {
					subgeoms = append(subgeoms, geos.Must(g.Geometry(i)))
				}
				draw(subgeoms...)
			default:
				log.Fatalf("unknown geometry type %v", _type)
			}
		}
	}

	draw(geoms...)
}

func darken(c color.Color) color.Color {
	return color.RGBA{
		R: (c.(color.RGBA).R & 0xfe) >> 1,
		G: (c.(color.RGBA).G & 0xfe) >> 1,
		B: (c.(color.RGBA).B & 0xfe) >> 1,
		A: c.(color.RGBA).A,
	}
}

type Envelope struct {
	Min, Max Point
}

type Point struct {
	X, Y float64
}

func Env(minx, miny, maxx, maxy float64) Envelope {
	return Envelope{Point{minx, miny}, Point{maxx, maxy}}
}

func (e Envelope) Dx() float64 {
	return e.Max.X - e.Min.X
}

func (e Envelope) Dy() float64 {
	return e.Max.Y - e.Min.Y
}

func (e Envelope) Px(x float64) float64 {
	return (x - e.Min.X) / e.Dx()
}

func (e Envelope) Py(y float64) float64 {
	return (y - e.Min.Y) / e.Dy()
}

func envelope(g *geos.Geometry) Envelope {
	env, err := g.Envelope()
	if err != nil {
		log.Fatal(err)
	}
	ring, err := env.ExteriorRing()
	if err != nil {
		log.Fatal(err)
	}
	cs, err := ring.CoordSeq()
	if err != nil {
		log.Fatal(err)
	}
	getX := getOrd(cs, (*geos.CoordSeq).GetX)
	getY := getOrd(cs, (*geos.CoordSeq).GetY)
	return Env(getX(0), getY(0), getX(2), getY(2))
}

func getOrd(cs *geos.CoordSeq, fn func(*geos.CoordSeq, int) (float64, error)) func(int) float64 {
	return func(idx int) float64 {
		ord, err := fn(cs, idx)
		if err != nil {
			log.Fatal(err)
		}
		return ord
	}
}

func drawPoint(ctxt draw2d.GraphicContext, g *geos.Geometry, c color.Color, radius float64, scale func(x, y float64) (float64, float64)) {
	if c != nil {
		ctxt.SetFillColor(c)
	}
	x, err := g.X()
	if err != nil {
		log.Fatal(err)
	}
	y, err := g.Y()
	if err != nil {
		log.Fatal(err)
	}
	x, y = scale(x, y)
	ctxt.MoveTo(x, y)
	ctxt.ArcTo(x, y, radius, radius, 0, 2*math.Pi)
	ctxt.Fill()
}

func drawLine(ctxt draw2d.GraphicContext, g *geos.Geometry, c color.Color, width float64, scale func(x, y float64) (float64, float64)) {
	if c != nil {
		ctxt.SetStrokeColor(c)
	}
	if width != 0.0 {
		ctxt.SetLineWidth(width)
	}
	// XXX: should get a [] of points
	cs, err := g.CoordSeq()
	if err != nil {
		log.Fatal(err)
	}
	lineCoordSeq(ctxt, cs, scale)
	ctxt.Stroke()
}

func lineCoordSeq(ctxt draw2d.GraphicContext, cs *geos.CoordSeq, scale func(x, y float64) (float64, float64)) {
	n := cs.Size()
	if n == 0 {
		return
	}
	// XXX: interface like sql.Scan() and .Error()
	getX := getOrd(cs, (*geos.CoordSeq).GetX)
	getY := getOrd(cs, (*geos.CoordSeq).GetY)
	x, y := getX(0), getY(0)
	ctxt.MoveTo(scale(x, y))
	for i := 1; i < n; i++ {
		x, y = getX(i), getY(i)
		x, y = scale(x, y)
		ctxt.LineTo(x, y)
	}
}

func drawPolygon(ctxt draw2d.GraphicContext, g *geos.Geometry, fillColor color.Color, strokeColor color.Color, width float64, scale func(x, y float64) (float64, float64)) {
	ctxt.SetFillColor(fillColor)
	ctxt.SetStrokeColor(strokeColor)
	ctxt.SetLineWidth(width)
	// exterior ring
	ring := geos.Must(g.ExteriorRing())
	cs, err := ring.CoordSeq()
	if err != nil {
		log.Fatal(err)
	}
	lineCoordSeq(ctxt, cs, scale)
	ctxt.FillStroke()
	// interior rings...
}

func newContext(w, h int) (image.Image, draw2d.GraphicContext) {
	m := image.NewRGBA(image.Rect(0, 0, w, h))
	ctxt := draw2d.NewGraphicContext(m)
	ctxt.SetFillColor(image.White)
	ctxt.SetStrokeColor(image.Black)
	return m, ctxt
}

func saveImageToFile(m image.Image, filename string) {
	f, err := os.Create(filename)
	if err != nil {
		log.Fatal(err)
	}
	w := bufio.NewWriter(f)
	err = png.Encode(w, m)
	if err != nil {
		log.Fatal(err)
	}
	err = w.Flush()
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Add convex hull example<commit_after>// +build ignore

package main

import (
	"bufio"
	"image"
	"image/color"
	"image/png"
	"log"
	"math"
	"os"

	"code.google.com/p/draw2d/draw2d"

	"github.com/paulsmith/gogeos/geos"
)

const (
	WIDTH  = 512
	HEIGHT = 512
	PAD    = 20
)

func main() {
	ex1()
	ex2()
	ex3()
	ex4()
	ex5()
	ex6()
	ex7()
	ex8()
	ex9()
	ex10()
}

func ex1() {
	example("example1.png", geos.Must(geos.FromWKT("LINESTRING (0 0, 10 10, 20 20)")))
}

var (
	a = geos.Must(geos.FromWKT("POLYGON ((0 3, 2 3, 3 1, 1 0, 0 1.5, 0 3))"))
	b = geos.Must(geos.FromWKT("POLYGON ((1 2, 1.5 4, 3.5 4, 4.5 2.5, 1 2))"))
)

func ex2() {
	a := geos.Must(geos.FromWKT("LINEARRING (0 3, 2 3, 3 1, 1 0, 0 1.5, 0 3)"))
	b := geos.Must(geos.FromWKT("LINEARRING (1 2, 1.5 4, 3.5 4, 4.5 2.5, 1 2)"))
	example("example2-a-b.png", a, b)
}

func ex3() {
	example("example3-intersection.png", a, b, geos.Must(a.Intersection(b)))
}

func ex4() {
	example("example4-union.png", a, b, geos.Must(a.Union(b)))
}

func ex5() {
	example("example5-difference.png", a, b, geos.Must(a.Difference(b)))
}

func ex6() {
	example("example6-difference.png", a, b, geos.Must(b.Difference(a)))
}

func ex7() {
	example("example7-symdifference.png", geos.Must(a.SymDifference(b)))
}

func ex8() {
	l := geos.Must(geos.FromWKT("LINESTRING (0 2, 6 2, 3 4, 4.5 8, 1 8, 3 0)"))
	b := geos.Must(l.Buffer(0.5))
	example("example8-buffer.png", b, l)
}

func ex9() {
	wkt := []string{
		"LINESTRING (2 10, 3 10, 4 9, 5 8)",
		"LINESTRING (5 8, 6 6, 6 5)",
		"LINESTRING (6 5, 9 6, 10 7, 11 9)",
		"LINESTRING (11 9, 11 10, 10 11)",
		"LINESTRING (6 5, 3 4, 2 3)",
		"LINESTRING (2 3, 1.5 2.5, 1 1, 1 0)",
		"LINESTRING (1 0, 0 0, 0 -1, 1 -2)",
		"LINESTRING (1 -2, 2 -2, 3 -1, 3 0, 1 0)",
		"LINESTRING (6 5, 6 3, 6.5 2.5)",
		"LINESTRING (6.5 2.5, 7.5 2, 8.5 1.5)",
		"LINESTRING (8.5 1.5, 9 0.5, 10 0.5, 11 1.5)",
	}
	var linestrings []*geos.Geometry
	for i := range wkt {
		linestrings = append(linestrings, geos.Must(geos.FromWKT(wkt[i])))
	}
	example("example9-unmerged-linestrings.png", linestrings...)
	merged := geos.Must(geos.Must(geos.NewCollection(geos.MULTILINESTRING, linestrings...)).LineMerge())
	example("example9-merged-linestrings.png", merged)
}

func ex10() {
	l := geos.Must(geos.FromWKT("LINESTRING (0 2, 6 2, 3 4, 4.5 8, 1 8, 3 0)"))
	b := geos.Must(l.ConvexHull())
	example("example10-convex-hull.png", b, l)
}

func example(filename string, geoms ...*geos.Geometry) {
	m, ctxt := newContext(WIDTH, HEIGHT)
	ctxt.Clear()
	drawGeoms(m, ctxt, geoms...)
	saveImageToFile(m, filename)
}

func test() {
	g1 := geos.Must(geos.FromWKT("LINESTRING(0 0, 3 3, 3 2, 4 2, 7 5)"))
	g2 := geos.Must(geos.FromWKT("LINESTRING(0 10, 1 0, 0 10, 10 10)"))
	g3 := geos.Must(geos.FromWKT("POINT(5 5)"))
	g4 := geos.Must(g3.Buffer(1))
	g5 := geos.Must(geos.FromWKT("MULTIPOINT(5 6, 6 6, 3 8, 7 8)"))
	example("test.png", g1, g2, g4, g3, g5)
}

var (
	blue    = color.RGBA{0x4C, 0x94, 0xFF, 0xFF}
	orange  = color.RGBA{0xFF, 0xB8, 0x4C, 0xFF}
	magenta = color.RGBA{0xFF, 0x4C, 0xEE, 0xFF}
	green   = color.RGBA{0x4C, 0xFF, 0x5E, 0xFF}
	purple  = color.RGBA{0x8B, 0x4C, 0xFF, 0xFF}
	ygreen  = color.RGBA{0xC1, 0xFF, 0x4C, 0xFF}
	red     = color.RGBA{0xFF, 0x4C, 0x67, 0xFF}
	cyan    = color.RGBA{0x4C, 0xFF, 0xE5, 0xFF}
)

var colors = []color.Color{
	blue,
	orange,
	magenta,
	green,
	purple,
	ygreen,
	red,
	cyan,
}

func drawGeoms(m image.Image, ctxt draw2d.GraphicContext, geoms ...*geos.Geometry) {
	// get envelope and calculate scale fn
	var env Envelope
	if len(geoms) > 1 {
		coll, err := geos.NewCollection(geos.GEOMETRYCOLLECTION, geoms...)
		if err != nil {
			log.Fatal(err)
		}
		union, err := coll.UnaryUnion()
		if err != nil {
			log.Fatal(err)
		}
		env = envelope(union)
	} else {
		env = envelope(geoms[0])
	}
	scale := func(x, y float64) (float64, float64) {
		x = env.Px(x)*(WIDTH-2*PAD) + PAD
		y = HEIGHT - (env.Py(y)*(HEIGHT-2*PAD) + PAD)
		return x, y
	}

	var draw func(geoms ...*geos.Geometry)

	draw = func(geoms ...*geos.Geometry) {
		for i, g := range geoms {
			// pick color
			c := colors[i%len(colors)]
			// switch type
			_type, err := g.Type()
			if err != nil {
				log.Fatal(err)
			}
			switch _type {
			case geos.POINT:
				drawPoint(ctxt, g, c, 4.0, scale)
			case geos.LINESTRING, geos.LINEARRING:
				drawLine(ctxt, g, c, 4.0, scale)
			case geos.POLYGON:
				drawPolygon(ctxt, g, c, darken(c), 3.0, scale)
			case geos.MULTIPOINT, geos.MULTILINESTRING, geos.MULTIPOLYGON, geos.GEOMETRYCOLLECTION:
				n, err := g.NGeometry()
				if err != nil {
					log.Fatal(err)
				}
				var subgeoms []*geos.Geometry
				for i := 0; i < n; i++ {
					subgeoms = append(subgeoms, geos.Must(g.Geometry(i)))
				}
				draw(subgeoms...)
			default:
				log.Fatalf("unknown geometry type %v", _type)
			}
		}
	}

	draw(geoms...)
}

func darken(c color.Color) color.Color {
	return color.RGBA{
		R: (c.(color.RGBA).R & 0xfe) >> 1,
		G: (c.(color.RGBA).G & 0xfe) >> 1,
		B: (c.(color.RGBA).B & 0xfe) >> 1,
		A: c.(color.RGBA).A,
	}
}

type Envelope struct {
	Min, Max Point
}

type Point struct {
	X, Y float64
}

func Env(minx, miny, maxx, maxy float64) Envelope {
	return Envelope{Point{minx, miny}, Point{maxx, maxy}}
}

func (e Envelope) Dx() float64 {
	return e.Max.X - e.Min.X
}

func (e Envelope) Dy() float64 {
	return e.Max.Y - e.Min.Y
}

func (e Envelope) Px(x float64) float64 {
	return (x - e.Min.X) / e.Dx()
}

func (e Envelope) Py(y float64) float64 {
	return (y - e.Min.Y) / e.Dy()
}

func envelope(g *geos.Geometry) Envelope {
	env, err := g.Envelope()
	if err != nil {
		log.Fatal(err)
	}
	ring, err := env.ExteriorRing()
	if err != nil {
		log.Fatal(err)
	}
	cs, err := ring.CoordSeq()
	if err != nil {
		log.Fatal(err)
	}
	getX := getOrd(cs, (*geos.CoordSeq).GetX)
	getY := getOrd(cs, (*geos.CoordSeq).GetY)
	return Env(getX(0), getY(0), getX(2), getY(2))
}

func getOrd(cs *geos.CoordSeq, fn func(*geos.CoordSeq, int) (float64, error)) func(int) float64 {
	return func(idx int) float64 {
		ord, err := fn(cs, idx)
		if err != nil {
			log.Fatal(err)
		}
		return ord
	}
}

func drawPoint(ctxt draw2d.GraphicContext, g *geos.Geometry, c color.Color, radius float64, scale func(x, y float64) (float64, float64)) {
	if c != nil {
		ctxt.SetFillColor(c)
	}
	x, err := g.X()
	if err != nil {
		log.Fatal(err)
	}
	y, err := g.Y()
	if err != nil {
		log.Fatal(err)
	}
	x, y = scale(x, y)
	ctxt.MoveTo(x, y)
	ctxt.ArcTo(x, y, radius, radius, 0, 2*math.Pi)
	ctxt.Fill()
}

func drawLine(ctxt draw2d.GraphicContext, g *geos.Geometry, c color.Color, width float64, scale func(x, y float64) (float64, float64)) {
	if c != nil {
		ctxt.SetStrokeColor(c)
	}
	if width != 0.0 {
		ctxt.SetLineWidth(width)
	}
	// XXX: should get a [] of points
	cs, err := g.CoordSeq()
	if err != nil {
		log.Fatal(err)
	}
	lineCoordSeq(ctxt, cs, scale)
	ctxt.Stroke()
}

func lineCoordSeq(ctxt draw2d.GraphicContext, cs *geos.CoordSeq, scale func(x, y float64) (float64, float64)) {
	n := cs.Size()
	if n == 0 {
		return
	}
	// XXX: interface like sql.Scan() and .Error()
	getX := getOrd(cs, (*geos.CoordSeq).GetX)
	getY := getOrd(cs, (*geos.CoordSeq).GetY)
	x, y := getX(0), getY(0)
	ctxt.MoveTo(scale(x, y))
	for i := 1; i < n; i++ {
		x, y = getX(i), getY(i)
		x, y = scale(x, y)
		ctxt.LineTo(x, y)
	}
}

func drawPolygon(ctxt draw2d.GraphicContext, g *geos.Geometry, fillColor color.Color, strokeColor color.Color, width float64, scale func(x, y float64) (float64, float64)) {
	ctxt.SetFillColor(fillColor)
	ctxt.SetStrokeColor(strokeColor)
	ctxt.SetLineWidth(width)
	// exterior ring
	ring := geos.Must(g.ExteriorRing())
	cs, err := ring.CoordSeq()
	if err != nil {
		log.Fatal(err)
	}
	lineCoordSeq(ctxt, cs, scale)
	ctxt.FillStroke()
	// interior rings...
}

func newContext(w, h int) (image.Image, draw2d.GraphicContext) {
	m := image.NewRGBA(image.Rect(0, 0, w, h))
	ctxt := draw2d.NewGraphicContext(m)
	ctxt.SetFillColor(image.White)
	ctxt.SetStrokeColor(image.Black)
	return m, ctxt
}

func saveImageToFile(m image.Image, filename string) {
	f, err := os.Create(filename)
	if err != nil {
		log.Fatal(err)
	}
	w := bufio.NewWriter(f)
	err = png.Encode(w, m)
	if err != nil {
		log.Fatal(err)
	}
	err = w.Flush()
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package ffmpeg

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os/exec"
	"time"
)

type Info struct {
	Format  map[string]interface{}
	Streams []map[string]interface{}
}

// returns res attributes for the raw stream
func (info *Info) Bitrate() (bitrate uint, err error) {
	bit_rate, exist := info.Format["bit_rate"]
	if !exist {
		err = errors.New("no bit_rate key in format")
		return
	}
	_, err = fmt.Sscan(bit_rate.(string), &bitrate)
	return
}

func (info *Info) Duration() (duration time.Duration, err error) {
	di := info.Format["duration"]
	if di == nil {
		err = errors.New("no format duration")
		return
	}
	ds := di.(string)
	if ds == "N/A" {
		err = errors.New("N/A")
		return
	}
	var f float64
	_, err = fmt.Sscan(ds, &f)
	if err != nil {
		return
	}
	duration = time.Duration(f * float64(time.Second))
	return
}

var (
	ffprobePath      string
	outputFormatFlag = "-of"
)

func isExecErrNotFound(err error) bool {
	if err == exec.ErrNotFound {
		return true
	}
	execErr, ok := err.(*exec.Error)
	if !ok {
		return false
	}
	return execErr.Err == exec.ErrNotFound
}

func init() {
	var err error
	ffprobePath, err = exec.LookPath("ffprobe")
	if err == nil {
		outputFormatFlag = "-print_format"
		return
	}
	if !isExecErrNotFound(err) {
		log.Print(err)
	}
	ffprobePath, err = exec.LookPath("avprobe")
	if err == nil {
		return
	}
	if isExecErrNotFound(err) {
		log.Print("ffprobe and avprobe not found in $PATH")
		return
	}
	log.Print(err)
}

var FfprobeUnavailableError = errors.New("ffprobe not available")

func Probe(path string) (info *Info, err error) {
	if ffprobePath == "" {
		err = FfprobeUnavailableError
		return
	}
	cmd := exec.Command(ffprobePath, "-show_format", "-show_streams", outputFormatFlag, "json", path)
	setHideWindow(cmd)
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	r := bufio.NewReader(out)
	info = &Info{}
	defer out.Close()
	defer func() {
		waitErr := cmd.Wait()
		if waitErr != nil {
			err = waitErr
		}
		if err != nil {
			info = nil
		}
	}()
	decoder := json.NewDecoder(r)
	if err := decoder.Decode(info); err != nil {
		return nil, err
	}
	return info, nil
}
<commit_msg>Give the last line on stderr from ffprobe in the ffmpeg.Probe error<commit_after>package ffmpeg

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"os/exec"
	"time"
)

type Info struct {
	Format  map[string]interface{}
	Streams []map[string]interface{}
}

// returns res attributes for the raw stream
func (info *Info) Bitrate() (bitrate uint, err error) {
	bit_rate, exist := info.Format["bit_rate"]
	if !exist {
		err = errors.New("no bit_rate key in format")
		return
	}
	_, err = fmt.Sscan(bit_rate.(string), &bitrate)
	return
}

func (info *Info) Duration() (duration time.Duration, err error) {
	di := info.Format["duration"]
	if di == nil {
		err = errors.New("no format duration")
		return
	}
	ds := di.(string)
	if ds == "N/A" {
		err = errors.New("N/A")
		return
	}
	var f float64
	_, err = fmt.Sscan(ds, &f)
	if err != nil {
		return
	}
	duration = time.Duration(f * float64(time.Second))
	return
}

var (
	ffprobePath      string
	outputFormatFlag = "-of"
)

func isExecErrNotFound(err error) bool {
	if err == exec.ErrNotFound {
		return true
	}
	execErr, ok := err.(*exec.Error)
	if !ok {
		return false
	}
	return execErr.Err == exec.ErrNotFound
}

func init() {
	var err error
	ffprobePath, err = exec.LookPath("ffprobe")
	if err == nil {
		outputFormatFlag = "-print_format"
		return
	}
	if !isExecErrNotFound(err) {
		log.Print(err)
	}
	ffprobePath, err = exec.LookPath("avprobe")
	if err == nil {
		return
	}
	if isExecErrNotFound(err) {
		log.Print("ffprobe and avprobe not found in $PATH")
		return
	}
	log.Print(err)
}

var FfprobeUnavailableError = errors.New("ffprobe not available")

// Sends the last line from r to ch, or returns the error scanning r.
func lastLine(r io.Reader, ch chan<- string) (err error) {
	defer close(ch)
	scanner := bufio.NewScanner(r)
	scanner.Split(bufio.ScanLines)
	var line string
	for scanner.Scan() {
		line = scanner.Text()
	}
	err = scanner.Err()
	if err == nil {
		ch <- line
	}
	return err
}

// Runs ffprobe or avprobe or similar on the given file path.
func Probe(path string) (info *Info, err error) {
	if ffprobePath == "" {
		err = FfprobeUnavailableError
		return
	}
	cmd := exec.Command(ffprobePath, "-loglevel", "error", "-show_format", "-show_streams", outputFormatFlag, "json", path)
	out, err := cmd.StdoutPipe()
	if err != nil {
		return
	}
	errPipe, err := cmd.StderrPipe()
	if err != nil {
		return
	}
	setHideWindow(cmd)
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	defer out.Close()
	lastLineCh := make(chan string, 1)
	go lastLine(errPipe, lastLineCh)
	defer func() {
		waitErr := cmd.Wait()
		if waitErr != nil {
			err = waitErr
			if lastLine, ok := <-lastLineCh; ok {
				err = fmt.Errorf("%s: %s", err, lastLine)
			}
		}
	}()
	decoder := json.NewDecoder(bufio.NewReader(out))
	if err := decoder.Decode(&info); err != nil {
		return nil, err
	}
	return info, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"fmt"

	"github.com/chai2010/gettext-go"
)

func main() {
	gettext.SetLocale("zh_CN")
	gettext.BindTextdomain("hello", "../examples/local", nil)
	gettext.Textdomain("hello")

	fmt.Println(gettext.Gettext("Hello, world!"))
	// Output: 你好, 世界!
}
<commit_msg>Fix import in hello.go<commit_after>// Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

package main

import (
	"fmt"

	"github.com/chai2010/gettext-go/gettext"
)

func main() {
	gettext.SetLocale("zh_CN")
	gettext.BindTextdomain("hello", "../examples/local", nil)
	gettext.Textdomain("hello")

	fmt.Println(gettext.Gettext("Hello, world!"))
	// Output: 你好, 世界!
}
<|endoftext|>


<commit_before>package gitdiff

import (
	"errors"
	"fmt"
	"io"
	"sort"
)

// Conflict indicates an apply failed due to a conflict between the patch and
// the source content.
//
// Users can test if an error was caused by a conflict by using errors.Is with
// an empty Conflict:
//
//     if errors.Is(err, &Conflict{}) {
//	       // handle conflict
//     }
//
type Conflict struct {
	msg string
}

func (c *Conflict) Error() string {
	return "conflict: " + c.msg
}

// Is implements error matching for Conflict. Passing an empty instance of
// Conflict always returns true.
func (c *Conflict) Is(other error) bool {
	if other, ok := other.(*Conflict); ok {
		return other.msg == "" || other.msg == c.msg
	}
	return false
}

// ApplyError wraps an error that occurs during patch application with
// additional location information, if it is available.
type ApplyError struct {
	// Line is the one-indexed line number in the source data
	Line int64
	// Fragment is the one-indexed fragment number in the file
	Fragment int
	// FragmentLine is the one-indexed line number in the fragment
	FragmentLine int

	err error
}

// Unwrap returns the wrapped error.
func (e *ApplyError) Unwrap() error {
	return e.err
}

func (e *ApplyError) Error() string {
	return fmt.Sprintf("%v", e.err)
}

type lineNum int
type fragNum int
type fragLineNum int

// applyError creates a new *ApplyError wrapping err or augments the information
// in err with args if it is already an *ApplyError. Returns nil if err is nil.
func applyError(err error, args ...interface{}) error {
	if err == nil {
		return nil
	}

	e, ok := err.(*ApplyError)
	if !ok {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		e = &ApplyError{err: err}
	}
	for _, arg := range args {
		switch v := arg.(type) {
		case lineNum:
			e.Line = int64(v) + 1
		case fragNum:
			e.Fragment = int(v) + 1
		case fragLineNum:
			e.FragmentLine = int(v) + 1
		}
	}
	return e
}

var (
	errApplyInProgress = errors.New("gitdiff: incompatible apply in progress")
)

const (
	applyInitial = iota
	applyText
	applyBinary
	applyFile
)

// Applier applies changes described in fragments to source data. If changes
// are described in multiple fragments, those fragments must be applied in
// order, usually by calling ApplyFile.
//
// By default, Applier operates in "strict" mode, where fragment content and
// positions must exactly match those of the source.
//
// If an error occurs while applying, methods on Applier return instances of
// *ApplyError that annotate the wrapped error with additional information
// when available. If the error is because of a conflict between a fragment and
// the source, the wrapped error will be a *Conflict.
//
// While an Applier can apply both text and binary fragments, only one fragment
// type can be used without resetting the Applier. The first fragment applied
// sets the type for the Applier. Mixing fragment types or mixing
// fragment-level and file-level applies results in an error.
type Applier struct {
	src       io.ReaderAt
	lineSrc   LineReaderAt
	nextLine  int64
	applyType int
}

// NewApplier creates an Applier that reads data from src. If src is a
// LineReaderAt, it is used directly to apply text fragments.
func NewApplier(src io.ReaderAt) *Applier {
	a := new(Applier)
	a.Reset(src)
	return a
}

// Reset resets the input and internal state of the Applier. If src is nil, the
// existing source is reused.
func (a *Applier) Reset(src io.ReaderAt) {
	if src != nil {
		a.src = src
		if lineSrc, ok := src.(LineReaderAt); ok {
			a.lineSrc = lineSrc
		} else {
			a.lineSrc = &lineReaderAt{r: src}
		}
	}
	a.nextLine = 0
	a.applyType = applyInitial
}

// ApplyFile applies the changes in all of the fragments of f and writes the
// result to dst.
func (a *Applier) ApplyFile(dst io.Writer, f *File) error {
	if a.applyType != applyInitial {
		return applyError(errApplyInProgress)
	}
	defer func() { a.applyType = applyFile }()

	if f.IsBinary && len(f.TextFragments) > 0 {
		return applyError(errors.New("binary file contains text fragments"))
	}
	if !f.IsBinary && f.BinaryFragment != nil {
		return applyError(errors.New("text file contains binary fragment"))
	}

	switch {
	case f.BinaryFragment != nil:
		return a.ApplyBinaryFragment(dst, f.BinaryFragment)

	case len(f.TextFragments) > 0:
		frags := make([]*TextFragment, len(f.TextFragments))
		copy(frags, f.TextFragments)

		sort.Slice(frags, func(i, j int) bool {
			return frags[i].OldPosition < frags[j].OldPosition
		})

		// TODO(bkeyes): consider merging overlapping fragments
		// right now, the application fails if fragments overlap, but it should be
		// possible to precompute the result of applying them in order

		for i, frag := range frags {
			if err := a.ApplyTextFragment(dst, frag); err != nil {
				return applyError(err, fragNum(i))
			}
		}
	}

	return applyError(a.Flush(dst))
}

// ApplyTextFragment applies the changes in the fragment f and writes unwritten
// data before the start of the fragment and the result to dst. If multiple
// text fragments apply to the same source, ApplyTextFragment must be called in
// order of increasing start position. As a result, each fragment can be
// applied at most once before a call to Reset.
func (a *Applier) ApplyTextFragment(dst io.Writer, f *TextFragment) error {
	if a.applyType != applyInitial && a.applyType != applyText {
		return applyError(errApplyInProgress)
	}
	defer func() { a.applyType = applyText }()

	// application code assumes fragment fields are consistent
	if err := f.Validate(); err != nil {
		return applyError(err)
	}

	// lines are 0-indexed, positions are 1-indexed (but new files have position = 0)
	fragStart := f.OldPosition - 1
	if fragStart < 0 {
		fragStart = 0
	}
	fragEnd := fragStart + f.OldLines

	start := a.nextLine
	if fragStart < start {
		return applyError(&Conflict{"fragment overlaps with an applied fragment"})
	}

	if f.OldPosition == 0 {
		ok, err := isLen(a.src, 0)
		if err != nil {
			return applyError(err)
		}
		if !ok {
			return applyError(&Conflict{"cannot create new file from non-empty src"})
		}
	}

	preimage := make([][]byte, fragEnd-start)
	n, err := a.lineSrc.ReadLinesAt(preimage, start)
	switch {
	case err == nil:
	case err == io.EOF && n == len(preimage): // last line of frag has no newline character
	default:
		return applyError(err, lineNum(start+int64(n)))
	}

	// copy leading data before the fragment starts
	for i, line := range preimage[:fragStart-start] {
		if _, err := dst.Write(line); err != nil {
			a.nextLine = start + int64(i)
			return applyError(err, lineNum(a.nextLine))
		}
	}
	preimage = preimage[fragStart-start:]

	// apply the changes in the fragment
	used := int64(0)
	for i, line := range f.Lines {
		if err := applyTextLine(dst, line, preimage, used); err != nil {
			a.nextLine = fragStart + used
			return applyError(err, lineNum(a.nextLine), fragLineNum(i))
		}
		if line.Old() {
			used++
		}
	}
	a.nextLine = fragStart + used
	return nil
}

func applyTextLine(dst io.Writer, line Line, preimage [][]byte, i int64) (err error) {
	if line.Old() && string(preimage[i]) != line.Line {
		return &Conflict{"fragment line does not match src line"}
	}
	if line.New() {
		_, err = io.WriteString(dst, line.Line)
	}
	return err
}

// Flush writes any data following the last applied fragment to dst.
func (a *Applier) Flush(dst io.Writer) (err error) {
	switch a.applyType {
	case applyInitial:
		_, err = copyFrom(dst, a.src, 0)
	case applyText:
		_, err = copyLinesFrom(dst, a.lineSrc, a.nextLine)
	case applyBinary:
		// nothing to flush, binary apply "consumes" full source
	}
	return err
}

// ApplyBinaryFragment applies the changes in the fragment f and writes the
// result to dst. At most one binary fragment can be applied before a call to
// Reset.
func (a *Applier) ApplyBinaryFragment(dst io.Writer, f *BinaryFragment) error {
	if a.applyType != applyInitial {
		return applyError(errApplyInProgress)
	}
	defer func() { a.applyType = applyBinary }()

	if f == nil {
		return applyError(errors.New("nil fragment"))
	}

	switch f.Method {
	case BinaryPatchLiteral:
		if _, err := dst.Write(f.Data); err != nil {
			return applyError(err)
		}
	case BinaryPatchDelta:
		if err := applyBinaryDeltaFragment(dst, a.src, f.Data); err != nil {
			return applyError(err)
		}
	default:
		return applyError(fmt.Errorf("unsupported binary patch method: %v", f.Method))
	}
	return nil
}

func applyBinaryDeltaFragment(dst io.Writer, src io.ReaderAt, frag []byte) error {
	srcSize, delta := readBinaryDeltaSize(frag)
	if err := checkBinarySrcSize(src, srcSize); err != nil {
		return err
	}

	dstSize, delta := readBinaryDeltaSize(delta)

	for len(delta) > 0 {
		op := delta[0]
		if op == 0 {
			return errors.New("invalid delta opcode 0")
		}

		var n int64
		var err error
		switch op & 0x80 {
		case 0x80:
			n, delta, err = applyBinaryDeltaCopy(dst, op, delta[1:], src)
		case 0x00:
			n, delta, err = applyBinaryDeltaAdd(dst, op, delta[1:])
		}
		if err != nil {
			return err
		}
		dstSize -= n
	}

	if dstSize != 0 {
		return errors.New("corrupt binary delta: insufficient or extra data")
	}
	return nil
}

// readBinaryDeltaSize reads a variable length size from a delta-encoded binary
// fragment, returing the size and the unused data. Data is encoded as:
//
//    [[1xxxxxxx]...] [0xxxxxxx]
//
// in little-endian order, with 7 bits of the value per byte.
func readBinaryDeltaSize(d []byte) (size int64, rest []byte) {
	shift := uint(0)
	for i, b := range d {
		size |= int64(b&0x7F) << shift
		shift += 7
		if b <= 0x7F {
			return size, d[i+1:]
		}
	}
	return size, nil
}

// applyBinaryDeltaAdd applies an add opcode in a delta-encoded binary
// fragment, returning the amount of data written and the usused part of the
// fragment. An add operation takes the form:
//
//     [0xxxxxx][[data1]...]
//
// where the lower seven bits of the opcode is the number of data bytes
// following the opcode. See also pack-format.txt in the Git source.
func applyBinaryDeltaAdd(w io.Writer, op byte, delta []byte) (n int64, rest []byte, err error) {
	size := int(op)
	if len(delta) < size {
		return 0, delta, errors.New("corrupt binary delta: incomplete add")
	}
	_, err = w.Write(delta[:size])
	return int64(size), delta[size:], err
}

// applyBinaryDeltaCopy applies a copy opcode in a delta-encoded binary
// fragment, returing the amount of data written and the unused part of the
// fragment. A copy operation takes the form:
//
//     [1xxxxxxx][offset1][offset2][offset3][offset4][size1][size2][size3]
//
// where the lower seven bits of the opcode determine which non-zero offset and
// size bytes are present in little-endian order: if bit 0 is set, offset1 is
// present, etc. If no offset or size bytes are present, offset is 0 and size
// is 0x10000. See also pack-format.txt in the Git source.
func applyBinaryDeltaCopy(w io.Writer, op byte, delta []byte, src io.ReaderAt) (n int64, rest []byte, err error) {
	const defaultSize = 0x10000

	unpack := func(start, bits uint) (v int64) {
		for i := uint(0); i < bits; i++ {
			mask := byte(1 << (i + start))
			if op&mask > 0 {
				if len(delta) == 0 {
					err = errors.New("corrupt binary delta: incomplete copy")
					return
				}
				v |= int64(delta[0]) << (8 * i)
				delta = delta[1:]
			}
		}
		return
	}

	offset := unpack(0, 4)
	size := unpack(4, 3)
	if err != nil {
		return 0, delta, err
	}
	if size == 0 {
		size = defaultSize
	}

	// TODO(bkeyes): consider pooling these buffers
	b := make([]byte, size)
	if _, err := src.ReadAt(b, offset); err != nil {
		return 0, delta, err
	}

	_, err = w.Write(b)
	return size, delta, err
}

func checkBinarySrcSize(r io.ReaderAt, size int64) error {
	ok, err := isLen(r, size)
	if err != nil {
		return err
	}
	if !ok {
		return &Conflict{"fragment src size does not match actual src size"}
	}
	return nil
}
<commit_msg>Add Apply convenience function to match Parse (#5)<commit_after>package gitdiff

import (
	"errors"
	"fmt"
	"io"
	"sort"
)

// Conflict indicates an apply failed due to a conflict between the patch and
// the source content.
//
// Users can test if an error was caused by a conflict by using errors.Is with
// an empty Conflict:
//
//     if errors.Is(err, &Conflict{}) {
//	       // handle conflict
//     }
//
type Conflict struct {
	msg string
}

func (c *Conflict) Error() string {
	return "conflict: " + c.msg
}

// Is implements error matching for Conflict. Passing an empty instance of
// Conflict always returns true.
func (c *Conflict) Is(other error) bool {
	if other, ok := other.(*Conflict); ok {
		return other.msg == "" || other.msg == c.msg
	}
	return false
}

// ApplyError wraps an error that occurs during patch application with
// additional location information, if it is available.
type ApplyError struct {
	// Line is the one-indexed line number in the source data
	Line int64
	// Fragment is the one-indexed fragment number in the file
	Fragment int
	// FragmentLine is the one-indexed line number in the fragment
	FragmentLine int

	err error
}

// Unwrap returns the wrapped error.
func (e *ApplyError) Unwrap() error {
	return e.err
}

func (e *ApplyError) Error() string {
	return fmt.Sprintf("%v", e.err)
}

type lineNum int
type fragNum int
type fragLineNum int

// applyError creates a new *ApplyError wrapping err or augments the information
// in err with args if it is already an *ApplyError. Returns nil if err is nil.
func applyError(err error, args ...interface{}) error {
	if err == nil {
		return nil
	}

	e, ok := err.(*ApplyError)
	if !ok {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
		e = &ApplyError{err: err}
	}
	for _, arg := range args {
		switch v := arg.(type) {
		case lineNum:
			e.Line = int64(v) + 1
		case fragNum:
			e.Fragment = int(v) + 1
		case fragLineNum:
			e.FragmentLine = int(v) + 1
		}
	}
	return e
}

var (
	errApplyInProgress = errors.New("gitdiff: incompatible apply in progress")
)

const (
	applyInitial = iota
	applyText
	applyBinary
	applyFile
)

// Apply is a convenience function that creates an Applier for src with default
// settings and applies the changes in f, writing the result to dst.
func Apply(dst io.Writer, src io.ReaderAt, f *File) error {
	return NewApplier(src).ApplyFile(dst, f)
}

// Applier applies changes described in fragments to source data. If changes
// are described in multiple fragments, those fragments must be applied in
// order, usually by calling ApplyFile.
//
// By default, Applier operates in "strict" mode, where fragment content and
// positions must exactly match those of the source.
//
// If an error occurs while applying, methods on Applier return instances of
// *ApplyError that annotate the wrapped error with additional information
// when available. If the error is because of a conflict between a fragment and
// the source, the wrapped error will be a *Conflict.
//
// While an Applier can apply both text and binary fragments, only one fragment
// type can be used without resetting the Applier. The first fragment applied
// sets the type for the Applier. Mixing fragment types or mixing
// fragment-level and file-level applies results in an error.
type Applier struct {
	src       io.ReaderAt
	lineSrc   LineReaderAt
	nextLine  int64
	applyType int
}

// NewApplier creates an Applier that reads data from src. If src is a
// LineReaderAt, it is used directly to apply text fragments.
func NewApplier(src io.ReaderAt) *Applier {
	a := new(Applier)
	a.Reset(src)
	return a
}

// Reset resets the input and internal state of the Applier. If src is nil, the
// existing source is reused.
func (a *Applier) Reset(src io.ReaderAt) {
	if src != nil {
		a.src = src
		if lineSrc, ok := src.(LineReaderAt); ok {
			a.lineSrc = lineSrc
		} else {
			a.lineSrc = &lineReaderAt{r: src}
		}
	}
	a.nextLine = 0
	a.applyType = applyInitial
}

// ApplyFile applies the changes in all of the fragments of f and writes the
// result to dst.
func (a *Applier) ApplyFile(dst io.Writer, f *File) error {
	if a.applyType != applyInitial {
		return applyError(errApplyInProgress)
	}
	defer func() { a.applyType = applyFile }()

	if f.IsBinary && len(f.TextFragments) > 0 {
		return applyError(errors.New("binary file contains text fragments"))
	}
	if !f.IsBinary && f.BinaryFragment != nil {
		return applyError(errors.New("text file contains binary fragment"))
	}

	switch {
	case f.BinaryFragment != nil:
		return a.ApplyBinaryFragment(dst, f.BinaryFragment)

	case len(f.TextFragments) > 0:
		frags := make([]*TextFragment, len(f.TextFragments))
		copy(frags, f.TextFragments)

		sort.Slice(frags, func(i, j int) bool {
			return frags[i].OldPosition < frags[j].OldPosition
		})

		// TODO(bkeyes): consider merging overlapping fragments
		// right now, the application fails if fragments overlap, but it should be
		// possible to precompute the result of applying them in order

		for i, frag := range frags {
			if err := a.ApplyTextFragment(dst, frag); err != nil {
				return applyError(err, fragNum(i))
			}
		}
	}

	return applyError(a.Flush(dst))
}

// ApplyTextFragment applies the changes in the fragment f and writes unwritten
// data before the start of the fragment and the result to dst. If multiple
// text fragments apply to the same source, ApplyTextFragment must be called in
// order of increasing start position. As a result, each fragment can be
// applied at most once before a call to Reset.
func (a *Applier) ApplyTextFragment(dst io.Writer, f *TextFragment) error {
	if a.applyType != applyInitial && a.applyType != applyText {
		return applyError(errApplyInProgress)
	}
	defer func() { a.applyType = applyText }()

	// application code assumes fragment fields are consistent
	if err := f.Validate(); err != nil {
		return applyError(err)
	}

	// lines are 0-indexed, positions are 1-indexed (but new files have position = 0)
	fragStart := f.OldPosition - 1
	if fragStart < 0 {
		fragStart = 0
	}
	fragEnd := fragStart + f.OldLines

	start := a.nextLine
	if fragStart < start {
		return applyError(&Conflict{"fragment overlaps with an applied fragment"})
	}

	if f.OldPosition == 0 {
		ok, err := isLen(a.src, 0)
		if err != nil {
			return applyError(err)
		}
		if !ok {
			return applyError(&Conflict{"cannot create new file from non-empty src"})
		}
	}

	preimage := make([][]byte, fragEnd-start)
	n, err := a.lineSrc.ReadLinesAt(preimage, start)
	switch {
	case err == nil:
	case err == io.EOF && n == len(preimage): // last line of frag has no newline character
	default:
		return applyError(err, lineNum(start+int64(n)))
	}

	// copy leading data before the fragment starts
	for i, line := range preimage[:fragStart-start] {
		if _, err := dst.Write(line); err != nil {
			a.nextLine = start + int64(i)
			return applyError(err, lineNum(a.nextLine))
		}
	}
	preimage = preimage[fragStart-start:]

	// apply the changes in the fragment
	used := int64(0)
	for i, line := range f.Lines {
		if err := applyTextLine(dst, line, preimage, used); err != nil {
			a.nextLine = fragStart + used
			return applyError(err, lineNum(a.nextLine), fragLineNum(i))
		}
		if line.Old() {
			used++
		}
	}
	a.nextLine = fragStart + used
	return nil
}

func applyTextLine(dst io.Writer, line Line, preimage [][]byte, i int64) (err error) {
	if line.Old() && string(preimage[i]) != line.Line {
		return &Conflict{"fragment line does not match src line"}
	}
	if line.New() {
		_, err = io.WriteString(dst, line.Line)
	}
	return err
}

// Flush writes any data following the last applied fragment to dst.
func (a *Applier) Flush(dst io.Writer) (err error) {
	switch a.applyType {
	case applyInitial:
		_, err = copyFrom(dst, a.src, 0)
	case applyText:
		_, err = copyLinesFrom(dst, a.lineSrc, a.nextLine)
	case applyBinary:
		// nothing to flush, binary apply "consumes" full source
	}
	return err
}

// ApplyBinaryFragment applies the changes in the fragment f and writes the
// result to dst. At most one binary fragment can be applied before a call to
// Reset.
func (a *Applier) ApplyBinaryFragment(dst io.Writer, f *BinaryFragment) error {
	if a.applyType != applyInitial {
		return applyError(errApplyInProgress)
	}
	defer func() { a.applyType = applyBinary }()

	if f == nil {
		return applyError(errors.New("nil fragment"))
	}

	switch f.Method {
	case BinaryPatchLiteral:
		if _, err := dst.Write(f.Data); err != nil {
			return applyError(err)
		}
	case BinaryPatchDelta:
		if err := applyBinaryDeltaFragment(dst, a.src, f.Data); err != nil {
			return applyError(err)
		}
	default:
		return applyError(fmt.Errorf("unsupported binary patch method: %v", f.Method))
	}
	return nil
}

func applyBinaryDeltaFragment(dst io.Writer, src io.ReaderAt, frag []byte) error {
	srcSize, delta := readBinaryDeltaSize(frag)
	if err := checkBinarySrcSize(src, srcSize); err != nil {
		return err
	}

	dstSize, delta := readBinaryDeltaSize(delta)

	for len(delta) > 0 {
		op := delta[0]
		if op == 0 {
			return errors.New("invalid delta opcode 0")
		}

		var n int64
		var err error
		switch op & 0x80 {
		case 0x80:
			n, delta, err = applyBinaryDeltaCopy(dst, op, delta[1:], src)
		case 0x00:
			n, delta, err = applyBinaryDeltaAdd(dst, op, delta[1:])
		}
		if err != nil {
			return err
		}
		dstSize -= n
	}

	if dstSize != 0 {
		return errors.New("corrupt binary delta: insufficient or extra data")
	}
	return nil
}

// readBinaryDeltaSize reads a variable length size from a delta-encoded binary
// fragment, returing the size and the unused data. Data is encoded as:
//
//    [[1xxxxxxx]...] [0xxxxxxx]
//
// in little-endian order, with 7 bits of the value per byte.
func readBinaryDeltaSize(d []byte) (size int64, rest []byte) {
	shift := uint(0)
	for i, b := range d {
		size |= int64(b&0x7F) << shift
		shift += 7
		if b <= 0x7F {
			return size, d[i+1:]
		}
	}
	return size, nil
}

// applyBinaryDeltaAdd applies an add opcode in a delta-encoded binary
// fragment, returning the amount of data written and the usused part of the
// fragment. An add operation takes the form:
//
//     [0xxxxxx][[data1]...]
//
// where the lower seven bits of the opcode is the number of data bytes
// following the opcode. See also pack-format.txt in the Git source.
func applyBinaryDeltaAdd(w io.Writer, op byte, delta []byte) (n int64, rest []byte, err error) {
	size := int(op)
	if len(delta) < size {
		return 0, delta, errors.New("corrupt binary delta: incomplete add")
	}
	_, err = w.Write(delta[:size])
	return int64(size), delta[size:], err
}

// applyBinaryDeltaCopy applies a copy opcode in a delta-encoded binary
// fragment, returing the amount of data written and the unused part of the
// fragment. A copy operation takes the form:
//
//     [1xxxxxxx][offset1][offset2][offset3][offset4][size1][size2][size3]
//
// where the lower seven bits of the opcode determine which non-zero offset and
// size bytes are present in little-endian order: if bit 0 is set, offset1 is
// present, etc. If no offset or size bytes are present, offset is 0 and size
// is 0x10000. See also pack-format.txt in the Git source.
func applyBinaryDeltaCopy(w io.Writer, op byte, delta []byte, src io.ReaderAt) (n int64, rest []byte, err error) {
	const defaultSize = 0x10000

	unpack := func(start, bits uint) (v int64) {
		for i := uint(0); i < bits; i++ {
			mask := byte(1 << (i + start))
			if op&mask > 0 {
				if len(delta) == 0 {
					err = errors.New("corrupt binary delta: incomplete copy")
					return
				}
				v |= int64(delta[0]) << (8 * i)
				delta = delta[1:]
			}
		}
		return
	}

	offset := unpack(0, 4)
	size := unpack(4, 3)
	if err != nil {
		return 0, delta, err
	}
	if size == 0 {
		size = defaultSize
	}

	// TODO(bkeyes): consider pooling these buffers
	b := make([]byte, size)
	if _, err := src.ReadAt(b, offset); err != nil {
		return 0, delta, err
	}

	_, err = w.Write(b)
	return size, delta, err
}

func checkBinarySrcSize(r io.ReaderAt, size int64) error {
	ok, err := isLen(r, size)
	if err != nil {
		return err
	}
	if !ok {
		return &Conflict{"fragment src size does not match actual src size"}
	}
	return 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"
	"time"
)

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

// Issue represents a GitHub issue on a repository.
type Issue struct {
	Number           *int              `json:"number,omitempty"`
	State            *string           `json:"state,omitempty"`
	Title            *string           `json:"title,omitempty"`
	Body             *string           `json:"body,omitempty"`
	User             *User             `json:"user,omitempty"`
	Labels           []Label           `json:"labels,omitempty"`
	Assignee         *User             `json:"assignee,omitempty"`
	Comments         *int              `json:"comments,omitempty"`
	ClosedAt         *time.Time        `json:"closed_at,omitempty"`
	CreatedAt        *time.Time        `json:"created_at,omitempty"`
	UpdatedAt        *time.Time        `json:"updated_at,omitempty"`
	URL              *string           `json:"url,omitempty"`
	HTMLURL          *string           `json:"html_url,omitempty"`
	Milestone        *Milestone        `json:"milestone,omitempty"`
	PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"`

	// TextMatches is only populated from search results that request text matches
	// See: search.go and https://developer.github.com/v3/search/#text-match-metadata
	TextMatches []TextMatch `json:"text_matches,omitempty"`
}

func (i Issue) String() string {
	return Stringify(i)
}

// IssueRequest represents a request to create/edit an issue.
// It is separate from Issue above because otherwise Labels
// and Assignee fail to serialize to the correct JSON.
type IssueRequest struct {
	Title     *string   `json:"title,omitempty"`
	Body      *string   `json:"body,omitempty"`
	Labels    *[]string `json:"labels,omitempty"`
	Assignee  *string   `json:"assignee,omitempty"`
	State     *string   `json:"state,omitempty"`
	Milestone *int      `json:"milestone,omitempty"`
}

// IssueListOptions specifies the optional parameters to the IssuesService.List
// and IssuesService.ListByOrg methods.
type IssueListOptions struct {
	// Filter specifies which issues to list.  Possible values are: assigned,
	// created, mentioned, subscribed, all.  Default is "assigned".
	Filter string `url:"filter,omitempty"`

	// State filters issues based on their state.  Possible values are: open,
	// closed, all.  Default is "open".
	State string `url:"state,omitempty"`

	// Labels filters issues based on their label.
	Labels []string `url:"labels,comma,omitempty"`

	// Sort specifies how to sort issues.  Possible values are: created, updated,
	// and comments.  Default value is "created".
	Sort string `url:"sort,omitempty"`

	// Direction in which to sort issues.  Possible values are: asc, desc.
	// Default is "asc".
	Direction string `url:"direction,omitempty"`

	// Since filters issues by time.
	Since time.Time `url:"since,omitempty"`

	ListOptions
}

// PullRequestLinks object is added to the Issue object when it's an issue included
// in the IssueCommentEvent webhook payload, if the webhooks is fired by a comment on a PR
type PullRequestLinks struct {
	URL      *string `json:"url,omitempty"`
	HTMLURL  *string `json:"html_url,omitempty"`
	DiffURL  *string `json:"diff_url,omitempty"`
	PatchURL *string `json:"patch_url,omitempty"`
}

// List the issues for the authenticated user.  If all is true, list issues
// across all the user's visible repositories including owned, member, and
// organization repositories; if false, list only owned and member
// repositories.
//
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues
func (s *IssuesService) List(all bool, opt *IssueListOptions) ([]Issue, *Response, error) {
	var u string
	if all {
		u = "issues"
	} else {
		u = "user/issues"
	}
	return s.listIssues(u, opt)
}

// ListByOrg fetches the issues in the specified organization for the
// authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues
func (s *IssuesService) ListByOrg(org string, opt *IssueListOptions) ([]Issue, *Response, error) {
	u := fmt.Sprintf("orgs/%v/issues", org)
	return s.listIssues(u, opt)
}

func (s *IssuesService) listIssues(u string, opt *IssueListOptions) ([]Issue, *Response, error) {
	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
	}

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

	return *issues, resp, err
}

// IssueListByRepoOptions specifies the optional parameters to the
// IssuesService.ListByRepo method.
type IssueListByRepoOptions struct {
	// Milestone limits issues for the specified milestone.  Possible values are
	// a milestone number, "none" for issues with no milestone, "*" for issues
	// with any milestone.
	Milestone string `url:"milestone,omitempty"`

	// State filters issues based on their state.  Possible values are: open,
	// closed, all.  Default is "open".
	State string `url:"state,omitempty"`

	// Assignee filters issues based on their assignee.  Possible values are a
	// user name, "none" for issues that are not assigned, "*" for issues with
	// any assigned user.
	Assignee string `url:"assignee,omitempty"`

	// Creator filters issues based on their creator.
	Creator string `url:"creator,omitempty"`

	// Mentioned filters issues to those mentioned a specific user.
	Mentioned string `url:"mentioned,omitempty"`

	// Labels filters issues based on their label.
	Labels []string `url:"labels,omitempty,comma"`

	// Sort specifies how to sort issues.  Possible values are: created, updated,
	// and comments.  Default value is "created".
	Sort string `url:"sort,omitempty"`

	// Direction in which to sort issues.  Possible values are: asc, desc.
	// Default is "asc".
	Direction string `url:"direction,omitempty"`

	// Since filters issues by time.
	Since time.Time `url:"since,omitempty"`

	ListOptions
}

// ListByRepo lists the issues for the specified repository.
//
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues-for-a-repository
func (s *IssuesService) ListByRepo(owner string, repo string, opt *IssueListByRepoOptions) ([]Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
	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
	}

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

	return *issues, resp, err
}

// Get a single issue.
//
// GitHub API docs: http://developer.github.com/v3/issues/#get-a-single-issue
func (s *IssuesService) Get(owner string, repo string, number int) (*Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

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

	return issue, resp, err
}

// Create a new issue on the specified repository.
//
// GitHub API docs: http://developer.github.com/v3/issues/#create-an-issue
func (s *IssuesService) Create(owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
	req, err := s.client.NewRequest("POST", u, issue)
	if err != nil {
		return nil, nil, err
	}

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

	return i, resp, err
}

// Edit an issue.
//
// GitHub API docs: http://developer.github.com/v3/issues/#edit-an-issue
func (s *IssuesService) Edit(owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
	req, err := s.client.NewRequest("PATCH", u, issue)
	if err != nil {
		return nil, nil, err
	}

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

	return i, resp, err
}
<commit_msg>Fix default comment for Direction in issue lists<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"
	"time"
)

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

// Issue represents a GitHub issue on a repository.
type Issue struct {
	Number           *int              `json:"number,omitempty"`
	State            *string           `json:"state,omitempty"`
	Title            *string           `json:"title,omitempty"`
	Body             *string           `json:"body,omitempty"`
	User             *User             `json:"user,omitempty"`
	Labels           []Label           `json:"labels,omitempty"`
	Assignee         *User             `json:"assignee,omitempty"`
	Comments         *int              `json:"comments,omitempty"`
	ClosedAt         *time.Time        `json:"closed_at,omitempty"`
	CreatedAt        *time.Time        `json:"created_at,omitempty"`
	UpdatedAt        *time.Time        `json:"updated_at,omitempty"`
	URL              *string           `json:"url,omitempty"`
	HTMLURL          *string           `json:"html_url,omitempty"`
	Milestone        *Milestone        `json:"milestone,omitempty"`
	PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"`

	// TextMatches is only populated from search results that request text matches
	// See: search.go and https://developer.github.com/v3/search/#text-match-metadata
	TextMatches []TextMatch `json:"text_matches,omitempty"`
}

func (i Issue) String() string {
	return Stringify(i)
}

// IssueRequest represents a request to create/edit an issue.
// It is separate from Issue above because otherwise Labels
// and Assignee fail to serialize to the correct JSON.
type IssueRequest struct {
	Title     *string   `json:"title,omitempty"`
	Body      *string   `json:"body,omitempty"`
	Labels    *[]string `json:"labels,omitempty"`
	Assignee  *string   `json:"assignee,omitempty"`
	State     *string   `json:"state,omitempty"`
	Milestone *int      `json:"milestone,omitempty"`
}

// IssueListOptions specifies the optional parameters to the IssuesService.List
// and IssuesService.ListByOrg methods.
type IssueListOptions struct {
	// Filter specifies which issues to list.  Possible values are: assigned,
	// created, mentioned, subscribed, all.  Default is "assigned".
	Filter string `url:"filter,omitempty"`

	// State filters issues based on their state.  Possible values are: open,
	// closed, all.  Default is "open".
	State string `url:"state,omitempty"`

	// Labels filters issues based on their label.
	Labels []string `url:"labels,comma,omitempty"`

	// Sort specifies how to sort issues.  Possible values are: created, updated,
	// and comments.  Default value is "created".
	Sort string `url:"sort,omitempty"`

	// Direction in which to sort issues.  Possible values are: asc, desc.
	// Default is "desc".
	Direction string `url:"direction,omitempty"`

	// Since filters issues by time.
	Since time.Time `url:"since,omitempty"`

	ListOptions
}

// PullRequestLinks object is added to the Issue object when it's an issue included
// in the IssueCommentEvent webhook payload, if the webhooks is fired by a comment on a PR
type PullRequestLinks struct {
	URL      *string `json:"url,omitempty"`
	HTMLURL  *string `json:"html_url,omitempty"`
	DiffURL  *string `json:"diff_url,omitempty"`
	PatchURL *string `json:"patch_url,omitempty"`
}

// List the issues for the authenticated user.  If all is true, list issues
// across all the user's visible repositories including owned, member, and
// organization repositories; if false, list only owned and member
// repositories.
//
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues
func (s *IssuesService) List(all bool, opt *IssueListOptions) ([]Issue, *Response, error) {
	var u string
	if all {
		u = "issues"
	} else {
		u = "user/issues"
	}
	return s.listIssues(u, opt)
}

// ListByOrg fetches the issues in the specified organization for the
// authenticated user.
//
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues
func (s *IssuesService) ListByOrg(org string, opt *IssueListOptions) ([]Issue, *Response, error) {
	u := fmt.Sprintf("orgs/%v/issues", org)
	return s.listIssues(u, opt)
}

func (s *IssuesService) listIssues(u string, opt *IssueListOptions) ([]Issue, *Response, error) {
	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
	}

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

	return *issues, resp, err
}

// IssueListByRepoOptions specifies the optional parameters to the
// IssuesService.ListByRepo method.
type IssueListByRepoOptions struct {
	// Milestone limits issues for the specified milestone.  Possible values are
	// a milestone number, "none" for issues with no milestone, "*" for issues
	// with any milestone.
	Milestone string `url:"milestone,omitempty"`

	// State filters issues based on their state.  Possible values are: open,
	// closed, all.  Default is "open".
	State string `url:"state,omitempty"`

	// Assignee filters issues based on their assignee.  Possible values are a
	// user name, "none" for issues that are not assigned, "*" for issues with
	// any assigned user.
	Assignee string `url:"assignee,omitempty"`

	// Creator filters issues based on their creator.
	Creator string `url:"creator,omitempty"`

	// Mentioned filters issues to those mentioned a specific user.
	Mentioned string `url:"mentioned,omitempty"`

	// Labels filters issues based on their label.
	Labels []string `url:"labels,omitempty,comma"`

	// Sort specifies how to sort issues.  Possible values are: created, updated,
	// and comments.  Default value is "created".
	Sort string `url:"sort,omitempty"`

	// Direction in which to sort issues.  Possible values are: asc, desc.
	// Default is "desc".
	Direction string `url:"direction,omitempty"`

	// Since filters issues by time.
	Since time.Time `url:"since,omitempty"`

	ListOptions
}

// ListByRepo lists the issues for the specified repository.
//
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues-for-a-repository
func (s *IssuesService) ListByRepo(owner string, repo string, opt *IssueListByRepoOptions) ([]Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
	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
	}

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

	return *issues, resp, err
}

// Get a single issue.
//
// GitHub API docs: http://developer.github.com/v3/issues/#get-a-single-issue
func (s *IssuesService) Get(owner string, repo string, number int) (*Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, nil, err
	}

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

	return issue, resp, err
}

// Create a new issue on the specified repository.
//
// GitHub API docs: http://developer.github.com/v3/issues/#create-an-issue
func (s *IssuesService) Create(owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
	req, err := s.client.NewRequest("POST", u, issue)
	if err != nil {
		return nil, nil, err
	}

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

	return i, resp, err
}

// Edit an issue.
//
// GitHub API docs: http://developer.github.com/v3/issues/#edit-an-issue
func (s *IssuesService) Edit(owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) {
	u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
	req, err := s.client.NewRequest("PATCH", u, issue)
	if err != nil {
		return nil, nil, err
	}

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

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


<commit_before>package glog

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

type logstashMessage struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

// handleLogstashMessages sends logs to logstash.
func (l *loggingT) handleLogstashMessages() {
	var conn net.Conn

	initialDelay := 500.0 * time.Millisecond
	maxDelay := 90.0 * time.Second
	delay := initialDelay
	conn, _ = net.DialTimeout("tcp", l.logstashURL, 1*time.Second)
	for {
		select {
		case _ = <-l.logstashStop:
			conn.Close()
			return
		case data := <-l.logstashChan:
			lm := logstashMessage{}
			lm.Type = l.logstashType
			lm.Message = strings.TrimSpace(data)
			packet, err := json.Marshal(lm)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: Failed to marshall logstashMessage.\n",  msgPrefix())
				continue
			} else if conn != nil {
				_, err := fmt.Fprintln(conn, string(packet))
				if err != nil {
					conn = nil
				} else {
					// reset the delay once we were able to write something to logstash
					delay = initialDelay
				}
			} else {
				// There is no connection, so the log line is dropped.
				// Might be nice to add a buffer here so that we can ship
				// logs after the connection is up.
			}
		default:
			time.Sleep(1 * time.Second)
		}

		if conn == nil {
			delay *= 2.0
			if delay > maxDelay {
				delay = maxDelay
			}
			time.Sleep(delay)

			var err error
			conn, err = net.DialTimeout("tcp", l.logstashURL, 1*time.Second)
			if err != nil {
				conn = nil
			}
		}
	}
}

// Message prefix for direct writes to stderr
func msgPrefix() string {
	return fmt.Sprintf("glog: %s: ", time.Now().Format("15:04:05.00000"))
}

// StartLogstash creates the logstash channel and kicks off handleLogstashMessages.
func (l *loggingT) startLogstash() {
	l.logstashChan = make(chan string, 100)
	go l.handleLogstashMessages()
}

// StopLogstash signals handleLogstashMessages to exit.
func (l *loggingT) StopLogstash() {
	l.logstashStop <- true
}
<commit_msg>Add an error message on connect failure for traceability<commit_after>package glog

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

type logstashMessage struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

// handleLogstashMessages sends logs to logstash.
func (l *loggingT) handleLogstashMessages() {
	var conn net.Conn

	initialDelay := 500.0 * time.Millisecond
	maxDelay := 90.0 * time.Second
	delay := initialDelay
	conn, _ = net.DialTimeout("tcp", l.logstashURL, 1*time.Second)
	for {
		select {
		case _ = <-l.logstashStop:
			conn.Close()
			return
		case data := <-l.logstashChan:
			lm := logstashMessage{}
			lm.Type = l.logstashType
			lm.Message = strings.TrimSpace(data)
			packet, err := json.Marshal(lm)
			if err != nil {
				fmt.Fprintf(os.Stderr, "%s: Failed to marshall logstashMessage.\n",  msgPrefix())
				continue
			} else if conn != nil {
				_, err := fmt.Fprintln(conn, string(packet))
				if err != nil {
					conn = nil
				} else {
					// reset the delay once we were able to write something to logstash
					delay = initialDelay
				}
			} else {
				// There is no connection, so the log line is dropped.
				// Might be nice to add a buffer here so that we can ship
				// logs after the connection is up.
			}
		default:
			time.Sleep(1 * time.Second)
		}

		if conn == nil {
			delay *= 2.0
			if delay > maxDelay {
				delay = maxDelay
			}
			time.Sleep(delay)

			var err error
			conn, err = net.DialTimeout("tcp", l.logstashURL, 1*time.Second)
			if err != nil {
				conn = nil
				fmt.Fprintf(os.Stderr, "%s: Could not connect to logstash; will retry later\n",  msgPrefix())
			}
		}
	}
}

// Message prefix for direct writes to stderr
func msgPrefix() string {
	return fmt.Sprintf("glog: %s: ", time.Now().Format("15:04:05.00000"))
}

// StartLogstash creates the logstash channel and kicks off handleLogstashMessages.
func (l *loggingT) startLogstash() {
	l.logstashChan = make(chan string, 100)
	go l.handleLogstashMessages()
}

// StopLogstash signals handleLogstashMessages to exit.
func (l *loggingT) StopLogstash() {
	l.logstashStop <- true
}
<|endoftext|>


<commit_before>package main

import L "github.com/syndtr/goleveldb/leveldb"
import opt "github.com/syndtr/goleveldb/leveldb/opt"

import (
  "flag"
  "fmt"
  "os"
)

var dbpath = flag.String("db", "", "path to database")

func main() {
  flag.Parse()
  a := flag.Args()

  if *dbpath == "" {
    panic("You must set flag --db=database_path")
  }

  t, err := L.OpenFile(*dbpath, &opt.Options{})
  if err != nil { panic(err) }
  defer func() {
    t.Close()
  }()

  if len(a) < 1 {
    Usage()
  }

  switch a[0] {
    case "scan":
      it := t.NewIterator(nil, &opt.ReadOptions{})
      if err != nil { panic(err) }

      it.First()
      for it.Valid() {
        fmt.Printf("%q :: %q\n", it.Key(), it.Value())
        it.Next()
      }
    case "get":
      val, err := t.Get([]byte(a[1]), &opt.ReadOptions{})
      if err != nil { panic(err) }
      fmt.Printf("%q\n", val)
    case "put":
      err := t.Put([]byte(a[1]), []byte(a[2]), &opt.WriteOptions{})
      if err != nil { panic(err) }
    case "del":
      err := t.Delete([]byte(a[1]), &opt.WriteOptions{})
      if err != nil { panic(err) }
    default:
      Usage()
  }
}

func Usage() {
  fmt.Fprintf(os.Stderr, `Usage:
    db scan
    db get Key
    db put Key value
    db del Key
`)
  os.Exit(13)
}
<commit_msg>gofmt<commit_after>package main

import L "github.com/syndtr/goleveldb/leveldb"
import opt "github.com/syndtr/goleveldb/leveldb/opt"

import (
	"flag"
	"fmt"
	"os"
)

var dbpath = flag.String("db", "", "path to database")

func main() {
	flag.Parse()
	a := flag.Args()

	if *dbpath == "" {
		panic("You must set flag --db=database_path")
	}

	t, err := L.OpenFile(*dbpath, &opt.Options{})
	if err != nil {
		panic(err)
	}
	defer func() {
		t.Close()
	}()

	if len(a) < 1 {
		Usage()
	}

	switch a[0] {
	case "scan":
		it := t.NewIterator(nil, &opt.ReadOptions{})
		if err != nil {
			panic(err)
		}

		it.First()
		for it.Valid() {
			fmt.Printf("%q :: %q\n", it.Key(), it.Value())
			it.Next()
		}
	case "get":
		val, err := t.Get([]byte(a[1]), &opt.ReadOptions{})
		if err != nil {
			panic(err)
		}
		fmt.Printf("%q\n", val)
	case "put":
		err := t.Put([]byte(a[1]), []byte(a[2]), &opt.WriteOptions{})
		if err != nil {
			panic(err)
		}
	case "del":
		err := t.Delete([]byte(a[1]), &opt.WriteOptions{})
		if err != nil {
			panic(err)
		}
	default:
		Usage()
	}
}

func Usage() {
	fmt.Fprintf(os.Stderr, `Usage:
    db scan
    db get Key
    db put Key value
    db del Key
`)
	os.Exit(13)
}
<|endoftext|>


<commit_before>package orcraft

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"

	"github.com/openark/golib/log"

	"github.com/hashicorp/raft"
)

const (
	retainSnapshotCount = 2
	raftTimeout         = 10 * time.Second
)

type Store struct {
	raftDir  string
	raftBind string

	raft *raft.Raft // The consensus mechanism

	applier CommandApplier
}

type storeCommand struct {
	Op    string `json:"op,omitempty"`
	Value []byte `json:"value,omitempty"`
}

// NewStore inits and returns a new store
func NewStore(raftDir string, raftBind string, applier CommandApplier) *Store {
	return &Store{
		raftDir:  raftDir,
		raftBind: raftBind,
		applier:  applier,
	}
}

// Open opens the store. If enableSingle is set, and there are no existing peers,
// then this node becomes the first node, and therefore leader, of the cluster.
func (store *Store) Open(peerNodes []string) error {
	// Setup Raft configuration.
	config := raft.DefaultConfig()

	// Setup Raft communication.
	addr, err := net.ResolveTCPAddr("tcp", store.raftBind)
	if err != nil {
		return err
	}
	log.Debugf("raft: addr=%+v", addr)
	transport, err := raft.NewTCPTransport(store.raftBind, addr, 3, 10*time.Second, os.Stderr)
	if err != nil {
		return err
	}
	log.Debugf("raft: transport=%+v", transport)

	peers := make([]string, 0, 10)
	for _, peerNode := range peerNodes {
		peerNode = strings.TrimSpace(peerNode)
		peers = raft.AddUniquePeer(peers, peerNode)
	}
	log.Debugf("raft: peers=%+v", peers)

	// Create peer storage.
	peerStore := &raft.StaticPeers{}
	if err := peerStore.SetPeers(peers); err != nil {
		return err
	}

	// Allow the node to enter single-mode, potentially electing itself, if
	// explicitly enabled and there is only 1 node in the cluster already.
	if len(peerNodes) == 0 && len(peers) <= 1 {
		log.Infof("enabling single-node mode")
		config.EnableSingleNode = true
		config.DisableBootstrapAfterElect = false
	}
	config.SnapshotThreshold = 8

	// Create the snapshot store. This allows the Raft to truncate the log.
	snapshots, err := raft.NewFileSnapshotStore(store.raftDir, retainSnapshotCount, os.Stderr)
	if err != nil {
		return log.Errorf("file snapshot store: %s", err)
	}

	// Create the log store and stable store.
	logStore := NewRelationalStore()
	log.Debugf("raft: logStore=%+v", logStore)

	// Instantiate the Raft systems.
	if store.raft, err = raft.NewRaft(config, (*fsm)(store), logStore, logStore, snapshots, peerStore, transport); err != nil {
		return fmt.Errorf("error creating new raft: %s", err)
	}
	log.Infof("new raft created")

	return nil
}

// Join joins a node, located at addr, to this store. The node must be ready to
// respond to Raft communications at that address.
func (store *Store) Join(addr string) error {
	log.Infof("received join request for remote node as %s", addr)

	f := store.raft.AddPeer(addr)
	if f.Error() != nil {
		return f.Error()
	}
	log.Infof("node at %s joined successfully", addr)
	return nil
}

// genericCommand requests consensus for applying a single command.
// This is an internal orchestrator implementation
func (store *Store) genericCommand(op string, b []byte) error {
	if store.raft.State() != raft.Leader {
		return fmt.Errorf("not leader")
	}

	b, err := json.Marshal(&storeCommand{Op: op, Value: b})
	if err != nil {
		return err
	}

	f := store.raft.Apply(b, raftTimeout)
	if err = f.Error(); err != nil {
		return err
	}
	r := f.Response()
	log.Debugf("=====response: %+v", r)
	return nil
}
<commit_msg>aggressive snapshots<commit_after>package orcraft

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"strings"
	"time"

	"github.com/openark/golib/log"

	"github.com/hashicorp/raft"
)

const (
	retainSnapshotCount = 2
	raftTimeout         = 10 * time.Second
)

type Store struct {
	raftDir  string
	raftBind string

	raft *raft.Raft // The consensus mechanism

	applier CommandApplier
}

type storeCommand struct {
	Op    string `json:"op,omitempty"`
	Value []byte `json:"value,omitempty"`
}

// NewStore inits and returns a new store
func NewStore(raftDir string, raftBind string, applier CommandApplier) *Store {
	return &Store{
		raftDir:  raftDir,
		raftBind: raftBind,
		applier:  applier,
	}
}

// Open opens the store. If enableSingle is set, and there are no existing peers,
// then this node becomes the first node, and therefore leader, of the cluster.
func (store *Store) Open(peerNodes []string) error {
	// Setup Raft configuration.
	config := raft.DefaultConfig()
	config.SnapshotThreshold = 1
	config.SnapshotInterval = time.Minute

	// Setup Raft communication.
	addr, err := net.ResolveTCPAddr("tcp", store.raftBind)
	if err != nil {
		return err
	}
	log.Debugf("raft: addr=%+v", addr)
	transport, err := raft.NewTCPTransport(store.raftBind, addr, 3, 10*time.Second, os.Stderr)
	if err != nil {
		return err
	}
	log.Debugf("raft: transport=%+v", transport)

	peers := make([]string, 0, 10)
	for _, peerNode := range peerNodes {
		peerNode = strings.TrimSpace(peerNode)
		peers = raft.AddUniquePeer(peers, peerNode)
	}
	log.Debugf("raft: peers=%+v", peers)

	// Create peer storage.
	peerStore := &raft.StaticPeers{}
	if err := peerStore.SetPeers(peers); err != nil {
		return err
	}

	// Allow the node to enter single-mode, potentially electing itself, if
	// explicitly enabled and there is only 1 node in the cluster already.
	if len(peerNodes) == 0 && len(peers) <= 1 {
		log.Infof("enabling single-node mode")
		config.EnableSingleNode = true
		config.DisableBootstrapAfterElect = false
	}

	// Create the snapshot store. This allows the Raft to truncate the log.
	snapshots, err := raft.NewFileSnapshotStore(store.raftDir, retainSnapshotCount, os.Stderr)
	if err != nil {
		return log.Errorf("file snapshot store: %s", err)
	}

	// Create the log store and stable store.
	logStore := NewRelationalStore()
	log.Debugf("raft: logStore=%+v", logStore)

	// Instantiate the Raft systems.
	if store.raft, err = raft.NewRaft(config, (*fsm)(store), logStore, logStore, snapshots, peerStore, transport); err != nil {
		return fmt.Errorf("error creating new raft: %s", err)
	}
	log.Infof("new raft created")

	return nil
}

// Join joins a node, located at addr, to this store. The node must be ready to
// respond to Raft communications at that address.
func (store *Store) Join(addr string) error {
	log.Infof("received join request for remote node as %s", addr)

	f := store.raft.AddPeer(addr)
	if f.Error() != nil {
		return f.Error()
	}
	log.Infof("node at %s joined successfully", addr)
	return nil
}

// genericCommand requests consensus for applying a single command.
// This is an internal orchestrator implementation
func (store *Store) genericCommand(op string, b []byte) error {
	if store.raft.State() != raft.Leader {
		return fmt.Errorf("not leader")
	}

	b, err := json.Marshal(&storeCommand{Op: op, Value: b})
	if err != nil {
		return err
	}

	f := store.raft.Apply(b, raftTimeout)
	if err = f.Error(); err != nil {
		return err
	}
	r := f.Response()
	log.Debugf("=====response: %+v", r)
	return nil
}
<|endoftext|>