<commit_before>package graft

import (
	"fmt"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/lostz/graft/protocol"
	"github.com/uber-go/zap"

	"google.golang.org/grpc"
)

//Node ...
type Node struct {
	electTimer   *time.Timer
	id           string
	ip           string
	port         int
	leader       string
	logPath      string
	mu           sync.Mutex
	peers        []string
	quit         chan chan struct{}
	server       *grpc.Server
	state        State
	StateChg     chan StateChange
	term         uint64
	vote         string
	VoteRequests chan *protocol.VoteRequest
	VoteResponse chan *protocol.VoteResponse
	HeartBeats   chan *protocol.HeartbeatRequest
}

func (n *Node) broadcastVote() {
	for _, peer := range n.peers {
		n.votePeer(peer)
	}
}

func (n *Node) addPeer(peer string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	if peer == n.id {
		return
	}
	for _, p := range n.peers {
		if p == peer {
			return
		}
	}
	n.peers = append(n.peers, peer)
}

func (n *Node) votePeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(300 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.SendVoteRequest(context.Background(), &protocol.VoteRequest{Term: n.term, Candidate: n.id})
	return
}

func (n *Node) broadcastHearbeat() {

	for _, peer := range n.peers {
		go n.heartbeatPeer(peer)

	}
}

func (n *Node) heartbeatPeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(1000 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("err", err.Error()),
		)
		//peer offline
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.Heartbeat(context.Background(), &protocol.HeartbeatRequest{Term: n.term, Leader: n.id})

}

func (n *Node) clearTimers() {
	if n.electTimer != nil {
		n.electTimer.Stop()
		n.electTimer = nil
	}
}

//Close wait until the state is processed
func (n *Node) Close() {
	if n.State() == CLOSED {
		return
	}
	n.server.GracefulStop()
	n.waitOnLoopFinish()
	n.clearTimers()
	n.closeLog()

}

//Heartbeat from leader
func (n *Node) Heartbeat(context context.Context, requset *protocol.HeartbeatRequest) (*protocol.Response, error) {
	if requset.Term < n.term {
		return &protocol.Response{}, nil
	}
	saveState := false
	if n.State() == LEADER {
		n.switchToFollower("")
	}
	n.mu.Lock()
	n.term = requset.Term
	n.vote = ""
	n.mu.Unlock()
	saveState = true
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
		}
	}
	n.switchToFollower(requset.Leader)
	return &protocol.Response{}, nil
}

func (n *Node) handleHeartBeat(hb *protocol.HeartbeatRequest) bool {
	if hb.Term < n.term {
		return false
	}
	saveState := false
	stepDown := false
	if hb.Term > n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	if n.State() == CANDIDATE && hb.Term >= n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
			stepDown = true
		}
	}
	return stepDown

}

func (n *Node) handleVoteRequest(vreq *protocol.VoteRequest) bool {
	deny := &protocol.VoteResponse{Term: n.term, Granted: false}
	n.addPeer(vreq.Candidate)
	if vreq.Term < n.term {
		n.sendVoteResponse(vreq.Candidate, deny)
		return false
	}
	saveState := false
	stepDown := false
	if vreq.Term > n.term {
		n.term = vreq.Term
		n.vote = ""
		n.leader = ""
		stepDown = true
		saveState = true
	}
	if n.State() == LEADER && !stepDown {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	if n.vote != "" && n.vote != vreq.Candidate {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	n.setVote(vreq.Candidate)
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
			n.setVote("")
			n.sendVoteResponse(vreq.Candidate, deny)
			n.resetElectionTimeout()
			return true
		}

	}
	accept := &protocol.VoteResponse{Term: n.term, Granted: true}
	n.sendVoteResponse(vreq.Candidate, accept)
	n.resetElectionTimeout()
	return stepDown

}

func (n *Node) isRunning() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state != CLOSED
}

func (n *Node) loop() {
	for n.isRunning() {
		switch n.State() {
		case FOLLOWER:
			n.runAsFollower()
		case CANDIDATE:
			n.runAsCandidate()
		case LEADER:
			n.runAsLeader()
		}

	}

}

func (n *Node) processQuit(q chan struct{}) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.state = CLOSED
	close(q)
}

func (n *Node) resetElectionTimeout() {
	n.electTimer.Reset(randElectionTimeout())
}

func (n *Node) runAsCandidate() {
	votes := 1
	n.setVote(n.id)
	if err := n.writeState(); err != nil {
		logger.Error(
			"write state",
			zap.Uint64("term", n.term),
			zap.String("error", err.Error()),
		)
		n.switchToFollower("")
		return
	}
	if n.wonElection(votes) {
		n.switchToLeader()
		return
	}
	n.broadcastVote()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vresp := <-n.VoteResponse:
			if vresp.Granted && vresp.Term == n.term {
				votes++
				if n.wonElection(votes) {
					n.switchToLeader()
					return
				}

			}
		case vreq := <-n.VoteRequests:
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}
	}

}

func (n *Node) runAsFollower() {
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vreq := <-n.VoteRequests:
			if shouldReturn := n.handleVoteRequest(vreq); shouldReturn {
				return
			}
		case hb := <-n.HeartBeats:
			if n.leader == "" {
				n.setLeader(hb.Leader)
			}
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.setLeader(hb.Leader)
			}

		}

	}

}

func (n *Node) runAsLeader() {
	hb := time.NewTicker(100 * time.Millisecond)
	defer hb.Stop()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-hb.C:
			n.broadcastHearbeat()
		case vreq := <-n.VoteRequests:
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}

	}

}

// SendVoteResponse receive voteresponse
func (n *Node) SendVoteResponse(context context.Context, vresp *protocol.VoteResponse) (*protocol.Response, error) {
	n.VoteResponse <- vresp
	return &protocol.Response{}, nil
}

func (n *Node) sendVoteResponse(peer string, vresp *protocol.VoteResponse) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(500 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	_, err = c.SendVoteResponse(context.Background(), vresp)
	if err != nil {
		//peer offline
	}
	return

}

func (n *Node) setTerm(term uint64) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term = term
}
func (n *Node) setVote(candidate string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.vote = candidate
}

func (n *Node) setLeader(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
}

//State node current state
func (n *Node) State() State {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state

}

func (n *Node) switchState(state State) {
	if state == n.state {
		return
	}
	old := n.state
	n.state = state
	sc := StateChange{From: old, To: state}
	n.StateChg <- sc
}

func (n *Node) switchToCandidate() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term++
	n.leader = ""
	n.state = CANDIDATE
	n.resetElectionTimeout()
	n.switchState(CANDIDATE)

}
func (n *Node) switchToFollower(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
	n.switchState(FOLLOWER)
}

func (n *Node) switchToLeader() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = n.id
	n.switchState(LEADER)
}

//SendVoteRequest grpc
func (n *Node) SendVoteRequest(context context.Context, vreq *protocol.VoteRequest) (*protocol.Response, error) {
	n.VoteRequests <- vreq
	return &protocol.Response{}, nil
}

func (n *Node) waitOnLoopFinish() {
	q := make(chan struct{})
	n.quit <- q
	<-q
}

func (n *Node) wonElection(votes int) bool {
	return votes >= quorumNeeded(len(n.peers)+1)
}

//NewNode ....
func NewNode(peers []string, ip, logPath string, port int) (*Node, error) {
	n := &Node{
		ip:    ip,
		state: FOLLOWER,
		peers: peers,
		port:  port,
	}
	n.StateChg = make(chan StateChange)
	n.quit = make(chan chan struct{})
	n.HeartBeats = make(chan *protocol.HeartbeatRequest)
	n.VoteRequests = make(chan *protocol.VoteRequest)
	n.VoteResponse = make(chan *protocol.VoteResponse)
	n.id = fmt.Sprintf("%s:%d", ip, port)
	n.electTimer = time.NewTimer(randElectionTimeout())
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	n.logPath = logPath
	if err := n.initLog(n.logPath); err != nil {
		return nil, err
	}
	g := grpc.NewServer()
	protocol.RegisterRaftServer(g, n)
	n.server = g
	go g.Serve(lis)
	go n.loop()
	return n, nil
}

func quorumNeeded(clusterSize int) int {
	switch clusterSize {
	// Handle 0, but 0 is really an invalid cluster size.
	case 0:
		return 0
	default:
		return clusterSize/2 + 1
	}
}

func randElectionTimeout() time.Duration {
	delta := rand.Int63n(int64(500 * time.Millisecond))
	return (500*time.Millisecond + time.Duration(delta))
}

func appendPeer(peers []string, peer string) []string {
	var tmp []string
	for _, p := range peers {
		if p == peer {
			continue
		}
		tmp = append(tmp, p)
	}
	return tmp
}
<commit_msg>fix bug<commit_after>package graft

import (
	"fmt"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/lostz/graft/protocol"
	"github.com/uber-go/zap"

	"google.golang.org/grpc"
)

//Node ...
type Node struct {
	electTimer   *time.Timer
	id           string
	ip           string
	port         int
	leader       string
	logPath      string
	mu           sync.Mutex
	peers        []string
	quit         chan chan struct{}
	server       *grpc.Server
	state        State
	StateChg     chan StateChange
	term         uint64
	vote         string
	VoteRequests chan *protocol.VoteRequest
	VoteResponse chan *protocol.VoteResponse
	HeartBeats   chan *protocol.HeartbeatRequest
}

func (n *Node) broadcastVote() {
	for _, peer := range n.peers {
		n.votePeer(peer)
	}
}

func (n *Node) addPeer(peer string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	if peer == n.id {
		return
	}
	for _, p := range n.peers {
		if p == peer {
			return
		}
	}
	n.peers = append(n.peers, peer)
}

func (n *Node) votePeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(300 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.SendVoteRequest(context.Background(), &protocol.VoteRequest{Term: n.term, Candidate: n.id})
	return
}

func (n *Node) broadcastHearbeat() {

	for _, peer := range n.peers {
		go n.heartbeatPeer(peer)

	}
}

func (n *Node) heartbeatPeer(peer string) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(1000 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("err", err.Error()),
		)
		//peer offline
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	c.Heartbeat(context.Background(), &protocol.HeartbeatRequest{Term: n.term, Leader: n.id})

}

func (n *Node) clearTimers() {
	if n.electTimer != nil {
		n.electTimer.Stop()
		n.electTimer = nil
	}
}

//Close wait until the state is processed
func (n *Node) Close() {
	if n.State() == CLOSED {
		return
	}
	n.server.GracefulStop()
	n.waitOnLoopFinish()
	n.clearTimers()
	n.closeLog()

}

//Heartbeat from leader
func (n *Node) Heartbeat(context context.Context, requset *protocol.HeartbeatRequest) (*protocol.Response, error) {
	if requset.Term < n.term {
		return &protocol.Response{}, nil
	}
	saveState := false
	if n.State() == LEADER {
		n.switchToFollower("")
	}
	n.mu.Lock()
	n.term = requset.Term
	n.vote = ""
	n.mu.Unlock()
	saveState = true
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
		}
	}
	n.switchToFollower(requset.Leader)
	return &protocol.Response{}, nil
}

func (n *Node) handleHeartBeat(hb *protocol.HeartbeatRequest) bool {
	if hb.Term < n.term {
		return false
	}
	saveState := false
	stepDown := false
	if hb.Term > n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	if n.State() == CANDIDATE && hb.Term >= n.term {
		n.term = hb.Term
		n.vote = ""
		stepDown = true
		saveState = true
	}
	n.resetElectionTimeout()
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
			stepDown = true
		}
	}
	return stepDown

}

