<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package snowman

import (
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow"
	"github.com/ava-labs/gecko/snow/consensus/snowball"
)

// TopologicalFactory implements Factory by returning a topological struct
type TopologicalFactory struct{}

// New implements Factory
func (TopologicalFactory) New() Consensus { return &Topological{} }

// Topological implements the Snowman interface by using a tree tracking the
// strongly preferred branch. This tree structure amortizes network polls to
// vote on more than just the next block.
type Topological struct {
	metrics

	ctx    *snow.Context
	params snowball.Parameters

	head   ids.ID
	blocks map[[32]byte]*snowmanBlock // ParentID -> Snowball instance
	tail   ids.ID
}

// Used to track the kahn topological sort status
type kahnNode struct {
	// inDegree is the number of children that haven't been processed yet. If
	// inDegree is 0, then this node is a leaf
	inDegree int
	// votes for all the children of this node, so far
	votes ids.Bag
}

// Used to track which children should receive votes
type votes struct {
	// parentID is the parent of all the votes provided in the votes bag
	parentID ids.ID
	// votes for all the children of the parent
	votes ids.Bag
}

// Initialize implements the Snowman interface
func (ts *Topological) Initialize(ctx *snow.Context, params snowball.Parameters, rootID ids.ID) {
	ts.ctx = ctx
	ts.params = params

	if err := ts.metrics.Initialize(ctx.Log, params.Namespace, params.Metrics); err != nil {
		ts.ctx.Log.Error("%s", err)
	}

	ts.head = rootID
	ts.blocks = map[[32]byte]*snowmanBlock{
		rootID.Key(): &snowmanBlock{
			sm: ts,
		},
	}
	ts.tail = rootID
}

// Parameters implements the Snowman interface
func (ts *Topological) Parameters() snowball.Parameters { return ts.params }

// Add implements the Snowman interface
func (ts *Topological) Add(blk Block) {
	parent := blk.Parent()
	parentID := parent.ID()
	parentKey := parentID.Key()

	blkID := blk.ID()
	blkBytes := blk.Bytes()

	// Notify anyone listening that this block was issued.
	ts.ctx.DecisionDispatcher.Issue(ts.ctx.ChainID, blkID, blkBytes)
	ts.ctx.ConsensusDispatcher.Issue(ts.ctx.ChainID, blkID, blkBytes)
	ts.metrics.Issued(blkID)

	parentNode, ok := ts.blocks[parentKey]
	if !ok {
		// If the ancestor is missing, this means the ancestor must have already
		// been pruned. Therefore, the dependent should be transitively
		// rejected.
		blk.Reject()

		// Notify anyone listening that this block was rejected.
		ts.ctx.DecisionDispatcher.Reject(ts.ctx.ChainID, blkID, blkBytes)
		ts.ctx.ConsensusDispatcher.Reject(ts.ctx.ChainID, blkID, blkBytes)
		ts.metrics.Rejected(blkID)
		return
	}

	parentNode.AddChild(blk)

	ts.blocks[blkID.Key()] = &snowmanBlock{
		sm:  ts,
		blk: blk,
	}

	// If we are extending the tail, this is the new tail
	if ts.tail.Equals(parentID) {
		ts.tail = blkID
	}
}

// Issued implements the Snowman interface
func (ts *Topological) Issued(blk Block) bool {
	// If the block is decided, then it must have been previously issued.
	if blk.Status().Decided() {
		return true
	}
	// If the block is in the map of current blocks, then the block was issued.
	_, ok := ts.blocks[blk.ID().Key()]
	return ok
}

// Preference implements the Snowman interface
func (ts *Topological) Preference() ids.ID { return ts.tail }

// RecordPoll implements the Snowman interface
// This performs Kahn’s algorithm.
// When a node is removed from the leaf queue, it is checked to see if the
// number of votes is >= alpha. If it is, then it is added to the vote stack.
// Once there are no nodes in the leaf queue. The vote stack is unwound and
// voted on. If a decision is made, then that choice is marked as accepted, and
// all alternative choices are marked as rejected.
// The complexity of this function is:
// Runtime = 3 * |live set| + |votes|
// Space = |live set| + |votes|
func (ts *Topological) RecordPoll(votes ids.Bag) {
	// Runtime = |live set| + |votes| ; Space = |live set| + |votes|
	kahnGraph, leaves := ts.calculateInDegree(votes)

	// Runtime = |live set| ; Space = |live set|
	voteStack := ts.pushVotes(kahnGraph, leaves)

	// Runtime = |live set| ; Space = Constant
	preferred := ts.vote(voteStack)

	// Runtime = |live set| ; Space = Constant
	ts.tail = ts.getPreferredDecendent(preferred)
}

// Finalized implements the Snowman interface
func (ts *Topological) Finalized() bool { return len(ts.blocks) == 1 }

// takes in a list of votes and sets up the topological ordering. Returns the
// reachable section of the graph annotated with the number of inbound edges and
// the non-transitively applied votes. Also returns the list of leaf nodes.
func (ts *Topological) calculateInDegree(
	votes ids.Bag) (map[[32]byte]kahnNode, []ids.ID) {
	kahns := make(map[[32]byte]kahnNode)
	leaves := ids.Set{}

	for _, vote := range votes.List() {
		voteNode, validVote := ts.blocks[vote.Key()]
		// If it is not found, then the vote is either for something rejected,
		// or something we haven't heard of yet.
		if validVote && voteNode.blk != nil && !voteNode.blk.Status().Decided() {
			parentID := voteNode.blk.Parent().ID()
			parentKey := parentID.Key()
			kahn, previouslySeen := kahns[parentKey]
			// Add this new vote to the current bag of votes
			kahn.votes.AddCount(vote, votes.Count(vote))
			kahns[parentKey] = kahn

			if !previouslySeen {
				// If I've never seen this node before, it is currently a leaf.
				leaves.Add(parentID)

				for n, e := ts.blocks[parentKey]; e; n, e = ts.blocks[parentKey] {
					if n.blk == nil || n.blk.Status().Decided() {
						break // Ensure that we haven't traversed off the tree
					}
					parentID := n.blk.Parent().ID()
					parentKey = parentID.Key()

					kahn := kahns[parentKey]
					kahn.inDegree++
					kahns[parentKey] = kahn

					if kahn.inDegree == 1 {
						// If I am transitively seeing this node for the first
						// time, it is no longer a leaf.
						leaves.Remove(parentID)
					} else {
						// If I have already traversed this branch, stop.
						break
					}
				}
			}
		}
	}

	return kahns, leaves.List()
}

// convert the tree into a branch of snowball instances with an alpha threshold
func (ts *Topological) pushVotes(
	kahnNodes map[[32]byte]kahnNode, leaves []ids.ID) []votes {
	voteStack := []votes(nil)
	for len(leaves) > 0 {
		newLeavesSize := len(leaves) - 1
		leaf := leaves[newLeavesSize]
		leaves = leaves[:newLeavesSize]

		leafKey := leaf.Key()
		kahn := kahnNodes[leafKey]

		if node, shouldVote := ts.blocks[leafKey]; shouldVote {
			if kahn.votes.Len() >= ts.params.Alpha {
				voteStack = append(voteStack, votes{
					parentID: leaf,
					votes:    kahn.votes,
				})
			}

			if node.blk == nil || node.blk.Status().Decided() {
				continue // Stop traversing once we pass into the decided frontier
			}

			parentID := node.blk.Parent().ID()
			parentKey := parentID.Key()
			if depNode, notPruned := kahnNodes[parentKey]; notPruned {
				// Remove one of the in-bound edges
				depNode.inDegree--
				// Push the votes to my parent
				depNode.votes.AddCount(leaf, kahn.votes.Len())
				kahnNodes[parentKey] = depNode

				if depNode.inDegree == 0 {
					// Once I have no in-bound edges, I'm a leaf
					leaves = append(leaves, parentID)
				}
			}
		}
	}
	return voteStack
}

func (ts *Topological) vote(voteStack []votes) ids.ID {
	if len(voteStack) == 0 {
		headKey := ts.head.Key()
		headNode := ts.blocks[headKey]
		headNode.shouldFalter = true

		ts.ctx.Log.Verbo("No progress was made on this vote even though we have %d pending blocks", len(ts.blocks)-1)
		return ts.tail
	}

	onTail := true
	tail := ts.head
	for len(voteStack) > 0 {
		newStackSize := len(voteStack) - 1
		voteGroup := voteStack[newStackSize]
		voteStack = voteStack[:newStackSize]

		voteParentKey := voteGroup.parentID.Key()
		parentNode, stillExists := ts.blocks[voteParentKey]
		if !stillExists {
			break
		}

		shouldTransFalter := parentNode.shouldFalter
		if parentNode.shouldFalter {
			parentNode.sb.RecordUnsuccessfulPoll()
			parentNode.shouldFalter = false
			ts.ctx.Log.Verbo("Reset confidence below %s", voteGroup.parentID)
		}
		parentNode.sb.RecordPoll(voteGroup.votes)

		// Only accept when you are finalized and the head.
		if parentNode.sb.Finalized() && ts.head.Equals(voteGroup.parentID) {
			ts.accept(parentNode)
			tail = parentNode.sb.Preference()
			delete(ts.blocks, voteParentKey)
		}

		// If this is the last id that got votes, default to the empty id. This
		// will cause all my children to be reset below.
		nextID := ids.ID{}
		if len(voteStack) > 0 {
			nextID = voteStack[newStackSize-1].parentID
		}

		onTail = onTail && nextID.Equals(parentNode.sb.Preference())
		if onTail {
			tail = nextID
		}

		// If there wasn't an alpha threshold on the branch (either on this vote
		// or a past transitive vote), I should falter now.
		for childIDBytes := range parentNode.children {
			if childID := ids.NewID(childIDBytes); shouldTransFalter || !childID.Equals(nextID) {
				if childNode, childExists := ts.blocks[childIDBytes]; childExists {
					// The existence check is needed in case the current node
					// was finalized. However, in this case, we still need to
					// check for the next id.
					ts.ctx.Log.Verbo("Defering confidence reset below %s with %d children. NextID: %s", childID, len(parentNode.children), nextID)
					childNode.shouldFalter = true
				}
			}
		}
	}
	return tail
}

// Get the preferred decendent of the provided block ID
func (ts *Topological) getPreferredDecendent(blkID ids.ID) ids.ID {
	// Traverse from the provided ID to the preferred child until there are no
	// children.
	for block := ts.blocks[blkID.Key()]; block.sb != nil; block = ts.blocks[blkID.Key()] {
		blkID = block.sb.Preference()
	}
	return blkID
}

func (ts *Topological) accept(n *snowmanBlock) {
	// Accept the preference, reject all transitive rejections
	pref := n.sb.Preference()

	rejects := []ids.ID(nil)
	for childIDBytes := range n.children {
		if childID := ids.NewID(childIDBytes); !childID.Equals(pref) {
			child := n.children[childIDBytes]
			child.Reject()

			bytes := child.Bytes()
			ts.ctx.DecisionDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
			ts.ctx.ConsensusDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
			ts.metrics.Rejected(childID)

			rejects = append(rejects, childID)
		}
	}
	ts.rejectTransitively(rejects...)

	ts.head = pref
	child := n.children[pref.Key()]
	ts.ctx.Log.Verbo("Accepting block with ID %s", child.ID())

	bytes := child.Bytes()
	ts.ctx.DecisionDispatcher.Accept(ts.ctx.ChainID, child.ID(), bytes)
	ts.ctx.ConsensusDispatcher.Accept(ts.ctx.ChainID, child.ID(), bytes)

	child.Accept()
	ts.metrics.Accepted(pref)
}