func (n *Node) handleVoteRequest(vreq *protocol.VoteRequest) bool {
	deny := &protocol.VoteResponse{Term: n.term, Granted: false}
	n.addPeer(vreq.Candidate)
	if vreq.Term < n.term {
		n.sendVoteResponse(vreq.Candidate, deny)
		return false
	}
	saveState := false
	stepDown := false
	if vreq.Term > n.term {
		n.term = vreq.Term
		n.vote = ""
		n.leader = ""
		stepDown = true
		saveState = true
	}
	if n.State() == LEADER && !stepDown {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	if n.vote != "" && n.vote != vreq.Candidate {
		n.sendVoteResponse(vreq.Candidate, deny)
		return stepDown
	}
	n.setVote(vreq.Candidate)
	if saveState {
		if err := n.writeState(); err != nil {
			logger.Error(
				"write state",
				zap.Uint64("term", n.term),
				zap.String("error", err.Error()),
			)
			n.setVote("")
			n.sendVoteResponse(vreq.Candidate, deny)
			n.resetElectionTimeout()
			return true
		}

	}
	accept := &protocol.VoteResponse{Term: n.term, Granted: true}
	n.sendVoteResponse(vreq.Candidate, accept)
	n.resetElectionTimeout()
	return stepDown

}

func (n *Node) isRunning() bool {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state != CLOSED
}

func (n *Node) loop() {
	for n.isRunning() {
		switch n.State() {
		case FOLLOWER:
			n.runAsFollower()
		case CANDIDATE:
			n.runAsCandidate()
		case LEADER:
			n.runAsLeader()
		}

	}

}

func (n *Node) processQuit(q chan struct{}) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.state = CLOSED
	close(q)
}

func (n *Node) resetElectionTimeout() {
	n.electTimer.Reset(randElectionTimeout())
}

func (n *Node) runAsCandidate() {
	n.switchToCandidate()
	votes := 1
	n.setVote(n.id)
	if err := n.writeState(); err != nil {
		logger.Error(
			"write state",
			zap.Uint64("term", n.term),
			zap.String("error", err.Error()),
		)
		n.switchToFollower("")
		return
	}
	if n.wonElection(votes) {
		n.switchToLeader()
		return
	}
	n.broadcastVote()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vresp := <-n.VoteResponse:
			if vresp.Granted && vresp.Term == n.term {
				votes++
				if n.wonElection(votes) {
					n.switchToLeader()
					return
				}

			}
		case vreq := <-n.VoteRequests:
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}
	}

}

func (n *Node) runAsFollower() {
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-n.electTimer.C:
			n.switchToCandidate()
			return
		case vreq := <-n.VoteRequests:
			if shouldReturn := n.handleVoteRequest(vreq); shouldReturn {
				return
			}
		case hb := <-n.HeartBeats:
			if n.leader == "" {
				n.setLeader(hb.Leader)
			}
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.setLeader(hb.Leader)
			}

		}

	}

}

func (n *Node) runAsLeader() {
	n.switchToLeader()
	hb := time.NewTicker(100 * time.Millisecond)
	defer hb.Stop()
	for {
		select {
		case q := <-n.quit:
			n.processQuit(q)
			return
		case <-hb.C:
			n.broadcastHearbeat()
		case vreq := <-n.VoteRequests:
			if stepDown := n.handleVoteRequest(vreq); stepDown {
				n.switchToFollower("")
				return
			}
		case hb := <-n.HeartBeats:
			if stepDown := n.handleHeartBeat(hb); stepDown {
				n.switchToFollower(hb.Leader)
				return
			}

		}

	}

}

// SendVoteResponse receive voteresponse
func (n *Node) SendVoteResponse(context context.Context, vresp *protocol.VoteResponse) (*protocol.Response, error) {
	n.VoteResponse <- vresp
	return &protocol.Response{}, nil
}

func (n *Node) sendVoteResponse(peer string, vresp *protocol.VoteResponse) {
	conn, err := grpc.Dial(peer, []grpc.DialOption{grpc.WithTimeout(500 * time.Millisecond), grpc.WithInsecure()}...)
	if err != nil {
		logger.Error(
			"grpc dial",
			zap.String("err", err.Error()),
		)
		return
	}
	defer conn.Close()
	c := protocol.NewRaftClient(conn)
	_, err = c.SendVoteResponse(context.Background(), vresp)
	if err != nil {
		//peer offline
	}
	return

}

func (n *Node) setTerm(term uint64) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term = term
}
func (n *Node) setVote(candidate string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.vote = candidate
}

func (n *Node) setLeader(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
}

//State node current state
func (n *Node) State() State {
	n.mu.Lock()
	defer n.mu.Unlock()
	return n.state

}

func (n *Node) switchState(state State) {
	if state == n.state {
		return
	}
	old := n.state
	n.state = state
	sc := StateChange{From: old, To: state}
	n.StateChg <- sc
}

func (n *Node) switchToCandidate() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.term++
	n.leader = ""
	n.resetElectionTimeout()
	n.switchState(CANDIDATE)

}
func (n *Node) switchToFollower(leader string) {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = leader
	n.switchState(FOLLOWER)
}

func (n *Node) switchToLeader() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.leader = n.id
	n.switchState(LEADER)
}

//SendVoteRequest grpc
func (n *Node) SendVoteRequest(context context.Context, vreq *protocol.VoteRequest) (*protocol.Response, error) {
	n.VoteRequests <- vreq
	return &protocol.Response{}, nil
}

func (n *Node) waitOnLoopFinish() {
	q := make(chan struct{})
	n.quit <- q
	<-q
}

func (n *Node) wonElection(votes int) bool {
	return votes >= quorumNeeded(len(n.peers)+1)
}

//NewNode ....
func NewNode(peers []string, ip, logPath string, port int) (*Node, error) {
	n := &Node{
		ip:    ip,
		state: FOLLOWER,
		peers: peers,
		port:  port,
	}
	n.StateChg = make(chan StateChange)
	n.quit = make(chan chan struct{})
	n.HeartBeats = make(chan *protocol.HeartbeatRequest)
	n.VoteRequests = make(chan *protocol.VoteRequest)
	n.VoteResponse = make(chan *protocol.VoteResponse)
	n.id = fmt.Sprintf("%s:%d", ip, port)
	n.electTimer = time.NewTimer(randElectionTimeout())
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	n.logPath = logPath
	if err := n.initLog(n.logPath); err != nil {
		return nil, err
	}
	g := grpc.NewServer()
	protocol.RegisterRaftServer(g, n)
	n.server = g
	go g.Serve(lis)
	go n.loop()
	return n, nil
}

func quorumNeeded(clusterSize int) int {
	switch clusterSize {
	// Handle 0, but 0 is really an invalid cluster size.
	case 0:
		return 0
	default:
		return clusterSize/2 + 1
	}
}

func randElectionTimeout() time.Duration {
	delta := rand.Int63n(int64(500 * time.Millisecond))
	return (500*time.Millisecond + time.Duration(delta))
}

func appendPeer(peers []string, peer string) []string {
	var tmp []string
	for _, p := range peers {
		if p == peer {
			continue
		}
		tmp = append(tmp, p)
	}
	return tmp
}
<|endoftext|>


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

package flow

import (
	"database/sql"
	"errors"
	"log"
)

// NodeID is the type of unique identifiers of nodes.
type NodeID int64

// NodeFunc defines the type of functions that generate notification
// messages in workflows.
//
// These functions are triggered by appropriate nodes, when document
// events are applied to documents to possibly transform them.
// Invocation of a `NodeFunc` should result in a message that can then
// be dispatched to applicable mailboxes.
//
// Error should be returned only when an impossible situation arises,
// and processing needs to abort.  Note that returning an error stops
// the workflow.  Manual intervention will be needed to move the
// document further.
//
// N. B. NodeFunc instances must be referentially transparent --
// stateless and not capture their environment in any manner.
// Unexpected bad things could happen otherwise!
type NodeFunc func(*Document, *DocEvent) *Message

// defNodeFunc prepares a simple message that can be posted to
// applicable mailboces.
func defNodeFunc(d *Document, event *DocEvent) *Message {
	return &Message{
		DocType: DocType{
			ID: d.DocType.ID,
		},
		DocID: d.ID,
		Event: event.ID,
		Title: d.Title,
		Data:  event.Text,
	}
}

// Node represents a specific logical unit of processing and routing
// in a workflow.
type Node struct {
	ID       NodeID          `json:"ID"`                      // Unique identifier of this node
	DocType  DocTypeID       `json:"DocType"`                 // Document type which this node's workflow manages
	State    DocStateID      `json:"DocState"`                // A document arriving at this node must be in this state
	AccCtx   AccessContextID `json:"AccessContext,omitempty"` // Specific access context associated with this state, if any
	Wflow    WorkflowID      `json:"Workflow"`                // Containing flow of this node
	Name     string          `json:"Name"`                    // Unique within its workflow
	NodeType NodeType        `json:"NodeType"`                // Topology type of this node
	nfunc    NodeFunc        // Processing function of this node
}

// Transitions answers the possible document states into which a
// document currently in the given state can transition.
func (n *Node) Transitions() (map[DocActionID]DocStateID, error) {
	return DocTypes._Transitions(n.DocType, n.State)
}

// SetFunc registers the given node function with this node.
//
// If `nil` is given, a default node function is registered instead.
// This default function sets the document title as the message
// subject, and the event's data as the message body.
func (n *Node) SetFunc(fn NodeFunc) error {
	if fn == nil {
		n.nfunc = defNodeFunc
		return nil
	}

	n.nfunc = fn
	return nil
}

// Func answers the processing function registered in this node
// definition.
func (n *Node) Func() NodeFunc {
	return n.nfunc
}

// applyEvent checks to see if the given event can be applied
// successfully.  Accordingly, it prepares a message by utilising the
// registered node function, and posts it to applicable mailboxes.
func (n *Node) applyEvent(otx *sql.Tx, event *DocEvent, recipients []GroupID) (DocStateID, error) {
	ts, err := n.Transitions()
	if err != nil {
		return 0, err
	}
	tstate, ok := ts[event.Action]
	if !ok {
		return 0, ErrWorkflowInvalidAction
	}

	// Check document's current state.
	doc, err := Documents.Get(otx, event.DocType, event.DocID)
	if err != nil {
		return 0, err
	}
	if doc.State.ID != event.State {
		return 0, ErrDocEventStateMismatch
	}

	// Document has already transitioned.  So, we note that the event
	// is applied, and return.
	//
	// N.B. This has implications for `NodeTypeJoinAny` below.  Should
	// you alter this logic or its position, verify that the
	// corresponding logic in the switch below is in coherence.
	if doc.State.ID == tstate {
		err = n.recordEvent(otx, event, tstate, true)
		if err != nil {
			return 0, err
		}
		return tstate, ErrDocEventRedundant
	}

	// Transition document state according to the target node type.

	tnode, err := Nodes.GetByState(n.DocType, tstate)
	if err != nil {
		return 0, err
	}

	switch tnode.NodeType {
	case NodeTypeJoinAny:
		// Multiple 'in's, but any one suffices.

		// We have already checked to see if the document has
		// transitioned into the target state.  If we have come this
		// far, the event can be applied.
		fallthrough

	case NodeTypeBegin, NodeTypeEnd, NodeTypeLinear, NodeTypeBranch:
		// Any node type having a single 'in'.

		// Update the document to transition the state.
		err = Documents.setState(otx, event.DocType, event.DocID, tstate, tnode.AccCtx)
		if err != nil {
			return 0, err
		}

		// Record event application.
		err = n.recordEvent(otx, event, tstate, false)
		if err != nil {
			return 0, err
		}

		// Post messages.
		msg := n.nfunc(doc, event)
		if len(recipients) == 0 {
			recipients, err = tnode.determineRecipients(otx, event.Group)
			if err != nil {
				return 0, err
			}
		}
		// It is legal to not have any recipients, too.
		if len(recipients) > 0 {
			err = n.postMessage(otx, msg, recipients)
			if err != nil {
				return 0, err
			}
		}

	case NodeTypeJoinAll:
		// Multiple 'in's, and all are required.

		// TODO(js)

	default:
		log.Panicf("unknown node type encountered : %s\n", tnode.NodeType)
	}

	return tstate, nil
}

// recordEvent writes a record stating that the given event has
// successfully been applied to effect a document state transition.
func (n *Node) recordEvent(otx *sql.Tx, event *DocEvent, tstate DocStateID, statusOnly bool) error {
	if !statusOnly {
		q := `
		INSERT INTO wf_docevent_application(doctype_id, doc_id, from_state_id, docevent_id, to_state_id)
		VALUES(?, ?, ?, ?, ?)
		`
		_, err := otx.Exec(q, event.DocType, event.DocID, event.State, event.ID, tstate)
		if err != nil {
			return err
		}
	}

	q := `UPDATE wf_docevents SET status = 'A' WHERE id = ?`
	_, err := otx.Exec(q, event.ID)
	if err != nil {
		return err
	}

	return nil
}

// determineRecipients takes the document type and access context into
// account, and determines the list of groups to which the
// notification should be posted.
func (n *Node) determineRecipients(otx *sql.Tx, group GroupID) ([]GroupID, error) {
	q := `
	SELECT reports_to
	FROM wf_ac_group_hierarchy
	WHERE ac_id = ?
	AND group_id = ?
	ORDER BY group_id
	LIMIT 1
	`
	rows, err := otx.Query(q, n.AccCtx, group)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ary := make([]GroupID, 0, 4)
	for rows.Next() {
		var gid int64
		err = rows.Scan(&gid)
		if err != nil {
			return nil, err
		}
		ary = append(ary, GroupID(gid))
	}
	if rows.Err() != nil {
		return nil, err
	}
	return ary, nil
}

// postMessage posts the given message into the mailboxes of the
// specified recipients.
func (n *Node) postMessage(otx *sql.Tx, msg *Message, recipients []GroupID) error {
	// Record the message.

	q := `
	INSERT INTO wf_messages(doctype_id, doc_id, docevent_id, title, data)
	VALUES(?, ?, ?, ?, ?)
	`
	res, err := otx.Exec(q, msg.DocType.ID, msg.DocID, msg.Event, msg.Title, msg.Data)
	if err != nil {
		return err
	}
	var msgid int64
	if msgid, err = res.LastInsertId(); err != nil {
		return err
	}

	// Post it into applicable mailboxes.

	q = `
	INSERT INTO wf_mailboxes(group_id, message_id, unread)
	VALUES(?, ?, 1)
	`
	for _, gid := range recipients {
		res, err = otx.Exec(q, gid, msgid)
		if err != nil {
			return err
		}
	}

	return nil
}

// Unexported type, only for convenience methods.
type _Nodes struct{}

// Nodes provides a resource-like interface to the nodes defined in
// this system.
var Nodes _Nodes

// List answers a list of the nodes comprising the given workflow.
func (_Nodes) List(id WorkflowID) ([]*Node, error) {
	q := `
	SELECT id, doctype_id, docstate_id, workflow_id, name, type
	FROM wf_workflow_nodes
	WHERE workflow_id = ?
	`
	rows, err := db.Query(q, id)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ary := make([]*Node, 0, 5)
	for rows.Next() {
		var elem Node
		err = rows.Scan(&elem.ID, &elem.DocType, &elem.State, &elem.Wflow, &elem.Name, &elem.NodeType)
		if err != nil {
			return nil, err
		}
		elem.nfunc = defNodeFunc
		ary = append(ary, &elem)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return ary, nil
}

// Get retrieves the requested node from the database.
func (_Nodes) Get(id NodeID) (*Node, error) {
	if id <= 0 {
		return nil, errors.New("node ID must be a positive integer")
	}

	var elem Node
	var acID sql.NullInt64
	q := `
	SELECT id, doctype_id, docstate_id, ac_id, workflow_id, name, type
	FROM wf_workflow_nodes
	WHERE id = ?
	`
	row := db.QueryRow(q, id)
	err := row.Scan(&elem.ID, &elem.DocType, &elem.State, &acID, &elem.Wflow, &elem.Name, &elem.NodeType)
	if err != nil {
		return nil, err
	}
	if acID.Valid {
		elem.AccCtx = AccessContextID(acID.Int64)
	}

	elem.nfunc = defNodeFunc
	return &elem, nil
}

// GetByState retrieves the requested node from the database, as per
// the document state specification.
func (_Nodes) GetByState(dtype DocTypeID, state DocStateID) (*Node, error) {
	var elem Node
	var acID sql.NullInt64
	q := `
	SELECT id, doctype_id, docstate_id, ac_id, workflow_id, name, type
	FROM wf_workflow_nodes
	WHERE doctype_id = ?
	AND docstate_id = ?
	`
	row := db.QueryRow(q, dtype, state)
	err := row.Scan(&elem.ID, &elem.DocType, &elem.State, &acID, &elem.Wflow, &elem.Name, &elem.NodeType)
	if err != nil {
		return nil, err
	}
	if acID.Valid {
		elem.AccCtx = AccessContextID(acID.Int64)
	}

	elem.nfunc = defNodeFunc
	return &elem, nil
}
<commit_msg>Move documents across access contexts, when needed, upon state change<commit_after>// (c) Copyright 2015-2017 JONNALAGADDA Srinivas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package flow

import (
	"database/sql"
	"errors"
	"log"
)

// NodeID is the type of unique identifiers of nodes.
type NodeID int64

// NodeFunc defines the type of functions that generate notification
// messages in workflows.
//
// These functions are triggered by appropriate nodes, when document
// events are applied to documents to possibly transform them.
// Invocation of a `NodeFunc` should result in a message that can then
// be dispatched to applicable mailboxes.
//
// Error should be returned only when an impossible situation arises,
// and processing needs to abort.  Note that returning an error stops
// the workflow.  Manual intervention will be needed to move the
// document further.
//
// N. B. NodeFunc instances must be referentially transparent --
// stateless and not capture their environment in any manner.
// Unexpected bad things could happen otherwise!
type NodeFunc func(*Document, *DocEvent) *Message

// defNodeFunc prepares a simple message that can be posted to
// applicable mailboces.
func defNodeFunc(d *Document, event *DocEvent) *Message {
	return &Message{
		DocType: DocType{
			ID: d.DocType.ID,
		},
		DocID: d.ID,
		Event: event.ID,
		Title: d.Title,
		Data:  event.Text,
	}
}

// Node represents a specific logical unit of processing and routing
// in a workflow.
type Node struct {
	ID       NodeID          `json:"ID"`                      // Unique identifier of this node
	DocType  DocTypeID       `json:"DocType"`                 // Document type which this node's workflow manages
	State    DocStateID      `json:"DocState"`                // A document arriving at this node must be in this state
	AccCtx   AccessContextID `json:"AccessContext,omitempty"` // Specific access context associated with this state, if any
	Wflow    WorkflowID      `json:"Workflow"`                // Containing flow of this node
	Name     string          `json:"Name"`                    // Unique within its workflow
	NodeType NodeType        `json:"NodeType"`                // Topology type of this node
	nfunc    NodeFunc        // Processing function of this node
}

// Transitions answers the possible document states into which a
// document currently in the given state can transition.
func (n *Node) Transitions() (map[DocActionID]DocStateID, error) {
	return DocTypes._Transitions(n.DocType, n.State)
}

// SetFunc registers the given node function with this node.
//
// If `nil` is given, a default node function is registered instead.
// This default function sets the document title as the message
// subject, and the event's data as the message body.
func (n *Node) SetFunc(fn NodeFunc) error {
	if fn == nil {
		n.nfunc = defNodeFunc
		return nil
	}

	n.nfunc = fn
	return nil
}

// Func answers the processing function registered in this node
// definition.
func (n *Node) Func() NodeFunc {
	return n.nfunc
}

// applyEvent checks to see if the given event can be applied
// successfully.  Accordingly, it prepares a message by utilising the
// registered node function, and posts it to applicable mailboxes.
func (n *Node) applyEvent(otx *sql.Tx, event *DocEvent, recipients []GroupID) (DocStateID, error) {
	ts, err := n.Transitions()
	if err != nil {
		return 0, err
	}
	tstate, ok := ts[event.Action]
	if !ok {
		return 0, ErrWorkflowInvalidAction
	}

	// Check document's current state.
	doc, err := Documents.Get(otx, event.DocType, event.DocID)
	if err != nil {
		return 0, err
	}
	if doc.State.ID != event.State {
		return 0, ErrDocEventStateMismatch
	}

	// Document has already transitioned.  So, we note that the event
	// is applied, and return.
	//
	// N.B. This has implications for `NodeTypeJoinAny` below.  Should
	// you alter this logic or its position, verify that the
	// corresponding logic in the switch below is in coherence.
	if doc.State.ID == tstate {
		err = n.recordEvent(otx, event, tstate, true)
		if err != nil {
			return 0, err
		}
		return tstate, ErrDocEventRedundant
	}

	// Transition document state according to the target node type.

	tnode, err := Nodes.GetByState(n.DocType, tstate)
	if err != nil {
		return 0, err
	}

	switch tnode.NodeType {
	case NodeTypeJoinAny:
		// Multiple 'in's, but any one suffices.

		// We have already checked to see if the document has
		// transitioned into the target state.  If we have come this
		// far, the event can be applied.
		fallthrough

	case NodeTypeBegin, NodeTypeEnd, NodeTypeLinear, NodeTypeBranch:
		// Any node type having a single 'in'.

		// Update the document to transition the state.
		tacid := tnode.AccCtx
		if tacid == 0 {
			tacid = doc.AccCtx.ID
		}
		err = Documents.setState(otx, event.DocType, event.DocID, tstate, tacid)
		if err != nil {
			return 0, err
		}

		// Record event application.
		err = n.recordEvent(otx, event, tstate, false)
		if err != nil {
			return 0, err
		}

		// Post messages.
		msg := n.nfunc(doc, event)
		recipients, err = tnode.determineRecipients(otx, recipients, doc, event, tacid)
		if err != nil {
			return 0, err
		}
		// It is legal to not have any recipients, too.
		if len(recipients) > 0 {
			err = n.postMessage(otx, msg, recipients)
			if err != nil {
				return 0, err
			}
		}

	case NodeTypeJoinAll:
		// Multiple 'in's, and all are required.

		// TODO(js)

	default:
		log.Panicf("unknown node type encountered : %s\n", tnode.NodeType)
	}

	return tstate, nil
}

// recordEvent writes a record stating that the given event has
// successfully been applied to effect a document state transition.
func (n *Node) recordEvent(otx *sql.Tx, event *DocEvent, tstate DocStateID, statusOnly bool) error {
	if !statusOnly {
		q := `
		INSERT INTO wf_docevent_application(doctype_id, doc_id, from_state_id, docevent_id, to_state_id)
		VALUES(?, ?, ?, ?, ?)
		`
		_, err := otx.Exec(q, event.DocType, event.DocID, event.State, event.ID, tstate)
		if err != nil {
			return err
		}
	}

	q := `UPDATE wf_docevents SET status = 'A' WHERE id = ?`
	_, err := otx.Exec(q, event.ID)
	if err != nil {
		return err
	}

	return nil
}

// determineRecipients takes the document type and access context into
// account, and determines the list of groups to which the
// notification should be posted.
func (n *Node) determineRecipients(otx *sql.Tx, ary []GroupID, doc *Document, event *DocEvent, acid AccessContextID) ([]GroupID, error) {
	// We have to notify reporting authorities.
	q := `
	SELECT reports_to
	FROM wf_ac_group_hierarchy
	WHERE ac_id = ?
	AND group_id = ?
	ORDER BY group_id
	LIMIT 1
	`
	rows, err := otx.Query(q, acid, event.Group)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var gid int64
		err = rows.Scan(&gid)
		if err != nil {
			return nil, err
		}
		ary = append(ary, GroupID(gid))
	}
	if rows.Err() != nil {
		return nil, err
	}

	// We also notify all participants in the thread.
	q2 := `
	SELECT DISTINCT (group_id)
	FROM wf_docevents
	WHERE doctype_id = ?
	AND doc_id = ?
	`
	rows2, err := otx.Query(q2, doc.DocType.ID, doc.ID)
	if err != nil {
		return nil, err
	}
	defer rows2.Close()

	for rows2.Next() {
		var gid int64
		err = rows2.Scan(&gid)
		if err != nil {
			return nil, err
		}
		ary = append(ary, GroupID(gid))
	}
	if rows2.Err() != nil {
		return nil, err
	}

	return ary, nil
}