// Takes in a list of newly rejected ids and rejects everything that depends on
// them
func (ts *Topological) rejectTransitively(rejected ...ids.ID) {
	for len(rejected) > 0 {
		newRejectedSize := len(rejected) - 1
		rejectID := rejected[newRejectedSize]
		rejected = rejected[:newRejectedSize]

		rejectKey := rejectID.Key()
		rejectNode := ts.blocks[rejectKey]
		delete(ts.blocks, rejectKey)

		for childIDBytes, child := range rejectNode.children {
			childID := ids.NewID(childIDBytes)
			rejected = append(rejected, childID)
			child.Reject()

			bytes := child.Bytes()
			ts.ctx.DecisionDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
			ts.ctx.ConsensusDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
			ts.metrics.Rejected(childID)
		}
	}
}
<commit_msg>Improved comments in acceptance and rejectance<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package snowman

import (
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/snow"
	"github.com/ava-labs/gecko/snow/consensus/snowball"
)

// TopologicalFactory implements Factory by returning a topological struct
type TopologicalFactory struct{}

// New implements Factory
func (TopologicalFactory) New() Consensus { return &Topological{} }

// Topological implements the Snowman interface by using a tree tracking the
// strongly preferred branch. This tree structure amortizes network polls to
// vote on more than just the next block.
type Topological struct {
	metrics

	ctx    *snow.Context
	params snowball.Parameters

	head   ids.ID
	blocks map[[32]byte]*snowmanBlock // ParentID -> Snowball instance
	tail   ids.ID
}

// Used to track the kahn topological sort status
type kahnNode struct {
	// inDegree is the number of children that haven't been processed yet. If
	// inDegree is 0, then this node is a leaf
	inDegree int
	// votes for all the children of this node, so far
	votes ids.Bag
}

// Used to track which children should receive votes
type votes struct {
	// parentID is the parent of all the votes provided in the votes bag
	parentID ids.ID
	// votes for all the children of the parent
	votes ids.Bag
}

// Initialize implements the Snowman interface
func (ts *Topological) Initialize(ctx *snow.Context, params snowball.Parameters, rootID ids.ID) {
	ts.ctx = ctx
	ts.params = params

	if err := ts.metrics.Initialize(ctx.Log, params.Namespace, params.Metrics); err != nil {
		ts.ctx.Log.Error("%s", err)
	}

	ts.head = rootID
	ts.blocks = map[[32]byte]*snowmanBlock{
		rootID.Key(): &snowmanBlock{
			sm: ts,
		},
	}
	ts.tail = rootID
}

// Parameters implements the Snowman interface
func (ts *Topological) Parameters() snowball.Parameters { return ts.params }

// Add implements the Snowman interface
func (ts *Topological) Add(blk Block) {
	parent := blk.Parent()
	parentID := parent.ID()
	parentKey := parentID.Key()

	blkID := blk.ID()
	blkBytes := blk.Bytes()

	// Notify anyone listening that this block was issued.
	ts.ctx.DecisionDispatcher.Issue(ts.ctx.ChainID, blkID, blkBytes)
	ts.ctx.ConsensusDispatcher.Issue(ts.ctx.ChainID, blkID, blkBytes)
	ts.metrics.Issued(blkID)

	parentNode, ok := ts.blocks[parentKey]
	if !ok {
		// If the ancestor is missing, this means the ancestor must have already
		// been pruned. Therefore, the dependent should be transitively
		// rejected.
		blk.Reject()

		// Notify anyone listening that this block was rejected.
		ts.ctx.DecisionDispatcher.Reject(ts.ctx.ChainID, blkID, blkBytes)
		ts.ctx.ConsensusDispatcher.Reject(ts.ctx.ChainID, blkID, blkBytes)
		ts.metrics.Rejected(blkID)
		return
	}

	parentNode.AddChild(blk)

	ts.blocks[blkID.Key()] = &snowmanBlock{
		sm:  ts,
		blk: blk,
	}

	// If we are extending the tail, this is the new tail
	if ts.tail.Equals(parentID) {
		ts.tail = blkID
	}
}

// Issued implements the Snowman interface
func (ts *Topological) Issued(blk Block) bool {
	// If the block is decided, then it must have been previously issued.
	if blk.Status().Decided() {
		return true
	}
	// If the block is in the map of current blocks, then the block was issued.
	_, ok := ts.blocks[blk.ID().Key()]
	return ok
}

// Preference implements the Snowman interface
func (ts *Topological) Preference() ids.ID { return ts.tail }

// RecordPoll implements the Snowman interface
// This performs Kahn’s algorithm.
// When a node is removed from the leaf queue, it is checked to see if the
// number of votes is >= alpha. If it is, then it is added to the vote stack.
// Once there are no nodes in the leaf queue. The vote stack is unwound and
// voted on. If a decision is made, then that choice is marked as accepted, and
// all alternative choices are marked as rejected.
// The complexity of this function is:
// Runtime = 3 * |live set| + |votes|
// Space = |live set| + |votes|
func (ts *Topological) RecordPoll(votes ids.Bag) {
	// Runtime = |live set| + |votes| ; Space = |live set| + |votes|
	kahnGraph, leaves := ts.calculateInDegree(votes)

	// Runtime = |live set| ; Space = |live set|
	voteStack := ts.pushVotes(kahnGraph, leaves)

	// Runtime = |live set| ; Space = Constant
	preferred := ts.vote(voteStack)

	// Runtime = |live set| ; Space = Constant
	ts.tail = ts.getPreferredDecendent(preferred)
}

// Finalized implements the Snowman interface
func (ts *Topological) Finalized() bool { return len(ts.blocks) == 1 }

// takes in a list of votes and sets up the topological ordering. Returns the
// reachable section of the graph annotated with the number of inbound edges and
// the non-transitively applied votes. Also returns the list of leaf nodes.
func (ts *Topological) calculateInDegree(
	votes ids.Bag) (map[[32]byte]kahnNode, []ids.ID) {
	kahns := make(map[[32]byte]kahnNode)
	leaves := ids.Set{}

	for _, vote := range votes.List() {
		voteNode, validVote := ts.blocks[vote.Key()]
		// If it is not found, then the vote is either for something rejected,
		// or something we haven't heard of yet.
		if validVote && voteNode.blk != nil && !voteNode.blk.Status().Decided() {
			parentID := voteNode.blk.Parent().ID()
			parentKey := parentID.Key()
			kahn, previouslySeen := kahns[parentKey]
			// Add this new vote to the current bag of votes
			kahn.votes.AddCount(vote, votes.Count(vote))
			kahns[parentKey] = kahn

			if !previouslySeen {
				// If I've never seen this node before, it is currently a leaf.
				leaves.Add(parentID)

				for n, e := ts.blocks[parentKey]; e; n, e = ts.blocks[parentKey] {
					if n.blk == nil || n.blk.Status().Decided() {
						break // Ensure that we haven't traversed off the tree
					}
					parentID := n.blk.Parent().ID()
					parentKey = parentID.Key()

					kahn := kahns[parentKey]
					kahn.inDegree++
					kahns[parentKey] = kahn

					if kahn.inDegree == 1 {
						// If I am transitively seeing this node for the first
						// time, it is no longer a leaf.
						leaves.Remove(parentID)
					} else {
						// If I have already traversed this branch, stop.
						break
					}
				}
			}
		}
	}

	return kahns, leaves.List()
}

// convert the tree into a branch of snowball instances with an alpha threshold
func (ts *Topological) pushVotes(
	kahnNodes map[[32]byte]kahnNode, leaves []ids.ID) []votes {
	voteStack := []votes(nil)
	for len(leaves) > 0 {
		newLeavesSize := len(leaves) - 1
		leaf := leaves[newLeavesSize]
		leaves = leaves[:newLeavesSize]

		leafKey := leaf.Key()
		kahn := kahnNodes[leafKey]

		if node, shouldVote := ts.blocks[leafKey]; shouldVote {
			if kahn.votes.Len() >= ts.params.Alpha {
				voteStack = append(voteStack, votes{
					parentID: leaf,
					votes:    kahn.votes,
				})
			}

			if node.blk == nil || node.blk.Status().Decided() {
				continue // Stop traversing once we pass into the decided frontier
			}

			parentID := node.blk.Parent().ID()
			parentKey := parentID.Key()
			if depNode, notPruned := kahnNodes[parentKey]; notPruned {
				// Remove one of the in-bound edges
				depNode.inDegree--
				// Push the votes to my parent
				depNode.votes.AddCount(leaf, kahn.votes.Len())
				kahnNodes[parentKey] = depNode

				if depNode.inDegree == 0 {
					// Once I have no in-bound edges, I'm a leaf
					leaves = append(leaves, parentID)
				}
			}
		}
	}
	return voteStack
}

func (ts *Topological) vote(voteStack []votes) ids.ID {
	if len(voteStack) == 0 {
		headKey := ts.head.Key()
		headNode := ts.blocks[headKey]
		headNode.shouldFalter = true

		ts.ctx.Log.Verbo("No progress was made on this vote even though we have %d pending blocks", len(ts.blocks)-1)
		return ts.tail
	}

	onTail := true
	tail := ts.head
	for len(voteStack) > 0 {
		newStackSize := len(voteStack) - 1
		voteGroup := voteStack[newStackSize]
		voteStack = voteStack[:newStackSize]

		voteParentKey := voteGroup.parentID.Key()
		parentNode, stillExists := ts.blocks[voteParentKey]
		if !stillExists {
			break
		}

		shouldTransFalter := parentNode.shouldFalter
		if parentNode.shouldFalter {
			parentNode.sb.RecordUnsuccessfulPoll()
			parentNode.shouldFalter = false
			ts.ctx.Log.Verbo("Reset confidence below %s", voteGroup.parentID)
		}
		parentNode.sb.RecordPoll(voteGroup.votes)

		// Only accept when you are finalized and the head.
		if parentNode.sb.Finalized() && ts.head.Equals(voteGroup.parentID) {
			ts.accept(parentNode)
			tail = parentNode.sb.Preference()
			delete(ts.blocks, voteParentKey)
		}

		// If this is the last id that got votes, default to the empty id. This
		// will cause all my children to be reset below.
		nextID := ids.ID{}
		if len(voteStack) > 0 {
			nextID = voteStack[newStackSize-1].parentID
		}

		onTail = onTail && nextID.Equals(parentNode.sb.Preference())
		if onTail {
			tail = nextID
		}

		// If there wasn't an alpha threshold on the branch (either on this vote
		// or a past transitive vote), I should falter now.
		for childIDBytes := range parentNode.children {
			if childID := ids.NewID(childIDBytes); shouldTransFalter || !childID.Equals(nextID) {
				if childNode, childExists := ts.blocks[childIDBytes]; childExists {
					// The existence check is needed in case the current node
					// was finalized. However, in this case, we still need to
					// check for the next id.
					ts.ctx.Log.Verbo("Defering confidence reset below %s with %d children. NextID: %s", childID, len(parentNode.children), nextID)
					childNode.shouldFalter = true
				}
			}
		}
	}
	return tail
}

// Get the preferred decendent of the provided block ID
func (ts *Topological) getPreferredDecendent(blkID ids.ID) ids.ID {
	// Traverse from the provided ID to the preferred child until there are no
	// children.
	for block := ts.blocks[blkID.Key()]; block.sb != nil; block = ts.blocks[blkID.Key()] {
		blkID = block.sb.Preference()
	}
	return blkID
}

func (ts *Topological) accept(n *snowmanBlock) {
	// We are finalizing the block's child, so we need to get the preference
	pref := n.sb.Preference()

	ts.ctx.Log.Verbo("Accepting block with ID %s", pref)

	// Get the child and accept it
	child := n.children[pref.Key()]
	child.Accept()

	// Notify anyone listening that this block was accepted.
	bytes := child.Bytes()
	ts.ctx.DecisionDispatcher.Accept(ts.ctx.ChainID, pref, bytes)
	ts.ctx.ConsensusDispatcher.Accept(ts.ctx.ChainID, pref, bytes)
	ts.metrics.Accepted(pref)

	// Because this is the newest accepted block, this is the new head.
	ts.head = pref

	// Because ts.blocks contains the last accepted block, we don't delete the
	// block from the blocks map here.

	rejects := []ids.ID(nil)
	for childIDKey, child := range n.children {
		childID := ids.NewID(childIDKey)
		if childID.Equals(pref) {
			// don't reject the block we just accepted
			continue
		}

		child.Reject()

		// Notify anyone listening that this block was rejected.
		bytes := child.Bytes()
		ts.ctx.DecisionDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
		ts.ctx.ConsensusDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
		ts.metrics.Rejected(childID)

		// Track which blocks have been directly rejected
		rejects = append(rejects, childID)
	}

	// reject all the decendants of the blocks we just rejected
	ts.rejectTransitively(rejects)
}