// postMessage posts the given message into the mailboxes of the
// specified recipients.
func (n *Node) postMessage(otx *sql.Tx, msg *Message, recipients []GroupID) error {
	// Record the message.

	q := `
	INSERT INTO wf_messages(doctype_id, doc_id, docevent_id, title, data)
	VALUES(?, ?, ?, ?, ?)
	`
	res, err := otx.Exec(q, msg.DocType.ID, msg.DocID, msg.Event, msg.Title, msg.Data)
	if err != nil {
		return err
	}
	var msgid int64
	if msgid, err = res.LastInsertId(); err != nil {
		return err
	}

	// Post it into applicable mailboxes.

	q = `
	INSERT INTO wf_mailboxes(group_id, message_id, unread)
	VALUES(?, ?, 1)
	`
	for _, gid := range recipients {
		res, err = otx.Exec(q, gid, msgid)
		if err != nil {
			return err
		}
	}

	return nil
}

// Unexported type, only for convenience methods.
type _Nodes struct{}

// Nodes provides a resource-like interface to the nodes defined in
// this system.
var Nodes _Nodes

// List answers a list of the nodes comprising the given workflow.
func (_Nodes) List(id WorkflowID) ([]*Node, error) {
	q := `
	SELECT id, doctype_id, docstate_id, workflow_id, name, type
	FROM wf_workflow_nodes
	WHERE workflow_id = ?
	`
	rows, err := db.Query(q, id)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ary := make([]*Node, 0, 5)
	for rows.Next() {
		var elem Node
		err = rows.Scan(&elem.ID, &elem.DocType, &elem.State, &elem.Wflow, &elem.Name, &elem.NodeType)
		if err != nil {
			return nil, err
		}
		elem.nfunc = defNodeFunc
		ary = append(ary, &elem)
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}

	return ary, nil
}

// Get retrieves the requested node from the database.
func (_Nodes) Get(id NodeID) (*Node, error) {
	if id <= 0 {
		return nil, errors.New("node ID must be a positive integer")
	}

	var elem Node
	var acID sql.NullInt64
	q := `
	SELECT id, doctype_id, docstate_id, ac_id, workflow_id, name, type
	FROM wf_workflow_nodes
	WHERE id = ?
	`
	row := db.QueryRow(q, id)
	err := row.Scan(&elem.ID, &elem.DocType, &elem.State, &acID, &elem.Wflow, &elem.Name, &elem.NodeType)
	if err != nil {
		return nil, err
	}
	if acID.Valid {
		elem.AccCtx = AccessContextID(acID.Int64)
	}

	elem.nfunc = defNodeFunc
	return &elem, nil
}

// GetByState retrieves the requested node from the database, as per
// the document state specification.
func (_Nodes) GetByState(dtype DocTypeID, state DocStateID) (*Node, error) {
	var elem Node
	var acID sql.NullInt64
	q := `
	SELECT id, doctype_id, docstate_id, ac_id, workflow_id, name, type
	FROM wf_workflow_nodes
	WHERE doctype_id = ?
	AND docstate_id = ?
	`
	row := db.QueryRow(q, dtype, state)
	err := row.Scan(&elem.ID, &elem.DocType, &elem.State, &acID, &elem.Wflow, &elem.Name, &elem.NodeType)
	if err != nil {
		return nil, err
	}
	if acID.Valid {
		elem.AccCtx = AccessContextID(acID.Int64)
	}

	elem.nfunc = defNodeFunc
	return &elem, nil
}
<|endoftext|>


<commit_before>package yamlpatch

import "reflect"

// Node holds a YAML document that has not yet been processed into a NodeMap or
// NodeSlice
type Node struct {
	raw       *interface{}
	container Container
}

// NewNode returns a new Node. It expects a pointer to an interface{}
func NewNode(raw *interface{}) *Node {
	return &Node{
		raw: raw,
	}
}

// MarshalYAML implements yaml.Marshaler, and returns the correct interface{}
// to be marshaled
func (n *Node) MarshalYAML() (interface{}, error) {
	if n == nil {
		return nil, nil
	}

	if n.container != nil {
		return n.container, nil
	}

	return *n.raw, nil
}

// UnmarshalYAML implements yaml.Unmarshaler
func (n *Node) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var data interface{}

	err := unmarshal(&data)
	if err != nil {
		return err
	}

	n.raw = &data
	return nil
}

// Empty returns whether the raw value is nil
func (n *Node) Empty() bool {
	return n == nil || *n.raw == nil
}

// Container returns the node as a Container
func (n *Node) Container() Container {
	if n.container != nil {
		return n.container
	}

	switch rt := (*n.raw).(type) {
	case []interface{}:
		c := make(nodeSlice, len(rt))
		n.container = &c

		for i := range rt {
			c[i] = NewNode(&rt[i])
		}
	case map[interface{}]interface{}:
		c := make(nodeMap, len(rt))
		n.container = &c

		for k := range rt {
			v := rt[k]
			c[k] = NewNode(&v)
		}
	}

	return n.container
}

// Equal compares the values of the raw interfaces that the YAML was
// unmarshaled into
func (n *Node) Equal(other *Node) bool {
	if n == nil {
		return other == nil
	}

	return reflect.DeepEqual(*n.raw, *other.raw)
}

// Value returns the raw value of the node
func (n *Node) Value() interface{} {
	return *n.raw
}
<commit_msg>Add helper methods for creating nodes<commit_after>package yamlpatch

import "reflect"

// Node holds a YAML document that has not yet been processed into a NodeMap or
// NodeSlice
type Node struct {
	raw       *interface{}
	container Container
}

// NewNode returns a new Node. It expects a pointer to an interface{}
func NewNode(raw *interface{}) *Node {
	return &Node{
		raw: raw,
	}
}

// NewNodeFromMap returns a new Node based on a map[interface{}]interface{}
func NewNodeFromMap(m map[interface{}]interface{}) *Node {
	var raw interface{}
	raw = m

	return &Node{
		raw: &raw,
	}
}

// NewNodeFromSlice returns a new Node based on a []interface{}
func NewNodeFromSlice(s []interface{}) *Node {
	var raw interface{}
	raw = s

	return &Node{
		raw: &raw,
	}
}

// MarshalYAML implements yaml.Marshaler, and returns the correct interface{}
// to be marshaled
func (n *Node) MarshalYAML() (interface{}, error) {
	if n == nil {
		return nil, nil
	}

	if n.container != nil {
		return n.container, nil
	}

	return *n.raw, nil
}

// UnmarshalYAML implements yaml.Unmarshaler
func (n *Node) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var data interface{}

	err := unmarshal(&data)
	if err != nil {
		return err
	}

	n.raw = &data
	return nil
}

// Empty returns whether the raw value is nil
func (n *Node) Empty() bool {
	return n == nil || *n.raw == nil
}

// Container returns the node as a Container
func (n *Node) Container() Container {
	if n.container != nil {
		return n.container
	}

	switch rt := (*n.raw).(type) {
	case []interface{}:
		c := make(nodeSlice, len(rt))
		n.container = &c

		for i := range rt {
			c[i] = NewNode(&rt[i])
		}
	case map[interface{}]interface{}:
		c := make(nodeMap, len(rt))
		n.container = &c

		for k := range rt {
			v := rt[k]
			c[k] = NewNode(&v)
		}
	}

	return n.container
}

// Equal compares the values of the raw interfaces that the YAML was
// unmarshaled into
func (n *Node) Equal(other *Node) bool {
	if n == nil {
		return other == nil
	}

	return reflect.DeepEqual(*n.raw, *other.raw)
}

// Value returns the raw value of the node
func (n *Node) Value() interface{} {
	return *n.raw
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"sort"
)

type Note struct {
	NoteId int64
	UserId int64
	Text string
	Link sql.NullString
	LinkType sql.NullString
	Date mysql.NullTime
	Edited bool
	Deleted bool
	GroupId int64
}

// interface for sorting strings by length
type ByLength []string
func (s ByLength) Len() int {
    return len(s)
}
func (s ByLength) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
    return len(s[i]) < len(s[j])
}

func parseNote(text string) (note *Note) {
	note = new(Note)
	note.Text = text

	// TODO: find @mentions
	// atrx := MustCompile("@([[:alnum:]]{1,16})@((?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z]))")
	// TODO: to be shortened, @mentions must be users that exist and are not blocking this user
	// TODO: defer that process so we can return to the user right away and return 202 Accepted

	// TODO: we will replace @mentions with @<number>
	// TODO: find the highest @<number> that doesn't collide with existing text
	// clients will substitute mentions for highest @<numbers> in reverse order

	// find all things that look like links
	linkrx := regexp.MustCompile("\\b(?i:https?|ftp)://\\S+")
	matches := linkrx.FindAllString(note.Text, -1)
	//fmt.Println(matches)

	if matches != nil {
		// find the longest link
		sort.Sort(ByLength(matches))
		note.Link.String = matches[len(matches) - 1]
		note.Link.Valid = true

		// ‡<number> indicates location to insert link
		dagIdx := 0
		// if for some strange reason someone actually typed that, we search for the highest non-colliding index
		dagrx := regexp.MustCompile("‡\\d+")
		daggers := dagrx.FindAllString(note.Text, 0)
		if daggers != nil {
			for _, dagger := range daggers {
				d2, _ := strconv.Atoi(dagger[1:])
				if d2 >= dagIdx {
					dagIdx = d2 + 1
				}
			}
		}

		// replace the link with our symbol
		// clients can later replace ‡<highest number> with the link
		dagger := fmt.Sprintf("‡%d", dagIdx)
		note.Text = strings.Replace(note.Text, note.Link.String, dagger, 1)
	}

	if len(note.Text) > 140 || len(note.Text) == 0 {
		// TODO: return error?
		return nil
	}
	return note
}

func ListNotesHandler(rw http.ResponseWriter, r *http.Request) {
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	// TODO: range query parms
	// TODO: guest authorization and user id parm

	notes := []Note{}
	err = db.Select(&notes, "SELECT * FROM Note WHERE UserId = ?", token.UserId)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}

	sendData(rw, http.StatusOK, notes)
}

func PostNoteHandler(rw http.ResponseWriter, r *http.Request) {
	// TODO: we should make an auth middleware, once I can wrap my head around that
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	r.ParseForm()

	noteText := r.PostFormValue("note")
	note := parseNote(noteText)

	if note == nil {
		sendError(rw, http.StatusBadRequest, "Bad Request")
		return
	}

	// TODO: groups
	group := r.PostFormValue("group")
	if len(group) > 0 {
		groupId, _ := strconv.Atoi(group)
		note.GroupId = int64(groupId)
	}

	// TODO: defer processing mentions

	note.UserId = token.UserId
	result, err := db.NamedExec("INSERT INTO `Note` (`UserId`, `Text`, `Link`, `LinkType`, `GroupId`) " +
			"VALUES (:UserId, :Text, :Link, :LinkType, :GroupId)", note)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	note.NoteId, err = result.LastInsertId()
	if err != nil {
		fmt.Println(err)
	}

	sendData(rw, http.StatusCreated, note)
}

func GetNoteHandler(rw http.ResponseWriter, r *http.Request) {
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	noteId, err := strconv.Atoi(mux.Vars(r)["id"])
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusBadRequest, err.Error())
		return
	}

	// TODO: guest authorization

	note := new(Note)
	err = db.Get(note, "SELECT * FROM Note WHERE NoteId = ?", noteId)
	if err == sql.ErrNoRows {
		sendError(rw, http.StatusNotFound, "There is no note with that ID.")
		return
	} else if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}

	sendData(rw, http.StatusOK, note)
}