// Takes in a list of rejected ids and rejects all decendants of these IDs
func (ts *Topological) rejectTransitively(rejected []ids.ID) {
	for len(rejected) > 0 {
		// pop the rejected ID off the queue
		newRejectedSize := len(rejected) - 1
		rejectedID := rejected[newRejectedSize]
		rejected = rejected[:newRejectedSize]

		// get the rejected node, and remove it from the tree
		rejectedKey := rejectedID.Key()
		rejectedNode := ts.blocks[rejectedKey]
		delete(ts.blocks, rejectedKey)

		for childIDKey, child := range rejectedNode.children {
			childID := ids.NewID(childIDKey)

			child.Reject()

			// Notify anyone listening that this block was rejected.
			bytes := child.Bytes()
			ts.ctx.DecisionDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
			ts.ctx.ConsensusDispatcher.Reject(ts.ctx.ChainID, childID, bytes)
			ts.metrics.Rejected(childID)

			// add the newly rejected block to the end of the queue
			rejected = append(rejected, childID)
		}
	}
}
<|endoftext|>


<commit_before>package sqlite3

import "testing"
import "time"

func (db *Database) createTestTables(t *testing.T, tables... *Table) {
	for _, table := range tables {
		table.Drop(db)
		table.Create(db)
		if c, _ := table.Rows(db); c != 0 {
			t.Fatalf("%v already contains data", table.Name)
		}
	}
}

func (db *Database) createTestData(t *testing.T, repeats int) {
	db.runQuery(t, "PRAGMA synchronous=OFF")
	for i := 0; i < repeats; i++ {
		if i % 2 == 0 {
			db.runQuery(t, "INSERT INTO foo values (?, 'holy moly')", i)
			db.runQuery(t, "INSERT INTO bar values (?, ?)", i, TwoItems{ "holy moly", "guacomole" })
		} else {
			db.runQuery(t, "INSERT INTO foo values (?, 'guacomole')", i)
			db.runQuery(t, "INSERT INTO bar values (?, ?)", i, TwoItems{ "guacomole", "holy moly" })
		}
	}
	db.runQuery(t, "PRAGMA synchronous=NORMAL")
}

func TestTransfers(t *testing.T) {
	TransientSession(func(source *Database) {
		source.createTestTables(t, FOO, BAR)
		source.createTestData(t, 1000)
		Session("target.db", func(target *Database) {
			t.Logf("Database opened: %v [flags: %v]", target.Filename, int(target.Flags))
			target.createTestTables(t, FOO, BAR)
			fatalOnError(t, target.Load(source, "main"), "loading from %v[%]", source.Filename, "main")
			for _, table := range []*Table{ FOO, BAR } {
				i, _ := table.Rows(target)
				j, _ := table.Rows(source)
				if i != j {
					t.Fatalf("failed to load data for table %v", table.Name)
				}
			}

			Session("backup.db", func(backup *Database) {
				t.Logf("Database opened: %v [flags: %v]", backup.Filename, int(backup.Flags))
				backup.createTestTables(t, FOO, BAR)
				fatalOnError(t, target.Save(backup, "main"), "saving to %v[%v]", backup.Filename, "main")
				for _, table := range []*Table{ FOO, BAR } {
					i, _ := table.Rows(target)
					j, _ := table.Rows(backup)
					if i != j {
						t.Fatalf("failed to load data for table %v", table.Name)
					}
				}
			})
		})
	})
}

func (r *Reporter) finished(t *testing.T) (finished bool) {
	report := <- (*r)
	if report != nil {
		switch e := report.Error.(type) {
		case Errno:		if e != DONE { t.Fatalf("Backup error %v", e) }
//		case nil:		t.Logf("Backup still has %v pages of %v to copy to %v", report.Remaining, report.PageCount, report.Target)
		}
	}
	return closed(*r)
}

func TestBackup(t *testing.T) {
	var messages	int

	Session("test.db", func(db *Database) {
		db.createTestTables(t, FOO, BAR)
		db.createTestData(t, 1000)

		if sync_reporter, e := db.Backup(BackupParameters{Target: "sync.db", PagesPerStep: 3, QueueLength: 1}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !sync_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v synchronous messages and took %vns", db.Filename, messages, time.Nanoseconds() - d)
		}

		if sync_reporter, e := db.Backup(BackupParameters{Target: "sync.db", PagesPerStep: 3, QueueLength: 1, Interval: 100000}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !sync_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v synchronous messages and took %vns with interval %v", db.Filename, messages, time.Nanoseconds() - d, 100000)
		}

		if async_reporter, e := db.Backup(BackupParameters{Target: "async.db", PagesPerStep: 3, QueueLength: 8}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !async_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v asynchronous messages and took %vns", db.Filename, messages, time.Nanoseconds() - d)
		}

		if async_reporter, e := db.Backup(BackupParameters{Target: "async.db", PagesPerStep: 3, QueueLength: 8}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !async_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v asynchronous messages and took %vns with interval %v", db.Filename, messages, time.Nanoseconds() - d, 100000)
		}
	})
}<commit_msg>Release has caught up.  Revert the API downgrade.<commit_after>package sqlite3

import "testing"
import "time"

func (db *Database) createTestTables(t *testing.T, tables... *Table) {
	for _, table := range tables {
		table.Drop(db)
		table.Create(db)
		if c, _ := table.Rows(db); c != 0 {
			t.Fatalf("%v already contains data", table.Name)
		}
	}
}

func (db *Database) createTestData(t *testing.T, repeats int) {
	db.runQuery(t, "PRAGMA synchronous=OFF")
	for i := 0; i < repeats; i++ {
		if i % 2 == 0 {
			db.runQuery(t, "INSERT INTO foo values (?, 'holy moly')", i)
			db.runQuery(t, "INSERT INTO bar values (?, ?)", i, TwoItems{ "holy moly", "guacomole" })
		} else {
			db.runQuery(t, "INSERT INTO foo values (?, 'guacomole')", i)
			db.runQuery(t, "INSERT INTO bar values (?, ?)", i, TwoItems{ "guacomole", "holy moly" })
		}
	}
	db.runQuery(t, "PRAGMA synchronous=NORMAL")
}

func TestTransfers(t *testing.T) {
	TransientSession(func(source *Database) {
		source.createTestTables(t, FOO, BAR)
		source.createTestData(t, 1000)
		Session("target.db", func(target *Database) {
			t.Logf("Database opened: %v [flags: %v]", target.Filename, int(target.Flags))
			target.createTestTables(t, FOO, BAR)
			fatalOnError(t, target.Load(source, "main"), "loading from %v[%]", source.Filename, "main")
			for _, table := range []*Table{ FOO, BAR } {
				i, _ := table.Rows(target)
				j, _ := table.Rows(source)
				if i != j {
					t.Fatalf("failed to load data for table %v", table.Name)
				}
			}

			Session("backup.db", func(backup *Database) {
				t.Logf("Database opened: %v [flags: %v]", backup.Filename, int(backup.Flags))
				backup.createTestTables(t, FOO, BAR)
				fatalOnError(t, target.Save(backup, "main"), "saving to %v[%v]", backup.Filename, "main")
				for _, table := range []*Table{ FOO, BAR } {
					i, _ := table.Rows(target)
					j, _ := table.Rows(backup)
					if i != j {
						t.Fatalf("failed to load data for table %v", table.Name)
					}
				}
			})
		})
	})
}

func (r *Reporter) finished(t *testing.T) bool {
	report, ok := <- (*r)
	if report != nil {
		switch e := report.Error.(type) {
		case Errno:		if e != DONE { t.Fatalf("Backup error %v", e) }
//		case nil:		t.Logf("Backup still has %v pages of %v to copy to %v", report.Remaining, report.PageCount, report.Target)
		}
	}
	return !ok
}

func TestBackup(t *testing.T) {
	var messages	int

	Session("test.db", func(db *Database) {
		db.createTestTables(t, FOO, BAR)
		db.createTestData(t, 1000)

		if sync_reporter, e := db.Backup(BackupParameters{Target: "sync.db", PagesPerStep: 3, QueueLength: 1}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !sync_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v synchronous messages and took %vns", db.Filename, messages, time.Nanoseconds() - d)
		}

		if sync_reporter, e := db.Backup(BackupParameters{Target: "sync.db", PagesPerStep: 3, QueueLength: 1, Interval: 100000}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !sync_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v synchronous messages and took %vns with interval %v", db.Filename, messages, time.Nanoseconds() - d, 100000)
		}

		if async_reporter, e := db.Backup(BackupParameters{Target: "async.db", PagesPerStep: 3, QueueLength: 8}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !async_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v asynchronous messages and took %vns", db.Filename, messages, time.Nanoseconds() - d)
		}

		if async_reporter, e := db.Backup(BackupParameters{Target: "async.db", PagesPerStep: 3, QueueLength: 8}); e == nil {
			d := time.Nanoseconds()
			for messages = 0; !async_reporter.finished(t); messages++ {}
			t.Logf("backup of %v generated %v asynchronous messages and took %vns with interval %v", db.Filename, messages, time.Nanoseconds() - d, 100000)
		}
	})
}<|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.

package main

import (
	"bytes"
	"text/template"

	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/golang/protobuf/protoc-gen-go/generator"
)

const netrpcPluginName = "netrpc"

// netrpcPlugin produce the Service interface.
type netrpcPlugin struct {
	*generator.Generator
}

// Name returns the name of the plugin.
func (p *netrpcPlugin) Name() string { return netrpcPluginName }

// Init is called once after data structures are built but before
// code generation begins.
func (p *netrpcPlugin) Init(g *generator.Generator) {
	p.Generator = g
}

// Generate produces the code generated by the plugin for this file.
func (p *netrpcPlugin) GenerateImports(file *generator.FileDescriptor) {
	if len(file.Service) > 0 {
		p.P(`import "io"`)
		p.P(`import "log"`)
		p.P(`import "net"`)
		p.P(`import "net/rpc"`)
		p.P(`import "time"`)
	}
}

// Generate generates the Service interface.
// rpc service can't handle other proto message!!!
func (p *netrpcPlugin) Generate(file *generator.FileDescriptor) {
	for _, svc := range file.Service {
		p.genServiceInterface(file, svc)
		p.genServiceServer(file, svc)
		p.genServiceClient(file, svc)
	}
}

func (p *netrpcPlugin) genServiceInterface(
	file *generator.FileDescriptor,
	svc *descriptor.ServiceDescriptorProto,
) {
	const serviceInterfaceTmpl = `
type {{.ServiceName}} interface {
	{{.CallMethodList}}
}
`
	const callMethodTmpl = `
{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}) error`

	// gen call method list
	var callMethodList string
	for _, m := range svc.Method {
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(callMethodTmpl))
		t.Execute(out, &struct{ ServiceName, MethodName, ArgsType, ReplyType string }{
			ServiceName: generator.CamelCase(svc.GetName()),
			MethodName:  generator.CamelCase(m.GetName()),
			ArgsType:    p.TypeName(p.ObjectNamed(m.GetInputType())),
			ReplyType:   p.TypeName(p.ObjectNamed(m.GetOutputType())),
		})
		callMethodList += out.String()

		p.RecordTypeUse(m.GetInputType())
		p.RecordTypeUse(m.GetOutputType())
	}

	// gen all interface code
	{
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(serviceInterfaceTmpl))
		t.Execute(out, &struct{ ServiceName, CallMethodList string }{
			ServiceName:    generator.CamelCase(svc.GetName()),
			CallMethodList: callMethodList,
		})
		p.P(out.String())
	}
}