func PutNoteHandler(rw http.ResponseWriter, r *http.Request) {
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	noteId, err := strconv.Atoi(mux.Vars(r)["id"])
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusBadRequest, err.Error())
		return
	}

	var noteUser int64
	err = db.Get(&noteUser, "SELECT UserId FROM Note WHERE NoteId = ?", noteId)
	if err == sql.ErrNoRows {
		sendError(rw, http.StatusNotFound, "There is no note with that ID.")
		return
	} else if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	} else if noteUser != token.UserId {
		sendError(rw, http.StatusUnauthorized, "Only the note's author may edit it.")
		return
	}

	r.ParseForm()
	noteText := r.PostFormValue("note")
	note := parseNote(noteText)
	if note == nil {
		sendError(rw, http.StatusBadRequest, "Bad Request")
		return
	}

	_, err = db.NamedExec("UPDATE Note SET Text = :Text, Link = :Link, Edited = 1 "+
		"WHERE NoteId = :NoteId", note)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}

	sendData(rw, http.StatusOK, note)
}

func DeleteNoteHandler(rw http.ResponseWriter, r *http.Request) {
	// TODO: auth middleware
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	// TODO: noteId parm middleware
	noteId, err := strconv.Atoi(mux.Vars(r)["id"])
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusBadRequest, err.Error())
		return
	}

	// TODO: owner auth middleware
	var noteUser int64
	err = db.Get(&noteUser, "SELECT UserId FROM Note WHERE NoteId = ?", noteId)
	if err == sql.ErrNoRows {
		sendError(rw, http.StatusNotFound, "There is no note with that ID.")
		return
	} else if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	} else if noteUser != token.UserId {
		sendError(rw, http.StatusUnauthorized, "Only the note's author may delete it.")
		return
	}

	_, err = db.Exec("DELETE FROM `Note` WHERE NoteId = ?", noteId)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	sendData(rw, http.StatusNoContent, "")
}

<commit_msg>friendlier note data format<commit_after>package main

import (
	"database/sql"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"sort"
)

type Note struct {
	NoteId int64
	UserId int64
	Text string
	Link sql.NullString
	LinkType sql.NullString
	Date mysql.NullTime
	Edited bool
	Deleted bool
	GroupId int64
}

// TODO: use Marshaler interface
func (n *Note) AsMap() *map[string]interface{} {
	m := map[string]interface{}{
		"NoteId": n.NoteId,
		"UserId": n.UserId,
		"Text": n.Text,
		"Date": n.Date.Time.Unix(),
		"Edited": n.Edited,
		"GroupId": n.GroupId,
	}
	if n.Link.Valid {
		m["Link"] = n.Link.String
	}
	if n.LinkType.Valid {
		m["LinkType"] = n.LinkType.String
	}
	return &m
}

// interface for sorting strings by length
type ByLength []string
func (s ByLength) Len() int {
    return len(s)
}
func (s ByLength) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func (s ByLength) Less(i, j int) bool {
    return len(s[i]) < len(s[j])
}

func parseNote(text string) (note *Note) {
	note = new(Note)
	note.Text = text

	// TODO: find @mentions
	// atrx := MustCompile("@([[:alnum:]]{1,16})@((?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z]))")
	// TODO: to be shortened, @mentions must be users that exist and are not blocking this user
	// TODO: defer that process so we can return to the user right away and return 202 Accepted

	// TODO: we will replace @mentions with @<number>
	// TODO: find the highest @<number> that doesn't collide with existing text
	// clients will substitute mentions for highest @<numbers> in reverse order

	// find all things that look like links
	linkrx := regexp.MustCompile("\\b(?i:https?|ftp)://\\S+")
	matches := linkrx.FindAllString(note.Text, -1)
	//fmt.Println(matches)

	if matches != nil {
		// find the longest link
		sort.Sort(ByLength(matches))
		note.Link.String = matches[len(matches) - 1]
		note.Link.Valid = true

		// ‡<number> indicates location to insert link
		dagIdx := 0
		// if for some strange reason someone actually typed that, we search for the highest non-colliding index
		dagrx := regexp.MustCompile("‡\\d+")
		daggers := dagrx.FindAllString(note.Text, 0)
		if daggers != nil {
			for _, dagger := range daggers {
				d2, _ := strconv.Atoi(dagger[1:])
				if d2 >= dagIdx {
					dagIdx = d2 + 1
				}
			}
		}

		// replace the link with our symbol
		// clients can later replace ‡<highest number> with the link
		dagger := fmt.Sprintf("‡%d", dagIdx)
		note.Text = strings.Replace(note.Text, note.Link.String, dagger, 1)
	}

	if len(note.Text) > 140 || len(note.Text) == 0 {
		// TODO: return error?
		return nil
	}
	return note
}

func ListNotesHandler(rw http.ResponseWriter, r *http.Request) {
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	// TODO: range query parms
	// TODO: guest authorization and user id parm

	notes := []Note{}
	err = db.Select(&notes, "SELECT * FROM Note WHERE UserId = ?", token.UserId)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}

	notes2 := []interface{}{}
	for _, note := range notes {
		notes2 = append(notes2, note.AsMap())
	}

	sendData(rw, http.StatusOK, notes2)
}

func PostNoteHandler(rw http.ResponseWriter, r *http.Request) {
	// TODO: we should make an auth middleware, once I can wrap my head around that
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	r.ParseForm()

	noteText := r.PostFormValue("note")
	note := parseNote(noteText)

	if note == nil {
		sendError(rw, http.StatusBadRequest, "Bad Request")
		return
	}

	// TODO: groups
	group := r.PostFormValue("group")
	if len(group) > 0 {
		groupId, _ := strconv.Atoi(group)
		note.GroupId = int64(groupId)
	}

	// TODO: defer processing mentions

	note.UserId = token.UserId
	result, err := db.NamedExec("INSERT INTO `Note` (`UserId`, `Text`, `Link`, `LinkType`, `GroupId`) " +
			"VALUES (:UserId, :Text, :Link, :LinkType, :GroupId)", note)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	note.NoteId, err = result.LastInsertId()
	if err != nil {
		fmt.Println(err)
	}

	sendData(rw, http.StatusCreated, note)
}

func GetNoteHandler(rw http.ResponseWriter, r *http.Request) {
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	noteId, err := strconv.Atoi(mux.Vars(r)["id"])
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusBadRequest, err.Error())
		return
	}

	// TODO: guest authorization

	note := new(Note)
	err = db.Get(note, "SELECT * FROM Note WHERE NoteId = ?", noteId)
	if err == sql.ErrNoRows {
		sendError(rw, http.StatusNotFound, "There is no note with that ID.")
		return
	} else if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}

	sendData(rw, http.StatusOK, note)
}

func PutNoteHandler(rw http.ResponseWriter, r *http.Request) {
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	noteId, err := strconv.Atoi(mux.Vars(r)["id"])
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusBadRequest, err.Error())
		return
	}

	var noteUser int64
	err = db.Get(&noteUser, "SELECT UserId FROM Note WHERE NoteId = ?", noteId)
	if err == sql.ErrNoRows {
		sendError(rw, http.StatusNotFound, "There is no note with that ID.")
		return
	} else if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	} else if noteUser != token.UserId {
		sendError(rw, http.StatusUnauthorized, "Only the note's author may edit it.")
		return
	}

	r.ParseForm()
	noteText := r.PostFormValue("note")
	note := parseNote(noteText)
	if note == nil {
		sendError(rw, http.StatusBadRequest, "Bad Request")
		return
	}

	_, err = db.NamedExec("UPDATE Note SET Text = :Text, Link = :Link, Edited = 1 "+
		"WHERE NoteId = :NoteId", note)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}

	sendData(rw, http.StatusOK, note)
}

func DeleteNoteHandler(rw http.ResponseWriter, r *http.Request) {
	// TODO: auth middleware
	token, err := FetchToken(db, r)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	if token == nil {
		fmt.Println(err)
		sendError(rw, http.StatusUnauthorized, "Unauthorized")
		return
	}

	// TODO: noteId parm middleware
	noteId, err := strconv.Atoi(mux.Vars(r)["id"])
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusBadRequest, err.Error())
		return
	}

	// TODO: owner auth middleware
	var noteUser int64
	err = db.Get(&noteUser, "SELECT UserId FROM Note WHERE NoteId = ?", noteId)
	if err == sql.ErrNoRows {
		sendError(rw, http.StatusNotFound, "There is no note with that ID.")
		return
	} else if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	} else if noteUser != token.UserId {
		sendError(rw, http.StatusUnauthorized, "Only the note's author may delete it.")
		return
	}

	_, err = db.Exec("DELETE FROM `Note` WHERE NoteId = ?", noteId)
	if err != nil {
		fmt.Println(err)
		sendError(rw, http.StatusInternalServerError, err.Error())
		return
	}
	sendData(rw, http.StatusNoContent, "")
}

<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

const (
	pushbulletEnv = "NOTI_PB_ACCESS"
	voiceEnv      = "NOTI_VOICE"
	soundEnv      = "NOTI_SOUND"
	defaultEnv    = "NOTI_DEFAULT"

	version = "v2dev"
)

var (
	title       = flag.String("t", "noti", "")
	message     = flag.String("m", "Done!", "")
	showVersion = flag.Bool("v", false, "")
	showHelp    = flag.Bool("h", false, "")

	// Notifications
	pushbullet = flag.Bool("p", false, "")
	speech     = flag.Bool("s", false, "")
)

func init() {
	flag.StringVar(title, "title", "noti", "")
	flag.StringVar(message, "message", "Done!", "")
	flag.BoolVar(showVersion, "version", false, "")
	flag.BoolVar(showHelp, "help", false, "")

	// Notifications
	flag.BoolVar(speech, "speech", false, "")
	flag.BoolVar(pushbullet, "pushbullet", false, "")
}

func main() {
	log.SetFlags(0)
	flag.Parse()

	if *showVersion {
		fmt.Printf("noti version %s\n", version)
		return
	}
	if *showHelp {
		flag.Usage()
		return
	}

	switch strings.ToLower(os.Getenv(defaultEnv)) {
	case "pushbullet":
		pushbulletNotify()
		return
	case "speech":
		speechNotify()
		return
	case "desktop":
		desktopNotify()
		return
	}

	switch {
	case *pushbullet:
		pushbulletNotify()
	case *speech:
		speechNotify()
	default:
		desktopNotify()
	}
}

func pushbulletNotify() {
	runUtility()

	accessToken := os.Getenv(pushbulletEnv)
	if accessToken == "" {
		log.Fatal("Missing Pushbullet access token, NOTI_PB_ACCESS must be set")
	}

	payload := bytes.NewBuffer([]byte(fmt.Sprintf(
		`{"body":%q,"title":%q,"type":"note"}`, *message, *title,
	)))

	req, err := http.NewRequest("POST", "https://api.pushbullet.com/v2/pushes", payload)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Access-Token", accessToken)
	req.Header.Set("Content-Type", "application/json")

	if _, err = http.DefaultClient.Do(req); err != nil {
		log.Fatal(err)
	}
}

func runUtility() {
	var cmd *exec.Cmd

	if args := flag.Args(); len(args) < 1 {
		return
	} else {
		cmd = exec.Command(args[0], args[1:]...)
		*title = args[0]
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		*title = *title + " failed"
		*message = err.Error()
	}
}
<commit_msg>Add slack<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"strings"
)

const (
	pushbulletEnv = "NOTI_PUSHBULLET_TOK"
	slackEnv      = "NOTI_SLACK_TOK"
	voiceEnv      = "NOTI_VOICE"
	soundEnv      = "NOTI_SOUND"
	defaultEnv    = "NOTI_DEFAULT"

	version = "v2dev"
)

var (
	title       = flag.String("t", "noti", "")
	message     = flag.String("m", "Done!", "")
	showVersion = flag.Bool("v", false, "")
	showHelp    = flag.Bool("h", false, "")

	// Notifications
	pushbullet = flag.Bool("p", false, "")
	speech     = flag.Bool("s", false, "")
	slack      = flag.Bool("S", false, "")
)

func init() {
	flag.StringVar(title, "title", "noti", "")
	flag.StringVar(message, "message", "Done!", "")
	flag.BoolVar(showVersion, "version", false, "")
	flag.BoolVar(showHelp, "help", false, "")

	// Notifications
	flag.BoolVar(speech, "speech", false, "")
	flag.BoolVar(pushbullet, "pushbullet", false, "")
	flag.BoolVar(slack, "slack", false, "")
}