func (p *netrpcPlugin) genServiceServer(
	file *generator.FileDescriptor,
	svc *descriptor.ServiceDescriptorProto,
) {
	const serviceHelperFunTmpl = `
// Accept{{.ServiceName}}Client accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func Accept{{.ServiceName}}Client(lis net.Listener, x {{.ServiceName}}) {
	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		log.Fatal(err)
	}

	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatalf("lis.Accept(): %v\n", err)
		}
		go srv.ServeConn(conn)
	}
}

// Register{{.ServiceName}} publish the given {{.ServiceName}} implementation on the server.
func Register{{.ServiceName}}(srv *rpc.Server, x {{.ServiceName}}) error {
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		return err
	}
	return nil
}

// New{{.ServiceName}}Server returns a new {{.ServiceName}} Server.
func New{{.ServiceName}}Server(x {{.ServiceName}}) *rpc.Server {
	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		log.Fatal(err)
	}
	return srv
}

// ListenAndServe{{.ServiceName}} listen announces on the local network address laddr
// and serves the given {{.ServiceName}} implementation.
func ListenAndServe{{.ServiceName}}(network, addr string, x {{.ServiceName}}) error {
	lis, err := net.Listen(network, addr)
	if err != nil {
		return err
	}
	defer lis.Close()

	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		return err
	}

	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatalf("lis.Accept(): %v\n", err)
		}
		go srv.ServeConn(conn)
	}
}
`
	{
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(serviceHelperFunTmpl))
		t.Execute(out, &struct{ PackageName, ServiceName, ServiceRegisterName string }{
			PackageName: file.GetPackage(),
			ServiceName: generator.CamelCase(svc.GetName()),
			ServiceRegisterName: p.makeServiceRegisterName(
				file, file.GetPackage(), generator.CamelCase(svc.GetName()),
			),
		})
		p.P(out.String())
	}
}

func (p *netrpcPlugin) genServiceClient(
	file *generator.FileDescriptor,
	svc *descriptor.ServiceDescriptorProto,
) {
	const clientHelperFuncTmpl = `
type {{.ServiceName}}Client struct {
	*rpc.Client
}

// New{{.ServiceName}}Client returns a {{.ServiceName}} stub to handle
// requests to the set of {{.ServiceName}} at the other end of the connection.
func New{{.ServiceName}}Client(conn io.ReadWriteCloser) (*{{.ServiceName}}Client) {
	c := rpc.NewClient(conn)
	return &{{.ServiceName}}Client{c}
}

{{.MethodList}}

// Dial{{.ServiceName}} connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}(network, addr string) (*{{.ServiceName}}Client, error) {
	c, err := rpc.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	return &{{.ServiceName}}Client{c}, nil
}

// Dial{{.ServiceName}}Timeout connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}Timeout(network, addr string, timeout time.Duration) (*{{.ServiceName}}Client, error) {
	conn, err := net.DialTimeout(network, addr, timeout)
	if err != nil {
		return nil, err
	}
	return &{{.ServiceName}}Client{rpc.NewClient(conn)}, nil
}
`
	const clientMethodTmpl = `
func (c *{{.ServiceName}}Client) {{.MethodName}}(in *{{.ArgsType}}) (out *{{.ReplyType}}, err error) {
	if in == nil {
		in = new({{.ArgsType}})
	}
	type Validator interface {
		Validate() error
	}
	if x, ok := proto.Message(in).(Validator); ok {
		if err := x.Validate(); err != nil {
			return nil, err
		}
	}
	out = new({{.ReplyType}})
	if err = c.Call("{{.ServiceRegisterName}}.{{.MethodName}}", in, out); err != nil {
		return nil, err
	}
	if x, ok := proto.Message(out).(Validator); ok {
		if err := x.Validate(); err != nil {
			return out, err
		}
	}
	return out, nil
}

func (c *{{.ServiceName}}Client) Async{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}, done chan *rpc.Call) *rpc.Call {
	if in == nil {
		in = new({{.ArgsType}})
	}
	return c.Go(
		"{{.ServiceRegisterName}}.{{.MethodName}}",
		in, out,
		done,
	)
}
`

	// gen client method list
	var methodList string
	for _, m := range svc.Method {
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(clientMethodTmpl))
		t.Execute(out, &struct{ ServiceName, ServiceRegisterName, MethodName, ArgsType, ReplyType string }{
			ServiceName: generator.CamelCase(svc.GetName()),
			ServiceRegisterName: p.makeServiceRegisterName(
				file, file.GetPackage(), generator.CamelCase(svc.GetName()),
			),
			MethodName: generator.CamelCase(m.GetName()),
			ArgsType:   p.TypeName(p.ObjectNamed(m.GetInputType())),
			ReplyType:  p.TypeName(p.ObjectNamed(m.GetOutputType())),
		})
		methodList += out.String()
	}

	// gen all client code
	{
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(clientHelperFuncTmpl))
		t.Execute(out, &struct{ PackageName, ServiceName, MethodList string }{
			PackageName: file.GetPackage(),
			ServiceName: generator.CamelCase(svc.GetName()),
			MethodList:  methodList,
		})
		p.P(out.String())
	}
}

func (p *netrpcPlugin) makeServiceRegisterName(
	file *generator.FileDescriptor,
	packageName, serviceName string,
) string {
	return packageName + "." + serviceName
}

func init() {
	generator.RegisterPlugin(new(netrpcPlugin))
}
<commit_msg>protoc-gen-stdrpc: generate missing Serve{{.ServiceName}} function<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.

package main

import (
	"bytes"
	"text/template"

	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/golang/protobuf/protoc-gen-go/generator"
)

const netrpcPluginName = "netrpc"

// netrpcPlugin produce the Service interface.
type netrpcPlugin struct {
	*generator.Generator
}

// Name returns the name of the plugin.
func (p *netrpcPlugin) Name() string { return netrpcPluginName }

// Init is called once after data structures are built but before
// code generation begins.
func (p *netrpcPlugin) Init(g *generator.Generator) {
	p.Generator = g
}

// Generate produces the code generated by the plugin for this file.
func (p *netrpcPlugin) GenerateImports(file *generator.FileDescriptor) {
	if len(file.Service) > 0 {
		p.P(`import "io"`)
		p.P(`import "log"`)
		p.P(`import "net"`)
		p.P(`import "net/rpc"`)
		p.P(`import "time"`)
	}
}

// Generate generates the Service interface.
// rpc service can't handle other proto message!!!
func (p *netrpcPlugin) Generate(file *generator.FileDescriptor) {
	for _, svc := range file.Service {
		p.genServiceInterface(file, svc)
		p.genServiceServer(file, svc)
		p.genServiceClient(file, svc)
	}
}

func (p *netrpcPlugin) genServiceInterface(
	file *generator.FileDescriptor,
	svc *descriptor.ServiceDescriptorProto,
) {
	const serviceInterfaceTmpl = `
type {{.ServiceName}} interface {
	{{.CallMethodList}}
}
`
	const callMethodTmpl = `
{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}) error`

	// gen call method list
	var callMethodList string
	for _, m := range svc.Method {
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(callMethodTmpl))
		t.Execute(out, &struct{ ServiceName, MethodName, ArgsType, ReplyType string }{
			ServiceName: generator.CamelCase(svc.GetName()),
			MethodName:  generator.CamelCase(m.GetName()),
			ArgsType:    p.TypeName(p.ObjectNamed(m.GetInputType())),
			ReplyType:   p.TypeName(p.ObjectNamed(m.GetOutputType())),
		})
		callMethodList += out.String()

		p.RecordTypeUse(m.GetInputType())
		p.RecordTypeUse(m.GetOutputType())
	}

	// gen all interface code
	{
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(serviceInterfaceTmpl))
		t.Execute(out, &struct{ ServiceName, CallMethodList string }{
			ServiceName:    generator.CamelCase(svc.GetName()),
			CallMethodList: callMethodList,
		})
		p.P(out.String())
	}
}

func (p *netrpcPlugin) genServiceServer(
	file *generator.FileDescriptor,
	svc *descriptor.ServiceDescriptorProto,
) {
	const serviceHelperFunTmpl = `
// Accept{{.ServiceName}}Client accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func Accept{{.ServiceName}}Client(lis net.Listener, x {{.ServiceName}}) {
	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		log.Fatal(err)
	}

	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatalf("lis.Accept(): %v\n", err)
		}
		go srv.ServeConn(conn)
	}
}

// Register{{.ServiceName}} publish the given {{.ServiceName}} implementation on the server.
func Register{{.ServiceName}}(srv *rpc.Server, x {{.ServiceName}}) error {
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		return err
	}
	return nil
}

// New{{.ServiceName}}Server returns a new {{.ServiceName}} Server.
func New{{.ServiceName}}Server(x {{.ServiceName}}) *rpc.Server {
	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		log.Fatal(err)
	}
	return srv
}

// ListenAndServe{{.ServiceName}} listen announces on the local network address laddr
// and serves the given {{.ServiceName}} implementation.
func ListenAndServe{{.ServiceName}}(network, addr string, x {{.ServiceName}}) error {
	lis, err := net.Listen(network, addr)
	if err != nil {
		return err
	}
	defer lis.Close()

	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		return err
	}

	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatalf("lis.Accept(): %v\n", err)
		}
		go srv.ServeConn(conn)
	}
}

// Serve{{.ServiceName}} serves the given {{.ServiceName}} implementation.
func Serve{{.ServiceName}}(conn io.ReadWriteCloser, x {{.ServiceName}}) {
	srv := rpc.NewServer()
	if err := srv.RegisterName("{{.ServiceRegisterName}}", x); err != nil {
		log.Fatal(err)
	}
	srv.ServeConn(conn)
}
`
	{
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(serviceHelperFunTmpl))
		t.Execute(out, &struct{ PackageName, ServiceName, ServiceRegisterName string }{
			PackageName: file.GetPackage(),
			ServiceName: generator.CamelCase(svc.GetName()),
			ServiceRegisterName: p.makeServiceRegisterName(
				file, file.GetPackage(), generator.CamelCase(svc.GetName()),
			),
		})
		p.P(out.String())
	}
}

func (p *netrpcPlugin) genServiceClient(
	file *generator.FileDescriptor,
	svc *descriptor.ServiceDescriptorProto,
) {
	const clientHelperFuncTmpl = `
type {{.ServiceName}}Client struct {
	*rpc.Client
}

// New{{.ServiceName}}Client returns a {{.ServiceName}} stub to handle
// requests to the set of {{.ServiceName}} at the other end of the connection.
func New{{.ServiceName}}Client(conn io.ReadWriteCloser) (*{{.ServiceName}}Client) {
	c := rpc.NewClient(conn)
	return &{{.ServiceName}}Client{c}
}

{{.MethodList}}

// Dial{{.ServiceName}} connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}(network, addr string) (*{{.ServiceName}}Client, error) {
	c, err := rpc.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	return &{{.ServiceName}}Client{c}, nil
}

// Dial{{.ServiceName}}Timeout connects to an {{.ServiceName}} at the specified network address.
func Dial{{.ServiceName}}Timeout(network, addr string, timeout time.Duration) (*{{.ServiceName}}Client, error) {
	conn, err := net.DialTimeout(network, addr, timeout)
	if err != nil {
		return nil, err
	}
	return &{{.ServiceName}}Client{rpc.NewClient(conn)}, nil
}
`
	const clientMethodTmpl = `
func (c *{{.ServiceName}}Client) {{.MethodName}}(in *{{.ArgsType}}) (out *{{.ReplyType}}, err error) {
	if in == nil {
		in = new({{.ArgsType}})
	}
	type Validator interface {
		Validate() error
	}
	if x, ok := proto.Message(in).(Validator); ok {
		if err := x.Validate(); err != nil {
			return nil, err
		}
	}
	out = new({{.ReplyType}})
	if err = c.Call("{{.ServiceRegisterName}}.{{.MethodName}}", in, out); err != nil {
		return nil, err
	}
	if x, ok := proto.Message(out).(Validator); ok {
		if err := x.Validate(); err != nil {
			return out, err
		}
	}
	return out, nil
}

func (c *{{.ServiceName}}Client) Async{{.MethodName}}(in *{{.ArgsType}}, out *{{.ReplyType}}, done chan *rpc.Call) *rpc.Call {
	if in == nil {
		in = new({{.ArgsType}})
	}
	return c.Go(
		"{{.ServiceRegisterName}}.{{.MethodName}}",
		in, out,
		done,
	)
}
`

	// gen client method list
	var methodList string
	for _, m := range svc.Method {
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(clientMethodTmpl))
		t.Execute(out, &struct{ ServiceName, ServiceRegisterName, MethodName, ArgsType, ReplyType string }{
			ServiceName: generator.CamelCase(svc.GetName()),
			ServiceRegisterName: p.makeServiceRegisterName(
				file, file.GetPackage(), generator.CamelCase(svc.GetName()),
			),
			MethodName: generator.CamelCase(m.GetName()),
			ArgsType:   p.TypeName(p.ObjectNamed(m.GetInputType())),
			ReplyType:  p.TypeName(p.ObjectNamed(m.GetOutputType())),
		})
		methodList += out.String()
	}

	// gen all client code
	{
		out := bytes.NewBuffer([]byte{})
		t := template.Must(template.New("").Parse(clientHelperFuncTmpl))
		t.Execute(out, &struct{ PackageName, ServiceName, MethodList string }{
			PackageName: file.GetPackage(),
			ServiceName: generator.CamelCase(svc.GetName()),
			MethodList:  methodList,
		})
		p.P(out.String())
	}
}

func (p *netrpcPlugin) makeServiceRegisterName(
	file *generator.FileDescriptor,
	packageName, serviceName string,
) string {
	return packageName + "." + serviceName
}

func init() {
	generator.RegisterPlugin(new(netrpcPlugin))
}
<|endoftext|>


<commit_before>package vec2

import (
	"fmt"
)

// Rect is a coordinate system aligned rectangle defined by a Min and Max vector.
type Rect struct {
	Min T
	Max T
}

// ParseRect parses a Rect from a string. See also String()
func ParseRect(s string) (r Rect, err error) {
	_, err = fmt.Sscanf(s, "%f %f %f %f", &r.Min[0], &r.Min[1], &r.Max[0], &r.Max[1])
	return r, err
}

// String formats Rect as string. See also ParseRect().
func (rect *Rect) String() string {
	return rect.Min.String() + " " + rect.Max.String()
}

// ContainsPoint returns if a point is contained within the rectangle.
func (rect *Rect) ContainsPoint(p *T) bool {
	return p[0] >= rect.Min[0] && p[0] <= rect.Max[0] &&
		p[1] >= rect.Min[1] && p[1] <= rect.Max[1]
}

// func (rect *Rect) Contains(other *Rect) bool {
// 	panic("not implemented")
// }

// func (rect *Rect) Intersects(other *Rect) bool {
// 	panic("not implemented")
// }

// func Intersect(a, b *Rect) Rect {
// 	panic("not implemented")
// }

// func Join(a, b *Rect) Rect {
// 	panic("not implemented")
// }
<commit_msg>Implement Rect.Contains and Rect.Intersects.<commit_after>package vec2

import (
	"fmt"
)

// Rect is a coordinate system aligned rectangle defined by a Min and Max vector.
type Rect struct {
	Min T
	Max T
}

// ParseRect parses a Rect from a string. See also String()
func ParseRect(s string) (r Rect, err error) {
	_, err = fmt.Sscanf(s, "%f %f %f %f", &r.Min[0], &r.Min[1], &r.Max[0], &r.Max[1])
	return r, err
}

// String formats Rect as string. See also ParseRect().
func (rect *Rect) String() string {
	return rect.Min.String() + " " + rect.Max.String()
}

// ContainsPoint returns if a point is contained within the rectangle.
func (rect *Rect) ContainsPoint(p *T) bool {
	return p[0] >= rect.Min[0] && p[0] <= rect.Max[0] &&
		p[1] >= rect.Min[1] && p[1] <= rect.Max[1]
}

func (rect *Rect) Contains(other *Rect) bool {
	return other.Min[0] >= rect.Min[0] &&
		other.Max[0] <= rect.Max[0] &&
		other.Min[1] >= rect.Min[1] &&
		other.Max[1] <= rect.Max[1]
}

func (rect *Rect) Intersects(other *Rect) bool {
	return other.Max[0] >= rect.Min[0] &&
		other.Min[0] <= rect.Max[0] &&
		other.Max[1] >= rect.Min[0] &&
		other.Min[1] <= rect.Max[1]
}

// func Intersect(a, b *Rect) Rect {
// 	panic("not implemented")
// }

// func Join(a, b *Rect) Rect {
// 	panic("not implemented")
// }
<|endoftext|>


<commit_before>// Package shopify implements the OAuth2 protocol for authenticating users through Shopify.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package shopify

// Define scopes supported by Shopify.
// See: https://help.shopify.com/en/api/getting-started/authentication/oauth/scopes#authenticated-access-scopes
const (
	ScopeReadContent                 = "read_content"
	ScopeWriteContent                = "write_content"
	ScopeReadThemes                  = "read_themes"
	ScopeWriteThemes                 = "write_themes"
	ScopeReadProducts                = "read_products"
	ScopeWriteProducts               = "write_products"
	ScopeReadProductListings         = "read_product_listings"
	ScopeReadCustomers               = "read_customers"
	ScopeWriteCustomers              = "write_customers"
	ScopeReadOrders                  = "read_orders"
	ScopeWriteOrders                 = "write_orders"
	ScopeReadDrafOrders              = "read_draft_orders"
	ScopeWriteDrafOrders             = "write_draft_orders"
	ScopeReadInventory               = "read_inventory"
	ScopeWriteInventory              = "write_inventory"
	ScopeReadLocations               = "read_locations"
	ScopeReadScriptTags              = "read_script_tags"
	ScopeWriteScriptTags             = "write_script_tags"
	ScopeReadFulfillments            = "read_fulfillments"
	ScopeWriteFulfillments           = "write_fulfillments"
	ScopeReadShipping                = "read_shipping"
	ScopeWriteShipping               = "write_shipping"
	ScopeReadAnalytics               = "read_analytics"
	ScopeReadUsers                   = "read_users"
	ScopeWriteUsers                  = "write_users"
	ScopeReadCheckouts               = "read_checkouts"
	ScopeWriteCheckouts              = "write_checkouts"
	ScopeReadReports                 = "read_reports"
	ScopeWriteReports                = "write_reports"
	ScopeReadPriceRules              = "read_price_rules"
	ScopeWritePriceRules             = "write_price_rules"
	ScopeMarketingEvents             = "read_marketing_events"
	ScopeWriteMarketingEvents        = "write_marketing_events"
	ScopeReadResourceFeedbacks       = "read_resource_feedbacks"
	ScopeWriteResourceFeedbacks      = "write_resource_feedbacks"
	ScopeReadShopifyPaymentsPayouts  = "read_shopify_payments_payouts"
	ScopeReadShopifyPaymentsDisputes = "read_shopify_payments_disputes"

	// Special:
	// Grants access to all orders rather than the default window of 60 days worth of orders.
	// This OAuth scope is used in conjunction with read_orders, or write_orders. You need to request
	// this scope from your Partner Dashboard before adding it to your app.
	ScopeReadAllOrders = "read_all_orders"
)
<commit_msg>Updated comments<commit_after>package shopify

// Define scopes supported by Shopify.
// See: https://help.shopify.com/en/api/getting-started/authentication/oauth/scopes#authenticated-access-scopes
const (
	ScopeReadContent                 = "read_content"
	ScopeWriteContent                = "write_content"
	ScopeReadThemes                  = "read_themes"
	ScopeWriteThemes                 = "write_themes"
	ScopeReadProducts                = "read_products"
	ScopeWriteProducts               = "write_products"
	ScopeReadProductListings         = "read_product_listings"
	ScopeReadCustomers               = "read_customers"
	ScopeWriteCustomers              = "write_customers"
	ScopeReadOrders                  = "read_orders"
	ScopeWriteOrders                 = "write_orders"
	ScopeReadDrafOrders              = "read_draft_orders"
	ScopeWriteDrafOrders             = "write_draft_orders"
	ScopeReadInventory               = "read_inventory"
	ScopeWriteInventory              = "write_inventory"
	ScopeReadLocations               = "read_locations"
	ScopeReadScriptTags              = "read_script_tags"
	ScopeWriteScriptTags             = "write_script_tags"
	ScopeReadFulfillments            = "read_fulfillments"
	ScopeWriteFulfillments           = "write_fulfillments"
	ScopeReadShipping                = "read_shipping"
	ScopeWriteShipping               = "write_shipping"
	ScopeReadAnalytics               = "read_analytics"
	ScopeReadUsers                   = "read_users"
	ScopeWriteUsers                  = "write_users"
	ScopeReadCheckouts               = "read_checkouts"
	ScopeWriteCheckouts              = "write_checkouts"
	ScopeReadReports                 = "read_reports"
	ScopeWriteReports                = "write_reports"
	ScopeReadPriceRules              = "read_price_rules"
	ScopeWritePriceRules             = "write_price_rules"
	ScopeMarketingEvents             = "read_marketing_events"
	ScopeWriteMarketingEvents        = "write_marketing_events"
	ScopeReadResourceFeedbacks       = "read_resource_feedbacks"
	ScopeWriteResourceFeedbacks      = "write_resource_feedbacks"
	ScopeReadShopifyPaymentsPayouts  = "read_shopify_payments_payouts"
	ScopeReadShopifyPaymentsDisputes = "read_shopify_payments_disputes"

	// Special:
	// Grants access to all orders rather than the default window of 60 days worth of orders.
	// This OAuth scope is used in conjunction with read_orders, or write_orders. You need to request
	// this scope from your Partner Dashboard before adding it to your app.
	ScopeReadAllOrders = "read_all_orders"
)
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/sorcix/irc.v2"
)

type client struct {
	conn *irc.Conn
	mux  sync.Mutex

	nick    string
	buffers map[string]chan *irc.Message

	directory string
}

const serverBufferName = "$server"

func Connect() {
	hostname := "irc.freenode.net"
	port := 6667
	nick := "ep`uex"

	baseDir, err := ioutil.TempDir("", hostname)
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(baseDir)

	fmt.Printf("==> Output to %s\n", baseDir)

	for {
		client, err := createClient(hostname, port, baseDir)
		if err != nil {
			fmt.Printf("connect failed: %+v\n", err)
			goto retry
		}

		client.initialize(nick, "")

		// If we exit `runLoop` cleanly, it was an intentional process exit.
		if err := client.runLoop(); err == nil {
			break
		}

		fmt.Printf("IRC connection errored: %+v\n", err)
	retry:
		fmt.Println("... sleeping 5 seconds before reconnecting")
		time.Sleep(5 * time.Second)
	}
}

func createClient(hostname string, port int, baseDir string) (*client, error) {
	server := fmt.Sprintf("%s:%d", hostname, port)
	// TODO: handle TLS connection here as well
	conn, err := irc.Dial(server)
	if err != nil {
		return nil, err
	}

	client := &client{
		conn:      conn,
		directory: filepath.Join(baseDir, server),
		buffers:   make(map[string]chan *irc.Message),
	}

	return client, nil
}

func (c *client) initialize(nick, pass string) {
	if pass != "" {
		c.send("PASS", pass)
	}

	c.send("NICK", nick)
	c.send("USER", nick, "*", "*", "real name")

	c.nick = nick
}

func (c *client) send(cmd string, params ...string) {
	msg := &irc.Message{
		Command: cmd,
		Params:  params,
	}

	c.conn.Encode(msg)

	fmt.Printf("--> %+v\n", msg)
}