func main() {
	log.SetFlags(0)
	flag.Parse()

	if *showVersion {
		fmt.Printf("noti version %s\n", version)
		return
	}
	if *showHelp {
		flag.Usage()
		return
	}

	switch strings.ToLower(os.Getenv(defaultEnv)) {
	case "slack":
		slackNotify()
		return
	case "pushbullet":
		pushbulletNotify()
		return
	case "speech":
		speechNotify()
		return
	case "desktop":
		desktopNotify()
		return
	}

	switch {
	case *slack:
		slackNotify()
	case *pushbullet:
		pushbulletNotify()
	case *speech:
		speechNotify()
	default:
		desktopNotify()
	}
}

func pushbulletNotify() {
	runUtility()

	accessToken := os.Getenv(pushbulletEnv)
	if accessToken == "" {
		log.Fatalf("Missing access token, %s must be set", pushbulletEnv)
	}

	payload := bytes.NewBuffer([]byte(fmt.Sprintf(
		`{"body":%q,"title":%q,"type":"note"}`, *message, *title,
	)))

	req, err := http.NewRequest("POST", "https://api.pushbullet.com/v2/pushes", payload)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Access-Token", accessToken)
	req.Header.Set("Content-Type", "application/json")

	if _, err = http.DefaultClient.Do(req); err != nil {
		log.Fatal(err)
	}
}

func slackNotify() {
	runUtility()

	accessToken := os.Getenv(slackEnv)
	if accessToken == "" {
		log.Fatalf("Missing access token, %s must be set", slackEnv)
	}

	vals := make(url.Values)
	vals.Set("token", accessToken)
	vals.Set("text", fmt.Sprintf("%s\n%s", *title, *message))
	vals.Set("username", "noti")
	vals.Set("channel", "#random")

	resp, err := http.PostForm("https://slack.com/api/chat.postMessage", vals)
	if err != nil {
		log.Fatal(err)
	}

	r := make(map[string]interface{})
	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		resp.Body.Close()
		log.Fatal(err)
	}
	resp.Body.Close()

	if r["ok"] == false {
		log.Fatal("Slack API error: ", r["error"])
	}
}

func runUtility() {
	var cmd *exec.Cmd

	if args := flag.Args(); len(args) < 1 {
		return
	} else {
		cmd = exec.Command(args[0], args[1:]...)
		*title = args[0]
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		*title = *title + " failed"
		*message = err.Error()
	}
}
<|endoftext|>


<commit_before>package bbolt

import (
	"fmt"
	"os"
	"sort"
	"unsafe"
)

const pageHeaderSize = unsafe.Sizeof(page{})

const minKeysPerPage = 2

const branchPageElementSize = unsafe.Sizeof(branchPageElement{})
const leafPageElementSize = unsafe.Sizeof(leafPageElement{})

const (
	branchPageFlag   = 0x01
	leafPageFlag     = 0x02
	metaPageFlag     = 0x04
	freelistPageFlag = 0x10
)

const (
	bucketLeafFlag = 0x01
)

type pgid uint64

type page struct {
	id       pgid
	flags    uint16
	count    uint16
	overflow uint32
}

// typ returns a human readable page type string used for debugging.
func (p *page) typ() string {
	if (p.flags & branchPageFlag) != 0 {
		return "branch"
	} else if (p.flags & leafPageFlag) != 0 {
		return "leaf"
	} else if (p.flags & metaPageFlag) != 0 {
		return "meta"
	} else if (p.flags & freelistPageFlag) != 0 {
		return "freelist"
	}
	return fmt.Sprintf("unknown<%02x>", p.flags)
}

// meta returns a pointer to the metadata section of the page.
func (p *page) meta() *meta {
	return (*meta)(unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)))
}

// leafPageElement retrieves the leaf node by index
func (p *page) leafPageElement(index uint16) *leafPageElement {
	return (*leafPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		leafPageElementSize, int(index)))
}

// leafPageElements retrieves a list of leaf nodes.
func (p *page) leafPageElements() []leafPageElement {
	if p.count == 0 {
		return nil
	}
	return (*[maxAllocSize]leafPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		unsafe.Sizeof(leafPageElement{}), 0))[:p.count:p.count]
}

// branchPageElement retrieves the branch node by index
func (p *page) branchPageElement(index uint16) *branchPageElement {
	return (*branchPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		unsafe.Sizeof(branchPageElement{}), int(index)))
}

// branchPageElements retrieves a list of branch nodes.
func (p *page) branchPageElements() []branchPageElement {
	if p.count == 0 {
		return nil
	}
	return (*[maxAllocSize]branchPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		unsafe.Sizeof(branchPageElement{}), 0))[:p.count:p.count]
}

// dump writes n bytes of the page to STDERR as hex output.
func (p *page) hexdump(n int) {
	buf := unsafeByteSlice(unsafe.Pointer(p), 0, 0, n)
	fmt.Fprintf(os.Stderr, "%x\n", buf)
}

type pages []*page

func (s pages) Len() int           { return len(s) }
func (s pages) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s pages) Less(i, j int) bool { return s[i].id < s[j].id }

// branchPageElement represents a node on a branch page.
type branchPageElement struct {
	pos   uint32
	ksize uint32
	pgid  pgid
}

// key returns a byte slice of the node key.
func (n *branchPageElement) key() []byte {
	return unsafeByteSlice(unsafe.Pointer(n), 0, int(n.pos), int(n.pos)+int(n.ksize))
}

// leafPageElement represents a node on a leaf page.
type leafPageElement struct {
	flags uint32
	pos   uint32
	ksize uint32
	vsize uint32
}

// key returns a byte slice of the node key.
func (n *leafPageElement) key() []byte {
	i := int(n.pos)
	j := i + int(n.ksize)
	return unsafeByteSlice(unsafe.Pointer(n), 0, i, j)
}

// value returns a byte slice of the node value.
func (n *leafPageElement) value() []byte {
	i := int(n.pos) + int(n.ksize)
	j := i + int(n.vsize)
	return unsafeByteSlice(unsafe.Pointer(n), 0, i, j)
}

// PageInfo represents human readable information about a page.
type PageInfo struct {
	ID            int
	Type          string
	Count         int
	OverflowCount int
}

type pgids []pgid

func (s pgids) Len() int           { return len(s) }
func (s pgids) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s pgids) Less(i, j int) bool { return s[i] < s[j] }

// merge returns the sorted union of a and b.
func (a pgids) merge(b pgids) pgids {
	// Return the opposite slice if one is nil.
	if len(a) == 0 {
		return b
	}
	if len(b) == 0 {
		return a
	}
	merged := make(pgids, len(a)+len(b))
	mergepgids(merged, a, b)
	return merged
}

// mergepgids copies the sorted union of a and b into dst.
// If dst is too small, it panics.
func mergepgids(dst, a, b pgids) {
	if len(dst) < len(a)+len(b) {
		panic(fmt.Errorf("mergepgids bad len %d < %d + %d", len(dst), len(a), len(b)))
	}
	// Copy in the opposite slice if one is nil.
	if len(a) == 0 {
		copy(dst, b)
		return
	}
	if len(b) == 0 {
		copy(dst, a)
		return
	}

	// Merged will hold all elements from both lists.
	merged := dst[:0]

	// Assign lead to the slice with a lower starting value, follow to the higher value.
	lead, follow := a, b
	if b[0] < a[0] {
		lead, follow = b, a
	}

	// Continue while there are elements in the lead.
	for len(lead) > 0 {
		// Merge largest prefix of lead that is ahead of follow[0].
		n := sort.Search(len(lead), func(i int) bool { return lead[i] > follow[0] })
		merged = append(merged, lead[:n]...)
		if n >= len(lead) {
			break
		}

		// Swap lead and follow.
		lead, follow = follow, lead[n:]
	}

	// Append what's left in follow.
	_ = append(merged, follow...)
}
<commit_msg>Shorten max array lengths for the data type<commit_after>package bbolt

import (
	"fmt"
	"os"
	"sort"
	"unsafe"
)

const pageHeaderSize = unsafe.Sizeof(page{})

const minKeysPerPage = 2

const branchPageElementSize = unsafe.Sizeof(branchPageElement{})
const leafPageElementSize = unsafe.Sizeof(leafPageElement{})

const (
	branchPageFlag   = 0x01
	leafPageFlag     = 0x02
	metaPageFlag     = 0x04
	freelistPageFlag = 0x10
)

const (
	bucketLeafFlag = 0x01
)

type pgid uint64

type page struct {
	id       pgid
	flags    uint16
	count    uint16
	overflow uint32
}

// typ returns a human readable page type string used for debugging.
func (p *page) typ() string {
	if (p.flags & branchPageFlag) != 0 {
		return "branch"
	} else if (p.flags & leafPageFlag) != 0 {
		return "leaf"
	} else if (p.flags & metaPageFlag) != 0 {
		return "meta"
	} else if (p.flags & freelistPageFlag) != 0 {
		return "freelist"
	}
	return fmt.Sprintf("unknown<%02x>", p.flags)
}

// meta returns a pointer to the metadata section of the page.
func (p *page) meta() *meta {
	return (*meta)(unsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)))
}

// leafPageElement retrieves the leaf node by index
func (p *page) leafPageElement(index uint16) *leafPageElement {
	return (*leafPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		leafPageElementSize, int(index)))
}

// leafPageElements retrieves a list of leaf nodes.
func (p *page) leafPageElements() []leafPageElement {
	if p.count == 0 {
		return nil
	}
	const maxArraySize = maxAllocSize / unsafe.Sizeof(leafPageElement{})
	return (*[maxArraySize]leafPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		unsafe.Sizeof(leafPageElement{}), 0))[:p.count:p.count]
}

// branchPageElement retrieves the branch node by index
func (p *page) branchPageElement(index uint16) *branchPageElement {
	return (*branchPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		unsafe.Sizeof(branchPageElement{}), int(index)))
}

// branchPageElements retrieves a list of branch nodes.
func (p *page) branchPageElements() []branchPageElement {
	if p.count == 0 {
		return nil
	}
	const maxArraySize = maxAllocSize / unsafe.Sizeof(branchPageElement{})
	return (*[maxArraySize]branchPageElement)(unsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p),
		unsafe.Sizeof(branchPageElement{}), 0))[:p.count:p.count]
}

// dump writes n bytes of the page to STDERR as hex output.
func (p *page) hexdump(n int) {
	buf := unsafeByteSlice(unsafe.Pointer(p), 0, 0, n)
	fmt.Fprintf(os.Stderr, "%x\n", buf)
}

type pages []*page

func (s pages) Len() int           { return len(s) }
func (s pages) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s pages) Less(i, j int) bool { return s[i].id < s[j].id }

// branchPageElement represents a node on a branch page.
type branchPageElement struct {
	pos   uint32
	ksize uint32
	pgid  pgid
}

// key returns a byte slice of the node key.
func (n *branchPageElement) key() []byte {
	return unsafeByteSlice(unsafe.Pointer(n), 0, int(n.pos), int(n.pos)+int(n.ksize))
}

// leafPageElement represents a node on a leaf page.
type leafPageElement struct {
	flags uint32
	pos   uint32
	ksize uint32
	vsize uint32
}

// key returns a byte slice of the node key.
func (n *leafPageElement) key() []byte {
	i := int(n.pos)
	j := i + int(n.ksize)
	return unsafeByteSlice(unsafe.Pointer(n), 0, i, j)
}

// value returns a byte slice of the node value.
func (n *leafPageElement) value() []byte {
	i := int(n.pos) + int(n.ksize)
	j := i + int(n.vsize)
	return unsafeByteSlice(unsafe.Pointer(n), 0, i, j)
}

// PageInfo represents human readable information about a page.
type PageInfo struct {
	ID            int
	Type          string
	Count         int
	OverflowCount int
}

type pgids []pgid

func (s pgids) Len() int           { return len(s) }
func (s pgids) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s pgids) Less(i, j int) bool { return s[i] < s[j] }