func (c *client) handleMessage(msg *irc.Message) {
	buf := c.getBuffer(serverBufferName)

	switch msg.Command {
	case irc.PING:
		c.send(irc.PONG, msg.Params...)

	case irc.NICK:
		from := msg.Prefix.Name
		to := msg.Params[0]

		if from == c.nick {
			fmt.Printf("updating my nick to %s\n", to)
			c.nick = to
		} else {
			// TODO: broadcast renames to all bufs having that user?
			// requires more tracking
			buf = nil
		}

	case irc.JOIN:
		buf = c.getBuffer(msg.Params[0])

	case irc.PRIVMSG, irc.NOTICE:
		target := msg.Params[0]

		// Group all messages sent by the server together,
		// regardless of server name.
		//
		// For direct messages, we want to look at the sender.
		if msg.Prefix.IsServer() {
			target = serverBufferName
		} else if !isChannel(target) {
			target = msg.Prefix.Name
		}

		buf = c.getBuffer(target)
	}

	if buf != nil {
		buf <- msg
	}
}

func (c *client) getBuffer(name string) chan *irc.Message {
	c.mux.Lock()
	defer c.mux.Unlock()

	// Sent early on, at least by freenode.
	if name == "*" {
		name = serverBufferName
	}

	if ch, exists := c.buffers[name]; exists {
		return ch
	}

	path := c.directory

	// We want to write __in, __out top level for the server, and
	// as a child for every other buffer.
	if name != serverBufferName {
		path = filepath.Join(path, name)
	}

	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		log.Fatal(err)
	}

	ch := make(chan *irc.Message)
	c.buffers[name] = ch

	go c.bufferInputHandler(path, name)
	go c.bufferOutputHandler(path, ch)

	return ch
}

func (c *client) bufferOutputHandler(path string, ch chan *irc.Message) {
	name := filepath.Join(path, "__out")
	mode := os.O_APPEND | os.O_RDWR | os.O_CREATE
	file, err := os.OpenFile(name, mode, 0644)
	if err != nil {
		log.Fatal(err)
	}

	defer file.Close()

	// TODO: better serialization?? colors?? etc.
	for msg := range ch {
		if _, err := file.WriteString(fmt.Sprintf(">> %+v\n", msg)); err != nil {
			log.Fatal(err)
		}
		if err := file.Sync(); err != nil {
			log.Fatal(err)
		}
	}
}

func (c *client) bufferInputHandler(path, bufName string) {
	name := filepath.Join(path, "__in")
	err := syscall.Mkfifo(name, 0777)

	// Doesn't matter if the FIFO already exists from a previous run.
	if err != nil && err != syscall.EEXIST {
		log.Fatal(err)
	}

	for {
		buf, err := ioutil.ReadFile(name)
		if err != nil {
			log.Fatal(err)
		}

		if len(buf) == 0 {
			time.Sleep(100 * time.Millisecond)
			continue
		}

		lines := strings.Split(string(buf), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}

			c.handleInputLine(bufName, line)
		}
	}
}

func isChannel(target string) bool {
	if target == "" {
		return false
	}

	return target[0] == '#' || target[0] == '&'
}

func splitInputCommand(bufName, line string) (string, string) {
	// Without a prefix, it's just a regular PRIVMSG
	if !strings.HasPrefix(line, "/") {
		return "/msg", bufName + " " + line
	}
	// Double slash at start means privmsg with leading slash
	if strings.HasPrefix(line, "//") {
		return "/msg", bufName + " " + line[1:]
	}

	s := strings.SplitN(line, " ", 2)
	cmd := strings.ToLower(s[0])

	if len(s) == 1 {
		return cmd, ""
	}

	return cmd, s[1]
}

func (c *client) handleInputLine(bufName, line string) {
	fmt.Printf("%s >> %s\n", bufName, line)

	cmd, rest := splitInputCommand(bufName, line)

	switch cmd {
	case "/m", "/msg":
		s := strings.SplitN(rest, " ", 2)
		if len(s) != 2 {
			fmt.Printf("expected: /msg TARGET MESSAGE")
			return
		}
		c.send("PRIVMSG", s...)

	case "/j", "/join":
		if !isChannel(rest) {
			fmt.Printf("expected: /join TARGET")
			return
		}
		c.send("JOIN", rest)

	case "/quote":
		params := strings.Split(rest, " ")
		if len(params) == 1 {
			c.send(params[1])
		} else {
			c.send(params[1], params[1:]...)
		}

	default:
		fmt.Printf("Unknown command: %s %s\n", cmd, rest)
	}
}

func (c *client) runLoop() error {
	for {
		message, err := c.conn.Decode()
		if err != nil {
			return err
		}

		fmt.Printf("<-- %+v\n", message)
		c.handleMessage(message)
	}
}
<commit_msg>Attach more type information to buffers<commit_after>package cmd

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/sorcix/irc.v2"
)

type client struct {
	conn *irc.Conn
	mux  sync.Mutex

	nick    string
	buffers map[string]buffer

	directory string
}

type buffer struct {
	ch     chan *irc.Message
	client *client
	path   string

	name  string
	topic string
	users map[string]string
}

const serverBufferName = "$server"

func Connect() {
	hostname := "irc.freenode.net"
	port := 6667
	nick := "ep`uex"

	baseDir, err := ioutil.TempDir("", hostname)
	if err != nil {
		log.Fatal(err)
	}
	defer os.RemoveAll(baseDir)

	fmt.Printf("==> Output to %s\n", baseDir)

	for {
		client, err := createClient(hostname, port, baseDir)
		if err != nil {
			fmt.Printf("connect failed: %+v\n", err)
			goto retry
		}

		client.initialize(nick, "")

		// If we exit `runLoop` cleanly, it was an intentional process exit.
		if err := client.runLoop(); err == nil {
			break
		}

		fmt.Printf("IRC connection errored: %+v\n", err)
	retry:
		fmt.Println("... sleeping 5 seconds before reconnecting")
		time.Sleep(5 * time.Second)
	}
}

func createClient(hostname string, port int, baseDir string) (*client, error) {
	server := fmt.Sprintf("%s:%d", hostname, port)
	// TODO: handle TLS connection here as well
	conn, err := irc.Dial(server)
	if err != nil {
		return nil, err
	}

	client := &client{
		conn:      conn,
		directory: filepath.Join(baseDir, server),
		buffers:   make(map[string]buffer),
	}

	return client, nil
}

func (c *client) initialize(nick, pass string) {
	if pass != "" {
		c.send("PASS", pass)
	}

	c.send("NICK", nick)
	c.send("USER", nick, "*", "*", "real name")

	c.nick = nick
}

func (c *client) send(cmd string, params ...string) {
	msg := &irc.Message{
		Command: cmd,
		Params:  params,
	}

	c.conn.Encode(msg)

	fmt.Printf("--> %+v\n", msg)
}

func (c *client) handleMessage(msg *irc.Message) {
	buf := c.getBuffer(serverBufferName)

	switch msg.Command {
	case irc.PING:
		c.send(irc.PONG, msg.Params...)

	case irc.NICK:
		from := msg.Prefix.Name
		to := msg.Params[0]

		if from == c.nick {
			fmt.Printf("updating my nick to %s\n", to)
			c.nick = to
		} else {
			// TODO: broadcast renames to all bufs having that user?
			// requires more tracking
			buf = nil
		}

	case irc.JOIN:
		buf = c.getBuffer(msg.Params[0])

	case irc.PRIVMSG, irc.NOTICE:
		target := msg.Params[0]

		// Group all messages sent by the server together,
		// regardless of server name.
		//
		// For direct messages, we want to look at the sender.
		if msg.Prefix.IsServer() {
			target = serverBufferName
		} else if !isChannel(target) {
			target = msg.Prefix.Name
		}

		buf = c.getBuffer(target)

	case irc.RPL_TOPIC:
		target := msg.Params[1]
		topic := msg.Params[2]

		buf = c.getBuffer(target)
		buf.topic = topic
	}

	if buf != nil {
		buf.ch <- msg
	}
}

func (c *client) getBuffer(name string) *buffer {
	c.mux.Lock()
	defer c.mux.Unlock()

	// Sent early on, at least by freenode.
	if name == "*" {
		name = serverBufferName
	}

	if buf, exists := c.buffers[name]; exists {
		return &buf
	}

	path := c.directory

	// We want to write __in, __out top level for the server, and
	// as a child for every other buffer.
	if name != serverBufferName {
		path = filepath.Join(path, name)
	}

	if err := os.MkdirAll(path, os.ModePerm); err != nil {
		log.Fatal(err)
	}

	c.buffers[name] = buffer{
		ch:     make(chan *irc.Message),
		client: c,
		path:   path,

		name:  name,
		topic: "",
		users: make(map[string]string),
	}

	b := c.buffers[name]

	go b.inputHandler()
	go b.outputHandler()

	return &b
}

func (b *buffer) outputHandler() {
	name := filepath.Join(b.path, "__out")
	mode := os.O_APPEND | os.O_RDWR | os.O_CREATE
	file, err := os.OpenFile(name, mode, 0644)
	if err != nil {
		log.Fatal(err)
	}

	defer file.Close()

	// TODO: better serialization?? colors?? etc.
	for msg := range b.ch {
		if _, err := file.WriteString(fmt.Sprintf(">> %+v\n", msg)); err != nil {
			log.Fatal(err)
		}
		if err := file.Sync(); err != nil {
			log.Fatal(err)
		}
	}
}

func (b *buffer) inputHandler() {
	name := filepath.Join(b.path, "__in")
	err := syscall.Mkfifo(name, 0777)

	// Doesn't matter if the FIFO already exists from a previous run.
	if err != nil && err != syscall.EEXIST {
		log.Fatal(err)
	}

	for {
		buf, err := ioutil.ReadFile(name)
		if err != nil {
			log.Fatal(err)
		}

		if len(buf) == 0 {
			time.Sleep(100 * time.Millisecond)
			continue
		}

		lines := strings.Split(string(buf), "\n")
		for _, line := range lines {
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}

			b.client.handleInputLine(b.name, line)
		}
	}
}

func isChannel(target string) bool {
	if target == "" {
		return false
	}

	return target[0] == '#' || target[0] == '&'
}

func splitInputCommand(bufName, line string) (string, string) {
	// Without a prefix, it's just a regular PRIVMSG
	if !strings.HasPrefix(line, "/") {
		return "/msg", bufName + " " + line
	}
	// Double slash at start means privmsg with leading slash
	if strings.HasPrefix(line, "//") {
		return "/msg", bufName + " " + line[1:]
	}

	s := strings.SplitN(line, " ", 2)
	cmd := strings.ToLower(s[0])

	if len(s) == 1 {
		return cmd, ""
	}

	return cmd, s[1]
}

func (c *client) handleInputLine(bufName, line string) {
	fmt.Printf("%s >> %s\n", bufName, line)

	cmd, rest := splitInputCommand(bufName, line)

	switch cmd {
	case "/m", "/msg":
		s := strings.SplitN(rest, " ", 2)
		if len(s) != 2 {
			fmt.Printf("expected: /msg TARGET MESSAGE")
			return
		}
		c.send("PRIVMSG", s...)

	case "/j", "/join":
		if !isChannel(rest) {
			fmt.Printf("expected: /join TARGET")
			return
		}
		c.send("JOIN", rest)

	case "/quote":
		params := strings.Split(rest, " ")
		if len(params) == 1 {
			c.send(params[1])
		} else {
			c.send(params[1], params[1:]...)
		}

	default:
		fmt.Printf("Unknown command: %s %s\n", cmd, rest)
	}
}