// merge returns the sorted union of a and b.
func (a pgids) merge(b pgids) pgids {
	// Return the opposite slice if one is nil.
	if len(a) == 0 {
		return b
	}
	if len(b) == 0 {
		return a
	}
	merged := make(pgids, len(a)+len(b))
	mergepgids(merged, a, b)
	return merged
}

// mergepgids copies the sorted union of a and b into dst.
// If dst is too small, it panics.
func mergepgids(dst, a, b pgids) {
	if len(dst) < len(a)+len(b) {
		panic(fmt.Errorf("mergepgids bad len %d < %d + %d", len(dst), len(a), len(b)))
	}
	// Copy in the opposite slice if one is nil.
	if len(a) == 0 {
		copy(dst, b)
		return
	}
	if len(b) == 0 {
		copy(dst, a)
		return
	}

	// Merged will hold all elements from both lists.
	merged := dst[:0]

	// Assign lead to the slice with a lower starting value, follow to the higher value.
	lead, follow := a, b
	if b[0] < a[0] {
		lead, follow = b, a
	}

	// Continue while there are elements in the lead.
	for len(lead) > 0 {
		// Merge largest prefix of lead that is ahead of follow[0].
		n := sort.Search(len(lead), func(i int) bool { return lead[i] > follow[0] })
		merged = append(merged, lead[:n]...)
		if n >= len(lead) {
			break
		}

		// Swap lead and follow.
		lead, follow = follow, lead[n:]
	}

	// Append what's left in follow.
	_ = append(merged, follow...)
}
<|endoftext|>


<commit_before>package keyring

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func init() {
	supportedBackends[PassBackend] = opener(func(cfg Config) (Keyring, error) {
		pass := &passKeyring{
			passcmd: cfg.PassCmd,
			dir:     cfg.PassDir,
			prefix:  cfg.PassPrefix,
		}
		if cfg.PassCmd == "" {
			pass.passcmd = "pass"
		}
		if cfg.PassDir == "" {
			pass.dir = filepath.Join(os.Getenv("HOME"), ".password-store")
		}
		return pass, nil
	})
}

type passKeyring struct {
	dir     string
	passcmd string
	prefix  string
}

func (k *passKeyring) pass(args ...string) (*exec.Cmd, error) {
	cmd := exec.Command(k.passcmd, args...)
	if k.dir != "" {
		cmd.Env = append(os.Environ(), fmt.Sprintf("PASSWORD_STORE_DIR=%s", k.dir))
	}
	cmd.Stderr = os.Stderr

	return cmd, nil
}

func (k *passKeyring) Get(key string) (Item, error) {
	name := fmt.Sprintf("%s%s", k.prefix, key)
	cmd, err := k.pass("show", name)
	if err != nil {
		return Item{}, err
	}

	output, err := cmd.Output()
	if err != nil {
		return Item{}, err
	}

	var decoded Item
	err = json.Unmarshal(output, &decoded)

	return decoded, err
}

func (k *passKeyring) Set(i Item) error {
	bytes, err := json.Marshal(i)
	if err != nil {
		return err
	}

	name := fmt.Sprintf("%s%s", k.prefix, i.Key)
	cmd, err := k.pass("insert", "-m", "-f", name)
	if err != nil {
		return err
	}

	cmd.Stdin = strings.NewReader(string(bytes))

	err = cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

func (k *passKeyring) Remove(key string) error {
	name := fmt.Sprintf("%s%s", k.prefix, key)
	_, err := k.pass("rm", name)
	if err != nil {
		return err
	}

	return nil
}

func (k *passKeyring) Keys() ([]string, error) {
	var keys = []string{}
	var path = ""

	// TODO This is admittedly lazy; it assumes k.prefix always _ends_ with a "/". If it doesn't, and is instead something like "a/b-" then you'll end up breaking ioutil.ReadDir later
	if strings.Contains(k.prefix, "/") {
		path = fmt.Sprintf("%s/%s", k.dir, k.prefix)
	} else {
		path = k.dir
	}

	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return keys, nil
		}
		return keys, err
	}
	if !info.IsDir() {
		return keys, errors.New(fmt.Sprintf("%s is not a directory", path))
	}

	files, err := ioutil.ReadDir(path)
	if err != nil {
		return keys, err
	}

	for _, f := range files {
		if filepath.Ext(f.Name()) == ".gpg" {
			name := filepath.Base(f.Name())
			keys = append(keys, name[:len(name)-4])

		}
	}

	return keys, nil
}
<commit_msg>Use fmt.Errorf rather than errors.New<commit_after>package keyring

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func init() {
	supportedBackends[PassBackend] = opener(func(cfg Config) (Keyring, error) {
		pass := &passKeyring{
			passcmd: cfg.PassCmd,
			dir:     cfg.PassDir,
			prefix:  cfg.PassPrefix,
		}
		if cfg.PassCmd == "" {
			pass.passcmd = "pass"
		}
		if cfg.PassDir == "" {
			pass.dir = filepath.Join(os.Getenv("HOME"), ".password-store")
		}
		return pass, nil
	})
}

type passKeyring struct {
	dir     string
	passcmd string
	prefix  string
}

func (k *passKeyring) pass(args ...string) (*exec.Cmd, error) {
	cmd := exec.Command(k.passcmd, args...)
	if k.dir != "" {
		cmd.Env = append(os.Environ(), fmt.Sprintf("PASSWORD_STORE_DIR=%s", k.dir))
	}
	cmd.Stderr = os.Stderr

	return cmd, nil
}

func (k *passKeyring) Get(key string) (Item, error) {
	name := fmt.Sprintf("%s%s", k.prefix, key)
	cmd, err := k.pass("show", name)
	if err != nil {
		return Item{}, err
	}

	output, err := cmd.Output()
	if err != nil {
		return Item{}, err
	}

	var decoded Item
	err = json.Unmarshal(output, &decoded)

	return decoded, err
}

func (k *passKeyring) Set(i Item) error {
	bytes, err := json.Marshal(i)
	if err != nil {
		return err
	}

	name := fmt.Sprintf("%s%s", k.prefix, i.Key)
	cmd, err := k.pass("insert", "-m", "-f", name)
	if err != nil {
		return err
	}

	cmd.Stdin = strings.NewReader(string(bytes))

	err = cmd.Run()
	if err != nil {
		return err
	}

	return nil
}

func (k *passKeyring) Remove(key string) error {
	name := fmt.Sprintf("%s%s", k.prefix, key)
	_, err := k.pass("rm", name)
	if err != nil {
		return err
	}

	return nil
}

func (k *passKeyring) Keys() ([]string, error) {
	var keys = []string{}
	var path = ""

	// TODO This is admittedly lazy; it assumes k.prefix always _ends_ with a "/". If it doesn't, and is instead something like "a/b-" then you'll end up breaking ioutil.ReadDir later
	if strings.Contains(k.prefix, "/") {
		path = fmt.Sprintf("%s/%s", k.dir, k.prefix)
	} else {
		path = k.dir
	}

	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return keys, nil
		}
		return keys, err
	}
	if !info.IsDir() {
		return keys, fmt.Errorf("%s is not a directory", path)
	}

	files, err := ioutil.ReadDir(path)
	if err != nil {
		return keys, err
	}

	for _, f := range files {
		if filepath.Ext(f.Name()) == ".gpg" {
			name := filepath.Base(f.Name())
			keys = append(keys, name[:len(name)-4])

		}
	}

	return keys, nil
}
<|endoftext|>


<commit_before>// +build !windows
package mpvipc

import "net"

func dial(path string) (net.Conn, error) {
	return net.Dial("unix", path)
}
<commit_msg>a newline is needed between the build tags and the package name<commit_after>// +build !windows

package mpvipc

import "net"

func dial(path string) (net.Conn, error) {
	return net.Dial("unix", path)
}
<|endoftext|>


<commit_before>/*
Package pipe provides filters that can be chained together in a manner
similar to Unix pipelines.

Each filter is a function that takes as input a sequence of
strings (read from a channel) and produces as output a sequence of
strings (written to a channel).

Filters can be chained together (e.g., via the Run function), the
output of one filter is fed as input to the next filter.  The empty
input is passed to the first filter. The following sequence will
print two lines to standard output:

	err := Run(
		Echo("hello", "world"),
		Reverse(),
		WriteLines(os.Stdout),
	)

An application can implement its own filters easily. For example,
Repeat(n) returns a filter that repeats every input n times:

	func Repeat(n int) Filter {
		return func(arg Arg) {
			for s := range arg.In {
				for i := 0; i < n; i++ {
					arg.Out <- s
				}
			}
		}
	}

	Run(
		Echo("hello"),
		Repeat(10),
	)
*/
package pipe

import (
	"fmt"
	"regexp"
	"sync"
)

// filterErrors records errors accumulated during the execution of a filter.
type filterErrors struct {
	mu     sync.Mutex
	errors []error
}

// Arg contains the data passed to a Filter. Arg.In is a channel that
// produces the input to the filter, and Arg.Out is a channel that
// receives the output from the filter.
type Arg struct {
	In     <-chan string
	Out    chan<- string
	errors *filterErrors
}

// ReportError records an error encountered during an execution of a filter.
// This error will be reported by whatever facility (e.g., ForEach or Run)
// was being used to execute the filters.
func (a *Arg) ReportError(err error) {
	a.errors.mu.Lock()
	defer a.errors.mu.Unlock()
	a.errors.errors = append(a.errors.errors, err)
}

// Filter reads a sequence of strings from a channel and produces a
// sequence on another channel.
type Filter func(Arg)

// Sequence returns a filter that is the concatenation of all filter arguments.
// The output of a filter is fed as input to the next filter.
func Sequence(filters ...Filter) Filter {
	return func(arg Arg) {
		in := arg.In
		for _, f := range filters {
			c := make(chan string, 10000)
			go runAndClose(f, Arg{in, c, arg.errors})
			in = c
		}
		passThrough(Arg{in, arg.Out, arg.errors})
	}
}

// Run() executes the sequence of filters and discards all output.
// It returns either nil, an error if any filter reported an error.
func Run(filters ...Filter) error {
	return ForEach(Sequence(filters...), func(s string) {})
}

// ForEach() calls fn(s) for every item s in the output of filter and
// returns either nil, or any error reported by the execution of the filter.
func ForEach(filter Filter, fn func(s string)) error {
	in := make(chan string, 0)
	close(in)
	out := make(chan string, 10000)
	e := &filterErrors{}
	go runAndClose(filter, Arg{in, out, e})
	for s := range out {
		fn(s)
	}

	e.mu.Lock()
	defer e.mu.Unlock()
	switch len(e.errors) {
	case 0:
		return nil
	case 1:
		return e.errors[0]
	default:
		return fmt.Errorf("Filter errors: %s", e.errors)
	}
}

func runAndClose(f Filter, arg Arg) {
	f(arg)
	close(arg.Out)
}

// passThrough copies all items read from in to out.
func passThrough(arg Arg) {
	for s := range arg.In {
		arg.Out <- s
	}
}

// Echo emits items.
// Any input items are copied verbatim to the output before items are emitted.
func Echo(items ...string) Filter {
	return func(arg Arg) {
		passThrough(arg)
		for _, s := range items {
			arg.Out <- s
		}
	}
}

// Numbers copies its input and then emits the integers x..y
func Numbers(x, y int) Filter {
	return func(arg Arg) {
		passThrough(arg)
		for i := x; i <= y; i++ {
			arg.Out <- fmt.Sprintf("%d", i)
		}
	}
}

// Map calls fn(x) for every item x and yields the outputs of the fn calls.
func Map(fn func(string) string) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			arg.Out <- fn(s)
		}
	}
}

// If emits every input x for which fn(x) is true.
func If(fn func(string) bool) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			if fn(s) {
				arg.Out <- s
			}
		}
	}
}

// Grep emits every input x that matches the regular expression r.
func Grep(r string) Filter {
	re := regexp.MustCompile(r)
	return If(re.MatchString)
}

// GrepNot emits every input x that does not match the regular expression r.
func GrepNot(r string) Filter {
	re := regexp.MustCompile(r)
	return If(func(s string) bool { return !re.MatchString(s) })
}

// Uniq squashes adjacent identical items in arg.In into a single output.
func Uniq() Filter {
	return func(arg Arg) {
		first := true
		last := ""
		for s := range arg.In {
			if first || last != s {
				arg.Out <- s
			}
			last = s
			first = false
		}
	}
}

// UniqWithCount squashes adjacent identical items in arg.In into a single
// output prefixed with the count of identical items.
func UniqWithCount() Filter {
	return func(arg Arg) {
		current := ""
		count := 0
		for s := range arg.In {
			if s != current {
				if count > 0 {
					arg.Out <- fmt.Sprintf("%d %s", count, current)
				}
				count = 0
				current = s
			}
			count++
		}
		if count > 0 {
			arg.Out <- fmt.Sprintf("%d %s", count, current)
		}
	}
}

// Substitute replaces all occurrences of the regular expression r in
// an input item with replacement.  The replacement string can contain
// $1, $2, etc. which represent submatches of r.
func Substitute(r, replacement string) Filter {
	re := regexp.MustCompile(r)
	return func(arg Arg) {
		for s := range arg.In {
			arg.Out <- re.ReplaceAllString(s, replacement)
		}
	}
}

// Reverse yields items in the reverse of the order it received them.
func Reverse() Filter {
	return func(arg Arg) {
		var data []string
		for s := range arg.In {
			data = append(data, s)
		}
		for i := len(data) - 1; i >= 0; i-- {
			arg.Out <- data[i]
		}
	}
}

// First yields the first n items that it receives.
func First(n int) Filter {
	return func(arg Arg) {
		emitted := 0
		for s := range arg.In {
			if emitted < n {
				arg.Out <- s
				emitted++
			}
		}
	}
}

// DropFirst yields all items except for the first n items that it receives.
func DropFirst(n int) Filter {
	return func(arg Arg) {
		emitted := 0
		for s := range arg.In {
			if emitted >= n {
				arg.Out <- s
			}
			emitted++
		}
	}
}

// Last yields the last n items that it receives.
func Last(n int) Filter {
	return func(arg Arg) {
		var buf []string
		for s := range arg.In {
			buf = append(buf, s)
			if len(buf) > n {
				buf = buf[1:]
			}
		}
		for _, s := range buf {
			arg.Out <- s
		}
	}
}

// DropFirst yields all items except for the last n items that it receives.
func DropLast(n int) Filter {
	return func(arg Arg) {
		var buf []string
		for s := range arg.In {
			buf = append(buf, s)
			if len(buf) > n {
				arg.Out <- buf[0]
				buf = buf[1:]
			}
		}
	}
}

// NumberLines prefixes its item with its index in the input sequence
// (starting at 1).
func NumberLines() Filter {
	return func(arg Arg) {
		line := 1
		for s := range arg.In {
			arg.Out <- fmt.Sprintf("%5d %s", line, s)
			line++
		}
	}
}

// Cut emits just the bytes indexed [start..end] of each input item.
func Cut(start, end int) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			if len(s) > end {
				s = s[:end+1]
			}
			if len(s) < start {
				s = ""
			} else {
				s = s[start:]
			}
			arg.Out <- s
		}
	}
}

// Select splits each item into columns and yields the concatenation
// of the columns numbers passed as arguments to Select.  Columns are
// numbered starting at 1. A column number of 0 is interpreted as the
// full string.
func Select(columns ...int) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			result := ""
			for _, col := range columns {
				if _, c := column(s, col); c != "" {
					if result != "" {
						result = result + " "
					}
					result = result + c
				}
			}
			arg.Out <- result
		}
	}
}
<commit_msg>handle errors from regexp.Compile<commit_after>/*
Package pipe provides filters that can be chained together in a manner
similar to Unix pipelines.

Each filter is a function that takes as input a sequence of
strings (read from a channel) and produces as output a sequence of
strings (written to a channel).

Filters can be chained together (e.g., via the Run function), the
output of one filter is fed as input to the next filter.  The empty
input is passed to the first filter. The following sequence will
print two lines to standard output:

	err := Run(
		Echo("hello", "world"),
		Reverse(),
		WriteLines(os.Stdout),
	)

An application can implement its own filters easily. For example,
Repeat(n) returns a filter that repeats every input n times:

	func Repeat(n int) Filter {
		return func(arg Arg) {
			for s := range arg.In {
				for i := 0; i < n; i++ {
					arg.Out <- s
				}
			}
		}
	}

	Run(
		Echo("hello"),
		Repeat(10),
	)
*/
package pipe

import (
	"fmt"
	"regexp"
	"sync"
)

// filterErrors records errors accumulated during the execution of a filter.
type filterErrors struct {
	mu     sync.Mutex
	errors []error
}

// Arg contains the data passed to a Filter. Arg.In is a channel that
// produces the input to the filter, and Arg.Out is a channel that
// receives the output from the filter.
type Arg struct {
	In     <-chan string
	Out    chan<- string
	errors *filterErrors
}

// ReportError records an error encountered during an execution of a filter.
// This error will be reported by whatever facility (e.g., ForEach or Run)
// was being used to execute the filters.
func (a *Arg) ReportError(err error) {
	a.errors.mu.Lock()
	defer a.errors.mu.Unlock()
	a.errors.errors = append(a.errors.errors, err)
}

// Filter reads a sequence of strings from a channel and produces a
// sequence on another channel.
type Filter func(Arg)

// Sequence returns a filter that is the concatenation of all filter arguments.
// The output of a filter is fed as input to the next filter.
func Sequence(filters ...Filter) Filter {
	return func(arg Arg) {
		in := arg.In
		for _, f := range filters {
			c := make(chan string, 10000)
			go runAndClose(f, Arg{in, c, arg.errors})
			in = c
		}
		passThrough(Arg{in, arg.Out, arg.errors})
	}
}

// Run() executes the sequence of filters and discards all output.
// It returns either nil, an error if any filter reported an error.
func Run(filters ...Filter) error {
	return ForEach(Sequence(filters...), func(s string) {})
}

// ForEach() calls fn(s) for every item s in the output of filter and
// returns either nil, or any error reported by the execution of the filter.
func ForEach(filter Filter, fn func(s string)) error {
	in := make(chan string, 0)
	close(in)
	out := make(chan string, 10000)
	e := &filterErrors{}
	go runAndClose(filter, Arg{in, out, e})
	for s := range out {
		fn(s)
	}

	e.mu.Lock()
	defer e.mu.Unlock()
	switch len(e.errors) {
	case 0:
		return nil
	case 1:
		return e.errors[0]
	default:
		return fmt.Errorf("Filter errors: %s", e.errors)
	}
}

func runAndClose(f Filter, arg Arg) {
	f(arg)
	close(arg.Out)
}

// passThrough copies all items read from in to out.
func passThrough(arg Arg) {
	for s := range arg.In {
		arg.Out <- s
	}
}

// Echo emits items.
// Any input items are copied verbatim to the output before items are emitted.
func Echo(items ...string) Filter {
	return func(arg Arg) {
		passThrough(arg)
		for _, s := range items {
			arg.Out <- s
		}
	}
}

// Numbers copies its input and then emits the integers x..y
func Numbers(x, y int) Filter {
	return func(arg Arg) {
		passThrough(arg)
		for i := x; i <= y; i++ {
			arg.Out <- fmt.Sprintf("%d", i)
		}
	}
}

// Map calls fn(x) for every item x and yields the outputs of the fn calls.
func Map(fn func(string) string) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			arg.Out <- fn(s)
		}
	}
}

// If emits every input x for which fn(x) is true.
func If(fn func(string) bool) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			if fn(s) {
				arg.Out <- s
			}
		}
	}
}

func errorFilter(err error) Filter {
	return func(arg Arg) {
		arg.ReportError(err)
		for _ = range arg.In {
			// Drop the input
		}
	}
}

// Grep emits every input x that matches the regular expression r.
func Grep(r string) Filter {
	re, err := regexp.Compile(r)
	if err != nil {
		return errorFilter(err)
	}
	return If(re.MatchString)
}

// GrepNot emits every input x that does not match the regular expression r.
func GrepNot(r string) Filter {
	re, err := regexp.Compile(r)
	if err != nil {
		return errorFilter(err)
	}
	return If(func(s string) bool { return !re.MatchString(s) })
}

// Uniq squashes adjacent identical items in arg.In into a single output.
func Uniq() Filter {
	return func(arg Arg) {
		first := true
		last := ""
		for s := range arg.In {
			if first || last != s {
				arg.Out <- s
			}
			last = s
			first = false
		}
	}
}

// UniqWithCount squashes adjacent identical items in arg.In into a single
// output prefixed with the count of identical items.
func UniqWithCount() Filter {
	return func(arg Arg) {
		current := ""
		count := 0
		for s := range arg.In {
			if s != current {
				if count > 0 {
					arg.Out <- fmt.Sprintf("%d %s", count, current)
				}
				count = 0
				current = s
			}
			count++
		}
		if count > 0 {
			arg.Out <- fmt.Sprintf("%d %s", count, current)
		}
	}
}

// Substitute replaces all occurrences of the regular expression r in
// an input item with replacement.  The replacement string can contain
// $1, $2, etc. which represent submatches of r.
func Substitute(r, replacement string) Filter {
	re, err := regexp.Compile(r)
	if err != nil {
		return errorFilter(err)
	}
	return func(arg Arg) {
		for s := range arg.In {
			arg.Out <- re.ReplaceAllString(s, replacement)
		}
	}
}

// Reverse yields items in the reverse of the order it received them.
func Reverse() Filter {
	return func(arg Arg) {
		var data []string
		for s := range arg.In {
			data = append(data, s)
		}
		for i := len(data) - 1; i >= 0; i-- {
			arg.Out <- data[i]
		}
	}
}

// First yields the first n items that it receives.
func First(n int) Filter {
	return func(arg Arg) {
		emitted := 0
		for s := range arg.In {
			if emitted < n {
				arg.Out <- s
				emitted++
			}
		}
	}
}

// DropFirst yields all items except for the first n items that it receives.
func DropFirst(n int) Filter {
	return func(arg Arg) {
		emitted := 0
		for s := range arg.In {
			if emitted >= n {
				arg.Out <- s
			}
			emitted++
		}
	}
}

// Last yields the last n items that it receives.
func Last(n int) Filter {
	return func(arg Arg) {
		var buf []string
		for s := range arg.In {
			buf = append(buf, s)
			if len(buf) > n {
				buf = buf[1:]
			}
		}
		for _, s := range buf {
			arg.Out <- s
		}
	}
}

// DropFirst yields all items except for the last n items that it receives.
func DropLast(n int) Filter {
	return func(arg Arg) {
		var buf []string
		for s := range arg.In {
			buf = append(buf, s)
			if len(buf) > n {
				arg.Out <- buf[0]
				buf = buf[1:]
			}
		}
	}
}

// NumberLines prefixes its item with its index in the input sequence
// (starting at 1).
func NumberLines() Filter {
	return func(arg Arg) {
		line := 1
		for s := range arg.In {
			arg.Out <- fmt.Sprintf("%5d %s", line, s)
			line++
		}
	}
}

// Cut emits just the bytes indexed [start..end] of each input item.
func Cut(start, end int) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			if len(s) > end {
				s = s[:end+1]
			}
			if len(s) < start {
				s = ""
			} else {
				s = s[start:]
			}
			arg.Out <- s
		}
	}
}

// Select splits each item into columns and yields the concatenation
// of the columns numbers passed as arguments to Select.  Columns are
// numbered starting at 1. A column number of 0 is interpreted as the
// full string.
func Select(columns ...int) Filter {
	return func(arg Arg) {
		for s := range arg.In {
			result := ""
			for _, col := range columns {
				if _, c := column(s, col); c != "" {
					if result != "" {
						result = result + " "
					}
					result = result + c
				}
			}
			arg.Out <- result
		}
	}
}
<|endoftext|>