func (c *client) runLoop() error {
	for {
		message, err := c.conn.Decode()
		if err != nil {
			return err
		}

		fmt.Printf("<-- %+v\n", message)
		c.handleMessage(message)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package sm

import "fmt"

// TileProvider encapsulates all infos about a map tile provider service (name, url scheme, attribution, etc.)
type TileProvider struct {
	Name           string
	Attribution    string
	IgnoreNotFound bool
	TileSize       int
	URLPattern     string // "%[1]s" => shard, "%[2]d" => zoom, "%[3]d" => x, "%[4]d" => y
	Shards         []string
}

func (t *TileProvider) getURL(shard string, zoom, x, y int) string {
	return fmt.Sprintf(t.URLPattern, shard, zoom, x, y)
}

// NewTileProviderOpenStreetMaps creates a TileProvider struct for OSM's tile service
func NewTileProviderOpenStreetMaps() *TileProvider {
	t := new(TileProvider)
	t.Name = "osm"
	t.Attribution = "Maps and Data (c) openstreetmap.org and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.openstreetmap.org/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c"}
	return t
}

func newTileProviderThunderforest(name string) *TileProvider {
	t := new(TileProvider)
	t.Name = fmt.Sprintf("thunderforest-%s", name)
	t.Attribution = "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "https://%[1]s.tile.thunderforest.com/" + name + "/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c"}
	return t
}

// NewTileProviderThunderforestLandscape creates a TileProvider struct for thundeforests's 'landscape' tile service
func NewTileProviderThunderforestLandscape() *TileProvider {
	return newTileProviderThunderforest("landscape")
}

// NewTileProviderThunderforestOutdoors creates a TileProvider struct for thundeforests's 'outdoors' tile service
func NewTileProviderThunderforestOutdoors() *TileProvider {
	return newTileProviderThunderforest("outdoors")
}

// NewTileProviderThunderforestTransport creates a TileProvider struct for thundeforests's 'transport' tile service
func NewTileProviderThunderforestTransport() *TileProvider {
	return newTileProviderThunderforest("transport")
}

// NewTileProviderStamenToner creates a TileProvider struct for stamens' 'toner' tile service
func NewTileProviderStamenToner() *TileProvider {
	t := new(TileProvider)
	t.Name = "stamen-toner"
	t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.stamen.com/toner/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c", "d"}
	return t
}

// NewTileProviderStamenTerrain creates a TileProvider struct for stamens' 'terrain' tile service
func NewTileProviderStamenTerrain() *TileProvider {
	t := new(TileProvider)
	t.Name = "stamen-terrain"
	t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.stamen.com/terrain/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c", "d"}
	return t
}

// NewTileProviderOpenTopoMap creates a TileProvider struct for opentopomap's tile service
func NewTileProviderOpenTopoMap() *TileProvider {
	t := new(TileProvider)
	t.Name = "opentopomap"
	t.Attribution = "Maps (c) OpenTopoMap [CC-BY-SA]; Data (c) OSM and contributors [ODbL]; Data (c) SRTM"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.opentopomap.org/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c"}
	return t
}

// NewTileProviderWikimedia creates a TileProvider struct for Wikimedia's tile service
func NewTileProviderWikimedia() *TileProvider {
	t := new(TileProvider)
	t.Name = "wikimedia"
	t.Attribution = "Map (c) Wikimedia; Data (c) OSM and contributors, ODbL."
	t.TileSize = 256
	t.URLPattern = "https://maps.wikimedia.org/osm-intl/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{}
	return t
}

// NewTileProviderOpenCycleMap creates a TileProvider struct for OpenCycleMap's tile service
func NewTileProviderOpenCycleMap() *TileProvider {
	t := new(TileProvider)
	t.Name = "cycle"
	t.Attribution = "Maps and Data (c) openstreetmaps.org and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.opencyclemap.org/cycle/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b"}
	return t
}

func newTileProviderCarto(name string) *TileProvider {
	t := new(TileProvider)
	t.Name = fmt.Sprintf("carto-%s", name)
	t.Attribution = "Map (c) Carto [CC BY 3.0] Data (c) OSM and contributors, ODbL."
	t.TileSize = 256
	t.URLPattern = "https://cartodb-basemaps-%[1]s.global.ssl.fastly.net/" + name + "_all/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c", "d"}
	return t
}

// NewTileProviderCartoLight creates a TileProvider struct for Carto's tile service (light variant)
func NewTileProviderCartoLight() *TileProvider {
	return newTileProviderCarto("light")
}

// NewTileProviderCartoDark creates a TileProvider struct for Carto's tile service (dark variant)
func NewTileProviderCartoDark() *TileProvider {
	return newTileProviderCarto("dark")
}

// NewTileProviderArcgisWorldImagery creates a TileProvider struct for Arcgis' WorldImagery tiles
func NewTileProviderArcgisWorldImagery() *TileProvider {
	t := new(TileProvider)
	t.Name = "arcgis-worldimagery"
	t.Attribution = "Source: Esri, Maxar, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community"
	t.TileSize = 256
	t.URLPattern = "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/%[2]d/%[4]d/%[3]d"
	t.Shards = []string{}
	return t
}

// GetTileProviders returns a map of all available TileProviders
func GetTileProviders() map[string]*TileProvider {
	m := make(map[string]*TileProvider)

	list := []*TileProvider{
		NewTileProviderOpenStreetMaps(),
		NewTileProviderOpenCycleMap(),
		NewTileProviderThunderforestLandscape(),
		NewTileProviderThunderforestOutdoors(),
		NewTileProviderThunderforestTransport(),
		NewTileProviderStamenToner(),
		NewTileProviderStamenTerrain(),
		NewTileProviderOpenTopoMap(),
		NewTileProviderOpenStreetMaps(),
		NewTileProviderOpenCycleMap(),
		NewTileProviderCartoLight(),
		NewTileProviderCartoDark(),
		NewTileProviderArcgisWorldImagery(),
		NewTileProviderWikimedia(),
	}

	for _, tp := range list {
		m[tp.Name] = tp
	}

	return m
}
<commit_msg>Remove duplicate tile provider entries. Closes flopp/go-staticmaps#56 (#62)<commit_after>// Copyright 2016, 2017 Florian Pigorsch. All rights reserved.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package sm

import "fmt"

// TileProvider encapsulates all infos about a map tile provider service (name, url scheme, attribution, etc.)
type TileProvider struct {
	Name           string
	Attribution    string
	IgnoreNotFound bool
	TileSize       int
	URLPattern     string // "%[1]s" => shard, "%[2]d" => zoom, "%[3]d" => x, "%[4]d" => y
	Shards         []string
}

func (t *TileProvider) getURL(shard string, zoom, x, y int) string {
	return fmt.Sprintf(t.URLPattern, shard, zoom, x, y)
}

// NewTileProviderOpenStreetMaps creates a TileProvider struct for OSM's tile service
func NewTileProviderOpenStreetMaps() *TileProvider {
	t := new(TileProvider)
	t.Name = "osm"
	t.Attribution = "Maps and Data (c) openstreetmap.org and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.openstreetmap.org/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c"}
	return t
}

func newTileProviderThunderforest(name string) *TileProvider {
	t := new(TileProvider)
	t.Name = fmt.Sprintf("thunderforest-%s", name)
	t.Attribution = "Maps (c) Thundeforest; Data (c) OSM and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "https://%[1]s.tile.thunderforest.com/" + name + "/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c"}
	return t
}

// NewTileProviderThunderforestLandscape creates a TileProvider struct for thundeforests's 'landscape' tile service
func NewTileProviderThunderforestLandscape() *TileProvider {
	return newTileProviderThunderforest("landscape")
}

// NewTileProviderThunderforestOutdoors creates a TileProvider struct for thundeforests's 'outdoors' tile service
func NewTileProviderThunderforestOutdoors() *TileProvider {
	return newTileProviderThunderforest("outdoors")
}

// NewTileProviderThunderforestTransport creates a TileProvider struct for thundeforests's 'transport' tile service
func NewTileProviderThunderforestTransport() *TileProvider {
	return newTileProviderThunderforest("transport")
}

// NewTileProviderStamenToner creates a TileProvider struct for stamens' 'toner' tile service
func NewTileProviderStamenToner() *TileProvider {
	t := new(TileProvider)
	t.Name = "stamen-toner"
	t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.stamen.com/toner/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c", "d"}
	return t
}

// NewTileProviderStamenTerrain creates a TileProvider struct for stamens' 'terrain' tile service
func NewTileProviderStamenTerrain() *TileProvider {
	t := new(TileProvider)
	t.Name = "stamen-terrain"
	t.Attribution = "Maps (c) Stamen; Data (c) OSM and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.stamen.com/terrain/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c", "d"}
	return t
}

// NewTileProviderOpenTopoMap creates a TileProvider struct for opentopomap's tile service
func NewTileProviderOpenTopoMap() *TileProvider {
	t := new(TileProvider)
	t.Name = "opentopomap"
	t.Attribution = "Maps (c) OpenTopoMap [CC-BY-SA]; Data (c) OSM and contributors [ODbL]; Data (c) SRTM"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.opentopomap.org/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c"}
	return t
}

// NewTileProviderWikimedia creates a TileProvider struct for Wikimedia's tile service
func NewTileProviderWikimedia() *TileProvider {
	t := new(TileProvider)
	t.Name = "wikimedia"
	t.Attribution = "Map (c) Wikimedia; Data (c) OSM and contributors, ODbL."
	t.TileSize = 256
	t.URLPattern = "https://maps.wikimedia.org/osm-intl/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{}
	return t
}

// NewTileProviderOpenCycleMap creates a TileProvider struct for OpenCycleMap's tile service
func NewTileProviderOpenCycleMap() *TileProvider {
	t := new(TileProvider)
	t.Name = "cycle"
	t.Attribution = "Maps and Data (c) openstreetmaps.org and contributors, ODbL"
	t.TileSize = 256
	t.URLPattern = "http://%[1]s.tile.opencyclemap.org/cycle/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b"}
	return t
}

func newTileProviderCarto(name string) *TileProvider {
	t := new(TileProvider)
	t.Name = fmt.Sprintf("carto-%s", name)
	t.Attribution = "Map (c) Carto [CC BY 3.0] Data (c) OSM and contributors, ODbL."
	t.TileSize = 256
	t.URLPattern = "https://cartodb-basemaps-%[1]s.global.ssl.fastly.net/" + name + "_all/%[2]d/%[3]d/%[4]d.png"
	t.Shards = []string{"a", "b", "c", "d"}
	return t
}

// NewTileProviderCartoLight creates a TileProvider struct for Carto's tile service (light variant)
func NewTileProviderCartoLight() *TileProvider {
	return newTileProviderCarto("light")
}

// NewTileProviderCartoDark creates a TileProvider struct for Carto's tile service (dark variant)
func NewTileProviderCartoDark() *TileProvider {
	return newTileProviderCarto("dark")
}

// NewTileProviderArcgisWorldImagery creates a TileProvider struct for Arcgis' WorldImagery tiles
func NewTileProviderArcgisWorldImagery() *TileProvider {
	t := new(TileProvider)
	t.Name = "arcgis-worldimagery"
	t.Attribution = "Source: Esri, Maxar, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community"
	t.TileSize = 256
	t.URLPattern = "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/%[2]d/%[4]d/%[3]d"
	t.Shards = []string{}
	return t
}

// GetTileProviders returns a map of all available TileProviders
func GetTileProviders() map[string]*TileProvider {
	m := make(map[string]*TileProvider)

	list := []*TileProvider{
		NewTileProviderThunderforestLandscape(),
		NewTileProviderThunderforestOutdoors(),
		NewTileProviderThunderforestTransport(),
		NewTileProviderStamenToner(),
		NewTileProviderStamenTerrain(),
		NewTileProviderOpenTopoMap(),
		NewTileProviderOpenStreetMaps(),
		NewTileProviderOpenCycleMap(),
		NewTileProviderCartoLight(),
		NewTileProviderCartoDark(),
		NewTileProviderArcgisWorldImagery(),
		NewTileProviderWikimedia(),
	}

	for _, tp := range list {
		m[tp.Name] = tp
	}

	return m
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package workload

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/utils/set"
)

// The Juju-recognized states in which a workload might be.
const (
	StateUndefined = ""
	StateDefined   = "defined"
	StateStarting  = "starting"
	StateRunning   = "running"
	StateStopping  = "stopping"
	StateStopped   = "stopped"
)

var okayStates = set.NewStrings(
	StateStarting,
	StateRunning,
	StateStopping,
	StateStopped,
)

// The different kinds of status blockers.
const (
	NotBlocked = ""
	// BlockerError indicates that the plugin reported a problem with
	// the workload.
	BlockerError = "error"
	// BlockerFailed indicates that Juju got a failure while trying
	// to interact with the workload (via its plugin).
	BlockerFailed = "failed"
)

// TODO(ericsnow) Use a separate StatusInfo and keep Status (quasi-)immutable?

// Status is the Juju-level status of a workload.
type Status struct {
	// State is which state the workload is in relative to Juju.
	State string
	// Blocker identifies the kind of blocker preventing interaction
	// with the workload.
	Blocker string
	// Message is a human-readable message describing the current status
	// of the workload, why it is in the current state, or what Juju is
	// doing right now relative to the workload. There may be no message.
	Message string
}

// String returns a string representing the status of the workload.
func (s Status) String() string {
	message := s.Message
	if s.IsBlocked() {
		if message == "" {
			message = "<no message>"
		}
		return fmt.Sprintf("(%s) %s", s.Blocker, message)
	}
	return message
}

// IsBlocked indicates whether or not the workload may workload
// to the next state.
func (s *Status) IsBlocked() bool {
	return s.Blocker != NotBlocked
}

// Advance updates the state of the Status to the next appropriate one.
// If a message is provided, it is set. Otherwise the current message
// will be cleared.
func (s *Status) Advance(message string) error {
	if s.IsBlocked() {
		reason := s.Blocker
		return errors.Errorf("cannot advance state (" + reason + ")")
	}
	switch s.State {
	case StateUndefined:
		s.State = StateDefined
	case StateDefined:
		s.State = StateStarting
	case StateStarting:
		s.State = StateRunning
	case StateRunning:
		s.State = StateStopping
	case StateStopping:
		s.State = StateStopped
	case StateStopped:
		return errors.Errorf("cannot advance from a final state")
	default:
		return errors.NotValidf("unrecognized state %q", s.State)
	}
	s.Message = message
	return nil
}

// SetFailed records that Juju encountered a problem when trying to
// interact with the workload. If Status.Blocker is already failed then
// the message is updated. If the workload is in an initial or final
// state then an error is returned.
func (s *Status) SetFailed(message string) error {
	switch s.State {
	case StateUndefined:
		return errors.Errorf("cannot fail while in an initial state")
	case StateDefined:
		return errors.Errorf("cannot fail while in an initial state")
	case StateStopped:
		return errors.Errorf("cannot fail while in a final state")
	}

	if message == "" {
		message = "problem while interacting with workload"
	}
	s.Blocker = BlockerFailed
	s.Message = message
	return nil
}

// SetError records that the workload isn't working correctly,
// as reported by the plugin. If already in an error state then the
// message is updated. The workload must be in a running state for there
// to be an error. problems during starting and stopping are recorded
// as failures rather than errors.
func (s *Status) SetError(message string) error {
	// TODO(ericsnow) Allow errors in other states?
	switch s.State {
	case StateRunning:
	default:
		return errors.Errorf("can error only while running")
	}

	if message == "" {
		message = "the workload has an error"
	}
	s.Blocker = BlockerError
	s.Message = message
	return nil
}

// Resolve clears any existing error or failure status for the workload.
// If a message is provided then it is set. Otherwise a message
// describing what was resolved will be set. If the workload is both
// failed and in an error state then both will be resolved at once.
// If the workload isn't currently blocked then an error is returned.
func (s *Status) Resolve(message string) error {
	if !s.IsBlocked() {
		// TODO(ericsnow) Do nothing?
		return errors.Errorf("not in an error or failed state")
	}

	defaultMessage := fmt.Sprintf("resolved blocker %q", s.Blocker)
	if message == "" {
		// TODO(ericsnow) Add in the current message?
		message = defaultMessage
	}

	s.Blocker = NotBlocked
	s.Message = message
	return nil
}

// Validate checkes the status to ensure it contains valid data.
func (s Status) Validate() error {
	if !okayStates.Contains(s.State) {
		return errors.NotValidf("Status.State (%q)", s.State)
	}

	switch s.Blocker {
	case BlockerFailed:
		switch s.State {
		case StateUndefined:
			return errors.NotValidf("failure in an initial state")
		case StateDefined:
			return errors.NotValidf("failure in an initial state")
		case StateStopped:
			return errors.NotValidf("failure in a final state")
		}
	case BlockerError:
		if s.State != StateRunning {
			return errors.NotValidf("error outside running state")
		}
	}

	return nil
}

// ValidateState verifies the state passed in is a valid okayState.
func ValidateState(state string) error {
	if !okayStates.Contains(state) {
		return errors.NotValidf("state (%q)", state)
	}
	return nil
}

// PluginStatus represents the data returned from the Plugin.Status call.
type PluginStatus struct {
	// State is the human-readable label returned by the plugin
	// that represents the status of the workload.
	State string `json:"state"`
}

// Validate returns nil if this value is valid, and an error that satisfies
// IsValid if it is not.
func (s PluginStatus) Validate() error {
	if s.State == "" {
		return errors.NotValidf("State cannot be empty")
	}
	return nil
}

// CombinedStatus holds the status information for a workload,
// from all sources.
type CombinedStatus struct {
	// Status is the Juju-level status information.
	Status Status
	// PluginStatus is the plugin-defined status information.
	PluginStatus PluginStatus
}

// Validate returns nil if this value is valid, and an error that satisfies
// IsValid if it is not.
func (s CombinedStatus) Validate() error {
	if err := s.Status.Validate(); err != nil {
		return errors.Trace(err)
	}
	if err := s.PluginStatus.Validate(); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<commit_msg>added todo comment<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package workload

import (
	"fmt"

	"github.com/juju/errors"
	"github.com/juju/utils/set"
)

// The Juju-recognized states in which a workload might be.
const (
	StateUndefined = ""
	//TODO(wwitzel3) remove defined throughout
	StateDefined  = "defined"
	StateStarting = "starting"
	StateRunning  = "running"
	StateStopping = "stopping"
	StateStopped  = "stopped"
)

var okayStates = set.NewStrings(
	StateStarting,
	StateRunning,
	StateStopping,
	StateStopped,
)

// The different kinds of status blockers.
const (
	NotBlocked = ""
	// BlockerError indicates that the plugin reported a problem with
	// the workload.
	BlockerError = "error"
	// BlockerFailed indicates that Juju got a failure while trying
	// to interact with the workload (via its plugin).
	BlockerFailed = "failed"
)

// TODO(ericsnow) Use a separate StatusInfo and keep Status (quasi-)immutable?

// Status is the Juju-level status of a workload.
type Status struct {
	// State is which state the workload is in relative to Juju.
	State string
	// Blocker identifies the kind of blocker preventing interaction
	// with the workload.
	Blocker string
	// Message is a human-readable message describing the current status
	// of the workload, why it is in the current state, or what Juju is
	// doing right now relative to the workload. There may be no message.
	Message string
}

// String returns a string representing the status of the workload.
func (s Status) String() string {
	message := s.Message
	if s.IsBlocked() {
		if message == "" {
			message = "<no message>"
		}
		return fmt.Sprintf("(%s) %s", s.Blocker, message)
	}
	return message
}

// IsBlocked indicates whether or not the workload may workload
// to the next state.
func (s *Status) IsBlocked() bool {
	return s.Blocker != NotBlocked
}

// Advance updates the state of the Status to the next appropriate one.
// If a message is provided, it is set. Otherwise the current message
// will be cleared.
func (s *Status) Advance(message string) error {
	if s.IsBlocked() {
		reason := s.Blocker
		return errors.Errorf("cannot advance state (" + reason + ")")
	}
	switch s.State {
	case StateUndefined:
		s.State = StateDefined
	case StateDefined:
		s.State = StateStarting
	case StateStarting:
		s.State = StateRunning
	case StateRunning:
		s.State = StateStopping
	case StateStopping:
		s.State = StateStopped
	case StateStopped:
		return errors.Errorf("cannot advance from a final state")
	default:
		return errors.NotValidf("unrecognized state %q", s.State)
	}
	s.Message = message
	return nil
}

// SetFailed records that Juju encountered a problem when trying to
// interact with the workload. If Status.Blocker is already failed then
// the message is updated. If the workload is in an initial or final
// state then an error is returned.
func (s *Status) SetFailed(message string) error {
	switch s.State {
	case StateUndefined:
		return errors.Errorf("cannot fail while in an initial state")
	case StateDefined:
		return errors.Errorf("cannot fail while in an initial state")
	case StateStopped:
		return errors.Errorf("cannot fail while in a final state")
	}

	if message == "" {
		message = "problem while interacting with workload"
	}
	s.Blocker = BlockerFailed
	s.Message = message
	return nil
}

// SetError records that the workload isn't working correctly,
// as reported by the plugin. If already in an error state then the
// message is updated. The workload must be in a running state for there
// to be an error. problems during starting and stopping are recorded
// as failures rather than errors.
func (s *Status) SetError(message string) error {
	// TODO(ericsnow) Allow errors in other states?
	switch s.State {
	case StateRunning:
	default:
		return errors.Errorf("can error only while running")
	}

	if message == "" {
		message = "the workload has an error"
	}
	s.Blocker = BlockerError
	s.Message = message
	return nil
}

// Resolve clears any existing error or failure status for the workload.
// If a message is provided then it is set. Otherwise a message
// describing what was resolved will be set. If the workload is both
// failed and in an error state then both will be resolved at once.
// If the workload isn't currently blocked then an error is returned.
func (s *Status) Resolve(message string) error {
	if !s.IsBlocked() {
		// TODO(ericsnow) Do nothing?
		return errors.Errorf("not in an error or failed state")
	}

	defaultMessage := fmt.Sprintf("resolved blocker %q", s.Blocker)
	if message == "" {
		// TODO(ericsnow) Add in the current message?
		message = defaultMessage
	}

	s.Blocker = NotBlocked
	s.Message = message
	return nil
}

// Validate checkes the status to ensure it contains valid data.
func (s Status) Validate() error {
	if !okayStates.Contains(s.State) {
		return errors.NotValidf("Status.State (%q)", s.State)
	}

	switch s.Blocker {
	case BlockerFailed:
		switch s.State {
		case StateUndefined:
			return errors.NotValidf("failure in an initial state")
		case StateDefined:
			return errors.NotValidf("failure in an initial state")
		case StateStopped:
			return errors.NotValidf("failure in a final state")
		}
	case BlockerError:
		if s.State != StateRunning {
			return errors.NotValidf("error outside running state")
		}
	}

	return nil
}

// ValidateState verifies the state passed in is a valid okayState.
func ValidateState(state string) error {
	if !okayStates.Contains(state) {
		return errors.NotValidf("state (%q)", state)
	}
	return nil
}

// PluginStatus represents the data returned from the Plugin.Status call.
type PluginStatus struct {
	// State is the human-readable label returned by the plugin
	// that represents the status of the workload.
	State string `json:"state"`
}

// Validate returns nil if this value is valid, and an error that satisfies
// IsValid if it is not.
func (s PluginStatus) Validate() error {
	if s.State == "" {
		return errors.NotValidf("State cannot be empty")
	}
	return nil
}

// CombinedStatus holds the status information for a workload,
// from all sources.
type CombinedStatus struct {
	// Status is the Juju-level status information.
	Status Status
	// PluginStatus is the plugin-defined status information.
	PluginStatus PluginStatus
}

// Validate returns nil if this value is valid, and an error that satisfies
// IsValid if it is not.
func (s CombinedStatus) Validate() error {
	if err := s.Status.Validate(); err != nil {
		return errors.Trace(err)
	}
	if err := s.PluginStatus.Validate(); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<|endoftext|>